Skip to content

Commit ea2ff54

Browse files
committed
[flang] Extension: forward refs to dummy args under IMPLICIT NONE
Most Fortran compilers accept the following benign extension, and it appears in some applications: SUBROUTINE FOO(A,N) IMPLICIT NONE REAL A(N) ! N is used before being typed INTEGER N END Allow it in f18 only for default integer scalar dummy arguments. Differential Revesion: https://reviews.llvm.org/D96982
1 parent 4cf3c35 commit ea2ff54

File tree

5 files changed

+134
-40
lines changed

5 files changed

+134
-40
lines changed

flang/docs/Extensions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ accepted if enabled by command-line options.
129129
* DATA statement initialization is allowed for procedure pointers outside
130130
structure constructors.
131131
* Nonstandard intrinsic functions: ISNAN, SIZEOF
132+
* A forward reference to a default INTEGER scalar dummy argument is
133+
permitted to appear in a specification expression, such as an array
134+
bound, in a scope with IMPLICIT NONE(TYPE) if the name
135+
of the dummy argument would have caused it to be implicitly typed
136+
as default INTEGER if IMPLICIT NONE(TYPE) were absent.
132137

133138
### Extensions supported when enabled by options
134139

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
2929
AdditionalFormats, BigIntLiterals, RealDoControls,
3030
EquivalenceNumericWithCharacter, AdditionalIntrinsics, AnonymousParents,
3131
OldLabelDoEndStatements, LogicalIntegerAssignment, EmptySourceFile,
32-
ProgramReturn, ImplicitNoneTypeNever, ImplicitNoneTypeAlways)
32+
ProgramReturn, ImplicitNoneTypeNever, ImplicitNoneTypeAlways,
33+
ForwardRefDummyImplicitNone)
3334

3435
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
3536

flang/lib/Semantics/resolve-names.cpp

