[flang-commits] [flang] [flang] Accept label DO loop after !$ACC LOOP (PR #187581)
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Fri Mar 20 07:18:15 PDT 2026
https://github.com/klausler updated https://github.com/llvm/llvm-project/pull/187581
>From 12eba2713b1f3b971460cf1e7610637b2e23a8f6 Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Wed, 18 Mar 2026 12:50:18 -0700
Subject: [PATCH] [flang] Accept label DO loop after !$ACC LOOP
Extend Semantics/canonicalize-do.cpp to rewrite a label DO loop into
a DO construct in the parse tree even when its terminal statement is
an OpenACC block construct or atomic construct. Some tools were
relocated from Parser/openmp-utils.{h,cpp} to /tools.{h,cpp}.
---
flang/include/flang/Parser/openmp-utils.h | 11 ----
flang/include/flang/Parser/tools.h | 14 ++++
flang/lib/Parser/openmp-utils.cpp | 57 ----------------
flang/lib/Parser/tools.cpp | 80 +++++++++++++++++++++++
flang/lib/Semantics/canonicalize-do.cpp | 6 +-
flang/lib/Semantics/resolve-labels.cpp | 13 +++-
flang/test/Semantics/bug2436.f | 20 ++++++
flang/test/Semantics/canondo18.f90 | 5 ++
8 files changed, 135 insertions(+), 71 deletions(-)
create mode 100644 flang/test/Semantics/bug2436.f
create mode 100644 flang/test/Semantics/canondo18.f90
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index 20754ad28d26d..cee3963be4281 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -122,17 +122,6 @@ const OpenMPConstruct *GetOmp(const ExecutionPartConstruct &x);
const OpenMPLoopConstruct *GetOmpLoop(const ExecutionPartConstruct &x);
const DoConstruct *GetDoConstruct(const ExecutionPartConstruct &x);
-// Is the template argument "Statement<T>" for some T?
-template <typename T> struct IsStatement {
- static constexpr bool value{false};
-};
-template <typename T> struct IsStatement<Statement<T>> {
- static constexpr bool value{true};
-};
-
-std::optional<Label> GetStatementLabel(const ExecutionPartConstruct &x);
-std::optional<Label> GetFinalLabel(const OpenMPConstruct &x);
-
namespace detail {
// Clauses with flangClass = "OmpObjectList".
using MemberObjectListClauses =
diff --git a/flang/include/flang/Parser/tools.h b/flang/include/flang/Parser/tools.h
index 5e705a8b47276..04e6fa348f6ca 100644
--- a/flang/include/flang/Parser/tools.h
+++ b/flang/include/flang/Parser/tools.h
@@ -270,5 +270,19 @@ bool CheckForSingleVariableOnRHS(const AssignmentStmt &);
const Name *GetDesignatorNameIfDataRef(const Designator &);
+// Is the template argument "Statement<T>" for some T?
+template <typename T> struct IsStatement {
+ static constexpr bool value{false};
+};
+template <typename T> struct IsStatement<Statement<T>> {
+ static constexpr bool value{true};
+};
+
+std::optional<Label> GetStatementLabel(const ExecutionPartConstruct &);
+
+std::optional<Label> GetFinalLabel(const Block &);
+std::optional<Label> GetFinalLabel(const OpenMPConstruct &);
+std::optional<Label> GetFinalLabel(const OpenACCConstruct &);
+
} // namespace Fortran::parser
#endif // FORTRAN_PARSER_TOOLS_H_
diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index f83a658104396..a3bd266249f30 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -71,63 +71,6 @@ const DoConstruct *GetDoConstruct(const ExecutionPartConstruct &x) {
return nullptr;
}
-// Get the Label from a Statement<...> contained in an ExecutionPartConstruct,
-// or std::nullopt, if there is no Statement<...> contained in there.
-template <typename T>
-static std::optional<Label> GetStatementLabelHelper(const T &stmt) {
- if constexpr (IsStatement<T>::value) {
- return stmt.label;
- } else if constexpr (WrapperTrait<T>) {
- return GetStatementLabelHelper(stmt.v);
- } else if constexpr (UnionTrait<T>) {
- return common::visit(
- [&](auto &&s) { return GetStatementLabelHelper(s); }, stmt.u);
- }
- return std::nullopt;
-}
-
-std::optional<Label> GetStatementLabel(const ExecutionPartConstruct &x) {
- return GetStatementLabelHelper(x);
-}
-
-static std::optional<Label> GetFinalLabel(const Block &x) {
- if (!x.empty()) {
- const ExecutionPartConstruct &last{x.back()};
- if (auto *omp{Unwrap<OpenMPConstruct>(last)}) {
- return GetFinalLabel(*omp);
- } else if (auto *doLoop{Unwrap<DoConstruct>(last)}) {
- return GetFinalLabel(std::get<Block>(doLoop->t));
- } else {
- return GetStatementLabel(x.back());
- }
- } else {
- return std::nullopt;
- }
-}
-
-std::optional<Label> GetFinalLabel(const OpenMPConstruct &x) {
- return common::visit(
- [](auto &&s) -> std::optional<Label> {
- using TypeS = llvm::remove_cvref_t<decltype(s)>;
- if constexpr (std::is_same_v<TypeS, OpenMPSectionsConstruct>) {
- auto &list{std::get<std::list<OpenMPConstruct>>(s.t)};
- if (!list.empty()) {
- return GetFinalLabel(list.back());
- } else {
- return std::nullopt;
- }
- } else if constexpr ( //
- std::is_same_v<TypeS, OpenMPLoopConstruct> ||
- std::is_same_v<TypeS, OpenMPSectionConstruct> ||
- std::is_base_of_v<OmpBlockConstruct, TypeS>) {
- return GetFinalLabel(std::get<Block>(s.t));
- } else {
- return std::nullopt;
- }
- },
- x.u);
-}
-
const OmpObjectList *GetOmpObjectList(const OmpClause &clause) {
return common::visit([](auto &&s) { return GetOmpObjectList(s); }, clause.u);
}
diff --git a/flang/lib/Parser/tools.cpp b/flang/lib/Parser/tools.cpp
index ff0538ae565b2..9e166c9185096 100644
--- a/flang/lib/Parser/tools.cpp
+++ b/flang/lib/Parser/tools.cpp
@@ -188,4 +188,84 @@ const Name *GetDesignatorNameIfDataRef(const Designator &designator) {
return dataRef ? std::get_if<Name>(&dataRef->u) : nullptr;
}
+// Get the Label from a Statement<...> contained in an ExecutionPartConstruct,
+// or std::nullopt, if there is no Statement<...> contained in there.
+template <typename T>
+static std::optional<Label> GetStatementLabelHelper(const T &stmt) {
+ if constexpr (IsStatement<T>::value) {
+ return stmt.label;
+ } else if constexpr (WrapperTrait<T>) {
+ return GetStatementLabelHelper(stmt.v);
+ } else if constexpr (UnionTrait<T>) {
+ return common::visit(
+ [&](auto &&s) { return GetStatementLabelHelper(s); }, stmt.u);
+ }
+ return std::nullopt;
+}
+
+std::optional<Label> GetStatementLabel(const ExecutionPartConstruct &x) {
+ return GetStatementLabelHelper(x);
+}
+
+std::optional<Label> GetFinalLabel(const Block &x) {
+ if (!x.empty()) {
+ const ExecutionPartConstruct &last{x.back()};
+ if (auto *omp{Unwrap<OpenMPConstruct>(last)}) {
+ return GetFinalLabel(*omp);
+ } else if (auto *doLoop{Unwrap<DoConstruct>(last)}) {
+ return GetFinalLabel(std::get<Block>(doLoop->t));
+ } else {
+ return GetStatementLabel(x.back());
+ }
+ } else {
+ return std::nullopt;
+ }
+}
+
+std::optional<Label> GetFinalLabel(const OpenMPConstruct &x) {
+ return common::visit(
+ [](auto &&s) -> std::optional<Label> {
+ using TypeS = llvm::remove_cvref_t<decltype(s)>;
+ if constexpr (std::is_same_v<TypeS, OpenMPSectionsConstruct>) {
+ auto &list{std::get<std::list<OpenMPConstruct>>(s.t)};
+ if (!list.empty()) {
+ return GetFinalLabel(list.back());
+ } else {
+ return std::nullopt;
+ }
+ } else if constexpr ( //
+ std::is_same_v<TypeS, OpenMPLoopConstruct> ||
+ std::is_same_v<TypeS, OpenMPSectionConstruct> ||
+ std::is_base_of_v<OmpBlockConstruct, TypeS>) {
+ return GetFinalLabel(std::get<Block>(s.t));
+ } else {
+ return std::nullopt;
+ }
+ },
+ x.u);
+}
+
+std::optional<Label> GetFinalLabel(const OpenACCConstruct &x) {
+ return common::visit(
+ common::visitors{
+ [](const OpenACCBlockConstruct &x) -> std::optional<Label> {
+ return GetFinalLabel(std::get<Block>(x.t));
+ },
+ [](const OpenACCAtomicConstruct &x) -> std::optional<Label> {
+ return common::visit(
+ common::visitors{
+ [](const auto &x) { // AtomicRead, AtomicWrite, AtomicUpdate
+ return std::get<Statement<AssignmentStmt>>(x.t).label;
+ },
+ [](const AccAtomicCapture &x) {
+ return std::get<AccAtomicCapture::Stmt2>(x.t).v.label;
+ },
+ },
+ x.u);
+ },
+ [](const auto &) -> std::optional<Label> { return std::nullopt; },
+ },
+ x.u);
+}
+
} // namespace Fortran::parser
diff --git a/flang/lib/Semantics/canonicalize-do.cpp b/flang/lib/Semantics/canonicalize-do.cpp
index 8b23f88c3be90..ba4aeca1b0834 100644
--- a/flang/lib/Semantics/canonicalize-do.cpp
+++ b/flang/lib/Semantics/canonicalize-do.cpp
@@ -94,11 +94,15 @@ class CanonicalizationOfDoLoops {
// treat the label as ending the construct itself.
OpenMPConstruct &omp{construct.value()};
if (CanonicalizeIfMatch(
- block, stack, i, omp::GetFinalLabel(omp))) {
+ block, stack, i, GetFinalLabel(omp))) {
MarkOpenMPConstruct(
omp, OmpDirectiveSpecification::Flag::CrossesLabelDo);
}
},
+ [&](common::Indirection<OpenACCConstruct> &construct) {
+ OpenACCConstruct &acc{construct.value()};
+ CanonicalizeIfMatch(block, stack, i, GetFinalLabel(acc));
+ },
},
executableConstruct->u);
}
diff --git a/flang/lib/Semantics/resolve-labels.cpp b/flang/lib/Semantics/resolve-labels.cpp
index 9454ef9fe928a..2da42b2f26cb1 100644
--- a/flang/lib/Semantics/resolve-labels.cpp
+++ b/flang/lib/Semantics/resolve-labels.cpp
@@ -97,6 +97,13 @@ constexpr Legality IsLegalDoTerm(
}
}
+// Handles an assignment statement that might be wrapped by a construct
+// other than an ActionStmt.
+constexpr Legality IsLegalDoTerm(
+ const parser::Statement<parser::AssignmentStmt> &) {
+ return Legality::formerly;
+}
+
template <typename A> constexpr bool IsFormat(const parser::Statement<A> &) {
return std::is_same_v<A, common::Indirection<parser::FormatStmt>>;
}
@@ -1037,8 +1044,10 @@ void CheckLabelDoConstraints(const SourceStmtList &dos,
SayLabel(label));
} else if (!doTarget.labeledStmtClassificationSet.test(
TargetStatementEnum::Do)) {
- context.Say(doTarget.parserCharBlock,
- "A DO loop should terminate with an END DO or CONTINUE"_err_en_US);
+ context
+ .Say(doTarget.parserCharBlock,
+ "This statement cannot terminate the DO loop"_err_en_US)
+ .Attach(position, "which begins at"_en_US);
} else {
loopBodies.emplace_back(SkipLabel(position), doTarget.parserCharBlock);
}
diff --git a/flang/test/Semantics/bug2436.f b/flang/test/Semantics/bug2436.f
new file mode 100644
index 0000000000000..5bd29d342c427
--- /dev/null
+++ b/flang/test/Semantics/bug2436.f
@@ -0,0 +1,20 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenacc %s | FileCheck %s
+ subroutine s(a,b,n)
+*$acc routine gang
+ real a(n), b(n)
+ integer(8) j
+*$acc loop gang vector worker
+ do 10 j = 1, n
+*$acc atomic update
+10 a(j) = a(j) + b(j)
+ end
+
+!CHECK: SUBROUTINE s (a, b, n)
+!CHECK: !$ACC ROUTINE GANG
+!CHECK: REAL a(n), b(n)
+!CHECK: !$ACC LOOP GANG VECTOR WORKER
+!CHECK: DO j=1_4,n
+!CHECK: !$ACC ATOMIC UPDATE
+!CHECK: 10 a(j)=a(j)+b(j)
+!CHECK: END DO
+!CHECK: END SUBROUTINE
diff --git a/flang/test/Semantics/canondo18.f90 b/flang/test/Semantics/canondo18.f90
new file mode 100644
index 0000000000000..70f9cb9d54d34
--- /dev/null
+++ b/flang/test/Semantics/canondo18.f90
@@ -0,0 +1,5 @@
+!RUN: %python %S/test_errors.py %s %flang_fc1
+do 10 j=1,1
+!ERROR: This statement cannot terminate the DO loop
+10 stop
+end
More information about the flang-commits
mailing list