[flang-commits] [flang] [flang] Allow host-associated INTENT(OUT) in specification expr. (PR #135426)

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Fri Apr 11 12:40:12 PDT 2025


https://github.com/klausler created https://github.com/llvm/llvm-project/pull/135426

Nearly, but not all, other compilers have a blanket prohibition against the use of an INTENT(OUT) dummy argument in a specification expression.  Some compilers, however, permit an INTENT(OUT) dummy argument to appear in a specification expression in a BLOCK construct or inner procedure via host association.

The argument some have put forth to accept this usage comes from a reading of 10.1.11 (specification expressions) in Fortran 2023 that, if followed consistently, would also require host-associated OPTIONAL dummy argument to be allowed.  That would be dangerous for reasons that should be obvious.

However, I can agree that a non-OPTIONAL dummy argument can't be assumed to remain undefined on entry to a BLOCK construct or inner procedure, so we can accept host-associated INTENT(OUT) in specification expressions with a portability warning.

>From 4a3ae6358f24d253e0c7843633d7151563783a26 Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Fri, 11 Apr 2025 12:31:24 -0700
Subject: [PATCH] [flang] Allow host-associated INTENT(OUT) in specification
 expr.

Nearly, but not all, other compilers have a blanket prohibition
against the use of an INTENT(OUT) dummy argument in a specification
expression.  Some compilers, however, permit an INTENT(OUT) dummy
argument to appear in a specification expression in a BLOCK construct
or inner procedure via host association.

The argument some have put forth to accept this usage comes from
a reading of 10.1.11 (specification expressions) in Fortran 2023
that, if followed consistently, would also require host-associated
OPTIONAL dummy argument to be allowed.  That would be dangerous
for reasons that should be obvious.

However, I can agree that a non-OPTIONAL dummy argument can't be
assumed to remain undefined on entry to a BLOCK construct or inner
procedure, so we can accept host-associated INTENT(OUT) in specification
expressions with a portability warning.
---
 flang/docs/Extensions.md                      | 13 ++++++++
 .../include/flang/Support/Fortran-features.h  |  3 +-
 flang/lib/Evaluate/check-expression.cpp       | 32 ++++++++++++++-----
 flang/lib/Support/Fortran-features.cpp        |  1 +
 flang/test/Semantics/spec-expr.f90            | 13 ++++++++
 5 files changed, 53 insertions(+), 9 deletions(-)

diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index d781dee75e07e..05e21ef2d33b5 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -825,6 +825,19 @@ print *, [(j,j=1,10)]
   of these values in violation of the restriction in f23 clause 17.11.42 set
   the mode to ieee_nearest.
 
+* Some compilers allow an `INTENT(OUT)` dummy argument's value to appear
+  via host association in a specification expression.  A non-host-associated
+  use is an error because an `INTENT(OUT)` dummy argument's value is not
+  defined.  The argument put forth to accept this usage in a `BLOCK` construct
+  or inner procedure is that the language in 10.1.11 (specification expressions)
+  allows any host-associated object to appear, but that's unconvincing
+  because it would also allow a host-associated `OPTIONAL` dummy argument to
+  be used in a nested scope, and that doesn't make sense.  This compiler
+  accepts an `INTENT(OUT)` non-`OPTIONAL` host-associated value to appear
+  in a specification expression via host association with a portability
+  warning since such values may have become defined by the time the nested
+  expression's value is required.
+
 ## De Facto Standard Features
 
 * `EXTENDS_TYPE_OF()` returns `.TRUE.` if both of its arguments have the
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index 335273100d70e..5b22313754a0f 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -75,7 +75,8 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
     VectorSubscriptFinalization, UndefinedFunctionResult, UselessIomsg,
     MismatchingDummyProcedure, SubscriptedEmptyArray, UnsignedLiteralTruncation,
     CompatibleDeclarationsFromDistinctModules,
-    NullActualForDefaultIntentAllocatable, UseAssociationIntoSameNameSubprogram)
+    NullActualForDefaultIntentAllocatable, UseAssociationIntoSameNameSubprogram,
+    HostAssociatedIntentOutInSpecExpr)
 
 using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
 using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 4d272795ff9bd..78268cd13377a 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -545,6 +545,8 @@ class CheckSpecificationExprHelper
         !IsAllocatable(ultimate) && object &&
         (ultimate.test(Symbol::Flag::InDataStmt) ||
             object->init().has_value())};
