Skip to content

Commit 34a4eef

Browse files
authored
[flang] Warn about impure calls in concurrent headers (llvm#108436)
Emit a warning when an impure function is referenced from a DO CONCURRENT or FORALL concurrent-header that is not nested within another such construct. (That nested case is already an error.)
1 parent ddd1a02 commit 34a4eef

File tree

4 files changed

+82
-54
lines changed

4 files changed

+82
-54
lines changed

flang/lib/Semantics/check-do-forall.cpp

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ class DoConcurrentBodyEnforce {
122122
}
123123
return true;
124124
}
125+
bool Pre(const parser::ConcurrentHeader &) {
126+
// handled in CheckConcurrentHeader
127+
return false;
128+
}
125129
template <typename T> void Post(const T &) {}
126130

127131
// C1140 -- Can't deallocate a polymorphic entity in a DO CONCURRENT.
@@ -375,8 +379,13 @@ class DoConcurrentVariableEnforce {
375379
// Find a DO or FORALL and enforce semantics checks on its body
376380
class DoContext {
377381
public:
378-
DoContext(SemanticsContext &context, IndexVarKind kind, bool isNested)
379-
: context_{context}, kind_{kind}, isNested_{isNested} {}
382+
DoContext(SemanticsContext &context, IndexVarKind kind,
383+
const std::list<IndexVarKind> nesting)
384+
: context_{context}, kind_{kind} {
385+
if (!nesting.empty()) {
386+
concurrentNesting_ = nesting.back();
387+
}
388+
}
380389

381390
// Mark this DO construct as a point of definition for the DO variables
382391
// or index-names it contains. If they're already defined, emit an error
@@ -439,8 +448,8 @@ class DoContext {
439448
common::visitors{[&](const auto &x) { return GetAssignment(x); }},
440449
stmt.u)}) {
441450
CheckForallIndexesUsed(*assignment);
442-
CheckForImpureCall(assignment->lhs);
443-
CheckForImpureCall(assignment->rhs);
451+
CheckForImpureCall(assignment->lhs, kind_);
452+
CheckForImpureCall(assignment->rhs, kind_);
444453

445454
if (IsVariable(assignment->lhs)) {
446455
if (const Symbol * symbol{GetLastSymbol(assignment->lhs)}) {
@@ -455,23 +464,23 @@ class DoContext {
455464

456465
if (const auto *proc{
457466
std::get_if<evaluate::ProcedureRef>(&assignment->u)}) {
458-
CheckForImpureCall(*proc);
467+
CheckForImpureCall(*proc, kind_);
459468
}
460469
common::visit(
461470
common::visitors{
462471
[](const evaluate::Assignment::Intrinsic &) {},
463472
[&](const evaluate::ProcedureRef &proc) {
464-
CheckForImpureCall(proc);
473+
CheckForImpureCall(proc, kind_);
465474
},
466475
[&](const evaluate::Assignment::BoundsSpec &bounds) {
467476
for (const auto &bound : bounds) {
468-
CheckForImpureCall(SomeExpr{bound});
477+
CheckForImpureCall(SomeExpr{bound}, kind_);
469478
}
470479
},
471480
[&](const evaluate::Assignment::BoundsRemapping &bounds) {
472481
for (const auto &bound : bounds) {
473-
CheckForImpureCall(SomeExpr{bound.first});
474-
CheckForImpureCall(SomeExpr{bound.second});
482+
CheckForImpureCall(SomeExpr{bound.first}, kind_);
483+
CheckForImpureCall(SomeExpr{bound.second}, kind_);
475484
}
476485
},
477486
},
@@ -754,12 +763,10 @@ class DoContext {
754763
if (indexName.symbol) {
755764
indexNames.insert(*indexName.symbol);
756765
}
757-
if (isNested_) {
758-
CheckForImpureCall(std::get<1>(control.t));
759-
CheckForImpureCall(std::get<2>(control.t));
760-
if (const auto &stride{std::get<3>(control.t)}) {
761-
CheckForImpureCall(*stride);
762-
}
766+
CheckForImpureCall(std::get<1>(control.t), concurrentNesting_);
767+
CheckForImpureCall(std::get<2>(control.t), concurrentNesting_);
768+
if (const auto &stride{std::get<3>(control.t)}) {
769+
CheckForImpureCall(*stride, concurrentNesting_);
763770
}
764771
}
765772
if (!indexNames.empty()) {
@@ -819,20 +826,29 @@ class DoContext {
819826
CheckConcurrentHeader(std::get<parser::ConcurrentHeader>(concurrent.t));
820827
}
821828

822-
template <typename T> void CheckForImpureCall(const T &x) const {
829+
template <typename T>
830+
void CheckForImpureCall(
831+
const T &x, std::optional<IndexVarKind> nesting) const {
823832
if (auto bad{FindImpureCall(context_.foldingContext(), x)}) {
824-
context_.Say(
825-
"Impure procedure '%s' may not be referenced in a %s"_err_en_US, *bad,
826-
LoopKindName());
833+
if (nesting) {
834+
context_.Say(
835+
"Impure procedure '%s' may not be referenced in a %s"_err_en_US,
836+
*bad, LoopKindName(*nesting));
837+
} else {
838+
context_.Say(
839+
"Impure procedure '%s' should not be referenced in a %s header"_warn_en_US,
840+
*bad, LoopKindName(kind_));
841+
}
827842
}
828843
}
829-
void CheckForImpureCall(const parser::ScalarIntExpr &x) const {
844+
void CheckForImpureCall(const parser::ScalarIntExpr &x,
845+
std::optional<IndexVarKind> nesting) const {
830846
const auto &parsedExpr{x.thing.thing.value()};
831847
auto oldLocation{context_.location()};
832848
context_.set_location(parsedExpr.source);
833849
if (const auto &typedExpr{parsedExpr.typedExpr}) {
834850
if (const auto &expr{typedExpr->v}) {
835-
CheckForImpureCall(*expr);
851+
CheckForImpureCall(*expr, nesting);
836852
}
837853
}
838854
context_.set_location(oldLocation);
@@ -885,54 +901,59 @@ class DoContext {
885901
}
886902

887903
// For messages where the DO loop must be DO CONCURRENT, make that explicit.
888-
const char *LoopKindName() const {
889-
return kind_ == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL";
904+
const char *LoopKindName(IndexVarKind kind) const {
905+
return kind == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL";
890906
}
907+
const char *LoopKindName() const { return LoopKindName(kind_); }
891908

892909
SemanticsContext &context_;
893910
const IndexVarKind kind_;
894911
parser::CharBlock currentStatementSourcePosition_;
895-
bool isNested_{false};
912+
std::optional<IndexVarKind> concurrentNesting_;
896913
}; // class DoContext
897914

898915
void DoForallChecker::Enter(const parser::DoConstruct &doConstruct) {
899-
DoContext doContext{context_, IndexVarKind::DO, constructNesting_ > 0};
916+
DoContext doContext{context_, IndexVarKind::DO, nestedWithinConcurrent_};
917+
if (doConstruct.IsDoConcurrent()) {
918+
nestedWithinConcurrent_.push_back(IndexVarKind::DO);
919+
}
900920
doContext.DefineDoVariables(doConstruct);
921+
doContext.Check(doConstruct);
901922
}
902923

903924
void DoForallChecker::Leave(const parser::DoConstruct &doConstruct) {
904-
DoContext doContext{context_, IndexVarKind::DO, constructNesting_ > 0};
905-
++constructNesting_;
906-
doContext.Check(doConstruct);
925+
DoContext doContext{context_, IndexVarKind::DO, nestedWithinConcurrent_};
907926
doContext.ResetDoVariables(doConstruct);
908-
--constructNesting_;
927+
if (doConstruct.IsDoConcurrent()) {
928+
nestedWithinConcurrent_.pop_back();
929+
}
909930
}
910931

911932
void DoForallChecker::Enter(const parser::ForallConstruct &construct) {
912-
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
933+
DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
913934
doContext.ActivateIndexVars(GetControls(construct));
914-
++constructNesting_;
935+
nestedWithinConcurrent_.push_back(IndexVarKind::FORALL);
915936
doContext.Check(construct);
916937
}
917938
void DoForallChecker::Leave(const parser::ForallConstruct &construct) {
918-
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
939+
DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
919940
doContext.DeactivateIndexVars(GetControls(construct));
920-
--constructNesting_;
941+
nestedWithinConcurrent_.pop_back();
921942
}
922943

923944
void DoForallChecker::Enter(const parser::ForallStmt &stmt) {
924-
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
925-
++constructNesting_;
945+
DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
946+
nestedWithinConcurrent_.push_back(IndexVarKind::FORALL);
926947
doContext.Check(stmt);
927948
doContext.ActivateIndexVars(GetControls(stmt));
928949
}
929950
void DoForallChecker::Leave(const parser::ForallStmt &stmt) {
930-
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
951+
DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
931952
doContext.DeactivateIndexVars(GetControls(stmt));
932-
--constructNesting_;
953+
nestedWithinConcurrent_.pop_back();
933954
}
934955
void DoForallChecker::Leave(const parser::ForallAssignmentStmt &stmt) {
935-
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
956+
DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
936957
doContext.Check(stmt);
937958
}
938959

flang/lib/Semantics/check-do-forall.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class DoForallChecker : public virtual BaseChecker {
6060
private:
6161
SemanticsContext &context_;
6262
int exprDepth_{0};
63-
int constructNesting_{0};
63+
std::list<SemanticsContext::IndexVarKind> nestedWithinConcurrent_;
6464

6565
void SayBadLeave(
6666
StmtType, const char *enclosingStmt, const ConstructNode &) const;

flang/test/Semantics/OpenMP/workshare02.f90

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ subroutine workshare(aa, bb, cc, dd, ee, ff, n)
4040
cc = ee + my_func()
4141
end where
4242

43+
!WARNING: Impure procedure 'my_func' should not be referenced in a FORALL header
4344
!ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct
4445
forall (j = 1:my_func()) aa(j) = aa(j) + bb(j)
4546

flang/test/Semantics/call11.f90

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,30 +42,36 @@ subroutine test
4242
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
4343
a(j) = impure(j) ! C1139
4444
end do
45-
do concurrent (k=impure(1):1); end do ! ok
46-
do concurrent (k=1:impure(1)); end do ! ok
47-
do concurrent (k=1:1:impure(1)); end do ! ok
48-
forall (k=impure(1):1); end forall ! ok
49-
forall (k=1:impure(1)); end forall ! ok
50-
forall (k=1:1:impure(1)); end forall ! ok
45+
!WARNING: Impure procedure 'impure' should not be referenced in a DO CONCURRENT header
46+
do concurrent (k=impure(1):1); end do
47+
!WARNING: Impure procedure 'impure' should not be referenced in a DO CONCURRENT header
48+
do concurrent (k=1:impure(1)); end do
49+
!WARNING: Impure procedure 'impure' should not be referenced in a DO CONCURRENT header
50+
do concurrent (k=1:1:impure(1)); end do
51+
!WARNING: Impure procedure 'impure' should not be referenced in a FORALL header
52+
forall (k=impure(1):1); end forall
53+
!WARNING: Impure procedure 'impure' should not be referenced in a FORALL header
54+
forall (k=1:impure(1)); end forall
55+
!WARNING: Impure procedure 'impure' should not be referenced in a FORALL header
56+
forall (k=1:1:impure(1)); end forall
5157
do concurrent (j=1:1)
52-
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
58+
!ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
5359
do concurrent (k=impure(1):1); end do
54-
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
60+
!ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
5561
do concurrent (k=1:impure(1)); end do
56-
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
62+
!ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
5763
do concurrent (k=1:1:impure(1)); end do
58-
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
64+
!ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
5965
forall (k=impure(1):1); end forall
60-
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
66+
!ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
6167
forall (k=1:impure(1)); end forall
62-
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
68+
!ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
6369
forall (k=1:1:impure(1)); end forall
64-
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
70+
!ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
6571
forall (k=impure(1):1) a(k) = 0.
66-
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
72+
!ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
6773
forall (k=1:impure(1)) a(k) = 0.
68-
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
74+
!ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
6975
forall (k=1:1:impure(1)) a(k) = 0.
7076
end do
7177
forall (j=1:1)

0 commit comments

Comments
 (0)