Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flang][OpenMP] Use new modifier infrastructure for MAP/FROM/TO clauses #117447

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

kparzysz
Copy link
Contributor

This removes the specialized parsers and helper classes for these clauses, namely ConcatSeparated, MapModifiers, and MotionModifiers. Map and the motion clauses are now handled in the same way as all other clauses with modifiers, with one exception: the commas separating their modifiers are optional. This syntax is deprecated in OpenMP 5.2.

Implement version checks for modifiers: for a given modifier on a given clause, check if that modifier is allowed on this clause in the specified OpenMP version. This replaced several individual checks.

Add a testcase for handling map modifiers in a different order, and for diagnosing an ultimate modifier out of position.

@llvmbot
Copy link
Member

llvmbot commented Nov 23, 2024

@llvm/pr-subscribers-flang-openmp
@llvm/pr-subscribers-flang-parser

@llvm/pr-subscribers-flang-fir-hlfir

Author: Krzysztof Parzyszek (kparzysz)

Changes

This removes the specialized parsers and helper classes for these clauses, namely ConcatSeparated, MapModifiers, and MotionModifiers. Map and the motion clauses are now handled in the same way as all other clauses with modifiers, with one exception: the commas separating their modifiers are optional. This syntax is deprecated in OpenMP 5.2.

Implement version checks for modifiers: for a given modifier on a given clause, check if that modifier is allowed on this clause in the specified OpenMP version. This replaced several individual checks.

Add a testcase for handling map modifiers in a different order, and for diagnosing an ultimate modifier out of position.


Patch is 97.25 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/117447.diff

