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