[llvm-branch-commits] [flang] [flang] Provide "clause set" type as parameter to DirectiveStructureChecker (PR #211326)
Krzysztof Parzyszek via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Jul 23 06:02:36 PDT 2026
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/211326
>From 82801891b1ebcbd9b065a55c6afb4e85dd874fc1 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Tue, 21 Jul 2026 08:12:35 -0500
Subject: [PATCH] [flang] Provide "clause set" type as parameter to
DirectiveStructureChecker
This will remove the hardcoded dependence of DirectiveStructureChecker on
the common::EnumSet class. Both consumers of it will be able to use their
own type for the clause set.
The only complication was the ClauseSetToString member function, whose
implementation depended on the specifics of common::EnumSet, namely the
IterateOverMembers member function. It was moved out of the class, and
turned into a function template to make it possible to provide different
specializations for common::EnumSet and llvm::EnumSet.
---
flang/lib/Semantics/check-acc-structure.cpp | 12 ++
flang/lib/Semantics/check-acc-structure.h | 6 +-
.../lib/Semantics/check-directive-structure.h | 134 ++++++++----------
flang/lib/Semantics/check-omp-structure.cpp | 15 +-
flang/lib/Semantics/check-omp-structure.h | 6 +-
5 files changed, 95 insertions(+), 78 deletions(-)
diff --git a/flang/lib/Semantics/check-acc-structure.cpp b/flang/lib/Semantics/check-acc-structure.cpp
index e902a0b31df01..533cd367aa72b 100644
--- a/flang/lib/Semantics/check-acc-structure.cpp
+++ b/flang/lib/Semantics/check-acc-structure.cpp
@@ -63,6 +63,18 @@ static ReductionOpsSet reductionLogicalSet{
namespace Fortran::semantics {
+template <>
+std::string ClauseSetToString(const AccClauseSet &set,
+ std::function<llvm::StringRef(llvm::acc::Clause)> getName) {
+ std::string list;
+ set.IterateOverMembers([&](llvm::acc::Clause o) {
+ if (!list.empty())
+ list.append(", ");
+ list.append(parser::ToUpperCaseLetters(getName(o)));
+ });
+ return list;
+}
+
static constexpr inline AccClauseSet
computeConstructOnlyAllowedAfterDeviceTypeClauses{
llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
diff --git a/flang/lib/Semantics/check-acc-structure.h b/flang/lib/Semantics/check-acc-structure.h
index 31984519dd93d..667b43a269044 100644
--- a/flang/lib/Semantics/check-acc-structure.h
+++ b/flang/lib/Semantics/check-acc-structure.h
@@ -32,9 +32,13 @@ using AccClauseSet =
namespace Fortran::semantics {
+template <>
+std::string ClauseSetToString(const AccClauseSet &set,
+ std::function<llvm::StringRef(llvm::acc::Clause)> getName);
+
class AccStructureChecker
: public DirectiveStructureChecker<llvm::acc::Directive, llvm::acc::Clause,
- parser::AccClause, llvm::acc::Clause_enumSize> {
+ parser::AccClause, AccClauseSet> {
public:
AccStructureChecker(SemanticsContext &context)
: DirectiveStructureChecker(context,
diff --git a/flang/lib/Semantics/check-directive-structure.h b/flang/lib/Semantics/check-directive-structure.h
index 8b4c3be76098e..56d1208c7b660 100644
--- a/flang/lib/Semantics/check-directive-structure.h
+++ b/flang/lib/Semantics/check-directive-structure.h
@@ -17,18 +17,23 @@
#include "flang/Semantics/tools.h"
#include "llvm/ADT/iterator_range.h"
+#include <functional>
#include <set>
#include <unordered_map>
namespace Fortran::semantics {
-template <typename C, std::size_t ClauseEnumSize> struct DirectiveClauses {
- const common::EnumSet<C, ClauseEnumSize> allowed;
- const common::EnumSet<C, ClauseEnumSize> allowedOnce;
- const common::EnumSet<C, ClauseEnumSize> allowedExclusive;
- const common::EnumSet<C, ClauseEnumSize> requiredOneOf;
+template <typename ClauseSetTy> struct DirectiveClauses {
+ const ClauseSetTy allowed;
+ const ClauseSetTy allowedOnce;
+ const ClauseSetTy allowedExclusive;
+ const ClauseSetTy requiredOneOf;
};
+template <typename ClauseTy, typename ClauseSetTy>
+std::string ClauseSetToString(
+ const ClauseSetTy &set, std::function<llvm::StringRef(ClauseTy)> getName);
+
// Generic branching checker for invalid branching out of OpenMP/OpenACC
// directive.
// typename D is the directive enumeration.
@@ -204,11 +209,11 @@ template <typename D> class NoBranchingEnforce {
// typename D is the directive enumeration.
// typename C is the clause enumeration.
// typename PC is the parser class defined in parse-tree.h for the clauses.
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+template <typename D, typename C, typename PC, typename ClauseSetTy>
class DirectiveStructureChecker : public virtual BaseChecker {
protected:
DirectiveStructureChecker(SemanticsContext &context,
- const std::unordered_map<D, DirectiveClauses<C, ClauseEnumSize>>
+ const std::unordered_map<D, DirectiveClauses<ClauseSetTy>>
&directiveClausesMap)
: context_{context}, directiveClausesMap_(directiveClausesMap) {}
virtual ~DirectiveStructureChecker() {}
@@ -221,10 +226,10 @@ class DirectiveStructureChecker : public virtual BaseChecker {
parser::CharBlock directiveSource{nullptr};
parser::CharBlock clauseSource{nullptr};
D directive;
- common::EnumSet<C, ClauseEnumSize> allowedClauses{};
- common::EnumSet<C, ClauseEnumSize> allowedOnceClauses{};
- common::EnumSet<C, ClauseEnumSize> allowedExclusiveClauses{};
- common::EnumSet<C, ClauseEnumSize> requiredClauses{};
+ ClauseSetTy allowedClauses{};
+ ClauseSetTy allowedOnceClauses{};
+ ClauseSetTy allowedExclusiveClauses{};
+ ClauseSetTy requiredClauses{};
const PC *clause{nullptr};
ClauseMapTy clauseInfo;
@@ -265,21 +270,19 @@ class DirectiveStructureChecker : public virtual BaseChecker {
void SetContextDirectiveEnum(D dir) { GetContext().directive = dir; }
- void SetContextAllowed(const common::EnumSet<C, ClauseEnumSize> &allowed) {
+ void SetContextAllowed(const ClauseSetTy &allowed) {
GetContext().allowedClauses = allowed;
}
- void SetContextAllowedOnce(
- const common::EnumSet<C, ClauseEnumSize> &allowedOnce) {
+ void SetContextAllowedOnce(const ClauseSetTy &allowedOnce) {
GetContext().allowedOnceClauses = allowedOnce;
}
- void SetContextAllowedExclusive(
- const common::EnumSet<C, ClauseEnumSize> &allowedExclusive) {
+ void SetContextAllowedExclusive(const ClauseSetTy &allowedExclusive) {
GetContext().allowedExclusiveClauses = allowedExclusive;
}
- void SetContextRequired(const common::EnumSet<C, ClauseEnumSize> &required) {
+ void SetContextRequired(const ClauseSetTy &required) {
GetContext().requiredClauses = required;
}
@@ -378,7 +381,7 @@ class DirectiveStructureChecker : public virtual BaseChecker {
const parser::CharBlock &directiveSource);
// Check that only clauses in set are after the specific clauses.
- void CheckOnlyAllowedAfter(C clause, common::EnumSet<C, ClauseEnumSize> set);
+ void CheckOnlyAllowedAfter(C clause, ClauseSetTy set);
void CheckRequireAtLeastOneOf(bool warnInsteadOfError = false);
@@ -390,13 +393,11 @@ class DirectiveStructureChecker : public virtual BaseChecker {
// separator clause appears.
void CheckAllowedOncePerGroup(C clause, C separator);
- void CheckMutuallyExclusivePerGroup(
- C clause, C separator, common::EnumSet<C, ClauseEnumSize> set);
+ void CheckMutuallyExclusivePerGroup(C clause, C separator, ClauseSetTy set);
void CheckAtLeastOneClause();
- void CheckNotAllowedIfClause(
- C clause, common::EnumSet<C, ClauseEnumSize> set);
+ void CheckNotAllowedIfClause(C clause, ClauseSetTy set);
std::string ContextDirectiveAsFortran();
@@ -416,10 +417,7 @@ class DirectiveStructureChecker : public virtual BaseChecker {
SemanticsContext &context_;
std::vector<DirectiveContext> dirContext_; // used as a stack
- std::unordered_map<D, DirectiveClauses<C, ClauseEnumSize>>
- directiveClausesMap_;
-
- std::string ClauseSetToString(const common::EnumSet<C, ClauseEnumSize> set);
+ std::unordered_map<D, DirectiveClauses<ClauseSetTy>> directiveClausesMap_;
};
// Collect all labels defined in a block.
@@ -434,8 +432,8 @@ struct LabelCollector {
}
};
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckNoBranching(
+template <typename D, typename C, typename PC, typename ClauseSetTy>
+void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckNoBranching(
const parser::Block &block, D directive,
const parser::CharBlock &directiveSource) {
LabelCollector labelCollector;
@@ -449,9 +447,9 @@ void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckNoBranching(
// Check that only clauses included in the given set are present after the given
// clause.
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckOnlyAllowedAfter(
- C clause, common::EnumSet<C, ClauseEnumSize> set) {
+template <typename D, typename C, typename PC, typename ClauseSetTy>
+void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckOnlyAllowedAfter(
+ C clause, ClauseSetTy set) {
bool enforceCheck = false;
for (auto cl : GetContext().actualClauses) {
if (cl == clause) {
@@ -470,9 +468,8 @@ void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckOnlyAllowedAfter(
}
// Check that at least one clause is attached to the directive.
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-void DirectiveStructureChecker<D, C, PC,
- ClauseEnumSize>::CheckAtLeastOneClause() {
+template <typename D, typename C, typename PC, typename ClauseSetTy>
+void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckAtLeastOneClause() {
if (GetContext().actualClauses.empty()) {
context_.Say(GetContext().directiveSource,
"At least one clause is required on the %s directive"_err_en_US,
@@ -480,24 +477,11 @@ void DirectiveStructureChecker<D, C, PC,
}
}
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-std::string
-DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::ClauseSetToString(
- const common::EnumSet<C, ClauseEnumSize> set) {
- std::string list;
- set.IterateOverMembers([&](C o) {
- if (!list.empty())
- list.append(", ");
- list.append(parser::ToUpperCaseLetters(getClauseName(o).str()));
- });
- return list;
-}
-
// Check that at least one clause in the required set is present on the
// directive.
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-void DirectiveStructureChecker<D, C, PC,
- ClauseEnumSize>::CheckRequireAtLeastOneOf(bool warnInsteadOfError) {
+template <typename D, typename C, typename PC, typename ClauseSetTy>
+void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckRequireAtLeastOneOf(
+ bool warnInsteadOfError) {
if (GetContext().requiredClauses.empty()) {
return;
}
@@ -507,30 +491,31 @@ void DirectiveStructureChecker<D, C, PC,
}
}
// No clause matched in the actual clauses list
+ auto getName{[this](C c) { return getClauseName(c); }};
if (warnInsteadOfError) {
context_.Warn(common::UsageWarning::Portability,
GetContext().directiveSource,
"At least one of %s clause should appear on the %s directive"_port_en_US,
- ClauseSetToString(GetContext().requiredClauses),
+ ClauseSetToString<C>(GetContext().requiredClauses, getName),
ContextDirectiveAsFortran());
} else {
context_.Say(GetContext().directiveSource,
"At least one of %s clause must appear on the %s directive"_err_en_US,
- ClauseSetToString(GetContext().requiredClauses),
+ ClauseSetToString<C>(GetContext().requiredClauses, getName),
ContextDirectiveAsFortran());
}
}
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-std::string DirectiveStructureChecker<D, C, PC,
- ClauseEnumSize>::ContextDirectiveAsFortran() {
+template <typename D, typename C, typename PC, typename ClauseSetTy>
+std::string
+DirectiveStructureChecker<D, C, PC, ClauseSetTy>::ContextDirectiveAsFortran() {
return parser::ToUpperCaseLetters(
getDirectiveName(GetContext().directive).str());
}
// Check that clauses present on the directive are allowed clauses.
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-bool DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckAllowed(
+template <typename D, typename C, typename PC, typename ClauseSetTy>
+bool DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckAllowed(
C clause, bool warnInsteadOfError) {
if (!GetContext().allowedClauses.test(clause) &&
!GetContext().allowedOnceClauses.test(clause) &&
@@ -586,10 +571,9 @@ bool DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckAllowed(
// Enforce restriction where clauses in the given set are not allowed if the
// given clause appears.
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-void DirectiveStructureChecker<D, C, PC,
- ClauseEnumSize>::CheckNotAllowedIfClause(C clause,
- common::EnumSet<C, ClauseEnumSize> set) {
+template <typename D, typename C, typename PC, typename ClauseSetTy>
+void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckNotAllowedIfClause(
+ C clause, ClauseSetTy set) {
if (!llvm::is_contained(GetContext().actualClauses, clause)) {
return; // Clause is not present
}
@@ -605,9 +589,9 @@ void DirectiveStructureChecker<D, C, PC,
}
}
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-void DirectiveStructureChecker<D, C, PC,
- ClauseEnumSize>::CheckAllowedOncePerGroup(C clause, C separator) {
+template <typename D, typename C, typename PC, typename ClauseSetTy>
+void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckAllowedOncePerGroup(
+ C clause, C separator) {
bool clauseIsPresent = false;
for (auto cl : GetContext().actualClauses) {
if (cl == clause) {
@@ -626,10 +610,10 @@ void DirectiveStructureChecker<D, C, PC,
}
}
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+template <typename D, typename C, typename PC, typename ClauseSetTy>
void DirectiveStructureChecker<D, C, PC,
- ClauseEnumSize>::CheckMutuallyExclusivePerGroup(C clause, C separator,
- common::EnumSet<C, ClauseEnumSize> set) {
+ ClauseSetTy>::CheckMutuallyExclusivePerGroup(C clause, C separator,
+ ClauseSetTy set) {
// Checking of there is any offending clauses before the first separator.
for (auto cl : GetContext().actualClauses) {
@@ -658,9 +642,9 @@ void DirectiveStructureChecker<D, C, PC,
}
// Check the value of the clause is a constant positive integer.
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+template <typename D, typename C, typename PC, typename ClauseSetTy>
void DirectiveStructureChecker<D, C, PC,
- ClauseEnumSize>::RequiresConstantPositiveParameter(const C &clause,
+ ClauseSetTy>::RequiresConstantPositiveParameter(const C &clause,
const parser::ScalarIntConstantExpr &i) {
if (const auto v{GetIntValue(i)}) {
if (*v <= 0) {
@@ -673,17 +657,17 @@ void DirectiveStructureChecker<D, C, PC,
}
// Check the value of the clause is a constant positive parameter.
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+template <typename D, typename C, typename PC, typename ClauseSetTy>
void DirectiveStructureChecker<D, C, PC,
- ClauseEnumSize>::OptionalConstantPositiveParameter(const C &clause,
+ ClauseSetTy>::OptionalConstantPositiveParameter(const C &clause,
const std::optional<parser::ScalarIntConstantExpr> &o) {
if (o != std::nullopt) {
RequiresConstantPositiveParameter(clause, o.value());
}
}
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
-void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::SayNotMatching(
+template <typename D, typename C, typename PC, typename ClauseSetTy>
+void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::SayNotMatching(
const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
context_
.Say(endSource, "Unmatched %s directive"_err_en_US,
@@ -692,9 +676,9 @@ void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::SayNotMatching(
}
// Check the value of the clause is a positive parameter.
-template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+template <typename D, typename C, typename PC, typename ClauseSetTy>
void DirectiveStructureChecker<D, C, PC,
- ClauseEnumSize>::RequiresPositiveParameter(const C &clause,
+ ClauseSetTy>::RequiresPositiveParameter(const C &clause,
const parser::ScalarIntExpr &i, llvm::StringRef paramName, bool allowZero) {
if (const auto v{GetIntValue(i)}) {
if (*v < (allowZero ? 0 : 1)) {
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index e18e5bf455f26..42b27a332966a 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -62,6 +62,18 @@ namespace Fortran::semantics {
using namespace Fortran::semantics::omp;
using namespace Fortran::parser::omp;
+template <>
+std::string ClauseSetToString(const OmpClauseSet &set,
+ std::function<llvm::StringRef(llvm::omp::Clause)> getName) {
+ std::string list;
+ set.IterateOverMembers([&](llvm::omp::Clause o) {
+ if (!list.empty())
+ list.append(", ");
+ list.append(parser::ToUpperCaseLetters(getName(o)));
+ });
+ return list;
+}
+
OmpStructureChecker::OmpStructureChecker(SemanticsContext &context)
: DirectiveStructureChecker(context,
#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
@@ -1059,7 +1071,8 @@ void OmpStructureChecker::CheckClauses(parser::OmpDirectiveName dirName,
if (!requiredPresent && !requiredSet.empty()) {
context_.Say(dirName.source,
"At least one of %s %s must appear on %s directive"_err_en_US,
- ClauseSetToString(requiredSet),
+ ClauseSetToString<llvm::omp::Clause>(requiredSet,
+ [this](llvm::omp::Clause c) { return getClauseName(c); }),
requiredSet.count() == 1 ? "clause" : "clauses",
GetUpperName(dirName.v, version));
}
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index a0eed9e164888..6b137eb47469d 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -53,7 +53,11 @@ using DirectivesClauseTriple = std::multimap<llvm::omp::Directive,
std::pair<llvm::omp::Directive, const llvm::omp::ClauseSet>>;
using OmpStructureCheckerBase = DirectiveStructureChecker<llvm::omp::Directive,
- llvm::omp::Clause, parser::OmpClause, llvm::omp::Clause_enumSize>;
+ llvm::omp::Clause, parser::OmpClause, OmpClauseSet>;
+
+template <>
+std::string ClauseSetToString(const OmpClauseSet &set,
+ std::function<llvm::StringRef(llvm::omp::Clause)> getName);
class OmpStructureChecker : public OmpStructureCheckerBase {
public:
More information about the llvm-branch-commits
mailing list