Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:43:55

0001 //===- ClauseT.h -- clause template definitions ---------------------------===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 // This file contains template classes that represent OpenMP clauses, as
0009 // described in the OpenMP API specification.
0010 //
0011 // The general structure of any specific clause class is that it is either
0012 // empty, or it consists of a single data member, which can take one of these
0013 // three forms:
0014 // - a value member, named `v`, or
0015 // - a tuple of values, named `t`, or
0016 // - a variant (i.e. union) of values, named `u`.
0017 // To assist with generic visit algorithms, classes define one of the following
0018 // traits:
0019 // - EmptyTrait: the class has no data members.
0020 // - WrapperTrait: the class has a single member `v`
0021 // - TupleTrait: the class has a tuple member `t`
0022 // - UnionTrait the class has a variant member `u`
0023 // - IncompleteTrait: the class is a placeholder class that is currently empty,
0024 //   but will be completed at a later time.
0025 // Note: This structure follows the one used in flang parser.
0026 //
0027 // The types used in the class definitions follow the names used in the spec
0028 // (there are a few exceptions to this). For example, given
0029 //   Clause `foo`
0030 //   - foo-modifier : description...
0031 //   - list         : list of variables
0032 // the corresponding class would be
0033 //   template <...>
0034 //   struct FooT {
0035 //     using FooModifier = type that can represent the modifier
0036 //     using List = ListT<ObjectT<...>>;
0037 //     using TupleTrait = std::true_type;
0038 //     std::tuple<std::optional<FooModifier>, List> t;
0039 //   };
0040 //===----------------------------------------------------------------------===//
0041 #ifndef LLVM_FRONTEND_OPENMP_CLAUSET_H
0042 #define LLVM_FRONTEND_OPENMP_CLAUSET_H
0043 
0044 #include "llvm/ADT/ArrayRef.h"
0045 #include "llvm/ADT/DenseMap.h"
0046 #include "llvm/ADT/DenseSet.h"
0047 #include "llvm/ADT/STLExtras.h"
0048 #include "llvm/ADT/SmallVector.h"
0049 #include "llvm/Frontend/OpenMP/OMP.h"
0050 #include "llvm/Support/ErrorHandling.h"
0051 #include "llvm/Support/raw_ostream.h"
0052 
0053 #include <algorithm>
0054 #include <iterator>
0055 #include <optional>
0056 #include <tuple>
0057 #include <type_traits>
0058 #include <utility>
0059 #include <variant>
0060 
0061 #define ENUM(Name, ...) enum class Name { __VA_ARGS__ }
0062 #define OPT(x) std::optional<x>
0063 
0064 // A number of OpenMP clauses contain values that come from a given set of
0065 // possibilities. In the IR these are usually represented by enums. Both
0066 // clang and flang use different types for the enums, and the enum elements
0067 // representing the same thing may have different values between clang and
0068 // flang.
0069 // Since the representation below tries to adhere to the spec, and be source
0070 // language agnostic, it defines its own enums, independent from any language
0071 // frontend. As a consequence, when instantiating the templates below,
0072 // frontend-specific enums need to be translated into the representation
0073 // used here. The macros below are intended to assist with the conversion.
0074 
0075 // Helper macro for enum-class conversion.
0076 #define CLAUSET_SCOPED_ENUM_MEMBER_CONVERT(Ov, Tv)                             \
0077   if (v == OtherEnum::Ov) {                                                    \
0078     return ThisEnum::Tv;                                                       \
0079   }
0080 
0081 // Helper macro for enum (non-class) conversion.
0082 #define CLAUSET_UNSCOPED_ENUM_MEMBER_CONVERT(Ov, Tv)                           \
0083   if (v == Ov) {                                                               \
0084     return ThisEnum::Tv;                                                       \
0085   }
0086 
0087 #define CLAUSET_ENUM_CONVERT(func, OtherE, ThisE, Maps)                        \
0088   auto func = [](OtherE v) -> ThisE {                                          \
0089     using ThisEnum = ThisE;                                                    \
0090     using OtherEnum = OtherE;                                                  \
0091     (void)sizeof(OtherEnum); /*Avoid "unused local typedef" warning*/          \
0092     Maps;                                                                      \
0093     llvm_unreachable("Unexpected value in " #OtherE);                          \
0094   }
0095 
0096 // Usage:
0097 //
0098 // Given two enums,
0099 //   enum class Other { o1, o2 };
0100 //   enum class This { t1, t2 };
0101 // generate conversion function "Func : Other -> This" with
0102 //   CLAUSET_ENUM_CONVERT(
0103 //       Func, Other, This,
0104 //       CLAUSET_ENUM_MEMBER_CONVERT(o1, t1)      // <- No comma
0105 //       CLAUSET_ENUM_MEMBER_CONVERT(o2, t2)
0106 //       ...
0107 //   )
0108 //
0109 // Note that the sequence of M(other-value, this-value) is separated
0110 // with _spaces_, not commas.
0111 
0112 namespace detail {
0113 // Type trait to determine whether T is a specialization of std::variant.
0114 template <typename T> struct is_variant {
0115   static constexpr bool value = false;
0116 };
0117 
0118 template <typename... Ts> struct is_variant<std::variant<Ts...>> {
0119   static constexpr bool value = true;
0120 };
0121 
0122 template <typename T> constexpr bool is_variant_v = is_variant<T>::value;
0123 
0124 // Helper utility to create a type which is a union of two given variants.
0125 template <typename...> struct UnionOfTwo;
0126 
0127 template <typename... Types1, typename... Types2>
0128 struct UnionOfTwo<std::variant<Types1...>, std::variant<Types2...>> {
0129   using type = std::variant<Types1..., Types2...>;
0130 };
0131 } // namespace detail
0132 
0133 namespace tomp {
0134 namespace type {
0135 
0136 // Helper utility to create a type which is a union of an arbitrary number
0137 // of variants.
0138 template <typename...> struct Union;
0139 
0140 template <> struct Union<> {
0141   // Legal to define, illegal to instantiate.
0142   using type = std::variant<>;
0143 };
0144 
0145 template <typename T, typename... Ts> struct Union<T, Ts...> {
0146   static_assert(detail::is_variant_v<T>);
0147   using type =
0148       typename detail::UnionOfTwo<T, typename Union<Ts...>::type>::type;
0149 };
0150 
0151 template <typename T> using ListT = llvm::SmallVector<T, 0>;
0152 
0153 // The ObjectT class represents a variable or a locator (as defined in
0154 // the OpenMP spec).
0155 // Note: the ObjectT template is not defined. Any user of it is expected to
0156 // provide their own specialization that conforms to the requirements listed
0157 // below.
0158 //
0159 // Let ObjectS be any specialization of ObjectT:
0160 //
0161 // ObjectS must provide the following definitions:
0162 // {
0163 //    using IdTy = Id;
0164 //    using ExprTy = Expr;
0165 //
0166 //    auto id() const -> IdTy {
0167 //      // Return a value such that a.id() == b.id() if and only if:
0168 //      // (1) both `a` and `b` represent the same variable or location, or
0169 //      // (2) bool(a.id()) == false and bool(b.id()) == false
0170 //    }
0171 // }
0172 //
0173 // The type IdTy should be hashable (usable as key in unordered containers).
0174 //
0175 // Values of type IdTy should be contextually convertible to `bool`.
0176 //
0177 // If S is an object of type ObjectS, then `bool(S.id())` is `false` if
0178 // and only if S does not represent any variable or location.
0179 //
0180 // ObjectS should be copyable, movable, and default-constructible.
0181 template <typename IdType, typename ExprType> struct ObjectT;
0182 
0183 // By default, object equality is only determined by its identity.
0184 template <typename I, typename E>
0185 bool operator==(const ObjectT<I, E> &o1, const ObjectT<I, E> &o2) {
0186   return o1.id() == o2.id();
0187 }
0188 
0189 template <typename I, typename E> using ObjectListT = ListT<ObjectT<I, E>>;
0190 
0191 using DirectiveName = llvm::omp::Directive;
0192 
0193 template <typename I, typename E> //
0194 struct DefinedOperatorT {
0195   struct DefinedOpName {
0196     using WrapperTrait = std::true_type;
0197     ObjectT<I, E> v;
0198   };
0199   ENUM(IntrinsicOperator, Power, Multiply, Divide, Add, Subtract, Concat, LT,
0200        LE, EQ, NE, GE, GT, NOT, AND, OR, EQV, NEQV, Min, Max);
0201   using UnionTrait = std::true_type;
0202   std::variant<DefinedOpName, IntrinsicOperator> u;
0203 };
0204 
0205 // V5.2: [3.2.6] `iterator` modifier
0206 template <typename E> //
0207 struct RangeT {
0208   // range-specification: begin : end[: step]
0209   using TupleTrait = std::true_type;
0210   std::tuple<E, E, OPT(E)> t;
0211 };
0212 
0213 // V5.2: [3.2.6] `iterator` modifier
0214 template <typename TypeType, typename IdType, typename ExprType> //
0215 struct IteratorSpecifierT {
0216   // iterators-specifier: [ iterator-type ] identifier = range-specification
0217   using TupleTrait = std::true_type;
0218   std::tuple<OPT(TypeType), ObjectT<IdType, ExprType>, RangeT<ExprType>> t;
0219 };
0220 
0221 // Note:
0222 // For motion or map clauses the OpenMP spec allows a unique mapper modifier.
0223 // In practice, since these clauses apply to multiple objects, there can be
0224 // multiple effective mappers applicable to these objects (due to overloads,
0225 // etc.). Because of that store a list of mappers every time a mapper modifier
0226 // is allowed. If the mapper list contains a single element, it applies to
0227 // all objects in the clause, otherwise there should be as many mappers as
0228 // there are objects.
0229 // V5.2: [5.8.2] Mapper identifiers and `mapper` modifiers
0230 template <typename I, typename E> //
0231 struct MapperT {
0232   using MapperIdentifier = ObjectT<I, E>;
0233   using WrapperTrait = std::true_type;
0234   MapperIdentifier v;
0235 };
0236 
0237 // V5.2: [15.8.1] `memory-order` clauses
0238 // When used as arguments for other clauses, e.g. `fail`.
0239 ENUM(MemoryOrder, AcqRel, Acquire, Relaxed, Release, SeqCst);
0240 ENUM(MotionExpectation, Present);
0241 // Union of `dependence-type` and `task-depenence-type`.
0242 // V5.2: [15.9.1] `task-dependence-type` modifier
0243 ENUM(DependenceType, Depobj, In, Inout, Inoutset, Mutexinoutset, Out, Sink,
0244      Source);
0245 ENUM(Prescriptiveness, Strict);
0246 
0247 template <typename I, typename E> //
0248 struct LoopIterationT {
0249   struct Distance {
0250     using TupleTrait = std::true_type;
0251     std::tuple<DefinedOperatorT<I, E>, E> t;
0252   };
0253   using TupleTrait = std::true_type;
0254   std::tuple<ObjectT<I, E>, OPT(Distance)> t;
0255 };
0256 
0257 template <typename I, typename E> //
0258 struct ProcedureDesignatorT {
0259   using WrapperTrait = std::true_type;
0260   ObjectT<I, E> v;
0261 };
0262 
0263 // Note:
0264 // For reduction clauses the OpenMP spec allows a unique reduction identifier.
0265 // For reasons analogous to those listed for the MapperT type, clauses that
0266 // according to the spec contain a reduction identifier will contain a list of
0267 // reduction identifiers. The same constraints apply: there is either a single
0268 // identifier that applies to all objects, or there are as many identifiers
0269 // as there are objects.
0270 template <typename I, typename E> //
0271 struct ReductionIdentifierT {
0272   using UnionTrait = std::true_type;
0273   std::variant<DefinedOperatorT<I, E>, ProcedureDesignatorT<I, E>> u;
0274 };
0275 
0276 template <typename T, typename I, typename E> //
0277 using IteratorT = ListT<IteratorSpecifierT<T, I, E>>;
0278 
0279 template <typename T>
0280 std::enable_if_t<T::EmptyTrait::value, bool> operator==(const T &a,
0281                                                         const T &b) {
0282   return true;
0283 }
0284 template <typename T>
0285 std::enable_if_t<T::IncompleteTrait::value, bool> operator==(const T &a,
0286                                                              const T &b) {
0287   return true;
0288 }
0289 template <typename T>
0290 std::enable_if_t<T::WrapperTrait::value, bool> operator==(const T &a,
0291                                                           const T &b) {
0292   return a.v == b.v;
0293 }
0294 template <typename T>
0295 std::enable_if_t<T::TupleTrait::value, bool> operator==(const T &a,
0296                                                         const T &b) {
0297   return a.t == b.t;
0298 }
0299 template <typename T>
0300 std::enable_if_t<T::UnionTrait::value, bool> operator==(const T &a,
0301                                                         const T &b) {
0302   return a.u == b.u;
0303 }
0304 } // namespace type
0305 
0306 template <typename T> using ListT = type::ListT<T>;
0307 
0308 template <typename I, typename E> using ObjectT = type::ObjectT<I, E>;
0309 template <typename I, typename E> using ObjectListT = type::ObjectListT<I, E>;
0310 
0311 template <typename T, typename I, typename E>
0312 using IteratorT = type::IteratorT<T, I, E>;
0313 
0314 template <
0315     typename ContainerTy, typename FunctionTy,
0316     typename ElemTy = typename llvm::remove_cvref_t<ContainerTy>::value_type,
0317     typename ResultTy = std::invoke_result_t<FunctionTy, ElemTy>>
0318 ListT<ResultTy> makeList(ContainerTy &&container, FunctionTy &&func) {
0319   ListT<ResultTy> v;
0320   llvm::transform(container, std::back_inserter(v), func);
0321   return v;
0322 }
0323 
0324 namespace clause {
0325 using type::operator==;
0326 
0327 // V5.2: [8.3.1] `assumption` clauses
0328 template <typename T, typename I, typename E> //
0329 struct AbsentT {
0330   using List = ListT<type::DirectiveName>;
0331   using WrapperTrait = std::true_type;
0332   List v;
0333 };
0334 
0335 // V5.2: [15.8.1] `memory-order` clauses
0336 template <typename T, typename I, typename E> //
0337 struct AcqRelT {
0338   using EmptyTrait = std::true_type;
0339 };
0340 
0341 // V5.2: [15.8.1] `memory-order` clauses
0342 template <typename T, typename I, typename E> //
0343 struct AcquireT {
0344   using EmptyTrait = std::true_type;
0345 };
0346 
0347 // V5.2: [7.5.2] `adjust_args` clause
0348 template <typename T, typename I, typename E> //
0349 struct AdjustArgsT {
0350   using IncompleteTrait = std::true_type;
0351 };
0352 
0353 // V5.2: [12.5.1] `affinity` clause
0354 template <typename T, typename I, typename E> //
0355 struct AffinityT {
0356   using Iterator = type::IteratorT<T, I, E>;
0357   using LocatorList = ObjectListT<I, E>;
0358 
0359   using TupleTrait = std::true_type;
0360   std::tuple<OPT(Iterator), LocatorList> t;
0361 };
0362 
0363 // V5.2: [6.3] `align` clause
0364 template <typename T, typename I, typename E> //
0365 struct AlignT {
0366   using Alignment = E;
0367 
0368   using WrapperTrait = std::true_type;
0369   Alignment v;
0370 };
0371 
0372 // V5.2: [5.11] `aligned` clause
0373 template <typename T, typename I, typename E> //
0374 struct AlignedT {
0375   using Alignment = E;
0376   using List = ObjectListT<I, E>;
0377 
0378   using TupleTrait = std::true_type;
0379   std::tuple<OPT(Alignment), List> t;
0380 };
0381 
0382 template <typename T, typename I, typename E> //
0383 struct AllocatorT;
0384 
0385 // V5.2: [6.6] `allocate` clause
0386 template <typename T, typename I, typename E> //
0387 struct AllocateT {
0388   // AllocatorSimpleModifier is same as AllocatorComplexModifier.
0389   using AllocatorComplexModifier = AllocatorT<T, I, E>;
0390   using AlignModifier = AlignT<T, I, E>;
0391   using List = ObjectListT<I, E>;
0392 
0393   using TupleTrait = std::true_type;
0394   std::tuple<OPT(AllocatorComplexModifier), OPT(AlignModifier), List> t;
0395 };
0396 
0397 // V5.2: [6.4] `allocator` clause
0398 template <typename T, typename I, typename E> //
0399 struct AllocatorT {
0400   using Allocator = E;
0401   using WrapperTrait = std::true_type;
0402   Allocator v;
0403 };
0404 
0405 // V5.2: [7.5.3] `append_args` clause
0406 template <typename T, typename I, typename E> //
0407 struct AppendArgsT {
0408   using IncompleteTrait = std::true_type;
0409 };
0410 
0411 // V5.2: [8.1] `at` clause
0412 template <typename T, typename I, typename E> //
0413 struct AtT {
0414   ENUM(ActionTime, Compilation, Execution);
0415   using WrapperTrait = std::true_type;
0416   ActionTime v;
0417 };
0418 
0419 // V5.2: [8.2.1] `requirement` clauses
0420 template <typename T, typename I, typename E> //
0421 struct AtomicDefaultMemOrderT {
0422   using MemoryOrder = type::MemoryOrder;
0423   using WrapperTrait = std::true_type;
0424   MemoryOrder v; // Name not provided in spec
0425 };
0426 
0427 // V5.2: [11.7.1] `bind` clause
0428 template <typename T, typename I, typename E> //
0429 struct BindT {
0430   ENUM(Binding, Teams, Parallel, Thread);
0431   using WrapperTrait = std::true_type;
0432   Binding v;
0433 };
0434 
0435 // V5.2: [15.8.3] `extended-atomic` clauses
0436 template <typename T, typename I, typename E> //
0437 struct CaptureT {
0438   using EmptyTrait = std::true_type;
0439 };
0440 
0441 // V5.2: [4.4.3] `collapse` clause
0442 template <typename T, typename I, typename E> //
0443 struct CollapseT {
0444   using N = E;
0445   using WrapperTrait = std::true_type;
0446   N v;
0447 };
0448 
0449 // V5.2: [15.8.3] `extended-atomic` clauses
0450 template <typename T, typename I, typename E> //
0451 struct CompareT {
0452   using EmptyTrait = std::true_type;
0453 };
0454 
0455 // V5.2: [8.3.1] `assumption` clauses
0456 template <typename T, typename I, typename E> //
0457 struct ContainsT {
0458   using List = ListT<type::DirectiveName>;
0459   using WrapperTrait = std::true_type;
0460   List v;
0461 };
0462 
0463 // V5.2: [5.7.1] `copyin` clause
0464 template <typename T, typename I, typename E> //
0465 struct CopyinT {
0466   using List = ObjectListT<I, E>;
0467   using WrapperTrait = std::true_type;
0468   List v;
0469 };
0470 
0471 // V5.2: [5.7.2] `copyprivate` clause
0472 template <typename T, typename I, typename E> //
0473 struct CopyprivateT {
0474   using List = ObjectListT<I, E>;
0475   using WrapperTrait = std::true_type;
0476   List v;
0477 };
0478 
0479 // V5.2: [5.4.1] `default` clause
0480 template <typename T, typename I, typename E> //
0481 struct DefaultT {
0482   ENUM(DataSharingAttribute, Firstprivate, None, Private, Shared);
0483   using WrapperTrait = std::true_type;
0484   DataSharingAttribute v;
0485 };
0486 
0487 // V5.2: [5.8.7] `defaultmap` clause
0488 template <typename T, typename I, typename E> //
0489 struct DefaultmapT {
0490   ENUM(ImplicitBehavior, Alloc, To, From, Tofrom, Firstprivate, None, Default,
0491        Present);
0492   ENUM(VariableCategory, All, Scalar, Aggregate, Pointer, Allocatable);
0493   using TupleTrait = std::true_type;
0494   std::tuple<ImplicitBehavior, OPT(VariableCategory)> t;
0495 };
0496 
0497 template <typename T, typename I, typename E> //
0498 struct DoacrossT;
0499 
0500 // V5.2: [15.9.5] `depend` clause
0501 template <typename T, typename I, typename E> //
0502 struct DependT {
0503   using Iterator = type::IteratorT<T, I, E>;
0504   using LocatorList = ObjectListT<I, E>;
0505   using DependenceType = tomp::type::DependenceType;
0506 
0507   struct TaskDep { // The form with task dependence type.
0508     using TupleTrait = std::true_type;
0509     // Empty LocatorList means "omp_all_memory".
0510     std::tuple<DependenceType, OPT(Iterator), LocatorList> t;
0511   };
0512 
0513   using Doacross = DoacrossT<T, I, E>;
0514   using UnionTrait = std::true_type;
0515   std::variant<Doacross, TaskDep> u; // Doacross form is legacy
0516 };
0517 
0518 // V5.2: [3.5] `destroy` clause
0519 template <typename T, typename I, typename E> //
0520 struct DestroyT {
0521   using DestroyVar = ObjectT<I, E>;
0522   using WrapperTrait = std::true_type;
0523   // DestroyVar can be ommitted in "depobj destroy".
0524   OPT(DestroyVar) v;
0525 };
0526 
0527 // V5.2: [12.5.2] `detach` clause
0528 template <typename T, typename I, typename E> //
0529 struct DetachT {
0530   using EventHandle = ObjectT<I, E>;
0531   using WrapperTrait = std::true_type;
0532   EventHandle v;
0533 };
0534 
0535 // V5.2: [13.2] `device` clause
0536 template <typename T, typename I, typename E> //
0537 struct DeviceT {
0538   using DeviceDescription = E;
0539   ENUM(DeviceModifier, Ancestor, DeviceNum);
0540   using TupleTrait = std::true_type;
0541   std::tuple<OPT(DeviceModifier), DeviceDescription> t;
0542 };
0543 
0544 // V5.2: [13.1] `device_type` clause
0545 template <typename T, typename I, typename E> //
0546 struct DeviceTypeT {
0547   ENUM(DeviceTypeDescription, Any, Host, Nohost);
0548   using WrapperTrait = std::true_type;
0549   DeviceTypeDescription v;
0550 };
0551 
0552 // V5.2: [11.6.1] `dist_schedule` clause
0553 template <typename T, typename I, typename E> //
0554 struct DistScheduleT {
0555   ENUM(Kind, Static);
0556   using ChunkSize = E;
0557   using TupleTrait = std::true_type;
0558   std::tuple<Kind, OPT(ChunkSize)> t;
0559 };
0560 
0561 // V5.2: [15.9.6] `doacross` clause
0562 template <typename T, typename I, typename E> //
0563 struct DoacrossT {
0564   using Vector = ListT<type::LoopIterationT<I, E>>;
0565   using DependenceType = tomp::type::DependenceType;
0566   using TupleTrait = std::true_type;
0567   // Empty Vector means "omp_cur_iteration"
0568   std::tuple<DependenceType, Vector> t;
0569 };
0570 
0571 // V5.2: [8.2.1] `requirement` clauses
0572 template <typename T, typename I, typename E> //
0573 struct DynamicAllocatorsT {
0574   using EmptyTrait = std::true_type;
0575 };
0576 
0577 // V5.2: [5.8.4] `enter` clause
0578 template <typename T, typename I, typename E> //
0579 struct EnterT {
0580   using List = ObjectListT<I, E>;
0581   using WrapperTrait = std::true_type;
0582   List v;
0583 };
0584 
0585 // V5.2: [5.6.2] `exclusive` clause
0586 template <typename T, typename I, typename E> //
0587 struct ExclusiveT {
0588   using WrapperTrait = std::true_type;
0589   using List = ObjectListT<I, E>;
0590   List v;
0591 };
0592 
0593 // V5.2: [15.8.3] `extended-atomic` clauses
0594 template <typename T, typename I, typename E> //
0595 struct FailT {
0596   using MemoryOrder = type::MemoryOrder;
0597   using WrapperTrait = std::true_type;
0598   MemoryOrder v;
0599 };
0600 
0601 // V5.2: [10.5.1] `filter` clause
0602 template <typename T, typename I, typename E> //
0603 struct FilterT {
0604   using ThreadNum = E;
0605   using WrapperTrait = std::true_type;
0606   ThreadNum v;
0607 };
0608 
0609 // V5.2: [12.3] `final` clause
0610 template <typename T, typename I, typename E> //
0611 struct FinalT {
0612   using Finalize = E;
0613   using WrapperTrait = std::true_type;
0614   Finalize v;
0615 };
0616 
0617 // V5.2: [5.4.4] `firstprivate` clause
0618 template <typename T, typename I, typename E> //
0619 struct FirstprivateT {
0620   using List = ObjectListT<I, E>;
0621   using WrapperTrait = std::true_type;
0622   List v;
0623 };
0624 
0625 // V5.2: [5.9.2] `from` clause
0626 template <typename T, typename I, typename E> //
0627 struct FromT {
0628   using LocatorList = ObjectListT<I, E>;
0629   using Expectation = type::MotionExpectation;
0630   using Iterator = type::IteratorT<T, I, E>;
0631   // See note at the definition of the MapperT type.
0632   using Mappers = ListT<type::MapperT<I, E>>; // Not a spec name
0633 
0634   using TupleTrait = std::true_type;
0635   std::tuple<OPT(Expectation), OPT(Mappers), OPT(Iterator), LocatorList> t;
0636 };
0637 
0638 // V5.2: [9.2.1] `full` clause
0639 template <typename T, typename I, typename E> //
0640 struct FullT {
0641   using EmptyTrait = std::true_type;
0642 };
0643 
0644 // V5.2: [12.6.1] `grainsize` clause
0645 template <typename T, typename I, typename E> //
0646 struct GrainsizeT {
0647   using Prescriptiveness = type::Prescriptiveness;
0648   using GrainSize = E;
0649   using TupleTrait = std::true_type;
0650   std::tuple<OPT(Prescriptiveness), GrainSize> t;
0651 };
0652 
0653 // V5.2: [5.4.9] `has_device_addr` clause
0654 template <typename T, typename I, typename E> //
0655 struct HasDeviceAddrT {
0656   using List = ObjectListT<I, E>;
0657   using WrapperTrait = std::true_type;
0658   List v;
0659 };
0660 
0661 // V5.2: [15.1.2] `hint` clause
0662 template <typename T, typename I, typename E> //
0663 struct HintT {
0664   using HintExpr = E;
0665   using WrapperTrait = std::true_type;
0666   HintExpr v;
0667 };
0668 
0669 // V5.2: [8.3.1] Assumption clauses
0670 template <typename T, typename I, typename E> //
0671 struct HoldsT {
0672   using WrapperTrait = std::true_type;
0673   E v; // No argument name in spec 5.2
0674 };
0675 
0676 // V5.2: [3.4] `if` clause
0677 template <typename T, typename I, typename E> //
0678 struct IfT {
0679   using DirectiveNameModifier = type::DirectiveName;
0680   using IfExpression = E;
0681   using TupleTrait = std::true_type;
0682   std::tuple<OPT(DirectiveNameModifier), IfExpression> t;
0683 };
0684 
0685 // V5.2: [7.7.1] `branch` clauses
0686 template <typename T, typename I, typename E> //
0687 struct InbranchT {
0688   using EmptyTrait = std::true_type;
0689 };
0690 
0691 // V5.2: [5.6.1] `exclusive` clause
0692 template <typename T, typename I, typename E> //
0693 struct InclusiveT {
0694   using List = ObjectListT<I, E>;
0695   using WrapperTrait = std::true_type;
0696   List v;
0697 };
0698 
0699 // V5.2: [7.8.3] `indirect` clause
0700 template <typename T, typename I, typename E> //
0701 struct IndirectT {
0702   using InvokedByFptr = E;
0703   using WrapperTrait = std::true_type;
0704   InvokedByFptr v;
0705 };
0706 
0707 // V5.2: [14.1.2] `init` clause
0708 template <typename T, typename I, typename E> //
0709 struct InitT {
0710   using ForeignRuntimeId = E;
0711   using InteropVar = ObjectT<I, E>;
0712   using InteropPreference = ListT<ForeignRuntimeId>;
0713   ENUM(InteropType, Target, Targetsync);   // Repeatable
0714   using InteropTypes = ListT<InteropType>; // Not a spec name
0715 
0716   using TupleTrait = std::true_type;
0717   std::tuple<OPT(InteropPreference), InteropTypes, InteropVar> t;
0718 };
0719 
0720 // V5.2: [5.5.4] `initializer` clause
0721 template <typename T, typename I, typename E> //
0722 struct InitializerT {
0723   using InitializerExpr = E;
0724   using WrapperTrait = std::true_type;
0725   InitializerExpr v;
0726 };
0727 
0728 // V5.2: [5.5.10] `in_reduction` clause
0729 template <typename T, typename I, typename E> //
0730 struct InReductionT {
0731   using List = ObjectListT<I, E>;
0732   // See note at the definition of the ReductionIdentifierT type.
0733   // The name ReductionIdentifiers is not a spec name.
0734   using ReductionIdentifiers = ListT<type::ReductionIdentifierT<I, E>>;
0735   using TupleTrait = std::true_type;
0736   std::tuple<ReductionIdentifiers, List> t;
0737 };
0738 
0739 // V5.2: [5.4.7] `is_device_ptr` clause
0740 template <typename T, typename I, typename E> //
0741 struct IsDevicePtrT {
0742   using List = ObjectListT<I, E>;
0743   using WrapperTrait = std::true_type;
0744   List v;
0745 };
0746 
0747 // V5.2: [5.4.5] `lastprivate` clause
0748 template <typename T, typename I, typename E> //
0749 struct LastprivateT {
0750   using List = ObjectListT<I, E>;
0751   ENUM(LastprivateModifier, Conditional);
0752   using TupleTrait = std::true_type;
0753   std::tuple<OPT(LastprivateModifier), List> t;
0754 };
0755 
0756 // V5.2: [5.4.6] `linear` clause
0757 template <typename T, typename I, typename E> //
0758 struct LinearT {
0759   // std::get<type> won't work here due to duplicate types in the tuple.
0760   using List = ObjectListT<I, E>;
0761   // StepSimpleModifier is same as StepComplexModifier.
0762   using StepComplexModifier = E;
0763   ENUM(LinearModifier, Ref, Val, Uval);
0764 
0765   using TupleTrait = std::true_type;
0766   // Step == nullopt means 1.
0767   std::tuple<OPT(StepComplexModifier), OPT(LinearModifier), List> t;
0768 };
0769 
0770 // V5.2: [5.8.5] `link` clause
0771 template <typename T, typename I, typename E> //
0772 struct LinkT {
0773   using List = ObjectListT<I, E>;
0774   using WrapperTrait = std::true_type;
0775   List v;
0776 };
0777 
0778 // V5.2: [5.8.3] `map` clause
0779 template <typename T, typename I, typename E> //
0780 struct MapT {
0781   using LocatorList = ObjectListT<I, E>;
0782   ENUM(MapType, To, From, Tofrom, Alloc, Release, Delete);
0783   ENUM(MapTypeModifier, Always, Close, Present, OmpxHold);
0784   // See note at the definition of the MapperT type.
0785   using Mappers = ListT<type::MapperT<I, E>>; // Not a spec name
0786   using Iterator = type::IteratorT<T, I, E>;
0787   using MapTypeModifiers = ListT<MapTypeModifier>; // Not a spec name
0788 
0789   using TupleTrait = std::true_type;
0790   std::tuple<OPT(MapType), OPT(MapTypeModifiers), OPT(Mappers), OPT(Iterator),
0791              LocatorList>
0792       t;
0793 };
0794 
0795 // V5.2: [7.5.1] `match` clause
0796 template <typename T, typename I, typename E> //
0797 struct MatchT {
0798   using IncompleteTrait = std::true_type;
0799 };
0800 
0801 // V5.2: [12.2] `mergeable` clause
0802 template <typename T, typename I, typename E> //
0803 struct MergeableT {
0804   using EmptyTrait = std::true_type;
0805 };
0806 
0807 // V5.2: [8.5.2] `message` clause
0808 template <typename T, typename I, typename E> //
0809 struct MessageT {
0810   using MsgString = E;
0811   using WrapperTrait = std::true_type;
0812   MsgString v;
0813 };
0814 
0815 // V5.2: [7.6.2] `nocontext` clause
0816 template <typename T, typename I, typename E> //
0817 struct NocontextT {
0818   using DoNotUpdateContext = E;
0819   using WrapperTrait = std::true_type;
0820   DoNotUpdateContext v;
0821 };
0822 
0823 // V5.2: [15.7] `nowait` clause
0824 template <typename T, typename I, typename E> //
0825 struct NogroupT {
0826   using EmptyTrait = std::true_type;
0827 };
0828 
0829 // V5.2: [10.4.1] `nontemporal` clause
0830 template <typename T, typename I, typename E> //
0831 struct NontemporalT {
0832   using List = ObjectListT<I, E>;
0833   using WrapperTrait = std::true_type;
0834   List v;
0835 };
0836 
0837 // V5.2: [8.3.1] `assumption` clauses
0838 template <typename T, typename I, typename E> //
0839 struct NoOpenmpT {
0840   using EmptyTrait = std::true_type;
0841 };
0842 
0843 // V5.2: [8.3.1] `assumption` clauses
0844 template <typename T, typename I, typename E> //
0845 struct NoOpenmpRoutinesT {
0846   using EmptyTrait = std::true_type;
0847 };
0848 
0849 // V5.2: [8.3.1] `assumption` clauses
0850 template <typename T, typename I, typename E> //
0851 struct NoParallelismT {
0852   using EmptyTrait = std::true_type;
0853 };
0854 
0855 // V5.2: [7.7.1] `branch` clauses
0856 template <typename T, typename I, typename E> //
0857 struct NotinbranchT {
0858   using EmptyTrait = std::true_type;
0859 };
0860 
0861 // V5.2: [7.6.1] `novariants` clause
0862 template <typename T, typename I, typename E> //
0863 struct NovariantsT {
0864   using DoNotUseVariant = E;
0865   using WrapperTrait = std::true_type;
0866   DoNotUseVariant v;
0867 };
0868 
0869 // V5.2: [15.6] `nowait` clause
0870 template <typename T, typename I, typename E> //
0871 struct NowaitT {
0872   using EmptyTrait = std::true_type;
0873 };
0874 
0875 // V5.2: [12.6.2] `num_tasks` clause
0876 template <typename T, typename I, typename E> //
0877 struct NumTasksT {
0878   using Prescriptiveness = type::Prescriptiveness;
0879   using NumTasks = E;
0880   using TupleTrait = std::true_type;
0881   std::tuple<OPT(Prescriptiveness), NumTasks> t;
0882 };
0883 
0884 // V5.2: [10.2.1] `num_teams` clause
0885 template <typename T, typename I, typename E> //
0886 struct NumTeamsT {
0887   using LowerBound = E;
0888   using UpperBound = E;
0889 
0890   // The name Range is not a spec name.
0891   struct Range {
0892     using TupleTrait = std::true_type;
0893     std::tuple<OPT(LowerBound), UpperBound> t;
0894   };
0895 
0896   // The name List is not a spec name. The list is an extension to allow
0897   // specifying a grid with connection with the ompx_bare clause.
0898   using List = ListT<Range>;
0899   using WrapperTrait = std::true_type;
0900   List v;
0901 };
0902 
0903 // V5.2: [10.1.2] `num_threads` clause
0904 template <typename T, typename I, typename E> //
0905 struct NumThreadsT {
0906   using Nthreads = E;
0907   using WrapperTrait = std::true_type;
0908   Nthreads v;
0909 };
0910 
0911 template <typename T, typename I, typename E> //
0912 struct OmpxAttributeT {
0913   using EmptyTrait = std::true_type;
0914 };
0915 
0916 template <typename T, typename I, typename E> //
0917 struct OmpxBareT {
0918   using EmptyTrait = std::true_type;
0919 };
0920 
0921 template <typename T, typename I, typename E> //
0922 struct OmpxDynCgroupMemT {
0923   using WrapperTrait = std::true_type;
0924   E v;
0925 };
0926 
0927 // V5.2: [10.3] `order` clause
0928 template <typename T, typename I, typename E> //
0929 struct OrderT {
0930   ENUM(OrderModifier, Reproducible, Unconstrained);
0931   ENUM(Ordering, Concurrent);
0932   using TupleTrait = std::true_type;
0933   std::tuple<OPT(OrderModifier), Ordering> t;
0934 };
0935 
0936 // V5.2: [4.4.4] `ordered` clause
0937 template <typename T, typename I, typename E> //
0938 struct OrderedT {
0939   using N = E;
0940   using WrapperTrait = std::true_type;
0941   OPT(N) v;
0942 };
0943 
0944 // V5.2: [7.4.2] `otherwise` clause
0945 template <typename T, typename I, typename E> //
0946 struct OtherwiseT {
0947   using IncompleteTrait = std::true_type;
0948 };
0949 
0950 // V5.2: [9.2.2] `partial` clause
0951 template <typename T, typename I, typename E> //
0952 struct PartialT {
0953   using UnrollFactor = E;
0954   using WrapperTrait = std::true_type;
0955   OPT(UnrollFactor) v;
0956 };
0957 
0958 // V6.0:  `permutation` clause
0959 template <typename T, typename I, typename E> //
0960 struct PermutationT {
0961   using ArgList = ListT<E>;
0962   using WrapperTrait = std::true_type;
0963   ArgList v;
0964 };
0965 
0966 // V5.2: [12.4] `priority` clause
0967 template <typename T, typename I, typename E> //
0968 struct PriorityT {
0969   using PriorityValue = E;
0970   using WrapperTrait = std::true_type;
0971   PriorityValue v;
0972 };
0973 
0974 // V5.2: [5.4.3] `private` clause
0975 template <typename T, typename I, typename E> //
0976 struct PrivateT {
0977   using List = ObjectListT<I, E>;
0978   using WrapperTrait = std::true_type;
0979   List v;
0980 };
0981 
0982 // V5.2: [10.1.4] `proc_bind` clause
0983 template <typename T, typename I, typename E> //
0984 struct ProcBindT {
0985   ENUM(AffinityPolicy, Close, Master, Spread, Primary);
0986   using WrapperTrait = std::true_type;
0987   AffinityPolicy v;
0988 };
0989 
0990 // V5.2: [15.8.2] Atomic clauses
0991 template <typename T, typename I, typename E> //
0992 struct ReadT {
0993   using EmptyTrait = std::true_type;
0994 };
0995 
0996 // V5.2: [5.5.8] `reduction` clause
0997 template <typename T, typename I, typename E> //
0998 struct ReductionT {
0999   using List = ObjectListT<I, E>;
1000   // See note at the definition of the ReductionIdentifierT type.
1001   // The name ReductionIdentifiers is not a spec name.
1002   using ReductionIdentifiers = ListT<type::ReductionIdentifierT<I, E>>;
1003   ENUM(ReductionModifier, Default, Inscan, Task);
1004   using TupleTrait = std::true_type;
1005   std::tuple<OPT(ReductionModifier), ReductionIdentifiers, List> t;
1006 };
1007 
1008 // V5.2: [15.8.1] `memory-order` clauses
1009 template <typename T, typename I, typename E> //
1010 struct RelaxedT {
1011   using EmptyTrait = std::true_type;
1012 };
1013 
1014 // V5.2: [15.8.1] `memory-order` clauses
1015 template <typename T, typename I, typename E> //
1016 struct ReleaseT {
1017   using EmptyTrait = std::true_type;
1018 };
1019 
1020 // V5.2: [8.2.1] `requirement` clauses
1021 template <typename T, typename I, typename E> //
1022 struct ReverseOffloadT {
1023   using EmptyTrait = std::true_type;
1024 };
1025 
1026 // V5.2: [10.4.2] `safelen` clause
1027 template <typename T, typename I, typename E> //
1028 struct SafelenT {
1029   using Length = E;
1030   using WrapperTrait = std::true_type;
1031   Length v;
1032 };
1033 
1034 // V5.2: [11.5.3] `schedule` clause
1035 template <typename T, typename I, typename E> //
1036 struct ScheduleT {
1037   ENUM(Kind, Static, Dynamic, Guided, Auto, Runtime);
1038   using ChunkSize = E;
1039   ENUM(OrderingModifier, Monotonic, Nonmonotonic);
1040   ENUM(ChunkModifier, Simd);
1041   using TupleTrait = std::true_type;
1042   std::tuple<Kind, OPT(OrderingModifier), OPT(ChunkModifier), OPT(ChunkSize)> t;
1043 };
1044 
1045 // V5.2: [15.8.1] Memory-order clauses
1046 template <typename T, typename I, typename E> //
1047 struct SeqCstT {
1048   using EmptyTrait = std::true_type;
1049 };
1050 
1051 // V5.2: [8.5.1] `severity` clause
1052 template <typename T, typename I, typename E> //
1053 struct SeverityT {
1054   ENUM(SevLevel, Fatal, Warning);
1055   using WrapperTrait = std::true_type;
1056   SevLevel v;
1057 };
1058 
1059 // V5.2: [5.4.2] `shared` clause
1060 template <typename T, typename I, typename E> //
1061 struct SharedT {
1062   using List = ObjectListT<I, E>;
1063   using WrapperTrait = std::true_type;
1064   List v;
1065 };
1066 
1067 // V5.2: [15.10.3] `parallelization-level` clauses
1068 template <typename T, typename I, typename E> //
1069 struct SimdT {
1070   using EmptyTrait = std::true_type;
1071 };
1072 
1073 // V5.2: [10.4.3] `simdlen` clause
1074 template <typename T, typename I, typename E> //
1075 struct SimdlenT {
1076   using Length = E;
1077   using WrapperTrait = std::true_type;
1078   Length v;
1079 };
1080 
1081 // V5.2: [9.1.1] `sizes` clause
1082 template <typename T, typename I, typename E> //
1083 struct SizesT {
1084   using SizeList = ListT<E>;
1085   using WrapperTrait = std::true_type;
1086   SizeList v;
1087 };
1088 
1089 // V5.2: [5.5.9] `task_reduction` clause
1090 template <typename T, typename I, typename E> //
1091 struct TaskReductionT {
1092   using List = ObjectListT<I, E>;
1093   // See note at the definition of the ReductionIdentifierT type.
1094   // The name ReductionIdentifiers is not a spec name.
1095   using ReductionIdentifiers = ListT<type::ReductionIdentifierT<I, E>>;
1096   using TupleTrait = std::true_type;
1097   std::tuple<ReductionIdentifiers, List> t;
1098 };
1099 
1100 // V5.2: [13.3] `thread_limit` clause
1101 template <typename T, typename I, typename E> //
1102 struct ThreadLimitT {
1103   using Threadlim = E;
1104   using WrapperTrait = std::true_type;
1105   Threadlim v;
1106 };
1107 
1108 // V5.2: [15.10.3] `parallelization-level` clauses
1109 template <typename T, typename I, typename E> //
1110 struct ThreadsT {
1111   using EmptyTrait = std::true_type;
1112 };
1113 
1114 // V5.2: [5.9.1] `to` clause
1115 template <typename T, typename I, typename E> //
1116 struct ToT {
1117   using LocatorList = ObjectListT<I, E>;
1118   using Expectation = type::MotionExpectation;
1119   // See note at the definition of the MapperT type.
1120   using Mappers = ListT<type::MapperT<I, E>>; // Not a spec name
1121   using Iterator = type::IteratorT<T, I, E>;
1122 
1123   using TupleTrait = std::true_type;
1124   std::tuple<OPT(Expectation), OPT(Mappers), OPT(Iterator), LocatorList> t;
1125 };
1126 
1127 // V5.2: [8.2.1] `requirement` clauses
1128 template <typename T, typename I, typename E> //
1129 struct UnifiedAddressT {
1130   using EmptyTrait = std::true_type;
1131 };
1132 
1133 // V5.2: [8.2.1] `requirement` clauses
1134 template <typename T, typename I, typename E> //
1135 struct UnifiedSharedMemoryT {
1136   using EmptyTrait = std::true_type;
1137 };
1138 
1139 // V5.2: [5.10] `uniform` clause
1140 template <typename T, typename I, typename E> //
1141 struct UniformT {
1142   using ParameterList = ObjectListT<I, E>;
1143   using WrapperTrait = std::true_type;
1144   ParameterList v;
1145 };
1146 
1147 template <typename T, typename I, typename E> //
1148 struct UnknownT {
1149   using EmptyTrait = std::true_type;
1150 };
1151 
1152 // V5.2: [12.1] `untied` clause
1153 template <typename T, typename I, typename E> //
1154 struct UntiedT {
1155   using EmptyTrait = std::true_type;
1156 };
1157 
1158 // Both of the following
1159 // V5.2: [15.8.2] `atomic` clauses
1160 // V5.2: [15.9.3] `update` clause
1161 template <typename T, typename I, typename E> //
1162 struct UpdateT {
1163   using DependenceType = tomp::type::DependenceType;
1164   using WrapperTrait = std::true_type;
1165   OPT(DependenceType) v;
1166 };
1167 
1168 // V5.2: [14.1.3] `use` clause
1169 template <typename T, typename I, typename E> //
1170 struct UseT {
1171   using InteropVar = ObjectT<I, E>;
1172   using WrapperTrait = std::true_type;
1173   InteropVar v;
1174 };
1175 
1176 // V5.2: [5.4.10] `use_device_addr` clause
1177 template <typename T, typename I, typename E> //
1178 struct UseDeviceAddrT {
1179   using List = ObjectListT<I, E>;
1180   using WrapperTrait = std::true_type;
1181   List v;
1182 };
1183 
1184 // V5.2: [5.4.8] `use_device_ptr` clause
1185 template <typename T, typename I, typename E> //
1186 struct UseDevicePtrT {
1187   using List = ObjectListT<I, E>;
1188   using WrapperTrait = std::true_type;
1189   List v;
1190 };
1191 
1192 // V5.2: [6.8] `uses_allocators` clause
1193 template <typename T, typename I, typename E> //
1194 struct UsesAllocatorsT {
1195   using MemSpace = E;
1196   using TraitsArray = ObjectT<I, E>;
1197   using Allocator = E;
1198   struct AllocatorSpec { // Not a spec name
1199     using TupleTrait = std::true_type;
1200     std::tuple<OPT(MemSpace), OPT(TraitsArray), Allocator> t;
1201   };
1202   using Allocators = ListT<AllocatorSpec>; // Not a spec name
1203   using WrapperTrait = std::true_type;
1204   Allocators v;
1205 };
1206 
1207 // V5.2: [15.8.3] `extended-atomic` clauses
1208 template <typename T, typename I, typename E> //
1209 struct WeakT {
1210   using EmptyTrait = std::true_type;
1211 };
1212 
1213 // V5.2: [7.4.1] `when` clause
1214 template <typename T, typename I, typename E> //
1215 struct WhenT {
1216   using IncompleteTrait = std::true_type;
1217 };
1218 
1219 // V5.2: [15.8.2] Atomic clauses
1220 template <typename T, typename I, typename E> //
1221 struct WriteT {
1222   using EmptyTrait = std::true_type;
1223 };
1224 
1225 // ---
1226 
1227 template <typename T, typename I, typename E>
1228 using ExtensionClausesT =
1229     std::variant<OmpxAttributeT<T, I, E>, OmpxBareT<T, I, E>,
1230                  OmpxDynCgroupMemT<T, I, E>>;
1231 
1232 template <typename T, typename I, typename E>
1233 using EmptyClausesT = std::variant<
1234     AcqRelT<T, I, E>, AcquireT<T, I, E>, CaptureT<T, I, E>, CompareT<T, I, E>,
1235     DynamicAllocatorsT<T, I, E>, FullT<T, I, E>, InbranchT<T, I, E>,
1236     MergeableT<T, I, E>, NogroupT<T, I, E>, NoOpenmpRoutinesT<T, I, E>,
1237     NoOpenmpT<T, I, E>, NoParallelismT<T, I, E>, NotinbranchT<T, I, E>,
1238     NowaitT<T, I, E>, ReadT<T, I, E>, RelaxedT<T, I, E>, ReleaseT<T, I, E>,
1239     ReverseOffloadT<T, I, E>, SeqCstT<T, I, E>, SimdT<T, I, E>,
1240     ThreadsT<T, I, E>, UnifiedAddressT<T, I, E>, UnifiedSharedMemoryT<T, I, E>,
1241     UnknownT<T, I, E>, UntiedT<T, I, E>, UseT<T, I, E>, WeakT<T, I, E>,
1242     WriteT<T, I, E>>;
1243 
1244 template <typename T, typename I, typename E>
1245 using IncompleteClausesT =
1246     std::variant<AdjustArgsT<T, I, E>, AppendArgsT<T, I, E>, MatchT<T, I, E>,
1247                  OtherwiseT<T, I, E>, WhenT<T, I, E>>;
1248 
1249 template <typename T, typename I, typename E>
1250 using TupleClausesT =
1251     std::variant<AffinityT<T, I, E>, AlignedT<T, I, E>, AllocateT<T, I, E>,
1252                  DefaultmapT<T, I, E>, DeviceT<T, I, E>, DistScheduleT<T, I, E>,
1253                  DoacrossT<T, I, E>, FromT<T, I, E>, GrainsizeT<T, I, E>,
1254                  IfT<T, I, E>, InitT<T, I, E>, InReductionT<T, I, E>,
1255                  LastprivateT<T, I, E>, LinearT<T, I, E>, MapT<T, I, E>,
1256                  NumTasksT<T, I, E>, OrderT<T, I, E>, ReductionT<T, I, E>,
1257                  ScheduleT<T, I, E>, TaskReductionT<T, I, E>, ToT<T, I, E>>;
1258 
1259 template <typename T, typename I, typename E>
1260 using UnionClausesT = std::variant<DependT<T, I, E>>;
1261 
1262 template <typename T, typename I, typename E>
1263 using WrapperClausesT = std::variant<
1264     AbsentT<T, I, E>, AlignT<T, I, E>, AllocatorT<T, I, E>,
1265     AtomicDefaultMemOrderT<T, I, E>, AtT<T, I, E>, BindT<T, I, E>,
1266     CollapseT<T, I, E>, ContainsT<T, I, E>, CopyinT<T, I, E>,
1267     CopyprivateT<T, I, E>, DefaultT<T, I, E>, DestroyT<T, I, E>,
1268     DetachT<T, I, E>, DeviceTypeT<T, I, E>, EnterT<T, I, E>,
1269     ExclusiveT<T, I, E>, FailT<T, I, E>, FilterT<T, I, E>, FinalT<T, I, E>,
1270     FirstprivateT<T, I, E>, HasDeviceAddrT<T, I, E>, HintT<T, I, E>,
1271     HoldsT<T, I, E>, InclusiveT<T, I, E>, IndirectT<T, I, E>,
1272     InitializerT<T, I, E>, IsDevicePtrT<T, I, E>, LinkT<T, I, E>,
1273     MessageT<T, I, E>, NocontextT<T, I, E>, NontemporalT<T, I, E>,
1274     NovariantsT<T, I, E>, NumTeamsT<T, I, E>, NumThreadsT<T, I, E>,
1275     OrderedT<T, I, E>, PartialT<T, I, E>, PriorityT<T, I, E>, PrivateT<T, I, E>,
1276     ProcBindT<T, I, E>, SafelenT<T, I, E>, SeverityT<T, I, E>, SharedT<T, I, E>,
1277     SimdlenT<T, I, E>, SizesT<T, I, E>, PermutationT<T, I, E>,
1278     ThreadLimitT<T, I, E>, UniformT<T, I, E>, UpdateT<T, I, E>,
1279     UseDeviceAddrT<T, I, E>, UseDevicePtrT<T, I, E>, UsesAllocatorsT<T, I, E>>;
1280 
1281 template <typename T, typename I, typename E>
1282 using UnionOfAllClausesT = typename type::Union< //
1283     EmptyClausesT<T, I, E>,                      //
1284     ExtensionClausesT<T, I, E>,                  //
1285     IncompleteClausesT<T, I, E>,                 //
1286     TupleClausesT<T, I, E>,                      //
1287     UnionClausesT<T, I, E>,                      //
1288     WrapperClausesT<T, I, E>                     //
1289     >::type;
1290 } // namespace clause
1291 
1292 using type::operator==;
1293 
1294 // The variant wrapper that encapsulates all possible specific clauses.
1295 // The `Extras` arguments are additional types representing local extensions
1296 // to the clause set, e.g.
1297 //
1298 // using Clause = ClauseT<Type, Id, Expr,
1299 //                        MyClause1, MyClause2>;
1300 //
1301 // The member Clause::u will be a variant containing all specific clauses
1302 // defined above, plus MyClause1 and MyClause2.
1303 //
1304 // Note: Any derived class must be constructible from the base class
1305 // ClauseT<...>.
1306 template <typename TypeType, typename IdType, typename ExprType,
1307           typename... Extras>
1308 struct ClauseT {
1309   using TypeTy = TypeType;
1310   using IdTy = IdType;
1311   using ExprTy = ExprType;
1312 
1313   // Type of "self" to specify this type given a derived class type.
1314   using BaseT = ClauseT<TypeType, IdType, ExprType, Extras...>;
1315 
1316   using VariantTy = typename type::Union<
1317       clause::UnionOfAllClausesT<TypeType, IdType, ExprType>,
1318       std::variant<Extras...>>::type;
1319 
1320   llvm::omp::Clause id; // The numeric id of the clause
1321   using UnionTrait = std::true_type;
1322   VariantTy u;
1323 };
1324 
1325 template <typename ClauseType> struct DirectiveWithClauses {
1326   llvm::omp::Directive id = llvm::omp::Directive::OMPD_unknown;
1327   tomp::type::ListT<ClauseType> clauses;
1328 };
1329 
1330 } // namespace tomp
1331 
1332 #undef OPT
1333 #undef ENUM
1334 
1335 #endif // LLVM_FRONTEND_OPENMP_CLAUSET_H