[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
This commit is contained in:
Eugene Epshteyn 2026-06-16 15:56:23 -04:00 committed by GitHub
parent 2dfdd094d0
commit 77fe8dc5c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 144 additions and 10 deletions

View file

@ -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()

View file

@ -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));

View file

@ -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 differs 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

View file

@ -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