[flang-commits] [flang] [llvm] [flang][OpenMP] Switch clause verification to descriptor-based (PR #229527)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Fri Oct 23 09:04:04 PDT 2026
================
@@ -326,6 +352,251 @@ static ResultTy VerifyUltimate(
return result;
}
+bool OmpStructureChecker::VerifyClauseVersion(
+ parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+ llvm::omp::Version maxVer{std::numeric_limits<int>::max()};
+ bool isCancel{dirName.v == llvm::omp::Directive::OMPD_cancel ||
+ dirName.v == llvm::omp::Directive::OMPD_cancellation_point};
+
+ auto result = VerifyVersions(info, dirName.v, version);
+
+ for (auto &[c, svr] : result) {
+ std::string cname{GetUpperName(c, version)};
+ std::string dname{GetUpperName(dirName.v, version)};
+ llvm::omp::Version since(svr.second.Min);
+ llvm::omp::Version until(svr.second.Max);
+
+ // Cancellation construct type clauses are directive names. They are
+ // only allowed on CANCEL and CANCELLATION_POINT directives. They may
+ // appear as byproducts of parsing an invalid directive name,
+ // e.g. SECTIONS PARALLEL, where SECTIONS will be the directive name,
+ // and PARALLEL will be a cancellation-construct-type clause.
+ // This may cause confusing error messages to be emitted, so deal with
+ // these cases separately.
+ if (!isCancel && c == llvm::omp::Clause::OMPC_cancellation_construct_type) {
+ context_.Say(svr.first, "%s cannot follow %s"_err_en_US,
+ parser::ToUpperCaseLetters(svr.first.ToString()), dname);
+ continue;
+ }
+
+ if (since == maxVer && until == 0u) {
+ context_.Say(svr.first,
+ "%s clause is not allowed on %s directive"_err_en_US, cname, dname);
+ } else if (since != maxVer && version < since) {
+ context_.Warn(common::UsageWarning::OpenMPFuture, svr.first,
+ "%s clause is not allowed on %s directive in %s, %s"_warn_en_US,
+ cname, dname, omp::ThisVersion(version), omp::TryVersion(since));
+ SetAllowedClauseOverride(c, dirName.v, since);
+ } else if (until != 0u && version > until) {
+ context_.Warn(common::UsageWarning::OpenMPDeprecated, svr.first,
+ "%s clause is no longer allowed on %s directive since %s"_warn_en_US,
+ cname, dname, omp::ThisVersion(NextVersion(until)));
+ SetAllowedClauseOverride(c, dirName.v);
+ }
+ }
+
+ return result.empty();
+}
+
+// In OpenMP 6.0+ the COMBINER clause is required on DECLARE_REDUCTION,
+// even though the old syntax (with the combiner expression inside the
+// directive argument) is still allowed.
+static bool missingCombiner(llvm::omp::Directive d, llvm::omp::Clause c) {
+ return d == llvm::omp::Directive::OMPD_declare_reduction &&
+ c == llvm::omp::Clause::OMPC_combiner;
+}
+
+bool OmpStructureChecker::VerifyClauseRequired(
+ parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+ auto result = VerifyRequired(info, dirName.v, version);
+
+ for (llvm::omp::Clause c : result.first) {
+ // Exceptions:
+ if (version >= 60 && missingCombiner(dirName.v, c)) {
+ continue;
+ }
+
+ context_.Say(dirName.source,
+ "%s clause is required on %s directive"_err_en_US,
+ GetUpperName(c, version), GetUpperName(dirName.v, version));
+ }
+
+ for (llvm::omp::ClauseSet s : result.second) {
+ // If the group is required, at least one clause from that group must
+ // be present.
+ // Note: The tricky part is that when a directive accepts a clause
+ // group, it may still have restrictions that exclude some members of
+ // that group. For example FLUSH accepts memory-order group, but not the
+ // RELAXED clause (note that the memory-order group is not "required").
+ // If such a restriction applied to a required group, we don't want to say
+ // Directive XYZ requires one of FOO, BAR or BAZ clauses
+ // and then
+ // BAZ clause is not allowed on XYZ directive
+ // This hasn't happened yet, but may happen in the future.
+ if (s != llvm::omp::ClauseSet::CancelDirectiveName) {
+ context_.Say(dirName.source, "%s is required on %s directive"_err_en_US,
+ OneOfClauses(s, version), GetUpperName(dirName.v, version));
+ } else {
+ // cancel-directive-name is somewhat special: the ClauseSet doesn't
+ // contain any actual clauses. Moreover, the clauses are cancellable
+ // directive names and have no separate definitions. They are encoded
+ // as directive ids inside OmpCancellationConstructTypeClause with the
+ // id OMPC_cancellation_construct_type.
+ context_.Say(dirName.source,
+ "One of '%s' clauses is required on %s directive"_err_en_US,
+ llvm::omp::getDescriptor(s).getName().str(),
+ GetUpperName(dirName.v, version));
+ }
+ }
+
+ return result.first.empty() && result.second.empty();
+}
+
+bool OmpStructureChecker::VerifyClauseUnique(
+ parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+ auto result = VerifyUnique(info, dirName.v, version);
+
+ for (auto [id, where] : result) {
+ context_
+ .Say(where.second,
+ "At most one %s clause can appear on %s directive"_err_en_US,
+ GetUpperName(id, version), GetUpperName(dirName.v, version))
+ .Attach(where.first, "previous occurrence of this clause"_en_US);
+ }
+ return result.empty();
+}
+
+bool OmpStructureChecker::VerifyClauseExclusive(
+ parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+ auto resultExcl = VerifyExclusive(info, dirName.v, version);
+
+ for (auto [id, wrong] : resultExcl) {
+ auto [otherId, source, otherSource] = wrong;
+ context_
+ .Say(source,
+ "%s clause cannot be specified together with a clause of a different type"_err_en_US,
+ GetUpperName(id, version))
+ .Attach(otherSource, "%s provided here"_en_US,
+ GetUpperName(otherId, version));
+ }
+
+ auto resultMut = VerifyMutuallyExclusive(info, dirName.v, version);
+
+ for (auto [id, wrong] : resultMut) {
+ auto [otherId, setId, source, otherSource] = wrong;
+ auto thisName{GetUpperName(id, version)};
+ std::string annot;
+ if (llvm::omp::isClauseGroup(setId)) {
+ auto &sdesc{llvm::omp::getDescriptor(setId)};
+ annot = " as members of '" + sdesc.getName().str() + "' clause group";
+ }
+ context_
+ .Say(otherSource,
+ "%s and %s clauses are mutually exclusive%s"_err_en_US,
+ GetUpperName(otherId, version), thisName, annot)
+ .Attach(source, "%s clause specified here"_en_US, thisName);
+ }
+
+ return resultExcl.empty() && resultMut.empty();
+}
+
+bool OmpStructureChecker::VerifyClauseUltimate(
+ parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+ llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+ auto result = VerifyUltimate(info, dirName.v, version, /*last=*/true);
----------------
kparzysz wrote:
Done
https://github.com/llvm/llvm-project/pull/229527
More information about the flang-commits
mailing list