+    bool hasHostAssociation{
+        &symbol.owner() != &scope_ || &ultimate.owner() != &scope_};
     if (const auto *assoc{
             ultimate.detailsIf<semantics::AssocEntityDetails>()}) {
       return (*this)(assoc->expr());
@@ -563,16 +565,30 @@ class CheckSpecificationExprHelper
       } else if (ultimate.attrs().test(semantics::Attr::OPTIONAL)) {
         return "reference to OPTIONAL dummy argument '"s +
             ultimate.name().ToString() + "'";
-      } else if (!inInquiry_ &&
+      } else if (!inInquiry_ && !hasHostAssociation &&
           ultimate.attrs().test(semantics::Attr::INTENT_OUT)) {
         return "reference to INTENT(OUT) dummy argument '"s +
             ultimate.name().ToString() + "'";
-      } else if (ultimate.has<semantics::ObjectEntityDetails>()) {
-        return std::nullopt;
-      } else {
+      } else if (!ultimate.has<semantics::ObjectEntityDetails>()) {
         return "dummy procedure argument";
+      } else {
+        // Sketchy case: some compilers allow an INTENT(OUT) dummy argument
+        // to be used in a specification expression if it is host-associated.
+        // The arguments raised in support this usage, however, depend on
+        // a reading of the standard that would also accept an OPTIONAL
+        // host-associated dummy argument, and that doesn't seem like a
+        // good idea.
+        if (!inInquiry_ && hasHostAssociation &&
+            ultimate.attrs().test(semantics::Attr::INTENT_OUT) &&
+            context_.languageFeatures().ShouldWarn(
+                common::UsageWarning::HostAssociatedIntentOutInSpecExpr)) {
+          context_.messages().Say(
+              "specification expression refers to host-associated INTENT(OUT) dummy argument '%s'"_port_en_US,
+              ultimate.name());
+        }
+        return std::nullopt;
       }
-    } else if (&symbol.owner() != &scope_ || &ultimate.owner() != &scope_) {
+    } else if (hasHostAssociation) {
       return std::nullopt; // host association is in play
     } else if (isInitialized &&
         context_.languageFeatures().IsEnabled(
@@ -582,7 +598,7 @@ class CheckSpecificationExprHelper
               common::LanguageFeature::SavedLocalInSpecExpr)) {
         context_.messages().Say(common::LanguageFeature::SavedLocalInSpecExpr,
             "specification expression refers to local object '%s' (initialized and saved)"_port_en_US,
-            ultimate.name().ToString());
+            ultimate.name());
       }
       return std::nullopt;
     } else if (const auto *object{
@@ -831,9 +847,9 @@ bool CheckSpecificationExprHelper::IsPermissibleInquiry(
 template <typename A>
 void CheckSpecificationExpr(const A &x, const semantics::Scope &scope,
     FoldingContext &context, bool forElementalFunctionResult) {
-  CheckSpecificationExprHelper helper{
+  CheckSpecificationExprHelper errors{
       scope, context, forElementalFunctionResult};
-  if (auto why{helper(x)}) {
+  if (auto why{errors(x)}) {
     context.messages().Say("Invalid specification expression%s: %s"_err_en_US,
         forElementalFunctionResult ? " for elemental function result" : "",
         *why);
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 4f1af27231301..b3cb62e62f5fb 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -86,6 +86,7 @@ LanguageFeatureControl::LanguageFeatureControl() {
   warnUsage_.set(UsageWarning::UnsignedLiteralTruncation);
   warnUsage_.set(UsageWarning::NullActualForDefaultIntentAllocatable);
   warnUsage_.set(UsageWarning::UseAssociationIntoSameNameSubprogram);
+  warnUsage_.set(UsageWarning::HostAssociatedIntentOutInSpecExpr);
   // New warnings, on by default
   warnLanguage_.set(LanguageFeature::SavedLocalInSpecExpr);
   warnLanguage_.set(LanguageFeature::NullActualForAllocatable);
diff --git a/flang/test/Semantics/spec-expr.f90 b/flang/test/Semantics/spec-expr.f90
index 9d209c3583b43..28ebea1109f1d 100644
--- a/flang/test/Semantics/spec-expr.f90
+++ b/flang/test/Semantics/spec-expr.f90
@@ -28,6 +28,19 @@ subroutine s2(inArg, inoutArg, outArg, optArg)
   real, dimension(optArg) :: realVar4
 
   outArg = 3
+  block
+    !PORTABILITY: specification expression refers to host-associated INTENT(OUT) dummy argument 'outarg'
+    real a(outArg)
+    !ERROR: Invalid specification expression: reference to OPTIONAL dummy argument 'optarg'
+    real b(optArg)
+  end block
+ contains
+  subroutine s2inner
+    !PORTABILITY: specification expression refers to host-associated INTENT(OUT) dummy argument 'outarg'
+    real a(outArg)
+    !ERROR: Invalid specification expression: reference to OPTIONAL dummy argument 'optarg'
+    real b(optArg)
+  end
 end subroutine s2
 
 ! an object designator with a base object that is in a common block,



More information about the flang-commits mailing list