[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