[flang-commits] [flang] [flang] Copy named-constant arrays passed through implicit interfaces (PR #228582)

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Fri Oct 2 15:01:51 PDT 2026


https://github.com/eugeneepshteyn updated https://github.com/llvm/llvm-project/pull/228582

>From 5263a14c3563fd51c2f73f7debe23ada1437d058 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 2 Oct 2026 13:36:15 -0700
Subject: [PATCH 1/2] [flang] Copy named-constant arrays passed through
 implicit interfaces

Since #222986, a named constant's own storage can be the actual argument.
A named constant is not definable, so a conforming procedure never
defines the dummy argument, but programs do pass named constants through
implicit interfaces to procedures that define the dummy argument, and
the dummy's characteristics are not known at such call sites. These
programs used to work, because the named constant was passed in a
temporary, and now crash writing into read-only memory.

Pass a whole named-constant array or a section of one in a temporary
again when the procedure is called through an implicit interface,
including an external procedure defined in the same compilation unit,
whose calls are prepared from the actual arguments. Array elements keep
the named constant's storage, since they may start a sequence
association that a temporary of the element alone would break, and
calls through explicit interfaces are unchanged.
---
 flang/include/flang/Lower/CallInterface.h     |  9 ++
 flang/lib/Lower/CallInterface.cpp             | 25 +++++-
 flang/lib/Lower/ConvertCall.cpp               | 23 ++++-
 .../Lower/HLFIR/call-parameter-array-arg.f90  |  8 +-
 .../call-parameter-implicit-interface.f90     | 88 +++++++++++++++++++
 5 files changed, 146 insertions(+), 7 deletions(-)
 create mode 100644 flang/test/Lower/HLFIR/call-parameter-implicit-interface.f90

diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index df0c73bd91eef..525d22e03a825 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -460,6 +460,15 @@ class CalleeInterface : public CallInterface<CalleeInterface> {
   Fortran::lower::pft::FunctionLikeUnit &funit;
 };
 
+/// Is the procedure called through an implicit interface at \p procRef?  This
+/// includes calls to an external procedure defined in the same compilation
+/// unit that can be called through an implicit interface: such calls are
+/// prepared according to the actual arguments, like calls to procedures whose
+/// definition is not visible.
+bool isCalledThroughImplicitInterface(
+    const Fortran::evaluate::ProcedureRef &procRef,
+    Fortran::evaluate::FoldingContext &foldingContext);
+
 /// Translate a procedure characteristics to an mlir::FunctionType signature.
 mlir::FunctionType
 translateSignature(const Fortran::evaluate::ProcedureDesignator &,
diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index 2bfe94559cd88..d246f6eb448cb 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -279,6 +279,27 @@ static bool isExternalDefinedInSameCompilationUnit(
   return false;
 }
 
+/// Is a call to \p proc, whose characteristics are \p characteristic,
+/// prepared according to the actual arguments rather than to the dummy
+/// arguments of the procedure (see CallerInterface::characterize)?
+static bool isPreparedFromActualArguments(
+    const Fortran::evaluate::characteristics::Procedure &characteristic,
+    const Fortran::evaluate::ProcedureDesignator &proc) {
+  return !characteristic.HasExplicitInterface() ||
+         (isExternalDefinedInSameCompilationUnit(proc) &&
+          characteristic.CanBeCalledViaImplicitInterface());
+}
+
+bool Fortran::lower::isCalledThroughImplicitInterface(
+    const Fortran::evaluate::ProcedureRef &procRef,
+    Fortran::evaluate::FoldingContext &foldingContext) {
+  std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
+      Fortran::evaluate::characteristics::Procedure::Characterize(
+          procRef.proc(), foldingContext, /*emitError=*/false);
+  return !characteristic ||
+         isPreparedFromActualArguments(*characteristic, procRef.proc());
+}
+
 Fortran::evaluate::characteristics::Procedure
 Fortran::lower::CallerInterface::characterize() const {
   Fortran::evaluate::FoldingContext &foldingContext =
@@ -290,9 +311,7 @@ Fortran::lower::CallerInterface::characterize() const {
   // The characteristic may not contain the argument characteristic if the
   // ProcedureDesignator has no interface, or may mismatch in case of implicit
   // interface.
-  if (!characteristic->HasExplicitInterface() ||
-      (isExternalDefinedInSameCompilationUnit(procRef.proc()) &&
-       characteristic->CanBeCalledViaImplicitInterface())) {
+  if (isPreparedFromActualArguments(*characteristic, procRef.proc())) {
     // In HLFIR lowering, calls to subprogram with implicit interfaces are
     // always prepared according to the actual arguments. This is to support
     // cases where the implicit interfaces are "abused" in old and not so old
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 6cb12b8208203..8568dd3e6b9c8 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1038,6 +1038,14 @@ struct CallContext {
     return false;
   }
 
+  /// Is the procedure called through an implicit interface?
+  bool calleeHasImplicitInterface() {
+    if (!implicitInterface)
+      implicitInterface = Fortran::lower::isCalledThroughImplicitInterface(
+          procRef, converter.getFoldingContext());
+    return *implicitInterface;
+  }
+
   const Fortran::evaluate::ProcedureRef &procRef;
   Fortran::lower::AbstractConverter &converter;
   Fortran::lower::SymMap &symMap;
@@ -1045,6 +1053,9 @@ struct CallContext {
   std::optional<mlir::Type> resultType;
   mlir::Location loc;
   bool doCopyIn;
+
+private:
+  std::optional<bool> implicitInterface;
 };
 
 using ExvAndCleanup =
@@ -1511,8 +1522,18 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
         // (genCopyIn requires a descriptor) and because compiler-generated
         // copy-out must never target the read-only storage of a
         // non-definable actual argument.
+        // A named constant is not definable, so a conforming procedure never
+        // defines a dummy argument associated with one.  Still, programs pass
+        // named constants through implicit interfaces to procedures that do
+        // define the dummy argument, and the dummy's characteristics are not
+        // known at such call sites.  Copy a whole named-constant array or a
+        // section of one in that case too, as was done before named constants
+        // were associated with their storage.  An array element is not
+        // copied: it may start a sequence association (F'2023 15.5.2.12),
+        // which a temporary holding only the element would break.
         (isParameterObjectOrSubObject(entity) &&
-         (suggestCopyIn || suggestCopyOut))) {
+         (suggestCopyIn || suggestCopyOut ||
+          (entity.isArray() && callContext.calleeHasImplicitInterface())))) {
       // Make a copy in a temporary.
       auto copy = hlfir::AsExprOp::create(builder, loc, entity);
       mlir::Type storageType = entity.getType();
diff --git a/flang/test/Lower/HLFIR/call-parameter-array-arg.f90 b/flang/test/Lower/HLFIR/call-parameter-array-arg.f90
index db89bd9462655..ea2cb462a8195 100644
--- a/flang/test/Lower/HLFIR/call-parameter-array-arg.f90
+++ b/flang/test/Lower/HLFIR/call-parameter-array-arg.f90
@@ -106,11 +106,13 @@ subroutine value_dummy()
   call byval(gp)
 end subroutine
 
-! Implicit interface: the whole named-constant array's address is passed
-! directly (known contiguous).
+! Implicit interface: the procedure may define the dummy argument, so the
+! whole named-constant array is passed in a temporary (see
+! call-parameter-implicit-interface.f90).
 ! CHECK-LABEL: func.func @_QPimplicit_iface
 ! CHECK: fir.address_of(@_QMmECgp)
-! CHECK-NOT: hlfir.as_expr
+! CHECK: hlfir.as_expr
+! CHECK: hlfir.associate
 ! CHECK: fir.call @_QPext_sub
 subroutine implicit_iface()
   use m
diff --git a/flang/test/Lower/HLFIR/call-parameter-implicit-interface.f90 b/flang/test/Lower/HLFIR/call-parameter-implicit-interface.f90
new file mode 100644
index 0000000000000..4a968af3273c0
--- /dev/null
+++ b/flang/test/Lower/HLFIR/call-parameter-implicit-interface.f90
@@ -0,0 +1,88 @@
+! RUN: %flang_fc1 -emit-hlfir %s -o - 2>/dev/null | FileCheck %s
+
+! Test that named-constant (PARAMETER) arrays and sections passed through an
+! implicit interface are copied into a temporary, since the procedure may
+! define the dummy argument, while array elements keep the named constant's
+! storage (they may start a sequence association) and calls through an
+! explicit interface still pass the storage directly.
+
+module m
+  implicit none
+contains
+  subroutine expl(x)
+    integer :: x(4)
+  end subroutine
+end module
+
+! Whole named-constant array to an external without an interface.
+! CHECK-LABEL: func.func @_QPwhole_implicit
+! CHECK:         %[[ADDR:.*]] = fir.address_of(@_QFwhole_implicitECc)
+! CHECK:         %[[DECL:.*]]:2 = hlfir.declare %[[ADDR]]
+! CHECK:         %[[EXPR:.*]] = hlfir.as_expr %[[DECL]]#0
+! CHECK:         %[[TMP:.*]]:3 = hlfir.associate %[[EXPR]]
+! CHECK:         fir.call @_QPext(%[[TMP]]#0)
+! CHECK:         hlfir.end_associate %[[TMP]]#1, %[[TMP]]#2
+subroutine whole_implicit()
+  integer, parameter :: c(4) = [1, 2, 3, 4]
+  call ext(c)
+end subroutine
+
+! Contiguous section of a named constant to an external without an interface.
+! CHECK-LABEL: func.func @_QPsection_implicit
+! CHECK:         %[[SEC:.*]] = hlfir.designate %{{.*}} (%{{.*}}:%{{.*}}:%{{.*}})
+! CHECK:         %[[EXPR:.*]] = hlfir.as_expr %[[SEC]]
+! CHECK:         %[[TMP:.*]]:3 = hlfir.associate %[[EXPR]]
+! CHECK:         %[[CAST:.*]] = fir.convert %[[TMP]]#0
+! CHECK:         fir.call @_QPext(%[[CAST]])
+! CHECK:         hlfir.end_associate %[[TMP]]#1, %[[TMP]]#2
+subroutine section_implicit()
+  integer, parameter :: c(4) = [1, 2, 3, 4]
+  call ext(c(2:3))
+end subroutine
+
+! Whole named-constant array to an external defined in this file but called
+! without an interface: the call is prepared from the actual arguments, as for
+! an external defined elsewhere.
+! CHECK-LABEL: func.func @_QPwhole_same_file
+! CHECK:         %[[EXPR:.*]] = hlfir.as_expr
+! CHECK:         %[[TMP:.*]]:3 = hlfir.associate %[[EXPR]]
+! CHECK:         fir.call @_QPdefine_it(
+subroutine whole_same_file()
+  integer, parameter :: c(4) = [1, 2, 3, 4]
+  call define_it(c)
+end subroutine
+subroutine define_it(x)
+  integer, intent(out) :: x(4)
+  x = 0
+end subroutine
+
+! Named-constant array element to an external without an interface: the
+! element's address is passed, so that a sequence association sees the rest
+! of the named constant.
+! CHECK-LABEL: func.func @_QPelement_implicit
+! CHECK:         %[[ADDR:.*]] = fir.address_of(@_QFelement_implicitECc)
+! CHECK:         %[[DECL:.*]]:2 = hlfir.declare %[[ADDR]]
+! CHECK:         %[[ELT:.*]] = hlfir.designate %[[DECL]]#0 (%{{.*}})
+! CHECK-NOT:     hlfir.as_expr
+! CHECK-NOT:     hlfir.associate
+! CHECK:         %[[CAST:.*]] = fir.convert %[[ELT]]
+! CHECK-NOT:     hlfir.associate
+! CHECK:         fir.call @_QPext(%[[CAST]])
+subroutine element_implicit()
+  integer, parameter :: c(4) = [1, 2, 3, 4]
+  call ext(c(2))
+end subroutine
+
+! Whole named-constant array through an explicit interface: the named
+! constant's storage is passed directly.
+! CHECK-LABEL: func.func @_QPwhole_explicit
+! CHECK:         %[[ADDR:.*]] = fir.address_of(@_QFwhole_explicitECc)
+! CHECK:         %[[DECL:.*]]:2 = hlfir.declare %[[ADDR]]
+! CHECK-NOT:     hlfir.as_expr
+! CHECK-NOT:     hlfir.associate
+! CHECK:         fir.call @_QMmPexpl(%[[DECL]]#0)
+subroutine whole_explicit()
+  use m
+  integer, parameter :: c(4) = [1, 2, 3, 4]
+  call expl(c)
+end subroutine

>From 4ef803e0ed4a821df2b4bf6226a8dd443f0b4066 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 2 Oct 2026 15:00:47 -0700
Subject: [PATCH 2/2] [flang] Decide the implicit-interface named-constant copy
 in ActualArgNeedsCopy

Move the decision to copy a named-constant array passed through an
implicit interface into ActualArgNeedsCopy, which already distinguishes
implicit interfaces, so that all argument copy decisions are made in one
place. ActualArgNeedsCopy cannot tell on its own that lowering prepares a
call from the actual arguments, since lowering then derives the dummy
characteristics from the actual arguments, so it takes that information
as a new parameter. CallerInterface records it when it characterizes the
call, which avoids characterizing the procedure again.
---
 .../include/flang/Evaluate/check-expression.h |  6 +++-
 flang/include/flang/Lower/CallInterface.h     | 19 ++++++-----
 flang/lib/Evaluate/check-expression.cpp       | 13 ++++++-
 flang/lib/Lower/CallInterface.cpp             | 27 +++------------
 flang/lib/Lower/ConvertCall.cpp               | 34 ++++++-------------
 5 files changed, 42 insertions(+), 57 deletions(-)

diff --git a/flang/include/flang/Evaluate/check-expression.h b/flang/include/flang/Evaluate/check-expression.h
index 3003179699faa..bc14b316f507b 100644
--- a/flang/include/flang/Evaluate/check-expression.h
+++ b/flang/include/flang/Evaluate/check-expression.h
@@ -187,8 +187,12 @@ bool IsNamedConstantDesignator(const Expr<SomeType> &);
 bool AnyNamedConstantActualArguments(const ActualArguments &);
 void FoldNamedConstantActualArguments(FoldingContext &, ActualArguments &);
 
+// \p calledThroughImplicitInterface tells that the procedure is called
+// through an implicit interface even though \p dummy is not null (e.g., when
+// the dummy characteristics were derived from the actual argument).
 std::optional<bool> ActualArgNeedsCopy(const ActualArgument *,
-    const characteristics::DummyArgument *, FoldingContext &, bool forCopyOut);
+    const characteristics::DummyArgument *, FoldingContext &, bool forCopyOut,
+    bool calledThroughImplicitInterface = false);
 
 // Scan expressions and note uses of values of symbols.
 semantics::UnorderedSymbolSet CollectUsedSymbolValues(
diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index 525d22e03a825..482b44ec59729 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -293,6 +293,14 @@ class CallerInterface : public CallInterface<CallerInterface> {
   mlir::Location getCalleeLocation() const;
   Fortran::evaluate::characteristics::Procedure characterize() const;
 
+  /// Is the procedure called through an implicit interface? Such calls,
+  /// including calls to an external procedure defined in the same
+  /// compilation unit that can be called through an implicit interface, are
+  /// prepared according to the actual arguments (see characterize()).
+  bool isCalledThroughImplicitInterface() const {
+    return calledThroughImplicitInterface;
+  }
+
   const Fortran::evaluate::ProcedureRef &getCallDescription() const {
     return procRef;
   }
@@ -410,6 +418,8 @@ class CallerInterface : public CallInterface<CallerInterface> {
   const Fortran::evaluate::ProcedureRef &procRef;
   llvm::SmallVector<mlir::Value> actualInputs;
   std::optional<mlir::Value> originalPassArg;
+  /// Set by characterize().
+  mutable bool calledThroughImplicitInterface = false;
 };
 
 //===----------------------------------------------------------------------===//
@@ -460,15 +470,6 @@ class CalleeInterface : public CallInterface<CalleeInterface> {
   Fortran::lower::pft::FunctionLikeUnit &funit;
 };
 
-/// Is the procedure called through an implicit interface at \p procRef?  This
-/// includes calls to an external procedure defined in the same compilation
-/// unit that can be called through an implicit interface: such calls are
-/// prepared according to the actual arguments, like calls to procedures whose
-/// definition is not visible.
-bool isCalledThroughImplicitInterface(
-    const Fortran::evaluate::ProcedureRef &procRef,
-    Fortran::evaluate::FoldingContext &foldingContext);
-
 /// Translate a procedure characteristics to an mlir::FunctionType signature.
 mlir::FunctionType
 translateSignature(const Fortran::evaluate::ProcedureDesignator &,
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index db4cf137b5eb0..5c22159dd1c6d 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1679,7 +1679,7 @@ void FoldNamedConstantActualArguments(
 
 std::optional<bool> ActualArgNeedsCopy(const ActualArgument *actual,
     const characteristics::DummyArgument *dummy, FoldingContext &fc,
-    bool forCopyOut) {
+    bool forCopyOut, bool calledThroughImplicitInterface) {
   if (!actual) {
     return std::nullopt;
   }
@@ -1704,6 +1704,17 @@ std::optional<bool> ActualArgNeedsCopy(const ActualArgument *actual,
     if (forCopyOut) {
       return false;
     }
+    // A conforming procedure never defines a dummy argument associated with
+    // a named constant.  Still, programs pass named constants through
+    // implicit interfaces to procedures that do define the dummy argument,
+    // and the dummy's characteristics are not known at such call sites:
+    // copy a whole named-constant array or a section of one into a
+    // temporary.  An array element is not copied: it may start a sequence
+    // association (F'2023 15.5.2.12), which a temporary holding only the
+    // element would break.
+    if ((calledThroughImplicitInterface || !dummyObj) && actual->Rank() > 0) {
+      return true;
+    }
   }
   if (forCopyOut) {
     // F2023 8.5.10 C846/p2/p6: a nonpointer INTENT(IN) dummy and its
diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index d246f6eb448cb..01f4053ef199c 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -279,27 +279,6 @@ static bool isExternalDefinedInSameCompilationUnit(
   return false;
 }
 
-/// Is a call to \p proc, whose characteristics are \p characteristic,
-/// prepared according to the actual arguments rather than to the dummy
-/// arguments of the procedure (see CallerInterface::characterize)?
-static bool isPreparedFromActualArguments(
-    const Fortran::evaluate::characteristics::Procedure &characteristic,
-    const Fortran::evaluate::ProcedureDesignator &proc) {
-  return !characteristic.HasExplicitInterface() ||
-         (isExternalDefinedInSameCompilationUnit(proc) &&
-          characteristic.CanBeCalledViaImplicitInterface());
-}
-
-bool Fortran::lower::isCalledThroughImplicitInterface(
-    const Fortran::evaluate::ProcedureRef &procRef,
-    Fortran::evaluate::FoldingContext &foldingContext) {
-  std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
-      Fortran::evaluate::characteristics::Procedure::Characterize(
-          procRef.proc(), foldingContext, /*emitError=*/false);
-  return !characteristic ||
-         isPreparedFromActualArguments(*characteristic, procRef.proc());
-}
-
 Fortran::evaluate::characteristics::Procedure
 Fortran::lower::CallerInterface::characterize() const {
   Fortran::evaluate::FoldingContext &foldingContext =
@@ -311,7 +290,11 @@ Fortran::lower::CallerInterface::characterize() const {
   // The characteristic may not contain the argument characteristic if the
   // ProcedureDesignator has no interface, or may mismatch in case of implicit
   // interface.
-  if (isPreparedFromActualArguments(*characteristic, procRef.proc())) {
+  calledThroughImplicitInterface =
+      !characteristic->HasExplicitInterface() ||
+      (isExternalDefinedInSameCompilationUnit(procRef.proc()) &&
+       characteristic->CanBeCalledViaImplicitInterface());
+  if (calledThroughImplicitInterface) {
     // In HLFIR lowering, calls to subprogram with implicit interfaces are
     // always prepared according to the actual arguments. This is to support
     // cases where the implicit interfaces are "abused" in old and not so old
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 8568dd3e6b9c8..c3cac85284310 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1038,14 +1038,6 @@ struct CallContext {
     return false;
   }
 
-  /// Is the procedure called through an implicit interface?
-  bool calleeHasImplicitInterface() {
-    if (!implicitInterface)
-      implicitInterface = Fortran::lower::isCalledThroughImplicitInterface(
-          procRef, converter.getFoldingContext());
-    return *implicitInterface;
-  }
-
   const Fortran::evaluate::ProcedureRef &procRef;
   Fortran::lower::AbstractConverter &converter;
   Fortran::lower::SymMap &symMap;
@@ -1053,9 +1045,9 @@ struct CallContext {
   std::optional<mlir::Type> resultType;
   mlir::Location loc;
   bool doCopyIn;
-
-private:
-  std::optional<bool> implicitInterface;
+  /// Is the procedure called through an implicit interface? Set from the
+  /// CallerInterface when the user call arguments are prepared.
+  bool calledThroughImplicitInterface = false;
 };
 
 using ExvAndCleanup =
@@ -1411,11 +1403,13 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       callContext.converter.getFoldingContext()};
   const bool suggestCopyIn{Fortran::evaluate::ActualArgNeedsCopy(
                                arg.entity, arg.characteristics, foldingContext,
-                               /*forCopyOut=*/false)
+                               /*forCopyOut=*/false,
+                               callContext.calledThroughImplicitInterface)
                                .value_or(true)};
   const bool suggestCopyOut{Fortran::evaluate::ActualArgNeedsCopy(
                                 arg.entity, arg.characteristics, foldingContext,
-                                /*forCopyOut=*/true)
+                                /*forCopyOut=*/true,
+                                callContext.calledThroughImplicitInterface)
                                 .value_or(true)};
   bool mustDoCopyIn{false};
   bool mustDoCopyOut{false};
@@ -1522,18 +1516,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
         // (genCopyIn requires a descriptor) and because compiler-generated
         // copy-out must never target the read-only storage of a
         // non-definable actual argument.
-        // A named constant is not definable, so a conforming procedure never
-        // defines a dummy argument associated with one.  Still, programs pass
-        // named constants through implicit interfaces to procedures that do
-        // define the dummy argument, and the dummy's characteristics are not
-        // known at such call sites.  Copy a whole named-constant array or a
-        // section of one in that case too, as was done before named constants
-        // were associated with their storage.  An array element is not
-        // copied: it may start a sequence association (F'2023 15.5.2.12),
-        // which a temporary holding only the element would break.
         (isParameterObjectOrSubObject(entity) &&
-         (suggestCopyIn || suggestCopyOut ||
-          (entity.isArray() && callContext.calleeHasImplicitInterface())))) {
+         (suggestCopyIn || suggestCopyOut))) {
       // Make a copy in a temporary.
       auto copy = hlfir::AsExprOp::create(builder, loc, entity);
       mlir::Type storageType = entity.getType();
@@ -1763,6 +1747,8 @@ void prepareUserCallArguments(
   bool mustRemapActualToDummyDescriptors = false;
   fir::FirOpBuilder &builder = callContext.getBuilder();
   std::optional<unsigned> passArg = caller.getPassArgIndex();
+  callContext.calledThroughImplicitInterface =
+      caller.isCalledThroughImplicitInterface();
   int argIndex = -1;
   for (auto [preparedActual, arg] :
        llvm::zip(loweredActuals, caller.getPassedArguments())) {



More information about the flang-commits mailing list