[flang-commits] [flang] [flang] Warn about impure calls in concurrent headers (PR #108436)
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Thu Sep 12 11:49:52 PDT 2024
https://github.com/klausler created https://github.com/llvm/llvm-project/pull/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.)
>From 55dcfcfa5c037b7ee27393322eab986a8baafe29 Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Thu, 12 Sep 2024 11:47:00 -0700
Subject: [PATCH] [flang] Warn about impure calls in concurrent headers
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.)
---
flang/lib/Semantics/check-do-forall.cpp | 97 +++++++++++++--------
flang/lib/Semantics/check-do-forall.h | 2 +-
flang/test/Semantics/OpenMP/workshare02.f90 | 1 +
flang/test/Semantics/call11.f90 | 36 ++++----
4 files changed, 82 insertions(+), 54 deletions(-)
diff --git a/flang/lib/Semantics/check-do-forall.cpp b/flang/lib/Semantics/check-do-forall.cpp
index d798244ff1ef2d..b9778e96080a62 100644
--- a/flang/lib/Semantics/check-do-forall.cpp
+++ b/flang/lib/Semantics/check-do-forall.cpp
@@ -122,6 +122,10 @@ class DoConcurrentBodyEnforce {
}
return true;
}
+ bool Pre(const parser::ConcurrentHeader &) {
+ // handled in CheckConcurrentHeader
+ return false;
+ }
template <typename T> void Post(const T &) {}
// C1140 -- Can't deallocate a polymorphic entity in a DO CONCURRENT.
@@ -375,8 +379,13 @@ class DoConcurrentVariableEnforce {
// Find a DO or FORALL and enforce semantics checks on its body
class DoContext {
public:
- DoContext(SemanticsContext &context, IndexVarKind kind, bool isNested)
- : context_{context}, kind_{kind}, isNested_{isNested} {}
+ DoContext(SemanticsContext &context, IndexVarKind kind,
+ const std::list<IndexVarKind> nesting)
+ : context_{context}, kind_{kind} {
+ if (!nesting.empty()) {
+ concurrentNesting_ = nesting.back();
+ }
+ }
// Mark this DO construct as a point of definition for the DO variables
// or index-names it contains. If they're already defined, emit an error
@@ -439,8 +448,8 @@ class DoContext {
common::visitors{[&](const auto &x) { return GetAssignment(x); }},
stmt.u)}) {
CheckForallIndexesUsed(*assignment);
- CheckForImpureCall(assignment->lhs);
- CheckForImpureCall(assignment->rhs);
+ CheckForImpureCall(assignment->lhs, kind_);
+ CheckForImpureCall(assignment->rhs, kind_);
if (IsVariable(assignment->lhs)) {
if (const Symbol * symbol{GetLastSymbol(assignment->lhs)}) {
@@ -455,23 +464,23 @@ class DoContext {
if (const auto *proc{
std::get_if<evaluate::ProcedureRef>(&assignment->u)}) {
- CheckForImpureCall(*proc);
+ CheckForImpureCall(*proc, kind_);
}
common::visit(
common::visitors{
[](const evaluate::Assignment::Intrinsic &) {},
[&](const evaluate::ProcedureRef &proc) {
- CheckForImpureCall(proc);
+ CheckForImpureCall(proc, kind_);
},
[&](const evaluate::Assignment::BoundsSpec &bounds) {
for (const auto &bound : bounds) {
- CheckForImpureCall(SomeExpr{bound});
+ CheckForImpureCall(SomeExpr{bound}, kind_);
}
},
[&](const evaluate::Assignment::BoundsRemapping &bounds) {
for (const auto &bound : bounds) {
- CheckForImpureCall(SomeExpr{bound.first});
- CheckForImpureCall(SomeExpr{bound.second});
+ CheckForImpureCall(SomeExpr{bound.first}, kind_);
+ CheckForImpureCall(SomeExpr{bound.second}, kind_);
}
},
},
@@ -754,12 +763,10 @@ class DoContext {
if (indexName.symbol) {
indexNames.insert(*indexName.symbol);
}
- if (isNested_) {
- CheckForImpureCall(std::get<1>(control.t));
- CheckForImpureCall(std::get<2>(control.t));
- if (const auto &stride{std::get<3>(control.t)}) {
- CheckForImpureCall(*stride);
- }
+ CheckForImpureCall(std::get<1>(control.t), concurrentNesting_);
+ CheckForImpureCall(std::get<2>(control.t), concurrentNesting_);
+ if (const auto &stride{std::get<3>(control.t)}) {
+ CheckForImpureCall(*stride, concurrentNesting_);
}
}
if (!indexNames.empty()) {
@@ -819,20 +826,29 @@ class DoContext {
CheckConcurrentHeader(std::get<parser::ConcurrentHeader>(concurrent.t));
}
- template <typename T> void CheckForImpureCall(const T &x) const {
+ template <typename T>
+ void CheckForImpureCall(
+ const T &x, std::optional<IndexVarKind> nesting) const {
if (auto bad{FindImpureCall(context_.foldingContext(), x)}) {
- context_.Say(
- "Impure procedure '%s' may not be referenced in a %s"_err_en_US, *bad,
- LoopKindName());
+ if (nesting) {
+ context_.Say(
+ "Impure procedure '%s' may not be referenced in a %s"_err_en_US,
+ *bad, LoopKindName(*nesting));
+ } else {
+ context_.Say(
+ "Impure procedure '%s' should not be referenced in a %s header"_warn_en_US,
+ *bad, LoopKindName(kind_));
+ }
}
}
- void CheckForImpureCall(const parser::ScalarIntExpr &x) const {
+ void CheckForImpureCall(const parser::ScalarIntExpr &x,
+ std::optional<IndexVarKind> nesting) const {
const auto &parsedExpr{x.thing.thing.value()};
auto oldLocation{context_.location()};
context_.set_location(parsedExpr.source);
if (const auto &typedExpr{parsedExpr.typedExpr}) {
if (const auto &expr{typedExpr->v}) {
- CheckForImpureCall(*expr);
+ CheckForImpureCall(*expr, nesting);
}
}
context_.set_location(oldLocation);
@@ -885,54 +901,59 @@ class DoContext {
}
// For messages where the DO loop must be DO CONCURRENT, make that explicit.
- const char *LoopKindName() const {
- return kind_ == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL";
+ const char *LoopKindName(IndexVarKind kind) const {
+ return kind == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL";
}
+ const char *LoopKindName() const { return LoopKindName(kind_); }
SemanticsContext &context_;
const IndexVarKind kind_;
parser::CharBlock currentStatementSourcePosition_;
- bool isNested_{false};
+ std::optional<IndexVarKind> concurrentNesting_;
}; // class DoContext
void DoForallChecker::Enter(const parser::DoConstruct &doConstruct) {
- DoContext doContext{context_, IndexVarKind::DO, constructNesting_ > 0};
+ DoContext doContext{context_, IndexVarKind::DO, nestedWithinConcurrent_};
+ if (doConstruct.IsDoConcurrent()) {
+ nestedWithinConcurrent_.push_back(IndexVarKind::DO);
+ }
doContext.DefineDoVariables(doConstruct);
+ doContext.Check(doConstruct);
}
void DoForallChecker::Leave(const parser::DoConstruct &doConstruct) {
- DoContext doContext{context_, IndexVarKind::DO, constructNesting_ > 0};
- ++constructNesting_;
- doContext.Check(doConstruct);
+ DoContext doContext{context_, IndexVarKind::DO, nestedWithinConcurrent_};
doContext.ResetDoVariables(doConstruct);
- --constructNesting_;
+ if (doConstruct.IsDoConcurrent()) {
+ nestedWithinConcurrent_.pop_back();
+ }
}
void DoForallChecker::Enter(const parser::ForallConstruct &construct) {
- DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
+ DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
doContext.ActivateIndexVars(GetControls(construct));
- ++constructNesting_;
+ nestedWithinConcurrent_.push_back(IndexVarKind::FORALL);
doContext.Check(construct);
}
void DoForallChecker::Leave(const parser::ForallConstruct &construct) {
- DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
+ DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
doContext.DeactivateIndexVars(GetControls(construct));
- --constructNesting_;
+ nestedWithinConcurrent_.pop_back();
}
void DoForallChecker::Enter(const parser::ForallStmt &stmt) {
- DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
- ++constructNesting_;
+ DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
+ nestedWithinConcurrent_.push_back(IndexVarKind::FORALL);
doContext.Check(stmt);
doContext.ActivateIndexVars(GetControls(stmt));
}
void DoForallChecker::Leave(const parser::ForallStmt &stmt) {
- DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
+ DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
doContext.DeactivateIndexVars(GetControls(stmt));
- --constructNesting_;
+ nestedWithinConcurrent_.pop_back();
}
void DoForallChecker::Leave(const parser::ForallAssignmentStmt &stmt) {
- DoContext doContext{context_, IndexVarKind::FORALL, constructNesting_ > 0};
+ DoContext doContext{context_, IndexVarKind::FORALL, nestedWithinConcurrent_};
doContext.Check(stmt);
}
diff --git a/flang/lib/Semantics/check-do-forall.h b/flang/lib/Semantics/check-do-forall.h
index f08ff3467d4cc9..4a65818364d764 100644
--- a/flang/lib/Semantics/check-do-forall.h
+++ b/flang/lib/Semantics/check-do-forall.h
@@ -60,7 +60,7 @@ class DoForallChecker : public virtual BaseChecker {
private:
SemanticsContext &context_;
int exprDepth_{0};
- int constructNesting_{0};
+ std::list<SemanticsContext::IndexVarKind> nestedWithinConcurrent_;
void SayBadLeave(
StmtType, const char *enclosingStmt, const ConstructNode &) const;
diff --git a/flang/test/Semantics/OpenMP/workshare02.f90 b/flang/test/Semantics/OpenMP/workshare02.f90
index e099ecb9f1e614..11f33d63a3eb80 100644
--- a/flang/test/Semantics/OpenMP/workshare02.f90
+++ b/flang/test/Semantics/OpenMP/workshare02.f90
@@ -40,6 +40,7 @@ subroutine workshare(aa, bb, cc, dd, ee, ff, n)
cc = ee + my_func()
end where
+ !WARNING: Impure procedure 'my_func' should not be referenced in a FORALL header
!ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct
forall (j = 1:my_func()) aa(j) = aa(j) + bb(j)
diff --git a/flang/test/Semantics/call11.f90 b/flang/test/Semantics/call11.f90
index 7bc4931890dee5..5358c741e2998e 100644
--- a/flang/test/Semantics/call11.f90
+++ b/flang/test/Semantics/call11.f90
@@ -42,30 +42,36 @@ subroutine test
!ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
a(j) = impure(j) ! C1139
end do
- do concurrent (k=impure(1):1); end do ! ok
- do concurrent (k=1:impure(1)); end do ! ok
- do concurrent (k=1:1:impure(1)); end do ! ok
- forall (k=impure(1):1); end forall ! ok
- forall (k=1:impure(1)); end forall ! ok
- forall (k=1:1:impure(1)); end forall ! ok
+ !WARNING: Impure procedure 'impure' should not be referenced in a DO CONCURRENT header
+ do concurrent (k=impure(1):1); end do
+ !WARNING: Impure procedure 'impure' should not be referenced in a DO CONCURRENT header
+ do concurrent (k=1:impure(1)); end do
+ !WARNING: Impure procedure 'impure' should not be referenced in a DO CONCURRENT header
+ do concurrent (k=1:1:impure(1)); end do
+ !WARNING: Impure procedure 'impure' should not be referenced in a FORALL header
+ forall (k=impure(1):1); end forall
+ !WARNING: Impure procedure 'impure' should not be referenced in a FORALL header
+ forall (k=1:impure(1)); end forall
+ !WARNING: Impure procedure 'impure' should not be referenced in a FORALL header
+ forall (k=1:1:impure(1)); end forall
do concurrent (j=1:1)
- !ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
+ !ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
do concurrent (k=impure(1):1); end do
- !ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
+ !ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
do concurrent (k=1:impure(1)); end do
- !ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
+ !ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
do concurrent (k=1:1:impure(1)); end do
- !ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
+ !ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
forall (k=impure(1):1); end forall
- !ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
+ !ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
forall (k=1:impure(1)); end forall
- !ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
+ !ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
forall (k=1:1:impure(1)); end forall
- !ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
+ !ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
forall (k=impure(1):1) a(k) = 0.
- !ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
+ !ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
forall (k=1:impure(1)) a(k) = 0.
- !ERROR: Impure procedure 'impure' may not be referenced in DO CONCURRENT
+ !ERROR: Impure procedure 'impure' may not be referenced in a DO CONCURRENT
forall (k=1:1:impure(1)) a(k) = 0.
end do
forall (j=1:1)
More information about the flang-commits
mailing list