[flang-commits] [flang] [flang] Accept pointer-valued function results as ASSOCIATED() arguments (PR #66238)

via flang-commits flang-commits at lists.llvm.org
Wed Sep 13 09:48:43 PDT 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-semantics
            
<details>
<summary>Changes</summary>
The POINTER= and TARGET= arguments to the intrinsic function ASSOCIATED() can be the results of references to functions that return object pointers or procedure pointers.  NULL() was working well but not program-defined pointer-valued functions.  Correct the validation of ASSOCIATED() and extend the infrastructure used to detect and characterize procedures and pointers.
--

Patch is 45.72 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/66238.diff

15 Files Affected:

- (modified) flang/include/flang/Evaluate/characteristics.h (+2) 
- (modified) flang/include/flang/Evaluate/tools.h (+28-8) 
- (modified) flang/lib/Evaluate/characteristics.cpp (+16) 
- (modified) flang/lib/Evaluate/fold-complex.cpp (+1-1) 
- (modified) flang/lib/Evaluate/intrinsics.cpp (+3-3) 
- (modified) flang/lib/Evaluate/tools.cpp (+24-37) 
- (modified) flang/lib/Lower/ConvertCall.cpp (+3-6) 
- (modified) flang/lib/Lower/ConvertExpr.cpp (+13-20) 
- (modified) flang/lib/Lower/ConvertVariable.cpp (+1-1) 
- (modified) flang/lib/Lower/CustomIntrinsicCall.cpp (+11-17) 
- (modified) flang/lib/Semantics/check-call.cpp (+38-65) 
- (modified) flang/test/Semantics/associate01.f90 (+4-4) 
- (modified) flang/test/Semantics/associated.f90 (+39-8) 
- (modified) flang/test/Semantics/call09.f90 (+7-7) 
- (modified) flang/test/Semantics/call24.f90 (+1-1) 


<pre>
diff --git a/flang/include/flang/Evaluate/characteristics.h b/flang/include/flang/Evaluate/characteristics.h
index 932f3220c2bcbbb..20750dfad8ce06e 100644
--- a/flang/include/flang/Evaluate/characteristics.h
+++ b/flang/include/flang/Evaluate/characteristics.h
@@ -349,6 +349,8 @@ struct Procedure {
       const ProcedureDesignator &amp;, FoldingContext &amp;);
   static std::optional&lt;Procedure&gt; Characterize(
       const ProcedureRef &amp;, FoldingContext &amp;);
+  static std::optional&lt;Procedure&gt; Characterize(
+      const Expr&lt;SomeType&gt; &amp;, FoldingContext &amp;);
   // Characterizes the procedure being referenced, deducing dummy argument
   // types from actual arguments in the case of an implicit interface.
   static std::optional&lt;Procedure&gt; FromActuals(
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index b3f8f4a67a7b5dd..6caad5db4b39b2d 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -243,6 +243,29 @@ auto UnwrapConvertedExpr(B &amp;x) -&gt; common::Constify&lt;A, B&gt; * {
   return nullptr;
 }
 
+// UnwrapProcedureRef() returns a pointer to a ProcedureRef when the whole
+// expression is a reference to a procedure.
+template &lt;typename A&gt; inline const ProcedureRef *UnwrapProcedureRef(const A &amp;) {
+  return nullptr;
+}
+
+inline const ProcedureRef *UnwrapProcedureRef(const ProcedureRef &amp;proc) {
+  // Reference to subroutine or to a function that returns
+  // an object pointer or procedure pointer
+  return &amp;proc;
+}
+
+template &lt;typename T&gt;
+inline const ProcedureRef *UnwrapProcedureRef(const FunctionRef&lt;T&gt; &amp;func) {
+  return &amp;func; // reference to a function returning a non-pointer
+}
+
+template &lt;typename T&gt;
+inline const ProcedureRef *UnwrapProcedureRef(const Expr&lt;T&gt; &amp;expr) {
+  return common::visit(
+      [](const auto &amp;x) { return UnwrapProcedureRef(x); }, expr.u);
+}
+
 // When an expression is a &quot;bare&quot; LEN= derived type parameter inquiry,
 // possibly wrapped in integer kind conversions &amp;/or parentheses, return
 // a pointer to the Symbol with TypeParamDetails.
@@ -894,10 +917,6 @@ template &lt;typename A&gt; const Symbol *GetLastSymbol(const A &amp;x) {
   }
 }
 
-// If a function reference constitutes an entire expression, return a pointer
-// to its PrcedureRef.
-const ProcedureRef *GetProcedureRef(const Expr&lt;SomeType&gt; &amp;);
-
 // For everyday variables: if GetLastSymbol() succeeds on the argument, return
 // its set of attributes, otherwise the empty set.  Also works on variables that
 // are pointer results of functions.
@@ -912,7 +931,7 @@ template &lt;typename A&gt; semantics::Attrs GetAttrs(const A &amp;x) {
 template &lt;&gt;
 inline semantics::Attrs GetAttrs&lt;Expr&lt;SomeType&gt;&gt;(const Expr&lt;SomeType&gt; &amp;x) {
   if (IsVariable(x)) {
-    if (const auto *procRef{GetProcedureRef(x)}) {
+    if (const auto *procRef{UnwrapProcedureRef(x)}) {
       if (const Symbol * interface{procRef-&gt;proc().GetInterfaceSymbol()}) {
         if (const auto *details{
                 interface-&gt;detailsIf&lt;semantics::SubprogramDetails&gt;()}) {
@@ -963,24 +982,25 @@ std::optional&lt;BaseObject&gt; GetBaseObject(const std::optional&lt;A&gt; &amp;x) {
 
 // Like IsAllocatableOrPointer, but accepts pointer function results as being
 // pointers too.
-bool IsAllocatableOrPointerObject(const Expr&lt;SomeType&gt; &amp;, FoldingContext &amp;);
+bool IsAllocatableOrPointerObject(const Expr&lt;SomeType&gt; &amp;);
 
 bool IsAllocatableDesignator(const Expr&lt;SomeType&gt; &amp;);
 
 // Procedure and pointer detection predicates
 bool IsProcedure(const Expr&lt;SomeType&gt; &amp;);
 bool IsFunction(const Expr&lt;SomeType&gt; &amp;);
+bool IsPointer(const Expr&lt;SomeType&gt; &amp;);
 bool IsProcedurePointer(const Expr&lt;SomeType&gt; &amp;);
 bool IsProcedurePointerTarget(const Expr&lt;SomeType&gt; &amp;);
 bool IsBareNullPointer(const Expr&lt;SomeType&gt; *); // NULL() w/o MOLD= or type
 bool IsNullObjectPointer(const Expr&lt;SomeType&gt; &amp;);
 bool IsNullProcedurePointer(const Expr&lt;SomeType&gt; &amp;);
 bool IsNullPointer(const Expr&lt;SomeType&gt; &amp;);
-bool IsObjectPointer(const Expr&lt;SomeType&gt; &amp;, FoldingContext &amp;);
+bool IsObjectPointer(const Expr&lt;SomeType&gt; &amp;);
 
 // Can Expr be passed as absent to an optional dummy argument.
 // See 15.5.2.12 point 1 for more details.
-bool MayBePassedAsAbsentOptional(const Expr&lt;SomeType&gt; &amp;, FoldingContext &amp;);
+bool MayBePassedAsAbsentOptional(const Expr&lt;SomeType&gt; &amp;);
 
 // Extracts the chain of symbols from a designator, which has perhaps been
 // wrapped in an Expr&lt;&gt;, removing all of the (co)subscripts.  The
diff --git a/flang/lib/Evaluate/characteristics.cpp b/flang/lib/Evaluate/characteristics.cpp
index 8d52eabc16d502b..6daa113abe64255 100644
--- a/flang/lib/Evaluate/characteristics.cpp
+++ b/flang/lib/Evaluate/characteristics.cpp
@@ -1268,6 +1268,22 @@ std::optional&lt;Procedure&gt; Procedure::Characterize(
   return std::nullopt;
 }
 
+std::optional&lt;Procedure&gt; Procedure::Characterize(
+    const Expr&lt;SomeType&gt; &amp;expr, FoldingContext &amp;context) {
+  if (const auto *procRef{UnwrapProcedureRef(expr)}) {
+    return Characterize(*procRef, context);
+  } else if (const auto *procDesignator{
+                 std::get_if&lt;ProcedureDesignator&gt;(&amp;expr.u)}) {
+    return Characterize(*procDesignator, context);
+  } else if (const Symbol * symbol{UnwrapWholeSymbolOrComponentDataRef(expr)}) {
+    return Characterize(*symbol, context);
+  } else {
+    context.messages().Say(
+        &quot;Expression &#x27;%s&#x27; is not a procedure&quot;_err_en_US, expr.AsFortran());
+    return std::nullopt;
+  }
+}
+
 std::optional&lt;Procedure&gt; Procedure::FromActuals(const ProcedureDesignator &amp;proc,
     const ActualArguments &amp;args, FoldingContext &amp;context) {
   auto callee{Characterize(proc, context)};
diff --git a/flang/lib/Evaluate/fold-complex.cpp b/flang/lib/Evaluate/fold-complex.cpp
index 520121ad254de77..e40e3a37df14948 100644
--- a/flang/lib/Evaluate/fold-complex.cpp
+++ b/flang/lib/Evaluate/fold-complex.cpp
@@ -47,7 +47,7 @@ Expr&lt;Type&lt;TypeCategory::Complex, KIND&gt;&gt; FoldIntrinsicFunction(
           // into a complex constructor so that lowering can deal with the
           // optional aspect (there is no optional aspect with the complex
           // constructor).
-          if (MayBePassedAsAbsentOptional(*args[1]-&gt;UnwrapExpr(), context)) {
+          if (MayBePassedAsAbsentOptional(*args[1]-&gt;UnwrapExpr())) {
             return Expr&lt;T&gt;{std::move(funcRef)};
           }
         }
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 030e5b2fd2c6d9d..448e9aae6d5403e 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -2577,7 +2577,7 @@ SpecificCall IntrinsicProcTable::Implementation::HandleNull(
       arguments[0]) {
     if (Expr&lt;SomeType&gt; * mold{arguments[0]-&gt;UnwrapExpr()}) {
       bool isProcPtrTarget{IsProcedurePointerTarget(*mold)};
-      if (isProcPtrTarget || IsAllocatableOrPointerObject(*mold, context)) {
+      if (isProcPtrTarget || IsAllocatableOrPointerObject(*mold)) {
         characteristics::DummyArguments args;
         std::optional&lt;characteristics::FunctionResult&gt; fResult;
         if (isProcPtrTarget) {
@@ -2747,7 +2747,7 @@ std::optional&lt;SpecificCall&gt; IntrinsicProcTable::Implementation::HandleC_Loc(
     CheckForCoindexedObject(context, arguments[0], &quot;c_loc&quot;, &quot;x&quot;);
     const auto *expr{arguments[0].value().UnwrapExpr()};
     if (expr &amp;&amp;
-        !(IsObjectPointer(*expr, context) ||
+        !(IsObjectPointer(*expr) ||
             (IsVariable(*expr) &amp;&amp; GetLastTarget(GetSymbolVector(*expr))))) {
       context.messages().Say(arguments[0]-&gt;sourceLocation(),
           &quot;C_LOC() argument must be a data pointer or target&quot;_err_en_US);
@@ -3094,7 +3094,7 @@ std::optional&lt;SpecificCall&gt; IntrinsicProcTable::Implementation::Probe(
       for (const auto &amp;arg : arguments) {
         if (const auto *expr{arg-&gt;UnwrapExpr()}) {
           optionalCount +=
-              Fortran::evaluate::MayBePassedAsAbsentOptional(*expr, context);
+              Fortran::evaluate::MayBePassedAsAbsentOptional(*expr);
         }
       }
       if (arguments.size() - optionalCount &gt; 1) {
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index d2fa5c9b5f36be6..0392adc60adb4e6 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -740,16 +740,25 @@ bool IsFunction(const Expr&lt;SomeType&gt; &amp;expr) {
   return designator &amp;&amp; designator-&gt;GetType().has_value();
 }
 
+bool IsPointer(const Expr&lt;SomeType&gt; &amp;expr) {
+  return IsObjectPointer(expr) || IsProcedurePointer(expr);
+}
+
 bool IsProcedurePointer(const Expr&lt;SomeType&gt; &amp;expr) {
-  return common::visit(common::visitors{
-                           [](const NullPointer &amp;) { return true; },
-                           [](const ProcedureRef &amp;) { return false; },
-                           [&amp;](const auto &amp;) {
-                             const Symbol *last{GetLastSymbol(expr)};
-                             return last &amp;&amp; IsProcedurePointer(*last);
-                           },
-                       },
-      expr.u);
+  if (IsNullProcedurePointer(expr)) {
+    return true;
+  } else if (const auto *funcRef{UnwrapProcedureRef(expr)}) {
+    if (const Symbol * proc{funcRef-&gt;proc().GetSymbol()}) {
+      const Symbol *result{FindFunctionResult(*proc)};
+      return result &amp;&amp; IsProcedurePointer(*result);
+    } else {
+      return false;
+    }
+  } else if (const auto *proc{std::get_if&lt;ProcedureDesignator&gt;(&amp;expr.u)}) {
+    return IsProcedurePointer(proc-&gt;GetSymbol());
+  } else {
+    return false;
+  }
 }
 
 bool IsProcedurePointerTarget(const Expr&lt;SomeType&gt; &amp;expr) {
@@ -765,23 +774,7 @@ bool IsProcedurePointerTarget(const Expr&lt;SomeType&gt; &amp;expr) {
       expr.u);
 }
 
-template &lt;typename A&gt; inline const ProcedureRef *UnwrapProcedureRef(const A &amp;) {
-  return nullptr;
-}
-
-template &lt;typename T&gt;
-inline const ProcedureRef *UnwrapProcedureRef(const FunctionRef&lt;T&gt; &amp;func) {
-  return &amp;func;
-}
-
-template &lt;typename T&gt;
-inline const ProcedureRef *UnwrapProcedureRef(const Expr&lt;T&gt; &amp;expr) {
-  return common::visit(
-      [](const auto &amp;x) { return UnwrapProcedureRef(x); }, expr.u);
-}
-
-// IsObjectPointer()
-bool IsObjectPointer(const Expr&lt;SomeType&gt; &amp;expr, FoldingContext &amp;context) {
+bool IsObjectPointer(const Expr&lt;SomeType&gt; &amp;expr) {
   if (IsNullObjectPointer(expr)) {
     return true;
   } else if (IsProcedurePointerTarget(expr)) {
@@ -795,10 +788,6 @@ bool IsObjectPointer(const Expr&lt;SomeType&gt; &amp;expr, FoldingContext &amp;context) {
   }
 }
 
-const ProcedureRef *GetProcedureRef(const Expr&lt;SomeType&gt; &amp;expr) {
-  return UnwrapProcedureRef(expr);
-}
-
 // IsNullPointer() &amp; variations
 
 template &lt;bool IS_PROC_PTR&gt; struct IsNullPointerHelper {
@@ -872,7 +861,7 @@ bool IsBareNullPointer(const Expr&lt;SomeType&gt; *expr) {
 // GetSymbolVector()
 auto GetSymbolVectorHelper::operator()(const Symbol &amp;x) const -&gt; Result {
   if (const auto *details{x.detailsIf&lt;semantics::AssocEntityDetails&gt;()}) {
-    if (IsVariable(details-&gt;expr()) &amp;&amp; !GetProcedureRef(*details-&gt;expr())) {
+    if (IsVariable(details-&gt;expr()) &amp;&amp; !UnwrapProcedureRef(*details-&gt;expr())) {
       // associate(x =&gt; variable that is not a pointer returned by a function)
       return (*this)(details-&gt;expr());
     }
@@ -1155,12 +1144,11 @@ std::optional&lt;Expr&lt;SomeType&gt;&gt; DataConstantConversionExtension(
   return std::nullopt;
 }
 
-bool IsAllocatableOrPointerObject(
-    const Expr&lt;SomeType&gt; &amp;expr, FoldingContext &amp;context) {
+bool IsAllocatableOrPointerObject(const Expr&lt;SomeType&gt; &amp;expr) {
   const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
   return (sym &amp;&amp;
              semantics::IsAllocatableOrObjectPointer(&amp;sym-&gt;GetUltimate())) ||
-      evaluate::IsObjectPointer(expr, context);
+      evaluate::IsObjectPointer(expr);
 }
 
 bool IsAllocatableDesignator(const Expr&lt;SomeType&gt; &amp;expr) {
@@ -1172,15 +1160,14 @@ bool IsAllocatableDesignator(const Expr&lt;SomeType&gt; &amp;expr) {
   return false;
 }
 
-bool MayBePassedAsAbsentOptional(
-    const Expr&lt;SomeType&gt; &amp;expr, FoldingContext &amp;context) {
+bool MayBePassedAsAbsentOptional(const Expr&lt;SomeType&gt; &amp;expr) {
   const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
   // 15.5.2.12 1. is pretty clear that an unallocated allocatable/pointer actual
   // may be passed to a non-allocatable/non-pointer optional dummy. Note that
   // other compilers (like nag, nvfortran, ifort, gfortran and xlf) seems to
   // ignore this point in intrinsic contexts (e.g CMPLX argument).
   return (sym &amp;&amp; semantics::IsOptional(*sym)) ||
-      IsAllocatableOrPointerObject(expr, context);
+      IsAllocatableOrPointerObject(expr);
 }
 
 std::optional&lt;Expr&lt;SomeType&gt;&gt; HollerithToBOZ(FoldingContext &amp;context,
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 15915b02aebaa70..fbf8eac642af2a7 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1165,8 +1165,7 @@ genUserCall(Fortran::lower::PreparedActualArguments &amp;loweredActuals,
         continue;
       }
       if (fir::isPointerType(argTy) &amp;&amp;
-          !Fortran::evaluate::IsObjectPointer(
-              *expr, callContext.converter.getFoldingContext())) {
+          !Fortran::evaluate::IsObjectPointer(*expr)) {
         // Passing a non POINTER actual argument to a POINTER dummy argument.
         // Create a pointer of the dummy argument type and assign the actual
         // argument to it.
@@ -1814,13 +1813,11 @@ genIsPresentIfArgMaybeAbsent(mlir::Location loc, hlfir::Entity actual,
                              const Fortran::lower::SomeExpr &amp;expr,
                              CallContext &amp;callContext,
                              bool passAsAllocatableOrPointer) {
-  if (!Fortran::evaluate::MayBePassedAsAbsentOptional(
-          expr, callContext.converter.getFoldingContext()))
+  if (!Fortran::evaluate::MayBePassedAsAbsentOptional(expr))
     return std::nullopt;
   fir::FirOpBuilder &amp;builder = callContext.getBuilder();
   if (!passAsAllocatableOrPointer &amp;&amp;
-      Fortran::evaluate::IsAllocatableOrPointerObject(
-          expr, callContext.converter.getFoldingContext())) {
+      Fortran::evaluate::IsAllocatableOrPointerObject(expr)) {
     // Passing Allocatable/Pointer to non-pointer/non-allocatable OPTIONAL.
     // Fortran 2018 15.5.2.12 point 1: If unallocated/disassociated, it is
     // as if the argument was absent. The main care here is to not do a
diff --git a/flang/lib/Lower/ConvertExpr.cpp b/flang/lib/Lower/ConvertExpr.cpp
index a9298be5532d905..26519d204460c67 100644
--- a/flang/lib/Lower/ConvertExpr.cpp
+++ b/flang/lib/Lower/ConvertExpr.cpp
@@ -1782,8 +1782,7 @@ class ScalarExprLowering {
   /// Helper to lower intrinsic arguments for inquiry intrinsic.
   ExtValue
   lowerIntrinsicArgumentAsInquired(const Fortran::lower::SomeExpr &amp;expr) {
-    if (Fortran::evaluate::IsAllocatableOrPointerObject(
-            expr, converter.getFoldingContext()))
+    if (Fortran::evaluate::IsAllocatableOrPointerObject(expr))
       return genMutableBoxValue(expr);
     /// Do not create temps for array sections whose properties only need to be
     /// inquired: create a descriptor that will be inquired.
@@ -1918,8 +1917,7 @@ class ScalarExprLowering {
       fir::ArgLoweringRule argRules =
           fir::lowerIntrinsicArgumentAs(*argLowering, arg.index());
       if (argRules.handleDynamicOptional &amp;&amp;
-          Fortran::evaluate::MayBePassedAsAbsentOptional(
-              *expr, converter.getFoldingContext())) {
+          Fortran::evaluate::MayBePassedAsAbsentOptional(*expr)) {
         ExtValue optional = lowerIntrinsicArgumentAsInquired(*expr);
         mlir::Value isPresent = genActualIsPresentTest(builder, loc, optional);
         switch (argRules.lowerAs) {
@@ -2392,8 +2390,7 @@ class ScalarExprLowering {
   std::pair&lt;ExtValue, mlir::Value&gt;
   prepareActualThatMayBeAbsent(const Fortran::lower::SomeExpr &amp;expr) {
     mlir::Location loc = getLoc();
-    if (Fortran::evaluate::IsAllocatableOrPointerObject(
-            expr, converter.getFoldingContext())) {
+    if (Fortran::evaluate::IsAllocatableOrPointerObject(expr)) {
       // Fortran 2018 15.5.2.12 point 1: If unallocated/disassociated,
       // it is as if the argument was absent. The main care here is to
       // not do a copy-in/copy-out because the temp address, even though
@@ -2496,8 +2493,8 @@ class ScalarExprLowering {
         // not passed.
         return {genTempExtAddr(expr), std::nullopt};
       ExtValue baseAddr;
-      if (arg.isOptional() &amp;&amp; Fortran::evaluate::MayBePassedAsAbsentOptional(
-                                  expr, converter.getFoldingContext())) {
+      if (arg.isOptional() &amp;&amp;
+          Fortran::evaluate::MayBePassedAsAbsentOptional(expr)) {
         auto [actualArgBind, isPresent] = prepareActualThatMayBeAbsent(expr);
         const ExtValue &amp;actualArg = actualArgBind;
         if (!needsCopy)
@@ -2631,8 +2628,7 @@ class ScalarExprLowering {
           continue;
         }
         if (fir::isPointerType(argTy) &amp;&amp;
-            !Fortran::evaluate::IsObjectPointer(
-                *expr, converter.getFoldingContext())) {
+            !Fortran::evaluate::IsObjectPointer(*expr)) {
           // Passing a non POINTER actual argument to a POINTER dummy argument.
           // Create a pointer of the dummy argument type and assign the actual
           // argument to it.
@@ -2759,8 +2755,7 @@ class ScalarExprLowering {
           }
 
         } else if (arg.isOptional() &amp;&amp;
-                   Fortran::evaluate::IsAllocatableOrPointerObject(
-                       *expr, converter.getFoldingContext())) {
+                   Fortran::evaluate::IsAllocatableOrPointerObject(*expr)) {
           // Before lowering to an address, handle the allocatable/pointer
           // actual argument to optional fir.box dummy. It is legal to pass
           // unallocated/disassociated entity to an optional. In this case, an
@@ -3355,8 +3350,7 @@ class ArrayExprLowering {
     setPointerAssignmentBounds(lbounds, ubounds);
     if (rhs.Rank() == 0 ||
         (Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs) &amp;&amp;
-         Fortran::evaluate::IsAllocatableOrPointerObject(
-             rhs, converter.getFoldingContext()))) {
+         Fortran::evaluate::IsAllocatableOrPointerObject(rhs))) {
       lowerScalarAssignment(lhs, rhs);
       return;
     }
@@ -4684,8 +4678,7 @@ class ArrayExprLowering {
         fir::ArgLoweringRule argRules =
             fir::lowerIntrinsicArgumentAs(*argLowering, arg.index());
         if (argRules.handleDynamicOptional &amp;&amp;
-            Fortran::evaluate::MayBePassedAsAbsentOptional(
-                *e...
<truncated>
</pre>
</details>


https://github.com/llvm/llvm-project/pull/66238


More information about the flang-commits mailing list