[flang-commits] [flang] e53f827 - [flang][OpenACC] Add semantic check for GOTO branching out of compute constructs (#189385)

via flang-commits flang-commits at lists.llvm.org
Mon Mar 30 11:00:54 PDT 2026


Author: khaki3
Date: 2026-03-30T11:00:47-07:00
New Revision: e53f82716b855ea436e6a9160d80417531b46c25

URL: https://github.com/llvm/llvm-project/commit/e53f82716b855ea436e6a9160d80417531b46c25
DIFF: https://github.com/llvm/llvm-project/commit/e53f82716b855ea436e6a9160d80417531b46c25.diff

LOG: [flang][OpenACC] Add semantic check for GOTO branching out of compute constructs (#189385)

Per OpenACC spec 2.5.4, branching out of `parallel`/`serial`/`kernels`
constructs is not allowed. Add a GOTO check to `NoBranchingEnforce` that
collects labels within the construct block and flags GOTOs targeting
labels outside. In-region GOTOs are allowed.

The check applies only to compute constructs (`parallel`, `serial`,
`kernels`), not to data constructs where GOTO out is valid.

Added: 
    

Modified: 
    flang/lib/Semantics/check-directive-structure.h
    flang/test/Semantics/OpenACC/acc-branch.f90

Removed: 
    


################################################################################
diff  --git a/flang/lib/Semantics/check-directive-structure.h b/flang/lib/Semantics/check-directive-structure.h
index bd78d3cfe91e7..46fbc5191b38a 100644
--- a/flang/lib/Semantics/check-directive-structure.h
+++ b/flang/lib/Semantics/check-directive-structure.h
@@ -17,6 +17,7 @@
 #include "flang/Semantics/tools.h"
 #include "llvm/ADT/iterator_range.h"
 
+#include <set>
 #include <unordered_map>
 
 namespace Fortran::semantics {
@@ -53,6 +54,21 @@ template <typename D> class NoBranchingEnforce {
   }
   void Post(const parser::DoConstruct &) { numDoConstruct_--; }
   void Post(const parser::ReturnStmt &) { EmitBranchOutError("RETURN"); }
+  void Post(const parser::GotoStmt &gotoStmt) {
+    if constexpr (std::is_same_v<D, llvm::acc::Directive>) {
+      switch ((llvm::acc::Directive)currentDirective_) {
+      case llvm::acc::Directive::ACCD_parallel:
+      case llvm::acc::Directive::ACCD_serial:
+      case llvm::acc::Directive::ACCD_kernels:
+        if (labelsInBlock_.count(gotoStmt.v) == 0)
+          EmitBranchOutOfComputeConstructError("GOTO");
+        break;
+      default:
+        break;
+      }
+    }
+  }
+  void CollectLabel(parser::Label label) { labelsInBlock_.insert(label); }
   void Post(const parser::ExitStmt &exitStmt) {
     if (const auto &exitName{exitStmt.v}) {
       CheckConstructNameBranching("EXIT", exitName.value());
@@ -115,6 +131,14 @@ template <typename D> class NoBranchingEnforce {
         .Attach(sourcePosition_, GetEnclosingMsg());
   }
 
+  void EmitBranchOutOfComputeConstructError(const char *stmt) const {
+    context_
+        .Say(currentStatementSourcePosition_,
+            "%s to a label outside of a %s construct is not allowed"_err_en_US,
+            stmt, upperCaseDirName_)
+        .Attach(sourcePosition_, GetEnclosingMsg());
+  }
+
   inline void EmitUnlabelledBranchOutError(const char *stmt) {
     context_
         .Say(currentStatementSourcePosition_,
@@ -172,6 +196,7 @@ template <typename D> class NoBranchingEnforce {
   D currentDirective_;
   int numDoConstruct_; // tracks number of DoConstruct found AFTER encountering
                        // an OpenMP/OpenACC directive
+  std::set<parser::Label> labelsInBlock_;
 };
 
 // Generic structure checker for directives/clauses language such as OpenMP
@@ -401,12 +426,28 @@ class DirectiveStructureChecker : public virtual BaseChecker {
   std::string ClauseSetToString(const common::EnumSet<C, ClauseEnumSize> set);
 };
 
+// Collect all labels defined in a block.
+struct LabelCollector {
+  std::set<parser::Label> labels;
+  template <typename T> bool Pre(const T &) { return true; }
+  template <typename T> void Post(const T &) {}
+  template <typename T> bool Pre(const parser::Statement<T> &stmt) {
+    if (stmt.label)
+      labels.insert(*stmt.label);
+    return true;
+  }
+};
+
 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
 void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckNoBranching(
     const parser::Block &block, D directive,
     const parser::CharBlock &directiveSource) {
+  LabelCollector labelCollector;
+  parser::Walk(block, labelCollector);
   NoBranchingEnforce<D> noBranchingEnforce{
       context_, directiveSource, directive, ContextDirectiveAsFortran()};
+  for (auto label : labelCollector.labels)
+    noBranchingEnforce.CollectLabel(label);
   parser::Walk(block, noBranchingEnforce);
 }
 

diff  --git a/flang/test/Semantics/OpenACC/acc-branch.f90 b/flang/test/Semantics/OpenACC/acc-branch.f90
index 0a1bdc3afd65a..a39fd4609d87a 100644
--- a/flang/test/Semantics/OpenACC/acc-branch.f90
+++ b/flang/test/Semantics/OpenACC/acc-branch.f90
@@ -196,4 +196,48 @@ subroutine openacc_clause_validity
 
   !$acc end data
 
+  ! GOTO branching out of compute constructs is not allowed (spec 2.5.4).
+  !$acc parallel
+  do i = 1, N
+    a(i) = 3.14d0
+    !ERROR: GOTO to a label outside of a PARALLEL construct is not allowed
+    if (i == N-1) goto 999
+  end do
+  !$acc end parallel
+999 continue
+
+  !$acc kernels
+  do i = 1, N
+    a(i) = 3.14d0
+    !ERROR: GOTO to a label outside of a KERNELS construct is not allowed
+    if (i == N-1) goto 998
+  end do
+  !$acc end kernels
+998 continue
+
+  !$acc serial
+  do i = 1, N
+    a(i) = 3.14d0
+    !ERROR: GOTO to a label outside of a SERIAL construct is not allowed
+    if (i == N-1) goto 997
+  end do
+  !$acc end serial
+997 continue
+
+  ! GOTO within a compute construct is allowed.
+  !$acc parallel
+  do i = 1, N
+    if (i == N-1) goto 996
+996 a(i) = 3.14d0
+  end do
+  !$acc end parallel
+
+  ! GOTO out of a data construct is allowed.
+  !$acc data create(a)
+  do i = 1, N
+    if (i == N-1) goto 995
+  end do
+  !$acc end data
+995 continue
+
 end subroutine openacc_clause_validity


        


More information about the flang-commits mailing list