[flang-commits] [flang] 0571ce4 - [flang][OpenMP] Move branching verification to semantic checks (#193324)

via flang-commits flang-commits at lists.llvm.org
Fri Apr 24 04:23:20 PDT 2026


Author: Krzysztof Parzyszek
Date: 2026-04-24T06:23:15-05:00
New Revision: 0571ce414ec0c499976fb73d9e4796326b4e3c1b

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

LOG: [flang][OpenMP] Move branching verification to semantic checks (#193324)

Move the check for branching into and out of an OpenMP construct from
symbol resolution into semantic checks.
Instead of using directive contexts to check for crossing a construct
boundary, use construct pointers and source ranges.

Added: 
    

Modified: 
    flang/lib/Semantics/check-omp-loop.cpp
    flang/lib/Semantics/check-omp-structure.cpp
    flang/lib/Semantics/check-omp-structure.h
    flang/lib/Semantics/resolve-directives.cpp
    flang/test/Semantics/OpenMP/parallel-master-goto.f90

Removed: 
    


################################################################################
diff  --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 739c76da15a7f..b246d027e4807 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -380,8 +380,6 @@ void OmpStructureChecker::CheckNestedConstruct(
 }
 
 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
-  loopStack_.push_back(&x);
-
   const parser::OmpDirectiveName &beginName{x.BeginDir().DirName()};
   PushContextAndClauseSets(beginName.source, beginName.v);
 
@@ -619,14 +617,6 @@ void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) {
     ExitDirectiveNest(SIMDNest);
   }
   dirContext_.pop_back();
-
-  assert(!loopStack_.empty() && "Expecting non-empty loop stack");
-#ifndef NDEBUG
-  const LoopConstruct &top{loopStack_.back()};
-  auto *loopc{std::get_if<const parser::OpenMPLoopConstruct *>(&top)};
-  assert(loopc != nullptr && *loopc == &x && "Mismatched loop constructs");
-#endif
-  loopStack_.pop_back();
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Depth &x) {
@@ -814,17 +804,17 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) {
 
 void OmpStructureChecker::Enter(const parser::DoConstruct &x) {
   Base::Enter(x);
-  loopStack_.push_back(&x);
+  constructStack_.push_back(&x);
 }
 
 void OmpStructureChecker::Leave(const parser::DoConstruct &x) {
-  assert(!loopStack_.empty() && "Expecting non-empty loop stack");
+  assert(!constructStack_.empty() && "Expecting non-empty construct stack");
 #ifndef NDEBUG
-  const LoopConstruct &top = loopStack_.back();
+  const LoopOrConstruct &top = constructStack_.back();
   auto *doc{std::get_if<const parser::DoConstruct *>(&top)};
-  assert(doc != nullptr && *doc == &x && "Mismatched loop constructs");
+  assert(doc != nullptr && *doc == &x && "Mismatched constructs");
 #endif
-  loopStack_.pop_back();
+  constructStack_.pop_back();
   Base::Leave(x);
 }
 

diff  --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index bc90247732fff..30db2f2ad4ec5 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -179,6 +179,14 @@ void OmpStructureChecker::Leave(const parser::BlockConstruct &x) {
   }
 }
 
+void OmpStructureChecker::Enter(const parser::InternalSubprogram &) {
+  ClearLabels();
+}
+
+void OmpStructureChecker::Enter(const parser::ModuleSubprogram &) {
+  ClearLabels();
+}
+
 void OmpStructureChecker::Enter(const parser::SpecificationPart &) {
   partStack_.push_back(PartKind::SpecificationPart);
 }
@@ -428,6 +436,118 @@ void OmpStructureChecker::AnalyzeObjects(const parser::OmpObjectList &objects) {
   }
 }
 
