[flang-commits] [flang] 03c3716 - [flang][OpenMP] Reject END DO on construct that crosses label-DO (#169714)

via flang-commits flang-commits at lists.llvm.org
Fri Dec 5 12:01:28 PST 2025


Author: Krzysztof Parzyszek
Date: 2025-12-05T14:01:24-06:00
New Revision: 03c37160a1735c12e4acbac70a593ff3e68d5ad7

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

LOG: [flang][OpenMP] Reject END DO on construct that crosses label-DO (#169714)

In a label-DO construct where two or more loops share the same
teminator, an OpenMP construct must enclose all the loops if an
end-directive is present. E.g.

```
  do 100 i = 1,10
!$omp do
    do 100 j = 1,10
    100 continue
!$omp end do    ! Error, but ok if this line is removed
```

Fixes https://github.com/llvm/llvm-project/issues/169536.

Added: 
    

Modified: 
    flang/include/flang/Parser/parse-tree.h
    flang/lib/Semantics/canonicalize-do.cpp
    flang/lib/Semantics/check-omp-loop.cpp
    flang/test/Parser/OpenMP/atomic-label-do.f90
    flang/test/Parser/OpenMP/cross-label-do.f90
    flang/test/Semantics/OpenMP/loop-association.f90

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index cbfcaafa78dc0..c4ace2d8a2135 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4982,7 +4982,7 @@ struct OmpClauseList {
 // --- Directives and constructs
 
 struct OmpDirectiveSpecification {
-  ENUM_CLASS(Flag, DeprecatedSyntax)
+  ENUM_CLASS(Flag, DeprecatedSyntax, CrossesLabelDo)
   using Flags = common::EnumSet<Flag, Flag_enumSize>;
 
   TUPLE_CLASS_BOILERPLATE(OmpDirectiveSpecification);

diff  --git a/flang/lib/Semantics/canonicalize-do.cpp b/flang/lib/Semantics/canonicalize-do.cpp
index 409195d5960b4..8b23f88c3be90 100644
--- a/flang/lib/Semantics/canonicalize-do.cpp
+++ b/flang/lib/Semantics/canonicalize-do.cpp
@@ -92,8 +92,12 @@ class CanonicalizationOfDoLoops {
                 [&](common::Indirection<OpenMPConstruct> &construct) {
                   // If the body of the OpenMP construct ends with a label,
                   // treat the label as ending the construct itself.
-                  CanonicalizeIfMatch(
-                      block, stack, i, omp::GetFinalLabel(construct.value()));
+                  OpenMPConstruct &omp{construct.value()};
+                  if (CanonicalizeIfMatch(
+                          block, stack, i, omp::GetFinalLabel(omp))) {
+                    MarkOpenMPConstruct(
+                        omp, OmpDirectiveSpecification::Flag::CrossesLabelDo);
+                  }
                 },
             },
             executableConstruct->u);
@@ -103,12 +107,12 @@ class CanonicalizationOfDoLoops {
 
 private:
   template <typename T>
-  void CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
+  bool CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
       Block::iterator &i, Statement<T> &statement) {
-    CanonicalizeIfMatch(originalBlock, stack, i, statement.label);
+    return CanonicalizeIfMatch(originalBlock, stack, i, statement.label);
   }
 
-  void CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
+  bool CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
       Block::iterator &i, std::optional<Label> label) {
     if (!stack.empty() && label && stack.back().label == *label) {
       auto currentLabel{stack.back().label};
@@ -141,8 +145,27 @@ class CanonicalizationOfDoLoops {
         stack.pop_back();
       } while (!stack.empty() && stack.back().label == currentLabel);
       i = --next;
+      return true;
+    } else {
+      return false;
     }
   }
+
+  void MarkOpenMPConstruct(
+      OpenMPConstruct &omp, OmpDirectiveSpecification::Flag flag) {
+    common::visit(
+        [&](const auto &s) {
+          using S = std::decay_t<decltype(s)>;
+          if constexpr (std::is_base_of_v<OmpBlockConstruct, S> ||
+              std::is_same_v<OpenMPLoopConstruct, S>) {
+            const OmpDirectiveSpecification &beginSpec{s.BeginDir()};
+            auto &flags{
+                std::get<OmpDirectiveSpecification::Flags>(beginSpec.t)};
+            const_cast<OmpDirectiveSpecification::Flags &>(flags).set(flag);
+          }
+        },
+        omp.u);
+  }
 };
 
 bool CanonicalizeDo(Program &program) {

diff  --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index fc4b9222d91b3..f25cf7eb33817 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -290,6 +290,25 @@ void OmpStructureChecker::CheckNestedConstruct(
     const parser::OpenMPLoopConstruct &x) {
   size_t nestedCount{0};
 
+  // End-directive is not allowed in such cases:
+  //   do 100 i = ...
+  //     !$omp do
+  //     do 100 j = ...
+  //   100 continue
+  //   !$omp end do    ! error
+  const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
+  auto &flags{std::get<parser::OmpDirectiveSpecification::Flags>(beginSpec.t)};
+  if (flags.test(parser::OmpDirectiveSpecification::Flag::CrossesLabelDo)) {
+    if (auto &endSpec{x.EndDir()}) {
+      parser::CharBlock beginSource{beginSpec.DirName().source};
+      context_
+          .Say(endSpec->DirName().source,
+              "END %s directive is not allowed when the construct does not contain all loops that share a loop-terminating statement"_err_en_US,
+              parser::ToUpperCaseLetters(beginSource.ToString()))
+          .Attach(beginSource, "The construct starts here"_en_US);
+    }
+  }
+
   auto &body{std::get<parser::Block>(x.t)};
   if (body.empty()) {
     context_.Say(x.source,

diff  --git a/flang/test/Parser/OpenMP/atomic-label-do.f90 b/flang/test/Parser/OpenMP/atomic-label-do.f90
index f0c83c01f7a21..1c7037f239191 100644
--- a/flang/test/Parser/OpenMP/atomic-label-do.f90
+++ b/flang/test/Parser/OpenMP/atomic-label-do.f90
@@ -29,7 +29,7 @@ subroutine f
 !PARSE-TREE: | | | OmpBeginDirective
 !PARSE-TREE: | | | | OmpDirectiveName -> llvm::omp::Directive = atomic
 !PARSE-TREE: | | | | OmpClauseList -> OmpClause -> Write
-!PARSE-TREE: | | | | Flags = {}
+!PARSE-TREE: | | | | Flags = {CrossesLabelDo}
 !PARSE-TREE: | | | Block
 !PARSE-TREE: | | | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'x=i'
 !PARSE-TREE: | | | | | Variable = 'x'

diff  --git a/flang/test/Parser/OpenMP/cross-label-do.f90 b/flang/test/Parser/OpenMP/cross-label-do.f90
index fd648e0248258..f1a406c334c39 100644
--- a/flang/test/Parser/OpenMP/cross-label-do.f90
+++ b/flang/test/Parser/OpenMP/cross-label-do.f90
@@ -32,7 +32,7 @@ subroutine f00
 !PARSE-TREE: | | | OmpBeginLoopDirective
 !PARSE-TREE: | | | | OmpDirectiveName -> llvm::omp::Directive = do
 !PARSE-TREE: | | | | OmpClauseList ->
-!PARSE-TREE: | | | | Flags = {}
+!PARSE-TREE: | | | | Flags = {CrossesLabelDo}
 !PARSE-TREE: | | | Block
 !PARSE-TREE: | | | | ExecutionPartConstruct -> ExecutableConstruct -> DoConstruct
 !PARSE-TREE: | | | | | NonLabelDoStmt

diff  --git a/flang/test/Semantics/OpenMP/loop-association.f90 b/flang/test/Semantics/OpenMP/loop-association.f90
index 4e63cafb3fda1..7070ff5638c72 100644
--- a/flang/test/Semantics/OpenMP/loop-association.f90
+++ b/flang/test/Semantics/OpenMP/loop-association.f90
@@ -64,6 +64,7 @@
      do 100 j=1, N
         a = 3.14
 100     continue
+    !ERROR: END DO directive is not allowed when the construct does not contain all loops that share a loop-terminating statement
     !$omp enddo
 
   !ERROR: Non-THREADPRIVATE object 'a' in COPYIN clause


        


More information about the flang-commits mailing list