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
{
20 // Predicate: is the left-hand side of an assignment an allocated allocatable
21 // that must be deallocated?
22 static inline RT_API_ATTRS
bool MustDeallocateLHS(
23 Descriptor
&to
, const Descriptor
&from
, Terminator
&terminator
, int flags
) {
24 // Top-level assignments to allocatable variables (*not* components)
25 // may first deallocate existing content if there's about to be a
26 // change in type or shape; see F'2018 10.2.1.3(3).
27 if (!(flags
& MaybeReallocate
)) {
30 if (!to
.IsAllocatable() || !to
.IsAllocated()) {
33 if (to
.type() != from
.type()) {
36 if (!(flags
& ExplicitLengthCharacterLHS
) && to
.type().IsCharacter() &&
37 to
.ElementBytes() != from
.ElementBytes()) {
40 if (flags
& PolymorphicLHS
) {
41 DescriptorAddendum
*toAddendum
{to
.Addendum()};
42 const typeInfo::DerivedType
*toDerived
{
43 toAddendum
? toAddendum
->derivedType() : nullptr};
44 const DescriptorAddendum
*fromAddendum
{from
.Addendum()};
45 const typeInfo::DerivedType
*fromDerived
{
46 fromAddendum
? fromAddendum
->derivedType() : nullptr};
47 if (toDerived
!= fromDerived
) {
51 // Distinct LEN parameters? Deallocate
52 std::size_t lenParms
{fromDerived
->LenParameters()};
53 for (std::size_t j
{0}; j
< lenParms
; ++j
) {
54 if (toAddendum
->LenParameterValue(j
) !=
55 fromAddendum
->LenParameterValue(j
)) {
61 if (from
.rank() > 0) {
62 // Distinct shape? Deallocate
64 for (int j
{0}; j
< rank
; ++j
) {
65 if (to
.GetDimension(j
).Extent() != from
.GetDimension(j
).Extent()) {
73 // Utility: allocate the allocatable left-hand side, either because it was
74 // originally deallocated or because it required reallocation
75 static RT_API_ATTRS
int AllocateAssignmentLHS(
76 Descriptor
&to
, const Descriptor
&from
, Terminator
&terminator
, int flags
) {
77 to
.raw().type
= from
.raw().type
;
78 if (!(flags
& ExplicitLengthCharacterLHS
)) {
79 to
.raw().elem_len
= from
.ElementBytes();
81 const typeInfo::DerivedType
*derived
{nullptr};
82 if (const DescriptorAddendum
* fromAddendum
{from
.Addendum()}) {
83 derived
= fromAddendum
->derivedType();
84 if (DescriptorAddendum
* toAddendum
{to
.Addendum()}) {
85 toAddendum
->set_derivedType(derived
);
86 std::size_t lenParms
{derived
? derived
->LenParameters() : 0};
87 for (std::size_t j
{0}; j
< lenParms
; ++j
) {
88 toAddendum
->SetLenParameterValue(j
, fromAddendum
->LenParameterValue(j
));
92 // subtle: leave bounds in place when "from" is scalar (10.2.1.3(3))
93 int rank
{from
.rank()};
94 auto stride
{static_cast<SubscriptValue
>(to
.ElementBytes())};
95 for (int j
{0}; j
< rank
; ++j
) {
96 auto &toDim
{to
.GetDimension(j
)};
97 const auto &fromDim
{from
.GetDimension(j
)};
98 toDim
.SetBounds(fromDim
.LowerBound(), fromDim
.UpperBound());
99 toDim
.SetByteStride(stride
);
100 stride
*= toDim
.Extent();
102 int result
{ReturnError(terminator
, to
.Allocate())};
103 if (result
== StatOk
&& derived
&& !derived
->noInitializationNeeded()) {
104 result
= ReturnError(terminator
, Initialize(to
, *derived
, terminator
));
109 // least <= 0, most >= 0
110 static RT_API_ATTRS
void MaximalByteOffsetRange(
111 const Descriptor
&desc
, std::int64_t &least
, std::int64_t &most
) {
113 if (desc
.ElementBytes() == 0) {
116 int n
{desc
.raw().rank
};
117 for (int j
{0}; j
< n
; ++j
) {
118 const auto &dim
{desc
.GetDimension(j
)};
119 auto extent
{dim
.Extent()};
121 auto sm
{dim
.ByteStride()};
123 least
+= (extent
- 1) * sm
;
125 most
+= (extent
- 1) * sm
;
129 most
+= desc
.ElementBytes() - 1;
132 static inline RT_API_ATTRS
bool RangesOverlap(const char *aStart
,
133 const char *aEnd
, const char *bStart
, const char *bEnd
) {
134 return aEnd
>= bStart
&& bEnd
>= aStart
;
137 // Predicate: could the left-hand and right-hand sides of the assignment
138 // possibly overlap in memory? Note that the descriptors themeselves
139 // are included in the test.
140 static RT_API_ATTRS
bool MayAlias(const Descriptor
&x
, const Descriptor
&y
) {
141 const char *xBase
{x
.OffsetElement()};
142 const char *yBase
{y
.OffsetElement()};
143 if (!xBase
|| !yBase
) {
144 return false; // not both allocated
146 const char *xDesc
{reinterpret_cast<const char *>(&x
)};
147 const char *xDescLast
{xDesc
+ x
.SizeInBytes() - 1};
148 const char *yDesc
{reinterpret_cast<const char *>(&y
)};
149 const char *yDescLast
{yDesc
+ y
.SizeInBytes() - 1};
150 std::int64_t xLeast
, xMost
, yLeast
, yMost
;
151 MaximalByteOffsetRange(x
, xLeast
, xMost
);
152 MaximalByteOffsetRange(y
, yLeast
, yMost
);
153 if (RangesOverlap(xDesc
, xDescLast
, yBase
+ yLeast
, yBase
+ yMost
) ||
154 RangesOverlap(yDesc
, yDescLast
, xBase
+ xLeast
, xBase
+ xMost
)) {
155 // A descriptor overlaps with the storage described by the other;
156 // this can arise when an allocatable or pointer component is
157 // being assigned to/from.
161 xBase
+ xLeast
, xBase
+ xMost
, yBase
+ yLeast
, yBase
+ yMost
)) {
162 return false; // no storage overlap
164 // TODO: check dimensions: if any is independent, return false
168 static RT_API_ATTRS
void DoScalarDefinedAssignment(const Descriptor
&to
,
169 const Descriptor
&from
, const typeInfo::SpecialBinding
&special
) {
170 bool toIsDesc
{special
.IsArgDescriptor(0)};
171 bool fromIsDesc
{special
.IsArgDescriptor(1)};
175 special
.GetProc
<void (*)(const Descriptor
&, const Descriptor
&)>()};
178 auto *p
{special
.GetProc
<void (*)(const Descriptor
&, void *)>()};
179 p(to
, from
.raw().base_addr
);
183 auto *p
{special
.GetProc
<void (*)(void *, const Descriptor
&)>()};
184 p(to
.raw().base_addr
, from
);
186 auto *p
{special
.GetProc
<void (*)(void *, void *)>()};
187 p(to
.raw().base_addr
, from
.raw().base_addr
);
192 static RT_API_ATTRS
void DoElementalDefinedAssignment(const Descriptor
&to
,
193 const Descriptor
&from
, const typeInfo::DerivedType
&derived
,
194 const typeInfo::SpecialBinding
&special
) {
195 SubscriptValue toAt
[maxRank
], fromAt
[maxRank
];
196 to
.GetLowerBounds(toAt
);
197 from
.GetLowerBounds(fromAt
);
198 StaticDescriptor
<maxRank
, true, 8 /*?*/> statDesc
[2];
199 Descriptor
&toElementDesc
{statDesc
[0].descriptor()};
200 Descriptor
&fromElementDesc
{statDesc
[1].descriptor()};
201 toElementDesc
.Establish(derived
, nullptr, 0, nullptr, CFI_attribute_pointer
);
202 fromElementDesc
.Establish(
203 derived
, nullptr, 0, nullptr, CFI_attribute_pointer
);
204 for (std::size_t toElements
{to
.Elements()}; toElements
-- > 0;
205 to
.IncrementSubscripts(toAt
), from
.IncrementSubscripts(fromAt
)) {
206 toElementDesc
.set_base_addr(to
.Element
<char>(toAt
));
207 fromElementDesc
.set_base_addr(from
.Element
<char>(fromAt
));
208 DoScalarDefinedAssignment(toElementDesc
, fromElementDesc
, special
);
212 template <typename CHAR
>
213 static RT_API_ATTRS
void BlankPadCharacterAssignment(Descriptor
&to
,
214 const Descriptor
&from
, SubscriptValue toAt
[], SubscriptValue fromAt
[],
215 std::size_t elements
, std::size_t toElementBytes
,
216 std::size_t fromElementBytes
) {
217 std::size_t padding
{(toElementBytes
- fromElementBytes
) / sizeof(CHAR
)};
218 std::size_t copiedCharacters
{fromElementBytes
/ sizeof(CHAR
)};
219 for (; elements
-- > 0;
220 to
.IncrementSubscripts(toAt
), from
.IncrementSubscripts(fromAt
)) {
221 CHAR
*p
{to
.Element
<CHAR
>(toAt
)};
222 Fortran::runtime::memmove(
223 p
, from
.Element
<std::add_const_t
<CHAR
>>(fromAt
), fromElementBytes
);
224 p
+= copiedCharacters
;
225 for (auto n
{padding
}; n
-- > 0;) {
231 // Common implementation of assignments, both intrinsic assignments and
232 // those cases of polymorphic user-defined ASSIGNMENT(=) TBPs that could not
233 // be resolved in semantics. Most assignment statements do not need any
234 // of the capabilities of this function -- but when the LHS is allocatable,
235 // the type might have a user-defined ASSIGNMENT(=), or the type might be
236 // finalizable, this function should be used.
237 // When "to" is not a whole allocatable, "from" is an array, and defined
238 // assignments are not used, "to" and "from" only need to have the same number
239 // of elements, but their shape need not to conform (the assignment is done in
240 // element sequence order). This facilitates some internal usages, like when
241 // dealing with array constructors.
242 RT_API_ATTRS
void Assign(Descriptor
&to
, const Descriptor
&from
,
243 Terminator
&terminator
, int flags
, MemmoveFct memmoveFct
) {
244 bool mustDeallocateLHS
{(flags
& DeallocateLHS
) ||
245 MustDeallocateLHS(to
, from
, terminator
, flags
)};
246 DescriptorAddendum
*toAddendum
{to
.Addendum()};
247 const typeInfo::DerivedType
*toDerived
{
248 toAddendum
? toAddendum
->derivedType() : nullptr};
249 if (toDerived
&& (flags
& NeedFinalization
) &&
250 toDerived
->noFinalizationNeeded()) {
251 flags
&= ~NeedFinalization
;
253 std::size_t toElementBytes
{to
.ElementBytes()};
254 std::size_t fromElementBytes
{from
.ElementBytes()};
255 // The following lambda definition violates the conding style,
256 // but cuda-11.8 nvcc hits an internal error with the brace initialization.
257 auto isSimpleMemmove
= [&]() {
258 return !toDerived
&& to
.rank() == from
.rank() && to
.IsContiguous() &&
259 from
.IsContiguous() && toElementBytes
== fromElementBytes
;
261 StaticDescriptor
<maxRank
, true, 10 /*?*/> deferredDeallocStatDesc
;
262 Descriptor
*deferDeallocation
{nullptr};
263 if (MayAlias(to
, from
)) {
264 if (mustDeallocateLHS
) {
265 deferDeallocation
= &deferredDeallocStatDesc
.descriptor();
266 std::memcpy(deferDeallocation
, &to
, to
.SizeInBytes());
267 to
.set_base_addr(nullptr);
268 } else if (!isSimpleMemmove()) {
269 // Handle LHS/RHS aliasing by copying RHS into a temp, then
270 // recursively assigning from that temp.
271 auto descBytes
{from
.SizeInBytes()};
272 StaticDescriptor
<maxRank
, true, 16> staticDesc
;
273 Descriptor
&newFrom
{staticDesc
.descriptor()};
274 std::memcpy(&newFrom
, &from
, descBytes
);
275 // Pretend the temporary descriptor is for an ALLOCATABLE
276 // entity, otherwise, the Deallocate() below will not
277 // free the descriptor memory.
278 newFrom
.raw().attribute
= CFI_attribute_allocatable
;
279 auto stat
{ReturnError(terminator
, newFrom
.Allocate())};
280 if (stat
== StatOk
) {
281 if (HasDynamicComponent(from
)) {
282 // If 'from' has allocatable/automatic component, we cannot
283 // just make a shallow copy of the descriptor member.
284 // This will still leave data overlap in 'to' and 'newFrom'.
287 // character, allocatable :: c(:)
291 // We have to make a deep copy into 'newFrom' in this case.
292 RTNAME(AssignTemporary
)
293 (newFrom
, from
, terminator
.sourceFileName(), terminator
.sourceLine());
295 ShallowCopy(newFrom
, from
, true, from
.IsContiguous());
297 Assign(to
, newFrom
, terminator
,
299 (NeedFinalization
| ComponentCanBeDefinedAssignment
|
300 ExplicitLengthCharacterLHS
| CanBeDefinedAssignment
));
301 newFrom
.Deallocate();
306 if (to
.IsAllocatable()) {
307 if (mustDeallocateLHS
) {
308 if (deferDeallocation
) {
309 if ((flags
& NeedFinalization
) && toDerived
) {
310 Finalize(*deferDeallocation
, *toDerived
, &terminator
);
311 flags
&= ~NeedFinalization
;
314 to
.Destroy((flags
& NeedFinalization
) != 0, /*destroyPointers=*/false,
316 flags
&= ~NeedFinalization
;
318 } else if (to
.rank() != from
.rank() && !to
.IsAllocated()) {
319 terminator
.Crash("Assign: mismatched ranks (%d != %d) in assignment to "
320 "unallocated allocatable",
321 to
.rank(), from
.rank());
323 if (!to
.IsAllocated()) {
324 if (AllocateAssignmentLHS(to
, from
, terminator
, flags
) != StatOk
) {
327 flags
&= ~NeedFinalization
;
328 toElementBytes
= to
.ElementBytes(); // may have changed
331 if (toDerived
&& (flags
& CanBeDefinedAssignment
)) {
332 // Check for a user-defined assignment type-bound procedure;
333 // see 10.2.1.4-5. A user-defined assignment TBP defines all of
334 // the semantics, including allocatable (re)allocation and any
337 // Note that the aliasing and LHS (re)allocation handling above
338 // needs to run even with CanBeDefinedAssignment flag, when
339 // the Assign() is invoked recursively for component-per-component
341 if (to
.rank() == 0) {
342 if (const auto *special
{toDerived
->FindSpecialBinding(
343 typeInfo::SpecialBinding::Which::ScalarAssignment
)}) {
344 return DoScalarDefinedAssignment(to
, from
, *special
);
347 if (const auto *special
{toDerived
->FindSpecialBinding(
348 typeInfo::SpecialBinding::Which::ElementalAssignment
)}) {
349 return DoElementalDefinedAssignment(to
, from
, *toDerived
, *special
);
352 SubscriptValue toAt
[maxRank
];
353 to
.GetLowerBounds(toAt
);
354 // Scalar expansion of the RHS is implied by using the same empty
355 // subscript values on each (seemingly) elemental reference into
357 SubscriptValue fromAt
[maxRank
];
358 from
.GetLowerBounds(fromAt
);
359 std::size_t toElements
{to
.Elements()};
360 if (from
.rank() > 0 && toElements
!= from
.Elements()) {
361 terminator
.Crash("Assign: mismatching element counts in array assignment "
362 "(to %zd, from %zd)",
363 toElements
, from
.Elements());
365 if (to
.type() != from
.type()) {
366 terminator
.Crash("Assign: mismatching types (to code %d != from code %d)",
367 to
.type().raw(), from
.type().raw());
369 if (toElementBytes
> fromElementBytes
&& !to
.type().IsCharacter()) {
370 terminator
.Crash("Assign: mismatching non-character element sizes (to %zd "
371 "bytes != from %zd bytes)",
372 toElementBytes
, fromElementBytes
);
374 if (const typeInfo::DerivedType
*
375 updatedToDerived
{toAddendum
? toAddendum
->derivedType() : nullptr}) {
376 // Derived type intrinsic assignment, which is componentwise and elementwise
377 // for all components, including parent components (10.2.1.2-3).
378 // The target is first finalized if still necessary (7.5.6.3(1))
379 if (flags
& NeedFinalization
) {
380 Finalize(to
, *updatedToDerived
, &terminator
);
381 } else if (updatedToDerived
&& !updatedToDerived
->noDestructionNeeded()) {
382 Destroy(to
, /*finalize=*/false, *updatedToDerived
, &terminator
);
384 // Copy the data components (incl. the parent) first.
385 const Descriptor
&componentDesc
{updatedToDerived
->component()};
386 std::size_t numComponents
{componentDesc
.Elements()};
387 for (std::size_t j
{0}; j
< toElements
;
388 ++j
, to
.IncrementSubscripts(toAt
), from
.IncrementSubscripts(fromAt
)) {
389 for (std::size_t k
{0}; k
< numComponents
; ++k
) {
391 *componentDesc
.ZeroBasedIndexedElement
<typeInfo::Component
>(
392 k
)}; // TODO: exploit contiguity here
393 // Use PolymorphicLHS for components so that the right things happen
394 // when the components are polymorphic; when they're not, they're both
395 // not, and their declared types will match.
396 int nestedFlags
{MaybeReallocate
| PolymorphicLHS
};
397 if (flags
& ComponentCanBeDefinedAssignment
) {
399 CanBeDefinedAssignment
| ComponentCanBeDefinedAssignment
;
401 switch (comp
.genre()) {
402 case typeInfo::Component::Genre::Data
:
403 if (comp
.category() == TypeCategory::Derived
) {
404 StaticDescriptor
<maxRank
, true, 10 /*?*/> statDesc
[2];
405 Descriptor
&toCompDesc
{statDesc
[0].descriptor()};
406 Descriptor
&fromCompDesc
{statDesc
[1].descriptor()};
407 comp
.CreatePointerDescriptor(toCompDesc
, to
, terminator
, toAt
);
408 comp
.CreatePointerDescriptor(
409 fromCompDesc
, from
, terminator
, fromAt
);
410 Assign(toCompDesc
, fromCompDesc
, terminator
, nestedFlags
);
411 } else { // Component has intrinsic type; simply copy raw bytes
412 std::size_t componentByteSize
{comp
.SizeInBytes(to
)};
413 memmoveFct(to
.Element
<char>(toAt
) + comp
.offset(),
414 from
.Element
<const char>(fromAt
) + comp
.offset(),
418 case typeInfo::Component::Genre::Pointer
: {
419 std::size_t componentByteSize
{comp
.SizeInBytes(to
)};
420 memmoveFct(to
.Element
<char>(toAt
) + comp
.offset(),
421 from
.Element
<const char>(fromAt
) + comp
.offset(),
424 case typeInfo::Component::Genre::Allocatable
:
425 case typeInfo::Component::Genre::Automatic
: {
426 auto *toDesc
{reinterpret_cast<Descriptor
*>(
427 to
.Element
<char>(toAt
) + comp
.offset())};
428 const auto *fromDesc
{reinterpret_cast<const Descriptor
*>(
429 from
.Element
<char>(fromAt
) + comp
.offset())};
430 // Allocatable components of the LHS are unconditionally
431 // deallocated before assignment (F'2018 10.2.1.3(13)(1)),
432 // unlike a "top-level" assignment to a variable, where
433 // deallocation is optional.
435 // Be careful not to destroy/reallocate the LHS, if there is
436 // overlap between LHS and RHS (it seems that partial overlap
437 // is not possible, though).
438 // Invoke Assign() recursively to deal with potential aliasing.
439 if (toDesc
->IsAllocatable()) {
440 if (!fromDesc
->IsAllocated()) {
443 // If to is not allocated, the Destroy() call is a no-op.
444 // This is just a shortcut, because the recursive Assign()
445 // below would initiate the destruction for to.
446 // No finalization is required.
448 /*finalize=*/false, /*destroyPointers=*/false, &terminator
);
449 continue; // F'2018 10.2.1.3(13)(2)
452 // Force LHS deallocation with DeallocateLHS flag.
453 // The actual deallocation may be avoided, if the existing
454 // location can be reoccupied.
455 Assign(*toDesc
, *fromDesc
, terminator
, nestedFlags
| DeallocateLHS
);
459 // Copy procedure pointer components
460 const Descriptor
&procPtrDesc
{updatedToDerived
->procPtr()};
461 std::size_t numProcPtrs
{procPtrDesc
.Elements()};
462 for (std::size_t k
{0}; k
< numProcPtrs
; ++k
) {
464 *procPtrDesc
.ZeroBasedIndexedElement
<typeInfo::ProcPtrComponent
>(
466 memmoveFct(to
.Element
<char>(toAt
) + procPtr
.offset
,
467 from
.Element
<const char>(fromAt
) + procPtr
.offset
,
468 sizeof(typeInfo::ProcedurePointer
));
471 } else { // intrinsic type, intrinsic assignment
472 if (isSimpleMemmove()) {
473 memmoveFct(to
.raw().base_addr
, from
.raw().base_addr
,
474 toElements
* toElementBytes
);
475 } else if (toElementBytes
> fromElementBytes
) { // blank padding
476 switch (to
.type().raw()) {
477 case CFI_type_signed_char
:
479 BlankPadCharacterAssignment
<char>(to
, from
, toAt
, fromAt
, toElements
,
480 toElementBytes
, fromElementBytes
);
482 case CFI_type_char16_t
:
483 BlankPadCharacterAssignment
<char16_t
>(to
, from
, toAt
, fromAt
,
484 toElements
, toElementBytes
, fromElementBytes
);
486 case CFI_type_char32_t
:
487 BlankPadCharacterAssignment
<char32_t
>(to
, from
, toAt
, fromAt
,
488 toElements
, toElementBytes
, fromElementBytes
);
491 terminator
.Crash("unexpected type code %d in blank padded Assign()",
494 } else { // elemental copies, possibly with character truncation
495 for (std::size_t n
{toElements
}; n
-- > 0;
496 to
.IncrementSubscripts(toAt
), from
.IncrementSubscripts(fromAt
)) {
497 memmoveFct(to
.Element
<char>(toAt
), from
.Element
<const char>(fromAt
),
502 if (deferDeallocation
) {
503 // deferDeallocation is used only when LHS is an allocatable.
504 // The finalization has already been run for it.
505 deferDeallocation
->Destroy(
506 /*finalize=*/false, /*destroyPointers=*/false, &terminator
);
510 RT_OFFLOAD_API_GROUP_BEGIN
512 RT_API_ATTRS
void DoFromSourceAssign(Descriptor
&alloc
,
513 const Descriptor
&source
, Terminator
&terminator
, MemmoveFct memmoveFct
) {
514 if (alloc
.rank() > 0 && source
.rank() == 0) {
515 // The value of each element of allocate object becomes the value of source.
516 DescriptorAddendum
*allocAddendum
{alloc
.Addendum()};
517 const typeInfo::DerivedType
*allocDerived
{
518 allocAddendum
? allocAddendum
->derivedType() : nullptr};
519 SubscriptValue allocAt
[maxRank
];
520 alloc
.GetLowerBounds(allocAt
);
522 for (std::size_t n
{alloc
.Elements()}; n
-- > 0;
523 alloc
.IncrementSubscripts(allocAt
)) {
524 Descriptor allocElement
{*Descriptor::Create(*allocDerived
,
525 reinterpret_cast<void *>(alloc
.Element
<char>(allocAt
)), 0)};
526 Assign(allocElement
, source
, terminator
, NoAssignFlags
, memmoveFct
);
528 } else { // intrinsic type
529 for (std::size_t n
{alloc
.Elements()}; n
-- > 0;
530 alloc
.IncrementSubscripts(allocAt
)) {
531 memmoveFct(alloc
.Element
<char>(allocAt
), source
.raw().base_addr
,
532 alloc
.ElementBytes());
536 Assign(alloc
, source
, terminator
, NoAssignFlags
, memmoveFct
);
540 RT_OFFLOAD_API_GROUP_END
543 RT_EXT_API_GROUP_BEGIN
545 void RTDEF(Assign
)(Descriptor
&to
, const Descriptor
&from
,
546 const char *sourceFile
, int sourceLine
) {
547 Terminator terminator
{sourceFile
, sourceLine
};
548 // All top-level defined assignments can be recognized in semantics and
549 // will have been already been converted to calls, so don't check for
550 // defined assignment apart from components.
551 Assign(to
, from
, terminator
,
552 MaybeReallocate
| NeedFinalization
| ComponentCanBeDefinedAssignment
);
555 void RTDEF(AssignTemporary
)(Descriptor
&to
, const Descriptor
&from
,
556 const char *sourceFile
, int sourceLine
) {
557 Terminator terminator
{sourceFile
, sourceLine
};
558 // Initialize the "to" if it is of derived type that needs initialization.
559 if (const DescriptorAddendum
* addendum
{to
.Addendum()}) {
560 if (const auto *derived
{addendum
->derivedType()}) {
561 // Do not invoke the initialization, if the descriptor is unallocated.
562 // AssignTemporary() is used for component-by-component assignments,
563 // for example, for structure constructors. This means that the LHS
564 // may be an allocatable component with unallocated status.
565 // The initialization will just fail in this case. By skipping
566 // the initialization we let Assign() automatically allocate
567 // and initialize the component according to the RHS.
568 // So we only need to initialize the LHS here if it is allocated.
569 // Note that initializing already initialized entity has no visible
570 // effect, though, it is assumed that the compiler does not initialize
571 // the temporary and leaves the initialization to this runtime code.
572 if (!derived
->noInitializationNeeded() && to
.IsAllocated()) {
573 if (ReturnError(terminator
, Initialize(to
, *derived
, terminator
)) !=
581 Assign(to
, from
, terminator
, MaybeReallocate
| PolymorphicLHS
);
584 void RTDEF(CopyInAssign
)(Descriptor
&temp
, const Descriptor
&var
,
585 const char *sourceFile
, int sourceLine
) {
586 Terminator terminator
{sourceFile
, sourceLine
};
588 temp
.set_base_addr(nullptr);
589 temp
.raw().attribute
= CFI_attribute_allocatable
;
590 RTNAME(AssignTemporary
)(temp
, var
, sourceFile
, sourceLine
);
593 void RTDEF(CopyOutAssign
)(
594 Descriptor
*var
, Descriptor
&temp
, const char *sourceFile
, int sourceLine
) {
595 Terminator terminator
{sourceFile
, sourceLine
};
597 // Copyout from the temporary must not cause any finalizations
598 // for LHS. The variable must be properly initialized already.
600 Assign(*var
, temp
, terminator
, NoAssignFlags
);
601 temp
.Destroy(/*finalize=*/false, /*destroyPointers=*/false, &terminator
);
604 void RTDEF(AssignExplicitLengthCharacter
)(Descriptor
&to
,
605 const Descriptor
&from
, const char *sourceFile
, int sourceLine
) {
606 Terminator terminator
{sourceFile
, sourceLine
};
607 Assign(to
, from
, terminator
,
608 MaybeReallocate
| NeedFinalization
| ComponentCanBeDefinedAssignment
|
609 ExplicitLengthCharacterLHS
);
612 void RTDEF(AssignPolymorphic
)(Descriptor
&to
, const Descriptor
&from
,
613 const char *sourceFile
, int sourceLine
) {
614 Terminator terminator
{sourceFile
, sourceLine
};
615 Assign(to
, from
, terminator
,
616 MaybeReallocate
| NeedFinalization
| ComponentCanBeDefinedAssignment
|
622 } // namespace Fortran::runtime