Skip to content

Commit d3a2c6f

Browse files
klauslerjeanPerier
authored andcommitted
[flang] Extension: reduced scope for some implied DO loop indices
The index of an implied DO loop in a DATA statement or array constructor is defined by Fortran 2018 to have scope over its implied DO loop. This definition is unfortunate, because it requires the implied DO loop's bounds expressions to be in the scope of the index variable. Consequently, in code like integer, parameter :: j = 5 real, save :: a(5) = [(j, j=1, j)] the upper bound of the loop is a reference to the index variable, not the parameter in the enclosing scope. This patch limits the scope of the index variable to the "body" of the implied DO loop as one would naturally expect, with a warning. I would have preferred to make this a hard error, but most Fortran compilers treat this case as f18 now does. If the standard were to be fixed, the warning could be made optional. Differential Revision: https://reviews.llvm.org/D108595
1 parent 7f93fd3 commit d3a2c6f

File tree

10 files changed

+140
-67
lines changed

10 files changed

+140
-67
lines changed

flang/docs/Extensions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ write(buffer,*,delim="QUOTE") quotes
5858
print "('>',a10,'<')", buffer
5959
end
6060
```
61+
* The name of the control variable in an implied DO loop in an array
62+
constructor or DATA statement has a scope over the value-list only,
63+
not the bounds of the implied DO loop. It is not advisable to use
64+
an object of the same name as the index variable in a bounds
65+
expression, but it will work, instead of being needlessly undefined.
6166

6267
## Extensions, deletions, and legacy features supported by default
6368

flang/lib/Semantics/expression.cpp

Lines changed: 48 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,15 +1409,6 @@ void ArrayConstructorContext::Add(const parser::AcImpliedDo &impliedDo) {
14091409
if (const auto dynamicType{DynamicType::From(symbol)}) {
14101410
kind = dynamicType->kind();
14111411
}
1412-
if (!exprAnalyzer_.AddImpliedDo(name, kind)) {
1413-
if (!(messageDisplayedSet_ & 0x20)) {
1414-
exprAnalyzer_.SayAt(name,
1415-
"Implied DO index is active in surrounding implied DO loop "
1416-
"and may not have the same name"_err_en_US); // C7115
1417-
messageDisplayedSet_ |= 0x20;
1418-
}
1419-
return;
1420-
}
14211412
std::optional<Expr<ImpliedDoIntType>> lower{
14221413
GetSpecificIntExpr<ImpliedDoIntType::kind>(bounds.lower)};
14231414
std::optional<Expr<ImpliedDoIntType>> upper{
@@ -1428,49 +1419,57 @@ void ArrayConstructorContext::Add(const parser::AcImpliedDo &impliedDo) {
14281419
if (!stride) {
14291420
stride = Expr<ImpliedDoIntType>{1};
14301421
}
1431-
// Check for constant bounds; the loop may require complete unrolling
1432-
// of the parse tree if all bounds are constant in order to allow the
1433-
// implied DO loop index to qualify as a constant expression.
1434-
auto cLower{ToInt64(lower)};
1435-
auto cUpper{ToInt64(upper)};
1436-
auto cStride{ToInt64(stride)};
1437-
if (!(messageDisplayedSet_ & 0x10) && cStride && *cStride == 0) {
1438-
exprAnalyzer_.SayAt(bounds.step.value().thing.thing.value().source,
1439-
"The stride of an implied DO loop must not be zero"_err_en_US);
1440-
messageDisplayedSet_ |= 0x10;
1441-
}
1442-
bool isConstant{cLower && cUpper && cStride && *cStride != 0};
1443-
bool isNonemptyConstant{isConstant &&
1444-
((*cStride > 0 && *cLower <= *cUpper) ||
1445-
(*cStride < 0 && *cLower >= *cUpper))};
1446-
bool unrollConstantLoop{false};
1447-
parser::Messages buffer;
1448-
auto saveMessagesDisplayed{messageDisplayedSet_};
1449-
{
1450-
auto messageRestorer{
1451-
exprAnalyzer_.GetContextualMessages().SetMessages(buffer)};
1452-
auto v{std::move(values_)};
1453-
for (const auto &value :
1454-
std::get<std::list<parser::AcValue>>(impliedDo.t)) {
1455-
Add(value);
1456-
}
1457-
std::swap(v, values_);
1458-
if (isNonemptyConstant && buffer.AnyFatalError()) {
1459-
unrollConstantLoop = true;
1460-
} else {
1461-
values_.Push(ImpliedDo<SomeType>{name, std::move(*lower),
1462-
std::move(*upper), std::move(*stride), std::move(v)});
1422+
if (exprAnalyzer_.AddImpliedDo(name, kind)) {
1423+
// Check for constant bounds; the loop may require complete unrolling
1424+
// of the parse tree if all bounds are constant in order to allow the
1425+
// implied DO loop index to qualify as a constant expression.
1426+
auto cLower{ToInt64(lower)};
1427+
auto cUpper{ToInt64(upper)};
1428+
auto cStride{ToInt64(stride)};
1429+
if (!(messageDisplayedSet_ & 0x10) && cStride && *cStride == 0) {
1430+
exprAnalyzer_.SayAt(bounds.step.value().thing.thing.value().source,
1431+
"The stride of an implied DO loop must not be zero"_err_en_US);
1432+
messageDisplayedSet_ |= 0x10;
1433+
}
1434+
bool isConstant{cLower && cUpper && cStride && *cStride != 0};
1435+
bool isNonemptyConstant{isConstant &&
1436+
((*cStride > 0 && *cLower <= *cUpper) ||
1437+
(*cStride < 0 && *cLower >= *cUpper))};
1438+
bool unrollConstantLoop{false};
1439+
parser::Messages buffer;
1440+
auto saveMessagesDisplayed{messageDisplayedSet_};
1441+
{
1442+
auto messageRestorer{
1443+
exprAnalyzer_.GetContextualMessages().SetMessages(buffer)};
1444+
auto v{std::move(values_)};
1445+
for (const auto &value :
1446+
std::get<std::list<parser::AcValue>>(impliedDo.t)) {
1447+
Add(value);
1448+
}
1449+
std::swap(v, values_);
1450+
if (isNonemptyConstant && buffer.AnyFatalError()) {
1451+
unrollConstantLoop = true;
1452+
} else {
1453+
values_.Push(ImpliedDo<SomeType>{name, std::move(*lower),
1454+
std::move(*upper), std::move(*stride), std::move(v)});
1455+
}
14631456
}
1464-
}
1465-
if (unrollConstantLoop) {
1466-
messageDisplayedSet_ = saveMessagesDisplayed;
1467-
UnrollConstantImpliedDo(impliedDo, name, *cLower, *cUpper, *cStride);
1468-
} else if (auto *messages{
1469-
exprAnalyzer_.GetContextualMessages().messages()}) {
1470-
messages->Annex(std::move(buffer));
1457+
if (unrollConstantLoop) {
1458+
messageDisplayedSet_ = saveMessagesDisplayed;
1459+
UnrollConstantImpliedDo(impliedDo, name, *cLower, *cUpper, *cStride);
1460+
} else if (auto *messages{
1461+
exprAnalyzer_.GetContextualMessages().messages()}) {
1462+
messages->Annex(std::move(buffer));
1463+
}
1464+
exprAnalyzer_.RemoveImpliedDo(name);
1465+
} else if (!(messageDisplayedSet_ & 0x20)) {
1466+
exprAnalyzer_.SayAt(name,
1467+
"Implied DO index '%s' is active in a surrounding implied DO loop "
1468+
"and may not have the same name"_err_en_US,
1469+
name); // C7115
1470+
messageDisplayedSet_ |= 0x20;
14711471
}
14721472
}
1473-
exprAnalyzer_.RemoveImpliedDo(name);
14741473
}
14751474

14761475
// Fortran considers an implied DO index of an array constructor to be

flang/lib/Semantics/resolve-names.cpp

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -901,11 +901,12 @@ class DeclarationVisitor : public ArraySpecVisitor,
901901
// it comes from the entity in the containing scope, or implicit rules.
902902
// Return pointer to the new symbol, or nullptr on error.
903903
Symbol *DeclareLocalEntity(const parser::Name &);
904-
// Declare a statement entity (e.g., an implied DO loop index).
905-
// If there isn't a type specified, implicit rules apply.
906-
// Return pointer to the new symbol, or nullptr on error.
907-
Symbol *DeclareStatementEntity(
908-
const parser::Name &, const std::optional<parser::IntegerTypeSpec> &);
904+
// Declare a statement entity (i.e., an implied DO loop index for
905+
// a DATA statement or an array constructor). If there isn't an explict
906+
// type specified, implicit rules apply. Return pointer to the new symbol,
907+
// or nullptr on error.
908+
Symbol *DeclareStatementEntity(const parser::DoVariable &,
909+
const std::optional<parser::IntegerTypeSpec> &);
909910
Symbol &MakeCommonBlockSymbol(const parser::Name &);
910911
Symbol &MakeCommonBlockSymbol(const std::optional<parser::Name> &);
911912
bool CheckUseError(const parser::Name &);
@@ -926,6 +927,16 @@ class DeclarationVisitor : public ArraySpecVisitor,
926927
Symbol *NoteInterfaceName(const parser::Name &);
927928
bool IsUplevelReference(const Symbol &);
928929

930+
std::optional<SourceName> BeginCheckOnIndexUseInOwnBounds(
931+
const parser::DoVariable &name) {
932+
std::optional<SourceName> result{checkIndexUseInOwnBounds_};
933+
checkIndexUseInOwnBounds_ = name.thing.thing.source;
934+
return result;
935+
}
936+
void EndCheckOnIndexUseInOwnBounds(const std::optional<SourceName> &restore) {
937+
checkIndexUseInOwnBounds_ = restore;
938+
}
939+
929940
private:
930941
// The attribute corresponding to the statement containing an ObjectDecl
931942
std::optional<Attr> objectDeclAttr_;
@@ -956,6 +967,9 @@ class DeclarationVisitor : public ArraySpecVisitor,
956967
} enumerationState_;
957968
// Set for OldParameterStmt processing
958969
bool inOldStyleParameterStmt_{false};
970+
// Set when walking DATA & array constructor implied DO loop bounds
971+
// to warn about use of the implied DO intex therein.
972+
std::optional<SourceName> checkIndexUseInOwnBounds_;
959973

960974
bool HandleAttributeStmt(Attr, const std::list<parser::Name> &);
961975
Symbol &HandleAttributeStmt(Attr, const parser::Name &);
@@ -5009,8 +5023,10 @@ Symbol *DeclarationVisitor::DeclareLocalEntity(const parser::Name &name) {
50095023
return &MakeHostAssocSymbol(name, prev);
50105024
}
50115025

5012-
Symbol *DeclarationVisitor::DeclareStatementEntity(const parser::Name &name,
5026+
Symbol *DeclarationVisitor::DeclareStatementEntity(
5027+
const parser::DoVariable &doVar,
50135028
const std::optional<parser::IntegerTypeSpec> &type) {
5029+
const parser::Name &name{doVar.thing.thing};
50145030
const DeclTypeSpec *declTypeSpec{nullptr};
50155031
if (auto *prev{FindSymbol(name)}) {
50165032
if (prev->owner() == currScope()) {
@@ -5036,7 +5052,9 @@ Symbol *DeclarationVisitor::DeclareStatementEntity(const parser::Name &name,
50365052
} else {
50375053
ApplyImplicitRules(symbol);
50385054
}
5039-
return Resolve(name, &symbol);
5055+
Symbol *result{Resolve(name, &symbol)};
5056+
AnalyzeExpr(context(), doVar); // enforce INTEGER type
5057+
return result;
50405058
}
50415059

50425060
// Set the type of an entity or report an error.
@@ -5320,9 +5338,7 @@ bool ConstructVisitor::Pre(const parser::LocalitySpec::Shared &x) {
53205338

53215339
bool ConstructVisitor::Pre(const parser::AcSpec &x) {
53225340
ProcessTypeSpec(x.type);
5323-
PushScope(Scope::Kind::ImpliedDos, nullptr);
53245341
Walk(x.values);
5325-
PopScope();
53265342
return false;
53275343
}
53285344

@@ -5333,9 +5349,18 @@ bool ConstructVisitor::Pre(const parser::AcImpliedDo &x) {
53335349
auto &control{std::get<parser::AcImpliedDoControl>(x.t)};
53345350
auto &type{std::get<std::optional<parser::IntegerTypeSpec>>(control.t)};
53355351
auto &bounds{std::get<parser::AcImpliedDoControl::Bounds>(control.t)};
5352+
// F'2018 has the scope of the implied DO variable covering the entire
5353+
// implied DO production (19.4(5)), which seems wrong in cases where the name
5354+
// of the implied DO variable appears in one of the bound expressions. Thus
5355+
// this extension, which shrinks the scope of the variable to exclude the
5356+
// expressions in the bounds.
5357+
auto restore{BeginCheckOnIndexUseInOwnBounds(bounds.name)};
5358+
Walk(bounds.lower);
5359+
Walk(bounds.upper);
5360+
Walk(bounds.step);
5361+
EndCheckOnIndexUseInOwnBounds(restore);
53365362
PushScope(Scope::Kind::ImpliedDos, nullptr);
5337-
DeclareStatementEntity(bounds.name.thing.thing, type);
5338-
Walk(bounds);
5363+
DeclareStatementEntity(bounds.name, type);
53395364
Walk(values);
53405365
PopScope();
53415366
return false;
@@ -5345,9 +5370,21 @@ bool ConstructVisitor::Pre(const parser::DataImpliedDo &x) {
53455370
auto &objects{std::get<std::list<parser::DataIDoObject>>(x.t)};
53465371
auto &type{std::get<std::optional<parser::IntegerTypeSpec>>(x.t)};
53475372
auto &bounds{std::get<parser::DataImpliedDo::Bounds>(x.t)};
5348-
DeclareStatementEntity(bounds.name.thing.thing, type);
5349-
Walk(bounds);
5373+
// See comment in Pre(AcImpliedDo) above.
5374+
auto restore{BeginCheckOnIndexUseInOwnBounds(bounds.name)};
5375+
Walk(bounds.lower);
5376+
Walk(bounds.upper);
5377+
Walk(bounds.step);
5378+
EndCheckOnIndexUseInOwnBounds(restore);
5379+
bool pushScope{currScope().kind() != Scope::Kind::ImpliedDos};
5380+
if (pushScope) {
5381+
PushScope(Scope::Kind::ImpliedDos, nullptr);
5382+
}
5383+
DeclareStatementEntity(bounds.name, type);
53505384
Walk(objects);
5385+
if (pushScope) {
5386+
PopScope();
5387+
}
53515388
return false;
53525389
}
53535390

@@ -5886,13 +5923,24 @@ const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) {
58865923
ConvertToObjectEntity(*symbol);
58875924
ApplyImplicitRules(*symbol);
58885925
}
5926+
if (checkIndexUseInOwnBounds_ &&
5927+
*checkIndexUseInOwnBounds_ == name.source) {
5928+
Say(name,
5929+
"Implied DO index '%s' uses an object of the same name in its bounds expressions"_en_US,
5930+
name.source);
5931+
}
58895932
return &name;
58905933
}
58915934
if (isImplicitNoneType()) {
58925935
Say(name, "No explicit type declared for '%s'"_err_en_US);
58935936
return nullptr;
58945937
}
58955938
// Create the symbol then ensure it is accessible
5939+
if (checkIndexUseInOwnBounds_ && *checkIndexUseInOwnBounds_ == name.source) {
5940+
Say(name,
5941+
"Implied DO index '%s' uses itself in its own bounds expressions"_err_en_US,
5942+
name.source);
5943+
}
58965944
MakeSymbol(InclusiveScope(), name.source, Attrs{});
58975945
auto *symbol{FindSymbol(name)};
58985946
if (!symbol) {

flang/test/Semantics/array-constr-values.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ subroutine checkC7115()
5858
real, dimension(10), parameter :: good1 = [(99.9, i = 1, 10)]
5959
real, dimension(100), parameter :: good2 = [((88.8, i = 1, 10), j = 1, 10)]
6060
real, dimension(-1:0), parameter :: good3 = [77.7, 66.6]
61-
!ERROR: Implied DO index is active in surrounding implied DO loop and may not have the same name
61+
!ERROR: Implied DO index 'i' is active in a surrounding implied DO loop and may not have the same name
6262
real, dimension(100), parameter :: bad = [((88.8, i = 1, 10), i = 1, 10)]
6363

6464
!ERROR: Value of named constant 'bad2' ([INTEGER(4)::(int(j,kind=4),INTEGER(8)::j=1_8,1_8,0_8)]) cannot be computed as a constant value

flang/test/Semantics/data11.f90

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
! RUN: %flang_fc1 -fsyntax-only -fdebug-dump-symbols %s 2>&1 | FileCheck %s
2+
! CHECK: Implied DO index 'j' uses an object of the same name in its bounds expressions
3+
! CHECK: ObjectEntity type: REAL(4) shape: 1_8:5_8 init:[REAL(4)::1._4,2._4,3._4,4._4,5._4]
4+
! Verify that the scope of a DATA statement implied DO loop index does
5+
! not include the bounds expressions (language extension, with warning)
6+
integer, parameter :: j = 5
7+
real, save :: a(j)
8+
data (a(j),j=1,j)/1,2,3,4,5/
9+
end

flang/test/Semantics/modfile25.f90

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ end module m1
3939
! integer(8),parameter::a1ss(1_8:*)=[INTEGER(8)::3_8]
4040
! integer(8),parameter::a1sss(1_8:*)=[INTEGER(8)::1_8]
4141
! integer(8),parameter::a1rs(1_8:*)=[INTEGER(8)::3_8,1_8,1_8,1_8]
42+
! intrinsic::rank
4243
! integer(8),parameter::a1n(1_8:*)=[INTEGER(8)::125_8,5_8,5_8]
44+
! intrinsic::size
4345
! integer(8),parameter::a1sn(1_8:*)=[INTEGER(8)::3_8,1_8,1_8]
4446
! integer(8),parameter::ac1s(1_8:*)=[INTEGER(8)::1_8]
4547
! integer(8),parameter::ac2s(1_8:*)=[INTEGER(8)::3_8]

flang/test/Semantics/modfile26.f90

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,15 @@ end module m1
6666
!Expect: m1.mod
6767
!module m1
6868
!integer(4),parameter::iranges(1_8:*)=[INTEGER(4)::2_4,4_4,9_4,18_4,38_4]
69+
!intrinsic::range
6970
!logical(4),parameter::ircheck=.true._4
7071
!intrinsic::all
7172
!integer(4),parameter::intpvals(1_8:*)=[INTEGER(4)::0_4,2_4,3_4,4_4,5_4,9_4,10_4,18_4,19_4,38_4,39_4]
7273
!integer(4),parameter::intpkinds(1_8:*)=[INTEGER(4)::1_4,1_4,2_4,2_4,4_4,4_4,8_4,8_4,16_4,16_4,-1_4]
74+
!intrinsic::size
7375
!logical(4),parameter::ipcheck=.true._4
7476
!integer(4),parameter::realprecs(1_8:*)=[INTEGER(4)::3_4,2_4,6_4,15_4,18_4,33_4]
77+
!intrinsic::precision
7578
!logical(4),parameter::rpreccheck=.true._4
7679
!integer(4),parameter::realpvals(1_8:*)=[INTEGER(4)::0_4,3_4,4_4,6_4,7_4,15_4,16_4,18_4,19_4,33_4,34_4]
7780
!integer(4),parameter::realpkinds(1_8:*)=[INTEGER(4)::2_4,2_4,4_4,4_4,8_4,8_4,10_4,10_4,16_4,16_4,-1_4]
@@ -82,7 +85,9 @@ end module m1
8285
!integer(4),parameter::realrkinds(1_8:*)=[INTEGER(4)::2_4,2_4,3_4,3_4,8_4,8_4,10_4,10_4,-2_4]
8386
!logical(4),parameter::realrcheck=.true._4
8487
!logical(4),parameter::radixcheck=.true._4
88+
!intrinsic::radix
8589
!integer(4),parameter::intdigits(1_8:*)=[INTEGER(4)::7_4,15_4,31_4,63_4,127_4]
90+
!intrinsic::digits
8691
!logical(4),parameter::intdigitscheck=.true._4
8792
!integer(4),parameter::realdigits(1_8:*)=[INTEGER(4)::11_4,8_4,24_4,53_4,64_4,113_4]
8893
!logical(4),parameter::realdigitscheck=.true._4

flang/test/Semantics/resolve106.f90

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
!RUN: %flang -fsyntax-only %s 2>&1 | FileCheck %s
2+
integer, parameter :: j = 10
3+
! CHECK: Implied DO index 'j' uses an object of the same name in its bounds expressions
4+
real :: a(10) = [(j, j=1,j)]
5+
end

flang/test/Semantics/resolve30.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ subroutine s3
3131
end
3232

3333
subroutine s4
34-
real :: i, j
34+
real :: j
3535
!ERROR: Must have INTEGER type, but is REAL(4)
36-
real :: a(16) = [(i, i=1, 16)]
36+
real :: a(16) = [(x, x=1, 16)]
3737
real :: b(16)
3838
!ERROR: Must have INTEGER type, but is REAL(4)
3939
data(b(j), j=1, 16) / 16 * 0.0 /

flang/test/Semantics/symbol05.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ subroutine s3
4949
!DEF: /s3/Block1/t DerivedType
5050
type :: t
5151
!DEF: /s3/Block1/t/x ObjectEntity REAL(4)
52-
!DEF: /s3/Block1/t/ImpliedDos1/ImpliedDos1/i (Implicit) ObjectEntity INTEGER(4)
52+
!DEF: /s3/Block1/t/ImpliedDos1/i (Implicit) ObjectEntity INTEGER(4)
5353
real :: x(10) = [(i, i=1,10)]
5454
!DEF: /s3/Block1/t/y ObjectEntity REAL(4)
55-
!DEF: /s3/Block1/t/ImpliedDos2/ImpliedDos1/j ObjectEntity INTEGER(8)
55+
!DEF: /s3/Block1/t/ImpliedDos2/j ObjectEntity INTEGER(8)
5656
real :: y(10) = [(j, j=1,10)]
5757
end type
5858
end block

0 commit comments

Comments
 (0)