Skip to content

Commit 82ecbeb

Browse files
authored
[flang][semantics] add semantic check that STAT and ERRMSG are not (de)allocated by same statement (#164529)
Almost all compilers statically error on the following case even though it isn't a numbered constraint. Now we do to instead segfaulting at runtime. ```fortran integer,pointer:: i allocate(i,stat=i) end ```
1 parent 1b3e7df commit 82ecbeb

File tree

4 files changed

+154
-47
lines changed

4 files changed

+154
-47
lines changed

flang/lib/Semantics/check-allocate.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ struct AllocateCheckerInfo {
2626
std::optional<evaluate::DynamicType> sourceExprType;
2727
std::optional<parser::CharBlock> sourceExprLoc;
2828
std::optional<parser::CharBlock> typeSpecLoc;
29+
std::optional<parser::CharBlock> statSource;
30+
std::optional<parser::CharBlock> msgSource;
31+
const SomeExpr *statVar{nullptr};
32+
const SomeExpr *msgVar{nullptr};
2933
int sourceExprRank{0}; // only valid if gotMold || gotSource
3034
bool gotStat{false};
3135
bool gotMsg{false};
@@ -141,12 +145,15 @@ static std::optional<AllocateCheckerInfo> CheckAllocateOptions(
141145
[&](const parser::StatOrErrmsg &statOrErr) {
142146
common::visit(
143147
common::visitors{
144-
[&](const parser::StatVariable &) {
148+
[&](const parser::StatVariable &var) {
145149
if (info.gotStat) { // C943
146150
context.Say(
147151
"STAT may not be duplicated in a ALLOCATE statement"_err_en_US);
148152
}
149153
info.gotStat = true;
154+
info.statVar = GetExpr(context, var);
155+
info.statSource =
156+
parser::Unwrap<parser::Variable>(var)->GetSource();
150157
},
151158
[&](const parser::MsgVariable &var) {
152159
WarnOnDeferredLengthCharacterScalar(context,
@@ -159,6 +166,9 @@ static std::optional<AllocateCheckerInfo> CheckAllocateOptions(
159166
"ERRMSG may not be duplicated in a ALLOCATE statement"_err_en_US);
160167
}
161168
info.gotMsg = true;
169+
info.msgVar = GetExpr(context, var);
170+
info.msgSource =
171+
parser::Unwrap<parser::Variable>(var)->GetSource();
162172
},
163173
},
164174
statOrErr.u);
@@ -460,6 +470,16 @@ static bool HaveCompatibleLengths(
460470
}
461471
}
462472

473+
bool AreSameAllocation(const SomeExpr *root, const SomeExpr *path) {
474+
if (root && path) {
475+
// For now we just use equality of expressions. If we implement a more
476+
// sophisticated alias analysis we should use it here.
477+
return *root == *path;
478+
} else {
479+
return false;
480+
}
481+
}
482+
463483
bool AllocationCheckerHelper::RunChecks(SemanticsContext &context) {
464484
if (!ultimate_) {
465485
CHECK(context.AnyFatalError());
@@ -690,6 +710,17 @@ bool AllocationCheckerHelper::RunChecks(SemanticsContext &context) {
690710
"Object in ALLOCATE must have DEVICE attribute when STREAM option is specified"_err_en_US);
691711
}
692712
}
713+
714+
if (const SomeExpr *allocObj{GetExpr(context, allocateObject_)}) {
715+
if (AreSameAllocation(allocObj, allocateInfo_.statVar)) {
716+
context.Say(allocateInfo_.statSource.value_or(name_.source),
717+
"STAT variable in ALLOCATE must not be the variable being allocated"_err_en_US);
718+
}
719+
if (AreSameAllocation(allocObj, allocateInfo_.msgVar)) {
720+
context.Say(allocateInfo_.msgSource.value_or(name_.source),
721+
"ERRMSG variable in ALLOCATE must not be the variable being allocated"_err_en_US);
722+
}
723+
}
693724
return RunCoarrayRelatedChecks(context);
694725
}
695726

flang/lib/Semantics/check-allocate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ class AllocateChecker : public virtual BaseChecker {
2424
private:
2525
SemanticsContext &context_;
2626
};
27+
bool AreSameAllocation(const SomeExpr *root, const SomeExpr *path);
2728
} // namespace Fortran::semantics
2829
#endif // FORTRAN_SEMANTICS_CHECK_ALLOCATE_H_

flang/lib/Semantics/check-deallocate.cpp

Lines changed: 65 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,87 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "check-deallocate.h"
10+
#include "check-allocate.h"
1011
#include "definable.h"
1112
#include "flang/Evaluate/type.h"
1213
#include "flang/Parser/message.h"
1314
#include "flang/Parser/parse-tree.h"
1415
#include "flang/Semantics/expression.h"
1516
#include "flang/Semantics/tools.h"
17+
#include <optional>
1618

1719
namespace Fortran::semantics {
1820

1921
void DeallocateChecker::Leave(const parser::DeallocateStmt &deallocateStmt) {
22+
bool gotStat{false}, gotMsg{false};
23+
const SomeExpr *statVar{nullptr}, *msgVar{nullptr};
24+
std::optional<parser::CharBlock> statSource;
25+
std::optional<parser::CharBlock> msgSource;
26+
for (const parser::StatOrErrmsg &deallocOpt :
27+
std::get<std::list<parser::StatOrErrmsg>>(deallocateStmt.t)) {
28+
common::visit(
29+
common::visitors{
30+
[&](const parser::StatVariable &var) {
31+
if (gotStat) {
32+
context_.Say(
33+
"STAT may not be duplicated in a DEALLOCATE statement"_err_en_US);
34+
}
35+
gotStat = true;
36+
statVar = GetExpr(context_, var);
37+
statSource = parser::Unwrap<parser::Variable>(var)->GetSource();
38+
},
39+
[&](const parser::MsgVariable &var) {
40+
WarnOnDeferredLengthCharacterScalar(context_,
41+
GetExpr(context_, var),
42+
parser::UnwrapRef<parser::Variable>(var).GetSource(),
43+
"ERRMSG=");
44+
if (gotMsg) {
45+
context_.Say(
46+
"ERRMSG may not be duplicated in a DEALLOCATE statement"_err_en_US);
47+
}
48+
gotMsg = true;
49+
msgVar = GetExpr(context_, var);
50+
msgSource = parser::Unwrap<parser::Variable>(var)->GetSource();
51+
},
52+
},
53+
deallocOpt.u);
54+
}
2055
for (const parser::AllocateObject &allocateObject :
2156
std::get<std::list<parser::AllocateObject>>(deallocateStmt.t)) {
57+
parser::CharBlock source;
2258
common::visit(
2359
common::visitors{
2460
[&](const parser::Name &name) {
2561
const Symbol *symbol{
2662
name.symbol ? &name.symbol->GetUltimate() : nullptr};
27-
;
63+
source = name.source;
2864
if (context_.HasError(symbol)) {
2965
// already reported an error
3066
} else if (!IsVariableName(*symbol)) {
31-
context_.Say(name.source,
67+
context_.Say(source,
3268
"Name in DEALLOCATE statement must be a variable name"_err_en_US);
3369
} else if (!IsAllocatableOrObjectPointer(symbol)) { // C936
34-
context_.Say(name.source,
70+
context_.Say(source,
3571
"Name in DEALLOCATE statement must have the ALLOCATABLE or POINTER attribute"_err_en_US);
36-
} else if (auto whyNot{WhyNotDefinable(name.source,
37-
context_.FindScope(name.source),
38-
{DefinabilityFlag::PointerDefinition,
39-
DefinabilityFlag::AcceptAllocatable,
40-
DefinabilityFlag::PotentialDeallocation},
41-
*symbol)}) {
72+
} else if (auto whyNot{
73+
WhyNotDefinable(source, context_.FindScope(source),
74+
{DefinabilityFlag::PointerDefinition,
75+
DefinabilityFlag::AcceptAllocatable,
76+
DefinabilityFlag::PotentialDeallocation},
77+
*symbol)}) {
4278
// Catch problems with non-definability of the
4379
// pointer/allocatable
4480
context_
45-
.Say(name.source,
81+
.Say(source,
4682
"Name in DEALLOCATE statement is not definable"_err_en_US)
4783
.Attach(std::move(
4884
whyNot->set_severity(parser::Severity::Because)));
49-
} else if (auto whyNot{WhyNotDefinable(name.source,
50-
context_.FindScope(name.source),
51-
DefinabilityFlags{}, *symbol)}) {
85+
} else if (auto whyNot{
86+
WhyNotDefinable(source, context_.FindScope(source),
87+
DefinabilityFlags{}, *symbol)}) {
5288
// Catch problems with non-definability of the dynamic object
5389
context_
54-
.Say(name.source,
90+
.Say(source,
5591
"Object in DEALLOCATE statement is not deallocatable"_err_en_US)
5692
.Attach(std::move(
5793
whyNot->set_severity(parser::Severity::Because)));
@@ -62,13 +98,12 @@ void DeallocateChecker::Leave(const parser::DeallocateStmt &deallocateStmt) {
6298
[&](const parser::StructureComponent &structureComponent) {
6399
// Only perform structureComponent checks if it was successfully
64100
// analyzed by expression analysis.
65-
auto source{structureComponent.component.source};
101+
source = structureComponent.component.source;
66102
if (const auto *expr{GetExpr(context_, allocateObject)}) {
67-
if (const Symbol *
68-
symbol{structureComponent.component.symbol
69-
? &structureComponent.component.symbol
70-
->GetUltimate()
71-
: nullptr};
103+
if (const Symbol *symbol{structureComponent.component.symbol
104+
? &structureComponent.component.symbol
105+
->GetUltimate()
106+
: nullptr};
72107
!IsAllocatableOrObjectPointer(symbol)) { // F'2023 C936
73108
context_.Say(source,
74109
"Component in DEALLOCATE statement must have the ALLOCATABLE or POINTER attribute"_err_en_US);
@@ -99,32 +134,16 @@ void DeallocateChecker::Leave(const parser::DeallocateStmt &deallocateStmt) {
99134
},
100135
},
101136
allocateObject.u);
102-
}
103-
bool gotStat{false}, gotMsg{false};
104-
for (const parser::StatOrErrmsg &deallocOpt :
105-
std::get<std::list<parser::StatOrErrmsg>>(deallocateStmt.t)) {
106-
common::visit(
107-
common::visitors{
108-
[&](const parser::StatVariable &) {
109-
if (gotStat) {
110-
context_.Say(
111-
"STAT may not be duplicated in a DEALLOCATE statement"_err_en_US);
112-
}
113-
gotStat = true;
114-
},
115-
[&](const parser::MsgVariable &var) {
116-
WarnOnDeferredLengthCharacterScalar(context_,
117-
GetExpr(context_, var),
118-
parser::UnwrapRef<parser::Variable>(var).GetSource(),
119-
"ERRMSG=");
120-
if (gotMsg) {
121-
context_.Say(
122-
"ERRMSG may not be duplicated in a DEALLOCATE statement"_err_en_US);
123-
}
124-
gotMsg = true;
125-
},
126-
},
127-
deallocOpt.u);
137+
if (const SomeExpr *allocObj{GetExpr(context_, allocateObject)}) {
138+
if (AreSameAllocation(allocObj, statVar)) {
139+
context_.Say(statSource.value_or(source),
140+
"STAT variable in DEALLOCATE must not be the variable being deallocated"_err_en_US);
141+
}
142+
if (AreSameAllocation(allocObj, msgVar)) {
143+
context_.Say(msgSource.value_or(source),
144+
"ERRMSG variable in DEALLOCATE must not be the variable being deallocated"_err_en_US);
145+
}
146+
}
128147
}
129148
}
130149

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
2+
! Check for semantic errors in ALLOCATE statements
3+
4+
program allocate14
5+
6+
integer, allocatable :: i1, i2
7+
character(200), allocatable :: msg1, msg2
8+
type t
9+
integer, allocatable :: i
10+
character(10), allocatable :: msg
11+
end type t
12+
type(t) :: tt(2)
13+
type(t), allocatable :: ts(:)
14+
15+
allocate(i1)
16+
allocate(msg1)
17+
18+
allocate(i2, stat=i1, errmsg=msg1)
19+
allocate(msg2, stat=i1, errmsg=msg1)
20+
deallocate(i2, stat=i1, errmsg=msg1)
21+
deallocate(msg2, stat=i1, errmsg=msg1)
22+
23+
!ERROR: STAT variable in ALLOCATE must not be the variable being allocated
24+
allocate(i2, stat=i2, errmsg=msg2)
25+
!ERROR: ERRMSG variable in ALLOCATE must not be the variable being allocated
26+
allocate(msg2, stat=i2, errmsg=msg2)
27+
!ERROR: STAT variable in DEALLOCATE must not be the variable being deallocated
28+
deallocate(i2, stat=i2, errmsg=msg2)
29+
!ERROR: ERRMSG variable in DEALLOCATE must not be the variable being deallocated
30+
deallocate(msg2, stat=i2, errmsg=msg2)
31+
32+
allocate(tt(1)%i)
33+
allocate(tt(1)%msg)
34+
35+
allocate(tt(2)%i, stat=tt(1)%i, errmsg=tt(1)%msg)
36+
allocate(tt(2)%msg, stat=tt(1)%i, errmsg=tt(1)%msg)
37+
deallocate(tt(2)%i, stat=tt(1)%i, errmsg=tt(1)%msg)
38+
deallocate(tt(2)%msg, stat=tt(1)%i, errmsg=tt(1)%msg)
39+
40+
!ERROR: STAT variable in ALLOCATE must not be the variable being allocated
41+
allocate(tt(2)%i, stat=tt(2)%i, errmsg=tt(2)%msg)
42+
!ERROR: ERRMSG variable in ALLOCATE must not be the variable being allocated
43+
allocate(tt(2)%msg, stat=tt(2)%i, errmsg=tt(2)%msg)
44+
!ERROR: STAT variable in DEALLOCATE must not be the variable being deallocated
45+
deallocate(tt(2)%i, stat=tt(2)%i, errmsg=tt(2)%msg)
46+
!ERROR: ERRMSG variable in DEALLOCATE must not be the variable being deallocated
47+
deallocate(tt(2)%msg, stat=tt(2)%i, errmsg=tt(2)%msg)
48+
49+
!TODO: STAT variable in ALLOCATE must not be the variable being allocated
50+
!TODO: ERRMSG variable in ALLOCATE must not be the variable being allocated
51+
allocate(ts(10), stat=ts(1)%i, errmsg=ts(1)%msg)
52+
!TODO: STAT variable in DEALLOCATE must not be the variable being deallocated
53+
!TODO: ERRMSG variable in DEALLOCATE must not be the variable being deallocated
54+
deallocate(ts, stat=ts(1)%i, errmsg=ts(1)%msg)
55+
end program
56+

0 commit comments

Comments
 (0)