//===- VariantTraits.h - Common interfaces for variant-like types --C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains common interfaces for variant-like types. // //===----------------------------------------------------------------------===// #include "llvm/ADT/STLExtras.h" #ifndef LLVM_ADT_VARIANTTRAITS_H #define LLVM_ADT_VARIANTTRAITS_H namespace llvm { /// Trait type which can be specialized over std::variant-like types to provide /// the minimum interface needed to share the implementation of llvm::visit and /// llvm::visitSameAlternative. template struct VariantTraits { // // Returns the number of alternative types of VariantT. // static constexpr size_t size(); // // // Returns the index of the current alternative type of Variant. // static constexpr size_t index(const VariantT &Variant); // // // Gets the alternative type at Index. // template // static constexpr decltype(auto) get(VariantT &&Variant); }; namespace variant_traits_detail { template using Traits = struct VariantTraits>; template struct HasTraits { using Absent = char; using Present = long; template static Absent size(...); template static Present size(SameType *); template static Absent index(...); template static Present index(SameType &), &U::index> *); template static Absent get(...); template static Present get(SameType &&), &U::get> *); static bool const value = // NOLINT(readability-identifier-naming) sizeof(size>(nullptr)) == sizeof(Present) && sizeof(index>(nullptr)) == sizeof(Present) && sizeof(get>(nullptr) == sizeof(Present)); }; template struct AreSame : conjunction...> {}; // FIXME: Peeling off the first ThunkT in this definition is only necessary to // work around an MSVC compiler issue, where it complains that std::is_same is // not provided enough template arguments. Verify what version of MSVC no // longer requires this workaround so this can be simplified. template {}, int> = 0> static constexpr auto makeThunkArray(HeadThunkT &&HeadThunk, TailThunkTs &&...TailThunks) { return make_array(std::forward(HeadThunk), std::forward(TailThunks)...); } template static constexpr decltype(auto) thunkForSameAlternative(VisitorT &&Visitor, VariantTs &&...Variants) { return std::forward(Visitor)(Traits::template get( std::forward(Variants))...); } template static constexpr auto makeThunkForSameAlternative() { return thunkForSameAlternative; } template static constexpr auto visitSameAlternativeImpl(size_t Index, std::index_sequence, VisitorT &&Visitor, HeadVariantT &&HeadVariant, TailVariantTs &&...TailVariants) { constexpr auto Thunks = makeThunkArray( makeThunkForSameAlternative()...); return Thunks[Index](std::forward(Visitor), std::forward(HeadVariant), std::forward(TailVariants)...); } template struct Thunk { template inline static constexpr decltype(auto) thunk(VisitorT &&Visitor, VariantTs &&...Variants) { return std::forward(Visitor)( Traits::template get( std::forward(Variants))...); } template inline static constexpr R thunkR(VisitorT &&Visitor, VariantTs &&...Variants) { return std::forward(Visitor)( Traits::template get( std::forward(Variants))...); } }; template static constexpr auto makeThunkForSequence(std::index_sequence) { return Thunk::template thunk; } template static constexpr auto makeThunkForSequenceR(std::index_sequence) { return Thunk::template thunkR; } template static constexpr auto accumulateCartesianProductThunks(std::index_sequence) { return makeThunkForSequence( std::index_sequence{}); } template static constexpr auto accumulateCartesianProductThunksR(std::index_sequence) { return makeThunkForSequenceR( std::index_sequence{}); } template static constexpr auto accumulateCartesianProductThunks(std::index_sequence, std::index_sequence, TailSequenceTs... Tail) { return makeThunkArray( accumulateCartesianProductThunks( std::index_sequence{}, Tail...)...); } template static constexpr auto accumulateCartesianProductThunksR(std::index_sequence, std::index_sequence, TailSequenceTs... Tail) { return makeThunkArray( accumulateCartesianProductThunksR( std::index_sequence{}, Tail...)...); } template static constexpr auto makeThunkMatrix() { return accumulateCartesianProductThunks( std::index_sequence<>{}, std::make_index_sequence::size()>{}...); } template static constexpr auto makeThunkMatrixR() { return accumulateCartesianProductThunksR( std::index_sequence<>{}, std::make_index_sequence::size()>{}...); } template static constexpr const ThunkT &indexThunkMatrix(const ThunkT &Thunk) { return Thunk; } template static constexpr auto &&indexThunkMatrix(const ThunkMatrixT &ThunkMatrix, size_t HeadIndex, TailIndexTs... TailIndexes) { return indexThunkMatrix(ThunkMatrix[HeadIndex], TailIndexes...); } } // namespace variant_traits_detail /// Invokes the provided Visitor using overload resolution based on the /// dynamic alternative type held in each Variant. See std::variant. /// /// The return type is effectively /// decltype(Visitor(Variants.get()...)). This must be a /// valid expression of the same type and value category for every combination /// of alternative types of the variant types. template < typename VisitorT, typename... VariantTs, typename std::enable_if_t< conjunction...>::value, int> = 0> constexpr decltype(auto) visit(VisitorT &&Visitor, VariantTs &&...Variants) { constexpr auto ThunkMatrix = variant_traits_detail::makeThunkMatrix(); const auto &Thunk = variant_traits_detail::indexThunkMatrix( ThunkMatrix, variant_traits_detail::Traits::index( std::forward(Variants))...); return Thunk(std::forward(Visitor), std::forward(Variants)...); } /// Invokes the provided Visitor using overload resolution based on the /// dynamic alternative type held in each Variant. See std::variant. /// /// The return type is effectively /// decltype(Visitor(Variants.get()...)), implicity converted /// to R. template < typename R, typename VisitorT, typename... VariantTs, typename std::enable_if_t< conjunction...>::value, int> = 0> constexpr R visit(VisitorT &&Visitor, VariantTs &&...Variants) { constexpr auto ThunkMatrix = variant_traits_detail::makeThunkMatrixR(); const auto &Thunk = variant_traits_detail::indexThunkMatrix( ThunkMatrix, variant_traits_detail::Traits::index( std::forward(Variants))...); return Thunk(std::forward(Visitor), std::forward(Variants)...); } /// Invokes the provided Visitor using overload resolution based on the dynamic /// alternative type held in each Variant, assuming the variants are all of the /// same type and hold the same dynamic alternative type. /// /// \warning llvm::visit must be used instead when there is no guarantee that /// all variants currently hold the same alternative type. However, when such a /// guarantee can be made llvm::visitSameAlternative may reduce code bloat, /// especially for debug builds. /// /// The return type is effectively /// decltype(Visitor(Variants.get()...)). This must be a valid /// expression of the same type and value category for every alternative type /// of the variant type. template < typename VisitorT, typename HeadVariantT, typename... TailVariantTs, typename std::enable_if_t< conjunction, variant_traits_detail::HasTraits...>::value, int> = 0> static constexpr decltype(auto) visitSameAlternative(VisitorT &&Visitor, HeadVariantT &&HeadVariant, TailVariantTs &&...TailVariants) { static_assert( conjunction, remove_cvref_t>...>::value, "all variant arguments to visitSameAlternative must " "be of the same type"); using Traits = variant_traits_detail::Traits; #ifdef EXPENSIVE_CHECKS size_t Index = Traits::index(std::forward(HeadVariant)); for (auto &&V : {std::forward(TailVariants)...}) assert(Traits::index(V) == Index && "all variant arguments to visitSameAlternative must have " "the same index"); #endif return variant_traits_detail::visitSameAlternativeImpl( Traits::index(std::forward(HeadVariant)), std::make_index_sequence{}, std::forward(Visitor), std::forward(HeadVariant), std::forward(TailVariants)...); } } // namespace llvm #endif // LLVM_ADT_VARIANTTRAITS_H