[flang-commits] [flang] 52755ac - [flang][OpenMP] Use new modifier infrastructure for MAP/FROM/TO clauses (#117447)

via flang-commits flang-commits at lists.llvm.org
Mon Nov 25 05:38:16 PST 2024


Author: Krzysztof Parzyszek
Date: 2024-11-25T07:38:12-06:00
New Revision: 52755ac2531529369f1f29b9d0b29645f304f389

URL: https://github.com/llvm/llvm-project/commit/52755ac2531529369f1f29b9d0b29645f304f389
DIFF: https://github.com/llvm/llvm-project/commit/52755ac2531529369f1f29b9d0b29645f304f389.diff

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

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.

Added: 
    

Modified: 
    flang/examples/FeatureList/FeatureList.cpp
    flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
    flang/examples/FlangOmpReport/FlangOmpReportVisitor.h
    flang/include/flang/Parser/dump-parse-tree.h
    flang/include/flang/Parser/parse-tree.h
    flang/include/flang/Semantics/openmp-modifiers.h
    flang/lib/Lower/OpenMP/ClauseProcessor.cpp
    flang/lib/Lower/OpenMP/Clauses.cpp
    flang/lib/Lower/OpenMP/Clauses.h
    flang/lib/Parser/openmp-parsers.cpp
    flang/lib/Parser/unparse.cpp
    flang/lib/Semantics/check-omp-structure.cpp
    flang/lib/Semantics/check-omp-structure.h
    flang/lib/Semantics/openmp-modifiers.cpp
    flang/lib/Semantics/resolve-directives.cpp
    flang/lib/Semantics/resolve-names.cpp
    flang/test/Lower/OpenMP/Todo/map-mapper.f90
    flang/test/Parser/OpenMP/from-clause.f90
    flang/test/Parser/OpenMP/map-modifiers.f90
    flang/test/Parser/OpenMP/target-update-to-clause.f90
    flang/test/Semantics/OpenMP/combined-constructs.f90
    flang/test/Semantics/OpenMP/defaultmap-clause-v45.f90
    flang/test/Semantics/OpenMP/from-clause-v45.f90
    flang/test/Semantics/OpenMP/from-clause-v51.f90
    flang/test/Semantics/OpenMP/map-clause.f90
    flang/test/Semantics/OpenMP/map-modifiers.f90
    flang/test/Semantics/OpenMP/to-clause-v45.f90
    flang/test/Semantics/OpenMP/to-clause-v51.f90

Removed: 
    


################################################################################
diff  --git a/flang/examples/FeatureList/FeatureList.cpp b/flang/examples/FeatureList/FeatureList.cpp
index 7e024dc3875160..6ae92acf20608a 100644
--- a/flang/examples/FeatureList/FeatureList.cpp
+++ b/flang/examples/FeatureList/FeatureList.cpp
@@ -498,8 +498,7 @@ struct NodeVisitor {
   READ_FEATURE(OmpLinearModifier::Value)
   READ_FEATURE(OmpLoopDirective)
   READ_FEATURE(OmpMapClause)
-  READ_FEATURE(OmpMapClause::TypeModifier)
-  READ_FEATURE(OmpMapClause::Type)
+  READ_FEATURE(OmpMapClause::Modifier)
   READ_FEATURE(OmpNumTasksClause)
   READ_FEATURE(OmpNumTasksClause::Prescriptiveness)
   READ_FEATURE(OmpObject)

diff  --git a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
index a3d9b0cfdc79b8..5bd8c761992781 100644
--- a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
+++ b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp
@@ -229,8 +229,8 @@ void OpenMPCounterVisitor::Post(const OmpTaskDependenceType::Value &c) {
   clauseDetails +=
       "type=" + std::string{OmpTaskDependenceType::EnumToString(c)} + ";";
 }
-void OpenMPCounterVisitor::Post(const OmpMapClause::Type &c) {
-  clauseDetails += "type=" + std::string{OmpMapClause::EnumToString(c)} + ";";
+void OpenMPCounterVisitor::Post(const OmpMapType::Value &c) {
+  clauseDetails += "type=" + std::string{OmpMapType::EnumToString(c)} + ";";
 }
 void OpenMPCounterVisitor::Post(const OmpScheduleClause::Kind &c) {
   clauseDetails +=

diff  --git a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.h b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.h
index 86f206ba85c6dc..7e9ae94bef2973 100644
--- a/flang/examples/FlangOmpReport/FlangOmpReportVisitor.h
+++ b/flang/examples/FlangOmpReport/FlangOmpReportVisitor.h
@@ -75,7 +75,7 @@ struct OpenMPCounterVisitor {
   void Post(const OmpLinearModifier::Value &c);
   void Post(const OmpOrderingModifier::Value &c);
   void Post(const OmpTaskDependenceType::Value &c);
-  void Post(const OmpMapClause::Type &c);
+  void Post(const OmpMapType::Value &c);
   void Post(const OmpScheduleClause::Kind &c);
   void Post(const OmpIfClause::DirectiveNameModifier &c);
   void Post(const OmpCancelType::Type &c);

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..8d7119a56b7f8e 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,34 @@ struct OmpLinearModifier {
   WRAPPER_CLASS_BOILERPLATE(OmpLinearModifier, Value);
 };
 
+// Ref: [5.0:176-180], [5.1:205-210], [5.2:149-150]
+//
+// mapper ->
+//    identifier                                    // since 4.5
+struct OmpMapper {
+  WRAPPER_CLASS_BOILERPLATE(OmpMapper, Name);
+};
+
+// Ref: [4.5:216-219], [5.0:315-324], [5.1:347-355], [5.2:150-158]
+//
+// map-type ->
+//    ALLOC | DELETE | FROM | RELEASE | TO | TOFROM // since 4.5
+struct OmpMapType {
+  ENUM_CLASS(Value, Alloc, Delete, From, Release, To, Tofrom);
+  WRAPPER_CLASS_BOILERPLATE(OmpMapType, Value);
+};
+
+// Ref: [4.5:216-219], [5.0:315-324], [5.1:347-355], [5.2:150-158]
+//
+// map-type-modifier ->
+//    ALWAYS |                                      // since 4.5
+//    CLOSE |                                       // since 5.0
+//    PRESENT                                       // since 5.1
+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 ->
@@ -3546,10 +3589,10 @@ struct OmpOrderModifier {
 // Ref: [4.5:201-207], [5.0:293-299], [5.1:325-331], [5.2:124]
 //
 // reduction-identifier ->
-//   base-language-identifier |                     // since 4.5
-//   - |                                            // since 4.5, until 5.2
-//   + | * | .AND. | .OR. | .EQV. | .NEQV. |        // since 4.5
-//   MIN | MAX | IAND | IOR | IEOR                  // since 4.5
+//    base-language-identifier |                    // since 4.5
+//    - |                                           // since 4.5, until 5.2
+//    + | * | .AND. | .OR. | .EQV. | .NEQV. |       // since 4.5
+//    MIN | MAX | IAND | IOR | IEOR                 // since 4.5
 struct OmpReductionIdentifier {
   UNION_CLASS_BOILERPLATE(OmpReductionIdentifier);
   std::variant<DefinedOperator, ProcedureDesignator> u;
@@ -3558,7 +3601,7 @@ struct OmpReductionIdentifier {
 // Ref: [5.0:300-302], [5.1:332-334], [5.2:134-137]
 //
 // reduction-modifier ->
-//   DEFAULT | INSCAN | TASK                        // since 5.0
+//    DEFAULT | INSCAN | TASK                       // since 5.0
 struct OmpReductionModifier {
   ENUM_CLASS(Value, Default, Inscan, Task);
   WRAPPER_CLASS_BOILERPLATE(OmpReductionModifier, Value);
@@ -3578,9 +3621,9 @@ struct OmpTaskDependenceType {
 // Ref: [4.5:229-230], [5.0:324-325], [5.1:357-358], [5.2:161-162]
 //
 // variable-category ->
-//   SCALAR |                                       // since 4.5
-//   AGGREGATE | ALLOCATABLE | POINTER |            // since 5.0
-//   ALL                                            // since 5.2
+//    SCALAR |                                      // since 4.5
+//    AGGREGATE | ALLOCATABLE | POINTER |           // since 5.0
+//    ALL                                           // since 5.2
 struct OmpVariableCategory {
   ENUM_CLASS(Value, Aggregate, All, Allocatable, Pointer, Scalar)
   WRAPPER_CLASS_BOILERPLATE(OmpVariableCategory, Value);
@@ -3723,15 +3766,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,31 +3831,19 @@ 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 : ]
-//         variable-name-list)
-// map-type-modifier-list -> map-type-modifier [,] [...]
-// map-type-modifier -> ALWAYS | CLOSE | PRESENT | OMPX_HOLD
-// map-type -> TO | FROM | TOFROM | ALLOC | RELEASE | DELETE
+// Ref: [4.5:216-219], [5.0:315-324], [5.1:347-355], [5.2:150-158]
+//
+// map-clause ->
+//    MAP([modifier...:] locator-list)              // since 4.5
+// modifier ->
+//    map-type-modifier |                           // since 4.5
+//    mapper |                                      // since 5.0
+//    iterator |                                    // since 5.1
+//    map-type                                      // since 4.5
 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]
@@ -3869,23 +3894,17 @@ struct OmpScheduleClause {
 // Ref: [4.5:107-109], [5.0:176-180], [5.1:205-210], [5.2:167-168]
 //
 // to-clause (in DECLARE TARGET) ->
-//    TO(extended-list) |                               // until 5.1
+//    TO(extended-list) |                           // until 5.1
 // to-clause (in TARGET UPDATE) ->
 //    TO(locator-list) |
-//    TO(mapper-modifier: locator-list) |               // since 5.0
-//    TO(motion-modifier[,] ...: locator-list)          // since 5.1
-//  motion-modifier ->
+//    TO(mapper-modifier: locator-list) |           // since 5.0
+//    TO(motion-modifier[,] ...: locator-list)      // since 5.1
+// 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)
@@ -3897,8 +3916,10 @@ struct OmpNumTasksClause {
 
 // Ref: [5.0:254-255], [5.1:287-288], [5.2:321-322]
 //
-// update-clause -> UPDATE(dependence-type)       // since 5.0, until 5.1
-// update-clause -> UPDATE(task-dependence-type)  // since 5.2
+// update-clause ->
+//    UPDATE(dependence-type)                       // since 5.0, until 5.1
+// update-clause ->
+//    UPDATE(task-dependence-type)                  // since 5.2
 struct OmpUpdateClause {
   UNION_CLASS_BOILERPLATE(OmpUpdateClause);
   std::variant<OmpDependenceType, OmpTaskDependenceType> u;

diff  --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
index beab4c9b46a21a..60f116e6f00339 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 
diff erent type"_err_en_US,
+        "An exclusive '%s' modifier cannot be specified together with a modifier of a 
diff erent 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(
       mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE;
     }
 
-    auto &modTypeMods =
-        std::get<std::optional<Map::MapTypeModifiers>>(clause.t);
-    if (modTypeMods) {
-      if (llvm::is_contained(*modTypeMods, Map::MapTypeModifier::Always))
+    if (typeMods) {
+      if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Always))
         mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS;
       // Diagnose unimplemented map-type-modifiers.
-      if (llvm::any_of(*modTypeMods, [](Map::MapTypeModifier m) {
+      if (llvm::any_of(*typeMods, [](Map::MapTypeModifier m) {
             return m != Map::MapTypeModifier::Always;
           })) {
         TODO(currentLocation, "Map type modifiers (other than 'ALWAYS')"
@@ -1043,10 +1041,14 @@ bool ClauseProcessor::processMap(
       }
     }
 
-    if (std::get<std::optional<omp::clause::Iterator>>(clause.t)) {
+    if (iterator) {
       TODO(currentLocation,
            "Support for iterator modifiers is not implemented yet");
     }
+    if (mappers) {
+      TODO(currentLocation,
+           "Support for mapper modifiers is not implemented yet");
+    }
 
     processMapObjects(stmtCtx, clauseLocation,
                       std::get<omp::ObjectList>(clause.t), mapTypeBits,

diff  --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
index 8639d08827f4ed..bf20f42bdecaf1 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -584,7 +584,7 @@ Defaultmap make(const parser::OmpClause::Defaultmap &inp,
       // clang-format on
   );
 
-  auto &mods{semantics::OmpGetModifiers(inp.v)};
+  auto &mods = semantics::OmpGetModifiers(inp.v);
   auto &t0 = std::get<wrapped::ImplicitBehavior>(inp.v.t);
   auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpVariableCategory>(mods);
 
@@ -764,37 +764,35 @@ Firstprivate make(const parser::OmpClause::Firstprivate &inp,
 From make(const parser::OmpClause::From &inp,
           semantics::SemanticsContext &semaCtx) {
   // inp.v -> parser::OmpFromClause
-  using wrapped = parser::OmpFromClause;
-
   CLAUSET_ENUM_CONVERT( //
-      convert, parser::OmpFromClause::Expectation, From::Expectation,
+      convert, parser::OmpExpectation::Value, From::Expectation,
       // clang-format off
       MS(Present, Present)
       // clang-format on
   );
 
-  auto &t0 = std::get<std::optional<std::list<wrapped::Expectation>>>(inp.v.t);
-  auto &t1 = std::get<std::optional<std::list<parser::OmpIterator>>>(inp.v.t);
-  auto &t2 = std::get<parser::OmpObjectList>(inp.v.t);
-
-  assert((!t0 || t0->size() == 1) && "Only one expectation modifier allowed");
-  assert((!t1 || t1->size() == 1) && "Only one iterator modifier allowed");
+  auto &mods = semantics::OmpGetModifiers(inp.v);
+  auto *t0 = semantics::OmpGetUniqueModifier<parser::OmpExpectation>(mods);
+  auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpMapper>(mods);
+  auto *t2 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods);
+  auto &t3 = std::get<parser::OmpObjectList>(inp.v.t);
 
-  auto expectation = [&]() -> std::optional<From::Expectation> {
-    if (t0)
-      return convert(t0->front());
+  auto mappers = [&]() -> std::optional<List<Mapper>> {
+    if (t1)
+      return List<Mapper>{Mapper{makeObject(t1->v, semaCtx)}};
     return std::nullopt;
   }();
 
   auto iterator = [&]() -> std::optional<Iterator> {
-    if (t1)
-      return makeIterator(t1->front(), semaCtx);
+    if (t2)
+      return makeIterator(*t2, semaCtx);
     return std::nullopt;
   }();
 
-  return From{{/*Expectation=*/std::move(expectation), /*Mapper=*/std::nullopt,
+  return From{{/*Expectation=*/maybeApplyToV(convert, t0),
+               /*Mappers=*/std::move(mappers),
                /*Iterator=*/std::move(iterator),
-               /*LocatorList=*/makeObjects(t2, semaCtx)}};
+               /*LocatorList=*/makeObjects(t3, semaCtx)}};
 }
 
 // Full: empty
@@ -963,10 +961,8 @@ Link make(const parser::OmpClause::Link &inp,
 Map make(const parser::OmpClause::Map &inp,
          semantics::SemanticsContext &semaCtx) {
   // inp.v -> parser::OmpMapClause
-  using wrapped = parser::OmpMapClause;
-
   CLAUSET_ENUM_CONVERT( //
-      convert1, parser::OmpMapClause::Type, Map::MapType,
+      convert1, parser::OmpMapType::Value, Map::MapType,
       // clang-format off
       MS(Alloc,    Alloc)
       MS(Delete,   Delete)
@@ -978,7 +974,7 @@ Map make(const parser::OmpClause::Map &inp,
   );
 
   CLAUSET_ENUM_CONVERT( //
-      convert2, parser::OmpMapClause::TypeModifier, Map::MapTypeModifier,
+      convert2, parser::OmpMapTypeModifier::Value, Map::MapTypeModifier,
       // clang-format off
       MS(Always,    Always)
       MS(Close,     Close)
@@ -987,42 +983,43 @@ Map make(const parser::OmpClause::Map &inp,
       // clang-format on
   );
 
-  auto &t0 = std::get<std::optional<std::list<wrapped::TypeModifier>>>(inp.v.t);
-  auto &t1 = std::get<std::optional<std::list<parser::OmpIterator>>>(inp.v.t);
-  auto &t2 = std::get<std::optional<std::list<wrapped::Type>>>(inp.v.t);
-  auto &t3 = std::get<parser::OmpObjectList>(inp.v.t);
-  auto &t4 = std::get<parser::OmpMapperIdentifier>(inp.v.t);
-
-  if (t4.v)
-    TODO_NOLOC("OmpMapClause(MAPPER(...)): user defined mapper not supported");
+  auto &mods = semantics::OmpGetModifiers(inp.v);
+  auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpMapper>(mods);
+  auto *t2 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods);
+  auto *t3 = semantics::OmpGetUniqueModifier<parser::OmpMapType>(mods);
+  auto &t4 = std::get<parser::OmpObjectList>(inp.v.t);
 
-  // These should have been diagnosed already.
-  assert((!t1 || t1->size() == 1) && "Only one iterator modifier is allowed");
-  assert((!t2 || t2->size() == 1) && "Only one map type is allowed");
+  auto mappers = [&]() -> std::optional<List<Mapper>> {
+    if (t1)
+      return List<Mapper>{Mapper{makeObject(t1->v, semaCtx)}};
+    return std::nullopt;
+  }();
 
   auto iterator = [&]() -> std::optional<Iterator> {
-    if (t1)
-      return makeIterator(t1->front(), semaCtx);
+    if (t2)
+      return makeIterator(*t2, semaCtx);
     return std::nullopt;
   }();
 
-  std::optional<Map::MapType> maybeType;
-  if (t2)
-    maybeType = maybeApply(convert1, std::optional<wrapped::Type>(t2->front()));
+  auto type = [&]() -> std::optional<Map::MapType> {
+    if (t3)
+      return convert1(t3->v);
+    return Map::MapType::Tofrom;
+  }();
 
-  std::optional<Map::MapTypeModifiers> maybeTypeMods = maybeApply(
-      [&](const std::list<wrapped::TypeModifier> &typeMods) {
-        Map::MapTypeModifiers mods;
-        for (wrapped::TypeModifier mod : typeMods)
-          mods.push_back(convert2(mod));
-        return mods;
-      },
-      t0);
+  Map::MapTypeModifiers typeMods;
+  for (auto *typeMod :
+       semantics::OmpGetRepeatableModifier<parser::OmpMapTypeModifier>(mods)) {
+    typeMods.push_back(convert2(typeMod->v));
+  }
+  std::optional<Map::MapTypeModifiers> maybeTypeMods{};
+  if (!typeMods.empty())
+    maybeTypeMods = std::move(typeMods);
 
-  return Map{{/*MapType=*/maybeType,
-              /*MapTypeModifiers=*/maybeTypeMods,
-              /*Mapper=*/std::nullopt, /*Iterator=*/std::move(iterator),
-              /*LocatorList=*/makeObjects(t3, semaCtx)}};
+  return Map{{/*MapType=*/std::move(type),
+              /*MapTypeModifiers=*/std::move(maybeTypeMods),
+              /*Mapper=*/std::move(mappers), /*Iterator=*/std::move(iterator),
+              /*LocatorList=*/makeObjects(t4, semaCtx)}};
 }
 
 // Match: incomplete
@@ -1316,37 +1313,35 @@ ThreadLimit make(const parser::OmpClause::ThreadLimit &inp,
 To make(const parser::OmpClause::To &inp,
         semantics::SemanticsContext &semaCtx) {
   // inp.v -> parser::OmpToClause
-  using wrapped = parser::OmpToClause;
-
   CLAUSET_ENUM_CONVERT( //
-      convert, parser::OmpToClause::Expectation, To::Expectation,
+      convert, parser::OmpExpectation::Value, To::Expectation,
       // clang-format off
       MS(Present, Present)
       // clang-format on
   );
 
-  auto &t0 = std::get<std::optional<std::list<wrapped::Expectation>>>(inp.v.t);
-  auto &t1 = std::get<std::optional<std::list<parser::OmpIterator>>>(inp.v.t);
-  auto &t2 = std::get<parser::OmpObjectList>(inp.v.t);
-
-  assert((!t0 || t0->size() == 1) && "Only one expectation modifier allowed");
-  assert((!t1 || t1->size() == 1) && "Only one iterator modifier allowed");
+  auto &mods = semantics::OmpGetModifiers(inp.v);
+  auto *t0 = semantics::OmpGetUniqueModifier<parser::OmpExpectation>(mods);
+  auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpMapper>(mods);
+  auto *t2 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods);
+  auto &t3 = std::get<parser::OmpObjectList>(inp.v.t);
 
-  auto expectation = [&]() -> std::optional<To::Expectation> {
-    if (t0)
-      return convert(t0->front());
+  auto mappers = [&]() -> std::optional<List<Mapper>> {
+    if (t1)
+      return List<Mapper>{Mapper{makeObject(t1->v, semaCtx)}};
     return std::nullopt;
   }();
 
   auto iterator = [&]() -> std::optional<Iterator> {
-    if (t1)
-      return makeIterator(t1->front(), semaCtx);
+    if (t2)
+      return makeIterator(*t2, semaCtx);
     return std::nullopt;
   }();
 
-  return To{{/*Expectation=*/std::move(expectation), /*Mapper=*/std::nullopt,
+  return To{{/*Expectation=*/maybeApplyToV(convert, t0),
+             /*Mappers=*/{std::move(mappers)},
              /*Iterator=*/std::move(iterator),
-             /*LocatorList=*/makeObjects(t2, semaCtx)}};
+             /*LocatorList=*/makeObjects(t3, semaCtx)}};
 }
 
 // UnifiedAddress: empty

diff  --git a/flang/lib/Lower/OpenMP/Clauses.h b/flang/lib/Lower/OpenMP/Clauses.h
index 9c74404801bbc0..5fac5c2271c3bf 100644
--- a/flang/lib/Lower/OpenMP/Clauses.h
+++ b/flang/lib/Lower/OpenMP/Clauses.h
@@ -168,6 +168,7 @@ std::optional<Object> getBaseObject(const Object &object,
 
 namespace clause {
 using Range = tomp::type::RangeT<ExprTy>;
+using Mapper = tomp::type::MapperT<IdTy, ExprTy>;
 using Iterator = tomp::type::IteratorT<TypeTy, IdTy, ExprTy>;
 using IteratorSpecifier = tomp::type::IteratorSpecifierT<TypeTy, IdTy, ExprTy>;
 using DefinedOperator = tomp::type::DefinedOperatorT<IdTy, ExprTy>;

diff  --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index ceae20270d13d0..2040a3e7ed5aeb 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -23,137 +23,36 @@ namespace Fortran::parser {
 constexpr auto startOmpLine = skipStuffBeforeStatement >> "!$OMP "_sptok;
 constexpr auto endOmpLine = space >> endOfLine;
 
-// Helper class to deal with a list of modifiers of various types.
-// The list (to be parsed) is assumed to start with all modifiers of the
-// first type, followed by a list of modifiers of the second type, etc.
-// Each list can be empty, e.g.
-//   mod_of_kind_2, mod_of_kind_3, mod_of_kind_5, ...
-// The result type is a tuple of optional lists of each modifier type.
-template <typename Separator, typename Parser, typename... Parsers>
-struct ConcatSeparated {
-  template <typename P>
-  using OptListOf = std::optional<std::list<typename P::resultType>>;
-  template <typename P> using TupleFor = std::tuple<OptListOf<P>>;
-
-  using resultType = std::tuple<OptListOf<Parser>, OptListOf<Parsers>...>;
-
-  constexpr ConcatSeparated(ConcatSeparated &&) = default;
-  constexpr ConcatSeparated(const ConcatSeparated &) = default;
-  constexpr ConcatSeparated(Separator sep, Parser p, Parsers... ps)
-      : parser_(p), sepAndParsers_(sep, ps...) {}
+template <typename Clause, typename Separator> struct ModifierList {
+  constexpr ModifierList(Separator sep) : sep_(sep) {}
+  constexpr ModifierList(const ModifierList &) = default;
+  constexpr ModifierList(ModifierList &&) = default;
 
-  std::optional<resultType> Parse(ParseState &state) const {
-    // firstParser is a list parser, it returns optional<list>.
-    auto firstParser =
-        attempt(nonemptySeparated(parser_, std::get<0>(sepAndParsers_)));
-
-    if constexpr (sizeof...(Parsers) == 0) {
-      return TupleFor<Parser>{std::move(firstParser.Parse(state))};
-    } else {
-      using restParserType = ConcatSeparated<Separator, Parsers...>;
-      auto restParser = std::make_from_tuple<restParserType>(sepAndParsers_);
-
-      if (auto first{firstParser.Parse(state)}) {
-        if (attempt(std::get<0>(sepAndParsers_)).Parse(state)) {
-          return std::tuple_cat(TupleFor<Parser>(std::move(*first)),
-              std::move(*restParser.Parse(state)));
-        }
-        return std::tuple_cat(TupleFor<Parser>{std::move(*first)},
-            std::tuple<OptListOf<Parsers>...>{});
-      }
-      return std::tuple_cat(
-          TupleFor<Parser>{}, std::move(*restParser.Parse(state)));
-    }
-  }
-
-private:
-  const Parser parser_;
-  const std::tuple<Separator, Parsers...> sepAndParsers_;
-};
-
-// Map modifiers come from four categories:
-// - map-type-modifier,
-// - mapper (not parsed yet),
-// - iterator,
-// - map-type.
-// There can be zero or more map-type-modifiers, and zero or one modifier
-// of every other kind.
-// Syntax-wise they look like a single list, where the last element could
-// be a map-type, and all elements in that list are comma-separated[1].
-// Only if there was at least one modifier (of any kind) specified, the
-// list must end with ":".
-// There are complications coming from the fact that the comma separating the
-// two kinds of modifiers is only allowed if there is at least one modifier of
-// each kind. The MapModifiers parser utilizes the ConcatSeparated parser, which
-// takes care of that. ConcatSeparated returns a tuple with optional lists of
-// modifiers for every type.
-// [1] Any of the commas are optional, but that syntax has been deprecated
-// in OpenMP 5.2, and the parsing code keeps a record of whether the commas
-// were present.
-template <typename Separator> struct MapModifiers {
-  constexpr MapModifiers(Separator sep) : sep_(sep) {}
-  constexpr MapModifiers(const MapModifiers &) = default;
-  constexpr MapModifiers(MapModifiers &&) = default;
-
-  // Parsing of mappers is not supported yet.
-  using TypeModParser = Parser<OmpMapClause::TypeModifier>;
-  using IterParser = Parser<OmpIterator>;
-  using TypeParser = Parser<OmpMapClause::Type>;
-  using ModParser =
-      ConcatSeparated<Separator, TypeModParser, IterParser, TypeParser>;
-
-  using resultType = typename ModParser::resultType;
+  using resultType = std::list<typename Clause::Modifier>;
 
   std::optional<resultType> Parse(ParseState &state) const {
-    auto mp = ModParser(sep_, TypeModParser{}, IterParser{}, TypeParser{});
-    auto mods = mp.Parse(state);
-    // The ModParser always "succeeds", i.e. even if the input is junk, it
-    // will return a tuple filled with nullopts. If any of the components
-    // is not a nullopt, expect a ":".
-    if (std::apply([](auto &&...opts) { return (... || !!opts); }, *mods)) {
+    auto listp{nonemptySeparated(Parser<typename Clause::Modifier>{}, sep_)};
+    if (auto result{attempt(listp).Parse(state)}) {
       if (!attempt(":"_tok).Parse(state)) {
         return std::nullopt;
       }
+      return std::move(result);
     }
-    return std::move(mods);
+    return resultType{};
   }
 
 private:
   const Separator sep_;
 };
 
-// This is almost exactly the same thing as MapModifiers. It has the same
-// issue (it expects modifiers in a specific order), and the fix for that
-// will change how modifiers are parsed. Instead of making this code more
-// generic, make it simple, and generalize after the fix is in place.
-template <typename Separator> struct MotionModifiers {
-  constexpr MotionModifiers(Separator sep) : sep_(sep) {}
-  constexpr MotionModifiers(const MotionModifiers &) = default;
-  constexpr MotionModifiers(MotionModifiers &&) = default;
-
-  using ExpParser = Parser<OmpFromClause::Expectation>;
-  using IterParser = Parser<OmpIterator>;
-  using ModParser = ConcatSeparated<Separator, ExpParser, IterParser>;
-
-  using resultType = typename ModParser::resultType;
-
-  std::optional<resultType> Parse(ParseState &state) const {
-    auto mp{ModParser(sep_, ExpParser{}, IterParser{})};
-    auto mods{mp.Parse(state)};
-    // The ModParser always "succeeds", i.e. even if the input is junk, it
-    // will return a tuple filled with nullopts. If any of the components
-    // is not a nullopt, expect a ":".
-    if (std::apply([](auto &&...opts) { return (... || !!opts); }, *mods)) {
-      if (!attempt(":"_tok).Parse(state)) {
-        return std::nullopt;
-      }
-    }
-    return std::move(mods);
-  }
-
-private:
-  const Separator sep_;
-};
+// Use a function to create ModifierList because functions allow "partial"
+// template argument deduction: "modifierList<Clause>(sep)" would be legal,
+// while "ModifierList<Clause>(sep)" would complain about a missing template
+// argument "Separator".
+template <typename Clause, typename Separator>
+constexpr ModifierList<Clause, Separator> modifierList(Separator sep) {
+  return ModifierList<Clause, Separator>(sep);
+}
 
 // OpenMP Clauses
 
@@ -192,6 +91,16 @@ static TypeDeclarationStmt makeIterSpecDecl(std::list<ObjectName> &&names) {
 
 // --- Parsers for clause modifiers -----------------------------------
 
+TYPE_PARSER(construct<OmpChunkModifier>( //
+    "SIMD" >> pure(OmpChunkModifier::Value::Simd)))
+
+TYPE_PARSER(construct<OmpDependenceType>(
+    "SINK" >> pure(OmpDependenceType::Value::Sink) ||
+    "SOURCE" >> pure(OmpDependenceType::Value::Source)))
+
+TYPE_PARSER(construct<OmpExpectation>( //
+    "PRESENT" >> pure(OmpExpectation::Value::Present)))
+
 TYPE_PARSER(construct<OmpIteratorSpecifier>(
     // Using Parser<TypeDeclarationStmt> or Parser<EntityDecl> has the problem
     // that they will attempt to treat what follows the '=' as initialization.
@@ -208,12 +117,9 @@ TYPE_PARSER(construct<OmpIteratorSpecifier>(
             makeIterSpecDecl, nonemptyList(Parser<ObjectName>{}) / "="_tok)),
     subscriptTriplet))
 
-TYPE_PARSER(construct<OmpDependenceType>(
-    "SINK" >> pure(OmpDependenceType::Value::Sink) ||
-    "SOURCE" >> pure(OmpDependenceType::Value::Source)))
-
 // [5.0] 2.1.6 iterator -> iterator-specifier-list
-TYPE_PARSER(construct<OmpIterator>("ITERATOR" >>
+TYPE_PARSER(construct<OmpIterator>( //
+    "ITERATOR" >>
     parenthesized(nonemptyList(sourced(Parser<OmpIteratorSpecifier>{})))))
 
 // 2.15.3.7 LINEAR (linear-list: linear-step)
@@ -224,13 +130,29 @@ TYPE_PARSER(construct<OmpLinearModifier>( //
     "VAL" >> pure(OmpLinearModifier::Value::Val) ||
     "UVAL" >> pure(OmpLinearModifier::Value::Uval)))
 
+TYPE_PARSER(construct<OmpMapper>( //
+    "MAPPER"_tok >> parenthesized(Parser<ObjectName>{})))
+
+// map-type -> ALLOC | DELETE | FROM | RELEASE | TO | TOFROM
+TYPE_PARSER(construct<OmpMapType>( //
+    "ALLOC" >> pure(OmpMapType::Value::Alloc) ||
+    "DELETE" >> pure(OmpMapType::Value::Delete) ||
+    "FROM" >> pure(OmpMapType::Value::From) ||
+    "RELEASE" >> pure(OmpMapType::Value::Release) ||
+    "TO"_id >> pure(OmpMapType::Value::To) ||
+    "TOFROM" >> pure(OmpMapType::Value::Tofrom)))
+
+// map-type-modifier -> ALWAYS | CLOSE | OMPX_HOLD | PRESENT
+TYPE_PARSER(construct<OmpMapTypeModifier>(
+    "ALWAYS" >> pure(OmpMapTypeModifier::Value::Always) ||
+    "CLOSE" >> pure(OmpMapTypeModifier::Value::Close) ||
+    "OMPX_HOLD" >> pure(OmpMapTypeModifier::Value::Ompx_Hold) ||
+    "PRESENT" >> pure(OmpMapTypeModifier::Value::Present)))
+
 // 2.15.3.6 REDUCTION (reduction-identifier: variable-name-list)
 TYPE_PARSER(construct<OmpReductionIdentifier>(Parser<DefinedOperator>{}) ||
     construct<OmpReductionIdentifier>(Parser<ProcedureDesignator>{}))
 
-TYPE_PARSER(construct<OmpChunkModifier>( //
-    "SIMD" >> pure(OmpChunkModifier::Value::Simd)))
-
 TYPE_PARSER(construct<OmpOrderModifier>(
     "REPRODUCIBLE" >> pure(OmpOrderModifier::Value::Reproducible) ||
     "UNCONSTRAINED" >> pure(OmpOrderModifier::Value::Unconstrained)))
@@ -261,6 +183,17 @@ TYPE_PARSER(construct<OmpVariableCategory>(
     "SCALAR" >> pure(OmpVariableCategory::Value::Scalar)))
 
 // This could be auto-generated.
+TYPE_PARSER(sourced(construct<OmpFromClause::Modifier>(
+    sourced(construct<OmpFromClause::Modifier>(Parser<OmpExpectation>{}) ||
+        construct<OmpFromClause::Modifier>(Parser<OmpMapper>{}) ||
+        construct<OmpFromClause::Modifier>(Parser<OmpIterator>{})))))
+
+TYPE_PARSER(sourced(construct<OmpMapClause::Modifier>(
+    sourced(construct<OmpMapClause::Modifier>(Parser<OmpMapTypeModifier>{}) ||
+        construct<OmpMapClause::Modifier>(Parser<OmpMapper>{}) ||
+        construct<OmpMapClause::Modifier>(Parser<OmpIterator>{}) ||
+        construct<OmpMapClause::Modifier>(Parser<OmpMapType>{})))))
+
 TYPE_PARSER(
     sourced(construct<OmpOrderClause::Modifier>(Parser<OmpOrderModifier>{})))
 
@@ -273,11 +206,33 @@ TYPE_PARSER(sourced(construct<OmpScheduleClause::Modifier>(sourced(
     construct<OmpScheduleClause::Modifier>(Parser<OmpChunkModifier>{}) ||
     construct<OmpScheduleClause::Modifier>(Parser<OmpOrderingModifier>{})))))
 
+TYPE_PARSER(sourced(construct<OmpToClause::Modifier>(
+    sourced(construct<OmpToClause::Modifier>(Parser<OmpExpectation>{}) ||
+        construct<OmpToClause::Modifier>(Parser<OmpMapper>{}) ||
+        construct<OmpToClause::Modifier>(Parser<OmpIterator>{})))))
+
 TYPE_PARSER(sourced(
     construct<OmpDefaultmapClause::Modifier>(Parser<OmpVariableCategory>{})))
 
 // --- Parsers for clauses --------------------------------------------
 
+/// `MOBClause` is a clause that has a
+///   std::tuple<Modifiers, OmpObjectList, bool>.
+/// Helper function to create a typical modifiers-objects clause, where the
+/// commas separating individual modifiers are optional, and the clause
+/// contains a bool member to indicate whether it was fully comma-separated
+/// or not.
+template <bool CommaSeparated, typename MOBClause>
+static inline MOBClause makeMobClause(
+    std::list<typename MOBClause::Modifier> &&mods, OmpObjectList &&objs) {
+  if (!mods.empty()) {
+    return MOBClause{std::move(mods), std::move(objs), CommaSeparated};
+  } else {
+    using ListTy = std::list<typename MOBClause::Modifier>;
+    return MOBClause{std::optional<ListTy>{}, std::move(objs), CommaSeparated};
+  }
+}
+
 // [5.0] 2.10.1 affinity([aff-modifier:] locator-list)
 //              aff-modifier: interator-modifier
 TYPE_PARSER(construct<OmpAffinityClause>(
@@ -290,53 +245,18 @@ TYPE_PARSER(construct<OmpDefaultClause>(
     "SHARED" >> pure(OmpDefaultClause::Type::Shared) ||
     "NONE" >> pure(OmpDefaultClause::Type::None)))
 
-// 2.5 PROC_BIND (MASTER | CLOSE | PRIMARY | SPREAD )
+// 2.5 PROC_BIND (MASTER | CLOSE | PRIMARY | SPREAD)
 TYPE_PARSER(construct<OmpProcBindClause>(
     "CLOSE" >> pure(OmpProcBindClause::Type::Close) ||
     "MASTER" >> pure(OmpProcBindClause::Type::Master) ||
     "PRIMARY" >> pure(OmpProcBindClause::Type::Primary) ||
     "SPREAD" >> pure(OmpProcBindClause::Type::Spread)))
 
-// 2.15.5.1 map ->
-//    MAP ([ [map-type-modifiers [,] ] map-type : ] variable-name-list)
-// map-type-modifiers -> map-type-modifier [,] [...]
-// map-type-modifier -> ALWAYS | CLOSE | OMPX_HOLD | PRESENT
-// map-type -> ALLOC | DELETE | FROM | RELEASE | TO | TOFROM
-TYPE_PARSER(construct<OmpMapClause::TypeModifier>(
-    "ALWAYS" >> pure(OmpMapClause::TypeModifier::Always) ||
-    "CLOSE" >> pure(OmpMapClause::TypeModifier::Close) ||
-    "OMPX_HOLD" >> pure(OmpMapClause::TypeModifier::Ompx_Hold) ||
-    "PRESENT" >> pure(OmpMapClause::TypeModifier::Present)))
-
-TYPE_PARSER(
-    construct<OmpMapClause::Type>("ALLOC" >> pure(OmpMapClause::Type::Alloc) ||
-        "DELETE" >> pure(OmpMapClause::Type::Delete) ||
-        "FROM" >> pure(OmpMapClause::Type::From) ||
-        "RELEASE" >> pure(OmpMapClause::Type::Release) ||
-        "TO"_id >> pure(OmpMapClause::Type::To) ||
-        "TOFROM" >> pure(OmpMapClause::Type::Tofrom)))
-
-template <bool CommasEverywhere>
-static inline OmpMapClause makeMapClause(OmpMapperIdentifier &&mm,
-    std::tuple<std::optional<std::list<OmpMapClause::TypeModifier>>,
-        std::optional<std::list<OmpIterator>>,
-        std::optional<std::list<OmpMapClause::Type>>> &&mods,
-    OmpObjectList &&objs) {
-  auto &&[tm, it, ty] = std::move(mods);
-  return OmpMapClause{std::move(mm), std::move(tm), std::move(it),
-      std::move(ty), std::move(objs), CommasEverywhere};
-}
-
-TYPE_PARSER(construct<OmpMapperIdentifier>(
-    maybe("MAPPER"_tok >> parenthesized(name) / ","_tok)))
-
 TYPE_PARSER(construct<OmpMapClause>(
-    applyFunction<OmpMapClause>(makeMapClause<true>,
-        Parser<OmpMapperIdentifier>{}, MapModifiers(","_tok),
-        Parser<OmpObjectList>{}) ||
-    applyFunction<OmpMapClause>(makeMapClause<false>,
-        Parser<OmpMapperIdentifier>{}, MapModifiers(maybe(","_tok)),
-        Parser<OmpObjectList>{})))
+    applyFunction<OmpMapClause>(makeMobClause<true>,
+        modifierList<OmpMapClause>(","_tok), Parser<OmpObjectList>{}) ||
+    applyFunction<OmpMapClause>(makeMobClause<false>,
+        modifierList<OmpMapClause>(maybe(","_tok)), Parser<OmpObjectList>{})))
 
 // [OpenMP 5.0]
 // 2.19.7.2 defaultmap(implicit-behavior[:variable-category])
@@ -463,30 +383,17 @@ TYPE_CONTEXT_PARSER("Omp Depend clause"_en_US,
 TYPE_CONTEXT_PARSER("Omp Doacross clause"_en_US,
     construct<OmpDoacrossClause>(Parser<OmpDoacross>{}))
 
-TYPE_PARSER(construct<OmpFromClause::Expectation>(
-    "PRESENT" >> pure(OmpFromClause::Expectation::Present)))
-
-template <typename MotionClause, bool CommasEverywhere>
-static inline MotionClause makeMotionClause(
-    std::tuple<std::optional<std::list<typename MotionClause::Expectation>>,
-        std::optional<std::list<OmpIterator>>> &&mods,
-    OmpObjectList &&objs) {
-  auto &&[exp, iter] = std::move(mods);
-  return MotionClause(
-      std::move(exp), std::move(iter), std::move(objs), CommasEverywhere);
-}
-
 TYPE_PARSER(construct<OmpFromClause>(
-    applyFunction<OmpFromClause>(makeMotionClause<OmpFromClause, true>,
-        MotionModifiers(","_tok), Parser<OmpObjectList>{}) ||
-    applyFunction<OmpFromClause>(makeMotionClause<OmpFromClause, false>,
-        MotionModifiers(maybe(","_tok)), Parser<OmpObjectList>{})))
+    applyFunction<OmpFromClause>(makeMobClause<true>,
+        modifierList<OmpFromClause>(","_tok), Parser<OmpObjectList>{}) ||
+    applyFunction<OmpFromClause>(makeMobClause<false>,
+        modifierList<OmpFromClause>(maybe(","_tok)), Parser<OmpObjectList>{})))
 
 TYPE_PARSER(construct<OmpToClause>(
-    applyFunction<OmpToClause>(makeMotionClause<OmpToClause, true>,
-        MotionModifiers(","_tok), Parser<OmpObjectList>{}) ||
-    applyFunction<OmpToClause>(makeMotionClause<OmpToClause, false>,
-        MotionModifiers(maybe(","_tok)), Parser<OmpObjectList>{})))
+    applyFunction<OmpToClause>(makeMobClause<true>,
+        modifierList<OmpToClause>(","_tok), Parser<OmpObjectList>{}) ||
+    applyFunction<OmpToClause>(makeMobClause<false>,
+        modifierList<OmpToClause>(maybe(","_tok)), Parser<OmpObjectList>{})))
 
 TYPE_CONTEXT_PARSER("Omp LINEAR clause"_en_US,
     construct<OmpLinearClause>(

diff  --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 4881da848c3473..fe3f6ce7aa6291 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2084,6 +2084,11 @@ class UnparseVisitor {
     Walk(x.v);
     Put(")");
   }
+  void Unparse(const OmpMapper &x) {
+    Word("MAPPER(");
+    Walk(x.v);
+    Put(")");
+  }
   void Unparse(const OmpLastprivateClause &x) {
     Walk(
         std::get<std::optional<OmpLastprivateClause::LastprivateModifier>>(x.t),
@@ -2091,46 +2096,8 @@ class UnparseVisitor {
     Walk(std::get<OmpObjectList>(x.t));
   }
   void Unparse(const OmpMapClause &x) {
-    auto &typeMod =
-        std::get<std::optional<std::list<OmpMapClause::TypeModifier>>>(x.t);
-    auto &iter = std::get<std::optional<std::list<OmpIterator>>>(x.t);
-    auto &type = std::get<std::optional<std::list<OmpMapClause::Type>>>(x.t);
-    auto &mapper = std::get<OmpMapperIdentifier>(x.t);
-
-    // For a given list of items, if the item has a value, then walk it.
-    // Print commas between items that have values.
-    // Return 'true' if something did get printed, otherwise 'false'.
-    bool needComma{false};
-    if (mapper.v) {
-      Word("MAPPER(");
-      Walk(*mapper.v);
-      Put(")");
-      needComma = true;
-    }
-    if (typeMod) {
-      if (needComma) {
-        Put(", ");
-      }
-      Walk(*typeMod);
-      needComma = true;
-    }
-    if (iter) {
-      if (needComma) {
-        Put(", ");
-      }
-      Walk(*iter);
-      needComma = true;
-    }
-    if (type) {
-      if (needComma) {
-        Put(", ");
-      }
-      Walk(*type);
-      needComma = true;
-    }
-    if (needComma) {
-      Put(": ");
-    }
+    using Modifier = OmpMapClause::Modifier;
+    Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
     Walk(std::get<OmpObjectList>(x.t));
   }
   void Unparse(const OmpScheduleClause &x) {
@@ -2153,24 +2120,8 @@ class UnparseVisitor {
     Walk(std::get<std::optional<ScalarIntConstantExpr>>(x.t));
   }
   void Unparse(const OmpFromClause &x) {
-    auto &expect{
-        std::get<std::optional<std::list<OmpFromClause::Expectation>>>(x.t)};
-    auto &iter{std::get<std::optional<std::list<OmpIterator>>>(x.t)};
-    bool needComma{false};
-    if (expect) {
-      Walk(*expect);
-      needComma = true;
-    }
-    if (iter) {
-      if (needComma) {
-        Put(", ");
-      }
-      Walk(*iter);
-      needComma = true;
-    }
-    if (needComma) {
-      Put(": ");
-    }
+    using Modifier = OmpFromClause::Modifier;
+    Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
     Walk(std::get<OmpObjectList>(x.t));
   }
   void Unparse(const OmpIfClause &x) {
@@ -2257,24 +2208,8 @@ class UnparseVisitor {
     Walk(":", std::get<std::optional<std::list<Modifier>>>(x.t));
   }
   void Unparse(const OmpToClause &x) {
-    auto &expect{
-        std::get<std::optional<std::list<OmpToClause::Expectation>>>(x.t)};
-    auto &iter{std::get<std::optional<std::list<OmpIterator>>>(x.t)};
-    bool needComma{false};
-    if (expect) {
-      Walk(*expect);
-      needComma = true;
-    }
-    if (iter) {
-      if (needComma) {
-        Put(", ");
-      }
-      Walk(*iter);
-      needComma = true;
-    }
-    if (needComma) {
-      Put(": ");
-    }
+    using Modifier = OmpToClause::Modifier;
+    Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
     Walk(std::get<OmpObjectList>(x.t));
   }
 #define GEN_FLANG_CLAUSE_UNPARSE
@@ -2913,7 +2848,7 @@ class UnparseVisitor {
   WALK_NESTED_ENUM(OmpDeviceClause, DeviceModifier) // OMP device modifier
   WALK_NESTED_ENUM(OmpDeviceTypeClause, Type) // OMP DEVICE_TYPE
   WALK_NESTED_ENUM(OmpReductionModifier, Value) // OMP reduction-modifier
-  WALK_NESTED_ENUM(OmpFromClause, Expectation) // OMP motion-expectation
+  WALK_NESTED_ENUM(OmpExpectation, Value) // OMP motion-expectation
   WALK_NESTED_ENUM(OmpIfClause, DirectiveNameModifier) // OMP directive-modifier
   WALK_NESTED_ENUM(OmpCancelType, Type) // OMP cancel-type
   WALK_NESTED_ENUM(OmpOrderClause, Ordering) // OMP ordering
@@ -2921,8 +2856,8 @@ class UnparseVisitor {
   WALK_NESTED_ENUM(
       OmpGrainsizeClause, Prescriptiveness) // OMP grainsize-modifier
   WALK_NESTED_ENUM(OmpNumTasksClause, Prescriptiveness) // OMP numtasks-modifier
-  WALK_NESTED_ENUM(OmpMapClause, Type) // OMP map-type
-  WALK_NESTED_ENUM(OmpMapClause, TypeModifier) // OMP map-type-modifier
+  WALK_NESTED_ENUM(OmpMapType, Value) // OMP map-type
+  WALK_NESTED_ENUM(OmpMapTypeModifier, Value) // OMP map-type-modifier
 #undef WALK_NESTED_ENUM
   void Unparse(const ReductionOperator::Operator x) {
     switch (x) {

diff  --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 6ef4e851f03ab3..3733ebfaf94925 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -2860,7 +2860,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Destroy &x) {
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) {
   CheckAllowedClause(llvm::omp::Clause::OMPC_reduction);
-  if (OmpVerifyModifiers(x.v, GetContext().clauseSource, context_)) {
+  if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_reduction,
+          GetContext().clauseSource, context_)) {
     if (CheckReductionOperators(x)) {
       CheckReductionTypeList(x);
     }
@@ -3401,7 +3402,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) {
           ThisVersion(version), TryVersion(50));
     }
   }
-  if (!OmpVerifyModifiers(x.v, GetContext().clauseSource, context_)) {
+  if (!OmpVerifyModifiers(x.v, llvm::omp::OMPC_defaultmap,
+          GetContext().clauseSource, context_)) {
     // If modifier verification fails, return early.
     return;
   }
@@ -3479,15 +3481,15 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
 }
 
 void OmpStructureChecker::CheckAllowedMapTypes(
-    const parser::OmpMapClause::Type &type,
-    const std::list<parser::OmpMapClause::Type> &allowedMapTypeList) {
+    const parser::OmpMapType::Value &type,
+    const std::list<parser::OmpMapType::Value> &allowedMapTypeList) {
   if (!llvm::is_contained(allowedMapTypeList, type)) {
     std::string commaSeparatedMapTypes;
     llvm::interleave(
         allowedMapTypeList.begin(), allowedMapTypeList.end(),
-        [&](const parser::OmpMapClause::Type &mapType) {
+        [&](const parser::OmpMapType::Value &mapType) {
           commaSeparatedMapTypes.append(parser::ToUpperCaseLetters(
-              parser::OmpMapClause::EnumToString(mapType)));
+              parser::OmpMapType::EnumToString(mapType)));
         },
         [&] { commaSeparatedMapTypes.append(", "); });
     context_.Say(GetContext().clauseSource,
@@ -3499,40 +3501,23 @@ void OmpStructureChecker::CheckAllowedMapTypes(
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
   CheckAllowedClause(llvm::omp::Clause::OMPC_map);
-  using TypeMod = parser::OmpMapClause::TypeModifier;
-  using Type = parser::OmpMapClause::Type;
-  using IterMod = parser::OmpIterator;
+  if (!OmpVerifyModifiers(
+          x.v, llvm::omp::OMPC_map, GetContext().clauseSource, context_)) {
+    return;
+  }
 
+  auto &modifiers{OmpGetModifiers(x.v)};
   unsigned version{context_.langOptions().OpenMPVersion};
   if (auto commas{std::get<bool>(x.v.t)}; !commas && version >= 52) {
     context_.Say(GetContext().clauseSource,
         "The specification of modifiers without comma separators for the "
         "'MAP' clause has been deprecated in OpenMP 5.2"_port_en_US);
   }
-  if (auto &mapTypeMod{std::get<std::optional<std::list<TypeMod>>>(x.v.t)}) {
-    if (auto *dup{FindDuplicateEntry(*mapTypeMod)}) {
-      context_.Say(GetContext().clauseSource,
-          "Duplicate map-type-modifier entry '%s' will be ignored"_warn_en_US,
-          parser::ToUpperCaseLetters(parser::OmpMapClause::EnumToString(*dup)));
-    }
-  }
-  // The size of any of the optional lists is never 0, instead of the list
-  // being empty, it will be a nullopt.
-  if (auto &iterMod{std::get<std::optional<std::list<IterMod>>>(x.v.t)}) {
-    if (iterMod->size() != 1) {
-      context_.Say(GetContext().clauseSource,
-          "Only one iterator-modifier is allowed"_err_en_US);
-    }
-    CheckIteratorModifier(iterMod->front());
+  if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) {
+    CheckIteratorModifier(*iter);
   }
-  if (auto &mapType{std::get<std::optional<std::list<Type>>>(x.v.t)}) {
-    if (mapType->size() != 1) {
-      context_.Say(GetContext().clauseSource,
-          "Multiple map types are not allowed"_err_en_US);
-      return;
-    }
-    parser::OmpMapClause::Type type{mapType->front()};
-
+  if (auto *type{OmpGetUniqueModifier<parser::OmpMapType>(modifiers)}) {
+    using Value = parser::OmpMapType::Value;
     switch (GetContext().directive) {
     case llvm::omp::Directive::OMPD_target:
     case llvm::omp::Directive::OMPD_target_teams:
@@ -3542,25 +3527,43 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
     case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
     case llvm::omp::Directive::OMPD_target_data:
       CheckAllowedMapTypes(
-          type, {Type::To, Type::From, Type::Tofrom, Type::Alloc});
+          type->v, {Value::To, Value::From, Value::Tofrom, Value::Alloc});
       break;
     case llvm::omp::Directive::OMPD_target_enter_data:
-      CheckAllowedMapTypes(type, {Type::To, Type::Alloc});
+      CheckAllowedMapTypes(type->v, {Value::To, Value::Alloc});
       break;
     case llvm::omp::Directive::OMPD_target_exit_data:
-      CheckAllowedMapTypes(type, {Type::From, Type::Release, Type::Delete});
+      CheckAllowedMapTypes(
+          type->v, {Value::From, Value::Release, Value::Delete});
       break;
     default:
       break;
     }
   }
+
+  auto &&typeMods{
+      OmpGetRepeatableModifier<parser::OmpMapTypeModifier>(modifiers)};
+  struct Less {
+    using Iterator = decltype(typeMods.begin());
+    bool operator()(Iterator a, Iterator b) const {
+      const parser::OmpMapTypeModifier *pa = *a;
+      const parser::OmpMapTypeModifier *pb = *b;
+      return pa->v < pb->v;
+    }
+  };
+  if (auto maybeIter{FindDuplicate<Less>(typeMods)}) {
+    context_.Say(GetContext().clauseSource,
+        "Duplicate map-type-modifier entry '%s' will be ignored"_warn_en_US,
+        parser::ToUpperCaseLetters(
+            parser::OmpMapTypeModifier::EnumToString((**maybeIter)->v)));
+  }
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) {
   CheckAllowedClause(llvm::omp::Clause::OMPC_schedule);
   const parser::OmpScheduleClause &scheduleClause = x.v;
-  if (!OmpVerifyModifiers(
-          scheduleClause, GetContext().clauseSource, context_)) {
+  if (!OmpVerifyModifiers(scheduleClause, llvm::omp::OMPC_schedule,
+          GetContext().clauseSource, context_)) {
     return;
   }
 
@@ -3730,8 +3733,8 @@ void OmpStructureChecker::CheckDoacross(const parser::OmpDoacross &doa) {
 
   // Check if the variables in the iteration vector are unique.
   struct Less {
-    bool operator()(
-        const parser::OmpIteration *a, const parser::OmpIteration *b) const {
+    using Iterator = std::list<parser::OmpIteration>::const_iterator;
+    bool operator()(Iterator a, Iterator b) const {
       auto namea{std::get<parser::Name>(a->t)};
       auto nameb{std::get<parser::Name>(b->t)};
       assert(namea.symbol && nameb.symbol && "Unresolved symbols");
@@ -3741,8 +3744,8 @@ void OmpStructureChecker::CheckDoacross(const parser::OmpDoacross &doa) {
           reinterpret_cast<uintptr_t>(nameb.symbol);
     }
   };
-  if (auto *duplicate{FindDuplicateEntry<parser::OmpIteration, Less>(vec)}) {
-    auto name{std::get<parser::Name>(duplicate->t)};
+  if (auto maybeIter{FindDuplicate<Less>(vec)}) {
+    auto name{std::get<parser::Name>((*maybeIter)->t)};
     context_.Say(name.source,
         "Duplicate variable '%s' in the iteration vector"_err_en_US,
         name.ToString());
@@ -4065,35 +4068,16 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Enter &x) {
 
 void OmpStructureChecker::Enter(const parser::OmpClause::From &x) {
   CheckAllowedClause(llvm::omp::Clause::OMPC_from);
-  unsigned version{context_.langOptions().OpenMPVersion};
-  using ExpMod = parser::OmpFromClause::Expectation;
-  using IterMod = parser::OmpIterator;
-
-  if (auto &expMod{std::get<std::optional<std::list<ExpMod>>>(x.v.t)}) {
-    unsigned allowedInVersion{51};
-    if (version < allowedInVersion) {
-      context_.Say(GetContext().clauseSource,
-          "The PRESENT modifier is not supported in %s, %s"_warn_en_US,
-          ThisVersion(version), TryVersion(allowedInVersion));
-    }
-    if (expMod->size() != 1) {
-      context_.Say(GetContext().clauseSource,
-          "Only one PRESENT modifier is allowed"_err_en_US);
-    }
+  if (!OmpVerifyModifiers(
+          x.v, llvm::omp::OMPC_from, GetContext().clauseSource, context_)) {
+    return;
   }
 
-  if (auto &iterMod{std::get<std::optional<std::list<IterMod>>>(x.v.t)}) {
-    unsigned allowedInVersion{51};
-    if (version < allowedInVersion) {
-      context_.Say(GetContext().clauseSource,
-          "Iterator modifiers are not supported in %s, %s"_warn_en_US,
-          ThisVersion(version), TryVersion(allowedInVersion));
-    }
-    if (iterMod->size() != 1) {
-      context_.Say(GetContext().clauseSource,
-          "Only one iterator-modifier is allowed"_err_en_US);
-    }
-    CheckIteratorModifier(iterMod->front());
+  auto &modifiers{OmpGetModifiers(x.v)};
+  unsigned version{context_.langOptions().OpenMPVersion};
+
+  if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) {
+    CheckIteratorModifier(*iter);
   }
 
   const auto &objList{std::get<parser::OmpObjectList>(x.v.t)};
@@ -4117,6 +4101,12 @@ void OmpStructureChecker::Enter(const parser::OmpClause::From &x) {
 
 void OmpStructureChecker::Enter(const parser::OmpClause::To &x) {
   CheckAllowedClause(llvm::omp::Clause::OMPC_to);
+  if (!OmpVerifyModifiers(
+          x.v, llvm::omp::OMPC_to, GetContext().clauseSource, context_)) {
+    return;
+  }
+
+  auto &modifiers{OmpGetModifiers(x.v)};
   unsigned version{context_.langOptions().OpenMPVersion};
 
   // The "to" clause is only allowed on "declare target" (pre-5.1), and
@@ -4129,35 +4119,10 @@ void OmpStructureChecker::Enter(const parser::OmpClause::To &x) {
   if (GetContext().directive == llvm::omp::OMPD_declare_target) {
     return;
   }
-  assert(GetContext().directive == llvm::omp::OMPD_target_update);
-  using ExpMod = parser::OmpFromClause::Expectation;
-  using IterMod = parser::OmpIterator;
 
-  if (auto &expMod{std::get<std::optional<std::list<ExpMod>>>(x.v.t)}) {
-    unsigned allowedInVersion{51};
-    if (version < allowedInVersion) {
-      context_.Say(GetContext().clauseSource,
-          "The PRESENT modifier is not supported in %s, %s"_warn_en_US,
-          ThisVersion(version), TryVersion(allowedInVersion));
-    }
-    if (expMod->size() != 1) {
-      context_.Say(GetContext().clauseSource,
-          "Only one PRESENT modifier is allowed"_err_en_US);
-    }
-  }
-
-  if (auto &iterMod{std::get<std::optional<std::list<IterMod>>>(x.v.t)}) {
-    unsigned allowedInVersion{51};
-    if (version < allowedInVersion) {
-      context_.Say(GetContext().clauseSource,
-          "Iterator modifiers are not supported in %s, %s"_warn_en_US,
-          ThisVersion(version), TryVersion(allowedInVersion));
-    }
-    if (iterMod->size() != 1) {
-      context_.Say(GetContext().clauseSource,
-          "Only one iterator-modifier is allowed"_err_en_US);
-    }
-    CheckIteratorModifier(iterMod->front());
+  assert(GetContext().directive == llvm::omp::OMPD_target_update);
+  if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) {
+    CheckIteratorModifier(*iter);
   }
 
   const auto &objList{std::get<parser::OmpObjectList>(x.v.t)};

diff  --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index cd0a4759376130..4ce52bebd5a735 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -161,16 +161,15 @@ class OmpStructureChecker
   void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x);
   void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x);
   // specific clause related
-  void CheckAllowedMapTypes(const parser::OmpMapClause::Type &,
-      const std::list<parser::OmpMapClause::Type> &);
+  void CheckAllowedMapTypes(const parser::OmpMapType::Value &,
+      const std::list<parser::OmpMapType::Value> &);
   llvm::StringRef getClauseName(llvm::omp::Clause clause) override;
   llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override;
 
-  template <typename T> struct DefaultLess {
-    bool operator()(const T *a, const T *b) const { return *a < *b; }
-  };
-  template <typename T, typename Less = DefaultLess<T>>
-  const T *FindDuplicateEntry(const std::list<T> &);
+  template < //
+      typename LessTy, typename RangeTy,
+      typename IterTy = decltype(std::declval<RangeTy>().begin())>
+  std::optional<IterTy> FindDuplicate(RangeTy &&);
 
   void CheckDependList(const parser::DataRef &);
   void CheckDependArraySection(
@@ -274,22 +273,20 @@ class OmpStructureChecker
   std::vector<LoopConstruct> loopStack_;
 };
 
-template <typename T, typename Less>
-const T *OmpStructureChecker::FindDuplicateEntry(const std::list<T> &list) {
-  // Add elements of the list to a set. If the insertion fails, return
-  // the address of the failing element.
-
-  // The objects of type T may not be copyable, so add their addresses
-  // to the set. The set will need to compare the actual objects, so
-  // the custom comparator is provided.
-  std::set<const T *, Less> uniq;
-
-  for (const T &item : list) {
-    if (!uniq.insert(&item).second) {
-      return &item;
+/// Find a duplicate entry in the range, and return an iterator to it.
+/// If there are no duplicate entries, return nullopt.
+template <typename LessTy, typename RangeTy, typename IterTy>
+std::optional<IterTy> OmpStructureChecker::FindDuplicate(RangeTy &&range) {
+  // Deal with iterators, since the actual elements may be rvalues (i.e.
+  // have no addresses), for example with custom-constructed ranges that
+  // are not simple c.begin()..c.end().
+  std::set<IterTy, LessTy> uniq;
+  for (auto it{range.begin()}, end{range.end()}; it != end; ++it) {
+    if (!uniq.insert(it).second) {
+      return it;
     }
   }
-  return nullptr;
+  return std::nullopt;
 }
 
 } // namespace Fortran::semantics

diff  --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp
index e0d73e605c73b0..1fd2358aa594ea 100644
--- a/flang/lib/Semantics/openmp-modifiers.cpp
+++ b/flang/lib/Semantics/openmp-modifiers.cpp
@@ -40,7 +40,13 @@ static unsigned findVersion(
     }
   }
 
-  assert(found != 0 && "cannot locate entry for version in map");
+  // It can happen that the above search will not find any version, for
+  // example when the minimum version in the map is higher than the current
+  // version. This is really an error, but this situation should be handled
+  // gracefully, so make some sensible choice and return it.
+  if (found == 0) {
+    found = !map.empty() ? map.begin()->first : versions.front();
+  }
   return found;
 }
 
@@ -52,6 +58,19 @@ const OmpClauses &OmpModifierDescriptor::clauses(unsigned version) const {
   return clauses_.at(findVersion(version, clauses_));
 }
 
+unsigned OmpModifierDescriptor::since(llvm::omp::Clause id) const {
+  unsigned found{[&]() {
+    for (auto &[v, cs] : clauses_) {
+      if (cs.test(id)) {
+        return v;
+      }
+    }
+    return ~0u;
+  }()};
+
+  return found <= 45 ? 0 : found;
+}
+
 // Note: The intent for these functions is to have them be automatically-
 // generated in the future.
 
@@ -89,6 +108,22 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpDependenceType>() {
   return desc;
 }
 
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpExpectation>() {
+  static const OmpModifierDescriptor desc{
+      /*name=*/"expectation",
+      /*props=*/
+      {
+          {51, {OmpProperty::Unique}},
+      },
+      /*clauses=*/
+      {
+          {51, {Clause::OMPC_from, Clause::OMPC_to}},
+      },
+  };
+  return desc;
+}
+
 template <>
 const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpIterator>() {
   static const OmpModifierDescriptor desc{
@@ -124,6 +159,54 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLinearModifier>() {
   return desc;
 }
 
+template <> //
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpMapper>() {
+  static const OmpModifierDescriptor desc{
+      /*name=*/"mapper",
+      /*props=*/
+      {
+          {50, {OmpProperty::Unique}},
+      },
+      /*clauses=*/
+      {
+          {50, {Clause::OMPC_from, Clause::OMPC_map, Clause::OMPC_to}},
+      },
+  };
+  return desc;
+}
+
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpMapType>() {
+  static const OmpModifierDescriptor desc{
+      /*name=*/"map-type",
+      /*props=*/
+      {
+          {45, {OmpProperty::Ultimate}},
+      },
+      /*clauses=*/
+      {
+          {45, {Clause::OMPC_map}},
+      },
+  };
+  return desc;
+}
+
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpMapTypeModifier>() {
+  static const OmpModifierDescriptor desc{
+      /*name=*/"map-type-modifier",
+      /*props=*/
+      {
+          {45, {}}, // Repeatable
+      },
+      /*clauses=*/
+      {
+          {45, {Clause::OMPC_map}},
+      },
+  };
+  return desc;
+}
+
 template <>
 const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpOrderModifier>() {
   static const OmpModifierDescriptor desc{

diff  --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 107bd3b09019a0..0c3708b3fd29b4 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -641,28 +641,25 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
 
   void Post(const parser::OmpMapClause &x) {
     Symbol::Flag ompFlag = Symbol::Flag::OmpMapToFrom;
-    // There is only one `type' allowed, but it's parsed as a list. Multiple
-    // types are diagnosed in the semantic checks for OpenMP.
-    if (const auto &mapType{
-            std::get<std::optional<std::list<parser::OmpMapClause::Type>>>(
-                x.t)}) {
-      switch (mapType->front()) {
-      case parser::OmpMapClause::Type::To:
+    auto &mods{OmpGetModifiers(x)};
+    if (auto *mapType{OmpGetUniqueModifier<parser::OmpMapType>(mods)}) {
+      switch (mapType->v) {
+      case parser::OmpMapType::Value::To:
         ompFlag = Symbol::Flag::OmpMapTo;
         break;
-      case parser::OmpMapClause::Type::From:
+      case parser::OmpMapType::Value::From:
         ompFlag = Symbol::Flag::OmpMapFrom;
         break;
-      case parser::OmpMapClause::Type::Tofrom:
+      case parser::OmpMapType::Value::Tofrom:
         ompFlag = Symbol::Flag::OmpMapToFrom;
         break;
-      case parser::OmpMapClause::Type::Alloc:
+      case parser::OmpMapType::Value::Alloc:
         ompFlag = Symbol::Flag::OmpMapAlloc;
         break;
-      case parser::OmpMapClause::Type::Release:
+      case parser::OmpMapType::Value::Release:
         ompFlag = Symbol::Flag::OmpMapRelease;
         break;
-      case parser::OmpMapClause::Type::Delete:
+      case parser::OmpMapType::Value::Delete:
         ompFlag = Symbol::Flag::OmpMapDelete;
         break;
       }

diff  --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 929d35a4717dcb..b576f59e8c7e52 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -31,6 +31,7 @@
 #include "flang/Parser/tools.h"
 #include "flang/Semantics/attr.h"
 #include "flang/Semantics/expression.h"
+#include "flang/Semantics/openmp-modifiers.h"
 #include "flang/Semantics/program-tree.h"
 #include "flang/Semantics/scope.h"
 #include "flang/Semantics/semantics.h"
@@ -1642,27 +1643,27 @@ bool OmpVisitor::Pre(const parser::OpenMPDeclareMapperConstruct &x) {
 }
 
 bool OmpVisitor::Pre(const parser::OmpMapClause &x) {
-  const auto &mid{std::get<parser::OmpMapperIdentifier>(x.t)};
-  if (const auto &mapperName{mid.v}) {
-    if (const auto symbol = FindSymbol(currScope(), *mapperName)) {
+  auto &mods{OmpGetModifiers(x)};
+  if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
+    if (auto *symbol{FindSymbol(currScope(), mapper->v)}) {
       // TODO: Do we need a specific flag or type here, to distinghuish against
       // other ConstructName things? Leaving this for the full implementation
       // of mapper lowering.
       auto *misc{symbol->detailsIf<MiscDetails>()};
       if (!misc || misc->kind() != MiscDetails::Kind::ConstructName)
-        context().Say(mapperName->source,
-            "Name '%s' should be a mapper name"_err_en_US, mapperName->source);
+        context().Say(mapper->v.source,
+            "Name '%s' should be a mapper name"_err_en_US, mapper->v.source);
       else
-        mapperName->symbol = symbol;
+        mapper->v.symbol = symbol;
     } else {
-      mapperName->symbol = &MakeSymbol(
-          *mapperName, MiscDetails{MiscDetails::Kind::ConstructName});
+      mapper->v.symbol =
+          &MakeSymbol(mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
       // TODO: When completing the implementation, we probably want to error if
       // the symbol is not declared, but right now, testing that the TODO for
-      // OmpMapclause happens is obscured by the TODO for declare mapper, so
+      // OmpMapClause happens is obscured by the TODO for declare mapper, so
       // leaving this out. Remove the above line once the declare mapper is
-      // implemented. context().Say(mapperName->source, "'%s' not
-      // declared"_err_en_US, mapperName->source);
+      // implemented. context().Say(mapper->v.source, "'%s' not
+      // declared"_err_en_US, mapper->v.source);
     }
   }
   return true;

diff  --git a/flang/test/Lower/OpenMP/Todo/map-mapper.f90 b/flang/test/Lower/OpenMP/Todo/map-mapper.f90
index d83c20db293072..9554ffd5fda7bd 100644
--- a/flang/test/Lower/OpenMP/Todo/map-mapper.f90
+++ b/flang/test/Lower/OpenMP/Todo/map-mapper.f90
@@ -8,7 +8,7 @@ program p
   !!end type t1
   !!!$omp declare mapper(xx : t1 :: nn) map(nn, nn%x)
   !$omp target map(mapper(xx), from:a)
-!CHECK: not yet implemented: OmpMapClause(MAPPER(...))
+!CHECK: not yet implemented: Support for mapper modifiers is not implemented yet
   do i=1,n
      a(i) = 4.2
   end do

diff  --git a/flang/test/Parser/OpenMP/from-clause.f90 b/flang/test/Parser/OpenMP/from-clause.f90
index cff9c077c0a947..acd5843ff0c4a9 100644
--- a/flang/test/Parser/OpenMP/from-clause.f90
+++ b/flang/test/Parser/OpenMP/from-clause.f90
@@ -28,7 +28,7 @@ subroutine f01(x)
 
 !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update
 !PARSE-TREE: OmpClauseList -> OmpClause -> From -> OmpFromClause
-!PARSE-TREE: | Expectation = Present
+!PARSE-TREE: | Modifier -> OmpExpectation -> Value = Present
 !PARSE-TREE: | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
 !PARSE-TREE: | bool = 'true'
 
@@ -44,8 +44,8 @@ subroutine f02(x)
 
 !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update
 !PARSE-TREE: OmpClauseList -> OmpClause -> From -> OmpFromClause
-!PARSE-TREE: | Expectation = Present
-!PARSE-TREE: | OmpIterator -> OmpIteratorSpecifier
+!PARSE-TREE: | Modifier -> OmpExpectation -> Value = Present
+!PARSE-TREE: | Modifier -> OmpIterator -> OmpIteratorSpecifier
 !PARSE-TREE: | | TypeDeclarationStmt
 !PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
 !PARSE-TREE: | | | EntityDecl
@@ -73,8 +73,8 @@ subroutine f03(x)
 
 !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update
 !PARSE-TREE: OmpClauseList -> OmpClause -> From -> OmpFromClause
-!PARSE-TREE: | Expectation = Present
-!PARSE-TREE: | OmpIterator -> OmpIteratorSpecifier
+!PARSE-TREE: | Modifier -> OmpExpectation -> Value = Present
+!PARSE-TREE: | Modifier -> OmpIterator -> OmpIteratorSpecifier
 !PARSE-TREE: | | TypeDeclarationStmt
 !PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
 !PARSE-TREE: | | | EntityDecl

diff  --git a/flang/test/Parser/OpenMP/map-modifiers.f90 b/flang/test/Parser/OpenMP/map-modifiers.f90
index 578512283c4dcf..4e034e51352e40 100644
--- a/flang/test/Parser/OpenMP/map-modifiers.f90
+++ b/flang/test/Parser/OpenMP/map-modifiers.f90
@@ -18,11 +18,11 @@ subroutine f00(x)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | TypeModifier = Ompx_Hold
-!PARSE-TREE: | | TypeModifier = Always
-!PARSE-TREE: | | TypeModifier = Present
-!PARSE-TREE: | | TypeModifier = Close
-!PARSE-TREE: | | Type = To
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Ompx_Hold
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Always
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Present
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Close
+!PARSE-TREE: | | Modifier -> OmpMapType -> Value = To
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
 !PARSE-TREE: | | bool = 'true'
 
@@ -43,10 +43,10 @@ subroutine f01(x)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | TypeModifier = Ompx_Hold
-!PARSE-TREE: | | TypeModifier = Always
-!PARSE-TREE: | | TypeModifier = Present
-!PARSE-TREE: | | TypeModifier = Close
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Ompx_Hold
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Always
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Present
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Close
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
 !PARSE-TREE: | | bool = 'true'
 
@@ -67,7 +67,7 @@ subroutine f02(x)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | Type = From
+!PARSE-TREE: | | Modifier -> OmpMapType -> Value = From
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
 !PARSE-TREE: | | bool = 'true'
 
@@ -108,11 +108,11 @@ subroutine f04(x)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | TypeModifier = Ompx_Hold
-!PARSE-TREE: | | TypeModifier = Always
-!PARSE-TREE: | | TypeModifier = Present
-!PARSE-TREE: | | TypeModifier = Close
-!PARSE-TREE: | | Type = To
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Ompx_Hold
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Always
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Present
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Close
+!PARSE-TREE: | | Modifier -> OmpMapType -> Value = To
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
 !PARSE-TREE: | | bool = 'false'
 
@@ -133,10 +133,10 @@ subroutine f05(x)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | TypeModifier = Ompx_Hold
-!PARSE-TREE: | | TypeModifier = Always
-!PARSE-TREE: | | TypeModifier = Present
-!PARSE-TREE: | | TypeModifier = Close
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Ompx_Hold
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Always
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Present
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Close
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
 
 !PARSE-TREE: | | bool = 'true'
@@ -158,8 +158,8 @@ subroutine f10(x)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | TypeModifier = Present
-!PARSE-TREE: | | OmpIterator -> OmpIteratorSpecifier
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Present
+!PARSE-TREE: | | Modifier -> OmpIterator -> OmpIteratorSpecifier
 !PARSE-TREE: | | | TypeDeclarationStmt
 !PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
 !PARSE-TREE: | | | | EntityDecl
@@ -169,7 +169,7 @@ subroutine f10(x)
 !PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '1'
 !PARSE-TREE: | | | | Scalar -> Integer -> Expr = '10_4'
 !PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '10'
-!PARSE-TREE: | | Type = To
+!PARSE-TREE: | | Modifier -> OmpMapType -> Value = To
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> ArrayElement
 !PARSE-TREE: | | | DataRef -> Name = 'x'
 !PARSE-TREE: | | | SectionSubscript -> Integer -> Expr = 'i'
@@ -193,8 +193,8 @@ subroutine f11(x)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | TypeModifier = Present
-!PARSE-TREE: | | OmpIterator -> OmpIteratorSpecifier
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Present
+!PARSE-TREE: | | Modifier -> OmpIterator -> OmpIteratorSpecifier
 !PARSE-TREE: | | | TypeDeclarationStmt
 !PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
 !PARSE-TREE: | | | | EntityDecl
@@ -204,7 +204,7 @@ subroutine f11(x)
 !PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '1'
 !PARSE-TREE: | | | | Scalar -> Integer -> Expr = '10_4'
 !PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '10'
-!PARSE-TREE: | | Type = To
+!PARSE-TREE: | | Modifier -> OmpMapType -> Value = To
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> ArrayElement
 !PARSE-TREE: | | | DataRef -> Name = 'x'
 !PARSE-TREE: | | | SectionSubscript -> Integer -> Expr = 'i'
@@ -228,8 +228,8 @@ subroutine f12(x)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | TypeModifier = Present
-!PARSE-TREE: | | OmpIterator -> OmpIteratorSpecifier
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Present
+!PARSE-TREE: | | Modifier -> OmpIterator -> OmpIteratorSpecifier
 !PARSE-TREE: | | | TypeDeclarationStmt
 !PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
 !PARSE-TREE: | | | | EntityDecl
@@ -239,17 +239,17 @@ subroutine f12(x)
 !PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '1'
 !PARSE-TREE: | | | | Scalar -> Integer -> Expr = '10_4'
 !PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '10'
-!PARSE-TREE: | | OmpIteratorSpecifier
-!PARSE-TREE: | | | TypeDeclarationStmt
-!PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
-!PARSE-TREE: | | | | EntityDecl
-!PARSE-TREE: | | | | | Name = 'j'
-!PARSE-TREE: | | | SubscriptTriplet
-!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '1_4'
-!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '1'
-!PARSE-TREE: | | | | Scalar -> Integer -> Expr = '10_4'
-!PARSE-TREE: | | | | | LiteralConstant -> IntLiteralConstant = '10'
-!PARSE-TREE: | | Type = To
+!PARSE-TREE: | | | OmpIteratorSpecifier
+!PARSE-TREE: | | | | TypeDeclarationStmt
+!PARSE-TREE: | | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
+!PARSE-TREE: | | | | | EntityDecl
+!PARSE-TREE: | | | | | | Name = 'j'
+!PARSE-TREE: | | | | SubscriptTriplet
+!PARSE-TREE: | | | | | Scalar -> Integer -> Expr = '1_4'
+!PARSE-TREE: | | | | | | LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: | | | | | Scalar -> Integer -> Expr = '10_4'
+!PARSE-TREE: | | | | | | LiteralConstant -> IntLiteralConstant = '10'
+!PARSE-TREE: | | Modifier -> OmpMapType -> Value = To
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> ArrayElement
 !PARSE-TREE: | | | DataRef -> Name = 'x'
 !PARSE-TREE: | | | SectionSubscript -> Integer -> Expr = '(i+j)/2_4'
@@ -265,7 +265,7 @@ subroutine f12(x)
 !PARSE-TREE: | | | | | | LiteralConstant -> IntLiteralConstant = '2'
 !PARSE-TREE: | | bool = 'true'
 
-subroutine f90(x, y)
+subroutine f20(x, y)
   integer :: x(10)
   integer :: y
   integer, parameter :: p = 23
@@ -274,7 +274,7 @@ subroutine f90(x, y)
   !$omp end target
 end
 
-!UNPARSE: SUBROUTINE f90 (x, y)
+!UNPARSE: SUBROUTINE f20 (x, y)
 !UNPARSE:  INTEGER x(10_4)
 !UNPARSE:  INTEGER y
 !UNPARSE:  INTEGER, PARAMETER :: p = 23_4
@@ -286,8 +286,8 @@ subroutine f90(x, y)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | TypeModifier = Present
-!PARSE-TREE: | | OmpIterator -> OmpIteratorSpecifier
+!PARSE-TREE: | | Modifier -> OmpMapTypeModifier -> Value = Present
+!PARSE-TREE: | | Modifier -> OmpIterator -> OmpIteratorSpecifier
 !PARSE-TREE: | | | TypeDeclarationStmt
 !PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
 !PARSE-TREE: | | | | EntityDecl
@@ -299,24 +299,24 @@ subroutine f90(x, y)
 !PARSE-TREE: | | | | | Designator -> DataRef -> Name = 'y'
 !PARSE-TREE: | | | | Scalar -> Integer -> Expr = '23_4'
 !PARSE-TREE: | | | | | Designator -> DataRef -> Name = 'p'
-!PARSE-TREE: | | OmpIteratorSpecifier
-!PARSE-TREE: | | | TypeDeclarationStmt
-!PARSE-TREE: | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
-!PARSE-TREE: | | | | EntityDecl
-!PARSE-TREE: | | | | | Name = 'k'
-!PARSE-TREE: | | | SubscriptTriplet
-!PARSE-TREE: | | | | Scalar -> Integer -> Expr = 'i'
-!PARSE-TREE: | | | | | Designator -> DataRef -> Name = 'i'
-!PARSE-TREE: | | | | Scalar -> Integer -> Expr = 'j'
-!PARSE-TREE: | | | | | Designator -> DataRef -> Name = 'j'
-!PARSE-TREE: | | Type = To
+!PARSE-TREE: | | | OmpIteratorSpecifier
+!PARSE-TREE: | | | | TypeDeclarationStmt
+!PARSE-TREE: | | | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
+!PARSE-TREE: | | | | | EntityDecl
+!PARSE-TREE: | | | | | | Name = 'k'
+!PARSE-TREE: | | | | SubscriptTriplet
+!PARSE-TREE: | | | | | Scalar -> Integer -> Expr = 'i'
+!PARSE-TREE: | | | | | | Designator -> DataRef -> Name = 'i'
+!PARSE-TREE: | | | | | Scalar -> Integer -> Expr = 'j'
+!PARSE-TREE: | | | | | | Designator -> DataRef -> Name = 'j'
+!PARSE-TREE: | | Modifier -> OmpMapType -> Value = To
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> ArrayElement
 !PARSE-TREE: | | | DataRef -> Name = 'x'
 !PARSE-TREE: | | | SectionSubscript -> Integer -> Expr = 'k'
 !PARSE-TREE: | | | | Designator -> DataRef -> Name = 'k'
 !PARSE-TREE: | | bool = 'true'
 
-subroutine f100(x, y)
+subroutine f21(x, y)
   integer :: x(10)
   integer :: y
   integer, parameter :: p = 23
@@ -325,7 +325,7 @@ subroutine f100(x, y)
   !$omp end target
 end
 
-!UNPARSE: SUBROUTINE f100 (x, y)
+!UNPARSE: SUBROUTINE f21 (x, y)
 !UNPARSE:  INTEGER x(10_4)
 !UNPARSE:  INTEGER y
 !UNPARSE:  INTEGER, PARAMETER :: p = 23_4
@@ -337,7 +337,42 @@ subroutine f100(x, y)
 !PARSE-TREE: OmpBeginBlockDirective
 !PARSE-TREE: | OmpBlockDirective -> llvm::omp::Directive = target
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Map -> OmpMapClause
-!PARSE-TREE: | | OmpMapperIdentifier -> Name = 'xx'
-!PARSE-TREE: | | Type = From
+!PARSE-TREE: | | Modifier -> OmpMapper -> Name = 'xx'
+!PARSE-TREE: | | Modifier -> OmpMapType -> Value = From
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
 
+subroutine f22(x)
+  integer :: x(10)
+  !$omp target map(present, iterator(i = 1:10), always, from: x(i))
+  x = x + 1
+  !$omp end target
+end
+
+!UNPARSE: SUBROUTINE f22 (x)
+!UNPARSE:  INTEGER x(10_4)
+!UNPARSE: !$OMP TARGET  MAP(PRESENT, ITERATOR(INTEGER i = 1_4:10_4), ALWAYS, FROM: x(i))
+!UNPARSE:   x=x+1_4
+!UNPARSE: !$OMP END TARGET
+!UNPARSE: END SUBROUTINE
+
+!PARSE-TREE: OmpBlockDirective -> llvm::omp::Directive = target
+!PARSE-TREE: OmpClauseList -> OmpClause -> Map -> OmpMapClause
+!PARSE-TREE: | Modifier -> OmpMapTypeModifier -> Value = Present
+!PARSE-TREE: | Modifier -> OmpIterator -> OmpIteratorSpecifier
+!PARSE-TREE: | | TypeDeclarationStmt
+!PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
+!PARSE-TREE: | | | EntityDecl
+!PARSE-TREE: | | | | Name = 'i'
+!PARSE-TREE: | | SubscriptTriplet
+!PARSE-TREE: | | | Scalar -> Integer -> Expr = '1_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: | | | Scalar -> Integer -> Expr = '10_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '10'
+!PARSE-TREE: | Modifier -> OmpMapTypeModifier -> Value = Always
+!PARSE-TREE: | Modifier -> OmpMapType -> Value = From
+!PARSE-TREE: | OmpObjectList -> OmpObject -> Designator -> DataRef -> ArrayElement
+!PARSE-TREE: | | DataRef -> Name = 'x'
+!PARSE-TREE: | | SectionSubscript -> Integer -> Expr = 'i'
+!PARSE-TREE: | | | Designator -> DataRef -> Name = 'i'
+!PARSE-TREE: | bool = 'true'
+

diff  --git a/flang/test/Parser/OpenMP/target-update-to-clause.f90 b/flang/test/Parser/OpenMP/target-update-to-clause.f90
index bb57270fc0bf9c..03006ba37334f4 100644
--- a/flang/test/Parser/OpenMP/target-update-to-clause.f90
+++ b/flang/test/Parser/OpenMP/target-update-to-clause.f90
@@ -28,7 +28,7 @@ subroutine f01(x)
 
 !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update
 !PARSE-TREE: OmpClauseList -> OmpClause -> To -> OmpToClause
-!PARSE-TREE: | Expectation = Present
+!PARSE-TREE: | Modifier -> OmpExpectation -> Value = Present
 !PARSE-TREE: | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
 !PARSE-TREE: | bool = 'true'
 
@@ -44,8 +44,8 @@ subroutine f02(x)
 
 !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update
 !PARSE-TREE: OmpClauseList -> OmpClause -> To -> OmpToClause
-!PARSE-TREE: | Expectation = Present
-!PARSE-TREE: | OmpIterator -> OmpIteratorSpecifier
+!PARSE-TREE: | Modifier -> OmpExpectation -> Value = Present
+!PARSE-TREE: | Modifier -> OmpIterator -> OmpIteratorSpecifier
 !PARSE-TREE: | | TypeDeclarationStmt
 !PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
 !PARSE-TREE: | | | EntityDecl
@@ -73,8 +73,8 @@ subroutine f03(x)
 
 !PARSE-TREE: OmpSimpleStandaloneDirective -> llvm::omp::Directive = target update
 !PARSE-TREE: OmpClauseList -> OmpClause -> To -> OmpToClause
-!PARSE-TREE: | Expectation = Present
-!PARSE-TREE: | OmpIterator -> OmpIteratorSpecifier
+!PARSE-TREE: | Modifier -> OmpExpectation -> Value = Present
+!PARSE-TREE: | Modifier -> OmpIterator -> OmpIteratorSpecifier
 !PARSE-TREE: | | TypeDeclarationStmt
 !PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
 !PARSE-TREE: | | | EntityDecl

diff  --git a/flang/test/Semantics/OpenMP/combined-constructs.f90 b/flang/test/Semantics/OpenMP/combined-constructs.f90
index 25893a47860f46..4f2a4a4f501b9d 100644
--- a/flang/test/Semantics/OpenMP/combined-constructs.f90
+++ b/flang/test/Semantics/OpenMP/combined-constructs.f90
@@ -33,7 +33,7 @@ program main
   enddo
   !$omp end target parallel
 
-  !ERROR: A variable-category modifier is required
+  !ERROR: 'variable-category' modifier is required
   !$omp target parallel defaultmap(tofrom)
   do i = 1, N
      a(i) = 3.14
@@ -80,7 +80,7 @@ program main
   enddo
   !$omp end target parallel do
 
-  !ERROR: A variable-category modifier is required
+  !ERROR: 'variable-category' modifier is required
   !$omp target parallel do defaultmap(tofrom)
   do i = 1, N
      a(i) = 3.14
@@ -140,7 +140,7 @@ program main
   enddo
   !$omp end target teams
 
-  !ERROR: A variable-category modifier is required
+  !ERROR: 'variable-category' modifier is required
   !$omp target teams defaultmap(tofrom)
   do i = 1, N
      a(i) = 3.14
@@ -240,7 +240,7 @@ program main
   enddo
   !$omp end target teams distribute
 
-  !ERROR: A variable-category modifier is required
+  !ERROR: 'variable-category' modifier is required
   !$omp target teams distribute defaultmap(tofrom)
   do i = 1, N
      a(i) = 3.14
@@ -333,7 +333,7 @@ program main
   enddo
   !$omp end target teams distribute parallel do
 
-  !ERROR: A variable-category modifier is required
+  !ERROR: 'variable-category' modifier is required
   !$omp target teams distribute parallel do defaultmap(tofrom)
   do i = 1, N
      a(i) = 3.14
@@ -433,7 +433,7 @@ program main
   enddo
   !$omp end target teams distribute parallel do simd
 
-  !ERROR: A variable-category modifier is required
+  !ERROR: 'variable-category' modifier is required
   !$omp target teams distribute parallel do simd defaultmap(tofrom)
   do i = 1, N
      a(i) = 3.14

diff  --git a/flang/test/Semantics/OpenMP/defaultmap-clause-v45.f90 b/flang/test/Semantics/OpenMP/defaultmap-clause-v45.f90
index 9cb91a71c55351..904fc306a31f4a 100644
--- a/flang/test/Semantics/OpenMP/defaultmap-clause-v45.f90
+++ b/flang/test/Semantics/OpenMP/defaultmap-clause-v45.f90
@@ -1,7 +1,7 @@
 !RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=45 -Werror
 
 subroutine f00
-!WARNING: A variable-category modifier is required
+!WARNING: 'variable-category' modifier is required
   !$omp target defaultmap(tofrom)
   !$omp end target
 end

diff  --git a/flang/test/Semantics/OpenMP/from-clause-v45.f90 b/flang/test/Semantics/OpenMP/from-clause-v45.f90
index 9c418a400e5488..98dff295c879d2 100644
--- a/flang/test/Semantics/OpenMP/from-clause-v45.f90
+++ b/flang/test/Semantics/OpenMP/from-clause-v45.f90
@@ -8,21 +8,22 @@ subroutine f00(x)
 
 subroutine f01(x)
   integer :: x(10)
-!WARNING: Iterator modifiers are not supported in OpenMP v4.5, try -fopenmp-version=51
+!WARNING: 'iterator' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
   !$omp target update from(iterator(i = 1:5): x(i))
 end
 
 subroutine f02(x)
   integer :: x(10)
-!WARNING: The PRESENT modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
-!WARNING: Iterator modifiers are not supported in OpenMP v4.5, try -fopenmp-version=51
+!WARNING: 'expectation' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
+!WARNING: 'iterator' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
   !$omp target update from(present, iterator(i = 1:5): x(i))
 end
 
 subroutine f03(x)
   integer :: x(10)
-!WARNING: The PRESENT modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
-!ERROR: Only one PRESENT modifier is allowed
+!WARNING: 'expectation' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
+!WARNING: 'expectation' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
+!ERROR: 'expectation' modifier cannot occur multiple times
   !$omp target update from(present, present: x)
 end
 

diff  --git a/flang/test/Semantics/OpenMP/from-clause-v51.f90 b/flang/test/Semantics/OpenMP/from-clause-v51.f90
index 18139f04c35cf0..70c00823d073e5 100644
--- a/flang/test/Semantics/OpenMP/from-clause-v51.f90
+++ b/flang/test/Semantics/OpenMP/from-clause-v51.f90
@@ -2,13 +2,13 @@
 
 subroutine f01(x)
   integer :: x(10)
-!ERROR: Only one iterator-modifier is allowed
+!ERROR: 'iterator' modifier cannot occur multiple times
   !$omp target update from(iterator(i = 1:5), iterator(j = 1:5): x(i + j))
 end
 
 subroutine f03(x)
   integer :: x(10)
-!ERROR: Only one PRESENT modifier is allowed
+!ERROR: 'expectation' modifier cannot occur multiple times
   !$omp target update from(present, present: x)
 end
 

diff  --git a/flang/test/Semantics/OpenMP/map-clause.f90 b/flang/test/Semantics/OpenMP/map-clause.f90
index efcef2571a04a8..65ecbd9456464a 100644
--- a/flang/test/Semantics/OpenMP/map-clause.f90
+++ b/flang/test/Semantics/OpenMP/map-clause.f90
@@ -1,4 +1,4 @@
-! RUN: %python %S/../test_errors.py %s %flang -fopenmp
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
 ! Check OpenMP MAP clause validity. Section 5.8.3 OpenMP 5.2.
 
 subroutine sb(arr)

diff  --git a/flang/test/Semantics/OpenMP/map-modifiers.f90 b/flang/test/Semantics/OpenMP/map-modifiers.f90
index f863185d111e01..aae918a2f1f943 100644
--- a/flang/test/Semantics/OpenMP/map-modifiers.f90
+++ b/flang/test/Semantics/OpenMP/map-modifiers.f90
@@ -83,8 +83,16 @@ subroutine f19(x)
 
 subroutine f1a(x)
   integer :: x(10)
-!ERROR: Only one iterator-modifier is allowed
+!ERROR: 'iterator' modifier cannot occur multiple times
   !$omp target map(present, iterator(i = 1:2), iterator(j = 1:2), to: x(i + j))
   x = x + 1
   !$omp end target
 end
+
+subroutine f23(x)
+  integer :: x(10)
+!ERROR: 'map-type' should be the last modifier
+  !$omp target map(present, from, iterator(i = 1:10): x(i))
+  x = x + 1
+  !$omp end target
+end

diff  --git a/flang/test/Semantics/OpenMP/to-clause-v45.f90 b/flang/test/Semantics/OpenMP/to-clause-v45.f90
index 39e842492ef084..e4d8967ca14df8 100644
--- a/flang/test/Semantics/OpenMP/to-clause-v45.f90
+++ b/flang/test/Semantics/OpenMP/to-clause-v45.f90
@@ -8,21 +8,22 @@ subroutine f00(x)
 
 subroutine f01(x)
   integer :: x(10)
-!WARNING: Iterator modifiers are not supported in OpenMP v4.5, try -fopenmp-version=51
+!WARNING: 'iterator' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
   !$omp target update to(iterator(i = 1:5): x(i))
 end
 
 subroutine f02(x)
   integer :: x(10)
-!WARNING: The PRESENT modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
-!WARNING: Iterator modifiers are not supported in OpenMP v4.5, try -fopenmp-version=51
+!WARNING: 'expectation' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
+!WARNING: 'iterator' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
   !$omp target update to(present, iterator(i = 1:5): x(i))
 end
 
 subroutine f03(x)
   integer :: x(10)
-!WARNING: The PRESENT modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
-!ERROR: Only one PRESENT modifier is allowed
+!WARNING: 'expectation' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
+!WARNING: 'expectation' modifier is not supported in OpenMP v4.5, try -fopenmp-version=51
+!ERROR: 'expectation' modifier cannot occur multiple times
   !$omp target update to(present, present: x)
 end
 

diff  --git a/flang/test/Semantics/OpenMP/to-clause-v51.f90 b/flang/test/Semantics/OpenMP/to-clause-v51.f90
index d4f5f15efeb973..8abbca3bb07cd4 100644
--- a/flang/test/Semantics/OpenMP/to-clause-v51.f90
+++ b/flang/test/Semantics/OpenMP/to-clause-v51.f90
@@ -2,13 +2,13 @@
 
 subroutine f01(x)
   integer :: x(10)
-!ERROR: Only one iterator-modifier is allowed
+!ERROR: 'iterator' modifier cannot occur multiple times
   !$omp target update to(iterator(i = 1:5), iterator(j = 1:5): x(i + j))
 end
 
 subroutine f03(x)
   integer :: x(10)
-!ERROR: Only one PRESENT modifier is allowed
+!ERROR: 'expectation' modifier cannot occur multiple times
   !$omp target update to(present, present: x)
 end
 


        


More information about the flang-commits mailing list