25 Files Affected:

  • (modified) flang/include/flang/Parser/dump-parse-tree.h (+10-6)
  • (modified) flang/include/flang/Parser/parse-tree.h (+39-32)
  • (modified) flang/include/flang/Semantics/openmp-modifiers.h (+130-50)
  • (modified) flang/lib/Lower/OpenMP/ClauseProcessor.cpp (+9-7)
  • (modified) flang/lib/Lower/OpenMP/Clauses.cpp (+60-65)
  • (modified) flang/lib/Lower/OpenMP/Clauses.h (+1)
  • (modified) flang/lib/Parser/openmp-parsers.cpp (+93-186)
  • (modified) flang/lib/Parser/unparse.cpp (+14-79)
  • (modified) flang/lib/Semantics/check-omp-structure.cpp (+61-96)
  • (modified) flang/lib/Semantics/check-omp-structure.h (+18-21)
  • (modified) flang/lib/Semantics/openmp-modifiers.cpp (+84-1)
  • (modified) flang/lib/Semantics/resolve-directives.cpp (+9-12)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+12-11)
  • (modified) flang/test/Lower/OpenMP/Todo/map-mapper.f90 (+1-1)
  • (modified) flang/test/Parser/OpenMP/from-clause.f90 (+5-5)
  • (modified) flang/test/Parser/OpenMP/map-modifiers.f90 (+92-57)
  • (modified) flang/test/Parser/OpenMP/target-update-to-clause.f90 (+5-5)
  • (modified) flang/test/Semantics/OpenMP/combined-constructs.f90 (+6-6)
  • (modified) flang/test/Semantics/OpenMP/defaultmap-clause-v45.f90 (+1-1)
  • (modified) flang/test/Semantics/OpenMP/from-clause-v45.f90 (+6-5)
  • (modified) flang/test/Semantics/OpenMP/from-clause-v51.f90 (+2-2)
  • (modified) flang/test/Semantics/OpenMP/map-clause.f90 (+1-1)
  • (modified) flang/test/Semantics/OpenMP/map-modifiers.f90 (+9-1)
  • (modified) flang/test/Semantics/OpenMP/to-clause-v45.f90 (+6-5)
  • (modified) flang/test/Semantics/OpenMP/to-clause-v51.f90 (+2-2)
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 6d1e7329d5cce8..68f9406dc28309 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -476,6 +476,11 @@ class ParseTreeDumper {
   NODE(parser, NullInit)
   NODE(parser, ObjectDecl)
   NODE(parser, OldParameterStmt)
+  NODE(parser, OmpMapper)
+  NODE(parser, OmpMapType)
+  NODE_ENUM(OmpMapType, Value)
+  NODE(parser, OmpMapTypeModifier)
+  NODE_ENUM(OmpMapTypeModifier, Value)
   NODE(parser, OmpIteratorSpecifier)
   NODE(parser, OmpIterator)
   NODE(parser, OmpAffinityClause)
@@ -536,7 +541,9 @@ class ParseTreeDumper {
   NODE(parser, OmpEndLoopDirective)
   NODE(parser, OmpEndSectionsDirective)
   NODE(parser, OmpFromClause)
-  NODE_ENUM(OmpFromClause, Expectation)
+  NODE(OmpFromClause, Modifier)
+  NODE(parser, OmpExpectation)
+  NODE_ENUM(OmpExpectation, Value)
   NODE(parser, OmpIfClause)
   NODE_ENUM(OmpIfClause, DirectiveNameModifier)
   NODE_ENUM(OmpLastprivateClause, LastprivateModifier)
@@ -548,9 +555,7 @@ class ParseTreeDumper {
   NODE_ENUM(OmpLinearModifier, Value)
   NODE(parser, OmpLoopDirective)
   NODE(parser, OmpMapClause)
-  NODE(parser, OmpMapperIdentifier)
-  NODE_ENUM(OmpMapClause, TypeModifier)
-  NODE_ENUM(OmpMapClause, Type)
+  NODE(OmpMapClause, Modifier)
   static std::string GetNodeName(const llvm::omp::Clause &x) {
     return llvm::Twine(
         "llvm::omp::Clause = ", llvm::omp::getOpenMPClauseName(x))
@@ -601,8 +606,7 @@ class ParseTreeDumper {
   NODE(parser, OmpSectionsDirective)
   NODE(parser, OmpSimpleStandaloneDirective)
   NODE(parser, OmpToClause)
-  // No NODE_ENUM for OmpToClause::Expectation, because it's an alias
-  // for OmpFromClause::Expectation.
+  NODE(OmpToClause, Modifier)
   NODE(parser, Only)
   NODE(parser, OpenACCAtomicConstruct)
   NODE(parser, OpenACCBlockConstruct)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index de179f47be8fca..9c868fe267a1d0 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3502,6 +3502,21 @@ struct OmpDependenceType {
   WRAPPER_CLASS_BOILERPLATE(OmpDependenceType, Value);
 };
 
+// Ref: [5.1:205-209], [5.2:166-168]
+//
+// motion-modifier ->
+//    PRESENT |                                     // since 5.0, until 5.0
+//    mapper | iterator
+// expectation ->
+//    PRESENT                                       // since 5.1
+//
+// The PRESENT value was a part of motion-modifier in 5.1, and became a
+// value of expectation in 5.2.
+struct OmpExpectation {
+  ENUM_CLASS(Value, Present);
+  WRAPPER_CLASS_BOILERPLATE(OmpExpectation, Value);
+};
+
 // Ref: [5.0:47-49], [5.1:49-51], [5.2:67-69]
 //
 // iterator-modifier ->
@@ -3519,6 +3534,24 @@ struct OmpLinearModifier {
   WRAPPER_CLASS_BOILERPLATE(OmpLinearModifier, Value);
 };
 
+// [5.0:176-180], [5.1:205-210], [5.2:149-150]
+//
+// mapper ->
+//    identifier                                    // since 4.5
+struct OmpMapper {
+  WRAPPER_CLASS_BOILERPLATE(OmpMapper, Name);
+};
+
+struct OmpMapType {
+  ENUM_CLASS(Value, Alloc, Delete, From, Release, To, Tofrom);
+  WRAPPER_CLASS_BOILERPLATE(OmpMapType, Value);
+};
+
+struct OmpMapTypeModifier {
+  ENUM_CLASS(Value, Always, Close, Present, Ompx_Hold)
+  WRAPPER_CLASS_BOILERPLATE(OmpMapTypeModifier, Value);
+};
+
 // Ref: [4.5:56-63], [5.0:101-109], [5.1:126-133], [5.2:252-254]
 //
 // modifier ->
@@ -3723,15 +3756,9 @@ struct OmpDeviceTypeClause {
 //  motion-modifier ->
 //    PRESENT | mapper-modifier | iterator-modifier
 struct OmpFromClause {
-  ENUM_CLASS(Expectation, Present);
   TUPLE_CLASS_BOILERPLATE(OmpFromClause);
-
-  // As in the case of MAP, modifiers are parsed as lists, even if they
-  // are unique. These restrictions will be checked in semantic checks.
-  std::tuple<std::optional<std::list<Expectation>>,
-      std::optional<std::list<OmpIterator>>, OmpObjectList,
-      bool> // were the modifiers comma-separated?
-      t;
+  MODIFIER_BOILERPLATE(OmpExpectation, OmpIterator, OmpMapper);
+  std::tuple<MODIFIERS(), OmpObjectList, bool> t;
 };
 
 // OMP 5.2 12.6.1 grainsize-clause -> grainsize ([prescriptiveness :] value)
@@ -3794,8 +3821,6 @@ struct OmpLinearClause {
   std::variant<WithModifier, WithoutModifier> u;
 };
 
-WRAPPER_CLASS(OmpMapperIdentifier, std::optional<Name>);
-
 // 2.15.5.1 map ->
 //    MAP ([MAPPER(mapper-identifier)] [[map-type-modifier-list [,]]
 //    [iterator-modifier [,]] map-type : ]
@@ -3804,21 +3829,9 @@ WRAPPER_CLASS(OmpMapperIdentifier, std::optional<Name>);
 // map-type-modifier -> ALWAYS | CLOSE | PRESENT | OMPX_HOLD
 // map-type -> TO | FROM | TOFROM | ALLOC | RELEASE | DELETE
 struct OmpMapClause {
-  ENUM_CLASS(TypeModifier, Always, Close, Present, Ompx_Hold);
-  ENUM_CLASS(Type, To, From, Tofrom, Alloc, Release, Delete)
   TUPLE_CLASS_BOILERPLATE(OmpMapClause);
-
-  // All modifiers are parsed into optional lists, even if they are unique.
-  // The checks for satisfying those constraints are deferred to semantics.
-  // In OpenMP 5.2 the non-comma syntax has been deprecated: keep the
-  // information about separator presence to emit a diagnostic if needed.
-  std::tuple<OmpMapperIdentifier, // Mapper name
-      std::optional<std::list<TypeModifier>>,
-      std::optional<std::list<OmpIterator>>, // unique
-      std::optional<std::list<Type>>, // unique
-      OmpObjectList,
-      bool> // were the modifiers comma-separated?
-      t;
+  MODIFIER_BOILERPLATE(OmpMapTypeModifier, OmpMapper, OmpIterator, OmpMapType);
+  std::tuple<MODIFIERS(), OmpObjectList, bool> t;
 };
 
 // Ref: [5.0:101-109], [5.1:126-134], [5.2:233-234]
@@ -3877,15 +3890,9 @@ struct OmpScheduleClause {
 //  motion-modifier ->
 //    PRESENT | mapper-modifier | iterator-modifier
 struct OmpToClause {
-  using Expectation = OmpFromClause::Expectation;
   TUPLE_CLASS_BOILERPLATE(OmpToClause);
-
-  // As in the case of MAP, modifiers are parsed as lists, even if they
-  // are unique. These restrictions will be checked in semantic checks.
-  std::tuple<std::optional<std::list<Expectation>>,
-      std::optional<std::list<OmpIterator>>, OmpObjectList,
-      bool> // were the modifiers comma-separated?
-      t;
+  MODIFIER_BOILERPLATE(OmpExpectation, OmpIterator, OmpMapper);
+  std::tuple<MODIFIERS(), OmpObjectList, bool> t;
 };
 
 // OMP 5.2 12.6.2 num_tasks-clause -> num_tasks ([prescriptiveness :] value)
diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
index beab4c9b46a21a..3b3aff37f082d3 100644
--- a/flang/include/flang/Semantics/openmp-modifiers.h
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -10,6 +10,7 @@
 #define FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
 
 #include "flang/Common/enum-set.h"
+#include "flang/Parser/characters.h"
 #include "flang/Parser/parse-tree.h"
 #include "flang/Semantics/semantics.h"
 #include "llvm/ADT/STLExtras.h"
@@ -18,6 +19,7 @@
 
 #include <cassert>
 #include <map>
+#include <memory>
 #include <optional>
 #include <variant>
 
@@ -51,6 +53,7 @@ struct OmpModifierDescriptor {
   // Modifier name for use in diagnostic messages.
   const OmpProperties &props(unsigned version) const;
   const OmpClauses &clauses(unsigned version) const;
+  unsigned since(llvm::omp::Clause id) const;
 
   const llvm::StringRef name;
   // Version-dependent properties of the modifier.
@@ -61,26 +64,25 @@ struct OmpModifierDescriptor {
 
 template <typename SpecificTy> const OmpModifierDescriptor &OmpGetDescriptor();
 
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpChunkModifier>();
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpDependenceType>();
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpIterator>();
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLinearModifier>();
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpOrderModifier>();
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpOrderingModifier>();
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpReductionIdentifier>();
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpReductionModifier>();
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpTaskDependenceType>();
-template <>
-const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpVariableCategory>();
+#define DECLARE_DESCRIPTOR(name) \
+  template <> const OmpModifierDescriptor &OmpGetDescriptor<name>();
+
+DECLARE_DESCRIPTOR(parser::OmpChunkModifier);
+DECLARE_DESCRIPTOR(parser::OmpDependenceType);
+DECLARE_DESCRIPTOR(parser::OmpExpectation);
+DECLARE_DESCRIPTOR(parser::OmpIterator);
+DECLARE_DESCRIPTOR(parser::OmpLinearModifier);
+DECLARE_DESCRIPTOR(parser::OmpMapper);
+DECLARE_DESCRIPTOR(parser::OmpMapType);
+DECLARE_DESCRIPTOR(parser::OmpMapTypeModifier);
+DECLARE_DESCRIPTOR(parser::OmpOrderModifier);
+DECLARE_DESCRIPTOR(parser::OmpOrderingModifier);
+DECLARE_DESCRIPTOR(parser::OmpReductionIdentifier);
+DECLARE_DESCRIPTOR(parser::OmpReductionModifier);
+DECLARE_DESCRIPTOR(parser::OmpTaskDependenceType);
+DECLARE_DESCRIPTOR(parser::OmpVariableCategory);
+
+#undef DECLARE_DESCRIPTOR
 
 // Explanation of terminology:
 //
@@ -94,7 +96,7 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpVariableCategory>();
 //     std::tuple<std::optional<std::list<Modifier>>, ...> t;
 //   };
 //
-// The Speficic1, etc. refer to parser classes that represent modifiers,
+// The Specific1, etc. refer to parser classes that represent modifiers,
 // e.g. OmpIterator or OmpTaskDependenceType. The Variant type contains
 // all modifiers that are allowed for a given clause. The Modifier class
 // is there to wrap the variant into the form that the parse tree visitor
@@ -148,39 +150,110 @@ typename std::list<UnionTy>::const_iterator findInRange(
 }
 } // namespace detail
 
-/// Finds the entry in the list that holds the `SpecificTy` alternative,
+/// Finds the first entry in the list that holds the `SpecificTy` alternative,
 /// and returns the pointer to that alternative. If such an entry does not
 /// exist, it returns nullptr.
-/// The list is assumed to contain at most one such item, with a check
-/// whether the condition is met.
-/// This function should only be called after the verification of modifier
-/// properties has been performed, since it will assert if multiple items
-/// are found.
 template <typename SpecificTy, typename UnionTy>
 const SpecificTy *OmpGetUniqueModifier(
     const std::optional<std::list<UnionTy>> &modifiers) {
   const SpecificTy *found{nullptr};
   if (modifiers) {
     auto end{modifiers->cend()};
-    // typename std::list<UnionTy>::iterator end{modifiers->end()};
     auto at{detail::findInRange<SpecificTy, UnionTy>(modifiers->cbegin(), end)};
     if (at != end) {
       found = &std::get<SpecificTy>(at->u);
-#ifndef NDEBUG
-      auto another{
-          detail::findInRange<SpecificTy, UnionTy>(std::next(at), end)};
-      assert(another == end && "repeated modifier");
-#endif
     }
   }
   return found;
 }
 
+template <typename SpecificTy> struct OmpSpecificModifierIterator {
+  using VectorTy = std::vector<const SpecificTy *>;
+  OmpSpecificModifierIterator(
+      std::shared_ptr<VectorTy> list, typename VectorTy::const_iterator where)
+      : specificList(list), at(where) {}
+
+  OmpSpecificModifierIterator &operator++() {
+    ++at;
+    return *this;
+  }
+  // OmpSpecificModifierIterator &operator++(int);
+  OmpSpecificModifierIterator &operator--() {
+    --at;
+    return *this;
+  }
+  // OmpSpecificModifierIterator &operator--(int);
+
+  const SpecificTy *operator*() const { return *at; }
+  bool operator==(const OmpSpecificModifierIterator &other) const {
+    assert(specificList.get() == other.specificList.get() &&
+        "comparing unrelated iterators");
+    return at == other.at;
+  }
+  bool operator!=(const OmpSpecificModifierIterator &other) const {
+    return !(*this == other);
+  }
+
+private:
+  std::shared_ptr<VectorTy> specificList;
+  typename VectorTy::const_iterator at;
+};
+
+template <typename SpecificTy, typename UnionTy>
+llvm::iterator_range<OmpSpecificModifierIterator<SpecificTy>>
+OmpGetRepeatableModifier(const std::optional<std::list<UnionTy>> &modifiers) {
+  using VectorTy = std::vector<const SpecificTy *>;
+  std::shared_ptr<VectorTy> items(new VectorTy);
+  if (modifiers) {
+    for (auto &m : *modifiers) {
+      if (auto *s = std::get_if<SpecificTy>(&m.u)) {
+        items->push_back(s);
+      }
+    }
+  }
+  return llvm::iterator_range(
+      OmpSpecificModifierIterator(items, items->begin()),
+      OmpSpecificModifierIterator(items, items->end()));
+}
+
+template <typename SpecificTy, typename UnionTy>
+llvm::iterator_range<OmpSpecificModifierIterator<SpecificTy>>
+OmpGetRepeatableModifier(std::optional<std::list<UnionTy>> &&) = delete;
+
 namespace detail {
 template <typename T> constexpr const T *make_nullptr() {
   return static_cast<const T *>(nullptr);
 }
 
+/// Verify that all modifiers are allowed in the given OpenMP version.
+template <typename UnionTy>
+bool verifyVersions(const std::optional<std::list<UnionTy>> &modifiers,
+    llvm::omp::Clause id, parser::CharBlock clauseSource,
+    SemanticsContext &semaCtx) {
+  if (!modifiers) {
+    return true;
+  }
+  unsigned version{semaCtx.langOptions().OpenMPVersion};
+  bool result{true};
+  for (auto &m : *modifiers) {
+    const OmpModifierDescriptor &desc{OmpGetDescriptor(m)};
+    unsigned since{desc.since(id)};
+    if (since == ~0u) {
+      // This shouldn't really happen, but have it just in case.
+      semaCtx.Say(m.source,
+          "'%s' modifier is not supported on %s clause"_err_en_US,
+          desc.name.str(),
+          parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
+    } else if (version < since) {
+      semaCtx.Say(m.source,
+          "'%s' modifier is not supported in OpenMP v%d.%d, try -fopenmp-version=%d"_warn_en_US,
+          desc.name.str(), version / 10, version % 10, since);
+      result = false;
+    }
+  }
+  return result;
+}
+
 /// Helper function for verifying the Required property:
 /// For a specific SpecificTy, if SpecificTy is has the Required property,
 /// check if the list has an item that holds SpecificTy as an alternative.
@@ -201,7 +274,7 @@ bool verifyIfRequired(const SpecificTy *,
   });
   if (!present) {
     semaCtx.Say(
-        clauseSource, "A %s modifier is required"_err_en_US, desc.name.str());
+        clauseSource, "'%s' modifier is required"_err_en_US, desc.name.str());
   }
   return present;
 }
@@ -224,7 +297,8 @@ bool verifyRequiredPack(const std::optional<std::list<UnionTy>> &modifiers,
 /// list is valid, or false otherwise.
 template <typename UnionTy>
 bool verifyRequired(const std::optional<std::list<UnionTy>> &modifiers,
-    parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
+    llvm::omp::Clause id, parser::CharBlock clauseSource,
+    SemanticsContext &semaCtx) {
   using VariantTy = typename UnionTy::Variant;
   return verifyRequiredPack(modifiers, clauseSource, semaCtx,
       std::make_index_sequence<std::variant_size_v<VariantTy>>{});
@@ -253,7 +327,8 @@ bool verifyIfUnique(const SpecificTy *,
     auto next{
         detail::findInRange<SpecificTy, UnionTy>(std::next(specific), end)};
     if (next != end) {
-      semaCtx.Say(next->source, "A %s cannot occur multiple times"_err_en_US,
+      semaCtx.Say(next->source,
+          "'%s' modifier cannot occur multiple times"_err_en_US,
           desc.name.str());
     }
   }
@@ -264,7 +339,8 @@ bool verifyIfUnique(const SpecificTy *,
 /// list is valid, or false otherwise.
 template <typename UnionTy>
 bool verifyUnique(const std::optional<std::list<UnionTy>> &modifiers,
-    parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
+    llvm::omp::Clause id, parser::CharBlock clauseSource,
+    SemanticsContext &semaCtx) {
   if (!modifiers) {
     return true;
   }
@@ -284,7 +360,8 @@ bool verifyUnique(const std::optional<std::list<UnionTy>> &modifiers,
 /// list is valid, or false otherwise.
 template <typename UnionTy>
 bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
-    parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
+    llvm::omp::Clause id, parser::CharBlock clauseSource,
+    SemanticsContext &semaCtx) {
   if (!modifiers || modifiers->size() <= 1) {
     return true;
   }
@@ -314,8 +391,8 @@ bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
                 }
                 llvm::StringRef where{isPre ? "last" : "first"};
                 semaCtx.Say(it->source,
-                    "The %s should be the %s modifier"_err_en_US,
-                    desc.name.str(), where.str());
+                    "'%s' should be the %s modifier"_err_en_US, desc.name.str(),
+                    where.str());
                 return false;
               }
               return true;
@@ -330,7 +407,8 @@ bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
 /// list is valid, or false otherwise.
 template <typename UnionTy>
 bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
-    parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
+    llvm::omp::Clause id, parser::CharBlock clauseSource,
+    SemanticsContext &semaCtx) {
   if (!modifiers || modifiers->size() <= 1) {
     return true;
   }
@@ -345,11 +423,11 @@ bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
     const OmpModifierDescriptor &descExcl{OmpGetDescriptor(excl)};
     const OmpModifierDescriptor &descOther{OmpGetDescriptor(other)};
     parser::MessageFormattedText txt(
-        "An exclusive %s cannot be specified together with a modifier of a different type"_err_en_US,
+        "An exclusive '%s' modifier cannot be specified together with a modifier of a different type"_err_en_US,
         descExcl.name.str());
     parser::Message message(excl.source, txt);
     message.Attach(
-        other.source, "%s provided here"_en_US, descOther.name.str());
+        other.source, "'%s' provided here"_en_US, descOther.name.str());
     semaCtx.Say(std::move(message));
   }};
 
@@ -387,14 +465,16 @@ bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
 } // namespace detail
 
 template <typename ClauseTy>
-bool OmpVerifyModifiers(const ClauseTy &clause, parser::CharBlock clauseSource,
-    SemanticsContext &semaCtx) {
+bool OmpVerifyModifiers(const ClauseTy &clause, llvm::omp::Clause id,
+    parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
   auto &modifiers{OmpGetModifiers(clause)};
-  bool result{detail::verifyRequired(modifiers, clauseSource, semaCtx)};
-  result = detail::verifyUnique(modifiers, clauseSource, semaCtx) && result;
-  result = detail::verifyUltimate(modifiers, clauseSource, semaCtx) && result;
-  result = detail::verifyExclusive(modifiers, clauseSource, semaCtx) && result;
-  return result;
+  bool results[]{//
+      detail::verifyVersions(modifiers, id, clauseSource, semaCtx),
+      detail::verifyRequired(modifiers, id, clauseSource, semaCtx),
+      detail::verifyUnique(modifiers, id, clauseSource, semaCtx),
+      detail::verifyUltimate(modifiers, id, clauseSource, semaCtx),
+      detail::verifyExclusive(modifiers, id, clauseSource, semaCtx)};
+  return llvm::all_of(results, [](bool x) { return x; });
 }
 } // namespace Fortran::semantics
 
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 0f2e849c2c6a0e..6baa22a44eafb1 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1000,7 +1000,7 @@ bool ClauseProcessor::processMap(
                      const parser::CharBlock &source) {
     using Map = omp::clause::Map;
     mlir::Location clauseLocation = converter.genLocation(source);
-    const auto &mapType = std::get<std::optional<Map::MapType>>(clause.t);
+    const auto &[mapType, typeMods, mappers, iterator, objects] = clause.t;
     llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
         llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;
     // If the map type is specified, then process it else Tofrom is the
@@ -1029,13 +1029,11 @@ bool ClauseProcessor::processMap(
      ...
[truncated]

This removes the specialized parsers and helper classes for these clauses,
namely ConcatSeparated, MapModifiers, and MotionModifiers. Map and the
motion clauses are now handled in the same way as all other clauses with
modifiers, with one exception: the commas separating their modifiers are
optional. This syntax is deprecated in OpenMP 5.2.

Implement version checks for modifiers: for a given modifier on a given
clause, check if that modifier is allowed on this clause in the specified
OpenMP version. This replaced several individual checks.

Add a testcase for handling map modifiers in a different order, and for
diagnosing an ultimate modifier out of position.
@kparzysz kparzysz force-pushed the users/kparzysz/spr/m06-map-motion branch from db510b8 to e184c0b Compare November 23, 2024 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants