[flang-commits] [flang] [flang][DRAFT-DEV][DO NOT REVIEW] Copy-in/Copy-out determination (PR #151426)

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Tue Aug 19 02:44:56 PDT 2025


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

>From 54c0158dcce9b5ebb6a3658298dedcda72b41f52 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 17:08:06 -0400
Subject: [PATCH 01/46] [flang][DRAFT] Copy-in/Copy-out determination

Plumbing/API for copy-in/copy-out
---
 flang/include/flang/Evaluate/call.h | 19 +++++++++++++++++--
 flang/lib/Evaluate/call.cpp         | 16 ++++++++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index 2a5929b873d74..ac11527e4ecaa 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -52,7 +52,7 @@ using SymbolRef = common::Reference<const Symbol>;
 
 class ActualArgument {
 public:
-  ENUM_CLASS(Attr, PassedObject, PercentVal, PercentRef);
+  ENUM_CLASS(Attr, PassedObject, PercentVal, PercentRef, CopyIn, CopyOut);
   using Attrs = common::EnumSet<Attr, Attr_enumSize>;
 
   // Dummy arguments that are TYPE(*) can be forwarded as actual arguments.
@@ -131,7 +131,6 @@ class ActualArgument {
     return *this;
   }
 
-  bool Matches(const characteristics::DummyArgument &) const;
   common::Intent dummyIntent() const { return dummyIntent_; }
   ActualArgument &set_dummyIntent(common::Intent intent) {
     dummyIntent_ = intent;
@@ -161,6 +160,20 @@ class ActualArgument {
     return *this;
   }
 
+  // This actual argument may need copy-in before the procedure call
+  bool mayNeedCopyIn() const { return attrs_.test(Attr::CopyIn); };
+  ActualArgument &set_mayNeedCopyIn() {
+    attrs_ = attrs_ + Attr::CopyIn;
+    return *this;
+  }
+
+  // This actual argument may need copy-out after the procedure call
+  bool mayNeedCopyOut() const { return attrs_.test(Attr::CopyOut); };
+  ActualArgument &set_mayNeedCopyOut() {
+    attrs_ = attrs_ + Attr::CopyOut;
+    return *this;
+  }
+
 private:
   // Subtlety: There is a distinction that must be maintained here between an
   // actual argument expression that is a variable and one that is not,
@@ -272,6 +285,8 @@ class ProcedureRef {
   bool operator==(const ProcedureRef &) const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
+  void DetermineCopyInOut();
+
 protected:
   ProcedureDesignator proc_;
   ActualArguments arguments_;
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index f77df92a7597a..1e582a516a694 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -13,6 +13,7 @@
 #include "flang/Evaluate/expression.h"
 #include "flang/Evaluate/tools.h"
 #include "flang/Semantics/symbol.h"
+#include "flang/Semantics/semantics.h"
 #include "flang/Support/Fortran.h"
 
 namespace Fortran::evaluate {
@@ -247,4 +248,19 @@ ProcedureRef::~ProcedureRef() {}
 
 void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
 
+void ProcedureRef::DetermineCopyInOut() {
+  if (!proc().GetSymbol()) {
+    return;
+  }
+  // Get folding context of the call site owner
+  FoldingContext &fc{proc_.GetSymbol()->owner().context().foldingContext()};
+  auto procInfo{characteristics::Procedure::Characterize(
+      proc(), fc, /*emitError=*/false)};
+  if (!procInfo) {
+    return;
+  }
+  // TODO: at this point have dummy arguments as procInfo->dummyArguments
+  // and have actual arguments via arguments_
+}
+
 } // namespace Fortran::evaluate

>From 808fb20c3e4d6dc80b7e3793fb28b4f7854b07f8 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 17:44:37 -0400
Subject: [PATCH 02/46] Call DetermineCopyInOut() from lowering

---
 flang/include/flang/Lower/CallInterface.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index 72bc9dd890a94..eca697d474c47 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -284,6 +284,9 @@ class CallerInterface : public CallInterface<CallerInterface> {
   CallerInterface(const Fortran::evaluate::ProcedureRef &p,
                   Fortran::lower::AbstractConverter &c)
       : CallInterface{c}, procRef{p} {
+    // Ensure that procRef gathers necessary information to determine the
+    // need for copy-in and copy-out
+    const_cast<Fortran::evaluate::ProcedureRef &>(procRef).DetermineCopyInOut();
     declare();
     mapPassedEntities();
     actualInputs.resize(getNumFIRArguments());

>From 28bc5bd95f83f131c4a905f69e402fc97a1b7e9d Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 18:20:23 -0400
Subject: [PATCH 03/46] clang-format

---
 flang/lib/Evaluate/call.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 1e582a516a694..0cfad4cfae17e 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -12,8 +12,8 @@
 #include "flang/Evaluate/check-expression.h"
 #include "flang/Evaluate/expression.h"
 #include "flang/Evaluate/tools.h"
-#include "flang/Semantics/symbol.h"
 #include "flang/Semantics/semantics.h"
+#include "flang/Semantics/symbol.h"
 #include "flang/Support/Fortran.h"
 
 namespace Fortran::evaluate {

>From f44b9459f6e2c0f60d6f66d00ec37d383befe021 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 19:54:01 -0400
Subject: [PATCH 04/46] DetermineCopyInOut() is now called from ProcedureRef
 constructor

---
 flang/include/flang/Evaluate/call.h       | 8 ++++++--
 flang/include/flang/Lower/CallInterface.h | 3 ---
 flang/lib/Evaluate/call.cpp               | 2 +-
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index ac11527e4ecaa..56338901b22bb 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -248,7 +248,11 @@ class ProcedureRef {
   ProcedureRef(ProcedureDesignator &&p, ActualArguments &&a,
       bool hasAlternateReturns = false)
       : proc_{std::move(p)}, arguments_{std::move(a)},
-        hasAlternateReturns_{hasAlternateReturns} {}
+        hasAlternateReturns_{hasAlternateReturns} {
+    // Gathers necessary information to determine the need for copy-in and
+    // copy-out
+    DetermineCopyInOut();
+  }
   ~ProcedureRef();
   static void Deleter(ProcedureRef *);
 
@@ -285,9 +289,9 @@ class ProcedureRef {
   bool operator==(const ProcedureRef &) const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
+protected:
   void DetermineCopyInOut();
 
-protected:
   ProcedureDesignator proc_;
   ActualArguments arguments_;
   Chevrons chevrons_;
diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index eca697d474c47..72bc9dd890a94 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -284,9 +284,6 @@ class CallerInterface : public CallInterface<CallerInterface> {
   CallerInterface(const Fortran::evaluate::ProcedureRef &p,
                   Fortran::lower::AbstractConverter &c)
       : CallInterface{c}, procRef{p} {
-    // Ensure that procRef gathers necessary information to determine the
-    // need for copy-in and copy-out
-    const_cast<Fortran::evaluate::ProcedureRef &>(procRef).DetermineCopyInOut();
     declare();
     mapPassedEntities();
     actualInputs.resize(getNumFIRArguments());
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 0cfad4cfae17e..c558335c16545 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -255,7 +255,7 @@ void ProcedureRef::DetermineCopyInOut() {
   // Get folding context of the call site owner
   FoldingContext &fc{proc_.GetSymbol()->owner().context().foldingContext()};
   auto procInfo{characteristics::Procedure::Characterize(
-      proc(), fc, /*emitError=*/false)};
+      proc_, fc, /*emitError=*/true)};
   if (!procInfo) {
     return;
   }

>From ffd65635da52fb2788056113b9fa3bdf2d430436 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 19:57:40 -0400
Subject: [PATCH 05/46] clang-format

---
 flang/lib/Evaluate/call.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index c558335c16545..5a94071d9bf20 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -254,8 +254,8 @@ void ProcedureRef::DetermineCopyInOut() {
   }
   // Get folding context of the call site owner
   FoldingContext &fc{proc_.GetSymbol()->owner().context().foldingContext()};
-  auto procInfo{characteristics::Procedure::Characterize(
-      proc_, fc, /*emitError=*/true)};
+  auto procInfo{
+      characteristics::Procedure::Characterize(proc_, fc, /*emitError=*/true)};
   if (!procInfo) {
     return;
   }

>From fb3a93c6da7f34110734f606e795533b83ff1562 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 22:39:44 -0400
Subject: [PATCH 06/46] Minor tweak

---
 flang/lib/Evaluate/call.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 5a94071d9bf20..605931d5d6dfb 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -249,7 +249,7 @@ ProcedureRef::~ProcedureRef() {}
 void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
 
 void ProcedureRef::DetermineCopyInOut() {
-  if (!proc().GetSymbol()) {
+  if (!proc_.GetSymbol()) {
     return;
   }
   // Get folding context of the call site owner

>From 527b2d7646ca11a4fa9921695b4efd360a9c5b54 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 31 Jul 2025 13:32:10 -0400
Subject: [PATCH 07/46] DetermineCopyInOut() is now called at ProcedureRef
 instantiation

---
 flang/include/flang/Evaluate/call.h | 5 +----
 flang/lib/Semantics/expression.cpp  | 1 +
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index 56338901b22bb..3d4f791380fba 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -249,9 +249,6 @@ class ProcedureRef {
       bool hasAlternateReturns = false)
       : proc_{std::move(p)}, arguments_{std::move(a)},
         hasAlternateReturns_{hasAlternateReturns} {
-    // Gathers necessary information to determine the need for copy-in and
-    // copy-out
-    DetermineCopyInOut();
   }
   ~ProcedureRef();
   static void Deleter(ProcedureRef *);
@@ -289,9 +286,9 @@ class ProcedureRef {
   bool operator==(const ProcedureRef &) const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
-protected:
   void DetermineCopyInOut();
 
+protected:
   ProcedureDesignator proc_;
   ActualArguments arguments_;
   Chevrons chevrons_;
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 92dbe0e5da11c..29917a570ddc5 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -3455,6 +3455,7 @@ void ExpressionAnalyzer::Analyze(const parser::CallStmt &callStmt) {
                 HasAlternateReturns(callee->arguments)},
             ProcedureRef::Deleter);
         DEREF(callStmt.typedCall.get()).set_chevrons(std::move(*chevrons));
+        DEREF(callStmt.typedCall.get()).DetermineCopyInOut();
         return;
       }
     }

>From 9c1755b248f4fe7b38547af5ee74563cfdcd6753 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 31 Jul 2025 22:01:17 -0400
Subject: [PATCH 08/46] Very rough beginnings of argument handling in
 DetermineCopyInOut()

---
 flang/lib/Evaluate/call.cpp | 61 +++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 605931d5d6dfb..6f604d2aed28b 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -248,6 +248,23 @@ ProcedureRef::~ProcedureRef() {}
 
 void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
 
+// We don't know the dummy argument info (e.g., procedure with implicit
+// interface
+static void DetermineCopyInOutArgument(
+    const characteristics::Procedure &procInfo, ActualArgument &actual) {
+
+  // Only check that actual argument is contiguous
+  // For non-contiguous, do copy-in
+}
+
+static void DetermineCopyInOutArgument(
+    const characteristics::Procedure &procInfo,
+    ActualArgument &actual, characteristics::DummyArgument &dummy) {
+
+  // TODO: assert? procInfo.HasExplicitInterface()
+
+}
+
 void ProcedureRef::DetermineCopyInOut() {
   if (!proc_.GetSymbol()) {
     return;
@@ -261,6 +278,50 @@ void ProcedureRef::DetermineCopyInOut() {
   }
   // TODO: at this point have dummy arguments as procInfo->dummyArguments
   // and have actual arguments via arguments_
+
+  // TODO: implicitly declared procedure may not have any information about
+  // its dummy args. Handle this case.
+
+  // Don't change anything about actual or dummy arguments, except for
+  // computing copy-in/copy-out information. If detect something wrong with
+  // the arguments, stop processing and let semantic analysis generate the
+  // error messages.
+  size_t index{0};
+  std::set<std::string> processedKeywords;
+  bool seenKeyword{false};
+  for (auto &actual : arguments_) {
+    if (!actual) {
+      continue;
+    }
+    if (index >= procInfo->dummyArguments.size()) {
+      // More actual arguments than dummy arguments. Semantic analysis will
+      // deal with the error.
+      return;
+    }
+    if (actual->keyword()) {
+      seenKeyword = true;
+      auto actualName = actual->keyword()->ToString();
+      if (processedKeywords.find(actualName) != processedKeywords.end()) {
+        // Actual arguments with duplicate keywords. Semantic analysis will
+        // deal with the error.
+        return;
+      }
+      else {
+        processedKeywords.insert(actualName);
+
+      }
+    }
+    else if (seenKeyword) {
+      // Non-keyword actual argument after have seen at least one keyword
+      // actual argument. Semantic analysis will deal with the error.
+      return;
+    }
+    else {
+      // Positional argument processing
+    }
+
+    ++index;
+  }
 }
 
 } // namespace Fortran::evaluate

>From 3198e9059218f6a1ecccd9e609e6eb9e425281e0 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 31 Jul 2025 22:20:18 -0400
Subject: [PATCH 09/46] More args handing in DetermineCopyInOut()

---
 flang/lib/Evaluate/call.cpp | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 6f604d2aed28b..be5db758e9717 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -276,12 +276,15 @@ void ProcedureRef::DetermineCopyInOut() {
   if (!procInfo) {
     return;
   }
-  // TODO: at this point have dummy arguments as procInfo->dummyArguments
-  // and have actual arguments via arguments_
-
-  // TODO: implicitly declared procedure may not have any information about
-  // its dummy args. Handle this case.
-
+  if (!procInfo->HasExplicitInterface()) {
+    for (auto &actual : arguments_) {
+      if (!actual) {
+        continue;
+      }
+      DetermineCopyInOutArgument(*procInfo, *actual);
+    }
+    return;
+  }
   // Don't change anything about actual or dummy arguments, except for
   // computing copy-in/copy-out information. If detect something wrong with
   // the arguments, stop processing and let semantic analysis generate the
@@ -308,7 +311,13 @@ void ProcedureRef::DetermineCopyInOut() {
       }
       else {
         processedKeywords.insert(actualName);
-
+        if (auto it = std::find_if(procInfo->dummyArguments.begin(),
+            procInfo->dummyArguments.end(),
+            [&](const characteristics::DummyArgument &dummy) {
+              return dummy.name == actualName;
+            }); it != procInfo->dummyArguments.end()) {
+          DetermineCopyInOutArgument(*procInfo, *actual, *it);
+        }
       }
     }
     else if (seenKeyword) {
@@ -318,6 +327,8 @@ void ProcedureRef::DetermineCopyInOut() {
     }
     else {
       // Positional argument processing
+      DetermineCopyInOutArgument(*procInfo, *actual,
+          procInfo->dummyArguments[index]);
     }
 
     ++index;

>From 0378a5f48279171a255c102c9c4b1401a2875161 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Sun, 3 Aug 2025 13:05:00 -0400
Subject: [PATCH 10/46] clang-format

---
 flang/lib/Evaluate/call.cpp | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index be5db758e9717..d8663d137e958 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -312,10 +312,11 @@ void ProcedureRef::DetermineCopyInOut() {
       else {
         processedKeywords.insert(actualName);
         if (auto it = std::find_if(procInfo->dummyArguments.begin(),
-            procInfo->dummyArguments.end(),
-            [&](const characteristics::DummyArgument &dummy) {
-              return dummy.name == actualName;
-            }); it != procInfo->dummyArguments.end()) {
+                procInfo->dummyArguments.end(),
+                [&](const characteristics::DummyArgument &dummy) {
+                  return dummy.name == actualName;
+                });
+            it != procInfo->dummyArguments.end()) {
           DetermineCopyInOutArgument(*procInfo, *actual, *it);
         }
       }
@@ -327,8 +328,8 @@ void ProcedureRef::DetermineCopyInOut() {
     }
     else {
       // Positional argument processing
-      DetermineCopyInOutArgument(*procInfo, *actual,
-          procInfo->dummyArguments[index]);
+      DetermineCopyInOutArgument(
+          *procInfo, *actual, procInfo->dummyArguments[index]);
     }
 
     ++index;

>From 5d5418a6add0e279870ba05be44b9a3e5dd3ee6d Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Sun, 3 Aug 2025 13:05:50 -0400
Subject: [PATCH 11/46] clang-format

---
 flang/lib/Evaluate/call.cpp | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index d8663d137e958..10920a4cf6b2f 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -258,11 +258,10 @@ static void DetermineCopyInOutArgument(
 }
 
 static void DetermineCopyInOutArgument(
-    const characteristics::Procedure &procInfo,
-    ActualArgument &actual, characteristics::DummyArgument &dummy) {
+    const characteristics::Procedure &procInfo, ActualArgument &actual,
+    characteristics::DummyArgument &dummy) {
 
   // TODO: assert? procInfo.HasExplicitInterface()
-
 }
 
 void ProcedureRef::DetermineCopyInOut() {
@@ -308,8 +307,7 @@ void ProcedureRef::DetermineCopyInOut() {
         // Actual arguments with duplicate keywords. Semantic analysis will
         // deal with the error.
         return;
-      }
-      else {
+      } else {
         processedKeywords.insert(actualName);
         if (auto it = std::find_if(procInfo->dummyArguments.begin(),
                 procInfo->dummyArguments.end(),
@@ -320,13 +318,11 @@ void ProcedureRef::DetermineCopyInOut() {
           DetermineCopyInOutArgument(*procInfo, *actual, *it);
         }
       }
-    }
-    else if (seenKeyword) {
+    } else if (seenKeyword) {
       // Non-keyword actual argument after have seen at least one keyword
       // actual argument. Semantic analysis will deal with the error.
       return;
-    }
-    else {
+    } else {
       // Positional argument processing
       DetermineCopyInOutArgument(
           *procInfo, *actual, procInfo->dummyArguments[index]);

>From 2cce4bba70f2ce3ad8f331601fc35a317468f5e2 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Sun, 3 Aug 2025 13:06:10 -0400
Subject: [PATCH 12/46] clang-format

---
 flang/include/flang/Evaluate/call.h | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index 3d4f791380fba..ac11527e4ecaa 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -248,8 +248,7 @@ class ProcedureRef {
   ProcedureRef(ProcedureDesignator &&p, ActualArguments &&a,
       bool hasAlternateReturns = false)
       : proc_{std::move(p)}, arguments_{std::move(a)},
-        hasAlternateReturns_{hasAlternateReturns} {
-  }
+        hasAlternateReturns_{hasAlternateReturns} {}
   ~ProcedureRef();
   static void Deleter(ProcedureRef *);
 

>From de06d25274f168993def6ba1bc3a28017ad68975 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 6 Aug 2025 22:10:30 -0400
Subject: [PATCH 13/46] Initial implementation of DetermineCopyInOutArgument()
 for implicit interfaces.

Certain helper routines gained overloads to work with ActualArgument.
---
 .../include/flang/Evaluate/check-expression.h |  3 ++
 flang/include/flang/Evaluate/tools.h          |  1 +
 flang/lib/Evaluate/call.cpp                   | 45 ++++++++++++++-----
 flang/lib/Evaluate/check-expression.cpp       | 11 +++++
 flang/lib/Evaluate/tools.cpp                  |  5 +++
 5 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/flang/include/flang/Evaluate/check-expression.h b/flang/include/flang/Evaluate/check-expression.h
index 0cf12f340ec5c..46b91cd2f93cc 100644
--- a/flang/include/flang/Evaluate/check-expression.h
+++ b/flang/include/flang/Evaluate/check-expression.h
@@ -118,6 +118,9 @@ std::optional<bool> IsContiguous(const A &, FoldingContext &,
 extern template std::optional<bool> IsContiguous(const Expr<SomeType> &,
     FoldingContext &, bool namedConstantSectionsAreContiguous,
     bool firstDimensionStride1);
+extern template std::optional<bool> IsContiguous(const ActualArgument &,
+    FoldingContext &, bool namedConstantSectionsAreContiguous,
+    bool firstDimensionStride1);
 extern template std::optional<bool> IsContiguous(const ArrayRef &,
     FoldingContext &, bool namedConstantSectionsAreContiguous,
     bool firstDimensionStride1);
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index 212356136d6ee..0be3f66321e4f 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1123,6 +1123,7 @@ extern template semantics::UnorderedSymbolSet CollectCudaSymbols(
 
 // Predicate: does a variable contain a vector-valued subscript (not a triplet)?
 bool HasVectorSubscript(const Expr<SomeType> &);
+bool HasVectorSubscript(const ActualArgument &);
 
 // Predicate: does an expression contain constant?
 bool HasConstant(const Expr<SomeType> &);
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 10920a4cf6b2f..365e98a7d800f 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -251,17 +251,39 @@ void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
 // We don't know the dummy argument info (e.g., procedure with implicit
 // interface
 static void DetermineCopyInOutArgument(
-    const characteristics::Procedure &procInfo, ActualArgument &actual) {
-
-  // Only check that actual argument is contiguous
-  // For non-contiguous, do copy-in
+    const characteristics::Procedure &procInfo, ActualArgument &actual,
+    semantics::SemanticsContext &sc) {
+  if (actual.isAlternateReturn()) {
+    return;
+  }
+  if (!evaluate::IsVariable(actual)) {
+    // Actual argument expressions that aren’t variables are copy-in, but
+    // not copy-out.
+    actual.set_mayNeedCopyIn();
+  }
+  else if (!IsSimplyContiguous(actual, sc.foldingContext())) {
+    // Actual arguments that are variables are copy-in when non-contiguous.
+    // They are copy-out when don't have vector subscripts
+    actual.set_mayNeedCopyIn();
+    if (!HasVectorSubscript(actual)) {
+      actual.set_mayNeedCopyOut();
+    }
+  }
+  else if (ExtractCoarrayRef(actual)) {
+    // Coindexed actual args need copy-in and copy-out
+    actual.set_mayNeedCopyIn();
+    actual.set_mayNeedCopyOut();
+  }
 }
 
 static void DetermineCopyInOutArgument(
     const characteristics::Procedure &procInfo, ActualArgument &actual,
-    characteristics::DummyArgument &dummy) {
-
-  // TODO: assert? procInfo.HasExplicitInterface()
+    characteristics::DummyArgument &dummy, semantics::SemanticsContext &sc) {
+  assert(procInfo.HasExplicitInterface() && "expect explicit interface proc");
+  if (actual.isAlternateReturn()) {
+    return;
+  }
+  // TODO
 }
 
 void ProcedureRef::DetermineCopyInOut() {
@@ -269,7 +291,8 @@ void ProcedureRef::DetermineCopyInOut() {
     return;
   }
   // Get folding context of the call site owner
-  FoldingContext &fc{proc_.GetSymbol()->owner().context().foldingContext()};
+  semantics::SemanticsContext &sc{proc_.GetSymbol()->owner().context()};
+  FoldingContext &fc{sc.foldingContext()};
   auto procInfo{
       characteristics::Procedure::Characterize(proc_, fc, /*emitError=*/true)};
   if (!procInfo) {
@@ -280,7 +303,7 @@ void ProcedureRef::DetermineCopyInOut() {
       if (!actual) {
         continue;
       }
-      DetermineCopyInOutArgument(*procInfo, *actual);
+      DetermineCopyInOutArgument(*procInfo, *actual, sc);
     }
     return;
   }
@@ -315,7 +338,7 @@ void ProcedureRef::DetermineCopyInOut() {
                   return dummy.name == actualName;
                 });
             it != procInfo->dummyArguments.end()) {
-          DetermineCopyInOutArgument(*procInfo, *actual, *it);
+          DetermineCopyInOutArgument(*procInfo, *actual, *it, sc);
         }
       }
     } else if (seenKeyword) {
@@ -325,7 +348,7 @@ void ProcedureRef::DetermineCopyInOut() {
     } else {
       // Positional argument processing
       DetermineCopyInOutArgument(
-          *procInfo, *actual, procInfo->dummyArguments[index]);
+          *procInfo, *actual, procInfo->dummyArguments[index], sc);
     }
 
     ++index;
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 3d7f01d56c465..eddccc57885e3 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1198,9 +1198,20 @@ std::optional<bool> IsContiguous(const A &x, FoldingContext &context,
   }
 }
 
+std::optional<bool> IsContiguous(const ActualArgument &actual,
+    FoldingContext &fc, bool namedConstantSectionsAreContiguous,
+    bool firstDimensionStride1) {
+  auto *expr{actual.UnwrapExpr()};
+  return expr && IsContiguous(*expr, fc, namedConstantSectionsAreContiguous,
+  firstDimensionStride1);
+}
+
 template std::optional<bool> IsContiguous(const Expr<SomeType> &,
     FoldingContext &, bool namedConstantSectionsAreContiguous,
     bool firstDimensionStride1);
+template std::optional<bool> IsContiguous(const ActualArgument &,
+    FoldingContext &, bool namedConstantSectionsAreContiguous,
+    bool firstDimensionStride1);
 template std::optional<bool> IsContiguous(const ArrayRef &, FoldingContext &,
     bool namedConstantSectionsAreContiguous, bool firstDimensionStride1);
 template std::optional<bool> IsContiguous(const Substring &, FoldingContext &,
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 9c059b08dd41c..3ac605d650b9f 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -1203,6 +1203,11 @@ bool HasVectorSubscript(const Expr<SomeType> &expr) {
   return HasVectorSubscriptHelper{}(expr);
 }
 
+bool HasVectorSubscript(const ActualArgument &actual) {
+  auto expr = actual.UnwrapExpr();
+  return expr && HasVectorSubscript(*expr);
+}
+
 // HasConstant()
 struct HasConstantHelper : public AnyTraverse<HasConstantHelper, bool,
                                /*TraverseAssocEntityDetails=*/false> {

>From cf42b128d0d3b679e338c7719883a82a9e659272 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 6 Aug 2025 22:16:38 -0400
Subject: [PATCH 14/46] Removed empty line

---
 flang/lib/Evaluate/call.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 365e98a7d800f..21117893f7698 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -350,7 +350,6 @@ void ProcedureRef::DetermineCopyInOut() {
       DetermineCopyInOutArgument(
           *procInfo, *actual, procInfo->dummyArguments[index], sc);
     }
-
     ++index;
   }
 }

>From 9373b90cb8fe1406dc4abf98869a9a511d3ed01c Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 6 Aug 2025 22:17:39 -0400
Subject: [PATCH 15/46] clang-format

---
 flang/lib/Evaluate/call.cpp             | 6 ++----
 flang/lib/Evaluate/check-expression.cpp | 5 +++--
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 21117893f7698..05fb29174e90e 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -260,16 +260,14 @@ static void DetermineCopyInOutArgument(
     // Actual argument expressions that aren’t variables are copy-in, but
     // not copy-out.
     actual.set_mayNeedCopyIn();
-  }
-  else if (!IsSimplyContiguous(actual, sc.foldingContext())) {
+  } else if (!IsSimplyContiguous(actual, sc.foldingContext())) {
     // Actual arguments that are variables are copy-in when non-contiguous.
     // They are copy-out when don't have vector subscripts
     actual.set_mayNeedCopyIn();
     if (!HasVectorSubscript(actual)) {
       actual.set_mayNeedCopyOut();
     }
-  }
-  else if (ExtractCoarrayRef(actual)) {
+  } else if (ExtractCoarrayRef(actual)) {
     // Coindexed actual args need copy-in and copy-out
     actual.set_mayNeedCopyIn();
     actual.set_mayNeedCopyOut();
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index eddccc57885e3..13bc4d4a05ab9 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1202,8 +1202,9 @@ std::optional<bool> IsContiguous(const ActualArgument &actual,
     FoldingContext &fc, bool namedConstantSectionsAreContiguous,
     bool firstDimensionStride1) {
   auto *expr{actual.UnwrapExpr()};
-  return expr && IsContiguous(*expr, fc, namedConstantSectionsAreContiguous,
-  firstDimensionStride1);
+  return expr &&
+      IsContiguous(
+          *expr, fc, namedConstantSectionsAreContiguous, firstDimensionStride1);
 }
 
 template std::optional<bool> IsContiguous(const Expr<SomeType> &,

>From 12ca5a8103c56d9fe2ca2c686226482469bd821f Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 8 Aug 2025 10:34:50 -0400
Subject: [PATCH 16/46] Braces!

---
 flang/lib/Evaluate/call.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 05fb29174e90e..592663323aa1f 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -323,18 +323,18 @@ void ProcedureRef::DetermineCopyInOut() {
     }
     if (actual->keyword()) {
       seenKeyword = true;
-      auto actualName = actual->keyword()->ToString();
+    auto actualName{actual->keyword()->ToString()};
       if (processedKeywords.find(actualName) != processedKeywords.end()) {
         // Actual arguments with duplicate keywords. Semantic analysis will
         // deal with the error.
         return;
       } else {
         processedKeywords.insert(actualName);
-        if (auto it = std::find_if(procInfo->dummyArguments.begin(),
+        if (auto it{std::find_if(procInfo->dummyArguments.begin(),
                 procInfo->dummyArguments.end(),
                 [&](const characteristics::DummyArgument &dummy) {
                   return dummy.name == actualName;
-                });
+                })};
             it != procInfo->dummyArguments.end()) {
           DetermineCopyInOutArgument(*procInfo, *actual, *it, sc);
         }

>From 143f7bd095ff24dd723e34b52f62a8b2cfb618f3 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 8 Aug 2025 16:02:46 -0400
Subject: [PATCH 17/46] clang-format

---
 flang/lib/Evaluate/call.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 592663323aa1f..cd48be276e94b 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -323,7 +323,7 @@ void ProcedureRef::DetermineCopyInOut() {
     }
     if (actual->keyword()) {
       seenKeyword = true;
-    auto actualName{actual->keyword()->ToString()};
+      auto actualName{actual->keyword()->ToString()};
       if (processedKeywords.find(actualName) != processedKeywords.end()) {
         // Actual arguments with duplicate keywords. Semantic analysis will
         // deal with the error.

>From 6d0935cf8091a59c0736cf95ec60fd4bfae791d3 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 11 Aug 2025 15:39:47 -0400
Subject: [PATCH 18/46] Renamed copy-in/copy-out getter/setter functions

---
 flang/include/flang/Evaluate/call.h |  8 ++++----
 flang/lib/Evaluate/call.cpp         | 10 +++++-----
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index ac11527e4ecaa..efdcee709d435 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -161,15 +161,15 @@ class ActualArgument {
   }
 
   // This actual argument may need copy-in before the procedure call
-  bool mayNeedCopyIn() const { return attrs_.test(Attr::CopyIn); };
-  ActualArgument &set_mayNeedCopyIn() {
+  bool GetMayNeedCopyIn() const { return attrs_.test(Attr::CopyIn); };
+  ActualArgument &SetMayNeedCopyIn() {
     attrs_ = attrs_ + Attr::CopyIn;
     return *this;
   }
 
   // This actual argument may need copy-out after the procedure call
-  bool mayNeedCopyOut() const { return attrs_.test(Attr::CopyOut); };
-  ActualArgument &set_mayNeedCopyOut() {
+  bool GetMayNeedCopyOut() const { return attrs_.test(Attr::CopyOut); };
+  ActualArgument &SetMayNeedCopyOut() {
     attrs_ = attrs_ + Attr::CopyOut;
     return *this;
   }
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index cd48be276e94b..3384563912720 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -259,18 +259,18 @@ static void DetermineCopyInOutArgument(
   if (!evaluate::IsVariable(actual)) {
     // Actual argument expressions that aren’t variables are copy-in, but
     // not copy-out.
-    actual.set_mayNeedCopyIn();
+    actual.SetMayNeedCopyIn();
   } else if (!IsSimplyContiguous(actual, sc.foldingContext())) {
     // Actual arguments that are variables are copy-in when non-contiguous.
     // They are copy-out when don't have vector subscripts
-    actual.set_mayNeedCopyIn();
+    actual.SetMayNeedCopyIn();
     if (!HasVectorSubscript(actual)) {
-      actual.set_mayNeedCopyOut();
+      actual.SetMayNeedCopyOut();
     }
   } else if (ExtractCoarrayRef(actual)) {
     // Coindexed actual args need copy-in and copy-out
-    actual.set_mayNeedCopyIn();
-    actual.set_mayNeedCopyOut();
+    actual.SetMayNeedCopyIn();
+    actual.SetMayNeedCopyOut();
   }
 }
 

>From 15db3f8db3769340527262f61098f61933e515c0 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 11 Aug 2025 20:08:32 -0400
Subject: [PATCH 19/46] Contiguity check

---
 flang/lib/Evaluate/call.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 3384563912720..aab2d3607eb9d 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -281,6 +281,16 @@ static void DetermineCopyInOutArgument(
   if (actual.isAlternateReturn()) {
     return;
   }
+  const auto *dummyObj = std::get_if<characteristics::DummyDataObject>(&dummy.u);
+  if (!dummyObj) {
+    // Only DummyDataObject has the information we need
+    return;
+  }
+  // Check actual contiguity, unless dummy doesn't care
+  bool actualContiguous = dummyObj->ignoreTKR.test(common::IgnoreTKR::Contiguous)
+      || IsSimplyContiguous(actual, sc.foldingContext());
+  bool actualVectorSubscript = HasVectorSubscript(actual);
+
   // TODO
 }
 

>From 58764a61ce508d1246e862097258d4b4f2c31800 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 12 Aug 2025 11:33:52 -0400
Subject: [PATCH 20/46] clang-format

---
 flang/lib/Evaluate/call.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index aab2d3607eb9d..4e054d7e51cbd 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -281,14 +281,16 @@ static void DetermineCopyInOutArgument(
   if (actual.isAlternateReturn()) {
     return;
   }
-  const auto *dummyObj = std::get_if<characteristics::DummyDataObject>(&dummy.u);
+  const auto *dummyObj =
+      std::get_if<characteristics::DummyDataObject>(&dummy.u);
   if (!dummyObj) {
     // Only DummyDataObject has the information we need
     return;
   }
   // Check actual contiguity, unless dummy doesn't care
-  bool actualContiguous = dummyObj->ignoreTKR.test(common::IgnoreTKR::Contiguous)
-      || IsSimplyContiguous(actual, sc.foldingContext());
+  bool actualContiguous =
+      dummyObj->ignoreTKR.test(common::IgnoreTKR::Contiguous) ||
+      IsSimplyContiguous(actual, sc.foldingContext());
   bool actualVectorSubscript = HasVectorSubscript(actual);
 
   // TODO

>From eb27031929e7c1d8f712cacd8ff189d54bdbb334 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 12 Aug 2025 13:41:37 -0400
Subject: [PATCH 21/46] Continue filling out DetermineCopyInOutArgument().
 Implemented IsExplicitShape(const Shape&)

---
 flang/include/flang/Evaluate/shape.h |  1 +
 flang/lib/Evaluate/call.cpp          | 36 ++++++++++++++++++++++++----
 flang/lib/Evaluate/shape.cpp         | 11 +++++++++
 3 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/flang/include/flang/Evaluate/shape.h b/flang/include/flang/Evaluate/shape.h
index f0505cfcdf2d7..32fcdc0281f26 100644
--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -35,6 +35,7 @@ using Shape = std::vector<MaybeExtentExpr>;
 
 bool IsImpliedShape(const Symbol &);
 bool IsExplicitShape(const Symbol &);
+bool IsExplicitShape(const Shape &);
 
 // Conversions between various representations of shapes.
 std::optional<ExtentExpr> AsExtentArrayExpr(const Shape &);
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 4e054d7e51cbd..d91eb998d88b0 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -281,17 +281,43 @@ static void DetermineCopyInOutArgument(
   if (actual.isAlternateReturn()) {
     return;
   }
-  const auto *dummyObj =
-      std::get_if<characteristics::DummyDataObject>(&dummy.u);
+  const auto *dummyObj{
+      std::get_if<characteristics::DummyDataObject>(&dummy.u)};
   if (!dummyObj) {
     // Only DummyDataObject has the information we need
     return;
   }
+
+  bool dummyIsValue{
+      dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Value)};
+  if (dummyIsValue) {
+    actual.SetMayNeedCopyIn();
+    return;
+  }
+
   // Check actual contiguity, unless dummy doesn't care
-  bool actualContiguous =
+  bool actualTreatAsContiguous{
       dummyObj->ignoreTKR.test(common::IgnoreTKR::Contiguous) ||
-      IsSimplyContiguous(actual, sc.foldingContext());
-  bool actualVectorSubscript = HasVectorSubscript(actual);
+      IsSimplyContiguous(actual, sc.foldingContext())};
+
+  bool actualHasVectorSubscript{HasVectorSubscript(actual)};
+  bool actualIsArray{actual.Rank() > 0};
+
+  bool dummyIsArray{dummyObj->type.Rank() > 0};
+  bool dummyIsExplicitShape{
+      dummyIsArray ? IsExplicitShape(*dummyObj->type.shape()) : false};
+  bool dummyIsAssumedSize{dummyObj->type.attrs().test(
+      characteristics::TypeAndShape::Attr::AssumedSize)};
+  bool dummyNeedsContiguity{dummyIsArray &&
+      (dummyIsExplicitShape || dummyIsAssumedSize ||
+      dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Contiguous))};
+  if (!actualTreatAsContiguous && dummyNeedsContiguity) {
+    actual.SetMayNeedCopyIn();
+    if (!actualHasVectorSubscript) {
+      actual.SetMayNeedCopyOut();
+    }
+  }
+
 
   // TODO
 }
diff --git a/flang/lib/Evaluate/shape.cpp b/flang/lib/Evaluate/shape.cpp
index 776866d1416d2..2c0191e866d3f 100644
--- a/flang/lib/Evaluate/shape.cpp
+++ b/flang/lib/Evaluate/shape.cpp
@@ -47,6 +47,17 @@ bool IsExplicitShape(const Symbol &original) {
   }
 }
 
+bool IsExplicitShape(const Shape &shape) {
+  // If extent expression is present for all dimensions, then assume
+  // explicit shape.
+  for (const auto &dim : shape) {
+    if (!dim) {
+      return false;
+    }
+  }
+  return true;
+}
+
 Shape GetShapeHelper::ConstantShape(const Constant<ExtentType> &arrayConstant) {
   CHECK(arrayConstant.Rank() == 1);
   Shape result;

>From 3ec01ad3b840e89f768d489a8cd5459196e1d166 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 12 Aug 2025 13:42:07 -0400
Subject: [PATCH 22/46] clang-format

---
 flang/lib/Evaluate/call.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index d91eb998d88b0..a07f57b43ccee 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -281,8 +281,7 @@ static void DetermineCopyInOutArgument(
   if (actual.isAlternateReturn()) {
     return;
   }
-  const auto *dummyObj{
-      std::get_if<characteristics::DummyDataObject>(&dummy.u)};
+  const auto *dummyObj{std::get_if<characteristics::DummyDataObject>(&dummy.u)};
   if (!dummyObj) {
     // Only DummyDataObject has the information we need
     return;
@@ -310,7 +309,8 @@ static void DetermineCopyInOutArgument(
       characteristics::TypeAndShape::Attr::AssumedSize)};
   bool dummyNeedsContiguity{dummyIsArray &&
       (dummyIsExplicitShape || dummyIsAssumedSize ||
-      dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Contiguous))};
+          dummyObj->attrs.test(
+              characteristics::DummyDataObject::Attr::Contiguous))};
   if (!actualTreatAsContiguous && dummyNeedsContiguity) {
     actual.SetMayNeedCopyIn();
     if (!actualHasVectorSubscript) {
@@ -318,7 +318,6 @@ static void DetermineCopyInOutArgument(
     }
   }
 
-
   // TODO
 }
 

>From 889d514e02d05ce27df80e41e8640f21370432d6 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Tue, 12 Aug 2025 16:40:12 -0400
Subject: [PATCH 23/46] In DetermineCopyInOutArgument(), handle INTENT(IN) and
 INTENT(OUT)

---
 flang/lib/Evaluate/call.cpp | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index a07f57b43ccee..6a69223725b28 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -286,13 +286,28 @@ static void DetermineCopyInOutArgument(
     // Only DummyDataObject has the information we need
     return;
   }
-
+  // Pass by value, always copy-in, never copy-out
   bool dummyIsValue{
       dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Value)};
   if (dummyIsValue) {
     actual.SetMayNeedCopyIn();
     return;
   }
+  bool dummyIntentIn{dummyObj->intent == common::Intent::In};
+  bool dummyIntentOut{dummyObj->intent == common::Intent::Out};
+
+  auto setCopyIn = [&]() {
+    if (!dummyIntentOut) {
+      // INTENT(OUT) never need copy-in
+      actual.SetMayNeedCopyIn();
+    }
+  };
+  auto setCopyOut = [&]() {
+    if (!dummyIntentIn) {
+      // INTENT(IN) never need copy-out
+      actual.SetMayNeedCopyOut();
+    }
+  };
 
   // Check actual contiguity, unless dummy doesn't care
   bool actualTreatAsContiguous{
@@ -312,12 +327,15 @@ static void DetermineCopyInOutArgument(
           dummyObj->attrs.test(
               characteristics::DummyDataObject::Attr::Contiguous))};
   if (!actualTreatAsContiguous && dummyNeedsContiguity) {
-    actual.SetMayNeedCopyIn();
+    setCopyIn();
     if (!actualHasVectorSubscript) {
-      actual.SetMayNeedCopyOut();
+      setCopyOut();
     }
+    return;
   }
 
+  // TODO: passing polymorphic to non-polymorphic
+
   // TODO
 }
 

>From 068216fadaea846648a11b5668446f16b5d7f798 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 13 Aug 2025 18:26:04 -0400
Subject: [PATCH 24/46] Support polymorphic-to-non-polymorphic case. Initial
 hookup into lowering, have LIT test failures

---
 flang/lib/Evaluate/call.cpp     | 32 ++++++++++++++++++++++++++++----
 flang/lib/Lower/ConvertCall.cpp | 14 ++++++++++++--
 2 files changed, 40 insertions(+), 6 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 6a69223725b28..e3119350a2252 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -260,7 +260,8 @@ static void DetermineCopyInOutArgument(
     // Actual argument expressions that aren’t variables are copy-in, but
     // not copy-out.
     actual.SetMayNeedCopyIn();
-  } else if (!IsSimplyContiguous(actual, sc.foldingContext())) {
+  } else if (bool actualIsArray{actual.Rank() > 0}; actualIsArray &&
+    !IsSimplyContiguous(actual, sc.foldingContext())) {
     // Actual arguments that are variables are copy-in when non-contiguous.
     // They are copy-out when don't have vector subscripts
     actual.SetMayNeedCopyIn();
@@ -281,6 +282,12 @@ static void DetermineCopyInOutArgument(
   if (actual.isAlternateReturn()) {
     return;
   }
+  if (!evaluate::IsVariable(actual)) {
+    // Actual argument expressions that aren’t variables are copy-in, but
+    // not copy-out.
+    actual.SetMayNeedCopyIn();
+    return;
+  }
   const auto *dummyObj{std::get_if<characteristics::DummyDataObject>(&dummy.u)};
   if (!dummyObj) {
     // Only DummyDataObject has the information we need
@@ -309,13 +316,17 @@ static void DetermineCopyInOutArgument(
     }
   };
 
+  bool actualIsArray{actual.Rank() > 0};
+  if (!actualIsArray) {
+    return;
+  }
+
   // Check actual contiguity, unless dummy doesn't care
   bool actualTreatAsContiguous{
       dummyObj->ignoreTKR.test(common::IgnoreTKR::Contiguous) ||
       IsSimplyContiguous(actual, sc.foldingContext())};
 
   bool actualHasVectorSubscript{HasVectorSubscript(actual)};
-  bool actualIsArray{actual.Rank() > 0};
 
   bool dummyIsArray{dummyObj->type.Rank() > 0};
   bool dummyIsExplicitShape{
@@ -328,15 +339,28 @@ static void DetermineCopyInOutArgument(
               characteristics::DummyDataObject::Attr::Contiguous))};
   if (!actualTreatAsContiguous && dummyNeedsContiguity) {
     setCopyIn();
+    // Cannot do copy-out for vector subscripts: there could be repeated
+    // indices, for example
     if (!actualHasVectorSubscript) {
       setCopyOut();
     }
     return;
   }
 
-  // TODO: passing polymorphic to non-polymorphic
+  if (!dummyObj->ignoreTKR.test(common::IgnoreTKR::Type)) {
+    // flang supports limited cases of passing polymorphic to non-polimorphic.
+    // These cases require temporary of non-polymorphic type.
+    auto actualType{characteristics::TypeAndShape::Characterize(
+        actual, sc.foldingContext())};
+    bool actualIsPolymorphic{actualType->type().IsPolymorphic()};
+    bool dummyIsPolymorphic{dummyObj->type.type().IsPolymorphic()};
+    if (actualIsPolymorphic && !dummyIsPolymorphic) {
+      setCopyIn();
+      setCopyOut();
+    }
+  }
 
-  // TODO
+  // TODO: character type differences?
 }
 
 void ProcedureRef::DetermineCopyInOut() {
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index bf713f5a0bc48..729b2e95a5397 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1257,10 +1257,19 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
   // The simple contiguity of the actual is "lost" when passing a polymorphic
   // to a non polymorphic entity because the dummy dynamic type matters for
   // the contiguity.
+#if 0
   const bool mustDoCopyInOut =
       actual.isArray() && arg.mustBeMadeContiguous() &&
       (passingPolymorphicToNonPolymorphic ||
        !isSimplyContiguous(*arg.entity, foldingContext));
+#else
+  bool mustDoCopyIn = false;
+  bool mustDoCopyOut = false;
+  if constexpr (std::is_same_v<decltype(arg.entity), const Fortran::evaluate::ActualArgument *>) {
+    mustDoCopyIn = arg.entity->GetMayNeedCopyIn();
+    mustDoCopyIn = arg.entity->GetMayNeedCopyOut();
+  }
+#endif
 
   const bool actualIsAssumedRank = actual.isAssumedRank();
   // Create dummy type with actual argument rank when the dummy is an assumed
@@ -1370,7 +1379,7 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       entity = hlfir::Entity{associate.getBase()};
       // Register the temporary destruction after the call.
       preparedDummy.pushExprAssociateCleanUp(associate);
-    } else if (mustDoCopyInOut) {
+    } else if (mustDoCopyIn) {
       // Copy-in non contiguous variables.
       // TODO: for non-finalizable monomorphic derived type actual
       // arguments associated with INTENT(OUT) dummy arguments
@@ -1379,7 +1388,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       // allocation for the temp in this case. We can communicate
       // this to the codegen via some CopyInOp flag.
       // This is a performance concern.
-      entity = genCopyIn(entity, arg.mayBeModifiedByCall());
+      //entity = genCopyIn(entity, arg.mayBeModifiedByCall());
+      entity = genCopyIn(entity, mustDoCopyOut);
     }
   } else {
     const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr();

>From 3f36c9fb0ccd3e7b70009193b57f7200898edbe9 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 13 Aug 2025 18:26:35 -0400
Subject: [PATCH 25/46] clang-format

---
 flang/lib/Evaluate/call.cpp     | 2 +-
 flang/lib/Lower/ConvertCall.cpp | 5 +++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index e3119350a2252..efaa3bd42a310 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -261,7 +261,7 @@ static void DetermineCopyInOutArgument(
     // not copy-out.
     actual.SetMayNeedCopyIn();
   } else if (bool actualIsArray{actual.Rank() > 0}; actualIsArray &&
-    !IsSimplyContiguous(actual, sc.foldingContext())) {
+             !IsSimplyContiguous(actual, sc.foldingContext())) {
     // Actual arguments that are variables are copy-in when non-contiguous.
     // They are copy-out when don't have vector subscripts
     actual.SetMayNeedCopyIn();
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 729b2e95a5397..85ac5c2539cc8 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1265,7 +1265,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
 #else
   bool mustDoCopyIn = false;
   bool mustDoCopyOut = false;
-  if constexpr (std::is_same_v<decltype(arg.entity), const Fortran::evaluate::ActualArgument *>) {
+  if constexpr (std::is_same_v<decltype(arg.entity),
+                               const Fortran::evaluate::ActualArgument *>) {
     mustDoCopyIn = arg.entity->GetMayNeedCopyIn();
     mustDoCopyIn = arg.entity->GetMayNeedCopyOut();
   }
@@ -1388,7 +1389,7 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       // allocation for the temp in this case. We can communicate
       // this to the codegen via some CopyInOp flag.
       // This is a performance concern.
-      //entity = genCopyIn(entity, arg.mayBeModifiedByCall());
+      // entity = genCopyIn(entity, arg.mayBeModifiedByCall());
       entity = genCopyIn(entity, mustDoCopyOut);
     }
   } else {

>From 070ffd1e80ded417d0ea8eac6c478f078f7fddf7 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 13 Aug 2025 22:35:21 -0400
Subject: [PATCH 26/46] array check

---
 flang/lib/Lower/ConvertCall.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 85ac5c2539cc8..003eb78093f8b 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1265,9 +1265,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
 #else
   bool mustDoCopyIn = false;
   bool mustDoCopyOut = false;
-  if constexpr (std::is_same_v<decltype(arg.entity),
-                               const Fortran::evaluate::ActualArgument *>) {
-    mustDoCopyIn = arg.entity->GetMayNeedCopyIn();
+  if constexpr (std::is_same_v<decltype(arg.entity), const Fortran::evaluate::ActualArgument *>) {
+    mustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
     mustDoCopyIn = arg.entity->GetMayNeedCopyOut();
   }
 #endif

>From 50622660e61229afcb4fdcf097e9026b6cfe4f93 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 13 Aug 2025 22:35:38 -0400
Subject: [PATCH 27/46] array check

---
 flang/lib/Lower/ConvertCall.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 003eb78093f8b..cdbc3ee259815 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1265,7 +1265,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
 #else
   bool mustDoCopyIn = false;
   bool mustDoCopyOut = false;
-  if constexpr (std::is_same_v<decltype(arg.entity), const Fortran::evaluate::ActualArgument *>) {
+  if constexpr (std::is_same_v<decltype(arg.entity),
+                               const Fortran::evaluate::ActualArgument *>) {
     mustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
     mustDoCopyIn = arg.entity->GetMayNeedCopyOut();
   }

>From 312cf2580f652f87b8d0269f31e94165b4633dee Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 14 Aug 2025 19:04:55 -0400
Subject: [PATCH 28/46] Changed IsExplicitShape() and restructured the checks

---
 .../include/flang/Evaluate/characteristics.h  |  6 ++++
 flang/include/flang/Evaluate/shape.h          |  1 -
 flang/lib/Evaluate/call.cpp                   | 29 +++++++++----------
 flang/lib/Evaluate/shape.cpp                  | 11 -------
 4 files changed, 20 insertions(+), 27 deletions(-)

diff --git a/flang/include/flang/Evaluate/characteristics.h b/flang/include/flang/Evaluate/characteristics.h
index d566c34ff71e8..b6a9ebefec9df 100644
--- a/flang/include/flang/Evaluate/characteristics.h
+++ b/flang/include/flang/Evaluate/characteristics.h
@@ -203,6 +203,12 @@ class TypeAndShape {
   std::optional<Expr<SubscriptInteger>> MeasureSizeInBytes(
       FoldingContext &) const;
 
+  bool IsExplicitShape() const {
+    // If it's array and no special attributes are set, then must be
+    // explicit shape.
+    return Rank() > 0 && attrs_.none();
+  }
+
   // called by Fold() to rewrite in place
   TypeAndShape &Rewrite(FoldingContext &);
 
diff --git a/flang/include/flang/Evaluate/shape.h b/flang/include/flang/Evaluate/shape.h
index 32fcdc0281f26..f0505cfcdf2d7 100644
--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -35,7 +35,6 @@ using Shape = std::vector<MaybeExtentExpr>;
 
 bool IsImpliedShape(const Symbol &);
 bool IsExplicitShape(const Symbol &);
-bool IsExplicitShape(const Shape &);
 
 // Conversions between various representations of shapes.
 std::optional<ExtentExpr> AsExtentArrayExpr(const Shape &);
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index efaa3bd42a310..888522577022c 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -300,6 +300,13 @@ static void DetermineCopyInOutArgument(
     actual.SetMayNeedCopyIn();
     return;
   }
+  // All the checks below are for arrays
+  bool actualIsArray{actual.Rank() > 0};
+  bool dummyIsArray{dummyObj->type.Rank() > 0};
+  if (!actualIsArray || !dummyIsArray) {
+    return;
+  }
+
   bool dummyIntentIn{dummyObj->intent == common::Intent::In};
   bool dummyIntentOut{dummyObj->intent == common::Intent::Out};
 
@@ -316,27 +323,17 @@ static void DetermineCopyInOutArgument(
     }
   };
 
-  bool actualIsArray{actual.Rank() > 0};
-  if (!actualIsArray) {
-    return;
-  }
-
   // Check actual contiguity, unless dummy doesn't care
   bool actualTreatAsContiguous{
       dummyObj->ignoreTKR.test(common::IgnoreTKR::Contiguous) ||
       IsSimplyContiguous(actual, sc.foldingContext())};
-
   bool actualHasVectorSubscript{HasVectorSubscript(actual)};
-
-  bool dummyIsArray{dummyObj->type.Rank() > 0};
-  bool dummyIsExplicitShape{
-      dummyIsArray ? IsExplicitShape(*dummyObj->type.shape()) : false};
+  bool dummyIsExplicitShape{dummyObj->type.IsExplicitShape()};
   bool dummyIsAssumedSize{dummyObj->type.attrs().test(
       characteristics::TypeAndShape::Attr::AssumedSize)};
-  bool dummyNeedsContiguity{dummyIsArray &&
-      (dummyIsExplicitShape || dummyIsAssumedSize ||
-          dummyObj->attrs.test(
-              characteristics::DummyDataObject::Attr::Contiguous))};
+  bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize ||
+      dummyObj->attrs.test(
+          characteristics::DummyDataObject::Attr::Contiguous)};
   if (!actualTreatAsContiguous && dummyNeedsContiguity) {
     setCopyIn();
     // Cannot do copy-out for vector subscripts: there could be repeated
@@ -349,7 +346,9 @@ static void DetermineCopyInOutArgument(
 
   if (!dummyObj->ignoreTKR.test(common::IgnoreTKR::Type)) {
     // flang supports limited cases of passing polymorphic to non-polimorphic.
-    // These cases require temporary of non-polymorphic type.
+    // These cases require temporary of non-polymorphic type. (For example,
+    // the actual argument could be polymorphic array of child type,
+    // while the dummy argument could be non-polymorphic array of parent type.)
     auto actualType{characteristics::TypeAndShape::Characterize(
         actual, sc.foldingContext())};
     bool actualIsPolymorphic{actualType->type().IsPolymorphic()};
diff --git a/flang/lib/Evaluate/shape.cpp b/flang/lib/Evaluate/shape.cpp
index 2c0191e866d3f..776866d1416d2 100644
--- a/flang/lib/Evaluate/shape.cpp
+++ b/flang/lib/Evaluate/shape.cpp
@@ -47,17 +47,6 @@ bool IsExplicitShape(const Symbol &original) {
   }
 }
 
-bool IsExplicitShape(const Shape &shape) {
-  // If extent expression is present for all dimensions, then assume
-  // explicit shape.
-  for (const auto &dim : shape) {
-    if (!dim) {
-      return false;
-    }
-  }
-  return true;
-}
-
 Shape GetShapeHelper::ConstantShape(const Constant<ExtentType> &arrayConstant) {
   CHECK(arrayConstant.Rank() == 1);
   Shape result;

>From bc2e17aedc60dfb389eb726516d969b55c436daa Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 14 Aug 2025 21:51:25 -0400
Subject: [PATCH 29/46] Redid the integration to basically the old code but
 debug output with the new code

---
 flang/include/flang/Lower/CallInterface.h |  2 ++
 flang/lib/Evaluate/call.cpp               |  7 ++-----
 flang/lib/Lower/ConvertCall.cpp           | 23 ++++++++++++-----------
 3 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index 72bc9dd890a94..6e3bdc0d9104c 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -159,6 +159,8 @@ class CallInterface {
   /// PassedEntity is what is provided back to the CallInterface user.
   /// It describe how the entity is plugged in the interface
   struct PassedEntity {
+    /// Helps with caller/callee differentiation
+    static constexpr bool isCaller = std::is_same_v<T, CallerInterface>;
     /// Is the dummy argument optional?
     bool isOptional() const;
     /// Can the argument be modified by the callee?
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 888522577022c..e79a314c837ed 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -309,16 +309,15 @@ static void DetermineCopyInOutArgument(
 
   bool dummyIntentIn{dummyObj->intent == common::Intent::In};
   bool dummyIntentOut{dummyObj->intent == common::Intent::Out};
-
   auto setCopyIn = [&]() {
     if (!dummyIntentOut) {
-      // INTENT(OUT) never need copy-in
+      // INTENT(OUT) dummy args never need copy-in
       actual.SetMayNeedCopyIn();
     }
   };
   auto setCopyOut = [&]() {
     if (!dummyIntentIn) {
-      // INTENT(IN) never need copy-out
+      // INTENT(IN) dummy args never need copy-out
       actual.SetMayNeedCopyOut();
     }
   };
@@ -358,8 +357,6 @@ static void DetermineCopyInOutArgument(
       setCopyOut();
     }
   }
-
-  // TODO: character type differences?
 }
 
 void ProcedureRef::DetermineCopyInOut() {
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index cdbc3ee259815..368ec1c23da52 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1257,20 +1257,22 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
   // The simple contiguity of the actual is "lost" when passing a polymorphic
   // to a non polymorphic entity because the dummy dynamic type matters for
   // the contiguity.
-#if 0
-  const bool mustDoCopyInOut =
+  bool mustDoCopyIn =
       actual.isArray() && arg.mustBeMadeContiguous() &&
       (passingPolymorphicToNonPolymorphic ||
        !isSimplyContiguous(*arg.entity, foldingContext));
-#else
-  bool mustDoCopyIn = false;
-  bool mustDoCopyOut = false;
-  if constexpr (std::is_same_v<decltype(arg.entity),
-                               const Fortran::evaluate::ActualArgument *>) {
-    mustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
-    mustDoCopyIn = arg.entity->GetMayNeedCopyOut();
+  bool mustDoCopyOut = arg.mayBeModifiedByCall();
+  if constexpr (Fortran::lower::CallerInterface::PassedEntity::isCaller) {
+    bool newMustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
+    bool newMustDoCopyOut = arg.entity->GetMayNeedCopyOut();
+    LLVM_DEBUG(llvm::dbgs() << "copyinout: CALLER " <<
+        "copy-in: old=" << mustDoCopyIn << ", new=" << newMustDoCopyIn <<
+        "| copy-out: old=" << mustDoCopyOut << ", new=" << newMustDoCopyOut <<
+        "\n");
+  } else {
+    LLVM_DEBUG(llvm::dbgs() << "copyinout: CALLEE " <<
+        "copy-in=" << mustDoCopyIn << ", copy-out=" << mustDoCopyOut << "\n");
   }
-#endif
 
   const bool actualIsAssumedRank = actual.isAssumedRank();
   // Create dummy type with actual argument rank when the dummy is an assumed
@@ -1389,7 +1391,6 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       // allocation for the temp in this case. We can communicate
       // this to the codegen via some CopyInOp flag.
       // This is a performance concern.
-      // entity = genCopyIn(entity, arg.mayBeModifiedByCall());
       entity = genCopyIn(entity, mustDoCopyOut);
     }
   } else {

>From d026f49b82575c71f0fd377634cd6f502ddf5a10 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 14 Aug 2025 22:56:34 -0400
Subject: [PATCH 30/46] Tweaks and debug output

---
 flang/lib/Lower/ConvertCall.cpp | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 368ec1c23da52..b0b130cce3ecc 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1261,17 +1261,21 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       actual.isArray() && arg.mustBeMadeContiguous() &&
       (passingPolymorphicToNonPolymorphic ||
        !isSimplyContiguous(*arg.entity, foldingContext));
-  bool mustDoCopyOut = arg.mayBeModifiedByCall();
+  bool mustDoCopyOut = mustDoCopyIn && arg.mayBeModifiedByCall();
   if constexpr (Fortran::lower::CallerInterface::PassedEntity::isCaller) {
     bool newMustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
     bool newMustDoCopyOut = arg.entity->GetMayNeedCopyOut();
-    LLVM_DEBUG(llvm::dbgs() << "copyinout: CALLER " <<
+#if 1
+    llvm::dbgs() << "copyinout: CALLER " <<
         "copy-in: old=" << mustDoCopyIn << ", new=" << newMustDoCopyIn <<
         "| copy-out: old=" << mustDoCopyOut << ", new=" << newMustDoCopyOut <<
-        "\n");
+        "\n";
+#endif
   } else {
-    LLVM_DEBUG(llvm::dbgs() << "copyinout: CALLEE " <<
-        "copy-in=" << mustDoCopyIn << ", copy-out=" << mustDoCopyOut << "\n");
+#if 1
+    llvm::dbgs() << "copyinout: CALLEE " <<
+        "copy-in=" << mustDoCopyIn << ", copy-out=" << mustDoCopyOut << "\n";
+#endif
   }
 
   const bool actualIsAssumedRank = actual.isAssumedRank();

>From 55b8e0eeb0fc846883b38e78c294ff54801bcd3b Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 14 Aug 2025 23:33:00 -0400
Subject: [PATCH 31/46] Switched to the new code. 5 failed tests left

---
 flang/lib/Lower/ConvertCall.cpp | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index b0b130cce3ecc..610dd6315d1f5 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1262,9 +1262,11 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       (passingPolymorphicToNonPolymorphic ||
        !isSimplyContiguous(*arg.entity, foldingContext));
   bool mustDoCopyOut = mustDoCopyIn && arg.mayBeModifiedByCall();
+  bool newMustDoCopyIn = false;
+  bool newMustDoCopyOut = false;
   if constexpr (Fortran::lower::CallerInterface::PassedEntity::isCaller) {
-    bool newMustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
-    bool newMustDoCopyOut = arg.entity->GetMayNeedCopyOut();
+    newMustDoCopyIn = arg.entity->GetMayNeedCopyIn();
+    newMustDoCopyOut = arg.entity->GetMayNeedCopyOut();
 #if 1
     llvm::dbgs() << "copyinout: CALLER " <<
         "copy-in: old=" << mustDoCopyIn << ", new=" << newMustDoCopyIn <<
@@ -1277,6 +1279,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
         "copy-in=" << mustDoCopyIn << ", copy-out=" << mustDoCopyOut << "\n";
 #endif
   }
+  mustDoCopyIn = newMustDoCopyIn;
+  mustDoCopyOut = newMustDoCopyOut;
 
   const bool actualIsAssumedRank = actual.isAssumedRank();
   // Create dummy type with actual argument rank when the dummy is an assumed

>From 258fc64ebbe362d0d724ea9d13ba854ab23eb517 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 15 Aug 2025 14:25:44 -0400
Subject: [PATCH 32/46] Fixed one issue with assumed rank arrays

---
 flang/lib/Evaluate/call.cpp | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index e79a314c837ed..ff7f7cde224d3 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -301,8 +301,12 @@ static void DetermineCopyInOutArgument(
     return;
   }
   // All the checks below are for arrays
-  bool actualIsArray{actual.Rank() > 0};
-  bool dummyIsArray{dummyObj->type.Rank() > 0};
+
+  bool actualIsAssumedRank{evaluate::IsAssumedRank(actual)};
+  bool actualIsArray{actualIsAssumedRank || actual.Rank() > 0};
+  bool dummyIsAssumedRank{dummyObj->type.attrs().test(
+      characteristics::TypeAndShape::Attr::AssumedRank)};
+  bool dummyIsArray{dummyIsAssumedRank || dummyObj->type.Rank() > 0};
   if (!actualIsArray || !dummyIsArray) {
     return;
   }

>From a197190365dd14a2ddcad28c4e070681a4815ab3 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 15 Aug 2025 15:36:10 -0400
Subject: [PATCH 33/46] clang-format

---
 flang/lib/Evaluate/call.cpp     |  3 +--
 flang/lib/Lower/ConvertCall.cpp | 19 +++++++++----------
 2 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index ff7f7cde224d3..5a8ef8de92305 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -335,8 +335,7 @@ static void DetermineCopyInOutArgument(
   bool dummyIsAssumedSize{dummyObj->type.attrs().test(
       characteristics::TypeAndShape::Attr::AssumedSize)};
   bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize ||
-      dummyObj->attrs.test(
-          characteristics::DummyDataObject::Attr::Contiguous)};
+      dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Contiguous)};
   if (!actualTreatAsContiguous && dummyNeedsContiguity) {
     setCopyIn();
     // Cannot do copy-out for vector subscripts: there could be repeated
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 610dd6315d1f5..1d92a634059ea 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1257,10 +1257,9 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
   // The simple contiguity of the actual is "lost" when passing a polymorphic
   // to a non polymorphic entity because the dummy dynamic type matters for
   // the contiguity.
-  bool mustDoCopyIn =
-      actual.isArray() && arg.mustBeMadeContiguous() &&
-      (passingPolymorphicToNonPolymorphic ||
-       !isSimplyContiguous(*arg.entity, foldingContext));
+  bool mustDoCopyIn = actual.isArray() && arg.mustBeMadeContiguous() &&
+                      (passingPolymorphicToNonPolymorphic ||
+                       !isSimplyContiguous(*arg.entity, foldingContext));
   bool mustDoCopyOut = mustDoCopyIn && arg.mayBeModifiedByCall();
   bool newMustDoCopyIn = false;
   bool newMustDoCopyOut = false;
@@ -1268,15 +1267,15 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
     newMustDoCopyIn = arg.entity->GetMayNeedCopyIn();
     newMustDoCopyOut = arg.entity->GetMayNeedCopyOut();
 #if 1
-    llvm::dbgs() << "copyinout: CALLER " <<
-        "copy-in: old=" << mustDoCopyIn << ", new=" << newMustDoCopyIn <<
-        "| copy-out: old=" << mustDoCopyOut << ", new=" << newMustDoCopyOut <<
-        "\n";
+    llvm::dbgs() << "copyinout: CALLER " << "copy-in: old=" << mustDoCopyIn
+                 << ", new=" << newMustDoCopyIn
+                 << "| copy-out: old=" << mustDoCopyOut
+                 << ", new=" << newMustDoCopyOut << "\n";
 #endif
   } else {
 #if 1
-    llvm::dbgs() << "copyinout: CALLEE " <<
-        "copy-in=" << mustDoCopyIn << ", copy-out=" << mustDoCopyOut << "\n";
+    llvm::dbgs() << "copyinout: CALLEE " << "copy-in=" << mustDoCopyIn
+                 << ", copy-out=" << mustDoCopyOut << "\n";
 #endif
   }
   mustDoCopyIn = newMustDoCopyIn;

>From 30d36b0b9644df27d1d248e6d52de6bc45c58442 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 15 Aug 2025 17:37:24 -0400
Subject: [PATCH 34/46] Expaned IsAssumedShape() checks and moved them to
 Fortran::evaluate namespace. In DetermineCopyInOutArgument() check for
 assumed shape and assumed rank

---
 flang/include/flang/Evaluate/tools.h       | 22 +++++++++++++++++++-
 flang/lib/Evaluate/call.cpp                |  9 +++++++-
 flang/lib/Evaluate/check-expression.cpp    |  2 +-
 flang/lib/Evaluate/tools.cpp               | 24 +++++++++++++++-------
 flang/lib/Lower/ConvertVariable.cpp        |  2 +-
 flang/lib/Semantics/check-call.cpp         |  4 ++--
 flang/lib/Semantics/check-declarations.cpp |  4 ++--
 flang/lib/Semantics/check-omp-loop.cpp     |  2 +-
 flang/lib/Semantics/resolve-directives.cpp |  4 ++--
 9 files changed, 55 insertions(+), 18 deletions(-)

diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index 0be3f66321e4f..95e60379f7e63 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -103,6 +103,27 @@ template <typename A> bool IsAssumedRank(const A *x) {
   return x && IsAssumedRank(*x);
 }
 
+// Predicate: true when an expression is assumed-shape
+bool IsAssumedShape(const Symbol &);
+bool IsAssumedShape(const ActualArgument &);
+template <typename A> bool IsAssumedShape(const A &) { return false; }
+template <typename A> bool IsAssumedShape(const Designator<A> &designator) {
+  if (const auto *symbol{std::get_if<SymbolRef>(&designator.u)}) {
+    return evaluate::IsAssumedShape(symbol->get());
+  } else {
+    return false;
+  }
+}
+template <typename T> bool IsAssumedShape(const Expr<T> &expr) {
+  return common::visit([](const auto &x) { return IsAssumedShape(x); }, expr.u);
+}
+template <typename A> bool IsAssumedShape(const std::optional<A> &x) {
+  return x && IsAssumedShape(*x);
+}
+template <typename A> bool IsAssumedShape(const A *x) {
+  return x && IsAssumedShape(*x);
+}
+
 // Finds the corank of an entity, possibly packaged in various ways.
 // Unlike rank, only data references have corank > 0.
 int GetCorank(const ActualArgument &);
@@ -1549,7 +1570,6 @@ bool IsAllocatableOrObjectPointer(const Symbol *);
 bool IsAutomatic(const Symbol &);
 bool IsSaved(const Symbol &); // saved implicitly or explicitly
 bool IsDummy(const Symbol &);
-bool IsAssumedShape(const Symbol &);
 bool IsDeferredShape(const Symbol &);
 bool IsFunctionResult(const Symbol &);
 bool IsKindTypeParameter(const Symbol &);
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 5a8ef8de92305..90356f6a34fcf 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -346,7 +346,14 @@ static void DetermineCopyInOutArgument(
     return;
   }
 
-  if (!dummyObj->ignoreTKR.test(common::IgnoreTKR::Type)) {
+  bool dummyIsAssumedShape{dummyObj->type.attrs().test(
+      characteristics::TypeAndShape::Attr::AssumedShape)};
+  bool actualIsAssumedShape{IsAssumedShape(actual)};
+  if ((actualIsAssumedRank && dummyIsAssumedRank) ||
+      (actualIsAssumedShape && dummyIsAssumedShape)) {
+      // Assumed-rank and assumed-shape arrays are represented by descriptors,
+      // so don't need to do polymorphic check.
+  } else if (!dummyObj->ignoreTKR.test(common::IgnoreTKR::Type)) {
     // flang supports limited cases of passing polymorphic to non-polimorphic.
     // These cases require temporary of non-polymorphic type. (For example,
     // the actual argument could be polymorphic array of child type,
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 10361b3b32bc5..d8780c2ca5aa5 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1002,7 +1002,7 @@ class IsContiguousHelper
         return Base::operator()(ultimate); // use expr
       }
     } else if (semantics::IsPointer(ultimate) ||
-        semantics::IsAssumedShape(ultimate) || IsAssumedRank(ultimate)) {
+        IsAssumedShape(ultimate) || IsAssumedRank(ultimate)) {
       return std::nullopt;
     } else if (ultimate.has<semantics::ObjectEntityDetails>()) {
       return true;
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 6eb910367e0a1..175f1e4ecfdb1 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -913,6 +913,23 @@ bool IsAssumedRank(const ActualArgument &arg) {
   }
 }
 
+bool IsAssumedShape(const Symbol &symbol) {
+  const Symbol &ultimate{ResolveAssociations(symbol)};
+  const auto *object{ultimate.detailsIf<semantics::ObjectEntityDetails>()};
+  return object && object->IsAssumedShape() &&
+      !semantics::IsAllocatableOrObjectPointer(&ultimate);
+}
+
+bool IsAssumedShape(const ActualArgument &arg) {
+  if (const auto *expr{arg.UnwrapExpr()}) {
+    return IsAssumedShape(*expr);
+  } else {
+    const Symbol *assumedTypeDummy{arg.GetAssumedTypeDummy()};
+    CHECK(assumedTypeDummy);
+    return IsAssumedShape(*assumedTypeDummy);
+  }
+}
+
 int GetCorank(const ActualArgument &arg) {
   const auto *expr{arg.UnwrapExpr()};
   return GetCorank(*expr);
@@ -2317,13 +2334,6 @@ bool IsDummy(const Symbol &symbol) {
       ResolveAssociations(symbol).details());
 }
 
-bool IsAssumedShape(const Symbol &symbol) {
-  const Symbol &ultimate{ResolveAssociations(symbol)};
-  const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
-  return object && object->IsAssumedShape() &&
-      !semantics::IsAllocatableOrObjectPointer(&ultimate);
-}
-
 bool IsDeferredShape(const Symbol &symbol) {
   const Symbol &ultimate{ResolveAssociations(symbol)};
   const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index fd66592bc285b..2cd666b85bb21 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -1111,7 +1111,7 @@ static bool needsRepack(Fortran::lower::AbstractConverter &converter,
   const auto &attrs = sym.attrs();
   if (!converter.getLoweringOptions().getRepackArrays() ||
       !converter.isRegisteredDummySymbol(sym) ||
-      !Fortran::semantics::IsAssumedShape(sym) ||
+      !Fortran::evaluate::IsAssumedShape(sym) ||
       Fortran::evaluate::IsSimplyContiguous(sym,
                                             converter.getFoldingContext()) ||
       // TARGET dummy may be accessed indirectly, so it is unsafe
diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index 83f59f0cac3df..70e79df42cb6e 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -600,7 +600,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
                   "Element of pointer array may not be associated with a %s array"_err_en_US,
                   dummyName);
             }
-          } else if (IsAssumedShape(*actualLastSymbol) &&
+          } else if (evaluate::IsAssumedShape(*actualLastSymbol) &&
               !dummy.ignoreTKR.test(common::IgnoreTKR::Contiguous)) {
             if (isOkBecauseContiguous) {
               context.Warn(
@@ -1390,7 +1390,7 @@ static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg,
                       assumed.name(), dummyName);
                 } else if (object.type.attrs().test(characteristics::
                                    TypeAndShape::Attr::AssumedRank) &&
-                    !IsAssumedShape(assumed) &&
+                    !evaluate::IsAssumedShape(assumed) &&
                     !evaluate::IsAssumedRank(assumed)) {
                   messages.Say( // C711
                       "Assumed-type '%s' must be either assumed shape or assumed rank to be associated with assumed rank %s"_err_en_US,
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index d769f221b1983..a72b850d2ba5f 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -1206,7 +1206,7 @@ void CheckHelper::CheckObjectEntity(
   if (derived && derived->IsVectorType()) {
     CHECK(type);
     std::string typeName{type->AsFortran()};
-    if (IsAssumedShape(symbol)) {
+    if (evaluate::IsAssumedShape(symbol)) {
       SayWithDeclaration(symbol,
           "Assumed-shape entity of %s type is not supported"_err_en_US,
           typeName);
@@ -2427,7 +2427,7 @@ void CheckHelper::CheckVolatile(const Symbol &symbol,
 
 void CheckHelper::CheckContiguous(const Symbol &symbol) {
   if (evaluate::IsVariable(symbol) &&
-      ((IsPointer(symbol) && symbol.Rank() > 0) || IsAssumedShape(symbol) ||
+      ((IsPointer(symbol) && symbol.Rank() > 0) || evaluate::IsAssumedShape(symbol) ||
           evaluate::IsAssumedRank(symbol))) {
   } else {
     parser::MessageFixedText msg{symbol.owner().IsDerivedType()
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 8dad1f5d605e7..166462da2fbb3 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -590,7 +590,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
         if (linearMod->v != parser::OmpLinearModifier::Value::Ref) {
           CheckIntegerNoRef(symbol, source);
         } else {
-          if (!IsAllocatable(*symbol) && !IsAssumedShape(*symbol) &&
+          if (!IsAllocatable(*symbol) && !evaluate::IsAssumedShape(*symbol) &&
               !IsPolymorphic(*symbol)) {
             context_.Say(source,
                 "The list item `%s` specified with the REF '%s' must be polymorphic variable, assumed-shape array, or a variable with the `ALLOCATABLE` attribute"_err_en_US,
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index fe0d2a73805de..e2832121f98b6 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2438,7 +2438,7 @@ static bool IsTargetCaptureImplicitlyFirstprivatizeable(const Symbol &symbol,
     // investigate the flags we can intermix with.
     if (!(dsa & (dataSharingAttributeFlags | dataMappingAttributeFlags))
             .none() ||
-        !checkSym.flags().none() || semantics::IsAssumedShape(checkSym) ||
+        !checkSym.flags().none() || evaluate::IsAssumedShape(checkSym) ||
         semantics::IsAllocatableOrPointer(checkSym)) {
       return false;
     }
@@ -3164,7 +3164,7 @@ static bool IsSymbolPrivate(const Symbol &symbol) {
     case Scope::Kind::Subprogram:
     case Scope::Kind::BlockConstruct:
       return !symbol.attrs().test(Attr::SAVE) &&
-          !symbol.attrs().test(Attr::PARAMETER) && !IsAssumedShape(symbol) &&
+          !symbol.attrs().test(Attr::PARAMETER) && !evaluate::IsAssumedShape(symbol) &&
           !symbol.flags().test(Symbol::Flag::InCommonBlock);
     default:
       return false;

>From 47f26560328e3f53127b72fd319522e07017271b Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 15 Aug 2025 17:38:21 -0400
Subject: [PATCH 35/46] clang-format

---
 flang/lib/Evaluate/call.cpp                | 4 ++--
 flang/lib/Evaluate/check-expression.cpp    | 4 ++--
 flang/lib/Semantics/check-declarations.cpp | 3 ++-
 flang/lib/Semantics/resolve-directives.cpp | 5 +++--
 4 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 90356f6a34fcf..839fecab41380 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -351,8 +351,8 @@ static void DetermineCopyInOutArgument(
   bool actualIsAssumedShape{IsAssumedShape(actual)};
   if ((actualIsAssumedRank && dummyIsAssumedRank) ||
       (actualIsAssumedShape && dummyIsAssumedShape)) {
-      // Assumed-rank and assumed-shape arrays are represented by descriptors,
-      // so don't need to do polymorphic check.
+    // Assumed-rank and assumed-shape arrays are represented by descriptors,
+    // so don't need to do polymorphic check.
   } else if (!dummyObj->ignoreTKR.test(common::IgnoreTKR::Type)) {
     // flang supports limited cases of passing polymorphic to non-polimorphic.
     // These cases require temporary of non-polymorphic type. (For example,
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index d8780c2ca5aa5..771f873de37e8 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1001,8 +1001,8 @@ class IsContiguousHelper
       } else {
         return Base::operator()(ultimate); // use expr
       }
-    } else if (semantics::IsPointer(ultimate) ||
-        IsAssumedShape(ultimate) || IsAssumedRank(ultimate)) {
+    } else if (semantics::IsPointer(ultimate) || IsAssumedShape(ultimate) ||
+        IsAssumedRank(ultimate)) {
       return std::nullopt;
     } else if (ultimate.has<semantics::ObjectEntityDetails>()) {
       return true;
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index a72b850d2ba5f..d44421f05f0a4 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -2427,7 +2427,8 @@ void CheckHelper::CheckVolatile(const Symbol &symbol,
 
 void CheckHelper::CheckContiguous(const Symbol &symbol) {
   if (evaluate::IsVariable(symbol) &&
-      ((IsPointer(symbol) && symbol.Rank() > 0) || evaluate::IsAssumedShape(symbol) ||
+      ((IsPointer(symbol) && symbol.Rank() > 0) ||
+          evaluate::IsAssumedShape(symbol) ||
           evaluate::IsAssumedRank(symbol))) {
   } else {
     parser::MessageFixedText msg{symbol.owner().IsDerivedType()
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index e2832121f98b6..3ebf099205d39 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2437,7 +2437,7 @@ static bool IsTargetCaptureImplicitlyFirstprivatizeable(const Symbol &symbol,
     // TODO: Relax restriction as we progress privitization and further
     // investigate the flags we can intermix with.
     if (!(dsa & (dataSharingAttributeFlags | dataMappingAttributeFlags))
-            .none() ||
+             .none() ||
         !checkSym.flags().none() || evaluate::IsAssumedShape(checkSym) ||
         semantics::IsAllocatableOrPointer(checkSym)) {
       return false;
@@ -3164,7 +3164,8 @@ static bool IsSymbolPrivate(const Symbol &symbol) {
     case Scope::Kind::Subprogram:
     case Scope::Kind::BlockConstruct:
       return !symbol.attrs().test(Attr::SAVE) &&
-          !symbol.attrs().test(Attr::PARAMETER) && !evaluate::IsAssumedShape(symbol) &&
+          !symbol.attrs().test(Attr::PARAMETER) &&
+          !evaluate::IsAssumedShape(symbol) &&
           !symbol.flags().test(Symbol::Flag::InCommonBlock);
     default:
       return false;

>From aa65a3ef202870c9be1bb777ae532e55a26827c6 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 09:44:43 -0400
Subject: [PATCH 36/46] Passes LIT tests

---
 flang/include/flang/Lower/CallInterface.h |  2 --
 flang/lib/Lower/ConvertCall.cpp           | 23 ++++++++++-------------
 flang/lib/Semantics/expression.cpp        |  1 +
 3 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index 6e3bdc0d9104c..72bc9dd890a94 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -159,8 +159,6 @@ class CallInterface {
   /// PassedEntity is what is provided back to the CallInterface user.
   /// It describe how the entity is plugged in the interface
   struct PassedEntity {
-    /// Helps with caller/callee differentiation
-    static constexpr bool isCaller = std::is_same_v<T, CallerInterface>;
     /// Is the dummy argument optional?
     bool isOptional() const;
     /// Can the argument be modified by the callee?
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 1d92a634059ea..fbc3837f61dcb 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1204,6 +1204,10 @@ static bool isParameterObjectOrSubObject(hlfir::Entity entity) {
 ///   fir.box_char...).
 /// This function should only be called with an actual that is present.
 /// The optional aspects must be handled by this function user.
+///
+/// Note: while Fortran::lower::CallerInterface::PassedEntity (the type of arg)
+/// is technically a template type, in the prepare*ActualArgument() calls
+/// it resolves to Fortran::evaluate::ActualArgument *
 static PreparedDummyArgument preparePresentUserCallActualArgument(
     mlir::Location loc, fir::FirOpBuilder &builder,
     const Fortran::lower::PreparedActualArgument &preparedActual,
@@ -1263,21 +1267,14 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
   bool mustDoCopyOut = mustDoCopyIn && arg.mayBeModifiedByCall();
   bool newMustDoCopyIn = false;
   bool newMustDoCopyOut = false;
-  if constexpr (Fortran::lower::CallerInterface::PassedEntity::isCaller) {
-    newMustDoCopyIn = arg.entity->GetMayNeedCopyIn();
-    newMustDoCopyOut = arg.entity->GetMayNeedCopyOut();
+  newMustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
+  newMustDoCopyOut = newMustDoCopyIn && arg.entity->GetMayNeedCopyOut();
 #if 1
-    llvm::dbgs() << "copyinout: CALLER " << "copy-in: old=" << mustDoCopyIn
-                 << ", new=" << newMustDoCopyIn
-                 << "| copy-out: old=" << mustDoCopyOut
-                 << ", new=" << newMustDoCopyOut << "\n";
+  llvm::dbgs() << "copyinout: CALLER " << "copy-in: old=" << mustDoCopyIn
+               << ", new=" << newMustDoCopyIn
+               << "| copy-out: old=" << mustDoCopyOut
+               << ", new=" << newMustDoCopyOut << "\n";
 #endif
-  } else {
-#if 1
-    llvm::dbgs() << "copyinout: CALLEE " << "copy-in=" << mustDoCopyIn
-                 << ", copy-out=" << mustDoCopyOut << "\n";
-#endif
-  }
   mustDoCopyIn = newMustDoCopyIn;
   mustDoCopyOut = newMustDoCopyOut;
 
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 56ae4099c5322..18ee83cf4da45 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -4389,6 +4389,7 @@ MaybeExpr ExpressionAnalyzer::MakeFunctionRef(parser::CharBlock callSite,
     if (chars->functionResult) {
       const auto &result{*chars->functionResult};
       ProcedureRef procRef{std::move(proc), std::move(arguments)};
+      procRef.DetermineCopyInOut();
       if (result.IsProcedurePointer()) {
         return Expr<SomeType>{std::move(procRef)};
       } else {

>From d285ded9079b4bebb2a30d5062efc8e4ad09500b Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 10:10:03 -0400
Subject: [PATCH 37/46] Clean up in lowering to switch to the new checks

---
 flang/lib/Lower/ConvertCall.cpp | 36 ++-------------------------------
 1 file changed, 2 insertions(+), 34 deletions(-)

diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index fbc3837f61dcb..d12f51218b606 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1161,18 +1161,6 @@ mlir::Value static getZeroLowerBounds(mlir::Location loc,
   return builder.genShift(loc, lowerBounds);
 }
 
-static bool
-isSimplyContiguous(const Fortran::evaluate::ActualArgument &arg,
-                   Fortran::evaluate::FoldingContext &foldingContext) {
-  if (const auto *expr = arg.UnwrapExpr())
-    return Fortran::evaluate::IsSimplyContiguous(*expr, foldingContext);
-  const Fortran::semantics::Symbol *sym = arg.GetAssumedTypeDummy();
-  assert(sym &&
-         "expect ActualArguments to be expression or assumed-type symbols");
-  return sym->Rank() == 0 ||
-         Fortran::evaluate::IsSimplyContiguous(*sym, foldingContext);
-}
-
 static bool isParameterObjectOrSubObject(hlfir::Entity entity) {
   mlir::Value base = entity;
   bool foundParameter = false;
@@ -1215,9 +1203,6 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
     const Fortran::lower::CallerInterface::PassedEntity &arg,
     CallContext &callContext) {
 
-  Fortran::evaluate::FoldingContext &foldingContext =
-      callContext.converter.getFoldingContext();
-
   // Step 1: get the actual argument, which includes addressing the
   // element if this is an array in an elemental call.
   hlfir::Entity actual = preparedActual.getActual(loc, builder);
@@ -1258,25 +1243,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       passingPolymorphicToNonPolymorphic &&
       (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType));
 
-  // The simple contiguity of the actual is "lost" when passing a polymorphic
-  // to a non polymorphic entity because the dummy dynamic type matters for
-  // the contiguity.
-  bool mustDoCopyIn = actual.isArray() && arg.mustBeMadeContiguous() &&
-                      (passingPolymorphicToNonPolymorphic ||
-                       !isSimplyContiguous(*arg.entity, foldingContext));
-  bool mustDoCopyOut = mustDoCopyIn && arg.mayBeModifiedByCall();
-  bool newMustDoCopyIn = false;
-  bool newMustDoCopyOut = false;
-  newMustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
-  newMustDoCopyOut = newMustDoCopyIn && arg.entity->GetMayNeedCopyOut();
-#if 1
-  llvm::dbgs() << "copyinout: CALLER " << "copy-in: old=" << mustDoCopyIn
-               << ", new=" << newMustDoCopyIn
-               << "| copy-out: old=" << mustDoCopyOut
-               << ", new=" << newMustDoCopyOut << "\n";
-#endif
-  mustDoCopyIn = newMustDoCopyIn;
-  mustDoCopyOut = newMustDoCopyOut;
+  bool mustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
+  bool mustDoCopyOut = mustDoCopyIn && arg.entity->GetMayNeedCopyOut();
 
   const bool actualIsAssumedRank = actual.isAssumedRank();
   // Create dummy type with actual argument rank when the dummy is an assumed

>From 413eafd25752fa4e0e440c56c55d30a9fff1dea5 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 11:33:52 -0400
Subject: [PATCH 38/46] Ignore rank support

---
 flang/lib/Evaluate/call.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 839fecab41380..a6031e2fa206a 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -307,7 +307,9 @@ static void DetermineCopyInOutArgument(
   bool dummyIsAssumedRank{dummyObj->type.attrs().test(
       characteristics::TypeAndShape::Attr::AssumedRank)};
   bool dummyIsArray{dummyIsAssumedRank || dummyObj->type.Rank() > 0};
-  if (!actualIsArray || !dummyIsArray) {
+  bool treatDummyScalarAsArray{dummyObj->type.Rank() == 0 &&
+      dummyObj->ignoreTKR.test(common::IgnoreTKR::Rank)};
+  if (!actualIsArray || !(dummyIsArray || treatDummyScalarAsArray)) {
     return;
   }
 
@@ -335,6 +337,7 @@ static void DetermineCopyInOutArgument(
   bool dummyIsAssumedSize{dummyObj->type.attrs().test(
       characteristics::TypeAndShape::Attr::AssumedSize)};
   bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize ||
+      treatDummyScalarAsArray ||
       dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Contiguous)};
   if (!actualTreatAsContiguous && dummyNeedsContiguity) {
     setCopyIn();

>From 4590955c478d30a739b0b66d4f63a13f1de3b1be Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 12:20:16 -0400
Subject: [PATCH 39/46] Polymorphic dummy with ignore rank

---
 flang/lib/Evaluate/call.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index a6031e2fa206a..51ce3a04166ac 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -336,8 +336,12 @@ static void DetermineCopyInOutArgument(
   bool dummyIsExplicitShape{dummyObj->type.IsExplicitShape()};
   bool dummyIsAssumedSize{dummyObj->type.attrs().test(
       characteristics::TypeAndShape::Attr::AssumedSize)};
+  bool dummyIsPolymorphic{dummyObj->type.type().IsPolymorphic()};
+  // Explicit shape and assumed size arrays must be contiguous
   bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize ||
-      treatDummyScalarAsArray ||
+      // Polymorphic dummy is descriptor based, so should be able to handle
+      // discontigunity.
+      (treatDummyScalarAsArray && !dummyIsPolymorphic) ||
       dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Contiguous)};
   if (!actualTreatAsContiguous && dummyNeedsContiguity) {
     setCopyIn();
@@ -364,7 +368,6 @@ static void DetermineCopyInOutArgument(
     auto actualType{characteristics::TypeAndShape::Characterize(
         actual, sc.foldingContext())};
     bool actualIsPolymorphic{actualType->type().IsPolymorphic()};
-    bool dummyIsPolymorphic{dummyObj->type.type().IsPolymorphic()};
     if (actualIsPolymorphic && !dummyIsPolymorphic) {
       setCopyIn();
       setCopyOut();

>From d026e0dfe0a80c372a03cc5992c993eeaf2ac571 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 12:28:39 -0400
Subject: [PATCH 40/46] clang-format (upstream)

---
 flang/lib/Evaluate/call.cpp                | 4 ++--
 flang/lib/Semantics/resolve-directives.cpp | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 51ce3a04166ac..353f17b57f15d 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -260,8 +260,8 @@ static void DetermineCopyInOutArgument(
     // Actual argument expressions that aren’t variables are copy-in, but
     // not copy-out.
     actual.SetMayNeedCopyIn();
-  } else if (bool actualIsArray{actual.Rank() > 0}; actualIsArray &&
-             !IsSimplyContiguous(actual, sc.foldingContext())) {
+  } else if (bool actualIsArray{actual.Rank() > 0};
+    actualIsArray &&!IsSimplyContiguous(actual, sc.foldingContext())) {
     // Actual arguments that are variables are copy-in when non-contiguous.
     // They are copy-out when don't have vector subscripts
     actual.SetMayNeedCopyIn();
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 3ebf099205d39..5a244e5183e39 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2437,7 +2437,7 @@ static bool IsTargetCaptureImplicitlyFirstprivatizeable(const Symbol &symbol,
     // TODO: Relax restriction as we progress privitization and further
     // investigate the flags we can intermix with.
     if (!(dsa & (dataSharingAttributeFlags | dataMappingAttributeFlags))
-             .none() ||
+            .none() ||
         !checkSym.flags().none() || evaluate::IsAssumedShape(checkSym) ||
         semantics::IsAllocatableOrPointer(checkSym)) {
       return false;

>From 0289de2ca7f6e90efa73416e969084f9c520d6e6 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 12:55:50 -0400
Subject: [PATCH 41/46] clang-format (upstream)

---
 flang/lib/Evaluate/call.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 353f17b57f15d..bdf9561d9582b 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -261,7 +261,7 @@ static void DetermineCopyInOutArgument(
     // not copy-out.
     actual.SetMayNeedCopyIn();
   } else if (bool actualIsArray{actual.Rank() > 0};
-    actualIsArray &&!IsSimplyContiguous(actual, sc.foldingContext())) {
+      actualIsArray &&!IsSimplyContiguous(actual, sc.foldingContext())) {
     // Actual arguments that are variables are copy-in when non-contiguous.
     // They are copy-out when don't have vector subscripts
     actual.SetMayNeedCopyIn();

>From 2464dd3e5b376e25f78cbc862b3f27d6b779cf3d Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 12:58:40 -0400
Subject: [PATCH 42/46] clang-format (started using upstream)

---
 flang/lib/Evaluate/call.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index bdf9561d9582b..03fdc9b147841 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -261,7 +261,7 @@ static void DetermineCopyInOutArgument(
     // not copy-out.
     actual.SetMayNeedCopyIn();
   } else if (bool actualIsArray{actual.Rank() > 0};
-      actualIsArray &&!IsSimplyContiguous(actual, sc.foldingContext())) {
+      actualIsArray && !IsSimplyContiguous(actual, sc.foldingContext())) {
     // Actual arguments that are variables are copy-in when non-contiguous.
     // They are copy-out when don't have vector subscripts
     actual.SetMayNeedCopyIn();

>From f76c9e33240e7dcd91b92d2d7d8c98d7431a37c6 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 13:39:48 -0400
Subject: [PATCH 43/46] Added LIT test

---
 flang/test/Lower/force-temp.f90 | 35 +++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 flang/test/Lower/force-temp.f90

diff --git a/flang/test/Lower/force-temp.f90 b/flang/test/Lower/force-temp.f90
new file mode 100644
index 0000000000000..1cfa218cfe7ab
--- /dev/null
+++ b/flang/test/Lower/force-temp.f90
@@ -0,0 +1,35 @@
+! RUN: bbc -emit-hlfir -o - %s | FileCheck %s
+! Ensure that copy-in/copy-out happens with specific ignore_tkr settings
+module test
+  interface
+    subroutine pass_ignore_tkr(buf)
+      implicit none
+      !DIR$ IGNORE_TKR buf
+      real :: buf
+    end subroutine
+    subroutine pass_ignore_tkr_c(buf)
+      implicit none
+      !DIR$ IGNORE_TKR (tkrc) buf
+      real :: buf
+    end subroutine
+  end interface
+contains
+  subroutine s1(buf)
+!CHECK-LABEL: func.func @_QMtestPs1
+!CHECK: hlfir.copy_in
+!CHECK: fir.call @_QPpass_ignore_tkr
+!CHECK: hlfir.copy_out
+    real, intent(inout) :: buf(:)
+    ! Create temp here
+    call pass_ignore_tkr(buf)
+  end subroutine
+  subroutine s2(buf)
+!CHECK-LABEL: func.func @_QMtestPs2
+!CHECK-NOT: hlfir.copy_in
+!CHECK: fir.call @_QPpass_ignore_tkr_c
+!CHECK-NOT: hlfir.copy_out
+    real, intent(inout) :: buf(:)
+    ! Don't create temp here
+    call pass_ignore_tkr_c(buf)
+  end subroutine
+end module

>From b12d2c82f35c0f2aa7ff9713c2c3aa866d73768b Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 15:56:28 -0400
Subject: [PATCH 44/46] Implement copy-in/copy-out determination in
 Fortran::evaluate::MayNeedCopyInOut()

---
 .../include/flang/Evaluate/check-expression.h |  22 +++
 flang/lib/Evaluate/check-expression.cpp       | 129 ++++++++++++++++++
 flang/lib/Lower/ConvertCall.cpp               |   8 +-
 3 files changed, 157 insertions(+), 2 deletions(-)

diff --git a/flang/include/flang/Evaluate/check-expression.h b/flang/include/flang/Evaluate/check-expression.h
index 7059e03b408c1..ff5b01edfce07 100644
--- a/flang/include/flang/Evaluate/check-expression.h
+++ b/flang/include/flang/Evaluate/check-expression.h
@@ -163,5 +163,27 @@ extern template bool IsErrorExpr(const Expr<SomeType> &);
 std::optional<parser::Message> CheckStatementFunction(
     const Symbol &, const Expr<SomeType> &, FoldingContext &);
 
+// Returns a pair of Booleans. The first boolean specifies whether given actual
+// argument may need copy-in operation and the second Boolean specifies whether
+// copy-out may be necessary. This function works with implicit interface
+// procedures.
+std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &,
+    FoldingContext &);
+
+// Returns a pair of Booleans. The first boolean specifies whether given actual
+// and dummy argument pair may need copy-in operation for the actual argument,
+// and the second Boolean specifies whether copy-out may be necessary.
+// This function works with explicit interface procedures.
+std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &,
+    const characteristics::DummyArgument &, FoldingContext &);
+
+inline std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &actual,
+    const characteristics::DummyArgument *dummy, FoldingContext &fc) {
+  if (dummy)
+    return MayNeedCopyInOut(actual, *dummy, fc);
+  else
+    return MayNeedCopyInOut(actual, fc);
+}
+
 } // namespace Fortran::evaluate
 #endif
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 771f873de37e8..dd174a564f0c9 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1446,4 +1446,133 @@ std::optional<parser::Message> CheckStatementFunction(
   return StmtFunctionChecker{sf, context}(expr);
 }
 
+std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &actual,
+    FoldingContext &fc) {
+  bool mayNeedCopyIn{false};
+  bool mayNeedCopyOut{false};
+  if (actual.isAlternateReturn()) {
+    return {mayNeedCopyIn, mayNeedCopyOut};
+  }
+  if (!evaluate::IsVariable(actual)) {
+    // Actual argument expressions that aren’t variables are copy-in, but
+    // not copy-out.
+    mayNeedCopyIn = true;
+  } else if (bool actualIsArray{actual.Rank() > 0};
+      actualIsArray && !IsSimplyContiguous(actual, fc)) {
+    // Actual arguments that are variables are copy-in when non-contiguous.
+    // They are copy-out when don't have vector subscripts
+    mayNeedCopyIn = true;
+    if (!HasVectorSubscript(actual)) {
+      mayNeedCopyOut = true;
+    }
+  } else if (ExtractCoarrayRef(actual)) {
+    // Coindexed actual args need copy-in and copy-out
+    mayNeedCopyIn = true;
+    mayNeedCopyOut = true;
+  }
+
+  return {mayNeedCopyIn, mayNeedCopyOut};
+}
+
+std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &actual,
+    const characteristics::DummyArgument &dummy, FoldingContext &fc) {
+  bool mayNeedCopyIn{false};
+  bool mayNeedCopyOut{false};
+  if (actual.isAlternateReturn()) {
+    return {mayNeedCopyIn, mayNeedCopyOut};
+  }
+  if (!evaluate::IsVariable(actual)) {
+    // Actual argument expressions that aren’t variables are copy-in, but
+    // not copy-out.
+    mayNeedCopyIn = true;
+    return {mayNeedCopyIn, mayNeedCopyOut};
+  }
+  const auto *dummyObj{std::get_if<characteristics::DummyDataObject>(&dummy.u)};
+  if (!dummyObj) {
+    // Only DummyDataObject has the information we need
+    return {mayNeedCopyIn, mayNeedCopyOut};
+  }
+  // Pass by value, always copy-in, never copy-out
+  bool dummyIsValue{
+      dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Value)};
+  if (dummyIsValue) {
+    mayNeedCopyIn = true;
+    return {mayNeedCopyIn, mayNeedCopyOut};
+  }
+  // All the checks below are for arrays
+
+  bool actualIsAssumedRank{evaluate::IsAssumedRank(actual)};
+  bool actualIsArray{actualIsAssumedRank || actual.Rank() > 0};
+  bool dummyIsAssumedRank{dummyObj->type.attrs().test(
+      characteristics::TypeAndShape::Attr::AssumedRank)};
+  bool dummyIsArray{dummyIsAssumedRank || dummyObj->type.Rank() > 0};
+  bool treatDummyScalarAsArray{dummyObj->type.Rank() == 0 &&
+      dummyObj->ignoreTKR.test(common::IgnoreTKR::Rank)};
+  if (!actualIsArray || !(dummyIsArray || treatDummyScalarAsArray)) {
+    return {mayNeedCopyIn, mayNeedCopyOut};
+  }
+
+  bool dummyIntentIn{dummyObj->intent == common::Intent::In};
+  bool dummyIntentOut{dummyObj->intent == common::Intent::Out};
+  auto setCopyIn = [&]() {
+    if (!dummyIntentOut) {
+      // INTENT(OUT) dummy args never need copy-in
+      mayNeedCopyIn = true;
+    }
+  };
+  auto setCopyOut = [&]() {
+    if (!dummyIntentIn) {
+      // INTENT(IN) dummy args never need copy-out
+      mayNeedCopyOut = true;
+    }
+  };
+
+  // Check actual contiguity, unless dummy doesn't care
+  bool actualTreatAsContiguous{
+      dummyObj->ignoreTKR.test(common::IgnoreTKR::Contiguous) ||
+      IsSimplyContiguous(actual, fc)};
+  bool actualHasVectorSubscript{HasVectorSubscript(actual)};
+  bool dummyIsExplicitShape{dummyObj->type.IsExplicitShape()};
+  bool dummyIsAssumedSize{dummyObj->type.attrs().test(
+      characteristics::TypeAndShape::Attr::AssumedSize)};
+  bool dummyIsPolymorphic{dummyObj->type.type().IsPolymorphic()};
+  // Explicit shape and assumed size arrays must be contiguous
+  bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize ||
+      // Polymorphic dummy is descriptor based, so should be able to handle
+      // discontigunity.
+      (treatDummyScalarAsArray && !dummyIsPolymorphic) ||
+      dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Contiguous)};
+  if (!actualTreatAsContiguous && dummyNeedsContiguity) {
+    setCopyIn();
+    // Cannot do copy-out for vector subscripts: there could be repeated
+    // indices, for example
+    if (!actualHasVectorSubscript) {
+      setCopyOut();
+    }
+    return {mayNeedCopyIn, mayNeedCopyOut};
+  }
+
+  bool dummyIsAssumedShape{dummyObj->type.attrs().test(
+      characteristics::TypeAndShape::Attr::AssumedShape)};
+  bool actualIsAssumedShape{IsAssumedShape(actual)};
+  if ((actualIsAssumedRank && dummyIsAssumedRank) ||
+      (actualIsAssumedShape && dummyIsAssumedShape)) {
+    // Assumed-rank and assumed-shape arrays are represented by descriptors,
+    // so don't need to do polymorphic check.
+  } else if (!dummyObj->ignoreTKR.test(common::IgnoreTKR::Type)) {
+    // flang supports limited cases of passing polymorphic to non-polimorphic.
+    // These cases require temporary of non-polymorphic type. (For example,
+    // the actual argument could be polymorphic array of child type,
+    // while the dummy argument could be non-polymorphic array of parent type.)
+    auto actualType{characteristics::TypeAndShape::Characterize(actual, fc)};
+    bool actualIsPolymorphic{actualType->type().IsPolymorphic()};
+    if (actualIsPolymorphic && !dummyIsPolymorphic) {
+      setCopyIn();
+      setCopyOut();
+    }
+  }
+
+  return {mayNeedCopyIn, mayNeedCopyOut};
+}
+
 } // namespace Fortran::evaluate
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index d12f51218b606..d7a5efdb00cfb 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1243,8 +1243,12 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       passingPolymorphicToNonPolymorphic &&
       (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType));
 
-  bool mustDoCopyIn = actual.isArray() && arg.entity->GetMayNeedCopyIn();
-  bool mustDoCopyOut = mustDoCopyIn && arg.entity->GetMayNeedCopyOut();
+  Fortran::evaluate::FoldingContext &foldingContext{
+      callContext.converter.getFoldingContext()};
+  auto [suggestCopyIn, suggestCopyOut] = Fortran::evaluate::MayNeedCopyInOut(
+      *arg.entity, arg.characteristics, foldingContext);
+  bool mustDoCopyIn = actual.isArray() && suggestCopyIn;
+  bool mustDoCopyOut = mustDoCopyIn && suggestCopyOut;
 
   const bool actualIsAssumedRank = actual.isAssumedRank();
   // Create dummy type with actual argument rank when the dummy is an assumed

>From 24c2040b84bb652c3638ee9b7aeec5c7ddba37fc Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 16:09:18 -0400
Subject: [PATCH 45/46] clang-format

---
 flang/include/flang/Evaluate/check-expression.h | 4 ++--
 flang/lib/Evaluate/check-expression.cpp         | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/flang/include/flang/Evaluate/check-expression.h b/flang/include/flang/Evaluate/check-expression.h
index ff5b01edfce07..0499f353025b5 100644
--- a/flang/include/flang/Evaluate/check-expression.h
+++ b/flang/include/flang/Evaluate/check-expression.h
@@ -167,8 +167,8 @@ std::optional<parser::Message> CheckStatementFunction(
 // argument may need copy-in operation and the second Boolean specifies whether
 // copy-out may be necessary. This function works with implicit interface
 // procedures.
-std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &,
-    FoldingContext &);
+std::pair<bool, bool> MayNeedCopyInOut(
+    const ActualArgument &, FoldingContext &);
 
 // Returns a pair of Booleans. The first boolean specifies whether given actual
 // and dummy argument pair may need copy-in operation for the actual argument,
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index dd174a564f0c9..d34c1381e6b28 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -1446,8 +1446,8 @@ std::optional<parser::Message> CheckStatementFunction(
   return StmtFunctionChecker{sf, context}(expr);
 }
 
-std::pair<bool, bool> MayNeedCopyInOut(const ActualArgument &actual,
-    FoldingContext &fc) {
+std::pair<bool, bool> MayNeedCopyInOut(
+    const ActualArgument &actual, FoldingContext &fc) {
   bool mayNeedCopyIn{false};
   bool mayNeedCopyOut{false};
   if (actual.isAlternateReturn()) {

>From a760320db1ea3d02768978c0ecbb58b3a5af060a Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 18 Aug 2025 19:02:42 -0400
Subject: [PATCH 46/46] Removed the old way to integrate copy-in/copy-out check

---
 flang/include/flang/Evaluate/call.h |  19 +--
 flang/lib/Evaluate/call.cpp         | 196 ----------------------------
 flang/lib/Semantics/expression.cpp  |   2 -
 3 files changed, 2 insertions(+), 215 deletions(-)

diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index efdcee709d435..2a5929b873d74 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -52,7 +52,7 @@ using SymbolRef = common::Reference<const Symbol>;
 
 class ActualArgument {
 public:
-  ENUM_CLASS(Attr, PassedObject, PercentVal, PercentRef, CopyIn, CopyOut);
+  ENUM_CLASS(Attr, PassedObject, PercentVal, PercentRef);
   using Attrs = common::EnumSet<Attr, Attr_enumSize>;
 
   // Dummy arguments that are TYPE(*) can be forwarded as actual arguments.
@@ -131,6 +131,7 @@ class ActualArgument {
     return *this;
   }
 
+  bool Matches(const characteristics::DummyArgument &) const;
   common::Intent dummyIntent() const { return dummyIntent_; }
   ActualArgument &set_dummyIntent(common::Intent intent) {
     dummyIntent_ = intent;
@@ -160,20 +161,6 @@ class ActualArgument {
     return *this;
   }
 
-  // This actual argument may need copy-in before the procedure call
-  bool GetMayNeedCopyIn() const { return attrs_.test(Attr::CopyIn); };
-  ActualArgument &SetMayNeedCopyIn() {
-    attrs_ = attrs_ + Attr::CopyIn;
-    return *this;
-  }
-
-  // This actual argument may need copy-out after the procedure call
-  bool GetMayNeedCopyOut() const { return attrs_.test(Attr::CopyOut); };
-  ActualArgument &SetMayNeedCopyOut() {
-    attrs_ = attrs_ + Attr::CopyOut;
-    return *this;
-  }
-
 private:
   // Subtlety: There is a distinction that must be maintained here between an
   // actual argument expression that is a variable and one that is not,
@@ -285,8 +272,6 @@ class ProcedureRef {
   bool operator==(const ProcedureRef &) const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
-  void DetermineCopyInOut();
-
 protected:
   ProcedureDesignator proc_;
   ActualArguments arguments_;
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 03fdc9b147841..f77df92a7597a 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -12,7 +12,6 @@
 #include "flang/Evaluate/check-expression.h"
 #include "flang/Evaluate/expression.h"
 #include "flang/Evaluate/tools.h"
-#include "flang/Semantics/semantics.h"
 #include "flang/Semantics/symbol.h"
 #include "flang/Support/Fortran.h"
 
@@ -248,199 +247,4 @@ ProcedureRef::~ProcedureRef() {}
 
 void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
 
-// We don't know the dummy argument info (e.g., procedure with implicit
-// interface
-static void DetermineCopyInOutArgument(
-    const characteristics::Procedure &procInfo, ActualArgument &actual,
-    semantics::SemanticsContext &sc) {
-  if (actual.isAlternateReturn()) {
-    return;
-  }
-  if (!evaluate::IsVariable(actual)) {
-    // Actual argument expressions that aren’t variables are copy-in, but
-    // not copy-out.
-    actual.SetMayNeedCopyIn();
-  } else if (bool actualIsArray{actual.Rank() > 0};
-      actualIsArray && !IsSimplyContiguous(actual, sc.foldingContext())) {
-    // Actual arguments that are variables are copy-in when non-contiguous.
-    // They are copy-out when don't have vector subscripts
-    actual.SetMayNeedCopyIn();
-    if (!HasVectorSubscript(actual)) {
-      actual.SetMayNeedCopyOut();
-    }
-  } else if (ExtractCoarrayRef(actual)) {
-    // Coindexed actual args need copy-in and copy-out
-    actual.SetMayNeedCopyIn();
-    actual.SetMayNeedCopyOut();
-  }
-}
-
-static void DetermineCopyInOutArgument(
-    const characteristics::Procedure &procInfo, ActualArgument &actual,
-    characteristics::DummyArgument &dummy, semantics::SemanticsContext &sc) {
-  assert(procInfo.HasExplicitInterface() && "expect explicit interface proc");
-  if (actual.isAlternateReturn()) {
-    return;
-  }
-  if (!evaluate::IsVariable(actual)) {
-    // Actual argument expressions that aren’t variables are copy-in, but
-    // not copy-out.
-    actual.SetMayNeedCopyIn();
-    return;
-  }
-  const auto *dummyObj{std::get_if<characteristics::DummyDataObject>(&dummy.u)};
-  if (!dummyObj) {
-    // Only DummyDataObject has the information we need
-    return;
-  }
-  // Pass by value, always copy-in, never copy-out
-  bool dummyIsValue{
-      dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Value)};
-  if (dummyIsValue) {
-    actual.SetMayNeedCopyIn();
-    return;
-  }
-  // All the checks below are for arrays
-
-  bool actualIsAssumedRank{evaluate::IsAssumedRank(actual)};
-  bool actualIsArray{actualIsAssumedRank || actual.Rank() > 0};
-  bool dummyIsAssumedRank{dummyObj->type.attrs().test(
-      characteristics::TypeAndShape::Attr::AssumedRank)};
-  bool dummyIsArray{dummyIsAssumedRank || dummyObj->type.Rank() > 0};
-  bool treatDummyScalarAsArray{dummyObj->type.Rank() == 0 &&
-      dummyObj->ignoreTKR.test(common::IgnoreTKR::Rank)};
-  if (!actualIsArray || !(dummyIsArray || treatDummyScalarAsArray)) {
-    return;
-  }
-
-  bool dummyIntentIn{dummyObj->intent == common::Intent::In};
-  bool dummyIntentOut{dummyObj->intent == common::Intent::Out};
-  auto setCopyIn = [&]() {
-    if (!dummyIntentOut) {
-      // INTENT(OUT) dummy args never need copy-in
-      actual.SetMayNeedCopyIn();
-    }
-  };
-  auto setCopyOut = [&]() {
-    if (!dummyIntentIn) {
-      // INTENT(IN) dummy args never need copy-out
-      actual.SetMayNeedCopyOut();
-    }
-  };
-
-  // Check actual contiguity, unless dummy doesn't care
-  bool actualTreatAsContiguous{
-      dummyObj->ignoreTKR.test(common::IgnoreTKR::Contiguous) ||
-      IsSimplyContiguous(actual, sc.foldingContext())};
-  bool actualHasVectorSubscript{HasVectorSubscript(actual)};
-  bool dummyIsExplicitShape{dummyObj->type.IsExplicitShape()};
-  bool dummyIsAssumedSize{dummyObj->type.attrs().test(
-      characteristics::TypeAndShape::Attr::AssumedSize)};
-  bool dummyIsPolymorphic{dummyObj->type.type().IsPolymorphic()};
-  // Explicit shape and assumed size arrays must be contiguous
-  bool dummyNeedsContiguity{dummyIsExplicitShape || dummyIsAssumedSize ||
-      // Polymorphic dummy is descriptor based, so should be able to handle
-      // discontigunity.
-      (treatDummyScalarAsArray && !dummyIsPolymorphic) ||
-      dummyObj->attrs.test(characteristics::DummyDataObject::Attr::Contiguous)};
-  if (!actualTreatAsContiguous && dummyNeedsContiguity) {
-    setCopyIn();
-    // Cannot do copy-out for vector subscripts: there could be repeated
-    // indices, for example
-    if (!actualHasVectorSubscript) {
-      setCopyOut();
-    }
-    return;
-  }
-
-  bool dummyIsAssumedShape{dummyObj->type.attrs().test(
-      characteristics::TypeAndShape::Attr::AssumedShape)};
-  bool actualIsAssumedShape{IsAssumedShape(actual)};
-  if ((actualIsAssumedRank && dummyIsAssumedRank) ||
-      (actualIsAssumedShape && dummyIsAssumedShape)) {
-    // Assumed-rank and assumed-shape arrays are represented by descriptors,
-    // so don't need to do polymorphic check.
-  } else if (!dummyObj->ignoreTKR.test(common::IgnoreTKR::Type)) {
-    // flang supports limited cases of passing polymorphic to non-polimorphic.
-    // These cases require temporary of non-polymorphic type. (For example,
-    // the actual argument could be polymorphic array of child type,
-    // while the dummy argument could be non-polymorphic array of parent type.)
-    auto actualType{characteristics::TypeAndShape::Characterize(
-        actual, sc.foldingContext())};
-    bool actualIsPolymorphic{actualType->type().IsPolymorphic()};
-    if (actualIsPolymorphic && !dummyIsPolymorphic) {
-      setCopyIn();
-      setCopyOut();
-    }
-  }
-}
-
-void ProcedureRef::DetermineCopyInOut() {
-  if (!proc_.GetSymbol()) {
-    return;
-  }
-  // Get folding context of the call site owner
-  semantics::SemanticsContext &sc{proc_.GetSymbol()->owner().context()};
-  FoldingContext &fc{sc.foldingContext()};
-  auto procInfo{
-      characteristics::Procedure::Characterize(proc_, fc, /*emitError=*/true)};
-  if (!procInfo) {
-    return;
-  }
-  if (!procInfo->HasExplicitInterface()) {
-    for (auto &actual : arguments_) {
-      if (!actual) {
-        continue;
-      }
-      DetermineCopyInOutArgument(*procInfo, *actual, sc);
-    }
-    return;
-  }
-  // Don't change anything about actual or dummy arguments, except for
-  // computing copy-in/copy-out information. If detect something wrong with
-  // the arguments, stop processing and let semantic analysis generate the
-  // error messages.
-  size_t index{0};
-  std::set<std::string> processedKeywords;
-  bool seenKeyword{false};
-  for (auto &actual : arguments_) {
-    if (!actual) {
-      continue;
-    }
-    if (index >= procInfo->dummyArguments.size()) {
-      // More actual arguments than dummy arguments. Semantic analysis will
-      // deal with the error.
-      return;
-    }
-    if (actual->keyword()) {
-      seenKeyword = true;
-      auto actualName{actual->keyword()->ToString()};
-      if (processedKeywords.find(actualName) != processedKeywords.end()) {
-        // Actual arguments with duplicate keywords. Semantic analysis will
-        // deal with the error.
-        return;
-      } else {
-        processedKeywords.insert(actualName);
-        if (auto it{std::find_if(procInfo->dummyArguments.begin(),
-                procInfo->dummyArguments.end(),
-                [&](const characteristics::DummyArgument &dummy) {
-                  return dummy.name == actualName;
-                })};
-            it != procInfo->dummyArguments.end()) {
-          DetermineCopyInOutArgument(*procInfo, *actual, *it, sc);
-        }
-      }
-    } else if (seenKeyword) {
-      // Non-keyword actual argument after have seen at least one keyword
-      // actual argument. Semantic analysis will deal with the error.
-      return;
-    } else {
-      // Positional argument processing
-      DetermineCopyInOutArgument(
-          *procInfo, *actual, procInfo->dummyArguments[index], sc);
-    }
-    ++index;
-  }
-}
-
 } // namespace Fortran::evaluate
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 18ee83cf4da45..d022378ce1455 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -3464,7 +3464,6 @@ void ExpressionAnalyzer::Analyze(const parser::CallStmt &callStmt) {
                 HasAlternateReturns(callee->arguments)},
             ProcedureRef::Deleter);
         DEREF(callStmt.typedCall.get()).set_chevrons(std::move(*chevrons));
-        DEREF(callStmt.typedCall.get()).DetermineCopyInOut();
         return;
       }
     }
@@ -4389,7 +4388,6 @@ MaybeExpr ExpressionAnalyzer::MakeFunctionRef(parser::CharBlock callSite,
     if (chars->functionResult) {
       const auto &result{*chars->functionResult};
       ProcedureRef procRef{std::move(proc), std::move(arguments)};
-      procRef.DetermineCopyInOut();
       if (result.IsProcedurePointer()) {
         return Expr<SomeType>{std::move(procRef)};
       } else {



More information about the flang-commits mailing list