[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