+const parser::OpenMPConstruct *
+OmpStructureChecker::GetCurrentConstruct() const {
+  for (const LoopOrConstruct &c : llvm::reverse(constructStack_)) {
+    if (auto *omp{std::get_if<const parser::OpenMPConstruct *>(&c)}) {
+      return *omp;
+    }
+  }
+  return nullptr;
+}
+
+void OmpStructureChecker::CheckSourceLabel(const parser::Label &label) {
+  // Get the context to check if the statement causing a jump to the 'label' is
+  // in an enclosing OpenMP construct
+  const parser::OpenMPConstruct *thisConstruct{GetCurrentConstruct()};
+  sourceLabels_.emplace(
+      label, std::make_pair(currentStatementSource_, thisConstruct));
+  // Check if the statement with 'label' to which a jump is being introduced
+  // has already been encountered
+  auto it{targetLabels_.find(label)};
+  if (it != targetLabels_.end()) {
+    // Check if both the statement with 'label' and the statement that causes a
+    // jump to the 'label' are in the same block.
+    CheckLabelContext(currentStatementSource_, it->second.first, thisConstruct,
+        it->second.second);
+  }
+}
+
+// Check for invalid branch into or out of OpenMP structured blocks
+void OmpStructureChecker::CheckLabelContext(const parser::CharBlock source,
+    const parser::CharBlock target, const parser::OpenMPConstruct *srcOmp,
+    const parser::OpenMPConstruct *tgtOmp) {
+  auto isSameOrIncludes = [&](const parser::OpenMPConstruct *lhs,
+                              const parser::OpenMPConstruct *rhs) -> bool {
+    assert(lhs && "Expecting first argument");
+    if (!rhs) {
+      return false;
+    }
+    if (lhs == rhs) {
+      return true;
+    }
+
+    auto getSource{[](const parser::OpenMPConstruct &c) -> parser::CharBlock {
+      std::optional<parser::CharBlock> src{parser::GetSource(c)};
+      assert(src.has_value() && "OpenMPConstruct should have source");
+      return *src;
+    }};
+
+    return getSource(*lhs).Contains(getSource(*rhs));
+  };
+
+  unsigned version{context_.langOptions().OpenMPVersion};
+  if (tgtOmp && !isSameOrIncludes(tgtOmp, srcOmp)) {
+    parser::OmpDirectiveName name{GetOmpDirectiveName(*tgtOmp)};
+    context_
+        .Say(source, "invalid branch into an OpenMP structured block"_err_en_US)
+        .Attach(target, "In the enclosing %s directive branched into"_en_US,
+            parser::omp::GetUpperName(name.v, version));
+  }
+  if (srcOmp && !isSameOrIncludes(srcOmp, tgtOmp)) {
+    parser::OmpDirectiveName name{GetOmpDirectiveName(*srcOmp)};
+    context_
+        .Say(source,
+            "invalid branch leaving an OpenMP structured block"_err_en_US)
+        .Attach(target, "Outside the enclosing %s directive"_en_US,
+            parser::omp::GetUpperName(name.v, version));
+  }
+}
+
+// Keep track of labels in the statements that causes jumps to target labels
+void OmpStructureChecker::Leave(const parser::GotoStmt &x) {
+  CheckSourceLabel(x.v);
+}
+
+void OmpStructureChecker::Leave(const parser::ComputedGotoStmt &x) {
+  for (auto &label : std::get<std::list<parser::Label>>(x.t)) {
+    CheckSourceLabel(label);
+  }
+}
+
+void OmpStructureChecker::Leave(const parser::ArithmeticIfStmt &x) {
+  CheckSourceLabel(std::get<1>(x.t));
+  CheckSourceLabel(std::get<2>(x.t));
+  CheckSourceLabel(std::get<3>(x.t));
+}
+
+void OmpStructureChecker::Leave(const parser::AssignedGotoStmt &x) {
+  for (auto &label : std::get<std::list<parser::Label>>(x.t)) {
+    CheckSourceLabel(label);
+  }
+}
+
+void OmpStructureChecker::Leave(const parser::AltReturnSpec &x) {
+  CheckSourceLabel(x.v);
+}
+
+void OmpStructureChecker::Leave(const parser::ErrLabel &x) {
+  CheckSourceLabel(x.v);
+}
+
+void OmpStructureChecker::Leave(const parser::EndLabel &x) {
+  CheckSourceLabel(x.v);
+}
+
+void OmpStructureChecker::Leave(const parser::EorLabel &x) {
+  CheckSourceLabel(x.v);
+}
+
+void OmpStructureChecker::ClearLabels() {
+  sourceLabels_.clear();
+  targetLabels_.clear();
+}
+
 bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet &set) {
   // Definition of close nesting:
   //
@@ -805,6 +925,8 @@ template <typename T>
 DirectiveSpellingVisitor(T &&) -> DirectiveSpellingVisitor<T>;
 
 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
+  constructStack_.push_back(&x);
+
   DirectiveSpellingVisitor visitor(
       [this](parser::CharBlock source, llvm::omp::Directive id) {
         return CheckDirectiveSpelling(source, id);
@@ -844,6 +966,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPConstruct &x) {
   if (GetOmpDirectiveName(x).v != llvm::omp::Directive::OMPD_section) {
     dirStack_.pop_back();
   }
+  constructStack_.pop_back();
 }
 
 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
@@ -4767,8 +4890,8 @@ void OmpStructureChecker::CheckDoacross(const parser::OmpDoacross &doa) {
   // number of DO constructs on the stack. This is checked elsewhere.
 
   std::set<const Symbol *> inductionVars;
-  for (const LoopConstruct &loop : llvm::reverse(loopStack_)) {
-    if (auto *doc{std::get_if<const parser::DoConstruct *>(&loop)}) {
+  for (const LoopOrConstruct &c : llvm::reverse(constructStack_)) {
+    if (auto *doc{std::get_if<const parser::DoConstruct *>(&c)}) {
       // Do-construct, collect the induction variable.
       if (auto &control{(*doc)->GetLoopControl()}) {
         if (auto *b{std::get_if<parser::LoopControl::Bounds>(&control->u)}) {
@@ -4777,15 +4900,17 @@ void OmpStructureChecker::CheckDoacross(const parser::OmpDoacross &doa) {
       }
     } else {
       // Omp-loop-construct, check if it's do/simd with an ORDERED clause.
-      auto *loopc{std::get_if<const parser::OpenMPLoopConstruct *>(&loop)};
-      assert(loopc && "Expecting OpenMPLoopConstruct");
-      const parser::OmpDirectiveSpecification &beginSpec{(*loopc)->BeginDir()};
-      llvm::omp::Directive loopDir{beginSpec.DirId()};
-      if (loopDir == llvm::omp::OMPD_do || loopDir == llvm::omp::OMPD_simd) {
-        // If it has ORDERED clause, stop the traversal.
-        if (parser::omp::FindClause(
-                beginSpec, llvm::omp::Clause::OMPC_ordered)) {
-          break;
+      auto *omp{std::get_if<const parser::OpenMPConstruct *>(&c)};
+      assert(omp && "Expecting OpenMPConstruct");
+      if (auto *loop{parser::Unwrap<parser::OpenMPLoopConstruct>(*omp)}) {
+        const parser::OmpDirectiveSpecification &beginSpec{loop->BeginDir()};
+        llvm::omp::Directive loopDir{beginSpec.DirId()};
+        if (loopDir == llvm::omp::OMPD_do || loopDir == llvm::omp::OMPD_simd) {
+          // If it has ORDERED clause, stop the traversal.
+          if (parser::omp::FindClause(
+                  beginSpec, llvm::omp::Clause::OMPC_ordered)) {
+            break;
+          }
         }
       }
     }

diff  --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 2d9472874b0c8..ce0061594b9fd 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -85,6 +85,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   bool Enter(const parser::EndFunctionStmt &);
   bool Enter(const parser::BlockConstruct &);
   void Leave(const parser::BlockConstruct &);
+  void Enter(const parser::InternalSubprogram &);
+  void Enter(const parser::ModuleSubprogram &);
 
   void Enter(const parser::SpecificationPart &);
   void Leave(const parser::SpecificationPart &);
@@ -178,10 +180,23 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   void Enter(const parser::OmpContextSelector &);
   void Leave(const parser::OmpContextSelector &);
 
+  template <typename A> void Enter(const parser::Statement<A> &);
+  void Leave(const parser::GotoStmt &);
+  void Leave(const parser::ComputedGotoStmt &);
+  void Leave(const parser::ArithmeticIfStmt &);
+  void Leave(const parser::AssignedGotoStmt &);
+  void Leave(const parser::AltReturnSpec &);
+  void Leave(const parser::ErrLabel &);
+  void Leave(const parser::EndLabel &);
+  void Leave(const parser::EorLabel &);
+
 #define GEN_FLANG_CLAUSE_CHECK_ENTER
 #include "llvm/Frontend/OpenMP/OMP.inc"
 
 private:
+  using LoopOrConstruct = std::variant<const parser::DoConstruct *,
+      const parser::OpenMPConstruct *>;
+
   // Most of these functions are defined in check-omp-structure.cpp, but
   // some groups have their own files.
 
@@ -273,6 +288,12 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   void CheckDirectiveDeprecation(const parser::OpenMPConstruct &x);
   void AnalyzeObject(const parser::OmpObject &object);
   void AnalyzeObjects(const parser::OmpObjectList &objects);
+
+  const parser::OpenMPConstruct *GetCurrentConstruct() const;
+  void CheckSourceLabel(const parser::Label &);
+  void CheckLabelContext(const parser::CharBlock, const parser::CharBlock,
+      const parser::OpenMPConstruct *, const parser::OpenMPConstruct *);
+  void ClearLabels();
   void CheckMultipleOccurrence(semantics::UnorderedSymbolSet &listVars,
       const std::list<parser::Name> &nameList, const parser::CharBlock &item,
       const std::string &clauseName);
@@ -399,14 +420,15 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   parser::CharBlock visitedAtomicSource_;
   SymbolSourceMap deferredNonVariables_;
 
-  using LoopConstruct = std::variant<const parser::DoConstruct *,
-      const parser::OpenMPLoopConstruct *>;
-  std::vector<LoopConstruct> loopStack_;
+  // Stack of nested DO loops and OpenMP constructs.
+  // This is used to verify DO loop nest for DOACROSS, and branches into
+  // and out of OpenMP constructs.
+  std::vector<LoopOrConstruct> constructStack_;
   // Scopes for scoping units.
   std::vector<const Scope *> scopeStack_;
   // Stack of directive specifications (except for SECTION).
   // This is to allow visitor functions to see all specified clauses, since
-  // they are only recorded in DirContext as they are processed.
+  // they are only recorded in DirectiveContext as they are processed.
   std::vector<const parser::OmpDirectiveSpecification *> dirStack_;
 
   enum class PartKind : int {
@@ -416,8 +438,39 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
     ExecutionPart,
   };
   std::vector<PartKind> partStack_;
+
+  std::multimap<const parser::Label,
+      std::pair<parser::CharBlock, const parser::OpenMPConstruct *>>
+      sourceLabels_;
+  std::map<const parser::Label,
+      std::pair<parser::CharBlock, const parser::OpenMPConstruct *>>
+      targetLabels_;
+  parser::CharBlock currentStatementSource_;
 };
 
+template <typename A>
+void OmpStructureChecker::Enter(const parser::Statement<A> &statement) {
+  currentStatementSource_ = statement.source;
+  // Keep track of the labels in all the labelled statements
+  if (statement.label) {
+    auto label{statement.label.value()};
+    // Get the context to check if the labelled statement is in an
+    // enclosing OpenMP construct
+    auto *thisConstruct{GetCurrentConstruct()};
+    targetLabels_.emplace(
+        label, std::make_pair(currentStatementSource_, thisConstruct));
+    // Check if a statement that causes a jump to the 'label'
+    // has already been encountered
+    auto range{sourceLabels_.equal_range(label)};
+    for (auto it{range.first}; it != range.second; ++it) {
+      // Check if both the statement with 'label' and the statement that
+      // causes a jump to the 'label' are in the same scope
+      CheckLabelContext(it->second.first, currentStatementSource_,
+          it->second.second, thisConstruct);
+    }
+  }
+}
+
 /// Find a duplicate entry in the range, and return an iterator to it.
 /// If there are no duplicate entries, return nullopt.
 template <typename LessTy, typename RangeTy, typename IterTy>

diff  --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index bc76f1f8cd52c..278c2765949e3 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -80,11 +80,6 @@ template <typename T> class DirectiveAttributeVisitor {
     CHECK(!dirContext_.empty());
     return dirContext_.back();
   }
-  std::optional<DirContext> GetContextIf() {
-    return dirContext_.empty()
-        ? std::nullopt
-        : std::make_optional<DirContext>(dirContext_.back());
-  }
   void PushContext(const parser::CharBlock &source, T dir, Scope &scope) {
     if constexpr (std::is_same_v<T, llvm::acc::Directive>) {
       dirContext_.emplace_back(source, dir, scope);
@@ -477,29 +472,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
   template <typename A> bool Pre(const A &) { return true; }
   template <typename A> void Post(const A &) {}
 
-  template <typename A> bool Pre(const parser::Statement<A> &statement) {
-    currentStatementSource_ = statement.source;
-    // Keep track of the labels in all the labelled statements
-    if (statement.label) {
-      auto label{statement.label.value()};
-      // Get the context to check if the labelled statement is in an
-      // enclosing OpenMP construct
-      std::optional<DirContext> thisContext{GetContextIf()};
-      targetLabels_.emplace(
-          label, std::make_pair(currentStatementSource_, thisContext));
-      // Check if a statement that causes a jump to the 'label'
-      // has already been encountered
-      auto range{sourceLabels_.equal_range(label)};
-      for (auto it{range.first}; it != range.second; ++it) {
-        // Check if both the statement with 'label' and the statement that
-        // causes a jump to the 'label' are in the same scope
-        CheckLabelContext(it->second.first, currentStatementSource_,
-            it->second.second, thisContext);
-      }
-    }
-    return true;
-  }
-
   bool Pre(const parser::SpecificationPart &) {
     partStack_.push_back(PartKind::SpecificationPart);
     return true;
@@ -512,18 +484,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
   }
   void Post(const parser::ExecutionPart &) { partStack_.pop_back(); }
 
-  bool Pre(const parser::InternalSubprogram &) {
-    // Clear the labels being tracked in the previous scope
-    ClearLabels();
-    return true;
-  }
-
-  bool Pre(const parser::ModuleSubprogram &) {
-    // Clear the labels being tracked in the previous scope
-    ClearLabels();
-    return true;
-  }
-
   bool Pre(const parser::StmtFunctionStmt &x) {
     const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)};
     if (const auto *expr{GetExpr(context_, parsedExpr)}) {
@@ -917,30 +877,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
 
   void Post(const parser::Name &);
 
-  // Keep track of labels in the statements that causes jumps to target labels
-  void Post(const parser::GotoStmt &gotoStmt) { CheckSourceLabel(gotoStmt.v); }
-  void Post(const parser::ComputedGotoStmt &computedGotoStmt) {
-    for (auto &label : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
-      CheckSourceLabel(label);
-    }
-  }
-  void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
-    CheckSourceLabel(std::get<1>(arithmeticIfStmt.t));
-    CheckSourceLabel(std::get<2>(arithmeticIfStmt.t));
-    CheckSourceLabel(std::get<3>(arithmeticIfStmt.t));
-  }
-  void Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
-    for (auto &label : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
-      CheckSourceLabel(label);
-    }
-  }
-  void Post(const parser::AltReturnSpec &altReturnSpec) {
-    CheckSourceLabel(altReturnSpec.v);
-  }
-  void Post(const parser::ErrLabel &errLabel) { CheckSourceLabel(errLabel.v); }
-  void Post(const parser::EndLabel &endLabel) { CheckSourceLabel(endLabel.v); }
-  void Post(const parser::EorLabel &eorLabel) { CheckSourceLabel(eorLabel.v); }
-
   void ResolveOmpObjectsForMapClause(
       Symbol::Flag mapFlag, const parser::OmpObjectList &objList) {
     for (const auto &ompObj : objList.v) {
@@ -1080,13 +1016,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
       Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate};
 
   UnorderedSymbolSet stmtFunctionExprSymbols_;
-  std::multimap<const parser::Label,
-      std::pair<parser::CharBlock, std::optional<DirContext>>>
-      sourceLabels_;
-  std::map<const parser::Label,
-      std::pair<parser::CharBlock, std::optional<DirContext>>>
-      targetLabels_;
-  parser::CharBlock currentStatementSource_;
 
   enum class PartKind : int {
     // There are also other "parts", such as internal-subprogram-part, etc,
@@ -1122,13 +1051,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
       const parser::Name &, const Symbol &, Symbol::Flag);
   void CheckObjectIsPrivatizable(
       const parser::Name &, const Symbol &, Symbol::Flag);
-  void CheckSourceLabel(const parser::Label &);
-  void CheckLabelContext(const parser::CharBlock, const parser::CharBlock,
-      std::optional<DirContext>, std::optional<DirContext>);
-  void ClearLabels() {
-    sourceLabels_.clear();
-    targetLabels_.clear();
-  };
 
   void AddOmpRequiresToScope(Scope &,
       const WithOmpDeclarative::RequiresClauses *,
@@ -3234,60 +3156,6 @@ void OmpAttributeVisitor::CheckObjectIsPrivatizable(
   }
 }
 
-void OmpAttributeVisitor::CheckSourceLabel(const parser::Label &label) {
-  // Get the context to check if the statement causing a jump to the 'label' is
-  // in an enclosing OpenMP construct
-  std::optional<DirContext> thisContext{GetContextIf()};
-  sourceLabels_.emplace(
-      label, std::make_pair(currentStatementSource_, thisContext));
-  // Check if the statement with 'label' to which a jump is being introduced
-  // has already been encountered
-  auto it{targetLabels_.find(label)};
-  if (it != targetLabels_.end()) {
-    // Check if both the statement with 'label' and the statement that causes a
-    // jump to the 'label' are in the same scope
-    CheckLabelContext(currentStatementSource_, it->second.first, thisContext,
-        it->second.second);
-  }
-}
-
-// Check for invalid branch into or out of OpenMP structured blocks
-void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
-    const parser::CharBlock target, std::optional<DirContext> sourceContext,
-    std::optional<DirContext> targetContext) {
-  auto dirContextsSame = [](DirContext &lhs, DirContext &rhs) -> bool {
-    // Sometimes nested constructs share a scope but are 
diff erent contexts.
-    // The directiveSource comparison is for OmpSection. Sections do not have
-    // their own scopes and two 
diff erent sections both have the same directive.
-    // Their source however is 
diff erent. This string comparison is unfortunate
-    // but should only happen for GOTOs inside of SECTION.
-    return (lhs.scope == rhs.scope) && (lhs.directive == rhs.directive) &&
-        (lhs.directiveSource == rhs.directiveSource);
-  };
-  unsigned version{context_.langOptions().OpenMPVersion};
-  if (targetContext &&
-      (!sourceContext ||
-          (!dirContextsSame(*targetContext, *sourceContext) &&
-              !DoesScopeContain(
-                  &targetContext->scope, sourceContext->scope)))) {
-    context_
-        .Say(source, "invalid branch into an OpenMP structured block"_err_en_US)
-        .Attach(target, "In the enclosing %s directive branched into"_en_US,
-            parser::omp::GetUpperName(targetContext->directive, version));
-  }
-  if (sourceContext &&
-      (!targetContext ||
-          (!dirContextsSame(*sourceContext, *targetContext) &&
-              !DoesScopeContain(
-                  &sourceContext->scope, targetContext->scope)))) {
-    context_
-        .Say(source,
-            "invalid branch leaving an OpenMP structured block"_err_en_US)
-        .Attach(target, "Outside the enclosing %s directive"_en_US,
-            parser::omp::GetUpperName(sourceContext->directive, version));
-  }
-}
-
 void OmpAttributeVisitor::AddOmpRequiresToScope(Scope &scope,
     const WithOmpDeclarative::RequiresClauses *reqs,
     const common::OmpMemoryOrderType *memOrder) {

diff  --git a/flang/test/Semantics/OpenMP/parallel-master-goto.f90 b/flang/test/Semantics/OpenMP/parallel-master-goto.f90
index 01d14aaa46d30..4adc38728fb8f 100644
--- a/flang/test/Semantics/OpenMP/parallel-master-goto.f90
+++ b/flang/test/Semantics/OpenMP/parallel-master-goto.f90
@@ -4,7 +4,6 @@
 !$omp parallel
 do i = 1, 2
 !ERROR: invalid branch into an OpenMP structured block
-!ERROR: invalid branch leaving an OpenMP structured block
   goto 10
 end do
 !$omp master


        


More information about the flang-commits mailing list