Skip to content

Commit 1457125

Browse files
authored
[flang] Allow forward reference to non-default INTEGER dummy (#141254)
A dummy argument with an explicit INTEGER type of non-default kind can be forward-referenced from a specification expression in many Fortran compilers. Handle by adding type declaration statements to the initial pass over a specification part's declaration constructs. Emit an optional warning under -pedantic. Fixes #140941.
1 parent a6432b9 commit 1457125

File tree

5 files changed

+104
-22
lines changed

5 files changed

+104
-22
lines changed

flang/docs/Extensions.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,10 @@ end
291291
* DATA statement initialization is allowed for procedure pointers outside
292292
structure constructors.
293293
* Nonstandard intrinsic functions: ISNAN, SIZEOF
294-
* A forward reference to a default INTEGER scalar dummy argument or
294+
* A forward reference to an INTEGER dummy argument is permitted to appear
295+
in a specification expression, such as an array bound, in a scope with
296+
IMPLICIT NONE(TYPE).
297+
* A forward reference to a default INTEGER scalar
295298
`COMMON` block variable is permitted to appear in a specification
296299
expression, such as an array bound, in a scope with IMPLICIT NONE(TYPE)
297300
if the name of the variable would have caused it to be implicitly typed

flang/include/flang/Support/Fortran-features.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
5555
UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr,
5656
SavedLocalInSpecExpr, PrintNamelist, AssumedRankPassedToNonAssumedRank,
5757
IgnoreIrrelevantAttributes, Unsigned, AmbiguousStructureConstructor,
58-
ContiguousOkForSeqAssociation)
58+
ContiguousOkForSeqAssociation, ForwardRefExplicitTypeDummy)
5959

6060
// Portability and suspicious usage warnings
6161
ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,

flang/lib/Semantics/resolve-names.cpp

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -768,10 +768,22 @@ class ScopeHandler : public ImplicitRulesVisitor {
768768
deferImplicitTyping_ = skipImplicitTyping_ = skip;
769769
}
770770

771+
void NoteEarlyDeclaredDummyArgument(Symbol &symbol) {
772+
earlyDeclaredDummyArguments_.insert(symbol);
773+
}
774+
bool IsEarlyDeclaredDummyArgument(Symbol &symbol) {
775+
return earlyDeclaredDummyArguments_.find(symbol) !=
776+
earlyDeclaredDummyArguments_.end();
777+
}
778+
void ForgetEarlyDeclaredDummyArgument(Symbol &symbol) {
779+
earlyDeclaredDummyArguments_.erase(symbol);
780+
}
781+
771782
private:
772783
Scope *currScope_{nullptr};
773784
FuncResultStack funcResultStack_{*this};
774785
std::map<Scope *, DeferredDeclarationState> deferred_;
786+
UnorderedSymbolSet earlyDeclaredDummyArguments_;
775787
};
776788

