[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