[flang-commits] [flang] cf25fb1 - [flang][OpenMP] Add structure checks for DECLARE VARIANT (#198799)
via flang-commits
flang-commits at lists.llvm.org
Tue Jun 2 08:57:49 PDT 2026
Author: Abid Qadeer
Date: 2026-06-02T16:57:43+01:00
New Revision: cf25fb1e0809aaf4dbb13f2b837d47e329f595b8
URL: https://github.com/llvm/llvm-project/commit/cf25fb1e0809aaf4dbb13f2b837d47e329f595b8
DIFF: https://github.com/llvm/llvm-project/commit/cf25fb1e0809aaf4dbb13f2b837d47e329f595b8.diff
LOG: [flang][OpenMP] Add structure checks for DECLARE VARIANT (#198799)
This PR adds declare-variant structure checking. Following checks are
added:
- Validate [base:]variant arguments (including implicit base for
single-name form).
- Require exactly one MATCH clause; reject a second MATCH on the same
directive.
- Reject duplicate (base, variant) across multiple declare variant
directives.
- Reject clauses not allowed on declare variant.
- Apply shared context-selector checks to MATCH (reuse metadirective
logic).
- Require constant user conditions in MATCH for declare variant (dynamic
selectors deferred).
Refactor metadirective support:
- Extract CheckContextSelectorSpecification for reuse.
- Reject SCORE on trait sets that do not allow it (also affects
metadirective).
Co-authored-by: Cursor <cursoragent at cursor.com>
Added:
flang/test/Semantics/OpenMP/declare-variant-match.f90
Modified:
flang/lib/Semantics/check-omp-metadirective.cpp
flang/lib/Semantics/check-omp-structure.cpp
flang/lib/Semantics/check-omp-structure.h
flang/test/Semantics/OpenMP/declare-variant.f90
Removed:
################################################################################
diff --git a/flang/lib/Semantics/check-omp-metadirective.cpp b/flang/lib/Semantics/check-omp-metadirective.cpp
index c8c19e4ac7dac..d492f7e7e2eb4 100644
--- a/flang/lib/Semantics/check-omp-metadirective.cpp
+++ b/flang/lib/Semantics/check-omp-metadirective.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Semantic checks for METADIRECTIVE and related constructs/clauses.
+// Semantic checks for METADIRECTIVE, DECLARE VARIANT, and related constructs.
//
//===----------------------------------------------------------------------===//
@@ -15,11 +15,13 @@
#include "flang/Common/idioms.h"
#include "flang/Common/indirection.h"
#include "flang/Common/visit.h"
+#include "flang/Evaluate/check-expression.h"
#include "flang/Parser/characters.h"
#include "flang/Parser/message.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/openmp-modifiers.h"
#include "flang/Semantics/openmp-utils.h"
+#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
#include "llvm/Frontend/OpenMP/OMP.h"
@@ -43,9 +45,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::When &x) {
x.v, llvm::omp::OMPC_when, GetContext().clauseSource, context_);
}
-void OmpStructureChecker::Enter(const parser::OmpContextSelector &ctx) {
- EnterDirectiveNest(ContextSelectorNest);
-
+void OmpStructureChecker::CheckContextSelectorSpecification(
+ const parser::OmpContextSelector &ctx) {
using SetName = parser::OmpTraitSetSelectorName;
std::map<SetName::Value, const SetName *> visited;
@@ -66,6 +67,11 @@ void OmpStructureChecker::Enter(const parser::OmpContextSelector &ctx) {
}
}
+void OmpStructureChecker::Enter(const parser::OmpContextSelector &ctx) {
+ EnterDirectiveNest(ContextSelectorNest);
+ CheckContextSelectorSpecification(ctx);
+}
+
void OmpStructureChecker::Leave(const parser::OmpContextSelector &) {
ExitDirectiveNest(ContextSelectorNest);
}
@@ -240,7 +246,11 @@ void OmpStructureChecker::CheckTraitSetSelector(
if (maybeProps) {
auto &[maybeScore, _]{maybeProps->t};
if (maybeScore) {
- CheckTraitScore(*maybeScore);
+ if (!config.allowsScore)
+ context_.Say(maybeScore->source,
+ "SCORE is not allowed for %s trait set"_err_en_US, usn);
+ else
+ CheckTraitScore(*maybeScore);
}
}
@@ -594,4 +604,151 @@ void OmpStructureChecker::Leave(
dirContext_.pop_back();
}
+static const parser::traits::OmpContextSelectorSpecification *
+getMatchClauseContextSelector(const parser::OmpDirectiveSpecification &spec) {
+ for (const parser::OmpClause &clause : spec.Clauses().v) {
+ if (clause.Id() == llvm::omp::Clause::OMPC_match)
+ return &std::get<parser::OmpClause::Match>(clause.u).v.v;
+ }
+ return nullptr;
+}
+
+void OmpStructureChecker::CheckDeclareVariantUserConditions(
+ const parser::OmpContextSelector &ctx) {
+ using SetName = parser::OmpTraitSetSelectorName;
+ using TraitName = parser::OmpTraitSelectorName;
+
+ for (const parser::OmpTraitSetSelector &traitSet : ctx.v) {
+ if (std::get<SetName>(traitSet.t).v != SetName::Value::User) {
+ continue;
+ }
+ for (const parser::OmpTraitSelector &trait :
+ std::get<std::list<parser::OmpTraitSelector>>(traitSet.t)) {
+ const auto &traitName{std::get<TraitName>(trait.t)};
+ if (!std::holds_alternative<TraitName::Value>(traitName.u) ||
+ std::get<TraitName::Value>(traitName.u) !=
+ TraitName::Value::Condition) {
+ continue;
+ }
+ const auto &maybeProps{
+ std::get<std::optional<parser::OmpTraitSelector::Properties>>(
+ trait.t)};
+ if (!maybeProps) {
+ continue;
+ }
+ const auto &properties{
+ std::get<std::list<parser::OmpTraitProperty>>(maybeProps->t)};
+ if (properties.size() != 1) {
+ continue;
+ }
+ const parser::OmpTraitProperty &property{properties.front()};
+ const parser::ScalarExpr &scalarExpr{
+ std::get<parser::ScalarExpr>(property.u)};
+ auto maybeType{GetDynamicType(scalarExpr.thing.value())};
+ if (!maybeType || maybeType->category() != TypeCategory::Logical) {
+ continue;
+ }
+ if (const auto *expr{GetExpr(scalarExpr)}) {
+ if (!IsConstantExpr(*expr, &context_.foldingContext())) {
+ context_.Say(property.source,
+ "Run-time USER condition in the MATCH clause is not yet implemented"_err_en_US);
+ }
+ }
+ }
+ }
+}
+
+void OmpStructureChecker::CheckOmpDeclareVariantDirective(
+ const parser::OmpDeclareVariantDirective &x) {
+ const parser::OmpDirectiveSpecification &spec{x.v};
+ const parser::OmpArgumentList &args{spec.Arguments()};
+
+ if (args.v.size() != 1) {
+ context_.Say(args.source,
+ "DECLARE_VARIANT directive should have a single argument"_err_en_US);
+ return;
+ }
+
+ auto InvalidArgument{[&](parser::CharBlock source) {
+ context_.Say(source,
+ "The argument to the DECLARE_VARIANT directive should be [base-name:]variant-name"_err_en_US);
+ }};
+
+ auto CheckProcedureSymbol{[&](const Symbol *sym, parser::CharBlock source) {
+ if (sym) {
+ if (!IsProcedure(*sym) && !IsFunction(*sym)) {
+ auto &msg{context_.Say(source,
+ "The name '%s' should refer to a procedure"_err_en_US,
+ sym->name())};
+ if (sym->test(Symbol::Flag::Implicit)) {
+ msg.Attach(source, "The name '%s' has been implicitly declared"_en_US,
+ sym->name());
+ }
+ }
+ } else {
+ InvalidArgument(source);
+ }
+ }};
+
+ const Symbol *base{nullptr};
+ const Symbol *variant{nullptr};
+ const parser::OmpArgument &arg{args.v.front()};
+ common::visit( //
+ common::visitors{
+ [&](const parser::OmpBaseVariantNames &y) {
+ base = GetObjectSymbol(std::get<0>(y.t));
+ variant = GetObjectSymbol(std::get<1>(y.t));
+ CheckProcedureSymbol(base, arg.source);
+ CheckProcedureSymbol(variant, arg.source);
+ },
+ [&](const parser::OmpLocator &y) {
+ variant = GetArgumentSymbol(arg);
+ CheckProcedureSymbol(variant, arg.source);
+ const Scope &containingScope{context_.FindScope(x.source)};
+ if (const Symbol *host{
+ GetProgramUnitContaining(containingScope).symbol()}) {
+ base = host;
+ }
+ },
+ [&](auto &&y) { InvalidArgument(arg.source); },
+ },
+ arg.u);
+
+ if (base && variant) {
+ base = &base->GetUltimate();
+ variant = &variant->GetUltimate();
+ if (base == variant) {
+ context_.Say(arg.source,
+ "The variant procedure must
diff er from the base procedure"_err_en_US);
+ } else if (!declareVariantPairs_.emplace(base, variant).second) {
+ context_.Say(arg.source,
+ "Variant '%s' was already specified for '%s' in another DECLARE VARIANT directive"_err_en_US,
+ variant->name(), base->name());
+ }
+ }
+
+ const parser::traits::OmpContextSelectorSpecification *matchSelector{
+ getMatchClauseContextSelector(spec)};
+ if (!matchSelector) {
+ context_.Say(x.source,
+ "DECLARE_VARIANT directive requires a MATCH clause"_err_en_US);
+ return;
+ }
+
+ EnterDirectiveNest(ContextSelectorNest);
+ CheckContextSelectorSpecification(*matchSelector);
+ CheckDeclareVariantUserConditions(*matchSelector);
+ ExitDirectiveNest(ContextSelectorNest);
+}
+
+void OmpStructureChecker::Enter(const parser::OmpDeclareVariantDirective &x) {
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+ PushContextAndClauseSets(dirName.source, dirName.v);
+ CheckOmpDeclareVariantDirective(x);
+}
+
+void OmpStructureChecker::Leave(const parser::OmpDeclareVariantDirective &) {
+ dirContext_.pop_back();
+}
+
} // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index eff8f5f5858d6..1ac43b666977b 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -71,6 +71,7 @@ OmpStructureChecker::OmpStructureChecker(SemanticsContext &context)
void OmpStructureChecker::Enter(const parser::ProgramUnit &) { //
ClearLabels();
+ declareVariantPairs_.clear();
}
void OmpStructureChecker::Enter(const parser::MainProgram &x) {
@@ -1714,59 +1715,6 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareSimdDirective &) {
dirContext_.pop_back();
}
-void OmpStructureChecker::Enter(const parser::OmpDeclareVariantDirective &x) {
- const parser::OmpDirectiveName &dirName{x.v.DirName()};
- PushContextAndClauseSets(dirName.source, dirName.v);
-
- const parser::OmpArgumentList &args{x.v.Arguments()};
- if (args.v.size() != 1) {
- context_.Say(args.source,
- "DECLARE_VARIANT directive should have a single argument"_err_en_US);
- return;
- }
-
- auto InvalidArgument{[&](parser::CharBlock source) {
- context_.Say(source,
- "The argument to the DECLARE_VARIANT directive should be [base-name:]variant-name"_err_en_US);
- }};
-
- auto CheckSymbol{[&](const Symbol *sym, parser::CharBlock source) {
- if (sym) {
- if (!IsProcedure(*sym) && !IsFunction(*sym)) {
- auto &msg{context_.Say(source,
- "The name '%s' should refer to a procedure"_err_en_US,
- sym->name())};
- if (sym->test(Symbol::Flag::Implicit)) {
- msg.Attach(source, "The name '%s' has been implicitly declared"_en_US,
- sym->name());
- }
- }
- } else {
- InvalidArgument(source);
- }
- }};
-
- const parser::OmpArgument &arg{args.v.front()};
- common::visit( //
- common::visitors{
- [&](const parser::OmpBaseVariantNames &y) {
- CheckSymbol(GetObjectSymbol(std::get<0>(y.t), /*ultimate=*/true),
- arg.source);
- CheckSymbol(GetObjectSymbol(std::get<1>(y.t), /*ultimate=*/true),
- arg.source);
- },
- [&](const parser::OmpLocator &y) {
- CheckSymbol(GetArgumentSymbol(arg, /*ultimate=*/true), arg.source);
- },
- [&](auto &&y) { InvalidArgument(arg.source); },
- },
- arg.u);
-}
-
-void OmpStructureChecker::Leave(const parser::OmpDeclareVariantDirective &) {
- dirContext_.pop_back();
-}
-
void OmpStructureChecker::CheckInitOnDepobj(
const parser::OpenMPDepobjConstruct &depobj,
const parser::OmpClause &initClause) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 487b7bf732b2f..676442d0bd666 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -259,12 +259,16 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void CheckDistLinear(const parser::OpenMPLoopConstruct &x);
// check-omp-metadirective.cpp
+ void CheckOmpDeclareVariantDirective(
+ const parser::OmpDeclareVariantDirective &);
+ void CheckDeclareVariantUserConditions(const parser::OmpContextSelector &);
const std::list<parser::OmpTraitProperty> &GetTraitPropertyList(
const parser::OmpTraitSelector &);
std::optional<llvm::omp::Clause> GetClauseFromProperty(
const parser::OmpTraitProperty &);
void CheckTraitSelectorList(const std::list<parser::OmpTraitSelector> &);
+ void CheckContextSelectorSpecification(const parser::OmpContextSelector &);
void CheckTraitSetSelector(const parser::OmpTraitSetSelector &);
void CheckTraitScore(const parser::OmpTraitScore &);
bool VerifyTraitPropertyLists(
@@ -418,6 +422,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
};
int directiveNest_[LastType + 1] = {0};
+ std::set<std::pair<const Symbol *, const Symbol *>> declareVariantPairs_;
+
int allocateDirectiveLevel_{0};
parser::CharBlock visitedAtomicSource_;
diff --git a/flang/test/Semantics/OpenMP/declare-variant-match.f90 b/flang/test/Semantics/OpenMP/declare-variant-match.f90
new file mode 100644
index 0000000000000..199d05f1750ee
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/declare-variant-match.f90
@@ -0,0 +1,118 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
+
+! MATCH clause checks for DECLARE VARIANT: required/duplicate clause and
+! context-selector validation (shared with METADIRECTIVE).
+
+subroutine f00
+ !$omp declare variant (sub:vsub) &
+ !$omp & match (implementation={vendor("this")}, &
+!ERROR: Repeated trait set name IMPLEMENTATION in a context specifier
+ !$omp & implementation={requires(unified_shared_memory)})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine f01
+ !$omp declare variant (sub:vsub) &
+!ERROR: Repeated trait name ISA in a trait set
+ !$omp & match (device={isa("this"), isa("that")})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine f02
+ !$omp declare variant (sub:vsub) &
+!ERROR: SCORE expression must be a non-negative constant integer expression
+ !$omp & match (user={condition(score(-2): .true.)})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine f03(x)
+ integer :: x
+ !$omp declare variant (sub:vsub) &
+!ERROR: SCORE expression must be a non-negative constant integer expression
+ !$omp & match (user={condition(score(x): .true.)})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine f04
+ !$omp declare variant (sub:vsub) &
+!ERROR: Trait property should be a scalar expression
+!ERROR: More invalid properties are present
+ !$omp & match (target_device={device_num("device", "foo"(1))})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine f05(x)
+ integer :: x
+ !$omp declare variant (sub:vsub) &
+ !$omp & match (user={ &
+!ERROR: CONDITION trait requires a single LOGICAL expression
+ !$omp & condition(score(2): x)})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine f06(x)
+ integer :: x
+!ERROR: Run-time USER condition in the MATCH clause is not yet implemented
+ !$omp declare variant (sub:vsub) match (user={condition(x > 0)})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine f07
+ !$omp declare variant (sub:vsub) &
+!ERROR: SCORE is not allowed for DEVICE trait set
+ !$omp & match (device={kind(score(1): host)})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine f08
+!ERROR: DECLARE_VARIANT directive requires a MATCH clause
+ !$omp declare variant (sub:vsub)
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine f09
+ !$omp declare variant (sub:vsub) match (construct={parallel}) &
+!ERROR: At most one MATCH clause can appear on the DECLARE VARIANT directive
+ !$omp & match (construct={teams})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/declare-variant.f90 b/flang/test/Semantics/OpenMP/declare-variant.f90
index 6fc94a4fb837f..443f767f73244 100644
--- a/flang/test/Semantics/OpenMP/declare-variant.f90
+++ b/flang/test/Semantics/OpenMP/declare-variant.f90
@@ -12,3 +12,33 @@ subroutine vsub
subroutine sub ()
end subroutine
end subroutine
+
+subroutine same_base_variant
+!ERROR: The variant procedure must
diff er from the base procedure
+ !$omp declare variant (sub:sub) match (construct={parallel})
+contains
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine duplicate_variant
+ !$omp declare variant (sub:vsub) match (construct={parallel})
+!ERROR: Variant 'vsub' was already specified for 'sub' in another DECLARE VARIANT directive
+ !$omp declare variant (sub:vsub) match (construct={teams})
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ end subroutine
+end subroutine
+
+subroutine invalid_clause
+!ERROR: PRIVATE clause is not allowed on the DECLARE VARIANT directive
+ !$omp declare variant (sub:vsub) match (construct={parallel}) private(x)
+contains
+ subroutine vsub
+ end subroutine
+ subroutine sub
+ integer :: x
+ end subroutine
+end subroutine
More information about the flang-commits
mailing list