777789
class ModuleVisitor : public virtual ScopeHandler {
@@ -1970,6 +1982,9 @@ class ResolveNamesVisitor : public virtual ScopeHandler,
19701982
Scope &topScope_;
19711983

19721984
void PreSpecificationConstruct(const parser::SpecificationConstruct &);
1985+
void EarlyDummyTypeDeclaration(
1986+
const parser::Statement<common::Indirection<parser::TypeDeclarationStmt>>
1987+
&);
19731988
void CreateCommonBlockSymbols(const parser::CommonStmt &);
19741989
void CreateObjectSymbols(const std::list<parser::ObjectDecl> &, Attr);
19751990
void CreateGeneric(const parser::GenericSpec &);
@@ -5605,6 +5620,7 @@ Symbol &DeclarationVisitor::DeclareUnknownEntity(
56055620
} else {
56065621
Symbol &symbol{DeclareEntity<EntityDetails>(name, attrs)};
56075622
if (auto *type{GetDeclTypeSpec()}) {
5623+
ForgetEarlyDeclaredDummyArgument(symbol);
56085624
SetType(name, *type);
56095625
}
56105626
charInfo_.length.reset();
@@ -5681,6 +5697,7 @@ Symbol &DeclarationVisitor::DeclareProcEntity(
56815697
symbol.set(Symbol::Flag::Subroutine);
56825698
}
56835699
} else if (auto *type{GetDeclTypeSpec()}) {
5700+
ForgetEarlyDeclaredDummyArgument(symbol);
56845701
SetType(name, *type);
56855702
symbol.set(Symbol::Flag::Function);
56865703
}
@@ -5695,6 +5712,7 @@ Symbol &DeclarationVisitor::DeclareObjectEntity(
56955712
Symbol &symbol{DeclareEntity<ObjectEntityDetails>(name, attrs)};
56965713
if (auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
56975714
if (auto *type{GetDeclTypeSpec()}) {
5715+
ForgetEarlyDeclaredDummyArgument(symbol);
56985716
SetType(name, *type);
56995717
}
57005718
if (!arraySpec().empty()) {
@@ -5705,9 +5723,11 @@ Symbol &DeclarationVisitor::DeclareObjectEntity(
57055723
context().SetError(symbol);
57065724
}
57075725
} else if (MustBeScalar(symbol)) {
5708-
context().Warn(common::UsageWarning::PreviousScalarUse, name.source,
5709-
"'%s' appeared earlier as a scalar actual argument to a specification function"_warn_en_US,
5710-
name.source);
5726+
if (!context().HasError(symbol)) {
5727+
context().Warn(common::UsageWarning::PreviousScalarUse, name.source,
5728+
"'%s' appeared earlier as a scalar actual argument to a specification function"_warn_en_US,
5729+
name.source);
5730+
}
57115731
} else if (details->init() || symbol.test(Symbol::Flag::InDataStmt)) {
57125732
Say(name, "'%s' was initialized earlier as a scalar"_err_en_US);
57135733
} else {
@@ -8461,6 +8481,11 @@ const parser::Name *DeclarationVisitor::ResolveDataRef(
84618481
x.u);
84628482
}
84638483

8484+
static bool TypesMismatchIfNonNull(
8485+
const DeclTypeSpec *type1, const DeclTypeSpec *type2) {
8486+
return type1 && type2 && *type1 != *type2;
8487+
}
8488+
84648489
// If implicit types are allowed, ensure name is in the symbol table.
84658490
// Otherwise, report an error if it hasn't been declared.
84668491
const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
@@ -8482,13 +8507,30 @@ const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
84828507
symbol->set(Symbol::Flag::ImplicitOrError, false);
84838508
if (IsUplevelReference(*symbol)) {
84848509
MakeHostAssocSymbol(name, *symbol);
8485-
} else if (IsDummy(*symbol) ||
8486-
(!symbol->GetType() && FindCommonBlockContaining(*symbol))) {
8510+
} else if (IsDummy(*symbol)) {
84878511
CheckEntryDummyUse(name.source, symbol);
8512+
ConvertToObjectEntity(*symbol);
8513+
if (IsEarlyDeclaredDummyArgument(*symbol)) {
8514+
ForgetEarlyDeclaredDummyArgument(*symbol);
8515+
if (isImplicitNoneType()) {
8516+
context().Warn(common::LanguageFeature::ForwardRefImplicitNone,
8517+
name.source,
8518+
"'%s' was used under IMPLICIT NONE(TYPE) before being explicitly typed"_warn_en_US,
8519+
name.source);
8520+
} else if (TypesMismatchIfNonNull(
8521+
symbol->GetType(), GetImplicitType(*symbol))) {
8522+
context().Warn(common::LanguageFeature::ForwardRefExplicitTypeDummy,
8523+
name.source,
8524+
"'%s' was used before being explicitly typed (and its implicit type would differ)"_warn_en_US,
8525+
name.source);
8526+
}
8527+
}
8528+
ApplyImplicitRules(*symbol);
8529+
} else if (!symbol->GetType() && FindCommonBlockContaining(*symbol)) {
84888530
ConvertToObjectEntity(*symbol);
84898531
ApplyImplicitRules(*symbol);
84908532
} else if (const auto *tpd{symbol->detailsIf<TypeParamDetails>()};
8491-
tpd && !tpd->attr()) {
8533+
tpd && !tpd->attr()) {
84928534
Say(name,
84938535
"Type parameter '%s' was referenced before being declared"_err_en_US,
84948536
name.source);
@@ -9031,11 +9073,6 @@ static bool IsLocallyImplicitGlobalSymbol(
90319073
return false;
90329074
}
90339075

9034-
static bool TypesMismatchIfNonNull(
9035-
const DeclTypeSpec *type1, const DeclTypeSpec *type2) {
9036-
return type1 && type2 && *type1 != *type2;
9037-
}
9038-
90399076
// Check and set the Function or Subroutine flag on symbol; false on error.
90409077
bool ResolveNamesVisitor::SetProcFlag(
90419078
const parser::Name &name, Symbol &symbol, Symbol::Flag flag) {
@@ -9252,6 +9289,10 @@ void ResolveNamesVisitor::PreSpecificationConstruct(
92529289
const parser::SpecificationConstruct &spec) {
92539290
common::visit(
92549291
common::visitors{
9292+
[&](const parser::Statement<
9293+
common::Indirection<parser::TypeDeclarationStmt>> &y) {
9294+
EarlyDummyTypeDeclaration(y);
9295+
},
92559296
[&](const parser::Statement<Indirection<parser::GenericStmt>> &y) {
92569297
CreateGeneric(std::get<parser::GenericSpec>(y.statement.value().t));
92579298
},
@@ -9280,6 +9321,44 @@ void ResolveNamesVisitor::PreSpecificationConstruct(
92809321
spec.u);
92819322
}
92829323

9324+
void ResolveNamesVisitor::EarlyDummyTypeDeclaration(
9325+
const parser::Statement<common::Indirection<parser::TypeDeclarationStmt>>
9326+
&stmt) {
9327+
context().set_location(stmt.source);
9328+
const auto &[declTypeSpec, attrs, entities] = stmt.statement.value().t;
9329+
if (const auto *intrin{
9330+
std::get_if<parser::IntrinsicTypeSpec>(&declTypeSpec.u)}) {
9331+
if (const auto *intType{std::get_if<parser::IntegerTypeSpec>(&intrin->u)}) {
9332+
if (const auto &kind{intType->v}) {
9333+
if (!parser::Unwrap<parser::KindSelector::StarSize>(*kind) &&
9334+
!parser::Unwrap<parser::IntLiteralConstant>(*kind)) {
9335+
return;
9336+
}
9337+
}
9338+
const DeclTypeSpec *type{nullptr};
9339+
for (const auto &ent : entities) {
9340+
const auto &objName{std::get<parser::ObjectName>(ent.t)};
9341+
Resolve(objName, FindInScope(currScope(), objName));
9342+
if (Symbol * symbol{objName.symbol};
9343+
symbol && IsDummy(*symbol) && NeedsType(*symbol)) {
9344+
if (!type) {
9345+
type = ProcessTypeSpec(declTypeSpec);
9346+
if (!type || !type->IsNumeric(TypeCategory::Integer)) {
9347+
break;
9348+
}
9349+
}
9350+
symbol->SetType(*type);
9351+
NoteEarlyDeclaredDummyArgument(*symbol);
9352+
// Set the Implicit flag to disable bogus errors from
9353+
// being emitted later when this declaration is processed
9354+
// again normally.
9355+
symbol->set(Symbol::Flag::Implicit);
9356+
}
9357+
}
9358+
}
9359+
}
9360+
}
9361+
92839362
void ResolveNamesVisitor::CreateCommonBlockSymbols(
92849363
const parser::CommonStmt &commonStmt) {
92859364
for (const parser::CommonStmt::Block &block : commonStmt.blocks) {

flang/test/Semantics/OpenMP/linear-clause01.f90

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ subroutine linear_clause_02(arg_01, arg_02)
2020
!$omp declare simd linear(val(arg_01))
2121
real, intent(in) :: arg_01(:)
2222

23-
!ERROR: The list item 'arg_02' specified without the REF 'linear-modifier' must be of INTEGER type
2423
!ERROR: If the `linear-modifier` is REF or UVAL, the list item 'arg_02' must be a dummy argument without the VALUE attribute
2524
!$omp declare simd linear(uval(arg_02))
26-
!ERROR: The type of 'arg_02' has already been implicitly declared
2725
integer, value, intent(in) :: arg_02
2826

2927
!ERROR: The list item 'var' specified without the REF 'linear-modifier' must be of INTEGER type

flang/test/Semantics/resolve103.f90

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
! RUN: not %flang_fc1 -pedantic %s 2>&1 | FileCheck %s
22
! Test extension: allow forward references to dummy arguments or COMMON
33
! from specification expressions in scopes with IMPLICIT NONE(TYPE),
4-
! as long as those symbols are eventually typed later with the
5-
! same integer type they would have had without IMPLICIT NONE.
4+
! as long as those symbols are eventually typed later.
65

76
!CHECK: warning: 'n1' was used without (or before) being explicitly typed
87
!CHECK: error: No explicit type declared for dummy argument 'n1'
@@ -19,12 +18,15 @@ subroutine foo2(a, n2)
1918
double precision n2
2019
end
2120

22-
!CHECK: warning: 'n3' was used without (or before) being explicitly typed
23-
!CHECK-NOT: error: Dummy argument 'n3'
24-
subroutine foo3(a, n3)
21+
!CHECK: warning: 'n3a' was used under IMPLICIT NONE(TYPE) before being explicitly typed
22+
!CHECK: warning: 'n3b' was used under IMPLICIT NONE(TYPE) before being explicitly typed
23+
!CHECK-NOT: error: Dummy argument 'n3a'
24+
!CHECK-NOT: error: Dummy argument 'n3b'
25+
subroutine foo3(a, n3a, n3b)
2526
implicit none
26-
real a(n3)
27-
integer n3
27+
integer a(n3a, n3b)
28+
integer n3a
29+
integer(8) n3b
2830
end
2931

3032
!CHECK: warning: 'n4' was used without (or before) being explicitly typed

0 commit comments

Comments
 (0)