[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