[flang-commits] [flang] [llvm] [flang][OpenMP] Move clause validity checks into OpenMP-specific code (PR #205607)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Wed Jun 24 16:06:35 PDT 2026
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/205607
>From b3db32fcef2be68b114bbf39675f9f68defe8ebd Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 18 Jun 2026 08:45:13 -0500
Subject: [PATCH 1/2] [flang][OpenMP] Move clause validity checks into
OpenMP-specific code
The checks for syntactic properties of clauses (e.g. uniqueness, being
required, etc.) were originally handled by infrastructure common to
OpenMP and OpenACC. That infrastructure, however, is not fully equipped
to handle OpenMP needs: being unable to express version-based properties
or clause set properties being two prominent examples.
The first step towards fulfilling the OpenMP requirements it is to
transfer the handling of clause validity checks into OpenMP-specific
code, which can then be modified without interfering with OpenACC.
In addition to that, this PR also changes the way that clauses on end-
directives are handled: first, a clause appearing on an end-directive
is checked to be allowed to appear on an end-directive, then all clauses
from the begin- and the end-directives are tested together. This unifies
checks for uniqueness of clauses that can appear in both places.
---
flang/lib/Semantics/check-omp-loop.cpp | 6 +
flang/lib/Semantics/check-omp-structure.cpp | 283 ++++++++++++------
flang/lib/Semantics/check-omp-structure.h | 20 +-
flang/test/Semantics/OpenMP/cancel.f90 | 4 +-
.../Semantics/OpenMP/clause-validity01.f90 | 13 +-
.../Semantics/OpenMP/clause-validity02.f90 | 1 +
.../OpenMP/declarative-directive01.f90 | 2 +-
.../Semantics/OpenMP/device-constructs.f90 | 2 +-
flang/test/Semantics/OpenMP/sections01.f90 | 2 +-
flang/test/Semantics/OpenMP/single04.f90 | 2 +-
flang/test/Semantics/OpenMP/workshare02.f90 | 2 +-
llvm/include/llvm/Frontend/OpenMP/OMP.h | 10 +
12 files changed, 235 insertions(+), 112 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index c77c1c53f4813..fcba3b82111f0 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -414,6 +414,12 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
HasInvalidWorksharingNesting(beginName, llvm::omp::nestedWorkshareErrSet);
}
+ if (llvm::omp::allTaskloopSet.test(beginName.v)) {
+ OmpClauseSet exclusive{
+ llvm::omp::Clause::OMPC_grainsize, llvm::omp::Clause::OMPC_num_tasks};
+ CheckExclusiveClauses(exclusive, x.BeginDir());
+ }
+
for (auto &construct : std::get<parser::Block>(x.t)) {
if (const auto *doConstruct{parser::omp::GetDoConstruct(construct)}) {
const auto &doBlock{std::get<parser::Block>(doConstruct->t)};
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 81600fa1ddbb9..98a286e85c94f 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -348,7 +348,12 @@ bool OmpStructureChecker::IsAllowedClause(llvm::omp::Clause clauseId) {
GetContext().directive, clauseId, context_.langOptions().OpenMPVersion);
}
-bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clause) {
+bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clauseId) {
+ return true;
+}
+
+bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clauseId,
+ parser::CharBlock clauseSource, llvm::omp::Directive dirId) {
// Do not do clause checks while processing METADIRECTIVE.
// Context selectors can contain clauses that are not given as a part
// of a construct, but as trait properties. Testing whether they are
@@ -360,16 +365,14 @@ bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clause) {
}
unsigned version{context_.langOptions().OpenMPVersion};
- DirectiveContext &dirCtx = GetContext();
- llvm::omp::Directive dir{dirCtx.directive};
- if (!llvm::omp::isAllowedClauseForDirective(dir, clause, version)) {
+ if (!llvm::omp::isAllowedClauseForDirective(dirId, clauseId, version)) {
unsigned allowedInVersion{[&] {
for (unsigned v : llvm::omp::getOpenMPVersions()) {
if (v <= version) {
continue;
}
- if (llvm::omp::isAllowedClauseForDirective(dir, clause, v)) {
+ if (llvm::omp::isAllowedClauseForDirective(dirId, clauseId, v)) {
return v;
}
}
@@ -379,14 +382,19 @@ bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clause) {
// Only report it if there is a later version that allows it.
// If it's not allowed at all, it will be reported by CheckAllowed.
if (allowedInVersion != 0) {
- context_.Say(dirCtx.clauseSource,
+ context_.Say(clauseSource,
"%s clause is not allowed on directive %s in %s, %s"_err_en_US,
- parser::omp::GetUpperName(clause, version),
- parser::omp::GetUpperName(dir, version), ThisVersion(version),
- TryVersion(allowedInVersion));
+ GetUpperName(clauseId, version), GetUpperName(dirId, version),
+ ThisVersion(version), TryVersion(allowedInVersion));
+ } else {
+ context_.Say(clauseSource,
+ "%s clause is not allowed on the %s directive"_err_en_US,
+ GetUpperName(clauseId, version), GetUpperName(dirId, version));
}
+ return false;
}
- return CheckAllowed(clause);
+
+ return true;
}
void OmpStructureChecker::AnalyzeObject(const parser::OmpObject &object) {
@@ -855,6 +863,150 @@ void OmpStructureChecker::CheckDirectiveDeprecation(
// one another, but only the top-level directive should cause a warning.
}
+std::pair<const parser::OmpClause *, const parser::OmpClause *>
+OmpStructureChecker::FindMutuallyExclusiveClauses(
+ OmpClauseSet exclusive, const parser::OmpClauseList &clauses) {
+ const parser::OmpClause *first{nullptr};
+ for (const parser::OmpClause &clause : clauses.v) {
+ llvm::omp::Clause clauseId{clause.Id()};
+ if (!exclusive.test(clauseId)) {
+ continue;
+ }
+ if (first) {
+ llvm::omp::Clause firstId{first->Id()};
+ if (clauseId < firstId) {
+ return std::make_pair(&clause, first);
+ } else if (clauseId > firstId) {
+ return std::make_pair(first, &clause);
+ }
+ } else {
+ first = &clause;
+ }
+ }
+ return std::make_pair(nullptr, nullptr);
+}
+
+void OmpStructureChecker::CheckExclusiveClauses(
+ OmpClauseSet exclusive, const parser::OmpDirectiveSpecification &spec) {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ auto pair{FindMutuallyExclusiveClauses(exclusive, spec.Clauses())};
+
+ if (pair.first && pair.second) {
+ std::string firstName{GetUpperName(pair.first->Id(), version)};
+ std::string secondName{GetUpperName(pair.second->Id(), version)};
+ context_
+ .Say(pair.second->source,
+ "%s and %s clauses are mutually exclusive and may not appear on the same %s directive"_err_en_US,
+ firstName, secondName, GetUpperName(spec.DirId(), version))
+ .Attach(pair.first->source, "%s clause was specified here"_en_US,
+ firstName);
+ }
+}
+
+void OmpStructureChecker::CheckClauses(parser::OmpDirectiveName dirName,
+ llvm::iterator_range<ClauseIterator> beginClauses,
+ llvm::iterator_range<ClauseIterator> endClauses) {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ llvm::omp::Directive dirId{dirName.v};
+ std::vector<const parser::OmpClause *> allClauses;
+
+ auto addClause{[&](const parser::OmpClause &clause) {
+ llvm::omp::Clause clauseId{clause.Id()};
+ AddClauseToCrtContext(clauseId);
+ SetContextClause(clause);
+ SetContextClauseInfo(clauseId);
+ allClauses.push_back(&clause);
+ }};
+
+ for (const parser::OmpClause &clause : beginClauses) {
+ addClause(clause);
+ }
+ for (const parser::OmpClause &clause : endClauses) {
+ llvm::omp::Clause clauseId{clause.Id()};
+ if (llvm::omp::isEndClause(clauseId)) {
+ addClause(clause);
+ } else {
+ context_.Say(clause.source,
+ "%s clause is not allowed on an end-directive"_err_en_US,
+ GetUpperName(clauseId, version));
+ }
+ }
+
+ OmpClauseSet notAllowed;
+
+ for (const parser::OmpClause *clause : allClauses) {
+ llvm::omp::Clause clauseId{clause->Id()};
+ switch (clauseId) {
+ // Special cases.
+ case llvm::omp::Clause::OMPC_cancellation_construct_type:
+ case llvm::omp::Clause::OMPC_ompx_bare:
+ continue;
+ default:
+ break;
+ }
+ if (!CheckAllowedClause(clauseId, clause->source, dirId)) {
+ notAllowed.set(clauseId);
+ }
+ }
+
+ std::multimap<llvm::omp::Clause, parser::CharBlock> present;
+ // Exclusive clauses aren't necessarily unique, but there is no way
+ // to specify a clause in both sets right now, and all clauses currently
+ // listed as exclusive also happen to be unique.
+ OmpClauseSet uniqueSet{//
+ directiveClausesMap_[dirId].allowedOnce |
+ directiveClausesMap_[dirId].allowedExclusive};
+
+ for (const parser::OmpClause *clause : allClauses) {
+ llvm::omp::Clause clauseId{clause->Id()};
+ if (notAllowed.test(clauseId)) {
+ continue;
+ }
+ // Special case.
+ if (clauseId == llvm::omp::Clause::OMPC_cancellation_construct_type) {
+ continue;
+ }
+ auto range{llvm::make_range(present.equal_range(clauseId))};
+ if (uniqueSet.test(clauseId)) {
+ // Only report any repeated clause once.
+ if (std::distance(range.begin(), range.end()) == 1) {
+ std::string clauseName{GetUpperName(clauseId, version)};
+ context_
+ .Say(clause->source,
+ "At most one %s clause can appear on the %s directive"_err_en_US,
+ clauseName, GetUpperName(dirId, version))
+ .Attach(range.begin()->second,
+ "%s clause was first specified here"_en_US, clauseName);
+ }
+ }
+ present.insert(std::make_pair(clauseId, clause->source));
+ }
+
+ bool requiredPresent{false};
+ // Prepare the requiredSet relevant to the current OpenMP version.
+ OmpClauseSet requiredSet;
+ directiveClausesMap_[dirId].requiredOneOf.IterateOverMembers( //
+ [&](llvm::omp::Clause id) {
+ if (IsAllowedClause(id)) {
+ requiredSet.set(id);
+ }
+ });
+ requiredSet.IterateOverMembers( //
+ [&](llvm::omp::Clause id) {
+ if (!requiredPresent && present.count(id) != 0) {
+ requiredPresent = true;
+ }
+ });
+
+ if (!requiredPresent && !requiredSet.empty()) {
+ context_.Say(dirName.source,
+ "At least one of %s %s must appear on the %s directive"_err_en_US,
+ ClauseSetToString(requiredSet),
+ requiredSet.count() == 1 ? "clause" : "clauses",
+ GetUpperName(dirName.v, version));
+ }
+}
+
void OmpStructureChecker::CheckMultipleOccurrence(
semantics::UnorderedSymbolSet &listVars,
const std::list<parser::Name> &nameList, const parser::CharBlock &item,
@@ -1066,6 +1218,26 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
dirStack_.push_back(&GetOmpDirectiveSpecification(x));
CheckDirectiveDeprecation(x);
+ // Verify clauses
+ common::visit(
+ [&](auto &&s) {
+ using TypeS = llvm::remove_cvref_t<decltype(s)>;
+ if constexpr ( //
+ std::is_base_of_v<parser::OmpBlockConstruct, TypeS> ||
+ std::is_same_v<parser::OpenMPSectionsConstruct, TypeS>) {
+ if (auto &endSpec{s.EndDir()}) {
+ CheckClauses(dirName,
+ llvm::iterator_range(s.BeginDir().Clauses().v),
+ llvm::iterator_range(endSpec->Clauses().v));
+ return;
+ }
+ }
+ CheckClauses(dirName,
+ llvm::iterator_range(dirStack_.back()->Clauses().v),
+ llvm::iterator_range(std::list<parser::OmpClause>{}));
+ },
+ x.u);
+
// Simd Construct with Ordered Construct Nesting check.
if (CurrentDirectiveIsNested()) {
if (GetDirectiveNest(SIMDNest) > 0) {
@@ -1097,6 +1269,10 @@ void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
parser::OmpDirectiveName dirName{GetOmpDirectiveName(x)};
PushContextAndClauseSets(dirName.source, dirName.v);
dirStack_.push_back(&GetOmpDirectiveSpecification(x));
+
+ CheckClauses(dirName, llvm::iterator_range(dirStack_.back()->Clauses().v),
+ llvm::iterator_range(std::list<parser::OmpClause>{}));
+
EnterDirectiveNest(DeclarativeNest);
}
@@ -1400,15 +1576,6 @@ void OmpStructureChecker::CheckSingleConstruct(
last = std::make_pair(symbol, source);
}
- if (!nowaitSource1.empty() && !nowaitSource2.empty()) {
- context_
- .Say(nowaitSource2,
- // Match the message text with the one emitted by "CheckAllowed".
- "At most one %s clause can appear on the %s directive"_err_en_US,
- nowaitName, singleName)
- .Attach(nowaitSource1, "Previous occurrence of %s"_en_US, nowaitName);
- }
-
if (version <= 52 && !copyPrivateSyms.empty() &&
(!nowaitSource1.empty() || !nowaitSource2.empty())) {
parser::CharBlock source{
@@ -1564,31 +1731,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
llvm::omp::nestedWorkshareErrSet);
}
-void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
- const parser::OmpDirectiveName &dirName{x.DirName()};
- ResetPartialContext(dirName.source);
- switch (dirName.v) {
- // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
- case llvm::omp::Directive::OMPD_sections:
- PushContextAndClauseSets(
- dirName.source, llvm::omp::Directive::OMPD_end_sections);
- break;
- default:
- // no clauses are allowed
- break;
- }
-}
-
-// TODO: Verify the popping of dirContext requirement after nowait
-// implementation, as there is an implicit barrier at the end of the worksharing
-// constructs unless a nowait clause is specified. Only OMPD_end_sections is
-// popped becuase it is pushed while entering the EndSectionsDirective.
-void OmpStructureChecker::Leave(const parser::OmpEndSectionsDirective &x) {
- if (GetContext().directive == llvm::omp::Directive::OMPD_end_sections) {
- dirContext_.pop_back();
- }
-}
-
void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar(
const parser::Designator &designator) {
auto *name{parser::Unwrap<parser::Name>(designator)};
@@ -1780,6 +1922,10 @@ void OmpStructureChecker::Enter(const parser::OmpDeclareSimdDirective &x) {
}
}
+ OmpClauseSet exclusive{
+ llvm::omp::Clause::OMPC_inbranch, llvm::omp::Clause::OMPC_notinbranch};
+ CheckExclusiveClauses(exclusive, x.v);
+
for (const parser::OmpClause &clause : x.v.Clauses().v) {
const auto *u = std::get_if<parser::OmpClause::Uniform>(&clause.u);
if (!u) {
@@ -3208,7 +3354,7 @@ std::optional<llvm::omp::Directive> OmpStructureChecker::GetCancelType(
if (auto *cctype{std::get_if<CancellationConstructType>(&clause.u)}) {
if (cancelee) {
context_.Say(cancelSource,
- "Multiple cancel-directive-name clauses are not allowed on the %s construct"_err_en_US,
+ "Multiple cancel-directive-name clauses are not allowed on %s construct"_err_en_US,
cancelName);
return std::nullopt;
}
@@ -3331,51 +3477,6 @@ void OmpStructureChecker::CheckCancellationNest(
}
}
-void OmpStructureChecker::Enter(const parser::OmpEndDirective &x) {
- parser::CharBlock source{x.DirName().source};
- ResetPartialContext(source);
- switch (x.DirId()) {
- case llvm::omp::Directive::OMPD_scope:
- PushContextAndClauseSets(source, llvm::omp::Directive::OMPD_end_scope);
- break;
- // 2.7.3 end-single-clause -> copyprivate-clause |
- // nowait-clause
- case llvm::omp::Directive::OMPD_single:
- PushContextAndClauseSets(source, llvm::omp::Directive::OMPD_end_single);
- break;
- // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
- case llvm::omp::Directive::OMPD_workshare:
- PushContextAndClauseSets(source, llvm::omp::Directive::OMPD_end_workshare);
- break;
- // 2.7.1 end-do -> END DO [nowait-clause]
- // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
- case llvm::omp::Directive::OMPD_do:
- PushContextAndClauseSets(source, llvm::omp::Directive::OMPD_end_do);
- break;
- case llvm::omp::Directive::OMPD_do_simd:
- PushContextAndClauseSets(source, llvm::omp::Directive::OMPD_end_do_simd);
- break;
- default:
- // no clauses are allowed
- break;
- }
-}
-
-// TODO: Verify the popping of dirContext requirement after nowait
-// implementation, as there is an implicit barrier at the end of the worksharing
-// constructs unless a nowait clause is specified. Only OMPD_end_single and
-// end_workshareare popped as they are pushed while entering the
-// EndBlockDirective.
-void OmpStructureChecker::Leave(const parser::OmpEndDirective &x) {
- if ((GetContext().directive == llvm::omp::Directive::OMPD_end_scope) ||
- (GetContext().directive == llvm::omp::Directive::OMPD_end_single) ||
- (GetContext().directive == llvm::omp::Directive::OMPD_end_workshare) ||
- (GetContext().directive == llvm::omp::Directive::OMPD_end_do) ||
- (GetContext().directive == llvm::omp::Directive::OMPD_end_do_simd)) {
- dirContext_.pop_back();
- }
-}
-
void OmpStructureChecker::Enter(const parser::OmpClauseList &) {
ifLeafs_.clear();
}
@@ -3608,8 +3709,6 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &x) {
firstClause = clause;
}
}
-
- CheckRequireAtLeastOneOf();
}
void OmpStructureChecker::Enter(const parser::OmpClause &x) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 4499e2a213384..8de0216d024e5 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -19,6 +19,7 @@
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/openmp-directive-sets.h"
#include "flang/Semantics/semantics.h"
+#include "llvm/ADT/iterator_range.h"
using OmpClauseSet =
Fortran::common::EnumSet<llvm::omp::Clause, llvm::omp::Clause_enumSize>;
@@ -101,12 +102,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void Leave(const parser::OmpBlockConstruct &);
void Enter(const parser::OmpBeginDirective &);
void Leave(const parser::OmpBeginDirective &);
- void Enter(const parser::OmpEndDirective &);
- void Leave(const parser::OmpEndDirective &);
void Enter(const parser::OpenMPSectionsConstruct &);
- void Enter(const parser::OmpEndSectionsDirective &);
- void Leave(const parser::OmpEndSectionsDirective &);
void Enter(const parser::OmpDeclareVariantDirective &);
void Enter(const parser::OmpDeclareSimdDirective &);
@@ -251,13 +248,25 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
// check-omp-structure.cpp
+ using ClauseIterator =
+ decltype(std::declval<const parser::OmpClauseList>().v.begin());
bool IsAllowedClause(llvm::omp::Clause clauseId);
- bool CheckAllowedClause(llvm::omp::Clause clause);
+ bool CheckAllowedClause(llvm::omp::Clause clauseId);
+ bool CheckAllowedClause(llvm::omp::Clause clauseId,
+ parser::CharBlock clauseSource, llvm::omp::Directive dirId);
void CheckArgumentObjectKind(const parser::OmpClause &x);
void CheckDirectiveSpelling(
parser::CharBlock spelling, llvm::omp::Directive id);
void CheckDirectiveDeprecation(const parser::OpenMPConstruct &x);
+ void CheckClauses(parser::OmpDirectiveName dirName,
+ llvm::iterator_range<ClauseIterator> beginClauses,
+ llvm::iterator_range<ClauseIterator> endClauses);
void AnalyzeObject(const parser::OmpObject &object);
+ std::pair<const parser::OmpClause *, const parser::OmpClause *>
+ FindMutuallyExclusiveClauses(
+ OmpClauseSet exclusive, const parser::OmpClauseList &clauses);
+ void CheckExclusiveClauses(
+ OmpClauseSet exclusive, const parser::OmpDirectiveSpecification &spec);
const parser::OpenMPConstruct *GetCurrentConstruct() const;
void CheckSourceLabel(const parser::Label &);
@@ -460,6 +469,5 @@ std::optional<IterTy> OmpStructureChecker::FindDuplicate(RangeTy &&range) {
}
return std::nullopt;
}
-
} // namespace Fortran::semantics
#endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
diff --git a/flang/test/Semantics/OpenMP/cancel.f90 b/flang/test/Semantics/OpenMP/cancel.f90
index 6f3a255312ccf..6147d581c4e69 100644
--- a/flang/test/Semantics/OpenMP/cancel.f90
+++ b/flang/test/Semantics/OpenMP/cancel.f90
@@ -9,7 +9,7 @@ subroutine f00
subroutine f01
!$omp parallel
-!ERROR: Multiple cancel-directive-name clauses are not allowed on the CANCEL construct
+!ERROR: Multiple cancel-directive-name clauses are not allowed on CANCEL construct
!$omp cancel parallel parallel
!$omp end parallel
end
@@ -23,7 +23,7 @@ subroutine f02
subroutine f03
!$omp parallel
-!ERROR: Multiple cancel-directive-name clauses are not allowed on the CANCELLATION POINT construct
+!ERROR: Multiple cancel-directive-name clauses are not allowed on CANCELLATION POINT construct
!$omp cancellation point parallel parallel
!$omp end parallel
end
diff --git a/flang/test/Semantics/OpenMP/clause-validity01.f90 b/flang/test/Semantics/OpenMP/clause-validity01.f90
index ec89afd53ab0d..aebb08bc71482 100644
--- a/flang/test/Semantics/OpenMP/clause-validity01.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity01.f90
@@ -121,7 +121,7 @@
do i = 1, N
a = 3.14
enddo
- !ERROR: NUM_THREADS clause is not allowed on the END PARALLEL directive
+ !ERROR: NUM_THREADS clause is not allowed on an end-directive
!$omp end parallel num_threads(4)
!ERROR: LASTPRIVATE clause is not allowed on the PARALLEL directive
@@ -145,7 +145,7 @@
do i = 1, N
a = 3.14
enddo
- !ERROR: NOWAIT clause is not allowed on the END PARALLEL directive
+ !ERROR: NOWAIT clause is not allowed on the PARALLEL directive
!$omp end parallel nowait
!$omp parallel num_threads(num-10)
@@ -285,7 +285,7 @@
!$omp section
c = 1
d = 2
- !ERROR: NUM_THREADS clause is not allowed on the END SECTIONS directive
+ !ERROR: NUM_THREADS clause is not allowed on an end-directive
!$omp end sections num_threads(4)
!$omp parallel
@@ -294,7 +294,7 @@
!$omp section
c = 1
d = 2
- !ERROR: At most one NOWAIT clause can appear on the END SECTIONS directive
+ !ERROR: At most one NOWAIT clause can appear on the SECTIONS directive
!$omp end sections nowait nowait
!$omp end parallel
@@ -319,7 +319,7 @@
!$omp end sections
!$omp parallel sections
- !ERROR: NOWAIT clause is not allowed on the END PARALLEL SECTIONS directive
+ !ERROR: NOWAIT clause is not allowed on the PARALLEL SECTIONS directive
!$omp end parallel sections nowait
! 2.7.3 single-clause -> private-clause |
@@ -335,7 +335,6 @@
a = 3.14
!ERROR: COPYPRIVATE variable 'a' may not appear on a PRIVATE or FIRSTPRIVATE clause on a SINGLE construct
!ERROR: At most one NOWAIT clause can appear on the SINGLE directive
- !ERROR: At most one NOWAIT clause can appear on the END SINGLE directive
!$omp end single copyprivate(a) nowait nowait
c = 2
!$omp end parallel
@@ -349,7 +348,7 @@
!ERROR: NUM_THREADS clause is not allowed on the WORKSHARE directive
!$omp workshare num_threads(4)
a = 1.0
- !ERROR: COPYPRIVATE clause is not allowed on the END WORKSHARE directive
+ !ERROR: COPYPRIVATE clause is not allowed on the WORKSHARE directive
!$omp end workshare nowait copyprivate(a)
!$omp workshare nowait
!$omp end workshare
diff --git a/flang/test/Semantics/OpenMP/clause-validity02.f90 b/flang/test/Semantics/OpenMP/clause-validity02.f90
index 7e61bf0fd3177..dd8823f234163 100644
--- a/flang/test/Semantics/OpenMP/clause-validity02.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity02.f90
@@ -3,6 +3,7 @@
subroutine bad_in_45(h_ptr)
integer, pointer :: h_ptr
!ERROR: USE_DEVICE_ADDR clause is not allowed on directive TARGET DATA in OpenMP v4.5, try -fopenmp-version=50
+ !ERROR: At least one of MAP, USE_DEVICE_PTR clauses must appear on the TARGET DATA directive
!$omp target data use_device_addr(h_ptr)
!$omp end target data
end
diff --git a/flang/test/Semantics/OpenMP/declarative-directive01.f90 b/flang/test/Semantics/OpenMP/declarative-directive01.f90
index 70c5618cc8760..39b9249aac7ea 100644
--- a/flang/test/Semantics/OpenMP/declarative-directive01.f90
+++ b/flang/test/Semantics/OpenMP/declarative-directive01.f90
@@ -38,7 +38,7 @@ end module m1
subroutine declare_simd_2
use m1
procedure (sub) sub1
- !ERROR: NOTINBRANCH and INBRANCH clauses are mutually exclusive and may not appear on the same DECLARE SIMD directive
+ !ERROR: INBRANCH and NOTINBRANCH clauses are mutually exclusive and may not appear on the same DECLARE SIMD directive
!$omp declare simd(sub1) inbranch notinbranch
procedure (sub), pointer::p
p=>sub1
diff --git a/flang/test/Semantics/OpenMP/device-constructs.f90 b/flang/test/Semantics/OpenMP/device-constructs.f90
index d74d720b2d35d..a1d1a811c2803 100644
--- a/flang/test/Semantics/OpenMP/device-constructs.f90
+++ b/flang/test/Semantics/OpenMP/device-constructs.f90
@@ -144,7 +144,7 @@ program main
cptr = c_null_ptr
!$omp end target data
- !ERROR: At least one of MAP, USE_DEVICE_ADDR, USE_DEVICE_PTR clause must appear on the TARGET DATA directive
+ !ERROR: At least one of MAP, USE_DEVICE_ADDR, USE_DEVICE_PTR clauses must appear on the TARGET DATA directive
!$omp target data device(0)
do i = 1, N
a = 3.14d0
diff --git a/flang/test/Semantics/OpenMP/sections01.f90 b/flang/test/Semantics/OpenMP/sections01.f90
index c26cc88dcc7af..166ef3d105d55 100644
--- a/flang/test/Semantics/OpenMP/sections01.f90
+++ b/flang/test/Semantics/OpenMP/sections01.f90
@@ -9,7 +9,7 @@ program omp_sections
!$omp sections
!$omp section
print *, "omp section"
- !ERROR: At most one NOWAIT clause can appear on the END SECTIONS directive
+ !ERROR: At most one NOWAIT clause can appear on the SECTIONS directive
!$omp end sections nowait nowait
end program omp_sections
diff --git a/flang/test/Semantics/OpenMP/single04.f90 b/flang/test/Semantics/OpenMP/single04.f90
index 58d6e6f0582cb..80e635f1e97c3 100644
--- a/flang/test/Semantics/OpenMP/single04.f90
+++ b/flang/test/Semantics/OpenMP/single04.f90
@@ -41,7 +41,7 @@ program single
!$omp single
print *, x
- !ERROR: At most one NOWAIT clause can appear on the END SINGLE directive
+ !ERROR: At most one NOWAIT clause can appear on the SINGLE directive
!$omp end single nowait nowait
!ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
diff --git a/flang/test/Semantics/OpenMP/workshare02.f90 b/flang/test/Semantics/OpenMP/workshare02.f90
index dddaa354fff9f..0f1f615917372 100644
--- a/flang/test/Semantics/OpenMP/workshare02.f90
+++ b/flang/test/Semantics/OpenMP/workshare02.f90
@@ -78,7 +78,7 @@ subroutine workshare(aa, bb, cc, dd, ee, ff, n)
!$omp workshare
j = j + 1
- !ERROR: At most one NOWAIT clause can appear on the END WORKSHARE directive
+ !ERROR: At most one NOWAIT clause can appear on the WORKSHARE directive
!$omp end workshare nowait nowait
end subroutine workshare
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.h b/llvm/include/llvm/Frontend/OpenMP/OMP.h
index 1faec3812412c..530f3f1410212 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.h
@@ -96,6 +96,16 @@ static constexpr inline bool isDataSharingAttributeClause(Clause C,
}
}
+static constexpr inline bool isEndClause(Clause C) {
+ switch (C) {
+ case OMPC_copyprivate:
+ case OMPC_nowait:
+ return true;
+ default:
+ return false;
+ }
+}
+
static constexpr unsigned FallbackVersion = 52;
LLVM_ABI ArrayRef<unsigned> getOpenMPVersions();
>From 0e99161ca0562b5e1cce03c4360932bd75740a35 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 24 Jun 2026 13:02:00 -0500
Subject: [PATCH 2/2] Remove unnecesary wording change
---
flang/lib/Semantics/check-omp-structure.cpp | 2 +-
flang/test/Semantics/OpenMP/cancel.f90 | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 98a286e85c94f..eec7607b52016 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -3354,7 +3354,7 @@ std::optional<llvm::omp::Directive> OmpStructureChecker::GetCancelType(
if (auto *cctype{std::get_if<CancellationConstructType>(&clause.u)}) {
if (cancelee) {
context_.Say(cancelSource,
- "Multiple cancel-directive-name clauses are not allowed on %s construct"_err_en_US,
+ "Multiple cancel-directive-name clauses are not allowed on the %s construct"_err_en_US,
cancelName);
return std::nullopt;
}
diff --git a/flang/test/Semantics/OpenMP/cancel.f90 b/flang/test/Semantics/OpenMP/cancel.f90
index 6147d581c4e69..6f3a255312ccf 100644
--- a/flang/test/Semantics/OpenMP/cancel.f90
+++ b/flang/test/Semantics/OpenMP/cancel.f90
@@ -9,7 +9,7 @@ subroutine f00
subroutine f01
!$omp parallel
-!ERROR: Multiple cancel-directive-name clauses are not allowed on CANCEL construct
+!ERROR: Multiple cancel-directive-name clauses are not allowed on the CANCEL construct
!$omp cancel parallel parallel
!$omp end parallel
end
@@ -23,7 +23,7 @@ subroutine f02
subroutine f03
!$omp parallel
-!ERROR: Multiple cancel-directive-name clauses are not allowed on CANCELLATION POINT construct
+!ERROR: Multiple cancel-directive-name clauses are not allowed on the CANCELLATION POINT construct
!$omp cancellation point parallel parallel
!$omp end parallel
end
More information about the flang-commits
mailing list