[flang-commits] [flang] [flang][semantics] Allow forward-typed PARAMETER constants under IMPLICIT NONE (PR #203398)

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Sun Jun 14 06:48:03 PDT 2026


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

>From d61362f7875d23dc6f4e9e1dc46ee3656ff9137c Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 11 Jun 2026 17:04:01 -0400
Subject: [PATCH 1/3] [flang][semantics] Allow forward-typed PARAMETER
 constants under IMPLICIT NONE

Under IMPLICIT NONE, flang rejected a named constant defined by a PARAMETER
statement whose explicit type declaration appears later in the same
specification part:

    implicit none
    parameter(n=4096)
    integer n          ! error: No explicit type declared for 'n'
    end

Accept it as an extension, reusing the existing ForwardRefImplicitNone language
feature that already permits forward references to dummy arguments and COMMON
variables under IMPLICIT NONE(TYPE). The behavior is accepted silently by
default and emits a portability warning under -pedantic.
---
 flang/docs/Extensions.md              |  4 +++
 flang/lib/Semantics/resolve-names.cpp | 33 +++++++++++++++------
 flang/test/Semantics/resolve130.f90   | 41 +++++++++++++++++++++++++++
 flang/test/Semantics/resolve131.f90   | 11 +++++++
 4 files changed, 80 insertions(+), 9 deletions(-)
 create mode 100644 flang/test/Semantics/resolve130.f90
 create mode 100644 flang/test/Semantics/resolve131.f90

diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 0cc9a8230dfb9..e05b0cf423101 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -347,6 +347,10 @@ end
   expression, such as an array bound, in a scope with IMPLICIT NONE(TYPE)
   if the name of the variable would have caused it to be implicitly typed
   as default INTEGER if IMPLICIT NONE(TYPE) were absent.
+* A named constant defined by a `PARAMETER` statement is permitted to appear
+  before its explicit type declaration in a scope with IMPLICIT NONE(TYPE);
+  it acquires the type it would have had under implicit typing rules (F2023 8.7),
+  and a later explicit declaration must specify that same type (F2023 8.6.11 p2).
 * OPEN(ACCESS='APPEND') is interpreted as OPEN(POSITION='APPEND')
   to ease porting from Sun Fortran.
 * Intrinsic subroutines EXIT([status]) and ABORT()
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 2a2073f29a26e..a2a1dc08887c3 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -3379,9 +3379,16 @@ void ScopeHandler::ApplyImplicitRules(
 // or variables in COMMON to appear in specification expressions under
 // IMPLICIT NONE(TYPE) when what would otherwise have been their implicit
 // type is default INTEGER.
+// Also allow a named constant defined by a PARAMETER statement to be
+// explicitly typed by a later type declaration statement in the same
+// specification part under IMPLICIT NONE(TYPE); the constant acquires the
+// type it would have had under implicit typing rules (F2023 8.7), which a
+// subsequent declaration must match (F2023 8.6.11 p2).
 bool ScopeHandler::ImplicitlyTypeForwardRef(Symbol &symbol) {
+  const bool isNamedConstant{IsNamedConstant(symbol)};
   if (!inSpecificationPart_ || context().HasError(symbol) ||
-      !(IsDummy(symbol) || FindCommonBlockContaining(symbol)) ||
+      !(isNamedConstant || IsDummy(symbol) ||
+          FindCommonBlockContaining(symbol)) ||
       symbol.Rank() != 0 ||
       !context().languageFeatures().IsEnabled(
           common::LanguageFeature::ForwardRefImplicitNone)) {
@@ -3389,12 +3396,20 @@ bool ScopeHandler::ImplicitlyTypeForwardRef(Symbol &symbol) {
   }
   const DeclTypeSpec *type{
       GetImplicitType(symbol, false /*ignore IMPLICIT NONE*/)};
-  if (!type || !type->IsNumeric(TypeCategory::Integer)) {
+  if (!type) {
     return false;
   }
-  auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())};
-  if (!kind || *kind != context().GetDefaultKind(TypeCategory::Integer)) {
-    return false;
+  if (!isNamedConstant) {
+    // For a forward-referenced dummy argument or COMMON variable, only a
+    // default-kind INTEGER implicit type is meaningful (e.g. as an array
+    // bound in a specification expression).
+    if (!type->IsNumeric(TypeCategory::Integer)) {
+      return false;
+    }
+    auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())};
+    if (!kind || *kind != context().GetDefaultKind(TypeCategory::Integer)) {
+      return false;
+    }
   }
   if (!ConvertToObjectEntity(symbol)) {
     return false;
@@ -3583,12 +3598,12 @@ bool ScopeHandler::CheckPossibleBadForwardRef(const Symbol &symbol) {
       context().SetError(symbol);
       return true;
     }
-    if ((IsDummy(symbol) ||
+    if ((IsDummy(symbol) || IsNamedConstant(symbol) ||
             (!symbol.has<UseDetails>() && FindCommonBlockContaining(symbol))) &&
         isImplicitNoneType() && symbol.test(Symbol::Flag::Implicit) &&
         !context().HasError(symbol)) {
-      // Dummy or COMMON was implicitly typed despite IMPLICIT NONE(TYPE) in
-      // ApplyImplicitRules() due to use in a specification expression,
+      // Dummy, COMMON, or PARAMETER named constant was implicitly typed despite
+      // IMPLICIT NONE(TYPE) in ApplyImplicitRules() due to a forward reference,
       // and no explicit type declaration appeared later.
       Say(symbol.name(), "No explicit type declared for '%s'"_err_en_US);
       context().SetError(symbol);
@@ -5942,7 +5957,7 @@ bool DeclarationVisitor::Pre(const parser::NamedConstantDef &x) {
     }
   } else {
     // standard-conforming PARAMETER statement (with parentheses)
-    ApplyImplicitRules(symbol);
+    ApplyImplicitRules(symbol, /*allowForwardReference=*/true);
     Walk(expr);
     if (auto converted{EvaluateNonPointerInitializer(symbol, expr, at)}) {
       details->set_init(std::move(*converted));
diff --git a/flang/test/Semantics/resolve130.f90 b/flang/test/Semantics/resolve130.f90
new file mode 100644
index 0000000000000..559b61f851486
--- /dev/null
+++ b/flang/test/Semantics/resolve130.f90
@@ -0,0 +1,41 @@
+! RUN: not %flang_fc1 -pedantic %s 2>&1 | FileCheck %s
+! Test extension: a named constant defined by a PARAMETER statement may appear
+! before its explicit type declaration in a scope with IMPLICIT NONE(TYPE).
+! It acquires the type it would have had under implicit typing rules, which a
+! later explicit declaration must match.
+
+! Forward reference accepted, matching INTEGER declaration appears later.
+!CHECK: warning: 'n' was used without (or before) being explicitly typed
+subroutine s1
+  implicit none
+  parameter(n=4096)
+  integer n
+end
+
+! The would-be-implicit type need not be INTEGER; a matching REAL declaration
+! is accepted.
+!CHECK: warning: 'x' was used without (or before) being explicitly typed
+subroutine s2
+  implicit none
+  parameter(x=1.5)
+  real x
+end
+
+! A later declaration whose type differs from the would-be-implicit type is
+! rejected.
+!CHECK: warning: 'm' was used without (or before) being explicitly typed
+subroutine s3
+  implicit none
+  parameter(m=4096)
+!CHECK: error: The type of 'm' has already been implicitly declared as INTEGER(4)
+  real m
+end
+
+! If no explicit type declaration ever appears, IMPLICIT NONE(TYPE) is still
+! enforced.
+!CHECK: warning: 'k' was used without (or before) being explicitly typed
+!CHECK: error: No explicit type declared for 'k'
+subroutine s4
+  implicit none
+  parameter(k=4096)
+end
diff --git a/flang/test/Semantics/resolve131.f90 b/flang/test/Semantics/resolve131.f90
new file mode 100644
index 0000000000000..fd2565e8818db
--- /dev/null
+++ b/flang/test/Semantics/resolve131.f90
@@ -0,0 +1,11 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! A named constant defined by a PARAMETER statement may be explicitly typed by a
+! later type declaration statement under IMPLICIT NONE(TYPE). This extension is
+! accepted silently by default (the portability warning appears only with
+! -pedantic); this test verifies no diagnostic is emitted by default.
+subroutine s1
+  implicit none
+  parameter(n=4096)
+  integer n
+  real a(n)
+end

>From 280051e54dcfbe0fbe8365d28dd10636562fa59c Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 11 Jun 2026 18:53:36 -0400
Subject: [PATCH 2/3] Fixed a regression in llvm-test-suite

---
 flang/lib/Semantics/resolve-names.cpp | 10 +++++++---
 flang/test/Semantics/resolve131.f90   | 14 ++++++++++++++
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 123eb28b91015..5c9695e05b42b 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -3635,13 +3635,17 @@ bool ScopeHandler::CheckPossibleBadForwardRef(const Symbol &symbol) {
       context().SetError(symbol);
       return true;
     }
-    if ((IsDummy(symbol) || IsNamedConstant(symbol) ||
-            (!symbol.has<UseDetails>() && FindCommonBlockContaining(symbol))) &&
+    if ((IsDummy(symbol) ||
+            (!symbol.has<UseDetails>() &&
+                (IsNamedConstant(symbol) || FindCommonBlockContaining(symbol)))) &&
         isImplicitNoneType() && symbol.test(Symbol::Flag::Implicit) &&
         !context().HasError(symbol)) {
       // Dummy, COMMON, or PARAMETER named constant was implicitly typed despite
       // IMPLICIT NONE(TYPE) in ApplyImplicitRules() due to a forward reference,
-      // and no explicit type declaration appeared later.
+      // and no explicit type declaration appeared later.  A use-associated
+      // named constant is excluded: its Symbol::Flag::Implicit may have been
+      // set legitimately by an IMPLICIT statement in the module that defined
+      // it, where IMPLICIT NONE(TYPE) was not in effect.
       Say(symbol.name(), "No explicit type declared for '%s'"_err_en_US);
       context().SetError(symbol);
       return true;
diff --git a/flang/test/Semantics/resolve131.f90 b/flang/test/Semantics/resolve131.f90
index fd2565e8818db..bdd68781db858 100644
--- a/flang/test/Semantics/resolve131.f90
+++ b/flang/test/Semantics/resolve131.f90
@@ -9,3 +9,17 @@ subroutine s1
   integer n
   real a(n)
 end
+
+! A named constant that is implicitly typed in a module (via an IMPLICIT
+! statement, where IMPLICIT NONE(TYPE) is not in effect) must not be flagged
+! as a bad forward reference when it is use-associated into an IMPLICIT NONE
+! scope.
+module m_implicit_test
+  implicit character(len=*) (x)
+  parameter(xc = 'abc')
+end module
+subroutine s2
+  use m_implicit_test
+  implicit none
+  if (xc /= 'abc') stop 1
+end

>From 2c5de573dad0fb2aa721359e69c44e55a091a991 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 11 Jun 2026 19:16:53 -0400
Subject: [PATCH 3/3] clang-format

---
 flang/lib/Semantics/resolve-names.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 5c9695e05b42b..6d2d0bf24b194 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -3637,7 +3637,8 @@ bool ScopeHandler::CheckPossibleBadForwardRef(const Symbol &symbol) {
     }
     if ((IsDummy(symbol) ||
             (!symbol.has<UseDetails>() &&
-                (IsNamedConstant(symbol) || FindCommonBlockContaining(symbol)))) &&
+                (IsNamedConstant(symbol) ||
+                    FindCommonBlockContaining(symbol)))) &&
         isImplicitNoneType() && symbol.test(Symbol::Flag::Implicit) &&
         !context().HasError(symbol)) {
       // Dummy, COMMON, or PARAMETER named constant was implicitly typed despite



More information about the flang-commits mailing list