[flang-commits] [flang] [flang][openacc] Accept enclosing labeled DO loops sharing a label with an ACC loop (PR #229502)

Valentin Clement バレンタイン クレメン via flang-commits flang-commits at lists.llvm.org
Tue Oct 6 10:51:07 PDT 2026


https://github.com/clementval created https://github.com/llvm/llvm-project/pull/229502

When an `!$acc loop` or combined construct is associated with a labeled DO,
AccNonBlockDoConstruct builds a DoConstruct for it during parsing. If an
enclosing labeled DO without a directive shares the same terminating label,
that outer loop remains a LabelDoStmt until CanonicalizeDo, while its
terminator is now nested inside the DoConstruct. AnalyzeLabels runs before
CanonicalizeDo and treats every DoConstruct as a new scope, so it rejected
the outer loop with "Label 'N' is not in DO loop scope":

      do 10 k = 1, m
!$acc loop
        do 10 i = 1, n
10        a(i, k) = 0.

In label analysis, recognize DO constructs built from a non-block DO by
their synthesized END DO statement, which has no source, and analyze them
as the original labeled DO statement: do not open a new scope and record a
DO reference to the terminating label. This restores the label checks on
the loop itself (e.g. "This statement cannot terminate the DO loop"), which
were silently skipped since the loop became a DoConstruct.

>From e7cf93c82186029861088de020f3fabefc0f229e Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Tue, 6 Oct 2026 10:48:43 -0700
Subject: [PATCH] [flang][openacc] Accept enclosing labeled DO loops sharing a
 label with an ACC loop

---
 flang/lib/Semantics/resolve-labels.cpp        | 34 ++++++++++++
 flang/test/Semantics/OpenACC/acc-label-do.f90 | 55 +++++++++++++++++++
 2 files changed, 89 insertions(+)
 create mode 100644 flang/test/Semantics/OpenACC/acc-label-do.f90

diff --git a/flang/lib/Semantics/resolve-labels.cpp b/flang/lib/Semantics/resolve-labels.cpp
index 52b35a90302460..734317b16eb1ec 100644
--- a/flang/lib/Semantics/resolve-labels.cpp
+++ b/flang/lib/Semantics/resolve-labels.cpp
@@ -10,6 +10,7 @@
 #include "flang/Common/enum-set.h"
 #include "flang/Common/template.h"
 #include "flang/Parser/parse-tree-visitor.h"
+#include "flang/Parser/tools.h"
 #include "flang/Semantics/semantics.h"
 #include <type_traits>
 
@@ -286,6 +287,9 @@ class ParseTreeAnalyzer {
     return PushConstructName(criticalConstruct);
   }
   bool Pre(const parser::DoConstruct &doConstruct) {
+    if (IsNonBlockDoConstruct(doConstruct)) {
+      return PreNonBlockDoConstruct(doConstruct);
+    }
     const auto &optionalName{std::get<std::optional<parser::Name>>(
         std::get<parser::Statement<parser::NonLabelDoStmt>>(doConstruct.t)
             .statement.t)};
@@ -642,6 +646,36 @@ class ParseTreeAnalyzer {
     return true;
   }
 
+  // The parser builds a DO construct, with a synthesized END DO statement
+  // that has no source, from a non-block (labeled) DO loop associated with an
+  // OpenACC LOOP or combined construct.  Such a loop may share its terminating
+  // label with enclosing labeled DO statements that are only turned into DO
+  // constructs later by CanonicalizeDo.
+  static bool IsNonBlockDoConstruct(const parser::DoConstruct &doConstruct) {
+    return std::get<parser::Statement<parser::EndDoStmt>>(doConstruct.t)
+        .source.empty();
+  }
+
+  // Analyze a DO construct built from a non-block DO loop as the original
+  // labeled DO statement: it does not open a new scope, and it references
+  // its terminating label.
+  bool PreNonBlockDoConstruct(const parser::DoConstruct &doConstruct) {
+    const auto &doStmt{
+        std::get<parser::Statement<parser::NonLabelDoStmt>>(doConstruct.t)};
+    currentPosition_ = doStmt.source;
+    if (doStmt.label) {
+      AddTargetLabelDefinition(*doStmt.label,
+          ConstructBranchTargetFlags(doStmt), currentScope_,
+          /*isExecutableConstructEndStmt=*/false, currentPosition_);
+    }
+    const auto &block{std::get<parser::Block>(doConstruct.t)};
+    if (auto label{parser::GetFinalLabel(block)}) {
+      AddLabelReferenceFromDoStmt(*label);
+    }
+    Walk(block, *this);
+    return false;
+  }
+
   template <typename A> bool PushConstructName(const A &a) {
     const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)};
     if (optionalName) {
diff --git a/flang/test/Semantics/OpenACC/acc-label-do.f90 b/flang/test/Semantics/OpenACC/acc-label-do.f90
new file mode 100644
index 00000000000000..a88a8ee168c1dd
--- /dev/null
+++ b/flang/test/Semantics/OpenACC/acc-label-do.f90
@@ -0,0 +1,55 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenacc
+
+! Labeled DO loops that share their terminating label with an inner labeled DO
+! loop associated with an OpenACC LOOP construct.
+
+subroutine outer_without_directive(a, n, m)
+  integer :: i, k, n, m
+  real :: a(n, m)
+  do 10 k = 1, m
+!$acc loop gang vector
+    do 10 i = 1, n
+10    a(i, k) = a(i, k) + 1.
+end
+
+subroutine outer_without_directive_atomic(a, b, n, m)
+  integer :: i, k, n, m
+  real :: a(n, m), b(n, m)
+  do 20 k = 1, m
+!$acc loop gang vector
+    do 20 i = 1, n
+!$acc atomic update
+      a(i, k) = a(i, k) + b(i, k)
+!$acc atomic update
+20    b(i, k) = b(i, k) + a(i, k)
+end
+
+subroutine two_outer_without_directive(a, n, m)
+  integer :: i, j, k, n, m
+  real :: a(n, m)
+  do 30 k = 1, m
+    do 30 j = 1, m
+!$acc loop
+      do 30 i = 1, n
+30      a(i, k) = a(i, j)
+end
+
+subroutine bad_terminator(n)
+  integer :: i, k, n
+  do 40 k = 1, n
+!$acc loop
+    do 40 i = 1, n
+!ERROR: This statement cannot terminate the DO loop
+!ERROR: This statement cannot terminate the DO loop
+40    goto 40
+end
+
+subroutine missing_label(a, n)
+  integer :: i, k, n
+  real :: a(n)
+  !ERROR: Label '50' cannot be found
+  do 50 k = 1, n
+!$acc loop
+    do 60 i = 1, n
+60    a(i) = 0.
+end



More information about the flang-commits mailing list