[flang-commits] [flang] 755535b - [flang][runtime] Handle aliasing in Assign()

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Tue Feb 14 09:54:23 PST 2023


Author: Peter Klausler
Date: 2023-02-14T09:54:08-08:00
New Revision: 755535b5eb5f6d60e9cc347cecd9e057231b92bb

URL: https://github.com/llvm/llvm-project/commit/755535b5eb5f6d60e9cc347cecd9e057231b92bb
DIFF: https://github.com/llvm/llvm-project/commit/755535b5eb5f6d60e9cc347cecd9e057231b92bb.diff

LOG: [flang][runtime] Handle aliasing in Assign()

Detect and handle LHS/RHS aliasing when effecting intrinsic
assignments via the Assign() runtime function.

Also: don't apply special handling for allocatable LHS when calling
a user-defined type-bound ASSIGNMENT(=) generic procedure for a
polymorphic type, and refactor some code into utility functions to
make Assign() more comprehensible.

Differential Revision: https://reviews.llvm.org/D144026

Added: 
    flang/runtime/assign-impl.h

Modified: 
    flang/include/flang/Runtime/assign.h
    flang/runtime/allocatable.cpp
    flang/runtime/assign.cpp
    flang/runtime/derived-api.cpp
    flang/runtime/pointer.cpp

Removed: 
    flang/runtime/assign.h


################################################################################
diff  --git a/flang/include/flang/Runtime/assign.h b/flang/include/flang/Runtime/assign.h
index fe06567376709..0cc2eb64536d0 100644
--- a/flang/include/flang/Runtime/assign.h
+++ b/flang/include/flang/Runtime/assign.h
@@ -17,8 +17,8 @@
 // must arrive here.
 //
 // Non-type-bound generic INTERFACE ASSIGNMENT(=) is resolved in semantics and
-// need not be handled here in the runtime; ditto for type conversions on
-// intrinsic assignments.
+// need not be handled here in the runtime apart from derived type components;
+// ditto for type conversions on intrinsic assignments.
 
 #ifndef FORTRAN_RUNTIME_ASSIGN_H_
 #define FORTRAN_RUNTIME_ASSIGN_H_
@@ -32,6 +32,10 @@ extern "C" {
 // API for lowering assignment
 void RTNAME(Assign)(Descriptor &to, const Descriptor &from,
     const char *sourceFile = nullptr, int sourceLine = 0);
+// This variant has no finalization, defined assignment, or allocatable
+// reallocation.
+void RTNAME(AssignTemporary)(Descriptor &to, const Descriptor &from,
+    const char *sourceFile = nullptr, int sourceLine = 0);
 } // extern "C"
 } // namespace Fortran::runtime
 #endif // FORTRAN_RUNTIME_ASSIGN_H_

diff  --git a/flang/runtime/allocatable.cpp b/flang/runtime/allocatable.cpp
index a7cc75b11feb7..6f066ead7d992 100644
--- a/flang/runtime/allocatable.cpp
+++ b/flang/runtime/allocatable.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Runtime/allocatable.h"
-#include "assign.h"
+#include "assign-impl.h"
 #include "derived.h"
 #include "stat.h"
 #include "terminator.h"

diff  --git a/flang/runtime/assign.h b/flang/runtime/assign-impl.h
similarity index 80%
rename from flang/runtime/assign.h
rename to flang/runtime/assign-impl.h
index 6b7f442bbfced..0cc3aab432fc2 100644
--- a/flang/runtime/assign.h
+++ b/flang/runtime/assign-impl.h
@@ -1,4 +1,4 @@
-//===-- runtime/assign.h-----------------------------------------*- C++ -*-===//
+//===-- runtime/assign-impl.h -----------------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef FORTRAN_RUNTIME_ASSIGN_INTERNAL_H_
-#define FORTRAN_RUNTIME_ASSIGN_INTERNAL_H_
+#ifndef FORTRAN_RUNTIME_ASSIGN_IMPL_H_
+#define FORTRAN_RUNTIME_ASSIGN_IMPL_H_
 
 namespace Fortran::runtime {
 class Descriptor;
@@ -20,4 +20,4 @@ class Terminator;
 void DoFromSourceAssign(Descriptor &, const Descriptor &, Terminator &);
 
 } // namespace Fortran::runtime
-#endif // FORTRAN_RUNTIME_ASSIGN_INTERNAL_H_
+#endif // FORTRAN_RUNTIME_ASSIGN_IMPL_H_

