1 //===- ClauseT.h -- clause template definitions ---------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // This file contains template classes that represent OpenMP clauses, as 9 // described in the OpenMP API specification. 10 // 11 // The general structure of any specific clause class is that it is either 12 // empty, or it consists of a single data member, which can take one of these 13 // three forms: 14 // - a value member, named `v`, or 15 // - a tuple of values, named `t`, or 16 // - a variant (i.e. union) of values, named `u`. 17 // To assist with generic visit algorithms, classes define one of the following 18 // traits: 19 // - EmptyTrait: the class has no data members. 20 // - WrapperTrait: the class has a single member `v` 21 // - TupleTrait: the class has a tuple member `t` 22 // - UnionTrait the class has a variant member `u` 23 // - IncompleteTrait: the class is a placeholder class that is currently empty, 24 // but will be completed at a later time. 25 // Note: This structure follows the one used in flang parser. 26 // 27 // The types used in the class definitions follow the names used in the spec 28 // (there are a few exceptions to this). For example, given 29 // Clause `foo` 30 // - foo-modifier : description... 31 // - list : list of variables 32 // the corresponding class would be 33 // template <...> 34 // struct FooT { 35 // using FooModifier = type that can represent the modifier 36 // using List = ListT<ObjectT<...>>; 37 // using TupleTrait = std::true_type; 38 // std::tuple<std::optional<FooModifier>, List> t; 39 // }; 40 //===----------------------------------------------------------------------===// 41 #ifndef LLVM_FRONTEND_OPENMP_CLAUSET_H 42 #define LLVM_FRONTEND_OPENMP_CLAUSET_H 43 44 #include "llvm/ADT/ArrayRef.h" 45 #include "llvm/ADT/DenseMap.h" 46 #include "llvm/ADT/DenseSet.h" 47 #include "llvm/ADT/STLExtras.h" 48 #include "llvm/ADT/SmallVector.h" 49 #include "llvm/Frontend/OpenMP/OMP.h" 50 #include "llvm/Support/ErrorHandling.h" 51 #include "llvm/Support/raw_ostream.h" 52 53 #include <algorithm> 54 #include <iterator> 55 #include <optional> 56 #include <tuple> 57 #include <type_traits> 58 #include <utility> 59 #include <variant> 60 61 #define ENUM(Name, ...) enum class Name { __VA_ARGS__ } 62 #define OPT(x) std::optional<x> 63 64 // A number of OpenMP clauses contain values that come from a given set of 65 // possibilities. In the IR these are usually represented by enums. Both 66 // clang and flang use different types for the enums, and the enum elements 67 // representing the same thing may have different values between clang and 68 // flang. 69 // Since the representation below tries to adhere to the spec, and be source 70 // language agnostic, it defines its own enums, independent from any language 71 // frontend. As a consequence, when instantiating the templates below, 72 // frontend-specific enums need to be translated into the representation 73 // used here. The macros below are intended to assist with the conversion. 74 75 // Helper macro for enum-class conversion. 76 #define CLAUSET_SCOPED_ENUM_MEMBER_CONVERT(Ov, Tv) \ 77 if (v == OtherEnum::Ov) { \ 78 return ThisEnum::Tv; \ 79 } 80 81 // Helper macro for enum (non-class) conversion. 82 #define CLAUSET_UNSCOPED_ENUM_MEMBER_CONVERT(Ov, Tv) \ 83 if (v == Ov) { \ 84 return ThisEnum::Tv; \ 85 } 86 87 #define CLAUSET_ENUM_CONVERT(func, OtherE, ThisE, Maps) \ 88 auto func = [](OtherE v) -> ThisE { \ 89 using ThisEnum = ThisE; \ 90 using OtherEnum = OtherE; \ 91 (void)sizeof(OtherEnum); /*Avoid "unused local typedef" warning*/ \ 92 Maps; \ 93 llvm_unreachable("Unexpected value in " #OtherE); \ 94 } 95 96 // Usage: 97 // 98 // Given two enums, 99 // enum class Other { o1, o2 }; 100 // enum class This { t1, t2 }; 101 // generate conversion function "Func : Other -> This" with 102 // CLAUSET_ENUM_CONVERT( 103 // Func, Other, This, 104 // CLAUSET_ENUM_MEMBER_CONVERT(o1, t1) // <- No comma 105 // CLAUSET_ENUM_MEMBER_CONVERT(o2, t2) 106 // ... 107 // ) 108 // 109 // Note that the sequence of M(other-value, this-value) is separated 110 // with _spaces_, not commas. 111 112 namespace detail { 113 // Type trait to determine whether T is a specialization of std::variant. 114 template <typename T> struct is_variant { 115 static constexpr bool value = false; 116 }; 117 118 template <typename... Ts> struct is_variant<std::variant<Ts...>> { 119 static constexpr bool value = true; 120 }; 121 122 template <typename T> constexpr bool is_variant_v = is_variant<T>::value; 123 124 // Helper utility to create a type which is a union of two given variants. 125 template <typename...> struct UnionOfTwo; 126 127 template <typename... Types1, typename... Types2> 128 struct UnionOfTwo<std::variant<Types1...>, std::variant<Types2...>> { 129 using type = std::variant<Types1..., Types2...>; 130 }; 131 } // namespace detail 132 133 namespace tomp { 134 namespace type { 135 136 // Helper utility to create a type which is a union of an arbitrary number 137 // of variants. 138 template <typename...> struct Union; 139 140 template <> struct Union<> { 141 // Legal to define, illegal to instantiate. 142 using type = std::variant<>; 143 }; 144 145 template <typename T, typename... Ts> struct Union<T, Ts...> { 146 static_assert(detail::is_variant_v<T>); 147 using type = 148 typename detail::UnionOfTwo<T, typename Union<Ts...>::type>::type; 149 }; 150 151 template <typename T> using ListT = llvm::SmallVector<T, 0>; 152 153 // The ObjectT class represents a variable or a locator (as defined in 154 // the OpenMP spec). 155 // Note: the ObjectT template is not defined. Any user of it is expected to 156 // provide their own specialization that conforms to the requirements listed 157 // below. 158 // 159 // Let ObjectS be any specialization of ObjectT: 160 // 161 // ObjectS must provide the following definitions: 162 // { 163 // using IdTy = Id; 164 // using ExprTy = Expr; 165 // 166 // auto id() const -> IdTy { 167 // // Return a value such that a.id() == b.id() if and only if: 168 // // (1) both `a` and `b` represent the same variable or location, or 169 // // (2) bool(a.id()) == false and bool(b.id()) == false 170 // } 171 // } 172 // 173 // The type IdTy should be hashable (usable as key in unordered containers). 174 // 175 // Values of type IdTy should be contextually convertible to `bool`. 176 // 177 // If S is an object of type ObjectS, then `bool(S.id())` is `false` if 178 // and only if S does not represent any variable or location. 179 // 180 // ObjectS should be copyable, movable, and default-constructible. 181 template <typename IdType, typename ExprType> struct ObjectT; 182 183 // By default, object equality is only determined by its identity. 184 template <typename I, typename E> 185 bool operator==(const ObjectT<I, E> &o1, const ObjectT<I, E> &o2) { 186 return o1.id() == o2.id(); 187 } 188 189 template <typename I, typename E> using ObjectListT = ListT<ObjectT<I, E>>; 190 191 using DirectiveName = llvm::omp::Directive; 192 193 template <typename I, typename E> // 194 struct DefinedOperatorT { 195 struct DefinedOpName { 196 using WrapperTrait = std::true_type; 197 ObjectT<I, E> v; 198 }; 199 ENUM(IntrinsicOperator, Power, Multiply, Divide, Add, Subtract, Concat, LT, 200 LE, EQ, NE, GE, GT, NOT, AND, OR, EQV, NEQV, Min, Max); 201 using UnionTrait = std::true_type; 202 std::variant<DefinedOpName, IntrinsicOperator> u; 203 }; 204 205 // V5.2: [3.2.6] `iterator` modifier 206 template <typename E> // 207 struct RangeT { 208 // range-specification: begin : end[: step] 209 using TupleTrait = std::true_type; 210 std::tuple<E, E, OPT(E)> t; 211 }; 212 213 // V5.2: [3.2.6] `iterator` modifier 214 template <typename TypeType, typename IdType, typename ExprType> // 215 struct IteratorSpecifierT { 216 // iterators-specifier: [ iterator-type ] identifier = range-specification 217 using TupleTrait = std::true_type; 218 std::tuple<OPT(TypeType), ObjectT<IdType, ExprType>, RangeT<ExprType>> t; 219 }; 220 221 // Note: 222 // For motion or map clauses the OpenMP spec allows a unique mapper modifier. 223 // In practice, since these clauses apply to multiple objects, there can be 224 // multiple effective mappers applicable to these objects (due to overloads, 225 // etc.). Because of that store a list of mappers every time a mapper modifier 226 // is allowed. If the mapper list contains a single element, it applies to 227 // all objects in the clause, otherwise there should be as many mappers as 228 // there are objects. 229 // V5.2: [5.8.2] Mapper identifiers and `mapper` modifiers 230 template <typename I, typename E> // 231 struct MapperT { 232 using MapperIdentifier = ObjectT<I, E>; 233 using WrapperTrait = std::true_type; 234 MapperIdentifier v; 235 }; 236 237 // V5.2: [15.8.1] `memory-order` clauses 238 // When used as arguments for other clauses, e.g. `fail`. 239 ENUM(MemoryOrder, AcqRel, Acquire, Relaxed, Release, SeqCst); 240 ENUM(MotionExpectation, Present); 241 // V5.2: [15.9.1] `task-dependence-type` modifier 242 ENUM(TaskDependenceType, In, Out, Inout, Mutexinoutset, Inoutset, Depobj); 243 244 template <typename I, typename E> // 245 struct LoopIterationT { 246 struct Distance { 247 using TupleTrait = std::true_type; 248 std::tuple<DefinedOperatorT<I, E>, E> t; 249 }; 250 using TupleTrait = std::true_type; 251 std::tuple<ObjectT<I, E>, OPT(Distance)> t; 252 }; 253 254 template <typename I, typename E> // 255 struct ProcedureDesignatorT { 256 using WrapperTrait = std::true_type; 257 ObjectT<I, E> v; 258 }; 259 260 // Note: 261 // For reduction clauses the OpenMP spec allows a unique reduction identifier. 262 // For reasons analogous to those listed for the MapperT type, clauses that 263 // according to the spec contain a reduction identifier will contain a list of 264 // reduction identifiers. The same constraints apply: there is either a single 265 // identifier that applies to all objects, or there are as many identifiers 266 // as there are objects. 267 template <typename I, typename E> // 268 struct ReductionIdentifierT { 269 using UnionTrait = std::true_type; 270 std::variant<DefinedOperatorT<I, E>, ProcedureDesignatorT<I, E>> u; 271 }; 272 273 template <typename T, typename I, typename E> // 274 using IteratorT = ListT<IteratorSpecifierT<T, I, E>>; 275 276 template <typename T> 277 std::enable_if_t<T::EmptyTrait::value, bool> operator==(const T &a, 278 const T &b) { 279 return true; 280 } 281 template <typename T> 282 std::enable_if_t<T::IncompleteTrait::value, bool> operator==(const T &a, 283 const T &b) { 284 return true; 285 } 286 template <typename T> 287 std::enable_if_t<T::WrapperTrait::value, bool> operator==(const T &a, 288 const T &b) { 289 return a.v == b.v; 290 } 291 template <typename T> 292 std::enable_if_t<T::TupleTrait::value, bool> operator==(const T &a, 293 const T &b) { 294 return a.t == b.t; 295 } 296 template <typename T> 297 std::enable_if_t<T::UnionTrait::value, bool> operator==(const T &a, 298 const T &b) { 299 return a.u == b.u; 300 } 301 } // namespace type 302 303 template <typename T> using ListT = type::ListT<T>; 304 305 template <typename I, typename E> using ObjectT = type::ObjectT<I, E>; 306 template <typename I, typename E> using ObjectListT = type::ObjectListT<I, E>; 307 308 template <typename T, typename I, typename E> 309 using IteratorT = type::IteratorT<T, I, E>; 310 311 template < 312 typename ContainerTy, typename FunctionTy, 313 typename ElemTy = typename llvm::remove_cvref_t<ContainerTy>::value_type, 314 typename ResultTy = std::invoke_result_t<FunctionTy, ElemTy>> 315 ListT<ResultTy> makeList(ContainerTy &&container, FunctionTy &&func) { 316 ListT<ResultTy> v; 317 llvm::transform(container, std::back_inserter(v), func); 318 return v; 319 } 320 321 namespace clause { 322 using type::operator==; 323 324 // V5.2: [8.3.1] `assumption` clauses 325 template <typename T, typename I, typename E> // 326 struct AbsentT { 327 using List = ListT<type::DirectiveName>; 328 using WrapperTrait = std::true_type; 329 List v; 330 }; 331 332 // V5.2: [15.8.1] `memory-order` clauses 333 template <typename T, typename I, typename E> // 334 struct AcqRelT { 335 using EmptyTrait = std::true_type; 336 }; 337 338 // V5.2: [15.8.1] `memory-order` clauses 339 template <typename T, typename I, typename E> // 340 struct AcquireT { 341 using EmptyTrait = std::true_type; 342 }; 343 344 // V5.2: [7.5.2] `adjust_args` clause 345 template <typename T, typename I, typename E> // 346 struct AdjustArgsT { 347 using IncompleteTrait = std::true_type; 348 }; 349 350 // V5.2: [12.5.1] `affinity` clause 351 template <typename T, typename I, typename E> // 352 struct AffinityT { 353 using Iterator = type::IteratorT<T, I, E>; 354 using LocatorList = ObjectListT<I, E>; 355 356 using TupleTrait = std::true_type; 357 std::tuple<OPT(Iterator), LocatorList> t; 358 }; 359 360 // V5.2: [6.3] `align` clause 361 template <typename T, typename I, typename E> // 362 struct AlignT { 363 using Alignment = E; 364 365 using WrapperTrait = std::true_type; 366 Alignment v; 367 }; 368 369 // V5.2: [5.11] `aligned` clause 370 template <typename T, typename I, typename E> // 371 struct AlignedT { 372 using Alignment = E; 373 using List = ObjectListT<I, E>; 374 375 using TupleTrait = std::true_type; 376 std::tuple<OPT(Alignment), List> t; 377 }; 378 379 template <typename T, typename I, typename E> // 380 struct AllocatorT; 381 382 // V5.2: [6.6] `allocate` clause 383 template <typename T, typename I, typename E> // 384 struct AllocateT { 385 using AllocatorSimpleModifier = E; 386 using AllocatorComplexModifier = AllocatorT<T, I, E>; 387 using AlignModifier = AlignT<T, I, E>; 388 using List = ObjectListT<I, E>; 389 390 using TupleTrait = std::true_type; 391 std::tuple<OPT(AllocatorSimpleModifier), OPT(AllocatorComplexModifier), 392 OPT(AlignModifier), List> 393 t; 394 }; 395 396 // V5.2: [6.4] `allocator` clause 397 template <typename T, typename I, typename E> // 398 struct AllocatorT { 399 using Allocator = E; 400 using WrapperTrait = std::true_type; 401 Allocator v; 402 }; 403 404 // V5.2: [7.5.3] `append_args` clause 405 template <typename T, typename I, typename E> // 406 struct AppendArgsT { 407 using IncompleteTrait = std::true_type; 408 }; 409 410 // V5.2: [8.1] `at` clause 411 template <typename T, typename I, typename E> // 412 struct AtT { 413 ENUM(ActionTime, Compilation, Execution); 414 using WrapperTrait = std::true_type; 415 ActionTime v; 416 }; 417 418 // V5.2: [8.2.1] `requirement` clauses 419 template <typename T, typename I, typename E> // 420 struct AtomicDefaultMemOrderT { 421 using MemoryOrder = type::MemoryOrder; 422 using WrapperTrait = std::true_type; 423 MemoryOrder v; // Name not provided in spec 424 }; 425 426 // V5.2: [11.7.1] `bind` clause 427 template <typename T, typename I, typename E> // 428 struct BindT { 429 ENUM(Binding, Teams, Parallel, Thread); 430 using WrapperTrait = std::true_type; 431 Binding v; 432 }; 433 434 // V5.2: [15.8.3] `extended-atomic` clauses 435 template <typename T, typename I, typename E> // 436 struct CaptureT { 437 using EmptyTrait = std::true_type; 438 }; 439 440 // V5.2: [4.4.3] `collapse` clause 441 template <typename T, typename I, typename E> // 442 struct CollapseT { 443 using N = E; 444 using WrapperTrait = std::true_type; 445 N v; 446 }; 447 448 // V5.2: [15.8.3] `extended-atomic` clauses 449 template <typename T, typename I, typename E> // 450 struct CompareT { 451 using EmptyTrait = std::true_type; 452 }; 453 454 // V5.2: [8.3.1] `assumption` clauses 455 template <typename T, typename I, typename E> // 456 struct ContainsT { 457 using List = ListT<type::DirectiveName>; 458 using WrapperTrait = std::true_type; 459 List v; 460 }; 461 462 // V5.2: [5.7.1] `copyin` clause 463 template <typename T, typename I, typename E> // 464 struct CopyinT { 465 using List = ObjectListT<I, E>; 466 using WrapperTrait = std::true_type; 467 List v; 468 }; 469 470 // V5.2: [5.7.2] `copyprivate` clause 471 template <typename T, typename I, typename E> // 472 struct CopyprivateT { 473 using List = ObjectListT<I, E>; 474 using WrapperTrait = std::true_type; 475 List v; 476 }; 477 478 // V5.2: [5.4.1] `default` clause 479 template <typename T, typename I, typename E> // 480 struct DefaultT { 481 ENUM(DataSharingAttribute, Firstprivate, None, Private, Shared); 482 using WrapperTrait = std::true_type; 483 DataSharingAttribute v; 484 }; 485 486 // V5.2: [5.8.7] `defaultmap` clause 487 template <typename T, typename I, typename E> // 488 struct DefaultmapT { 489 ENUM(ImplicitBehavior, Alloc, To, From, Tofrom, Firstprivate, None, Default, 490 Present); 491 ENUM(VariableCategory, Scalar, Aggregate, Pointer, Allocatable); 492 using TupleTrait = std::true_type; 493 std::tuple<ImplicitBehavior, OPT(VariableCategory)> t; 494 }; 495 496 template <typename T, typename I, typename E> // 497 struct DoacrossT; 498 499 // V5.2: [15.9.5] `depend` clause 500 template <typename T, typename I, typename E> // 501 struct DependT { 502 using Iterator = type::IteratorT<T, I, E>; 503 using LocatorList = ObjectListT<I, E>; 504 using TaskDependenceType = tomp::type::TaskDependenceType; 505 506 struct WithLocators { // Modern form 507 using TupleTrait = std::true_type; 508 // Empty LocatorList means "omp_all_memory". 509 std::tuple<TaskDependenceType, OPT(Iterator), LocatorList> t; 510 }; 511 512 using Doacross = DoacrossT<T, I, E>; 513 using UnionTrait = std::true_type; 514 std::variant<Doacross, WithLocators> u; // Doacross form is legacy 515 }; 516 517 // V5.2: [3.5] `destroy` clause 518 template <typename T, typename I, typename E> // 519 struct DestroyT { 520 using DestroyVar = ObjectT<I, E>; 521 using WrapperTrait = std::true_type; 522 // DestroyVar can be ommitted in "depobj destroy". 523 OPT(DestroyVar) v; 524 }; 525 526 // V5.2: [12.5.2] `detach` clause 527 template <typename T, typename I, typename E> // 528 struct DetachT { 529 using EventHandle = ObjectT<I, E>; 530 using WrapperTrait = std::true_type; 531 EventHandle v; 532 }; 533 534 // V5.2: [13.2] `device` clause 535 template <typename T, typename I, typename E> // 536 struct DeviceT { 537 using DeviceDescription = E; 538 ENUM(DeviceModifier, Ancestor, DeviceNum); 539 using TupleTrait = std::true_type; 540 std::tuple<OPT(DeviceModifier), DeviceDescription> t; 541 }; 542 543 // V5.2: [13.1] `device_type` clause 544 template <typename T, typename I, typename E> // 545 struct DeviceTypeT { 546 ENUM(DeviceTypeDescription, Any, Host, Nohost); 547 using WrapperTrait = std::true_type; 548 DeviceTypeDescription v; 549 }; 550 551 // V5.2: [11.6.1] `dist_schedule` clause 552 template <typename T, typename I, typename E> // 553 struct DistScheduleT { 554 ENUM(Kind, Static); 555 using ChunkSize = E; 556 using TupleTrait = std::true_type; 557 std::tuple<Kind, OPT(ChunkSize)> t; 558 }; 559 560 // V5.2: [15.9.6] `doacross` clause 561 template <typename T, typename I, typename E> // 562 struct DoacrossT { 563 using Vector = ListT<type::LoopIterationT<I, E>>; 564 ENUM(DependenceType, Source, Sink); 565 using TupleTrait = std::true_type; 566 // Empty Vector means "omp_cur_iteration" 567 std::tuple<DependenceType, Vector> t; 568 }; 569 570 // V5.2: [8.2.1] `requirement` clauses 571 template <typename T, typename I, typename E> // 572 struct DynamicAllocatorsT { 573 using EmptyTrait = std::true_type; 574 }; 575 576 // V5.2: [5.8.4] `enter` clause 577 template <typename T, typename I, typename E> // 578 struct EnterT { 579 using List = ObjectListT<I, E>; 580 using WrapperTrait = std::true_type; 581 List v; 582 }; 583 584 // V5.2: [5.6.2] `exclusive` clause 585 template <typename T, typename I, typename E> // 586 struct ExclusiveT { 587 using WrapperTrait = std::true_type; 588 using List = ObjectListT<I, E>; 589 List v; 590 }; 591 592 // V5.2: [15.8.3] `extended-atomic` clauses 593 template <typename T, typename I, typename E> // 594 struct FailT { 595 using MemoryOrder = type::MemoryOrder; 596 using WrapperTrait = std::true_type; 597 MemoryOrder v; 598 }; 599 600 // V5.2: [10.5.1] `filter` clause 601 template <typename T, typename I, typename E> // 602 struct FilterT { 603 using ThreadNum = E; 604 using WrapperTrait = std::true_type; 605 ThreadNum v; 606 }; 607 608 // V5.2: [12.3] `final` clause 609 template <typename T, typename I, typename E> // 610 struct FinalT { 611 using Finalize = E; 612 using WrapperTrait = std::true_type; 613 Finalize v; 614 }; 615 616 // V5.2: [5.4.4] `firstprivate` clause 617 template <typename T, typename I, typename E> // 618 struct FirstprivateT { 619 using List = ObjectListT<I, E>; 620 using WrapperTrait = std::true_type; 621 List v; 622 }; 623 624 // V5.2: [5.9.2] `from` clause 625 template <typename T, typename I, typename E> // 626 struct FromT { 627 using LocatorList = ObjectListT<I, E>; 628 using Expectation = type::MotionExpectation; 629 using Iterator = type::IteratorT<T, I, E>; 630 // See note at the definition of the MapperT type. 631 using Mappers = ListT<type::MapperT<I, E>>; // Not a spec name 632 633 using TupleTrait = std::true_type; 634 std::tuple<OPT(Expectation), OPT(Mappers), OPT(Iterator), LocatorList> t; 635 }; 636 637 // V5.2: [9.2.1] `full` clause 638 template <typename T, typename I, typename E> // 639 struct FullT { 640 using EmptyTrait = std::true_type; 641 }; 642 643 // V5.2: [12.6.1] `grainsize` clause 644 template <typename T, typename I, typename E> // 645 struct GrainsizeT { 646 ENUM(Prescriptiveness, Strict); 647 using GrainSize = E; 648 using TupleTrait = std::true_type; 649 std::tuple<OPT(Prescriptiveness), GrainSize> t; 650 }; 651 652 // V5.2: [5.4.9] `has_device_addr` clause 653 template <typename T, typename I, typename E> // 654 struct HasDeviceAddrT { 655 using List = ObjectListT<I, E>; 656 using WrapperTrait = std::true_type; 657 List v; 658 }; 659 660 // V5.2: [15.1.2] `hint` clause 661 template <typename T, typename I, typename E> // 662 struct HintT { 663 using HintExpr = E; 664 using WrapperTrait = std::true_type; 665 HintExpr v; 666 }; 667 668 // V5.2: [8.3.1] Assumption clauses 669 template <typename T, typename I, typename E> // 670 struct HoldsT { 671 using WrapperTrait = std::true_type; 672 E v; // No argument name in spec 5.2 673 }; 674 675 // V5.2: [3.4] `if` clause 676 template <typename T, typename I, typename E> // 677 struct IfT { 678 using DirectiveNameModifier = type::DirectiveName; 679 using IfExpression = E; 680 using TupleTrait = std::true_type; 681 std::tuple<OPT(DirectiveNameModifier), IfExpression> t; 682 }; 683 684 // V5.2: [7.7.1] `branch` clauses 685 template <typename T, typename I, typename E> // 686 struct InbranchT { 687 using EmptyTrait = std::true_type; 688 }; 689 690 // V5.2: [5.6.1] `exclusive` clause 691 template <typename T, typename I, typename E> // 692 struct InclusiveT { 693 using List = ObjectListT<I, E>; 694 using WrapperTrait = std::true_type; 695 List v; 696 }; 697 698 // V5.2: [7.8.3] `indirect` clause 699 template <typename T, typename I, typename E> // 700 struct IndirectT { 701 using InvokedByFptr = E; 702 using WrapperTrait = std::true_type; 703 InvokedByFptr v; 704 }; 705 706 // V5.2: [14.1.2] `init` clause 707 template <typename T, typename I, typename E> // 708 struct InitT { 709 using ForeignRuntimeId = E; 710 using InteropVar = ObjectT<I, E>; 711 using InteropPreference = ListT<ForeignRuntimeId>; 712 ENUM(InteropType, Target, Targetsync); // Repeatable 713 using InteropTypes = ListT<InteropType>; // Not a spec name 714 715 using TupleTrait = std::true_type; 716 std::tuple<OPT(InteropPreference), InteropTypes, InteropVar> t; 717 }; 718 719 // V5.2: [5.5.4] `initializer` clause 720 template <typename T, typename I, typename E> // 721 struct InitializerT { 722 using InitializerExpr = E; 723 using WrapperTrait = std::true_type; 724 InitializerExpr v; 725 }; 726 727 // V5.2: [5.5.10] `in_reduction` clause 728 template <typename T, typename I, typename E> // 729 struct InReductionT { 730 using List = ObjectListT<I, E>; 731 // See note at the definition of the ReductionIdentifierT type. 732 // The name ReductionIdentifiers is not a spec name. 733 using ReductionIdentifiers = ListT<type::ReductionIdentifierT<I, E>>; 734 using TupleTrait = std::true_type; 735 std::tuple<ReductionIdentifiers, List> t; 736 }; 737 738 // V5.2: [5.4.7] `is_device_ptr` clause 739 template <typename T, typename I, typename E> // 740 struct IsDevicePtrT { 741 using List = ObjectListT<I, E>; 742 using WrapperTrait = std::true_type; 743 List v; 744 }; 745 746 // V5.2: [5.4.5] `lastprivate` clause 747 template <typename T, typename I, typename E> // 748 struct LastprivateT { 749 using List = ObjectListT<I, E>; 750 ENUM(LastprivateModifier, Conditional); 751 using TupleTrait = std::true_type; 752 std::tuple<OPT(LastprivateModifier), List> t; 753 }; 754 755 // V5.2: [5.4.6] `linear` clause 756 template <typename T, typename I, typename E> // 757 struct LinearT { 758 // std::get<type> won't work here due to duplicate types in the tuple. 759 using List = ObjectListT<I, E>; 760 using StepSimpleModifier = E; 761 using StepComplexModifier = E; 762 ENUM(LinearModifier, Ref, Val, Uval); 763 764 using TupleTrait = std::true_type; 765 // Step == nullopt means 1. 766 std::tuple<OPT(StepSimpleModifier), OPT(StepComplexModifier), 767 OPT(LinearModifier), List> 768 t; 769 }; 770 771 // V5.2: [5.8.5] `link` clause 772 template <typename T, typename I, typename E> // 773 struct LinkT { 774 using List = ObjectListT<I, E>; 775 using WrapperTrait = std::true_type; 776 List v; 777 }; 778 779 // V5.2: [5.8.3] `map` clause 780 template <typename T, typename I, typename E> // 781 struct MapT { 782 using LocatorList = ObjectListT<I, E>; 783 ENUM(MapType, To, From, Tofrom, Alloc, Release, Delete); 784 ENUM(MapTypeModifier, Always, Close, Present, OmpxHold); 785 // See note at the definition of the MapperT type. 786 using Mappers = ListT<type::MapperT<I, E>>; // Not a spec name 787 using Iterator = type::IteratorT<T, I, E>; 788 using MapTypeModifiers = ListT<MapTypeModifier>; // Not a spec name 789 790 using TupleTrait = std::true_type; 791 std::tuple<OPT(MapType), OPT(MapTypeModifiers), OPT(Mappers), OPT(Iterator), 792 LocatorList> 793 t; 794 }; 795 796 // V5.2: [7.5.1] `match` clause 797 template <typename T, typename I, typename E> // 798 struct MatchT { 799 using IncompleteTrait = std::true_type; 800 }; 801 802 // V5.2: [12.2] `mergeable` clause 803 template <typename T, typename I, typename E> // 804 struct MergeableT { 805 using EmptyTrait = std::true_type; 806 }; 807 808 // V5.2: [8.5.2] `message` clause 809 template <typename T, typename I, typename E> // 810 struct MessageT { 811 using MsgString = E; 812 using WrapperTrait = std::true_type; 813 MsgString v; 814 }; 815 816 // V5.2: [7.6.2] `nocontext` clause 817 template <typename T, typename I, typename E> // 818 struct NocontextT { 819 using DoNotUpdateContext = E; 820 using WrapperTrait = std::true_type; 821 DoNotUpdateContext v; 822 }; 823 824 // V5.2: [15.7] `nowait` clause 825 template <typename T, typename I, typename E> // 826 struct NogroupT { 827 using EmptyTrait = std::true_type; 828 }; 829 830 // V5.2: [10.4.1] `nontemporal` clause 831 template <typename T, typename I, typename E> // 832 struct NontemporalT { 833 using List = ObjectListT<I, E>; 834 using WrapperTrait = std::true_type; 835 List v; 836 }; 837 838 // V5.2: [8.3.1] `assumption` clauses 839 template <typename T, typename I, typename E> // 840 struct NoOpenmpT { 841 using EmptyTrait = std::true_type; 842 }; 843 844 // V5.2: [8.3.1] `assumption` clauses 845 template <typename T, typename I, typename E> // 846 struct NoOpenmpRoutinesT { 847 using EmptyTrait = std::true_type; 848 }; 849 850 // V5.2: [8.3.1] `assumption` clauses 851 template <typename T, typename I, typename E> // 852 struct NoParallelismT { 853 using EmptyTrait = std::true_type; 854 }; 855 856 // V5.2: [7.7.1] `branch` clauses 857 template <typename T, typename I, typename E> // 858 struct NotinbranchT { 859 using EmptyTrait = std::true_type; 860 }; 861 862 // V5.2: [7.6.1] `novariants` clause 863 template <typename T, typename I, typename E> // 864 struct NovariantsT { 865 using DoNotUseVariant = E; 866 using WrapperTrait = std::true_type; 867 DoNotUseVariant v; 868 }; 869 870 // V5.2: [15.6] `nowait` clause 871 template <typename T, typename I, typename E> // 872 struct NowaitT { 873 using EmptyTrait = std::true_type; 874 }; 875 876 // V5.2: [12.6.2] `num_tasks` clause 877 template <typename T, typename I, typename E> // 878 struct NumTasksT { 879 using NumTasks = E; 880 ENUM(Prescriptiveness, Strict); 881 using TupleTrait = std::true_type; 882 std::tuple<OPT(Prescriptiveness), NumTasks> t; 883 }; 884 885 // V5.2: [10.2.1] `num_teams` clause 886 template <typename T, typename I, typename E> // 887 struct NumTeamsT { 888 using TupleTrait = std::true_type; 889 using LowerBound = E; 890 using UpperBound = E; 891 std::tuple<OPT(LowerBound), UpperBound> t; 892 }; 893 894 // V5.2: [10.1.2] `num_threads` clause 895 template <typename T, typename I, typename E> // 896 struct NumThreadsT { 897 using Nthreads = E; 898 using WrapperTrait = std::true_type; 899 Nthreads v; 900 }; 901 902 template <typename T, typename I, typename E> // 903 struct OmpxAttributeT { 904 using EmptyTrait = std::true_type; 905 }; 906 907 template <typename T, typename I, typename E> // 908 struct OmpxBareT { 909 using EmptyTrait = std::true_type; 910 }; 911 912 template <typename T, typename I, typename E> // 913 struct OmpxDynCgroupMemT { 914 using WrapperTrait = std::true_type; 915 E v; 916 }; 917 918 // V5.2: [10.3] `order` clause 919 template <typename T, typename I, typename E> // 920 struct OrderT { 921 ENUM(OrderModifier, Reproducible, Unconstrained); 922 ENUM(Ordering, Concurrent); 923 using TupleTrait = std::true_type; 924 std::tuple<OPT(OrderModifier), Ordering> t; 925 }; 926 927 // V5.2: [4.4.4] `ordered` clause 928 template <typename T, typename I, typename E> // 929 struct OrderedT { 930 using N = E; 931 using WrapperTrait = std::true_type; 932 OPT(N) v; 933 }; 934 935 // V5.2: [7.4.2] `otherwise` clause 936 template <typename T, typename I, typename E> // 937 struct OtherwiseT { 938 using IncompleteTrait = std::true_type; 939 }; 940 941 // V5.2: [9.2.2] `partial` clause 942 template <typename T, typename I, typename E> // 943 struct PartialT { 944 using UnrollFactor = E; 945 using WrapperTrait = std::true_type; 946 OPT(UnrollFactor) v; 947 }; 948 949 // V5.2: [12.4] `priority` clause 950 template <typename T, typename I, typename E> // 951 struct PriorityT { 952 using PriorityValue = E; 953 using WrapperTrait = std::true_type; 954 PriorityValue v; 955 }; 956 957 // V5.2: [5.4.3] `private` clause 958 template <typename T, typename I, typename E> // 959 struct PrivateT { 960 using List = ObjectListT<I, E>; 961 using WrapperTrait = std::true_type; 962 List v; 963 }; 964 965 // V5.2: [10.1.4] `proc_bind` clause 966 template <typename T, typename I, typename E> // 967 struct ProcBindT { 968 ENUM(AffinityPolicy, Close, Master, Spread, Primary); 969 using WrapperTrait = std::true_type; 970 AffinityPolicy v; 971 }; 972 973 // V5.2: [15.8.2] Atomic clauses 974 template <typename T, typename I, typename E> // 975 struct ReadT { 976 using EmptyTrait = std::true_type; 977 }; 978 979 // V5.2: [5.5.8] `reduction` clause 980 template <typename T, typename I, typename E> // 981 struct ReductionT { 982 using List = ObjectListT<I, E>; 983 // See note at the definition of the ReductionIdentifierT type. 984 // The name ReductionIdentifiers is not a spec name. 985 using ReductionIdentifiers = ListT<type::ReductionIdentifierT<I, E>>; 986 ENUM(ReductionModifier, Default, Inscan, Task); 987 using TupleTrait = std::true_type; 988 std::tuple<OPT(ReductionModifier), ReductionIdentifiers, List> t; 989 }; 990 991 // V5.2: [15.8.1] `memory-order` clauses 992 template <typename T, typename I, typename E> // 993 struct RelaxedT { 994 using EmptyTrait = std::true_type; 995 }; 996 997 // V5.2: [15.8.1] `memory-order` clauses 998 template <typename T, typename I, typename E> // 999 struct ReleaseT { 1000 using EmptyTrait = std::true_type; 1001 }; 1002 1003 // V5.2: [8.2.1] `requirement` clauses 1004 template <typename T, typename I, typename E> // 1005 struct ReverseOffloadT { 1006 using EmptyTrait = std::true_type; 1007 }; 1008 1009 // V5.2: [10.4.2] `safelen` clause 1010 template <typename T, typename I, typename E> // 1011 struct SafelenT { 1012 using Length = E; 1013 using WrapperTrait = std::true_type; 1014 Length v; 1015 }; 1016 1017 // V5.2: [11.5.3] `schedule` clause 1018 template <typename T, typename I, typename E> // 1019 struct ScheduleT { 1020 ENUM(Kind, Static, Dynamic, Guided, Auto, Runtime); 1021 using ChunkSize = E; 1022 ENUM(OrderingModifier, Monotonic, Nonmonotonic); 1023 ENUM(ChunkModifier, Simd); 1024 using TupleTrait = std::true_type; 1025 std::tuple<Kind, OPT(OrderingModifier), OPT(ChunkModifier), OPT(ChunkSize)> t; 1026 }; 1027 1028 // V5.2: [15.8.1] Memory-order clauses 1029 template <typename T, typename I, typename E> // 1030 struct SeqCstT { 1031 using EmptyTrait = std::true_type; 1032 }; 1033 1034 // V5.2: [8.5.1] `severity` clause 1035 template <typename T, typename I, typename E> // 1036 struct SeverityT { 1037 ENUM(SevLevel, Fatal, Warning); 1038 using WrapperTrait = std::true_type; 1039 SevLevel v; 1040 }; 1041 1042 // V5.2: [5.4.2] `shared` clause 1043 template <typename T, typename I, typename E> // 1044 struct SharedT { 1045 using List = ObjectListT<I, E>; 1046 using WrapperTrait = std::true_type; 1047 List v; 1048 }; 1049 1050 // V5.2: [15.10.3] `parallelization-level` clauses 1051 template <typename T, typename I, typename E> // 1052 struct SimdT { 1053 using EmptyTrait = std::true_type; 1054 }; 1055 1056 // V5.2: [10.4.3] `simdlen` clause 1057 template <typename T, typename I, typename E> // 1058 struct SimdlenT { 1059 using Length = E; 1060 using WrapperTrait = std::true_type; 1061 Length v; 1062 }; 1063 1064 // V5.2: [9.1.1] `sizes` clause 1065 template <typename T, typename I, typename E> // 1066 struct SizesT { 1067 using SizeList = ListT<E>; 1068 using WrapperTrait = std::true_type; 1069 SizeList v; 1070 }; 1071 1072 // V5.2: [5.5.9] `task_reduction` clause 1073 template <typename T, typename I, typename E> // 1074 struct TaskReductionT { 1075 using List = ObjectListT<I, E>; 1076 // See note at the definition of the ReductionIdentifierT type. 1077 // The name ReductionIdentifiers is not a spec name. 1078 using ReductionIdentifiers = ListT<type::ReductionIdentifierT<I, E>>; 1079 using TupleTrait = std::true_type; 1080 std::tuple<ReductionIdentifiers, List> t; 1081 }; 1082 1083 // V5.2: [13.3] `thread_limit` clause 1084 template <typename T, typename I, typename E> // 1085 struct ThreadLimitT { 1086 using Threadlim = E; 1087 using WrapperTrait = std::true_type; 1088 Threadlim v; 1089 }; 1090 1091 // V5.2: [15.10.3] `parallelization-level` clauses 1092 template <typename T, typename I, typename E> // 1093 struct ThreadsT { 1094 using EmptyTrait = std::true_type; 1095 }; 1096 1097 // V5.2: [5.9.1] `to` clause 1098 template <typename T, typename I, typename E> // 1099 struct ToT { 1100 using LocatorList = ObjectListT<I, E>; 1101 using Expectation = type::MotionExpectation; 1102 // See note at the definition of the MapperT type. 1103 using Mappers = ListT<type::MapperT<I, E>>; // Not a spec name 1104 using Iterator = type::IteratorT<T, I, E>; 1105 1106 using TupleTrait = std::true_type; 1107 std::tuple<OPT(Expectation), OPT(Mappers), OPT(Iterator), LocatorList> t; 1108 }; 1109 1110 // V5.2: [8.2.1] `requirement` clauses 1111 template <typename T, typename I, typename E> // 1112 struct UnifiedAddressT { 1113 using EmptyTrait = std::true_type; 1114 }; 1115 1116 // V5.2: [8.2.1] `requirement` clauses 1117 template <typename T, typename I, typename E> // 1118 struct UnifiedSharedMemoryT { 1119 using EmptyTrait = std::true_type; 1120 }; 1121 1122 // V5.2: [5.10] `uniform` clause 1123 template <typename T, typename I, typename E> // 1124 struct UniformT { 1125 using ParameterList = ObjectListT<I, E>; 1126 using WrapperTrait = std::true_type; 1127 ParameterList v; 1128 }; 1129 1130 template <typename T, typename I, typename E> // 1131 struct UnknownT { 1132 using EmptyTrait = std::true_type; 1133 }; 1134 1135 // V5.2: [12.1] `untied` clause 1136 template <typename T, typename I, typename E> // 1137 struct UntiedT { 1138 using EmptyTrait = std::true_type; 1139 }; 1140 1141 // Both of the following 1142 // V5.2: [15.8.2] `atomic` clauses 1143 // V5.2: [15.9.3] `update` clause 1144 template <typename T, typename I, typename E> // 1145 struct UpdateT { 1146 using TaskDependenceType = tomp::type::TaskDependenceType; 1147 using WrapperTrait = std::true_type; 1148 OPT(TaskDependenceType) v; 1149 }; 1150 1151 // V5.2: [14.1.3] `use` clause 1152 template <typename T, typename I, typename E> // 1153 struct UseT { 1154 using InteropVar = ObjectT<I, E>; 1155 using WrapperTrait = std::true_type; 1156 InteropVar v; 1157 }; 1158 1159 // V5.2: [5.4.10] `use_device_addr` clause 1160 template <typename T, typename I, typename E> // 1161 struct UseDeviceAddrT { 1162 using List = ObjectListT<I, E>; 1163 using WrapperTrait = std::true_type; 1164 List v; 1165 }; 1166 1167 // V5.2: [5.4.8] `use_device_ptr` clause 1168 template <typename T, typename I, typename E> // 1169 struct UseDevicePtrT { 1170 using List = ObjectListT<I, E>; 1171 using WrapperTrait = std::true_type; 1172 List v; 1173 }; 1174 1175 // V5.2: [6.8] `uses_allocators` clause 1176 template <typename T, typename I, typename E> // 1177 struct UsesAllocatorsT { 1178 using MemSpace = E; 1179 using TraitsArray = ObjectT<I, E>; 1180 using Allocator = E; 1181 struct AllocatorSpec { // Not a spec name 1182 using TupleTrait = std::true_type; 1183 std::tuple<OPT(MemSpace), OPT(TraitsArray), Allocator> t; 1184 }; 1185 using Allocators = ListT<AllocatorSpec>; // Not a spec name 1186 using WrapperTrait = std::true_type; 1187 Allocators v; 1188 }; 1189 1190 // V5.2: [15.8.3] `extended-atomic` clauses 1191 template <typename T, typename I, typename E> // 1192 struct WeakT { 1193 using EmptyTrait = std::true_type; 1194 }; 1195 1196 // V5.2: [7.4.1] `when` clause 1197 template <typename T, typename I, typename E> // 1198 struct WhenT { 1199 using IncompleteTrait = std::true_type; 1200 }; 1201 1202 // V5.2: [15.8.2] Atomic clauses 1203 template <typename T, typename I, typename E> // 1204 struct WriteT { 1205 using EmptyTrait = std::true_type; 1206 }; 1207 1208 // --- 1209 1210 template <typename T, typename I, typename E> 1211 using ExtensionClausesT = 1212 std::variant<OmpxAttributeT<T, I, E>, OmpxBareT<T, I, E>, 1213 OmpxDynCgroupMemT<T, I, E>>; 1214 1215 template <typename T, typename I, typename E> 1216 using EmptyClausesT = std::variant< 1217 AcqRelT<T, I, E>, AcquireT<T, I, E>, CaptureT<T, I, E>, CompareT<T, I, E>, 1218 DynamicAllocatorsT<T, I, E>, FullT<T, I, E>, InbranchT<T, I, E>, 1219 MergeableT<T, I, E>, NogroupT<T, I, E>, NoOpenmpRoutinesT<T, I, E>, 1220 NoOpenmpT<T, I, E>, NoParallelismT<T, I, E>, NotinbranchT<T, I, E>, 1221 NowaitT<T, I, E>, ReadT<T, I, E>, RelaxedT<T, I, E>, ReleaseT<T, I, E>, 1222 ReverseOffloadT<T, I, E>, SeqCstT<T, I, E>, SimdT<T, I, E>, 1223 ThreadsT<T, I, E>, UnifiedAddressT<T, I, E>, UnifiedSharedMemoryT<T, I, E>, 1224 UnknownT<T, I, E>, UntiedT<T, I, E>, UseT<T, I, E>, WeakT<T, I, E>, 1225 WriteT<T, I, E>>; 1226 1227 template <typename T, typename I, typename E> 1228 using IncompleteClausesT = 1229 std::variant<AdjustArgsT<T, I, E>, AppendArgsT<T, I, E>, MatchT<T, I, E>, 1230 OtherwiseT<T, I, E>, WhenT<T, I, E>>; 1231 1232 template <typename T, typename I, typename E> 1233 using TupleClausesT = 1234 std::variant<AffinityT<T, I, E>, AlignedT<T, I, E>, AllocateT<T, I, E>, 1235 DefaultmapT<T, I, E>, DeviceT<T, I, E>, DistScheduleT<T, I, E>, 1236 DoacrossT<T, I, E>, FromT<T, I, E>, GrainsizeT<T, I, E>, 1237 IfT<T, I, E>, InitT<T, I, E>, InReductionT<T, I, E>, 1238 LastprivateT<T, I, E>, LinearT<T, I, E>, MapT<T, I, E>, 1239 NumTasksT<T, I, E>, OrderT<T, I, E>, ReductionT<T, I, E>, 1240 ScheduleT<T, I, E>, TaskReductionT<T, I, E>, ToT<T, I, E>>; 1241 1242 template <typename T, typename I, typename E> 1243 using UnionClausesT = std::variant<DependT<T, I, E>>; 1244 1245 template <typename T, typename I, typename E> 1246 using WrapperClausesT = std::variant< 1247 AbsentT<T, I, E>, AlignT<T, I, E>, AllocatorT<T, I, E>, 1248 AtomicDefaultMemOrderT<T, I, E>, AtT<T, I, E>, BindT<T, I, E>, 1249 CollapseT<T, I, E>, ContainsT<T, I, E>, CopyinT<T, I, E>, 1250 CopyprivateT<T, I, E>, DefaultT<T, I, E>, DestroyT<T, I, E>, 1251 DetachT<T, I, E>, DeviceTypeT<T, I, E>, EnterT<T, I, E>, 1252 ExclusiveT<T, I, E>, FailT<T, I, E>, FilterT<T, I, E>, FinalT<T, I, E>, 1253 FirstprivateT<T, I, E>, HasDeviceAddrT<T, I, E>, HintT<T, I, E>, 1254 HoldsT<T, I, E>, InclusiveT<T, I, E>, IndirectT<T, I, E>, 1255 InitializerT<T, I, E>, IsDevicePtrT<T, I, E>, LinkT<T, I, E>, 1256 MessageT<T, I, E>, NocontextT<T, I, E>, NontemporalT<T, I, E>, 1257 NovariantsT<T, I, E>, NumTeamsT<T, I, E>, NumThreadsT<T, I, E>, 1258 OrderedT<T, I, E>, PartialT<T, I, E>, PriorityT<T, I, E>, PrivateT<T, I, E>, 1259 ProcBindT<T, I, E>, SafelenT<T, I, E>, SeverityT<T, I, E>, SharedT<T, I, E>, 1260 SimdlenT<T, I, E>, SizesT<T, I, E>, ThreadLimitT<T, I, E>, 1261 UniformT<T, I, E>, UpdateT<T, I, E>, UseDeviceAddrT<T, I, E>, 1262 UseDevicePtrT<T, I, E>, UsesAllocatorsT<T, I, E>>; 1263 1264 template <typename T, typename I, typename E> 1265 using UnionOfAllClausesT = typename type::Union< // 1266 EmptyClausesT<T, I, E>, // 1267 ExtensionClausesT<T, I, E>, // 1268 IncompleteClausesT<T, I, E>, // 1269 TupleClausesT<T, I, E>, // 1270 UnionClausesT<T, I, E>, // 1271 WrapperClausesT<T, I, E> // 1272 >::type; 1273 } // namespace clause 1274 1275 using type::operator==; 1276 1277 // The variant wrapper that encapsulates all possible specific clauses. 1278 // The `Extras` arguments are additional types representing local extensions 1279 // to the clause set, e.g. 1280 // 1281 // using Clause = ClauseT<Type, Id, Expr, 1282 // MyClause1, MyClause2>; 1283 // 1284 // The member Clause::u will be a variant containing all specific clauses 1285 // defined above, plus MyClause1 and MyClause2. 1286 // 1287 // Note: Any derived class must be constructible from the base class 1288 // ClauseT<...>. 1289 template <typename TypeType, typename IdType, typename ExprType, 1290 typename... Extras> 1291 struct ClauseT { 1292 using TypeTy = TypeType; 1293 using IdTy = IdType; 1294 using ExprTy = ExprType; 1295 1296 // Type of "self" to specify this type given a derived class type. 1297 using BaseT = ClauseT<TypeType, IdType, ExprType, Extras...>; 1298 1299 using VariantTy = typename type::Union< 1300 clause::UnionOfAllClausesT<TypeType, IdType, ExprType>, 1301 std::variant<Extras...>>::type; 1302 1303 llvm::omp::Clause id; // The numeric id of the clause 1304 using UnionTrait = std::true_type; 1305 VariantTy u; 1306 }; 1307 1308 template <typename ClauseType> struct DirectiveWithClauses { 1309 llvm::omp::Directive id = llvm::omp::Directive::OMPD_unknown; 1310 tomp::type::ListT<ClauseType> clauses; 1311 }; 1312 1313 } // namespace tomp 1314 1315 #undef OPT 1316 #undef ENUM 1317 1318 #endif // LLVM_FRONTEND_OPENMP_CLAUSET_H 1319