Skip to content

Commit 03eb172

Browse files
committed
[flang] Allow forward reference to non-default INTEGER dummy
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 ff7bb17 commit 03eb172

File tree

5 files changed

+102
-22
lines changed

5 files changed

+102
-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: 88 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 {
@@ -1119,6 +1131,7 @@ class DeclarationVisitor : public ArraySpecVisitor,
11191131
template <typename T>
11201132
Symbol &DeclareEntity(const parser::Name &name, Attrs attrs) {
11211133
Symbol &symbol{MakeSymbol(name, attrs)};
1134+
ForgetEarlyDeclaredDummyArgument(symbol);
11221135
if (context().HasError(symbol) || symbol.has<T>()) {
11231136
return symbol; // OK or error already reported
11241137
} else if (symbol.has<UnknownDetails>()) {
@@ -1976,6 +1989,9 @@ class ResolveNamesVisitor : public virtual ScopeHandler,
19761989
Scope &topScope_;
19771990

19781991
void PreSpecificationConstruct(const parser::SpecificationConstruct &);
1992+
void EarlyDummyTypeDeclaration(
1993+
const parser::Statement<common::Indirection<parser::TypeDeclarationStmt>>
1994+
&);
19791995
void CreateCommonBlockSymbols(const parser::CommonStmt &);
19801996
void CreateObjectSymbols(const std::list<parser::ObjectDecl> &, Attr);
19811997
void CreateGeneric(const parser::GenericSpec &);
@@ -5711,9 +5727,11 @@ Symbol &DeclarationVisitor::DeclareObjectEntity(
57115727
context().SetError(symbol);
57125728
}
57135729
} else if (MustBeScalar(symbol)) {
5714-
context().Warn(common::UsageWarning::PreviousScalarUse, name.source,
5715-
"'%s' appeared earlier as a scalar actual argument to a specification function"_warn_en_US,
5716-
name.source);
5730+
if (!context().HasError(symbol)) {
5731+
context().Warn(common::UsageWarning::PreviousScalarUse, name.source,
5732+
"'%s' appeared earlier as a scalar actual argument to a specification function"_warn_en_US,
5733+
name.source);
5734+
}
57175735
} else if (details->init() || symbol.test(Symbol::Flag::InDataStmt)) {
57185736
Say(name, "'%s' was initialized earlier as a scalar"_err_en_US);
57195737
} else {
@@ -8467,6 +8485,11 @@ const parser::Name *DeclarationVisitor::ResolveDataRef(
84678485
x.u);
84688486
}
84698487

8488+
static bool TypesMismatchIfNonNull(
8489+
const DeclTypeSpec *type1, const DeclTypeSpec *type2) {
8490+
return type1 && type2 && *type1 != *type2;
8491+
}
8492+
84708493
// If implicit types are allowed, ensure name is in the symbol table.
84718494
// Otherwise, report an error if it hasn't been declared.
84728495
const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
@@ -8488,13 +8511,30 @@ const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
84888511
symbol->set(Symbol::Flag::ImplicitOrError, false);
84898512
if (IsUplevelReference(*symbol)) {
84908513
MakeHostAssocSymbol(name, *symbol);
8491-
} else if (IsDummy(*symbol) ||
8492-
(!symbol->GetType() && FindCommonBlockContaining(*symbol))) {
8514+
} else if (IsDummy(*symbol)) {
84938515
CheckEntryDummyUse(name.source, symbol);
8516+
ConvertToObjectEntity(*symbol);
8517+
if (IsEarlyDeclaredDummyArgument(*symbol)) {
8518+
ForgetEarlyDeclaredDummyArgument(*symbol);
8519+
if (isImplicitNoneType()) {
8520+
context().Warn(common::LanguageFeature::ForwardRefImplicitNone,
8521+
name.source,
8522+
"'%s' was used under IMPLICIT NONE(TYPE) before being explicitly typed"_warn_en_US,
8523+
name.source);
8524+
} else if (TypesMismatchIfNonNull(
8525+
symbol->GetType(), GetImplicitType(*symbol))) {
8526+
context().Warn(common::LanguageFeature::ForwardRefExplicitTypeDummy,
8527+
name.source,
8528+
"'%s' was used before being explicitly typed (and its implicit type would differ)"_warn_en_US,
8529+
name.source);
8530+
}
8531+
}
8532+
ApplyImplicitRules(*symbol);
8533+
} else if (!symbol->GetType() && FindCommonBlockContaining(*symbol)) {
84948534
ConvertToObjectEntity(*symbol);
84958535
ApplyImplicitRules(*symbol);
84968536
} else if (const auto *tpd{symbol->detailsIf<TypeParamDetails>()};
8497-
tpd && !tpd->attr()) {
8537+
tpd && !tpd->attr()) {
84988538
Say(name,
84998539
"Type parameter '%s' was referenced before being declared"_err_en_US,
85008540
name.source);
@@ -9037,11 +9077,6 @@ static bool IsLocallyImplicitGlobalSymbol(
90379077
return false;
90389078
}
90399079

9040-
static bool TypesMismatchIfNonNull(
9041-
const DeclTypeSpec *type1, const DeclTypeSpec *type2) {
9042-
return type1 && type2 && *type1 != *type2;
9043-
}
9044-
90459080
// Check and set the Function or Subroutine flag on symbol; false on error.
90469081
bool ResolveNamesVisitor::SetProcFlag(
90479082
const parser::Name &name, Symbol &symbol, Symbol::Flag flag) {
@@ -9258,6 +9293,10 @@ void ResolveNamesVisitor::PreSpecificationConstruct(
92589293
const parser::SpecificationConstruct &spec) {
92599294
common::visit(
92609295
common::visitors{
9296+
[&](const parser::Statement<
9297+
common::Indirection<parser::TypeDeclarationStmt>> &y) {
9298+
EarlyDummyTypeDeclaration(y);
9299+
},
92619300
[&](const parser::Statement<Indirection<parser::GenericStmt>> &y) {
92629301
CreateGeneric(std::get<parser::GenericSpec>(y.statement.value().t));
92639302
},
@@ -9286,6 +9325,44 @@ void ResolveNamesVisitor::PreSpecificationConstruct(
92869325
spec.u);
92879326
}
92889327

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