diff  --git a/flang/runtime/assign.cpp b/flang/runtime/assign.cpp
index 74f4844c39e25..76f929ec0e7ab 100644
--- a/flang/runtime/assign.cpp
+++ b/flang/runtime/assign.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Runtime/assign.h"
-#include "assign.h"
+#include "assign-impl.h"
 #include "derived.h"
 #include "stat.h"
 #include "terminator.h"
@@ -16,6 +16,140 @@
 
 namespace Fortran::runtime {
 
+// Predicate: is the left-hand side of an assignment an allocated allocatable
+// that must be deallocated?
+static inline bool MustDeallocateLHS(
+    Descriptor &to, const Descriptor &from, Terminator &terminator) {
+  // Top-level assignments to allocatable variables (*not* components)
+  // may first deallocate existing content if there's about to be a
+  // change in type or shape; see F'2018 10.2.1.3(3).
+  if (!to.IsAllocatable() || !to.IsAllocated()) {
+    return false;
+  }
+  if (to.type() != from.type()) {
+    return true;
+  }
+  DescriptorAddendum *toAddendum{to.Addendum()};
+  const typeInfo::DerivedType *toDerived{
+      toAddendum ? toAddendum->derivedType() : nullptr};
+  const DescriptorAddendum *fromAddendum{from.Addendum()};
+  const typeInfo::DerivedType *fromDerived{
+      fromAddendum ? fromAddendum->derivedType() : nullptr};
+  if (toDerived != fromDerived) {
+    return true;
+  }
+  if (toAddendum) {
+    // Distinct LEN parameters? Deallocate
+    std::size_t lenParms{fromDerived ? fromDerived->LenParameters() : 0};
+    for (std::size_t j{0}; j < lenParms; ++j) {
+      if (toAddendum->LenParameterValue(j) !=
+          fromAddendum->LenParameterValue(j)) {
+        return true;
+      }
+    }
+  }
+  if (from.rank() > 0) {
+    // Distinct shape? Deallocate
+    int rank{to.rank()};
+    for (int j{0}; j < rank; ++j) {
+      if (to.GetDimension(j).Extent() != from.GetDimension(j).Extent()) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+// Utility: allocate the allocatable left-hand side, either because it was
+// originally deallocated or because it required reallocation
+static int AllocateAssignmentLHS(
+    Descriptor &to, const Descriptor &from, Terminator &terminator) {
+  to.raw().type = from.raw().type;
+  to.raw().elem_len = from.ElementBytes();
+  const typeInfo::DerivedType *derived{nullptr};
+  if (const DescriptorAddendum * fromAddendum{from.Addendum()}) {
+    derived = fromAddendum->derivedType();
+    if (DescriptorAddendum * toAddendum{to.Addendum()}) {
+      toAddendum->set_derivedType(derived);
+      std::size_t lenParms{derived ? derived->LenParameters() : 0};
+      for (std::size_t j{0}; j < lenParms; ++j) {
+        toAddendum->SetLenParameterValue(j, fromAddendum->LenParameterValue(j));
+      }
+    }
+  }
+  // subtle: leave bounds in place when "from" is scalar (10.2.1.3(3))
+  int rank{from.rank()};
+  auto stride{static_cast<SubscriptValue>(to.ElementBytes())};
+  for (int j{0}; j < rank; ++j) {
+    auto &toDim{to.GetDimension(j)};
+    const auto &fromDim{from.GetDimension(j)};
+    toDim.SetBounds(fromDim.LowerBound(), fromDim.UpperBound());
+    toDim.SetByteStride(stride);
+    stride *= toDim.Extent();
+  }
+  int result{ReturnError(terminator, to.Allocate())};
+  if (result == StatOk && derived && !derived->noInitializationNeeded()) {
+    result = ReturnError(terminator, Initialize(to, *derived, terminator));
+  }
+  return result;
+}
+
+// least <= 0, most >= 0
+static void MaximalByteOffsetRange(
+    const Descriptor &desc, std::int64_t &least, std::int64_t &most) {
+  least = most = 0;
+  if (desc.ElementBytes() == 0) {
+    return;
+  }
+  int n{desc.raw().rank};
+  for (int j{0}; j < n; ++j) {
+    const auto &dim{desc.GetDimension(j)};
+    auto extent{dim.Extent()};
+    if (extent > 0) {
+      auto sm{dim.ByteStride()};
+      if (sm < 0) {
+        least += extent * sm;
+      } else {
+        most += extent * sm;
+      }
+    }
+  }
+  most += desc.ElementBytes() - 1;
+}
+
+static inline bool RangesOverlap(const char *aStart, const char *aEnd,
+    const char *bStart, const char *bEnd) {
+  return aEnd >= bStart && bEnd >= aStart;
+}
+
+// Predicate: could the left-hand and right-hand sides of the assignment
+// possibly overlap in memory?  Note that the descriptors themeselves
+// are included in the test.
+static bool MayAlias(const Descriptor &x, const Descriptor &y) {
+  const char *xDesc{reinterpret_cast<const char *>(&x)};
+  const char *xDescLast{xDesc + x.SizeInBytes()};
+  const char *yDesc{reinterpret_cast<const char *>(&x)};
+  const char *yDescLast{yDesc + y.SizeInBytes()};
+  std::int64_t xLeast, xMost, yLeast, yMost;
+  MaximalByteOffsetRange(x, xLeast, xMost);
+  MaximalByteOffsetRange(y, yLeast, yMost);
+  const char *xBase{x.OffsetElement()};
+  const char *yBase{y.OffsetElement()};
+  if (RangesOverlap(xDesc, xDescLast, yBase + yLeast, yBase + yMost) ||
+      RangesOverlap(yDesc, yDescLast, xBase + xLeast, xBase + xMost)) {
+    // A descriptor overlaps with the storage described by the other;
+    // this can arise when an allocatable or pointer component is
+    // being assigned to/from.
+    return true;
+  }
+  if (!RangesOverlap(
+          xBase + xLeast, xBase + xMost, yBase + yLeast, yBase + yMost)) {
+    return false; // no storage overlap
+  }
+  // TODO: check dimensions: if any is independent, return false
+  return true;
+}
+
 static void DoScalarDefinedAssignment(const Descriptor &to,
     const Descriptor &from, const typeInfo::SpecialBinding &special) {
   bool toIsDesc{special.IsArgDescriptor(0)};
@@ -41,8 +175,10 @@ static void DoScalarDefinedAssignment(const Descriptor &to,
 }
 
 static void DoElementalDefinedAssignment(const Descriptor &to,
-    const Descriptor &from, const typeInfo::SpecialBinding &special,
-    std::size_t toElements, SubscriptValue toAt[], SubscriptValue fromAt[]) {
+    const Descriptor &from, const typeInfo::SpecialBinding &special) {
+  SubscriptValue toAt[maxRank], fromAt[maxRank];
+  to.GetLowerBounds(toAt);
+  from.GetLowerBounds(fromAt);
   StaticDescriptor<maxRank, true, 8 /*?*/> statDesc[2];
   Descriptor &toElementDesc{statDesc[0].descriptor()};
   Descriptor &fromElementDesc{statDesc[1].descriptor()};
@@ -52,66 +188,92 @@ static void DoElementalDefinedAssignment(const Descriptor &to,
   fromElementDesc = from;
   fromElementDesc.raw().attribute = CFI_attribute_pointer;
   fromElementDesc.raw().rank = 0;
-  for (std::size_t j{0}; j < toElements;
-       ++j, to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
+  for (std::size_t toElements{to.Elements()}; toElements-- > 0;
+       to.IncrementSubscripts(toAt), from.IncrementSubscripts(fromAt)) {
     toElementDesc.set_base_addr(to.Element<char>(toAt));
     fromElementDesc.set_base_addr(from.Element<char>(fromAt));
     DoScalarDefinedAssignment(toElementDesc, fromElementDesc, special);
   }
 }
 
-// Assigns one object to another via intrinsic assignment (F'2018 10.2.1.3) or
-// type-bound (only!) defined assignment (10.2.1.4), as appropriate.  Performs
-// finalization, scalar expansion, & allocatable (re)allocation as needed.
-// Does not perform intrinsic assignment implicit type conversion.  Both
-// descriptors must be initialized.  Recurses as needed to handle components.
-// Do not perform allocatable reallocation if \p skipRealloc is true, which is
-// used for allocate statement with source specifier.
+// Common implementation of assignments, both intrinsic assignments and
+// those cases of polymorphic user-defined ASSIGNMENT(=) TBPs that could not
+// be resolved in semantics.  Most assignment statements do not need any
+// of the capabilities of this function -- but when the LHS is allocatable,
+// the type might have a user-defined ASSIGNMENT(=), or the type might be
+// finalizable, this function should be used.
 static void Assign(Descriptor &to, const Descriptor &from,
-    Terminator &terminator, bool skipRealloc = false,
-    bool skipFinalization = false) {
+    Terminator &terminator, bool maybeReallocate, bool needFinalization,
+    bool canBeDefinedAssignment, bool componentCanBeDefinedAssignment) {
+  bool mustDeallocateLHS{
+      maybeReallocate && MustDeallocateLHS(to, from, terminator)};
   DescriptorAddendum *toAddendum{to.Addendum()};
   const typeInfo::DerivedType *toDerived{
       toAddendum ? toAddendum->derivedType() : nullptr};
-  const DescriptorAddendum *fromAddendum{from.Addendum()};
-  const typeInfo::DerivedType *fromDerived{
-      fromAddendum ? fromAddendum->derivedType() : nullptr};
-  bool wasJustAllocated{false};
-  if (to.IsAllocatable()) {
-    std::size_t lenParms{fromDerived ? fromDerived->LenParameters() : 0};
-    if (to.IsAllocated() && !skipRealloc) {
-      // Top-level assignments to allocatable variables (*not* components)
-      // may first deallocate existing content if there's about to be a
-      // change in type or shape; see F'2018 10.2.1.3(3).
-      bool deallocate{false};
-      if (to.type() != from.type()) {
-        deallocate = true;
-      } else if (toDerived != fromDerived) {
-        deallocate = true;
-      } else {
-        if (toAddendum) {
-          // Distinct LEN parameters? Deallocate
-          for (std::size_t j{0}; j < lenParms; ++j) {
-            if (toAddendum->LenParameterValue(j) !=
-                fromAddendum->LenParameterValue(j)) {
-              deallocate = true;
-              break;
-            }
-          }
-        }
-        if (from.rank() > 0) {
-          // Distinct shape? Deallocate
-          int rank{to.rank()};
-          for (int j{0}; j < rank; ++j) {
-            if (to.GetDimension(j).Extent() != from.GetDimension(j).Extent()) {
-              deallocate = true;
-              break;
-            }
+  if (canBeDefinedAssignment && toDerived) {
+    needFinalization &= !toDerived->noFinalizationNeeded();
+    // Check for a user-defined assignment type-bound procedure;
+    // see 10.2.1.4-5.  A user-defined assignment TBP defines all of
+    // the semantics, including allocatable (re)allocation and any
+    // finalization.
+    if (to.rank() == 0) {
+      if (const auto *special{toDerived->FindSpecialBinding(
+              typeInfo::SpecialBinding::Which::ScalarAssignment)}) {
+        return DoScalarDefinedAssignment(to, from, *special);
+      }
+    }
+    if (const auto *special{toDerived->FindSpecialBinding(
+            typeInfo::SpecialBinding::Which::ElementalAssignment)}) {
+      return DoElementalDefinedAssignment(to, from, *special);
+    }
+  }
+  bool isSimpleMemmove{!toDerived && to.rank() == from.rank() &&
+      to.IsContiguous() && from.IsContiguous()};
+  StaticDescriptor<maxRank, true, 10 /*?*/> deferredDeallocStatDesc;
+  Descriptor *deferDeallocation{nullptr};
+  if (MayAlias(to, from)) {
+    if (mustDeallocateLHS) {
+      deferDeallocation = &deferredDeallocStatDesc.descriptor();
+      std::memcpy(deferDeallocation, &to, to.SizeInBytes());
+      to.set_base_addr(nullptr);
+    } else if (!isSimpleMemmove) {
+      // Handle LHS/RHS aliasing by copying RHS into a temp, then
+      // recursively assigning from that temp.
+      auto descBytes{from.SizeInBytes()};
+      StaticDescriptor<maxRank, true, 16> staticDesc;
+      Descriptor &newFrom{staticDesc.descriptor()};
+      std::memcpy(&newFrom, &from, descBytes);
+      auto stat{ReturnError(terminator, newFrom.Allocate())};
+      if (stat == StatOk) {
+        char *toAt{newFrom.OffsetElement()};
+        std::size_t fromElements{from.Elements()};
+        std::size_t elementBytes{from.ElementBytes()};
+        if (from.IsContiguous()) {
+          std::memcpy(toAt, from.OffsetElement(), fromElements * elementBytes);
+        } else {
+          SubscriptValue fromAt[maxRank];
+          for (from.GetLowerBounds(fromAt); fromElements-- > 0;
+               toAt += elementBytes, from.IncrementSubscripts(fromAt)) {
+            std::memcpy(toAt, from.Element<char>(fromAt), elementBytes);
           }
         }
+        Assign(to, newFrom, terminator, /*maybeReallocate=*/false,
+            needFinalization, false, componentCanBeDefinedAssignment);
+        newFrom.Deallocate();
       }
-      if (deallocate) {
-        to.Destroy(true /*finalize*/);
+      return;
+    }
+  }
+  if (to.IsAllocatable()) {
+    if (mustDeallocateLHS) {
+      if (deferDeallocation) {
+        if (needFinalization && toDerived) {
+          Finalize(to, *toDerived);
+          needFinalization = false;
+        }
+      } else {
+        to.Destroy(/*finalize=*/needFinalization);
+        needFinalization = false;
       }
     } else if (to.rank() != from.rank()) {
       terminator.Crash("Assign: mismatched ranks (%d != %d) in assignment to "
@@ -119,31 +281,10 @@ static void Assign(Descriptor &to, const Descriptor &from,
           to.rank(), from.rank());
     }
     if (!to.IsAllocated()) {
-      to.raw().type = from.raw().type;
-      to.raw().elem_len = from.ElementBytes();
-      if (toAddendum) {
-        toDerived = fromDerived;
-        toAddendum->set_derivedType(toDerived);
-        for (std::size_t j{0}; j < lenParms; ++j) {
-          toAddendum->SetLenParameterValue(
-              j, fromAddendum->LenParameterValue(j));
-        }
-      }
-      // subtle: leave bounds in place when "from" is scalar (10.2.1.3(3))
-      int rank{from.rank()};
-      auto stride{static_cast<SubscriptValue>(to.ElementBytes())};
-      for (int j{0}; j < rank; ++j) {
-        auto &toDim{to.GetDimension(j)};
-        const auto &fromDim{from.GetDimension(j)};
-        toDim.SetBounds(fromDim.LowerBound(), fromDim.UpperBound());
-        toDim.SetByteStride(stride);
-        stride *= toDim.Extent();
+      if (AllocateAssignmentLHS(to, from, terminator) != StatOk) {
+        return;
       }
-      ReturnError(terminator, to.Allocate());
-      if (fromDerived && !fromDerived->noInitializationNeeded()) {
-        ReturnError(terminator, Initialize(to, *toDerived, terminator));
-      }
-      wasJustAllocated = true;
+      needFinalization = false;
     }
   }
   SubscriptValue toAt[maxRank];
@@ -169,24 +310,11 @@ static void Assign(Descriptor &to, const Descriptor &from,
         "Assign: mismatching element sizes (to %zd bytes != from %zd bytes)",
         elementBytes, from.ElementBytes());
   }
-  if (toDerived) { // Derived type assignment
-    // Check for defined assignment type-bound procedures (10.2.1.4-5)
-    if (to.rank() == 0) {
-      if (const auto *special{toDerived->FindSpecialBinding(
-              typeInfo::SpecialBinding::Which::ScalarAssignment)}) {
-        return DoScalarDefinedAssignment(to, from, *special);
-      }
-    }
-    if (const auto *special{toDerived->FindSpecialBinding(
-            typeInfo::SpecialBinding::Which::ElementalAssignment)}) {
-      return DoElementalDefinedAssignment(
-          to, from, *special, toElements, toAt, fromAt);
-    }
+  if (toDerived) {
     // Derived type intrinsic assignment, which is componentwise and elementwise
     // for all components, including parent components (10.2.1.2-3).
     // The target is first finalized if still necessary (7.5.6.3(1))
-    if (!wasJustAllocated && !toDerived->noFinalizationNeeded() &&
-        !skipFinalization) {
+    if (needFinalization) {
       Finalize(to, *toDerived);
     }
     // Copy the data components (incl. the parent) first.
@@ -207,8 +335,10 @@ static void Assign(Descriptor &to, const Descriptor &from,
             comp.CreatePointerDescriptor(toCompDesc, to, terminator, toAt);
             comp.CreatePointerDescriptor(
                 fromCompDesc, from, terminator, fromAt);
-            Assign(toCompDesc, fromCompDesc, terminator, /*skipRealloc=*/false,
-                /*skipFinalization=*/true);
+            Assign(toCompDesc, fromCompDesc, terminator,
+                /*maybeReallocate=*/true,
+                /*needFinalization=*/false, componentCanBeDefinedAssignment,
+                componentCanBeDefinedAssignment);
           }
         } else { // Component has intrinsic type; simply copy raw bytes
           std::size_t componentByteSize{comp.SizeInBytes(to)};
@@ -253,7 +383,9 @@ static void Assign(Descriptor &to, const Descriptor &from,
               continue; // F'2018 10.2.1.3(13)(2)
             }
           }
-          Assign(*toDesc, *fromDesc, terminator, /*skipRealloc=*/false);
+          Assign(*toDesc, *fromDesc, terminator, /*maybeReallocate=*/true,
+              /*needFinalization=*/false, componentCanBeDefinedAssignment,
+              componentCanBeDefinedAssignment);
         }
         break;
       }
@@ -272,8 +404,7 @@ static void Assign(Descriptor &to, const Descriptor &from,
       }
     }
   } else { // intrinsic type, intrinsic assignment
-    if (to.rank() == from.rank() && to.IsContiguous() && from.IsContiguous()) {
-      // Everything is contiguous; do a single big copy
+    if (isSimpleMemmove) {
       std::memmove(
           to.raw().base_addr, from.raw().base_addr, toElements * elementBytes);
     } else { // elemental copies
@@ -284,6 +415,9 @@ static void Assign(Descriptor &to, const Descriptor &from,
       }
     }
   }
+  if (deferDeallocation) {
+    deferDeallocation->Destroy();
+  }
 }
 
 void DoFromSourceAssign(
@@ -300,7 +434,8 @@ void DoFromSourceAssign(
            alloc.IncrementSubscripts(allocAt)) {
         Descriptor allocElement{*Descriptor::Create(*allocDerived,
             reinterpret_cast<void *>(alloc.Element<char>(allocAt)), 0)};
-        Assign(allocElement, source, terminator, /*skipRealloc=*/true);
+        Assign(allocElement, source, terminator, /*maybeReallocate=*/false,
+            /*needFinalization=*/false, false, false);
       }
     } else { // intrinsic type
       for (std::size_t n{alloc.Elements()}; n-- > 0;
@@ -310,7 +445,8 @@ void DoFromSourceAssign(
       }
     }
   } else {
-    Assign(alloc, source, terminator, /*skipRealloc=*/true);
+    Assign(alloc, source, terminator, /*maybeReallocate=*/false,
+        /*needFinalization=*/false, false, false);
   }
 }
 
@@ -318,7 +454,22 @@ extern "C" {
 void RTNAME(Assign)(Descriptor &to, const Descriptor &from,
     const char *sourceFile, int sourceLine) {
   Terminator terminator{sourceFile, sourceLine};
-  Assign(to, from, terminator);
+  // All top-level defined assignments can be recognized in semantics and
+  // will have been already been converted to calls, so don't check for
+  // defined assignment apart from components.
+  Assign(to, from, terminator, /*maybeReallocate=*/true,
+      /*needFinalization=*/true,
+      /*canBeDefinedAssignment=*/false,
+      /*componentCanBeDefinedAssignment=*/true);
+}
+
+void RTNAME(AssignTemporary)(Descriptor &to, const Descriptor &from,
+    const char *sourceFile, int sourceLine) {
+  Terminator terminator{sourceFile, sourceLine};
+  Assign(to, from, terminator, /*maybeReallocate=*/false,
+      /*needFinalization=*/false,
+      /*canBeDefinedAssignment=*/false,
+      /*componentCanBeDefinedAssignment=*/false);
 }
 
 } // extern "C"

diff  --git a/flang/runtime/derived-api.cpp b/flang/runtime/derived-api.cpp
index 4eb9a09be07c4..806a76ac95753 100644
--- a/flang/runtime/derived-api.cpp
+++ b/flang/runtime/derived-api.cpp
@@ -144,7 +144,5 @@ bool RTNAME(ExtendsTypeOf)(const Descriptor &a, const Descriptor &mold) {
   return false;
 }
 
-// TODO: Assign()
-
 } // extern "C"
 } // namespace Fortran::runtime

diff  --git a/flang/runtime/pointer.cpp b/flang/runtime/pointer.cpp
index 7bfe7756597b2..bae9e0327d9a0 100644
--- a/flang/runtime/pointer.cpp
+++ b/flang/runtime/pointer.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Runtime/pointer.h"
-#include "assign.h"
+#include "assign-impl.h"
 #include "derived.h"
 #include "stat.h"
 #include "terminator.h"


        


More information about the flang-commits mailing list