Lines changed: 98 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ class ImplicitRules {
6969
void set_isImplicitNoneExternal(bool x) { isImplicitNoneExternal_ = x; }
7070
void set_inheritFromParent(bool x) { inheritFromParent_ = x; }
7171
// Get the implicit type for this name. May be null.
72-
const DeclTypeSpec *GetType(SourceName) const;
72+
const DeclTypeSpec *GetType(
73+
SourceName, bool respectImplicitNone = true) const;
7374
// Record the implicit type for the range of characters [fromLetter,
7475
// toLetter].
7576
void SetTypeMapping(const DeclTypeSpec &type, parser::Location fromLetter,
@@ -380,8 +381,9 @@ class ImplicitRulesVisitor : public DeclTypeSpecVisitor {
380381
bool Pre(const parser::ImplicitSpec &);
381382
void Post(const parser::ImplicitSpec &);
382383

383-
const DeclTypeSpec *GetType(SourceName name) {
384-
return implicitRules_->GetType(name);
384+
const DeclTypeSpec *GetType(
385+
SourceName name, bool respectImplicitNoneType = true) {
386+
return implicitRules_->GetType(name, respectImplicitNoneType);
385387
}
386388
bool isImplicitNoneType() const {
387389
return implicitRules_->isImplicitNoneType();
@@ -583,9 +585,11 @@ class ScopeHandler : public ImplicitRulesVisitor {
583585

584586
protected:
585587
// Apply the implicit type rules to this symbol.
586-
void ApplyImplicitRules(Symbol &);
588+
void ApplyImplicitRules(Symbol &, bool allowForwardReference = false);
589+
bool ImplicitlyTypeForwardRef(Symbol &);
587590
void AcquireIntrinsicProcedureFlags(Symbol &);
588-
const DeclTypeSpec *GetImplicitType(Symbol &, const Scope &);
591+
const DeclTypeSpec *GetImplicitType(
592+
Symbol &, bool respectImplicitNoneType = true);
589593
bool ConvertToObjectEntity(Symbol &);
590594
bool ConvertToProcEntity(Symbol &);
591595

@@ -1412,14 +1416,15 @@ bool ImplicitRules::isImplicitNoneExternal() const {
14121416
}
14131417
}
14141418

1415-
const DeclTypeSpec *ImplicitRules::GetType(SourceName name) const {
1419+
const DeclTypeSpec *ImplicitRules::GetType(
1420+
SourceName name, bool respectImplicitNoneType) const {
14161421
char ch{name.begin()[0]};
1417-
if (isImplicitNoneType_) {
1422+
if (isImplicitNoneType_ && respectImplicitNoneType) {
14181423
return nullptr;
14191424
} else if (auto it{map_.find(ch)}; it != map_.end()) {
14201425
return &*it->second;
14211426
} else if (inheritFromParent_) {
1422-
return parent_->GetType(name);
1427+
return parent_->GetType(name, respectImplicitNoneType);
14231428
} else if (ch >= 'i' && ch <= 'n') {
14241429
return &context_.MakeNumericType(TypeCategory::Integer);
14251430
} else if (ch >= 'a' && ch <= 'z') {
@@ -2125,39 +2130,72 @@ static bool NeedsType(const Symbol &symbol) {
21252130
symbol.details());
21262131
}
21272132

2128-
void ScopeHandler::ApplyImplicitRules(Symbol &symbol) {
2129-
if (NeedsType(symbol)) {
2130-
const Scope *scope{&symbol.owner()};
2131-
if (scope->IsGlobal()) {
2132-
scope = &currScope();
2133+
void ScopeHandler::ApplyImplicitRules(
2134+
Symbol &symbol, bool allowForwardReference) {
2135+
if (!NeedsType(symbol)) {
2136+
return;
2137+
}
2138+
if (const DeclTypeSpec * type{GetImplicitType(symbol)}) {
2139+
symbol.set(Symbol::Flag::Implicit);
2140+
symbol.SetType(*type);
2141+
return;
2142+
}
2143+
if (symbol.has<ProcEntityDetails>() && !symbol.attrs().test(Attr::EXTERNAL)) {
2144+
std::optional<Symbol::Flag> functionOrSubroutineFlag;
2145+
if (symbol.test(Symbol::Flag::Function)) {
2146+
functionOrSubroutineFlag = Symbol::Flag::Function;
2147+
} else if (symbol.test(Symbol::Flag::Subroutine)) {
2148+
functionOrSubroutineFlag = Symbol::Flag::Subroutine;
21332149
}
2134-
if (const DeclTypeSpec *
2135-
type{GetImplicitType(symbol, GetInclusiveScope(*scope))}) {
2136-
symbol.set(Symbol::Flag::Implicit);
2137-
symbol.SetType(*type);
2150+
if (IsIntrinsic(symbol.name(), functionOrSubroutineFlag)) {
2151+
// type will be determined in expression semantics
2152+
AcquireIntrinsicProcedureFlags(symbol);
21382153
return;
21392154
}
2140-
if (symbol.has<ProcEntityDetails>() &&
2141-
!symbol.attrs().test(Attr::EXTERNAL)) {
2142-
std::optional<Symbol::Flag> functionOrSubroutineFlag;
2143-
if (symbol.test(Symbol::Flag::Function)) {
2144-
functionOrSubroutineFlag = Symbol::Flag::Function;
2145-
} else if (symbol.test(Symbol::Flag::Subroutine)) {
2146-
functionOrSubroutineFlag = Symbol::Flag::Subroutine;
2147-
}
2148-
if (IsIntrinsic(symbol.name(), functionOrSubroutineFlag)) {
2149-
// type will be determined in expression semantics
2150-
AcquireIntrinsicProcedureFlags(symbol);
2151-
return;
2152-
}
2153-
}
2154-
if (!context().HasError(symbol)) {
2155-
Say(symbol.name(), "No explicit type declared for '%s'"_err_en_US);
2156-
context().SetError(symbol);
2157-
}
2155+
}
2156+
if (allowForwardReference && ImplicitlyTypeForwardRef(symbol)) {
2157+
return;
2158+
}
2159+
if (!context().HasError(symbol)) {
2160+
Say(symbol.name(), "No explicit type declared for '%s'"_err_en_US);
2161+
context().SetError(symbol);
21582162
}
21592163
}
21602164

2165+
// Extension: Allow forward references to scalar integer dummy arguments
2166+
// to appear in specification expressions under IMPLICIT NONE(TYPE) when
2167+
// what would otherwise have been their implicit type is default INTEGER.
2168+
bool ScopeHandler::ImplicitlyTypeForwardRef(Symbol &symbol) {
2169+
if (!inSpecificationPart_ || context().HasError(symbol) || !IsDummy(symbol) ||
2170+
symbol.Rank() != 0 ||
2171+
!context().languageFeatures().IsEnabled(
2172+
common::LanguageFeature::ForwardRefDummyImplicitNone)) {
2173+
return false;
2174+
}
2175+
const DeclTypeSpec *type{
2176+
GetImplicitType(symbol, false /*ignore IMPLICIT NONE*/)};
2177+
if (!type || !type->IsNumeric(TypeCategory::Integer)) {
2178+
return false;
2179+
}
2180+
auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())};
2181+
if (!kind || *kind != context().GetDefaultKind(TypeCategory::Integer)) {
2182+
return false;
2183+
}
2184+
if (!ConvertToObjectEntity(symbol)) {
2185+
return false;
2186+
}
2187+
// TODO: check no INTENT(OUT)?
2188+
if (context().languageFeatures().ShouldWarn(
2189+
common::LanguageFeature::ForwardRefDummyImplicitNone)) {
2190+
Say(symbol.name(),
2191+
"Dummy argument '%s' was used without being explicitly typed"_en_US,
2192+
symbol.name());
2193+
}
2194+
symbol.set(Symbol::Flag::Implicit);
2195+
symbol.SetType(*type);
2196+
return true;
2197+
}
2198+
21612199
// Ensure that the symbol for an intrinsic procedure is marked with
21622200
// the INTRINSIC attribute. Also set PURE &/or ELEMENTAL as
21632201
// appropriate.
@@ -2177,8 +2215,14 @@ void ScopeHandler::AcquireIntrinsicProcedureFlags(Symbol &symbol) {
21772215
}
21782216

21792217
const DeclTypeSpec *ScopeHandler::GetImplicitType(
2180-
Symbol &symbol, const Scope &scope) {
2181-
const auto *type{implicitRulesMap_->at(&scope).GetType(symbol.name())};
2218+
Symbol &symbol, bool respectImplicitNoneType) {
2219+
const Scope *scope{&symbol.owner()};
2220+
if (scope->IsGlobal()) {
2221+
scope = &currScope();
2222+
}
2223+
scope = &GetInclusiveScope(*scope);
2224+
const auto *type{implicitRulesMap_->at(scope).GetType(
2225+
symbol.name(), respectImplicitNoneType)};
21822226
if (type) {
21832227
if (const DerivedTypeSpec * derived{type->AsDerived()}) {
21842228
// Resolve any forward-referenced derived type; a quick no-op else.
@@ -2282,6 +2326,16 @@ bool ScopeHandler::CheckPossibleBadForwardRef(const Symbol &symbol) {
22822326
context().SetError(symbol);
22832327
return true;
22842328
}
2329+
if (IsDummy(symbol) && isImplicitNoneType() &&
2330+
symbol.test(Symbol::Flag::Implicit) && !context().HasError(symbol)) {
2331+
// Dummy was implicitly typed despite IMPLICIT NONE(TYPE) in
2332+
// ApplyImplicitRules() due to use in a specification expression,
2333+
// and no explicit type declaration appeared later.
2334+
Say(symbol.name(),
2335+
"No explicit type declared for dummy argument '%s'"_err_en_US);
2336+
context().SetError(symbol);
2337+
return true;
2338+
}
22852339
}
22862340
return false;
22872341
}
@@ -5731,7 +5785,7 @@ bool DeclarationVisitor::CheckForHostAssociatedImplicit(
57315785
return false;
57325786
}
57335787
if (name.symbol) {
5734-
ApplyImplicitRules(*name.symbol);
5788+
ApplyImplicitRules(*name.symbol, true);
57355789
}
57365790
Symbol *hostSymbol;
57375791
Scope *host{GetHostProcedure()};
@@ -6282,6 +6336,12 @@ void ResolveNamesVisitor::FinishSpecificationPart(
62826336
if (NeedsExplicitType(symbol)) {
62836337
ApplyImplicitRules(symbol);
62846338
}
6339+
if (IsDummy(symbol) && isImplicitNoneType() &&
6340+
symbol.test(Symbol::Flag::Implicit) && !context().HasError(symbol)) {
6341+
Say(symbol.name(),
6342+
"No explicit type declared for dummy argument '%s'"_err_en_US);
6343+
context().SetError(symbol);
6344+
}
62856345
if (symbol.has<GenericDetails>()) {
62866346
CheckGenericProcedures(symbol);
62876347
}

flang/test/Semantics/assign04.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ real function f9() result(r)
126126
f9 = 1.0
127127
end
128128

129-
!ERROR: No explicit type declared for 'n'
129+
!ERROR: No explicit type declared for dummy argument 'n'
130130
subroutine s10(a, n)
131131
implicit none
132132
real a(n)

flang/test/Semantics/resolve103.f90

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
! RUN: not %f18 -Mstandard %s 2>&1 | FileCheck %s
2+
! Test extension: allow forward references to dummy arguments
3+
! 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.
6+
7+
!CHECK: Dummy argument 'n1' was used without being explicitly typed
8+
!CHECK: error: No explicit type declared for dummy argument 'n1'
9+
subroutine foo1(a, n1)
10+
implicit none
11+
real a(n1)
12+
end
13+
14+
!CHECK: Dummy argument 'n2' was used without being explicitly typed
15+
subroutine foo2(a, n2)
16+
implicit none
17+
real a(n2)
18+
!CHECK: error: The type of 'n2' has already been implicitly declared
19+
double precision n2
20+
end
21+
22+
!CHECK: Dummy argument 'n3' was used without being explicitly typed
23+
!CHECK-NOT: error:
24+
subroutine foo3(a, n3)
25+
implicit none
26+
real a(n3)
27+
integer n3
28+
end

0 commit comments

Comments
 (0)