1 //===-- runtime/assign.cpp ------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "flang/Runtime/assign.h"
10 #include "assign-impl.h"
13 #include "terminator.h"
15 #include "type-info.h"
16 #include "flang/Runtime/descriptor.h"
18 namespace Fortran::runtime
{
22 MaybeReallocate
= 1 << 0,
23 NeedFinalization
= 1 << 1,
24 CanBeDefinedAssignment
= 1 << 2,
25 ComponentCanBeDefinedAssignment
= 1 << 3,
26 ExplicitLengthCharacterLHS
= 1 << 4,
27 PolymorphicLHS
= 1 << 5,
28 DeallocateLHS
= 1 << 6
31 // Predicate: is the left-hand side of an assignment an allocated allocatable
32 // that must be deallocated?
33 static inline RT_API_ATTRS
bool MustDeallocateLHS(
34 Descriptor
&to
, const Descriptor
&from
, Terminator
&terminator
, int flags
) {
35 // Top-level assignments to allocatable variables (*not* components)
36 // may first deallocate existing content if there's about to be a
37 // change in type or shape; see F'2018 10.2.1.3(3).
38 if (!(flags
& MaybeReallocate
)) {
41 if (!to
.IsAllocatable() || !to
.IsAllocated()) {
44 if (to
.type() != from
.type()) {
47 if (!(flags
& ExplicitLengthCharacterLHS
) && to
.type().IsCharacter() &&
48 to
.ElementBytes() != from
.ElementBytes()) {
51 if (flags
& PolymorphicLHS
) {
52 DescriptorAddendum
*toAddendum
{to
.Addendum()};
53 const typeInfo::DerivedType
*toDerived
{
54 toAddendum
? toAddendum
->derivedType() : nullptr};
55 const DescriptorAddendum
*fromAddendum
{from
.Addendum()};
56 const typeInfo::DerivedType
*fromDerived
{
57 fromAddendum
? fromAddendum
->derivedType() : nullptr};
58 if (toDerived
!= fromDerived
) {
62 // Distinct LEN parameters? Deallocate
63 std::size_t lenParms
{fromDerived
->LenParameters()};
64 for (std::size_t j
{0}; j
< lenParms
; ++j
) {
65 if (toAddendum
->LenParameterValue(j
) !=
66 fromAddendum
->LenParameterValue(j
)) {
72 if (from
.rank() > 0) {
73 // Distinct shape? Deallocate
75 for (int j
{0}; j
< rank
; ++j
) {
76 if (to
.GetDimension(j
).Extent() != from
.GetDimension(j
).Extent()) {
84 // Utility: allocate the allocatable left-hand side, either because it was
85 // originally deallocated or because it required reallocation
86 static RT_API_ATTRS
int AllocateAssignmentLHS(
87 Descriptor
&to
, const Descriptor
&from
, Terminator
&terminator
, int flags
) {
88 to
.raw().type
= from
.raw().type
;
89 if (!(flags
& ExplicitLengthCharacterLHS
)) {
90 to
.raw().elem_len
= from
.ElementBytes();
92 const typeInfo::DerivedType
*derived
{nullptr};
93 if (const DescriptorAddendum
* fromAddendum
{from
.Addendum()}) {
94 derived
= fromAddendum
->derivedType();
95 if (DescriptorAddendum
* toAddendum
{to
.Addendum()}) {
96 toAddendum
->set_derivedType(derived
);
97 std::size_t lenParms
{derived
? derived
->LenParameters() : 0};
98 for (std::size_t j
{0}; j
< lenParms
; ++j
) {
99 toAddendum
->SetLenParameterValue(j
, fromAddendum
->LenParameterValue(j
));
103 // subtle: leave bounds in place when "from" is scalar (10.2.1.3(3))
104 int rank
{from
.rank()};
105 auto stride
{static_cast<SubscriptValue
>(to
.ElementBytes())};
106 for (int j
{0}; j
< rank
; ++j
) {
107 auto &toDim
{to
.GetDimension(j
)};
108 const auto &fromDim
{from
.GetDimension(j
)};
109 toDim
.SetBounds(fromDim
.LowerBound(), fromDim
.UpperBound());
110 toDim
.SetByteStride(stride
);
111 stride
*= toDim
.Extent();
113 int result
{ReturnError(terminator
, to
.Allocate())};
114 if (result
== StatOk
&& derived
&& !derived
->noInitializationNeeded()) {
115 result
= ReturnError(terminator
, Initialize(to
, *derived
, terminator
));
120 // least <= 0, most >= 0
121 static RT_API_ATTRS
void MaximalByteOffsetRange(
122 const Descriptor
&desc
, std::int64_t &least
, std::int64_t &most
) {
124 if (desc
.ElementBytes() == 0) {
127 int n
{desc
.raw().rank
};
128 for (int j
{0}; j
< n
; ++j
) {
129 const auto &dim
{desc
.GetDimension(j
)};
130 auto extent
{dim
.Extent()};
132 auto sm
{dim
.ByteStride()};
134 least
+= (extent
- 1) * sm
;
136 most
+= (extent
- 1) * sm
;
140 most
+= desc
.ElementBytes() - 1;
143 static inline RT_API_ATTRS
bool RangesOverlap(const char *aStart
,
144 const char *aEnd
, const char *bStart
, const char *bEnd
) {
145 return aEnd
>= bStart
&& bEnd
>= aStart
;
148 // Predicate: could the left-hand and right-hand sides of the assignment
149 // possibly overlap in memory? Note that the descriptors themeselves
150 // are included in the test.
151 static RT_API_ATTRS
bool MayAlias(const Descriptor
&x
, const Descriptor
&y
) {
152 const char *xBase
{x
.OffsetElement()};
153 const char *yBase
{y
.OffsetElement()};
154 if (!xBase
|| !yBase
) {
155 return false; // not both allocated
157 const char *xDesc
{reinterpret_cast<const char *>(&x
)};
158 const char *xDescLast
{xDesc
+ x
.SizeInBytes()};
159 const char *yDesc
{reinterpret_cast<const char *>(&y
)};
160 const char *yDescLast
{yDesc
+ y
.SizeInBytes()};
161 std::int64_t xLeast
, xMost
, yLeast
, yMost
;
162 MaximalByteOffsetRange(x
, xLeast
, xMost
);
163 MaximalByteOffsetRange(y
, yLeast
, yMost
);
164 if (RangesOverlap(xDesc
, xDescLast
, yBase
+ yLeast
, yBase
+ yMost
) ||
165 RangesOverlap(yDesc
, yDescLast
, xBase
+ xLeast
, xBase
+ xMost
)) {
166 // A descriptor overlaps with the storage described by the other;
167 // this can arise when an allocatable or pointer component is
168 // being assigned to/from.
172 xBase
+ xLeast
, xBase
+ xMost
, yBase
+ yLeast
, yBase
+ yMost
)) {
173 return false; // no storage overlap
175 // TODO: check dimensions: if any is independent, return false
179 static RT_API_ATTRS
void DoScalarDefinedAssignment(const Descriptor
&to
,
180 const Descriptor
&from
, const typeInfo::SpecialBinding
&special
) {
181 bool toIsDesc
{special
.IsArgDescriptor(0)};
182 bool fromIsDesc
{special
.IsArgDescriptor(1)};
186 special
.GetProc
<void (*)(const Descriptor
&, const Descriptor
&)>()};
189 auto *p
{special
.GetProc
<void (*)(const Descriptor
&, void *)>()};
190 p(to
, from
.raw().base_addr
);
194 auto *p
{special
.GetProc
<void (*)(void *, const Descriptor
&)>()};
195 p(to
.raw().base_addr
, from
);
197 auto *p
{special
.GetProc
<void (*)(void *, void *)>()};
198 p(to
.raw().base_addr
, from
.raw().base_addr
);
203 static RT_API_ATTRS
void DoElementalDefinedAssignment(const Descriptor
&to
,
204 const Descriptor
&from
, const typeInfo::DerivedType
&derived
,
205 const typeInfo::SpecialBinding
&special
) {
206 SubscriptValue toAt
[maxRank
], fromAt
[maxRank
];
207 to
.GetLowerBounds(toAt
);
208 from
.GetLowerBounds(fromAt
);
209 StaticDescriptor
<maxRank
, true, 8 /*?*/> statDesc
[2];
210 Descriptor
&toElementDesc
{statDesc
[0].descriptor()};
211 Descriptor
&fromElementDesc
{statDesc
[1].descriptor()};
212 toElementDesc
.Establish(derived
, nullptr, 0, nullptr, CFI_attribute_pointer
);
213 fromElementDesc
.Establish(
214 derived
, nullptr, 0, nullptr, CFI_attribute_pointer
);
215 for (std::size_t toElements
{to
.Elements()}; toElements
-- > 0;
216 to
.IncrementSubscripts(toAt
), from
.IncrementSubscripts(fromAt
)) {
217 toElementDesc
.set_base_addr(to
.Element
<char>(toAt
));
218 fromElementDesc
.set_base_addr(from
.Element
<char>(fromAt
));
219 DoScalarDefinedAssignment(toElementDesc
, fromElementDesc
, special
);
223 template <typename CHAR
>
224 static RT_API_ATTRS
void BlankPadCharacterAssignment(Descriptor
&to
,
225 const Descriptor
&from
, SubscriptValue toAt
[], SubscriptValue fromAt
[],
226 std::size_t elements
, std::size_t toElementBytes
,
227 std::size_t fromElementBytes
) {
228 std::size_t padding
{(toElementBytes
- fromElementBytes
) / sizeof(CHAR
)};
229 std::size_t copiedCharacters
{fromElementBytes
/ sizeof(CHAR
)};
230 for (; elements
-- > 0;
231 to
.IncrementSubscripts(toAt
), from
.IncrementSubscripts(fromAt
)) {
232 CHAR
*p
{to
.Element
<CHAR
>(toAt
)};
233 Fortran::runtime::memmove(
234 p
, from
.Element
<std::add_const_t
<CHAR
>>(fromAt
), fromElementBytes
);
235 p
+= copiedCharacters
;
236 for (auto n
{padding
}; n
-- > 0;) {
242 // Common implementation of assignments, both intrinsic assignments and
243 // those cases of polymorphic user-defined ASSIGNMENT(=) TBPs that could not
244 // be resolved in semantics. Most assignment statements do not need any
245 // of the capabilities of this function -- but when the LHS is allocatable,
246 // the type might have a user-defined ASSIGNMENT(=), or the type might be
247 // finalizable, this function should be used.
248 // When "to" is not a whole allocatable, "from" is an array, and defined
249 // assignments are not used, "to" and "from" only need to have the same number
250 // of elements, but their shape need not to conform (the assignment is done in
251 // element sequence order). This facilitates some internal usages, like when
252 // dealing with array constructors.
253 RT_API_ATTRS
static void Assign(
254 Descriptor
&to
, const Descriptor
&from
, Terminator
&terminator
, int flags
) {
255 bool mustDeallocateLHS
{(flags
& DeallocateLHS
) ||
256 MustDeallocateLHS(to
, from
, terminator
, flags
)};
257 DescriptorAddendum
*toAddendum
{to
.Addendum()};
258 const typeInfo::DerivedType
*toDerived
{
259 toAddendum
? toAddendum
->derivedType() : nullptr};
260 if (toDerived
&& (flags
& NeedFinalization
) &&
261 toDerived
->noFinalizationNeeded()) {
262 flags
&= ~NeedFinalization
;
264 std::size_t toElementBytes
{to
.ElementBytes()};
265 std::size_t fromElementBytes
{from
.ElementBytes()};
266 // The following lambda definition violates the conding style,
267 // but cuda-11.8 nvcc hits an internal error with the brace initialization.
268 auto isSimpleMemmove
= [&]() {
269 return !toDerived
&& to
.rank() == from
.rank() && to
.IsContiguous() &&
270 from
.IsContiguous() && toElementBytes
== fromElementBytes
;
272 StaticDescriptor
<maxRank
, true, 10 /*?*/> deferredDeallocStatDesc
;
273 Descriptor
*deferDeallocation
{nullptr};
274 if (MayAlias(to
, from
)) {
275 if (mustDeallocateLHS
) {
276 deferDeallocation
= &deferredDeallocStatDesc
.descriptor();
277 std::memcpy(deferDeallocation
, &to
, to
.SizeInBytes());
278 to
.set_base_addr(nullptr);
279 } else if (!isSimpleMemmove()) {
280 // Handle LHS/RHS aliasing by copying RHS into a temp, then
281 // recursively assigning from that temp.
282 auto descBytes
{from
.SizeInBytes()};
283 StaticDescriptor
<maxRank
, true, 16> staticDesc
;
284 Descriptor
&newFrom
{staticDesc
.descriptor()};
285 std::memcpy(&newFrom
, &from
, descBytes
);
286 // Pretend the temporary descriptor is for an ALLOCATABLE
287 // entity, otherwise, the Deallocate() below will not
288 // free the descriptor memory.
289 newFrom
.raw().attribute
= CFI_attribute_allocatable
;
290 auto stat
{ReturnError(terminator
, newFrom
.Allocate())};
291 if (stat
== StatOk
) {
292 if (HasDynamicComponent(from
)) {
293 // If 'from' has allocatable/automatic component, we cannot
294 // just make a shallow copy of the descriptor member.
295 // This will still leave data overlap in 'to' and 'newFrom'.
298 // character, allocatable :: c(:)
302 // We have to make a deep copy into 'newFrom' in this case.
303 RTNAME(AssignTemporary
)
304 (newFrom
, from
, terminator
.sourceFileName(), terminator
.sourceLine());
306 ShallowCopy(newFrom
, from
, true, from
.IsContiguous());
308 Assign(to
, newFrom
, terminator
,
310 (NeedFinalization
| ComponentCanBeDefinedAssignment
|
311 ExplicitLengthCharacterLHS
| CanBeDefinedAssignment
));
312 newFrom
.Deallocate();
317 if (to
.IsAllocatable()) {
318 if (mustDeallocateLHS
) {
319 if (deferDeallocation
) {
320 if ((flags
& NeedFinalization
) && toDerived
) {
321 Finalize(to
, *toDerived
, &terminator
);
322 flags
&= ~NeedFinalization
;
323 } else if (toDerived
&& !toDerived
->noDestructionNeeded()) {
324 Destroy(to
, /*finalize=*/false, *toDerived
, &terminator
);
327 to
.Destroy((flags
& NeedFinalization
) != 0, /*destroyPointers=*/false,
329 flags
&= ~NeedFinalization
;
331 } else if (to
.rank() != from
.rank() && !to
.IsAllocated()) {
332 terminator
.Crash("Assign: mismatched ranks (%d != %d) in assignment to "
333 "unallocated allocatable",
334 to
.rank(), from
.rank());
336 if (!to
.IsAllocated()) {
337 if (AllocateAssignmentLHS(to
, from
, terminator
, flags
) != StatOk
) {
340 flags
&= ~NeedFinalization
;
341 toElementBytes
= to
.ElementBytes(); // may have changed
344 if (toDerived
&& (flags
& CanBeDefinedAssignment
)) {
345 // Check for a user-defined assignment type-bound procedure;
346 // see 10.2.1.4-5. A user-defined assignment TBP defines all of
347 // the semantics, including allocatable (re)allocation and any
350 // Note that the aliasing and LHS (re)allocation handling above
351 // needs to run even with CanBeDefinedAssignment flag, when
352 // the Assign() is invoked recursively for component-per-component
354 if (to
.rank() == 0) {
355 if (const auto *special
{toDerived
->FindSpecialBinding(
356 typeInfo::SpecialBinding::Which::ScalarAssignment
)}) {
357 return DoScalarDefinedAssignment(to
, from
, *special
);
360 if (const auto *special
{toDerived
->FindSpecialBinding(
361 typeInfo::SpecialBinding::Which::ElementalAssignment
)}) {
362 return DoElementalDefinedAssignment(to
, from
, *toDerived
, *special
);
365 SubscriptValue toAt
[maxRank
];
366 to
.GetLowerBounds(toAt
);
367 // Scalar expansion of the RHS is implied by using the same empty
368 // subscript values on each (seemingly) elemental reference into
370 SubscriptValue fromAt
[maxRank
];
371 from
.GetLowerBounds(fromAt
);
372 std::size_t toElements
{to
.Elements()};
373 if (from
.rank() > 0 && toElements
!= from
.Elements()) {
374 terminator
.Crash("Assign: mismatching element counts in array assignment "
375 "(to %zd, from %zd)",
376 toElements
, from
.Elements());
378 if (to
.type() != from
.type()) {
379 terminator
.Crash("Assign: mismatching types (to code %d != from code %d)",
380 to
.type().raw(), from
.type().raw());
382 if (toElementBytes
> fromElementBytes
&& !to
.type().IsCharacter()) {
383 terminator
.Crash("Assign: mismatching non-character element sizes (to %zd "
384 "bytes != from %zd bytes)",
385 toElementBytes
, fromElementBytes
);
387 if (const typeInfo::DerivedType
*
388 updatedToDerived
{toAddendum
? toAddendum
->derivedType() : nullptr}) {
389 // Derived type intrinsic assignment, which is componentwise and elementwise
390 // for all components, including parent components (10.2.1.2-3).
391 // The target is first finalized if still necessary (7.5.6.3(1))
392 if (flags
& NeedFinalization
) {
393 Finalize(to
, *updatedToDerived
, &terminator
);
394 } else if (updatedToDerived
&& !updatedToDerived
->noDestructionNeeded()) {
395 Destroy(to
, /*finalize=*/false, *updatedToDerived
, &terminator
);
397 // Copy the data components (incl. the parent) first.
398 const Descriptor
&componentDesc
{updatedToDerived
->component()};
399 std::size_t numComponents
{componentDesc
.Elements()};
400 for (std::size_t j
{0}; j
< toElements
;
401 ++j
, to
.IncrementSubscripts(toAt
), from
.IncrementSubscripts(fromAt
)) {
402 for (std::size_t k
{0}; k
< numComponents
; ++k
) {
404 *componentDesc
.ZeroBasedIndexedElement
<typeInfo::Component
>(
405 k
)}; // TODO: exploit contiguity here
406 // Use PolymorphicLHS for components so that the right things happen
407 // when the components are polymorphic; when they're not, they're both
408 // not, and their declared types will match.
409 int nestedFlags
{MaybeReallocate
| PolymorphicLHS
};
410 if (flags
& ComponentCanBeDefinedAssignment
) {
412 CanBeDefinedAssignment
| ComponentCanBeDefinedAssignment
;
414 switch (comp
.genre()) {
415 case typeInfo::Component::Genre::Data
:
416 if (comp
.category() == TypeCategory::Derived
) {
417 StaticDescriptor
<maxRank
, true, 10 /*?*/> statDesc
[2];
418 Descriptor
&toCompDesc
{statDesc
[0].descriptor()};
419 Descriptor
&fromCompDesc
{statDesc
[1].descriptor()};
420 comp
.CreatePointerDescriptor(toCompDesc
, to
, terminator
, toAt
);
421 comp
.CreatePointerDescriptor(
422 fromCompDesc
, from
, terminator
, fromAt
);
423 Assign(toCompDesc
, fromCompDesc
, terminator
, nestedFlags
);
424 } else { // Component has intrinsic type; simply copy raw bytes
425 std::size_t componentByteSize
{comp
.SizeInBytes(to
)};
426 Fortran::runtime::memmove(to
.Element
<char>(toAt
) + comp
.offset(),
427 from
.Element
<const char>(fromAt
) + comp
.offset(),
431 case typeInfo::Component::Genre::Pointer
: {
432 std::size_t componentByteSize
{comp
.SizeInBytes(to
)};
433 Fortran::runtime::memmove(to
.Element
<char>(toAt
) + comp
.offset(),
434 from
.Element
<const char>(fromAt
) + comp
.offset(),
437 case typeInfo::Component::Genre::Allocatable
:
438 case typeInfo::Component::Genre::Automatic
: {
439 auto *toDesc
{reinterpret_cast<Descriptor
*>(
440 to
.Element
<char>(toAt
) + comp
.offset())};
441 const auto *fromDesc
{reinterpret_cast<const Descriptor
*>(
442 from
.Element
<char>(fromAt
) + comp
.offset())};
443 // Allocatable components of the LHS are unconditionally
444 // deallocated before assignment (F'2018 10.2.1.3(13)(1)),
445 // unlike a "top-level" assignment to a variable, where
446 // deallocation is optional.
448 // Be careful not to destroy/reallocate the LHS, if there is
449 // overlap between LHS and RHS (it seems that partial overlap
450 // is not possible, though).
451 // Invoke Assign() recursively to deal with potential aliasing.
452 if (toDesc
->IsAllocatable()) {
453 if (!fromDesc
->IsAllocated()) {
456 // If to is not allocated, the Destroy() call is a no-op.
457 // This is just a shortcut, because the recursive Assign()
458 // below would initiate the destruction for to.
459 // No finalization is required.
461 /*finalize=*/false, /*destroyPointers=*/false, &terminator
);
462 continue; // F'2018 10.2.1.3(13)(2)
465 // Force LHS deallocation with DeallocateLHS flag.
466 // The actual deallocation may be avoided, if the existing
467 // location can be reoccupied.
468 Assign(*toDesc
, *fromDesc
, terminator
, nestedFlags
| DeallocateLHS
);
472 // Copy procedure pointer components
473 const Descriptor
&procPtrDesc
{updatedToDerived
->procPtr()};
474 std::size_t numProcPtrs
{procPtrDesc
.Elements()};
475 for (std::size_t k
{0}; k
< numProcPtrs
; ++k
) {
477 *procPtrDesc
.ZeroBasedIndexedElement
<typeInfo::ProcPtrComponent
>(
479 Fortran::runtime::memmove(to
.Element
<char>(toAt
) + procPtr
.offset
,
480 from
.Element
<const char>(fromAt
) + procPtr
.offset
,
481 sizeof(typeInfo::ProcedurePointer
));
484 } else { // intrinsic type, intrinsic assignment
485 if (isSimpleMemmove()) {
486 Fortran::runtime::memmove(to
.raw().base_addr
, from
.raw().base_addr
,
487 toElements
* toElementBytes
);
488 } else if (toElementBytes
> fromElementBytes
) { // blank padding
489 switch (to
.type().raw()) {
490 case CFI_type_signed_char
:
492 BlankPadCharacterAssignment
<char>(to
, from
, toAt
, fromAt
, toElements
,
493 toElementBytes
, fromElementBytes
);
495 case CFI_type_char16_t
:
496 BlankPadCharacterAssignment
<char16_t
>(to
, from
, toAt
, fromAt
,
497 toElements
, toElementBytes
, fromElementBytes
);
499 case CFI_type_char32_t
:
500 BlankPadCharacterAssignment
<char32_t
>(to
, from
, toAt
, fromAt
,
501 toElements
, toElementBytes
, fromElementBytes
);
504 terminator
.Crash("unexpected type code %d in blank padded Assign()",
507 } else { // elemental copies, possibly with character truncation
508 for (std::size_t n
{toElements
}; n
-- > 0;
509 to
.IncrementSubscripts(toAt
), from
.IncrementSubscripts(fromAt
)) {
510 Fortran::runtime::memmove(to
.Element
<char>(toAt
),
511 from
.Element
<const char>(fromAt
), toElementBytes
);
515 if (deferDeallocation
) {
516 // deferDeallocation is used only when LHS is an allocatable.
517 // The finalization has already been run for it.
518 deferDeallocation
->Destroy(
519 /*finalize=*/false, /*destroyPointers=*/false, &terminator
);
523 RT_OFFLOAD_API_GROUP_BEGIN
525 RT_API_ATTRS
void DoFromSourceAssign(
526 Descriptor
&alloc
, const Descriptor
&source
, Terminator
&terminator
) {
527 if (alloc
.rank() > 0 && source
.rank() == 0) {
528 // The value of each element of allocate object becomes the value of source.
529 DescriptorAddendum
*allocAddendum
{alloc
.Addendum()};
530 const typeInfo::DerivedType
*allocDerived
{
531 allocAddendum
? allocAddendum
->derivedType() : nullptr};
532 SubscriptValue allocAt
[maxRank
];
533 alloc
.GetLowerBounds(allocAt
);
535 for (std::size_t n
{alloc
.Elements()}; n
-- > 0;
536 alloc
.IncrementSubscripts(allocAt
)) {
537 Descriptor allocElement
{*Descriptor::Create(*allocDerived
,
538 reinterpret_cast<void *>(alloc
.Element
<char>(allocAt
)), 0)};
539 Assign(allocElement
, source
, terminator
, NoAssignFlags
);
541 } else { // intrinsic type
542 for (std::size_t n
{alloc
.Elements()}; n
-- > 0;
543 alloc
.IncrementSubscripts(allocAt
)) {
544 Fortran::runtime::memmove(alloc
.Element
<char>(allocAt
),
545 source
.raw().base_addr
, alloc
.ElementBytes());
549 Assign(alloc
, source
, terminator
, NoAssignFlags
);
553 RT_OFFLOAD_API_GROUP_END
556 RT_EXT_API_GROUP_BEGIN
558 void RTDEF(Assign
)(Descriptor
&to
, const Descriptor
&from
,
559 const char *sourceFile
, int sourceLine
) {
560 Terminator terminator
{sourceFile
, sourceLine
};
561 // All top-level defined assignments can be recognized in semantics and
562 // will have been already been converted to calls, so don't check for
563 // defined assignment apart from components.
564 Assign(to
, from
, terminator
,
565 MaybeReallocate
| NeedFinalization
| ComponentCanBeDefinedAssignment
);
568 void RTDEF(AssignTemporary
)(Descriptor
&to
, const Descriptor
&from
,
569 const char *sourceFile
, int sourceLine
) {
570 Terminator terminator
{sourceFile
, sourceLine
};
571 // Initialize the "to" if it is of derived type that needs initialization.
572 if (const DescriptorAddendum
* addendum
{to
.Addendum()}) {
573 if (const auto *derived
{addendum
->derivedType()}) {
574 // Do not invoke the initialization, if the descriptor is unallocated.
575 // AssignTemporary() is used for component-by-component assignments,
576 // for example, for structure constructors. This means that the LHS
577 // may be an allocatable component with unallocated status.
578 // The initialization will just fail in this case. By skipping
579 // the initialization we let Assign() automatically allocate
580 // and initialize the component according to the RHS.
581 // So we only need to initialize the LHS here if it is allocated.
582 // Note that initializing already initialized entity has no visible
583 // effect, though, it is assumed that the compiler does not initialize
584 // the temporary and leaves the initialization to this runtime code.
585 if (!derived
->noInitializationNeeded() && to
.IsAllocated()) {
586 if (ReturnError(terminator
, Initialize(to
, *derived
, terminator
)) !=
594 Assign(to
, from
, terminator
, PolymorphicLHS
);
597 void RTDEF(CopyInAssign
)(Descriptor
&temp
, const Descriptor
&var
,
598 const char *sourceFile
, int sourceLine
) {
599 Terminator terminator
{sourceFile
, sourceLine
};
601 temp
.set_base_addr(nullptr);
602 temp
.raw().attribute
= CFI_attribute_allocatable
;
603 RTNAME(AssignTemporary
)(temp
, var
, sourceFile
, sourceLine
);
606 void RTDEF(CopyOutAssign
)(
607 Descriptor
*var
, Descriptor
&temp
, const char *sourceFile
, int sourceLine
) {
608 Terminator terminator
{sourceFile
, sourceLine
};
610 // Copyout from the temporary must not cause any finalizations
611 // for LHS. The variable must be properly initialized already.
613 Assign(*var
, temp
, terminator
, NoAssignFlags
);
614 temp
.Destroy(/*finalize=*/false, /*destroyPointers=*/false, &terminator
);
617 void RTDEF(AssignExplicitLengthCharacter
)(Descriptor
&to
,
618 const Descriptor
&from
, const char *sourceFile
, int sourceLine
) {
619 Terminator terminator
{sourceFile
, sourceLine
};
620 Assign(to
, from
, terminator
,
621 MaybeReallocate
| NeedFinalization
| ComponentCanBeDefinedAssignment
|
622 ExplicitLengthCharacterLHS
);
625 void RTDEF(AssignPolymorphic
)(Descriptor
&to
, const Descriptor
&from
,
626 const char *sourceFile
, int sourceLine
) {
627 Terminator terminator
{sourceFile
, sourceLine
};
628 Assign(to
, from
, terminator
,
629 MaybeReallocate
| NeedFinalization
| ComponentCanBeDefinedAssignment
|
635 } // namespace Fortran::runtime