[flang-commits] [flang] [flang][OpenMP] Add semantic checks for two DECLARE VARIANT restrictions (PR #209528)
Abid Qadeer via flang-commits
flang-commits at lists.llvm.org
Fri Jul 17 10:13:08 PDT 2026
https://github.com/abidh updated https://github.com/llvm/llvm-project/pull/209528
>From 5cacaf963106f2d143ffa1012387b5148335a8d5 Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Tue, 14 Jul 2026 16:19:09 +0100
Subject: [PATCH 1/4] [flang][OpenMP] Add semantic checks for two DECLARE
VARIANT restrictions
Diagnose two DECLARE VARIANT restrictions from the OpenMP specification
(5.2 [7.5], 6.0 [9.6]) that were previously accepted without error:
- If a procedure is determined to be a function variant through more than
one DECLARE VARIANT directive, the construct selector set of their
context selectors must be the same.
- A procedure determined to be a function variant may not be specified as
a base function in another DECLARE VARIANT directive.
Assisted-by: Cursor
---
flang/lib/Semantics/check-omp-structure.h | 6 ++
flang/lib/Semantics/check-omp-variant.cpp | 100 +++++++++++++++---
.../OpenMP/declare-variant-restriction.f90 | 86 +++++++++++++++
3 files changed, 176 insertions(+), 16 deletions(-)
create mode 100644 flang/test/Semantics/OpenMP/declare-variant-restriction.f90
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 3d4cdd08d07ca..3f1a561d59c54 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/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
using OmpClauseSet =
@@ -458,6 +459,11 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
int directiveNest_[LastType + 1] = {0};
std::set<std::pair<const Symbol *, const Symbol *>> declareVariantPairs_;
+ std::map<const Symbol *,
+ std::pair<llvm::SmallVector<llvm::omp::Directive, 4>, parser::CharBlock>>
+ declareVariantConstructSets_;
+ // Tracks the procedures that appear as a base in DECLARE VARIANT.
+ std::set<const Symbol *> declareVariantBases_;
int allocateDirectiveLevel_{0};
parser::CharBlock visitedAtomicSource_;
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index aeb5f1046d589..a72b01689cd17 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -878,6 +878,33 @@ static bool CheckVariantAccessibility(SemanticsContext &context,
return false;
}
+// Collect the construct selector set of `sel` into `constructs` as an ordered
+// list of leaf construct directives. Combined and composite constructs are
+// decomposed into their leaves (e.g. `target teams` -> target, teams), so two
+// match selectors have the same construct selector set iff the collected lists
+// are equal. An absent construct selector yields an empty list.
+static void CollectConstructSelectorSet(
+ const parser::traits::OmpContextSelectorSpecification &sel,
+ llvm::SmallVectorImpl<llvm::omp::Directive> &constructs) {
+ using SetName = parser::OmpTraitSetSelectorName;
+ using TraitName = parser::OmpTraitSelectorName;
+ for (const parser::OmpTraitSetSelector &traitSet : sel.v) {
+ if (std::get<SetName>(traitSet.t).v != SetName::Value::Construct) {
+ continue;
+ }
+ for (const parser::OmpTraitSelector &trait :
+ std::get<std::list<parser::OmpTraitSelector>>(traitSet.t)) {
+ const auto &traitName{std::get<TraitName>(trait.t)};
+ if (const auto *dir{std::get_if<llvm::omp::Directive>(&traitName.u)}) {
+ for (llvm::omp::Directive leaf :
+ llvm::omp::getLeafConstructsOrSelf(*dir)) {
+ constructs.push_back(leaf);
+ }
+ }
+ }
+ }
+}
+
void OmpStructureChecker::CheckOmpDeclareVariantDirective(
const parser::OmpDeclareVariantDirective &x) {
const parser::OmpDirectiveSpecification &spec{x.v};
@@ -965,23 +992,64 @@ void OmpStructureChecker::CheckOmpDeclareVariantDirective(
if (base == variant) {
context_.Say(arg.source,
"The variant procedure must differ 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());
- } else if (CheckVariantAccessibility(
- context_, *base, *variant, arg.source)) {
- if (!hasArgModifiers) {
- // adjust_args/append_args perform the "transformation for its OpenMP
- // context", so the variant interface intentionally differs from the
- // base; skip the same-interface check until they are supported.
- CheckDeclareVariantInterface(context_, *base, *variant, arg.source);
+ } else {
+ // OpenMP 5.2 [7.5] / 6.0 [9.6]: a procedure determined to be a function
+ // variant may not be a base function in another DECLARE VARIANT
+ // directive, and vice versa. A variant is recorded as a key in
+ // declareVariantConstructSets_ below, so that map serves as the set of
+ // procedures that appear as a variant.
+ if (declareVariantConstructSets_.find(base) !=
+ declareVariantConstructSets_.end()) {
+ context_.Say(arg.source,
+ "The base procedure '%s' is also specified as a variant procedure in another DECLARE VARIANT directive"_err_en_US,
+ base->name());
+ }
+ if (declareVariantBases_.find(variant) != declareVariantBases_.end()) {
+ context_.Say(arg.source,
+ "The variant procedure '%s' is also specified as a base procedure in another DECLARE VARIANT directive"_err_en_US,
+ variant->name());
}
- // Record the variant on its base procedure.
- if (base->has<SubprogramDetails>()) {
- auto &details{const_cast<Symbol &>(*base).get<SubprogramDetails>()};
- details.addOmpDeclareVariant(
- OmpDeclareVariantEntry{*variant, matchSelector});
+ declareVariantBases_.insert(base);
+
+ 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());
+ } else {
+ // OpenMP 5.2 [7.5] / 6.0 [9.6]: if a procedure is determined to be a
+ // function variant through more than one DECLARE VARIANT directive, the
+ // construct selector set of their context selectors must be the same.
+ llvm::SmallVector<llvm::omp::Directive, 4> constructs;
+ CollectConstructSelectorSet(*matchSelector, constructs);
+ auto it{declareVariantConstructSets_.find(variant)};
+ if (it == declareVariantConstructSets_.end()) {
+ declareVariantConstructSets_.emplace(
+ variant, std::make_pair(std::move(constructs), arg.source));
+ } else if (it->second.first != constructs) {
+ context_
+ .Say(arg.source,
+ "Variant procedure '%s' must have the same 'construct' selector set in all DECLARE VARIANT directives"_err_en_US,
+ variant->name())
+ .Attach(it->second.second,
+ "Previous DECLARE VARIANT directive for '%s'"_en_US,
+ variant->name());
+ }
+
+ if (CheckVariantAccessibility(context_, *base, *variant, arg.source)) {
+ if (!hasArgModifiers) {
+ // adjust_args/append_args perform the "transformation for its
+ // OpenMP context", so the variant interface intentionally differs
+ // from the base; skip the same-interface check until they are
+ // supported.
+ CheckDeclareVariantInterface(context_, *base, *variant, arg.source);
+ }
+ // Record the variant on its base procedure.
+ if (base->has<SubprogramDetails>()) {
+ auto &details{const_cast<Symbol &>(*base).get<SubprogramDetails>()};
+ details.addOmpDeclareVariant(
+ OmpDeclareVariantEntry{*variant, matchSelector});
+ }
+ }
}
}
}
diff --git a/flang/test/Semantics/OpenMP/declare-variant-restriction.f90 b/flang/test/Semantics/OpenMP/declare-variant-restriction.f90
new file mode 100644
index 0000000000000..a565f3a97abd2
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/declare-variant-restriction.f90
@@ -0,0 +1,86 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=51
+
+! Same variant (p3) used with different 'construct' selector sets: not conforming.
+subroutine r1_diff_sets
+ !$omp declare variant (p1:p3) match (construct={parallel})
+ !ERROR: Variant procedure 'p3' must have the same 'construct' selector set in all DECLARE VARIANT directives
+ !$omp declare variant (p2:p3) match (construct={do})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
+! Same variant (p3) used with identical 'construct' selector sets: allowed.
+subroutine r1_same_sets
+ !$omp declare variant (p1:p3) match (construct={parallel})
+ !$omp declare variant (p2:p3) match (construct={parallel})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
+! A procedure (p2) that is a variant may not later be used as a base function.
+subroutine r3_variant_then_base
+ !$omp declare variant (p1:p2) match (construct={parallel})
+ !ERROR: The base procedure 'p2' is also specified as a variant procedure in another DECLARE VARIANT directive
+ !$omp declare variant (p2:p3) match (construct={parallel})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
+! The same conflict is detected when the base use appears first: p1 is a base,
+! then used as a variant.
+subroutine r3_base_then_variant
+ !$omp declare variant (p1:p2) match (construct={parallel})
+ !ERROR: The variant procedure 'p1' is also specified as a base procedure in another DECLARE VARIANT directive
+ !$omp declare variant (p3:p1) match (construct={parallel})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
+! Cross-function usage
+module m_cross_function
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ !$omp declare variant (p1) match (user={condition(.false.)},construct={do})
+ end subroutine
+ subroutine p3
+ !ERROR: Variant procedure 'p1' must have the same 'construct' selector set in all DECLARE VARIANT directives
+ !$omp declare variant (p1) match (user={condition(.true.)})
+ end subroutine
+end module
+
+! Two directives for the same variant (p1) that both omit the 'construct'
+! selector have the same (empty) construct selector set: allowed. Non-construct
+! selectors (here 'user') do not affect the comparison.
+module m_no_construct_ok
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ !$omp declare variant (p1) match (user={condition(.true.)})
+ end subroutine
+ subroutine p3
+ !$omp declare variant (p1) match (user={condition(.false.)})
+ end subroutine
+end module
>From c05b1eb88b06e2bee76f35cce02c3f0a617063bf Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Wed, 15 Jul 2026 12:13:46 +0100
Subject: [PATCH 2/4] Handle review comments.
The simd needs sepaate handling.
---
flang/lib/Semantics/check-omp-variant.cpp | 6 ++++
.../OpenMP/declare-variant-restriction.f90 | 28 +++++++++++++++++++
2 files changed, 34 insertions(+)
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index a72b01689cd17..228d439d40433 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -900,6 +900,12 @@ static void CollectConstructSelectorSet(
llvm::omp::getLeafConstructsOrSelf(*dir)) {
constructs.push_back(leaf);
}
+ } else if (const auto *value{std::get_if<TraitName::Value>(&traitName.u)};
+ value && *value == TraitName::Value::Simd) {
+ // In a construct selector, `simd` is represented as Value::Simd (it can
+ // carry simd-specific properties), not as a Directive; treat it as
+ // OMPD_simd so it participates in the comparison.
+ constructs.push_back(llvm::omp::Directive::OMPD_simd);
}
}
}
diff --git a/flang/test/Semantics/OpenMP/declare-variant-restriction.f90 b/flang/test/Semantics/OpenMP/declare-variant-restriction.f90
index a565f3a97abd2..704271f087dcf 100644
--- a/flang/test/Semantics/OpenMP/declare-variant-restriction.f90
+++ b/flang/test/Semantics/OpenMP/declare-variant-restriction.f90
@@ -27,6 +27,34 @@ subroutine p3
end subroutine
end subroutine
+! 'simd' (represented specially in the parse tree) appears in only one of the
+! construct selector sets, so the sets differ: not conforming.
+subroutine r1_simd_diff
+ !$omp declare variant (p1:p3) match (construct={parallel})
+ !ERROR: Variant procedure 'p3' must have the same 'construct' selector set in all DECLARE VARIANT directives
+ !$omp declare variant (p2:p3) match (construct={parallel, simd})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
+! 'simd' appears in both construct selector sets: the sets are the same.
+subroutine r1_simd_same
+ !$omp declare variant (p1:p3) match (construct={parallel, simd})
+ !$omp declare variant (p2:p3) match (construct={parallel, simd})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
! A procedure (p2) that is a variant may not later be used as a base function.
subroutine r3_variant_then_base
!$omp declare variant (p1:p2) match (construct={parallel})
>From 13b6d19a5b0a35e726e50ff43a621351e72949ca Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Wed, 15 Jul 2026 12:24:32 +0100
Subject: [PATCH 3/4] Fix formatting issue.
---
flang/lib/Semantics/check-omp-variant.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 228d439d40433..40fdaef57e893 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -901,7 +901,7 @@ static void CollectConstructSelectorSet(
constructs.push_back(leaf);
}
} else if (const auto *value{std::get_if<TraitName::Value>(&traitName.u)};
- value && *value == TraitName::Value::Simd) {
+ value && *value == TraitName::Value::Simd) {
// In a construct selector, `simd` is represented as Value::Simd (it can
// carry simd-specific properties), not as a Directive; treat it as
// OMPD_simd so it participates in the comparison.
>From 0195a747266ec70ac9ff3b0a836a1eafe647b34f Mon Sep 17 00:00:00 2001
From: Abid Qadeer <haqadeer at amd.com>
Date: Fri, 17 Jul 2026 18:12:43 +0100
Subject: [PATCH 4/4] Handle review comments (2)
Consider trait properties when comparing construct selector sets
---
flang/lib/Semantics/check-omp-structure.h | 8 ++-
flang/lib/Semantics/check-omp-variant.cpp | 63 ++++++++++++++---
.../OpenMP/declare-variant-restriction.f90 | 70 +++++++++++++++++++
3 files changed, 131 insertions(+), 10 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 3f1a561d59c54..f03c3ad031b34 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -459,8 +459,14 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
int directiveNest_[LastType + 1] = {0};
std::set<std::pair<const Symbol *, const Symbol *>> declareVariantPairs_;
+ // For each variant procedure: its construct-selector-set as an ordered list
+ // of elements (a leaf construct directive plus a normalized rendering of any
+ // properties), and the source of the DECLARE VARIANT directive that first
+ // established the set.
std::map<const Symbol *,
- std::pair<llvm::SmallVector<llvm::omp::Directive, 4>, parser::CharBlock>>
+ std::pair<
+ llvm::SmallVector<std::pair<llvm::omp::Directive, std::string>, 4>,
+ parser::CharBlock>>
declareVariantConstructSets_;
// Tracks the procedures that appear as a base in DECLARE VARIANT.
std::set<const Symbol *> declareVariantBases_;
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 40fdaef57e893..cbad9d9d783cf 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -26,8 +26,10 @@
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Frontend/OpenMP/OMP.h"
+#include <algorithm>
#include <list>
#include <map>
#include <optional>
@@ -878,14 +880,54 @@ static bool CheckVariantAccessibility(SemanticsContext &context,
return false;
}
+// Normalize the text spanned by `source` for structural comparison: drop
+// whitespace and fold to lower case (Fortran is case-insensitive). This lets
+// e.g. `simdlen(4)` and `SIMDLEN( 4 )` compare equal while `simdlen(4)` and
+// `simdlen(8)` do not.
+static std::string NormalizeSelectorText(parser::CharBlock source) {
+ std::string text{source.ToString()};
+ llvm::erase_if(text, parser::IsWhiteSpace);
+ return parser::ToLowerCaseLetters(text);
+}
+
+// Render the properties of a trait selector into a canonical string (in a
+// construct selector set only `simd` currently carries properties, e.g.
+// simdlen, inbranch/notinbranch). The property list is unordered, so the
+// individual properties are sorted; two selectors have the same properties iff
+// these strings are equal.
+static std::string CanonicalizeTraitProperties(
+ const parser::OmpTraitSelector &trait) {
+ const auto &maybeProperties{
+ std::get<std::optional<parser::OmpTraitSelector::Properties>>(trait.t)};
+ if (!maybeProperties) {
+ return {};
+ }
+ llvm::SmallVector<std::string> items;
+ for (const parser::OmpTraitProperty &property :
+ std::get<std::list<parser::OmpTraitProperty>>(maybeProperties->t)) {
+ items.push_back(NormalizeSelectorText(property.source));
+ }
+ std::sort(items.begin(), items.end());
+ std::string result;
+ for (const std::string &item : items) {
+ result += item;
+ result += ';';
+ }
+ return result;
+}
+
// Collect the construct selector set of `sel` into `constructs` as an ordered
-// list of leaf construct directives. Combined and composite constructs are
-// decomposed into their leaves (e.g. `target teams` -> target, teams), so two
-// match selectors have the same construct selector set iff the collected lists
-// are equal. An absent construct selector yields an empty list.
+// list of elements: a leaf construct directive plus a normalized rendering of
+// any properties it carries. Combined and composite constructs are decomposed
+// into their leaves (e.g. `target teams` -> target, teams). The properties
+// are included so that e.g. `simd(simdlen(4))` and `simd(simdlen(8))` are
+// treated as different construct selector sets. Two match selectors have the
+// same construct selector set iff the collected lists are equal; an absent
+// construct selector yields an empty list.
static void CollectConstructSelectorSet(
const parser::traits::OmpContextSelectorSpecification &sel,
- llvm::SmallVectorImpl<llvm::omp::Directive> &constructs) {
+ llvm::SmallVectorImpl<std::pair<llvm::omp::Directive, std::string>>
+ &constructs) {
using SetName = parser::OmpTraitSetSelectorName;
using TraitName = parser::OmpTraitSelectorName;
for (const parser::OmpTraitSetSelector &traitSet : sel.v) {
@@ -898,14 +940,16 @@ static void CollectConstructSelectorSet(
if (const auto *dir{std::get_if<llvm::omp::Directive>(&traitName.u)}) {
for (llvm::omp::Directive leaf :
llvm::omp::getLeafConstructsOrSelf(*dir)) {
- constructs.push_back(leaf);
+ constructs.emplace_back(leaf, std::string{});
}
} else if (const auto *value{std::get_if<TraitName::Value>(&traitName.u)};
value && *value == TraitName::Value::Simd) {
// In a construct selector, `simd` is represented as Value::Simd (it can
// carry simd-specific properties), not as a Directive; treat it as
- // OMPD_simd so it participates in the comparison.
- constructs.push_back(llvm::omp::Directive::OMPD_simd);
+ // OMPD_simd and fold in its properties so they participate in the
+ // comparison.
+ constructs.emplace_back(llvm::omp::Directive::OMPD_simd,
+ CanonicalizeTraitProperties(trait));
}
}
}
@@ -1025,7 +1069,8 @@ void OmpStructureChecker::CheckOmpDeclareVariantDirective(
// OpenMP 5.2 [7.5] / 6.0 [9.6]: if a procedure is determined to be a
// function variant through more than one DECLARE VARIANT directive, the
// construct selector set of their context selectors must be the same.
- llvm::SmallVector<llvm::omp::Directive, 4> constructs;
+ llvm::SmallVector<std::pair<llvm::omp::Directive, std::string>, 4>
+ constructs;
CollectConstructSelectorSet(*matchSelector, constructs);
auto it{declareVariantConstructSets_.find(variant)};
if (it == declareVariantConstructSets_.end()) {
diff --git a/flang/test/Semantics/OpenMP/declare-variant-restriction.f90 b/flang/test/Semantics/OpenMP/declare-variant-restriction.f90
index 704271f087dcf..d3e67e2dc123c 100644
--- a/flang/test/Semantics/OpenMP/declare-variant-restriction.f90
+++ b/flang/test/Semantics/OpenMP/declare-variant-restriction.f90
@@ -55,6 +55,76 @@ subroutine p3
end subroutine
end subroutine
+! 'simd' appears in both sets but with different simd properties (simdlen), so
+! the construct selector sets differ: not conforming.
+subroutine r1_simd_simdlen_diff
+ !$omp declare variant (p1:p3) match (construct={simd(simdlen(4))})
+ !ERROR: Variant procedure 'p3' must have the same 'construct' selector set in all DECLARE VARIANT directives
+ !$omp declare variant (p2:p3) match (construct={simd(simdlen(8))})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
+! 'simd' with identical simd properties in both sets: allowed.
+subroutine r1_simd_simdlen_same
+ !$omp declare variant (p1:p3) match (construct={simd(simdlen(4))})
+ !$omp declare variant (p2:p3) match (construct={simd(simdlen(4))})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
+! Bare 'simd' versus 'simd' with a property: the sets differ.
+subroutine r1_simd_bare_vs_prop
+ !$omp declare variant (p1:p3) match (construct={simd})
+ !ERROR: Variant procedure 'p3' must have the same 'construct' selector set in all DECLARE VARIANT directives
+ !$omp declare variant (p2:p3) match (construct={simd(simdlen(4))})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
+! 'simd' with inbranch versus notinbranch: the sets differ.
+subroutine r1_simd_inbranch_diff
+ !$omp declare variant (p1:p3) match (construct={simd(inbranch)})
+ !ERROR: Variant procedure 'p3' must have the same 'construct' selector set in all DECLARE VARIANT directives
+ !$omp declare variant (p2:p3) match (construct={simd(notinbranch)})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
+! The same simd properties written in a different order describe the same set:
+! allowed (property order is not significant).
+subroutine r1_simd_prop_reorder
+ !$omp declare variant (p1:p3) match (construct={simd(simdlen(4), inbranch)})
+ !$omp declare variant (p2:p3) match (construct={simd(inbranch, simdlen(4))})
+contains
+ subroutine p1
+ end subroutine
+ subroutine p2
+ end subroutine
+ subroutine p3
+ end subroutine
+end subroutine
+
! A procedure (p2) that is a variant may not later be used as a base function.
subroutine r3_variant_then_base
!$omp declare variant (p1:p2) match (construct={parallel})
More information about the flang-commits
mailing list