Skip to content

Commit 9390eb9

Browse files
authored
[flang] Catch impure calls in nested concurrent-headers (#102075)
The start, end, and stride expressions of a concurrent-header in a DO CONCURRENT or FORALL statement can contain calls to impure functions... unless they appear in a statement that's nested in an enclosing DO CONCURRENT or FORALL construct. Ensure that we catch this nested case.
1 parent e83c5b2 commit 9390eb9

File tree

3 files changed

+81
-15
lines changed

3 files changed

+81
-15
lines changed

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

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,7 @@ class DoConcurrentBodyEnforce {
269269
const parser::CharBlock statementLocation{
270270
GetImageControlStmtLocation(construct)};
271271
auto &msg{context_.Say(statementLocation,
272-
"An image control statement is not allowed in DO"
273-
" CONCURRENT"_err_en_US)};
272+
"An image control statement is not allowed in DO CONCURRENT"_err_en_US)};
274273
if (auto coarrayMsg{GetImageControlStmtCoarrayMsg(construct)}) {
275274
msg.Attach(statementLocation, *coarrayMsg);
276275
}
@@ -372,8 +371,8 @@ class DoConcurrentVariableEnforce {
372371
// Find a DO or FORALL and enforce semantics checks on its body
373372
class DoContext {
374373
public:
375-
DoContext(SemanticsContext &context, IndexVarKind kind)
376-
: context_{context}, kind_{kind} {}
374+
DoContext(SemanticsContext &context, IndexVarKind kind, bool isNested)
375+
: context_{context}, kind_{kind}, isNested_{isNested} {}
377376

378377
// Mark this DO construct as a point of definition for the DO variables
379378
// or index-names it contains. If they're already defined, emit an error
@@ -743,13 +742,21 @@ class DoContext {
743742
std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) {
744743
CheckMaskIsPure(*mask);
745744
}
746-
auto &controls{std::get<std::list<parser::ConcurrentControl>>(header.t)};
745+
const auto &controls{
746+
std::get<std::list<parser::ConcurrentControl>>(header.t)};
747747
UnorderedSymbolSet indexNames;
748748
for (const parser::ConcurrentControl &control : controls) {
749749
const auto &indexName{std::get<parser::Name>(control.t)};
750750
if (indexName.symbol) {
751751
indexNames.insert(*indexName.symbol);
752752
}
753+
if (isNested_) {
754+
CheckForImpureCall(std::get<1>(control.t));
755+
CheckForImpureCall(std::get<2>(control.t));
756+
if (const auto &stride{std::get<3>(control.t)}) {
757+
CheckForImpureCall(*stride);
758+
}
759+
}
753760
}
754761
if (!indexNames.empty()) {
755762
for (const parser::ConcurrentControl &control : controls) {
@@ -808,13 +815,24 @@ class DoContext {
808815
CheckConcurrentHeader(std::get<parser::ConcurrentHeader>(concurrent.t));
809816
}
810817

811-
template <typename T> void CheckForImpureCall(const T &x) {
818+
template <typename T> void CheckForImpureCall(const T &x) const {
812819
if (auto bad{FindImpureCall(context_.foldingContext(), x)}) {
813820
context_.Say(
814821
"Impure procedure '%s' may not be referenced in a %s"_err_en_US, *bad,
815822
LoopKindName());
816823
}
817824
}
825+
void CheckForImpureCall(const parser::ScalarIntExpr &x) const {
826+
const auto &parsedExpr{x.thing.thing.value()};
827+
auto oldLocation{context_.location()};
828+
context_.set_location(parsedExpr.source);
829+
if (const auto &typedExpr{parsedExpr.typedExpr}) {
830+
if (const auto &expr{typedExpr->v}) {
831+
CheckForImpureCall(*expr);
832+
}
833+
}
834+
context_.set_location(oldLocation);
835+
}
818836

819837
// Each index should be used on the LHS of each assignment in a FORALL
820838
void CheckForallIndexesUsed(const evaluate::Assignment &assignment) {
@@ -870,40 +888,47 @@ class DoContext {
870888
SemanticsContext &context_;
871889
const IndexVarKind kind_;
872890
parser::CharBlock currentStatementSourcePosition_;
891+
bool isNested_{false};
873892
}; // class DoContext
874893

875894
void DoForallChecker::Enter(const parser::DoConstruct &doConstruct) {
876-
DoContext doContext{context_, IndexVarKind::DO};
895+
DoContext doContext{context_, IndexVarKind::DO, constructNesting_ > 0};
877896
doContext.DefineDoVariables(doConstruct);
878897
}
879898

880899
void DoForallChecker::Leave(const parser::DoConstruct &doConstruct) {
881-
DoContext doContext{context_, IndexVarKind::DO};
900+
DoContext doContext{context_, IndexVarKind::DO, constructNesting_ > 0};
901+
++constructNesting_;
882902
doContext.Check(doConstruct);
883903
doContext.ResetDoVariables(doConstruct);
904+
--constructNesting_;
884905
}
885906

886907
void DoForallChecker::Enter(const parser::ForallConstruct &construct) {
887-
DoContext doContext{context_, IndexVarKind::FORALL};
908+
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
888909
doContext.ActivateIndexVars(GetControls(construct));
910+
++constructNesting_;
911+
doContext.Check(construct);
889912
}
890913
void DoForallChecker::Leave(const parser::ForallConstruct &construct) {
891-
DoContext doContext{context_, IndexVarKind::FORALL};
892-
doContext.Check(construct);
914+
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
893915
doContext.DeactivateIndexVars(GetControls(construct));
916+
--constructNesting_;
894917
}
895918

896919
void DoForallChecker::Enter(const parser::ForallStmt &stmt) {
897-
DoContext doContext{context_, IndexVarKind::FORALL};
920+
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
921+
++constructNesting_;
922+
doContext.Check(stmt);
898923
doContext.ActivateIndexVars(GetControls(stmt));
899924
}
900925
void DoForallChecker::Leave(const parser::ForallStmt &stmt) {
901-
DoContext doContext{context_, IndexVarKind::FORALL};
902-
doContext.Check(stmt);
926+
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
903927
doContext.DeactivateIndexVars(GetControls(stmt));
928+
--constructNesting_;
904929
}
905930
void DoForallChecker::Leave(const parser::ForallAssignmentStmt &stmt) {
906-
DoContext doContext{context_, IndexVarKind::FORALL};
931+
DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
907932
doContext.Check(stmt);
908933
}
909934

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class DoForallChecker : public virtual BaseChecker {
6060
private:
6161
SemanticsContext &context_;
6262
int exprDepth_{0};
63+
int constructNesting_{0};
6364

6465
void SayBadLeave(
6566
StmtType, const char *enclosingStmt, const ConstructNode &) const;

flang/test/Semantics/call11.f90

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,46 @@ 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
51+
do concurrent (j=1:1)
52+
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
53+
do concurrent (k=impure(1):1); end do
54+
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
55+
do concurrent (k=1:impure(1)); end do
56+
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
57+
do concurrent (k=1:1:impure(1)); end do
58+
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
59+
forall (k=impure(1):1); end forall
60+
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
61+
forall (k=1:impure(1)); end forall
62+
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
63+
forall (k=1:1:impure(1)); end forall
64+
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
65+
forall (k=impure(1):1) a(k) = 0.
66+
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
67+
forall (k=1:impure(1)) a(k) = 0.
68+
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
69+
forall (k=1:1:impure(1)) a(k) = 0.
70+
end do
71+
forall (j=1:1)
72+
!ERROR: Impure procedure 'impure' may not be referenced in a FORALL
73+
forall (k=impure(1):1); end forall
74+
!ERROR: Impure procedure 'impure' may not be referenced in a FORALL
75+
forall (k=1:impure(1)); end forall
76+
!ERROR: Impure procedure 'impure' may not be referenced in a FORALL
77+
forall (k=1:1:impure(1)); end forall
78+
!ERROR: Impure procedure 'impure' may not be referenced in a FORALL
79+
forall (k=impure(1):1) a(j*k) = 0.
80+
!ERROR: Impure procedure 'impure' may not be referenced in a FORALL
81+
forall (k=1:impure(1)) a(j*k) = 0.
82+
!ERROR: Impure procedure 'impure' may not be referenced in a FORALL
83+
forall (k=1:1:impure(1)) a(j*k) = 0.
84+
end forall
4585
end subroutine
4686

4787
subroutine test2

0 commit comments

Comments
 (0)