xref: /llvm-project/flang/runtime/assign.cpp (revision 42be165dde50c29e1d104f38938c03c95b4471cf)
165f52904Speter klausler //===-- runtime/assign.cpp ------------------------------------------------===//
265f52904Speter klausler //
365f52904Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
465f52904Speter klausler // See https://llvm.org/LICENSE.txt for license information.
565f52904Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
665f52904Speter klausler //
765f52904Speter klausler //===----------------------------------------------------------------------===//
865f52904Speter klausler 
9830c0b90SPeter Klausler #include "flang/Runtime/assign.h"
10755535b5SPeter Klausler #include "assign-impl.h"
1165f52904Speter klausler #include "derived.h"
1265f52904Speter klausler #include "stat.h"
1365f52904Speter klausler #include "terminator.h"
14b21c24c3SPeter Klausler #include "tools.h"
1565f52904Speter klausler #include "type-info.h"
16830c0b90SPeter Klausler #include "flang/Runtime/descriptor.h"
1765f52904Speter klausler 
1865f52904Speter klausler namespace Fortran::runtime {
1965f52904Speter klausler 
20755535b5SPeter Klausler // Predicate: is the left-hand side of an assignment an allocated allocatable
21755535b5SPeter Klausler // that must be deallocated?
228b953fddSSlava Zakharin static inline RT_API_ATTRS bool MustDeallocateLHS(
238489f177SPeter Klausler     Descriptor &to, const Descriptor &from, Terminator &terminator, int flags) {
24755535b5SPeter Klausler   // Top-level assignments to allocatable variables (*not* components)
25755535b5SPeter Klausler   // may first deallocate existing content if there's about to be a
26755535b5SPeter Klausler   // change in type or shape; see F'2018 10.2.1.3(3).
278489f177SPeter Klausler   if (!(flags & MaybeReallocate)) {
288489f177SPeter Klausler     return false;
298489f177SPeter Klausler   }
30755535b5SPeter Klausler   if (!to.IsAllocatable() || !to.IsAllocated()) {
31755535b5SPeter Klausler     return false;
32755535b5SPeter Klausler   }
33755535b5SPeter Klausler   if (to.type() != from.type()) {
34755535b5SPeter Klausler     return true;
35755535b5SPeter Klausler   }
368489f177SPeter Klausler   if (!(flags & ExplicitLengthCharacterLHS) && to.type().IsCharacter() &&
378489f177SPeter Klausler       to.ElementBytes() != from.ElementBytes()) {
388489f177SPeter Klausler     return true;
398489f177SPeter Klausler   }
408489f177SPeter Klausler   if (flags & PolymorphicLHS) {
41755535b5SPeter Klausler     DescriptorAddendum *toAddendum{to.Addendum()};
42755535b5SPeter Klausler     const typeInfo::DerivedType *toDerived{
43755535b5SPeter Klausler         toAddendum ? toAddendum->derivedType() : nullptr};
44755535b5SPeter Klausler     const DescriptorAddendum *fromAddendum{from.Addendum()};
45755535b5SPeter Klausler     const typeInfo::DerivedType *fromDerived{
46755535b5SPeter Klausler         fromAddendum ? fromAddendum->derivedType() : nullptr};
47755535b5SPeter Klausler     if (toDerived != fromDerived) {
48755535b5SPeter Klausler       return true;
49755535b5SPeter Klausler     }
508489f177SPeter Klausler     if (fromDerived) {
51755535b5SPeter Klausler       // Distinct LEN parameters? Deallocate
528489f177SPeter Klausler       std::size_t lenParms{fromDerived->LenParameters()};
53755535b5SPeter Klausler       for (std::size_t j{0}; j < lenParms; ++j) {
54755535b5SPeter Klausler         if (toAddendum->LenParameterValue(j) !=
55755535b5SPeter Klausler             fromAddendum->LenParameterValue(j)) {
56755535b5SPeter Klausler           return true;
57755535b5SPeter Klausler         }
58755535b5SPeter Klausler       }
59755535b5SPeter Klausler     }
608489f177SPeter Klausler   }
61755535b5SPeter Klausler   if (from.rank() > 0) {
62755535b5SPeter Klausler     // Distinct shape? Deallocate
63755535b5SPeter Klausler     int rank{to.rank()};
64755535b5SPeter Klausler     for (int j{0}; j < rank; ++j) {
65755535b5SPeter Klausler       if (to.GetDimension(j).Extent() != from.GetDimension(j).Extent()) {
66755535b5SPeter Klausler         return true;
67755535b5SPeter Klausler       }
68755535b5SPeter Klausler     }
69755535b5SPeter Klausler   }
70755535b5SPeter Klausler   return false;
71755535b5SPeter Klausler }
72755535b5SPeter Klausler 
73755535b5SPeter Klausler // Utility: allocate the allocatable left-hand side, either because it was
74755535b5SPeter Klausler // originally deallocated or because it required reallocation
758b953fddSSlava Zakharin static RT_API_ATTRS int AllocateAssignmentLHS(
768489f177SPeter Klausler     Descriptor &to, const Descriptor &from, Terminator &terminator, int flags) {
77755535b5SPeter Klausler   to.raw().type = from.raw().type;
788489f177SPeter Klausler   if (!(flags & ExplicitLengthCharacterLHS)) {
79755535b5SPeter Klausler     to.raw().elem_len = from.ElementBytes();
808489f177SPeter Klausler   }
81755535b5SPeter Klausler   const typeInfo::DerivedType *derived{nullptr};
82755535b5SPeter Klausler   if (const DescriptorAddendum * fromAddendum{from.Addendum()}) {
83755535b5SPeter Klausler     derived = fromAddendum->derivedType();
84755535b5SPeter Klausler     if (DescriptorAddendum * toAddendum{to.Addendum()}) {
85755535b5SPeter Klausler       toAddendum->set_derivedType(derived);
86755535b5SPeter Klausler       std::size_t lenParms{derived ? derived->LenParameters() : 0};
87755535b5SPeter Klausler       for (std::size_t j{0}; j < lenParms; ++j) {
88755535b5SPeter Klausler         toAddendum->SetLenParameterValue(j, fromAddendum->LenParameterValue(j));
89755535b5SPeter Klausler       }
90755535b5SPeter Klausler     }
91755535b5SPeter Klausler   }
92755535b5SPeter Klausler   // subtle: leave bounds in place when "from" is scalar (10.2.1.3(3))
93755535b5SPeter Klausler   int rank{from.rank()};
94755535b5SPeter Klausler   auto stride{static_cast<SubscriptValue>(to.ElementBytes())};
95755535b5SPeter Klausler   for (int j{0}; j < rank; ++j) {
96755535b5SPeter Klausler     auto &toDim{to.GetDimension(j)};
97755535b5SPeter Klausler     const auto &fromDim{from.GetDimension(j)};
98755535b5SPeter Klausler     toDim.SetBounds(fromDim.LowerBound(), fromDim.UpperBound());
99755535b5SPeter Klausler     toDim.SetByteStride(stride);
100755535b5SPeter Klausler     stride *= toDim.Extent();
101755535b5SPeter Klausler   }
102755535b5SPeter Klausler   int result{ReturnError(terminator, to.Allocate())};
103755535b5SPeter Klausler   if (result == StatOk && derived && !derived->noInitializationNeeded()) {
104755535b5SPeter Klausler     result = ReturnError(terminator, Initialize(to, *derived, terminator));
105755535b5SPeter Klausler   }
106755535b5SPeter Klausler   return result;
107755535b5SPeter Klausler }
108755535b5SPeter Klausler 
109755535b5SPeter Klausler // least <= 0, most >= 0
1108b953fddSSlava Zakharin static RT_API_ATTRS void MaximalByteOffsetRange(
111755535b5SPeter Klausler     const Descriptor &desc, std::int64_t &least, std::int64_t &most) {
112755535b5SPeter Klausler   least = most = 0;
113755535b5SPeter Klausler   if (desc.ElementBytes() == 0) {
114755535b5SPeter Klausler     return;
115755535b5SPeter Klausler   }
116755535b5SPeter Klausler   int n{desc.raw().rank};
117755535b5SPeter Klausler   for (int j{0}; j < n; ++j) {
118755535b5SPeter Klausler     const auto &dim{desc.GetDimension(j)};
119755535b5SPeter Klausler     auto extent{dim.Extent()};
120755535b5SPeter Klausler     if (extent > 0) {
121755535b5SPeter Klausler       auto sm{dim.ByteStride()};
122755535b5SPeter Klausler       if (sm < 0) {
1237c7ffa7bSSlava Zakharin         least += (extent - 1) * sm;
124755535b5SPeter Klausler       } else {
1257c7ffa7bSSlava Zakharin         most += (extent - 1) * sm;
126755535b5SPeter Klausler       }
127755535b5SPeter Klausler     }
128755535b5SPeter Klausler   }
129755535b5SPeter Klausler   most += desc.ElementBytes() - 1;
130755535b5SPeter Klausler }
131755535b5SPeter Klausler 
1328b953fddSSlava Zakharin static inline RT_API_ATTRS bool RangesOverlap(const char *aStart,
1338b953fddSSlava Zakharin     const char *aEnd, const char *bStart, const char *bEnd) {
134755535b5SPeter Klausler   return aEnd >= bStart && bEnd >= aStart;
135755535b5SPeter Klausler }
136755535b5SPeter Klausler 
137755535b5SPeter Klausler // Predicate: could the left-hand and right-hand sides of the assignment
138755535b5SPeter Klausler // possibly overlap in memory?  Note that the descriptors themeselves
139755535b5SPeter Klausler // are included in the test.
1408b953fddSSlava Zakharin static RT_API_ATTRS bool MayAlias(const Descriptor &x, const Descriptor &y) {
14174a5d747SPeter Klausler   const char *xBase{x.OffsetElement()};
14274a5d747SPeter Klausler   const char *yBase{y.OffsetElement()};
14374a5d747SPeter Klausler   if (!xBase || !yBase) {
14474a5d747SPeter Klausler     return false; // not both allocated
14574a5d747SPeter Klausler   }
146755535b5SPeter Klausler   const char *xDesc{reinterpret_cast<const char *>(&x)};
14707e053fbSPeter Klausler   const char *xDescLast{xDesc + x.SizeInBytes() - 1};
148114ce273SPeter Klausler   const char *yDesc{reinterpret_cast<const char *>(&y)};
14907e053fbSPeter Klausler   const char *yDescLast{yDesc + y.SizeInBytes() - 1};
150755535b5SPeter Klausler   std::int64_t xLeast, xMost, yLeast, yMost;
151755535b5SPeter Klausler   MaximalByteOffsetRange(x, xLeast, xMost);
152755535b5SPeter Klausler   MaximalByteOffsetRange(y, yLeast, yMost);
153755535b5SPeter Klausler   if (RangesOverlap(xDesc, xDescLast, yBase + yLeast, yBase + yMost) ||
154755535b5SPeter Klausler       RangesOverlap(yDesc, yDescLast, xBase + xLeast, xBase + xMost)) {
155755535b5SPeter Klausler     // A descriptor overlaps with the storage described by the other;
156755535b5SPeter Klausler     // this can arise when an allocatable or pointer component is
157755535b5SPeter Klausler     // being assigned to/from.
158755535b5SPeter Klausler     return true;
159755535b5SPeter Klausler   }
160755535b5SPeter Klausler   if (!RangesOverlap(
161755535b5SPeter Klausler           xBase + xLeast, xBase + xMost, yBase + yLeast, yBase + yMost)) {
162755535b5SPeter Klausler     return false; // no storage overlap
163755535b5SPeter Klausler   }
164755535b5SPeter Klausler   // TODO: check dimensions: if any is independent, return false
165755535b5SPeter Klausler   return true;
166755535b5SPeter Klausler }
167755535b5SPeter Klausler 
1688b953fddSSlava Zakharin static RT_API_ATTRS void DoScalarDefinedAssignment(const Descriptor &to,
16965f52904Speter klausler     const Descriptor &from, const typeInfo::SpecialBinding &special) {
17065f52904Speter klausler   bool toIsDesc{special.IsArgDescriptor(0)};
17165f52904Speter klausler   bool fromIsDesc{special.IsArgDescriptor(1)};
17265f52904Speter klausler   if (toIsDesc) {
17365f52904Speter klausler     if (fromIsDesc) {
17465f52904Speter klausler       auto *p{
17565f52904Speter klausler           special.GetProc<void (*)(const Descriptor &, const Descriptor &)>()};
17665f52904Speter klausler       p(to, from);
17765f52904Speter klausler     } else {
17865f52904Speter klausler       auto *p{special.GetProc<void (*)(const Descriptor &, void *)>()};
17965f52904Speter klausler       p(to, from.raw().base_addr);
18065f52904Speter klausler     }
18165f52904Speter klausler   } else {
18265f52904Speter klausler     if (fromIsDesc) {
18365f52904Speter klausler       auto *p{special.GetProc<void (*)(void *, const Descriptor &)>()};
18465f52904Speter klausler       p(to.raw().base_addr, from);
18565f52904Speter klausler     } else {
18665f52904Speter klausler       auto *p{special.GetProc<void (*)(void *, void *)>()};
18765f52904Speter klausler       p(to.raw().base_addr, from.raw().base_addr);
18865f52904Speter klausler     }
18965f52904Speter klausler   }
19065f52904Speter klausler }
19165f52904Speter klausler 
1928b953fddSSlava Zakharin static RT_API_ATTRS void DoElementalDefinedAssignment(const Descriptor &to,
1939c3550f6SPeter Klausler     const Descriptor &from, const typeInfo::DerivedType &derived,
1949c3550f6SPeter Klausler     const typeInfo::SpecialBinding &special) {
195755535b5SPeter Klausler   SubscriptValue toAt[maxRank], fromAt[maxRank];
196755535b5SPeter Klausler   to.GetLowerBounds(toAt);
197755535b5SPeter Klausler   from.GetLowerBounds(fromAt);
19865f52904Speter klausler   StaticDescriptor<maxRank, true, 8 /*?*/> statDesc[2];
19965f52904Speter klausler   Descriptor &toElementDesc{statDesc[0].descriptor()};
20065f52904Speter klausler   Descriptor &fromElementDesc{statDesc[1].descriptor()};
2019c3550f6SPeter Klausler   toElementDesc.Establish(derived, nullptr, 0, nullptr, CFI_attribute_pointer);
2029c3550f6SPeter Klausler   fromElementDesc.Establish(
2039c3550f6SPeter Klausler       derived, nullptr, 0, nullptr, CFI_attribute_pointer);
204755535b5SPeter Klausler   for (std::size_t toElements{to.Elements()}; toElements-- > 0;
205755535b5SPeter Klausler        to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
20665f52904Speter klausler     toElementDesc.set_base_addr(to.Element<char>(toAt));
20765f52904Speter klausler     fromElementDesc.set_base_addr(from.Element<char>(fromAt));
20865f52904Speter klausler     DoScalarDefinedAssignment(toElementDesc, fromElementDesc, special);
20965f52904Speter klausler   }
21065f52904Speter klausler }
21165f52904Speter klausler 
2128489f177SPeter Klausler template <typename CHAR>
2138b953fddSSlava Zakharin static RT_API_ATTRS void BlankPadCharacterAssignment(Descriptor &to,
2148b953fddSSlava Zakharin     const Descriptor &from, SubscriptValue toAt[], SubscriptValue fromAt[],
2158b953fddSSlava Zakharin     std::size_t elements, std::size_t toElementBytes,
2168b953fddSSlava Zakharin     std::size_t fromElementBytes) {
2178489f177SPeter Klausler   std::size_t padding{(toElementBytes - fromElementBytes) / sizeof(CHAR)};
2181ac31a0bSJean Perier   std::size_t copiedCharacters{fromElementBytes / sizeof(CHAR)};
2198489f177SPeter Klausler   for (; elements-- > 0;
2208489f177SPeter Klausler        to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
2218489f177SPeter Klausler     CHAR *p{to.Element<CHAR>(toAt)};
2228b953fddSSlava Zakharin     Fortran::runtime::memmove(
2238489f177SPeter Klausler         p, from.Element<std::add_const_t<CHAR>>(fromAt), fromElementBytes);
2241ac31a0bSJean Perier     p += copiedCharacters;
2258489f177SPeter Klausler     for (auto n{padding}; n-- > 0;) {
2268489f177SPeter Klausler       *p++ = CHAR{' '};
2278489f177SPeter Klausler     }
2288489f177SPeter Klausler   }
2298489f177SPeter Klausler }
2308489f177SPeter Klausler 
231755535b5SPeter Klausler // Common implementation of assignments, both intrinsic assignments and
232755535b5SPeter Klausler // those cases of polymorphic user-defined ASSIGNMENT(=) TBPs that could not
233755535b5SPeter Klausler // be resolved in semantics.  Most assignment statements do not need any
234755535b5SPeter Klausler // of the capabilities of this function -- but when the LHS is allocatable,
235755535b5SPeter Klausler // the type might have a user-defined ASSIGNMENT(=), or the type might be
236755535b5SPeter Klausler // finalizable, this function should be used.
2375226f8a9SJean Perier // When "to" is not a whole allocatable, "from" is an array, and defined
2385226f8a9SJean Perier // assignments are not used, "to" and "from" only need to have the same number
2395226f8a9SJean Perier // of elements, but their shape need not to conform (the assignment is done in
2405226f8a9SJean Perier // element sequence order). This facilitates some internal usages, like when
2415226f8a9SJean Perier // dealing with array constructors.
2427792dbe2SValentin Clement (バレンタイン クレメン) RT_API_ATTRS void Assign(Descriptor &to, const Descriptor &from,
2437792dbe2SValentin Clement (バレンタイン クレメン)     Terminator &terminator, int flags, MemmoveFct memmoveFct) {
2446e4984a9SSlava Zakharin   bool mustDeallocateLHS{(flags & DeallocateLHS) ||
2456e4984a9SSlava Zakharin       MustDeallocateLHS(to, from, terminator, flags)};
24665f52904Speter klausler   DescriptorAddendum *toAddendum{to.Addendum()};
24765f52904Speter klausler   const typeInfo::DerivedType *toDerived{
24865f52904Speter klausler       toAddendum ? toAddendum->derivedType() : nullptr};
2496e4984a9SSlava Zakharin   if (toDerived && (flags & NeedFinalization) &&
2506e4984a9SSlava Zakharin       toDerived->noFinalizationNeeded()) {
2518489f177SPeter Klausler     flags &= ~NeedFinalization;
2528489f177SPeter Klausler   }
2538489f177SPeter Klausler   std::size_t toElementBytes{to.ElementBytes()};
2548489f177SPeter Klausler   std::size_t fromElementBytes{from.ElementBytes()};
255f92309a3SSlava Zakharin   // The following lambda definition violates the conding style,
256f92309a3SSlava Zakharin   // but cuda-11.8 nvcc hits an internal error with the brace initialization.
257f92309a3SSlava Zakharin   auto isSimpleMemmove = [&]() {
2588489f177SPeter Klausler     return !toDerived && to.rank() == from.rank() && to.IsContiguous() &&
2598489f177SPeter Klausler         from.IsContiguous() && toElementBytes == fromElementBytes;
260f92309a3SSlava Zakharin   };
261755535b5SPeter Klausler   StaticDescriptor<maxRank, true, 10 /*?*/> deferredDeallocStatDesc;
262755535b5SPeter Klausler   Descriptor *deferDeallocation{nullptr};
263755535b5SPeter Klausler   if (MayAlias(to, from)) {
264755535b5SPeter Klausler     if (mustDeallocateLHS) {
265755535b5SPeter Klausler       deferDeallocation = &deferredDeallocStatDesc.descriptor();
266755535b5SPeter Klausler       std::memcpy(deferDeallocation, &to, to.SizeInBytes());
267755535b5SPeter Klausler       to.set_base_addr(nullptr);
2688489f177SPeter Klausler     } else if (!isSimpleMemmove()) {
269755535b5SPeter Klausler       // Handle LHS/RHS aliasing by copying RHS into a temp, then
270755535b5SPeter Klausler       // recursively assigning from that temp.
271755535b5SPeter Klausler       auto descBytes{from.SizeInBytes()};
272755535b5SPeter Klausler       StaticDescriptor<maxRank, true, 16> staticDesc;
273755535b5SPeter Klausler       Descriptor &newFrom{staticDesc.descriptor()};
274755535b5SPeter Klausler       std::memcpy(&newFrom, &from, descBytes);
275bf536456SSlava Zakharin       // Pretend the temporary descriptor is for an ALLOCATABLE
276bf536456SSlava Zakharin       // entity, otherwise, the Deallocate() below will not
277bf536456SSlava Zakharin       // free the descriptor memory.
278bf536456SSlava Zakharin       newFrom.raw().attribute = CFI_attribute_allocatable;
279755535b5SPeter Klausler       auto stat{ReturnError(terminator, newFrom.Allocate())};
280755535b5SPeter Klausler       if (stat == StatOk) {
281c78b528fSSlava Zakharin         if (HasDynamicComponent(from)) {
282c78b528fSSlava Zakharin           // If 'from' has allocatable/automatic component, we cannot
283c78b528fSSlava Zakharin           // just make a shallow copy of the descriptor member.
284c78b528fSSlava Zakharin           // This will still leave data overlap in 'to' and 'newFrom'.
285c78b528fSSlava Zakharin           // For example:
286c78b528fSSlava Zakharin           //   type t
287c78b528fSSlava Zakharin           //     character, allocatable :: c(:)
288c78b528fSSlava Zakharin           //   end type t
289c78b528fSSlava Zakharin           //   type(t) :: x(3)
290c78b528fSSlava Zakharin           //   x(2:3) = x(1:2)
291c78b528fSSlava Zakharin           // We have to make a deep copy into 'newFrom' in this case.
292c78b528fSSlava Zakharin           RTNAME(AssignTemporary)
293c78b528fSSlava Zakharin           (newFrom, from, terminator.sourceFileName(), terminator.sourceLine());
294c78b528fSSlava Zakharin         } else {
295b21c24c3SPeter Klausler           ShallowCopy(newFrom, from, true, from.IsContiguous());
296c78b528fSSlava Zakharin         }
2978489f177SPeter Klausler         Assign(to, newFrom, terminator,
2988489f177SPeter Klausler             flags &
2998489f177SPeter Klausler                 (NeedFinalization | ComponentCanBeDefinedAssignment |
3006e4984a9SSlava Zakharin                     ExplicitLengthCharacterLHS | CanBeDefinedAssignment));
301755535b5SPeter Klausler         newFrom.Deallocate();
30265f52904Speter klausler       }
303755535b5SPeter Klausler       return;
30465f52904Speter klausler     }
30565f52904Speter klausler   }
306755535b5SPeter Klausler   if (to.IsAllocatable()) {
307755535b5SPeter Klausler     if (mustDeallocateLHS) {
308755535b5SPeter Klausler       if (deferDeallocation) {
3098489f177SPeter Klausler         if ((flags & NeedFinalization) && toDerived) {
31007e053fbSPeter Klausler           Finalize(*deferDeallocation, *toDerived, &terminator);
3118489f177SPeter Klausler           flags &= ~NeedFinalization;
31265f52904Speter klausler         }
313755535b5SPeter Klausler       } else {
314b21c24c3SPeter Klausler         to.Destroy((flags & NeedFinalization) != 0, /*destroyPointers=*/false,
315b21c24c3SPeter Klausler             &terminator);
3168489f177SPeter Klausler         flags &= ~NeedFinalization;
31765f52904Speter klausler       }
3188489f177SPeter Klausler     } else if (to.rank() != from.rank() && !to.IsAllocated()) {
31965f52904Speter klausler       terminator.Crash("Assign: mismatched ranks (%d != %d) in assignment to "
32065f52904Speter klausler                        "unallocated allocatable",
32165f52904Speter klausler           to.rank(), from.rank());
32265f52904Speter klausler     }
32365f52904Speter klausler     if (!to.IsAllocated()) {
3248489f177SPeter Klausler       if (AllocateAssignmentLHS(to, from, terminator, flags) != StatOk) {
325755535b5SPeter Klausler         return;
32665f52904Speter klausler       }
3278489f177SPeter Klausler       flags &= ~NeedFinalization;
3288489f177SPeter Klausler       toElementBytes = to.ElementBytes(); // may have changed
32965f52904Speter klausler     }
33065f52904Speter klausler   }
3316e4984a9SSlava Zakharin   if (toDerived && (flags & CanBeDefinedAssignment)) {
3326e4984a9SSlava Zakharin     // Check for a user-defined assignment type-bound procedure;
3336e4984a9SSlava Zakharin     // see 10.2.1.4-5.  A user-defined assignment TBP defines all of
3346e4984a9SSlava Zakharin     // the semantics, including allocatable (re)allocation and any
3356e4984a9SSlava Zakharin     // finalization.
3366e4984a9SSlava Zakharin     //
3376e4984a9SSlava Zakharin     // Note that the aliasing and LHS (re)allocation handling above
3386e4984a9SSlava Zakharin     // needs to run even with CanBeDefinedAssignment flag, when
3396e4984a9SSlava Zakharin     // the Assign() is invoked recursively for component-per-component
3406e4984a9SSlava Zakharin     // assignments.
3416e4984a9SSlava Zakharin     if (to.rank() == 0) {
3426e4984a9SSlava Zakharin       if (const auto *special{toDerived->FindSpecialBinding(
3436e4984a9SSlava Zakharin               typeInfo::SpecialBinding::Which::ScalarAssignment)}) {
3446e4984a9SSlava Zakharin         return DoScalarDefinedAssignment(to, from, *special);
3456e4984a9SSlava Zakharin       }
3466e4984a9SSlava Zakharin     }
3476e4984a9SSlava Zakharin     if (const auto *special{toDerived->FindSpecialBinding(
3486e4984a9SSlava Zakharin             typeInfo::SpecialBinding::Which::ElementalAssignment)}) {
3496e4984a9SSlava Zakharin       return DoElementalDefinedAssignment(to, from, *toDerived, *special);
3506e4984a9SSlava Zakharin     }
3516e4984a9SSlava Zakharin   }
35265f52904Speter klausler   SubscriptValue toAt[maxRank];
35365f52904Speter klausler   to.GetLowerBounds(toAt);
35465f52904Speter klausler   // Scalar expansion of the RHS is implied by using the same empty
35565f52904Speter klausler   // subscript values on each (seemingly) elemental reference into
35665f52904Speter klausler   // "from".
35765f52904Speter klausler   SubscriptValue fromAt[maxRank];
35865f52904Speter klausler   from.GetLowerBounds(fromAt);
35965f52904Speter klausler   std::size_t toElements{to.Elements()};
36065f52904Speter klausler   if (from.rank() > 0 && toElements != from.Elements()) {
36165f52904Speter klausler     terminator.Crash("Assign: mismatching element counts in array assignment "
36265f52904Speter klausler                      "(to %zd, from %zd)",
36365f52904Speter klausler         toElements, from.Elements());
36465f52904Speter klausler   }
36565f52904Speter klausler   if (to.type() != from.type()) {
36665f52904Speter klausler     terminator.Crash("Assign: mismatching types (to code %d != from code %d)",
36765f52904Speter klausler         to.type().raw(), from.type().raw());
36865f52904Speter klausler   }
3698489f177SPeter Klausler   if (toElementBytes > fromElementBytes && !to.type().IsCharacter()) {
3708489f177SPeter Klausler     terminator.Crash("Assign: mismatching non-character element sizes (to %zd "
3718489f177SPeter Klausler                      "bytes != from %zd bytes)",
3728489f177SPeter Klausler         toElementBytes, fromElementBytes);
37365f52904Speter klausler   }
3745c988cbaSValentin Clement   if (const typeInfo::DerivedType *
3755c988cbaSValentin Clement       updatedToDerived{toAddendum ? toAddendum->derivedType() : nullptr}) {
37665f52904Speter klausler     // Derived type intrinsic assignment, which is componentwise and elementwise
37765f52904Speter klausler     // for all components, including parent components (10.2.1.2-3).
37865f52904Speter klausler     // The target is first finalized if still necessary (7.5.6.3(1))
3798489f177SPeter Klausler     if (flags & NeedFinalization) {
380b21c24c3SPeter Klausler       Finalize(to, *updatedToDerived, &terminator);
3815f6e0f35SjeanPerier     } else if (updatedToDerived && !updatedToDerived->noDestructionNeeded()) {
3825f6e0f35SjeanPerier       Destroy(to, /*finalize=*/false, *updatedToDerived, &terminator);
38365f52904Speter klausler     }
38465f52904Speter klausler     // Copy the data components (incl. the parent) first.
3855c988cbaSValentin Clement     const Descriptor &componentDesc{updatedToDerived->component()};
38665f52904Speter klausler     std::size_t numComponents{componentDesc.Elements()};
387887783e0SPeter Klausler     for (std::size_t j{0}; j < toElements;
388887783e0SPeter Klausler          ++j, to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
38965f52904Speter klausler       for (std::size_t k{0}; k < numComponents; ++k) {
39065f52904Speter klausler         const auto &comp{
39165f52904Speter klausler             *componentDesc.ZeroBasedIndexedElement<typeInfo::Component>(
39265f52904Speter klausler                 k)}; // TODO: exploit contiguity here
3938489f177SPeter Klausler         // Use PolymorphicLHS for components so that the right things happen
3948489f177SPeter Klausler         // when the components are polymorphic; when they're not, they're both
3958489f177SPeter Klausler         // not, and their declared types will match.
3968489f177SPeter Klausler         int nestedFlags{MaybeReallocate | PolymorphicLHS};
3973acdd596SValentin Clement         if (flags & ComponentCanBeDefinedAssignment) {
398887783e0SPeter Klausler           nestedFlags |=
399887783e0SPeter Klausler               CanBeDefinedAssignment | ComponentCanBeDefinedAssignment;
4008489f177SPeter Klausler         }
40165f52904Speter klausler         switch (comp.genre()) {
40265f52904Speter klausler         case typeInfo::Component::Genre::Data:
40365f52904Speter klausler           if (comp.category() == TypeCategory::Derived) {
40465f52904Speter klausler             StaticDescriptor<maxRank, true, 10 /*?*/> statDesc[2];
40565f52904Speter klausler             Descriptor &toCompDesc{statDesc[0].descriptor()};
40665f52904Speter klausler             Descriptor &fromCompDesc{statDesc[1].descriptor()};
407050f785eSPhilip Reames             comp.CreatePointerDescriptor(toCompDesc, to, terminator, toAt);
408050f785eSPhilip Reames             comp.CreatePointerDescriptor(
409050f785eSPhilip Reames                 fromCompDesc, from, terminator, fromAt);
4108489f177SPeter Klausler             Assign(toCompDesc, fromCompDesc, terminator, nestedFlags);
41165f52904Speter klausler           } else { // Component has intrinsic type; simply copy raw bytes
41265f52904Speter klausler             std::size_t componentByteSize{comp.SizeInBytes(to)};
4137792dbe2SValentin Clement (バレンタイン クレメン)             memmoveFct(to.Element<char>(toAt) + comp.offset(),
41465f52904Speter klausler                 from.Element<const char>(fromAt) + comp.offset(),
41565f52904Speter klausler                 componentByteSize);
41665f52904Speter klausler           }
41765f52904Speter klausler           break;
41865f52904Speter klausler         case typeInfo::Component::Genre::Pointer: {
41965f52904Speter klausler           std::size_t componentByteSize{comp.SizeInBytes(to)};
4207792dbe2SValentin Clement (バレンタイン クレメン)           memmoveFct(to.Element<char>(toAt) + comp.offset(),
42165f52904Speter klausler               from.Element<const char>(fromAt) + comp.offset(),
42265f52904Speter klausler               componentByteSize);
42365f52904Speter klausler         } break;
42465f52904Speter klausler         case typeInfo::Component::Genre::Allocatable:
425887783e0SPeter Klausler         case typeInfo::Component::Genre::Automatic: {
42665f52904Speter klausler           auto *toDesc{reinterpret_cast<Descriptor *>(
42765f52904Speter klausler               to.Element<char>(toAt) + comp.offset())};
42865f52904Speter klausler           const auto *fromDesc{reinterpret_cast<const Descriptor *>(
42965f52904Speter klausler               from.Element<char>(fromAt) + comp.offset())};
43065f52904Speter klausler           // Allocatable components of the LHS are unconditionally
43165f52904Speter klausler           // deallocated before assignment (F'2018 10.2.1.3(13)(1)),
43265f52904Speter klausler           // unlike a "top-level" assignment to a variable, where
43365f52904Speter klausler           // deallocation is optional.
4346e4984a9SSlava Zakharin           //
4356e4984a9SSlava Zakharin           // Be careful not to destroy/reallocate the LHS, if there is
4366e4984a9SSlava Zakharin           // overlap between LHS and RHS (it seems that partial overlap
4376e4984a9SSlava Zakharin           // is not possible, though).
4386e4984a9SSlava Zakharin           // Invoke Assign() recursively to deal with potential aliasing.
4396e4984a9SSlava Zakharin           if (toDesc->IsAllocatable()) {
44065f52904Speter klausler             if (!fromDesc->IsAllocated()) {
4416e4984a9SSlava Zakharin               // No aliasing.
4426e4984a9SSlava Zakharin               //
4436e4984a9SSlava Zakharin               // If to is not allocated, the Destroy() call is a no-op.
4446e4984a9SSlava Zakharin               // This is just a shortcut, because the recursive Assign()
4456e4984a9SSlava Zakharin               // below would initiate the destruction for to.
4466e4984a9SSlava Zakharin               // No finalization is required.
447b21c24c3SPeter Klausler               toDesc->Destroy(
448b21c24c3SPeter Klausler                   /*finalize=*/false, /*destroyPointers=*/false, &terminator);
44965f52904Speter klausler               continue; // F'2018 10.2.1.3(13)(2)
45065f52904Speter klausler             }
4513acdd596SValentin Clement           }
4526e4984a9SSlava Zakharin           // Force LHS deallocation with DeallocateLHS flag.
4536e4984a9SSlava Zakharin           // The actual deallocation may be avoided, if the existing
4546e4984a9SSlava Zakharin           // location can be reoccupied.
4556e4984a9SSlava Zakharin           Assign(*toDesc, *fromDesc, terminator, nestedFlags | DeallocateLHS);
456887783e0SPeter Klausler         } break;
45765f52904Speter klausler         }
45865f52904Speter klausler       }
45965f52904Speter klausler       // Copy procedure pointer components
4605c988cbaSValentin Clement       const Descriptor &procPtrDesc{updatedToDerived->procPtr()};
46165f52904Speter klausler       std::size_t numProcPtrs{procPtrDesc.Elements()};
46265f52904Speter klausler       for (std::size_t k{0}; k < numProcPtrs; ++k) {
46365f52904Speter klausler         const auto &procPtr{
464887783e0SPeter Klausler             *procPtrDesc.ZeroBasedIndexedElement<typeInfo::ProcPtrComponent>(
465887783e0SPeter Klausler                 k)};
4667792dbe2SValentin Clement (バレンタイン クレメン)         memmoveFct(to.Element<char>(toAt) + procPtr.offset,
46765f52904Speter klausler             from.Element<const char>(fromAt) + procPtr.offset,
46865f52904Speter klausler             sizeof(typeInfo::ProcedurePointer));
46965f52904Speter klausler       }
47065f52904Speter klausler     }
47165f52904Speter klausler   } else { // intrinsic type, intrinsic assignment
4728489f177SPeter Klausler     if (isSimpleMemmove()) {
4737792dbe2SValentin Clement (バレンタイン クレメン)       memmoveFct(to.raw().base_addr, from.raw().base_addr,
4748489f177SPeter Klausler           toElements * toElementBytes);
4758489f177SPeter Klausler     } else if (toElementBytes > fromElementBytes) { // blank padding
4768489f177SPeter Klausler       switch (to.type().raw()) {
4778489f177SPeter Klausler       case CFI_type_signed_char:
4788489f177SPeter Klausler       case CFI_type_char:
4798489f177SPeter Klausler         BlankPadCharacterAssignment<char>(to, from, toAt, fromAt, toElements,
4808489f177SPeter Klausler             toElementBytes, fromElementBytes);
4818489f177SPeter Klausler         break;
4828489f177SPeter Klausler       case CFI_type_char16_t:
4838489f177SPeter Klausler         BlankPadCharacterAssignment<char16_t>(to, from, toAt, fromAt,
4848489f177SPeter Klausler             toElements, toElementBytes, fromElementBytes);
4858489f177SPeter Klausler         break;
4868489f177SPeter Klausler       case CFI_type_char32_t:
4878489f177SPeter Klausler         BlankPadCharacterAssignment<char32_t>(to, from, toAt, fromAt,
4888489f177SPeter Klausler             toElements, toElementBytes, fromElementBytes);
4898489f177SPeter Klausler         break;
4908489f177SPeter Klausler       default:
4918489f177SPeter Klausler         terminator.Crash("unexpected type code %d in blank padded Assign()",
4928489f177SPeter Klausler             to.type().raw());
4938489f177SPeter Klausler       }
4948489f177SPeter Klausler     } else { // elemental copies, possibly with character truncation
49565f52904Speter klausler       for (std::size_t n{toElements}; n-- > 0;
49665f52904Speter klausler            to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
4977792dbe2SValentin Clement (バレンタイン クレメン)         memmoveFct(to.Element<char>(toAt), from.Element<const char>(fromAt),
4987792dbe2SValentin Clement (バレンタイン クレメン)             toElementBytes);
49965f52904Speter klausler       }
50065f52904Speter klausler     }
50165f52904Speter klausler   }
502755535b5SPeter Klausler   if (deferDeallocation) {
5036e4984a9SSlava Zakharin     // deferDeallocation is used only when LHS is an allocatable.
5046e4984a9SSlava Zakharin     // The finalization has already been run for it.
505b21c24c3SPeter Klausler     deferDeallocation->Destroy(
506b21c24c3SPeter Klausler         /*finalize=*/false, /*destroyPointers=*/false, &terminator);
507755535b5SPeter Klausler   }
50865f52904Speter klausler }
50965f52904Speter klausler 
5108b953fddSSlava Zakharin RT_OFFLOAD_API_GROUP_BEGIN
5118b953fddSSlava Zakharin 
512*42be165dSValentin Clement RT_API_ATTRS void DoFromSourceAssign(Descriptor &alloc,
513*42be165dSValentin Clement     const Descriptor &source, Terminator &terminator, MemmoveFct memmoveFct) {
514f783c9bbSPeixin Qiao   if (alloc.rank() > 0 && source.rank() == 0) {
515f783c9bbSPeixin Qiao     // The value of each element of allocate object becomes the value of source.
516f783c9bbSPeixin Qiao     DescriptorAddendum *allocAddendum{alloc.Addendum()};
517f783c9bbSPeixin Qiao     const typeInfo::DerivedType *allocDerived{
518f783c9bbSPeixin Qiao         allocAddendum ? allocAddendum->derivedType() : nullptr};
519f783c9bbSPeixin Qiao     SubscriptValue allocAt[maxRank];
520f783c9bbSPeixin Qiao     alloc.GetLowerBounds(allocAt);
521f783c9bbSPeixin Qiao     if (allocDerived) {
522f783c9bbSPeixin Qiao       for (std::size_t n{alloc.Elements()}; n-- > 0;
523f783c9bbSPeixin Qiao            alloc.IncrementSubscripts(allocAt)) {
524f783c9bbSPeixin Qiao         Descriptor allocElement{*Descriptor::Create(*allocDerived,
525f783c9bbSPeixin Qiao             reinterpret_cast<void *>(alloc.Element<char>(allocAt)), 0)};
526*42be165dSValentin Clement         Assign(allocElement, source, terminator, NoAssignFlags, memmoveFct);
527f783c9bbSPeixin Qiao       }
528f783c9bbSPeixin Qiao     } else { // intrinsic type
529f783c9bbSPeixin Qiao       for (std::size_t n{alloc.Elements()}; n-- > 0;
530f783c9bbSPeixin Qiao            alloc.IncrementSubscripts(allocAt)) {
531*42be165dSValentin Clement         memmoveFct(alloc.Element<char>(allocAt), source.raw().base_addr,
532*42be165dSValentin Clement             alloc.ElementBytes());
533f783c9bbSPeixin Qiao       }
534f783c9bbSPeixin Qiao     }
535f783c9bbSPeixin Qiao   } else {
536*42be165dSValentin Clement     Assign(alloc, source, terminator, NoAssignFlags, memmoveFct);
537f783c9bbSPeixin Qiao   }
538f783c9bbSPeixin Qiao }
539f783c9bbSPeixin Qiao 
5408b953fddSSlava Zakharin RT_OFFLOAD_API_GROUP_END
5418b953fddSSlava Zakharin 
54265f52904Speter klausler extern "C" {
5438b953fddSSlava Zakharin RT_EXT_API_GROUP_BEGIN
5448b953fddSSlava Zakharin 
5458b953fddSSlava Zakharin void RTDEF(Assign)(Descriptor &to, const Descriptor &from,
54665f52904Speter klausler     const char *sourceFile, int sourceLine) {
54765f52904Speter klausler   Terminator terminator{sourceFile, sourceLine};
548755535b5SPeter Klausler   // All top-level defined assignments can be recognized in semantics and
549755535b5SPeter Klausler   // will have been already been converted to calls, so don't check for
550755535b5SPeter Klausler   // defined assignment apart from components.
5518489f177SPeter Klausler   Assign(to, from, terminator,
5528489f177SPeter Klausler       MaybeReallocate | NeedFinalization | ComponentCanBeDefinedAssignment);
553755535b5SPeter Klausler }
554755535b5SPeter Klausler 
5558b953fddSSlava Zakharin void RTDEF(AssignTemporary)(Descriptor &to, const Descriptor &from,
556755535b5SPeter Klausler     const char *sourceFile, int sourceLine) {
557755535b5SPeter Klausler   Terminator terminator{sourceFile, sourceLine};
5581aff61ecSSlava Zakharin   // Initialize the "to" if it is of derived type that needs initialization.
5591aff61ecSSlava Zakharin   if (const DescriptorAddendum * addendum{to.Addendum()}) {
5601aff61ecSSlava Zakharin     if (const auto *derived{addendum->derivedType()}) {
5613a4e9f7aSSlava Zakharin       // Do not invoke the initialization, if the descriptor is unallocated.
5623a4e9f7aSSlava Zakharin       // AssignTemporary() is used for component-by-component assignments,
5633a4e9f7aSSlava Zakharin       // for example, for structure constructors. This means that the LHS
5643a4e9f7aSSlava Zakharin       // may be an allocatable component with unallocated status.
5653a4e9f7aSSlava Zakharin       // The initialization will just fail in this case. By skipping
5663a4e9f7aSSlava Zakharin       // the initialization we let Assign() automatically allocate
5673a4e9f7aSSlava Zakharin       // and initialize the component according to the RHS.
5683a4e9f7aSSlava Zakharin       // So we only need to initialize the LHS here if it is allocated.
5693a4e9f7aSSlava Zakharin       // Note that initializing already initialized entity has no visible
5703a4e9f7aSSlava Zakharin       // effect, though, it is assumed that the compiler does not initialize
5713a4e9f7aSSlava Zakharin       // the temporary and leaves the initialization to this runtime code.
5723a4e9f7aSSlava Zakharin       if (!derived->noInitializationNeeded() && to.IsAllocated()) {
5731aff61ecSSlava Zakharin         if (ReturnError(terminator, Initialize(to, *derived, terminator)) !=
5741aff61ecSSlava Zakharin             StatOk) {
5751aff61ecSSlava Zakharin           return;
5761aff61ecSSlava Zakharin         }
5771aff61ecSSlava Zakharin       }
5781aff61ecSSlava Zakharin     }
5791aff61ecSSlava Zakharin   }
580da60b9e7SSlava Zakharin 
581797f0119SLeandro Lupori   Assign(to, from, terminator, MaybeReallocate | PolymorphicLHS);
58265f52904Speter klausler }
58365f52904Speter klausler 
5849f44d5d9SjeanPerier void RTDEF(CopyInAssign)(Descriptor &temp, const Descriptor &var,
5859f44d5d9SjeanPerier     const char *sourceFile, int sourceLine) {
586da60b9e7SSlava Zakharin   Terminator terminator{sourceFile, sourceLine};
5879f44d5d9SjeanPerier   temp = var;
5889f44d5d9SjeanPerier   temp.set_base_addr(nullptr);
5899f44d5d9SjeanPerier   temp.raw().attribute = CFI_attribute_allocatable;
5909f44d5d9SjeanPerier   RTNAME(AssignTemporary)(temp, var, sourceFile, sourceLine);
591da60b9e7SSlava Zakharin }
592da60b9e7SSlava Zakharin 
5939f44d5d9SjeanPerier void RTDEF(CopyOutAssign)(
5949f44d5d9SjeanPerier     Descriptor *var, Descriptor &temp, const char *sourceFile, int sourceLine) {
5959f44d5d9SjeanPerier   Terminator terminator{sourceFile, sourceLine};
5969f44d5d9SjeanPerier 
597da60b9e7SSlava Zakharin   // Copyout from the temporary must not cause any finalizations
5989f44d5d9SjeanPerier   // for LHS. The variable must be properly initialized already.
5999f44d5d9SjeanPerier   if (var)
6009f44d5d9SjeanPerier     Assign(*var, temp, terminator, NoAssignFlags);
6019f44d5d9SjeanPerier   temp.Destroy(/*finalize=*/false, /*destroyPointers=*/false, &terminator);
602da60b9e7SSlava Zakharin }
603da60b9e7SSlava Zakharin 
6048b953fddSSlava Zakharin void RTDEF(AssignExplicitLengthCharacter)(Descriptor &to,
6058489f177SPeter Klausler     const Descriptor &from, const char *sourceFile, int sourceLine) {
6068489f177SPeter Klausler   Terminator terminator{sourceFile, sourceLine};
6078489f177SPeter Klausler   Assign(to, from, terminator,
6088489f177SPeter Klausler       MaybeReallocate | NeedFinalization | ComponentCanBeDefinedAssignment |
6098489f177SPeter Klausler           ExplicitLengthCharacterLHS);
6108489f177SPeter Klausler }
6118489f177SPeter Klausler 
6128b953fddSSlava Zakharin void RTDEF(AssignPolymorphic)(Descriptor &to, const Descriptor &from,
6138489f177SPeter Klausler     const char *sourceFile, int sourceLine) {
6148489f177SPeter Klausler   Terminator terminator{sourceFile, sourceLine};
6158489f177SPeter Klausler   Assign(to, from, terminator,
6168489f177SPeter Klausler       MaybeReallocate | NeedFinalization | ComponentCanBeDefinedAssignment |
6178489f177SPeter Klausler           PolymorphicLHS);
6188489f177SPeter Klausler }
6198b953fddSSlava Zakharin 
6208b953fddSSlava Zakharin RT_EXT_API_GROUP_END
62165f52904Speter klausler } // extern "C"
62265f52904Speter klausler } // namespace Fortran::runtime
623