[flang-commits] [flang] 77fe8dc - [flang][semantics] Allow forward-typed PARAMETER constants under IMPLICIT NONE (#203398)
via flang-commits
flang-commits at lists.llvm.org
Tue Jun 16 12:56:28 PDT 2026
Author: Eugene Epshteyn
Date: 2026-06-16T19:56:23Z
New Revision: 77fe8dc5c970fb46092c3abed59e2fb66529ec4a
URL: https://github.com/llvm/llvm-project/commit/77fe8dc5c970fb46092c3abed59e2fb66529ec4a
DIFF: https://github.com/llvm/llvm-project/commit/77fe8dc5c970fb46092c3abed59e2fb66529ec4a.diff
LOG: [flang][semantics] Allow forward-typed PARAMETER constants under IMPLICIT NONE (#203398)
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.
Assisted-by: AI
Added:
flang/test/Semantics/resolve130.f90
flang/test/Semantics/resolve131.f90
Modified:
flang/docs/Extensions.md
flang/lib/Semantics/resolve-names.cpp
Removed:
################################################################################
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 2acdaa916f557..6d2d0bf24b194 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -3416,9 +3416,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)) {
@@ -3426,12 +3433,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;
@@ -3621,12 +3636,17 @@ bool ScopeHandler::CheckPossibleBadForwardRef(const Symbol &symbol) {
return true;
}
if ((IsDummy(symbol) ||
- (!symbol.has<UseDetails>() && FindCommonBlockContaining(symbol))) &&
+ (!symbol.has<UseDetails>() &&
+ (IsNamedConstant(symbol) ||
+ 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,
- // and no explicit type declaration appeared later.
+ // 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. 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;
@@ -5979,7 +5999,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..a83a08b554c5b
--- /dev/null
+++ b/flang/test/Semantics/resolve130.f90
@@ -0,0 +1,76 @@
+! 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
diff ers from the would-be-implicit type is
+! rejected. 'm' begins with a letter in I-N, so under implicit typing rules
+! its type would be default INTEGER (reported below as INTEGER(4), the default
+! integer kind on this target), which the later REAL declaration contradicts.
+!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
+
+! Without IMPLICIT NONE(TYPE) this is not an extension: implicit typing is in
+! effect, so no forward-reference portability warning is emitted. A later
+! declaration that contradicts the would-be-implicit type is still rejected.
+!CHECK: error: The type of 'mm' has already been implicitly declared as INTEGER(4)
+subroutine s5
+ parameter(mm=4096)
+ real mm
+end
+
+! A later declaration must also match the kind of the would-be-implicit type
+! (F2023 8.6.11 p2: the declared type and type parameters must match). 'kn'
+! would be default INTEGER(4), so a later INTEGER(8) declaration is rejected.
+!CHECK: warning: 'kn' was used without (or before) being explicitly typed
+subroutine s6
+ implicit none
+ parameter(kn=4)
+!CHECK: error: The type of 'kn' has already been implicitly declared as INTEGER(4)
+ integer(8) kn
+end
+
+! When the PARAMETER initializer is incompatible with the would-be-implicit
+! type, the constant is first given that implicit type ('c' would be default
+! REAL), so the initializer conversion fails and the later CHARACTER
+! declaration additionally conflicts with the implicit type.
+!CHECK: warning: 'c' was used without (or before) being explicitly typed
+subroutine s7
+ implicit none
+!CHECK: error: Initialization expression cannot be converted to declared type of 'c' from CHARACTER(KIND=1,LEN=2_8)
+ parameter(c='hi')
+!CHECK: error: The type of 'c' has already been implicitly declared as REAL(4)
+ character(2) c
+end
diff --git a/flang/test/Semantics/resolve131.f90 b/flang/test/Semantics/resolve131.f90
new file mode 100644
index 0000000000000..8e7c15c73e977
--- /dev/null
+++ b/flang/test/Semantics/resolve131.f90
@@ -0,0 +1,34 @@
+! 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
+
+! Without IMPLICIT NONE(TYPE) the same code is plain implicit typing rather than
+! an extension: the named constant is implicitly typed and the later matching
+! declaration is accepted, with no diagnostic even under -pedantic.
+subroutine s1b
+ parameter(n=4096)
+ 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
More information about the flang-commits
mailing list