[PATCH] D121047: Add missing template keywords

Christoph GrĂ¼ninger via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 21 15:05:05 PDT 2022


gruenich added a comment.

By full context, you mean the whole file with the diff included? Or something else?

  //===- llvm/ADT/PointerSumType.h --------------------------------*- 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
  //
  //===----------------------------------------------------------------------===//
  
  #ifndef LLVM_ADT_POINTERSUMTYPE_H
  #define LLVM_ADT_POINTERSUMTYPE_H
  
  #include "llvm/ADT/bit.h"
  #include "llvm/ADT/DenseMapInfo.h"
  #include "llvm/Support/PointerLikeTypeTraits.h"
  #include <cassert>
  #include <cstdint>
  #include <type_traits>
  
  namespace llvm {
  
  /// A compile time pair of an integer tag and the pointer-like type which it
  /// indexes within a sum type. Also allows the user to specify a particular
  /// traits class for pointer types with custom behavior such as over-aligned
  /// allocation.
  template <uintptr_t N, typename PointerArgT,
            typename TraitsArgT = PointerLikeTypeTraits<PointerArgT>>
  struct PointerSumTypeMember {
    enum { Tag = N };
    using PointerT = PointerArgT;
    using TraitsT = TraitsArgT;
  };
  
  namespace detail {
  
  template <typename TagT, typename... MemberTs> struct PointerSumTypeHelper;
  
  } // end namespace detail
  
  /// A sum type over pointer-like types.
  ///
  /// This is a normal tagged union across pointer-like types that uses the low
  /// bits of the pointers to store the tag.
  ///
  /// Each member of the sum type is specified by passing a \c
  /// PointerSumTypeMember specialization in the variadic member argument list.
  /// This allows the user to control the particular tag value associated with
  /// a particular type, use the same type for multiple different tags, and
  /// customize the pointer-like traits used for a particular member. Note that
  /// these *must* be specializations of \c PointerSumTypeMember, no other type
  /// will suffice, even if it provides a compatible interface.
  ///
  /// This type implements all of the comparison operators and even hash table
  /// support by comparing the underlying storage of the pointer values. It
  /// doesn't support delegating to particular members for comparisons.
  ///
  /// It also default constructs to a zero tag with a null pointer, whatever that
  /// would be. This means that the zero value for the tag type is significant
  /// and may be desirable to set to a state that is particularly desirable to
  /// default construct.
  ///
  /// Having a supported zero-valued tag also enables getting the address of a
  /// pointer stored with that tag provided it is stored in its natural bit
  /// representation. This works because in the case of a zero-valued tag, the
  /// pointer's value is directly stored into this object and we can expose the
  /// address of that internal storage. This is especially useful when building an
  /// `ArrayRef` of a single pointer stored in a sum type.
  ///
  /// There is no support for constructing or accessing with a dynamic tag as
  /// that would fundamentally violate the type safety provided by the sum type.
  template <typename TagT, typename... MemberTs> class PointerSumType {
    using HelperT = detail::PointerSumTypeHelper<TagT, MemberTs...>;
  
    // We keep both the raw value and the min tag value's pointer in a union. When
    // the minimum tag value is zero, this allows code below to cleanly expose the
    // address of the zero-tag pointer instead of just the zero-tag pointer
    // itself. This is especially useful when building `ArrayRef`s out of a single
    // pointer. However, we have to carefully access the union due to the active
    // member potentially changing. When we *store* a new value, we directly
    // access the union to allow us to store using the obvious types. However,
    // when we *read* a value, we copy the underlying storage out to avoid relying
    // on one member or the other being active.
    union StorageT {
      // Ensure we get a null default constructed value. We don't use a member
      // initializer because some compilers seem to not implement those correctly
      // for a union.
      StorageT() : Value(0) {}
  
      uintptr_t Value;
  
      typename HelperT::template Lookup<HelperT::MinTag>::PointerT MinTagPointer;
    };
  
    StorageT Storage;
  
  public:
    constexpr PointerSumType() = default;
  
    /// A typed setter to a given tagged member of the sum type.
    template <TagT N>
    void set(typename HelperT::template Lookup<N>::PointerT Pointer) {
      void *V = HelperT::template Lookup<N>::TraitsT::getAsVoidPointer(Pointer);
      assert((reinterpret_cast<uintptr_t>(V) & HelperT::TagMask) == 0 &&
             "Pointer is insufficiently aligned to store the discriminant!");
      Storage.Value = reinterpret_cast<uintptr_t>(V) | N;
    }
  
    /// A typed constructor for a specific tagged member of the sum type.
    template <TagT N>
    static PointerSumType
    create(typename HelperT::template Lookup<N>::PointerT Pointer) {
      PointerSumType Result;
      Result.set<N>(Pointer);
      return Result;
    }
  
    /// Clear the value to null with the min tag type.
    void clear() { set<HelperT::MinTag>(nullptr); }
  
    TagT getTag() const {
      return static_cast<TagT>(getOpaqueValue() & HelperT::TagMask);
    }
  
    template <TagT N> bool is() const { return N == getTag(); }
  
    template <TagT N> typename HelperT::template Lookup<N>::PointerT get() const {
      void *P = is<N>() ? getVoidPtr() : nullptr;
      return HelperT::template Lookup<N>::TraitsT::getFromVoidPointer(P);
    }
  
    template <TagT N>
    typename HelperT::template Lookup<N>::PointerT cast() const {
      assert(is<N>() && "This instance has a different active member.");
      return HelperT::template Lookup<N>::TraitsT::getFromVoidPointer(
          getVoidPtr());
    }
  
    /// If the tag is zero and the pointer's value isn't changed when being
    /// stored, get the address of the stored value type-punned to the zero-tag's
    /// pointer type.
    typename HelperT::template Lookup<HelperT::MinTag>::PointerT const *
    getAddrOfZeroTagPointer() const {
      return const_cast<PointerSumType *>(this)->getAddrOfZeroTagPointer();
    }
  
    /// If the tag is zero and the pointer's value isn't changed when being
    /// stored, get the address of the stored value type-punned to the zero-tag's
    /// pointer type.
    typename HelperT::template Lookup<HelperT::MinTag>::PointerT *
    getAddrOfZeroTagPointer() {
      static_assert(HelperT::MinTag == 0, "Non-zero minimum tag value!");
      assert(is<HelperT::MinTag>() && "The active tag is not zero!");
      // Store the initial value of the pointer when read out of our storage.
      auto InitialPtr = get<HelperT::MinTag>();
      // Now update the active member of the union to be the actual pointer-typed
      // member so that accessing it indirectly through the returned address is
      // valid.
      Storage.MinTagPointer = InitialPtr;
      // Finally, validate that this was a no-op as expected by reading it back
      // out using the same underlying-storage read as above.
      assert(InitialPtr == get<HelperT::MinTag>() &&
             "Switching to typed storage changed the pointer returned!");
      // Now we can correctly return an address to typed storage.
      return &Storage.MinTagPointer;
    }
  
    explicit operator bool() const {
      return getOpaqueValue() & HelperT::PointerMask;
    }
    bool operator==(const PointerSumType &R) const {
      return getOpaqueValue() == R.getOpaqueValue();
    }
    bool operator!=(const PointerSumType &R) const {
      return getOpaqueValue() != R.getOpaqueValue();
    }
    bool operator<(const PointerSumType &R) const {
      return getOpaqueValue() < R.getOpaqueValue();
    }
    bool operator>(const PointerSumType &R) const {
      return getOpaqueValue() > R.getOpaqueValue();
    }
    bool operator<=(const PointerSumType &R) const {
      return getOpaqueValue() <= R.getOpaqueValue();
    }
    bool operator>=(const PointerSumType &R) const {
      return getOpaqueValue() >= R.getOpaqueValue();
    }
  
    uintptr_t getOpaqueValue() const {
      // Read the underlying storage of the union, regardless of the active
      // member.
      return bit_cast<uintptr_t>(Storage);
    }
  
  protected:
    void *getVoidPtr() const {
      return reinterpret_cast<void *>(getOpaqueValue() & HelperT::PointerMask);
    }
  };
  
  namespace detail {
  
  /// A helper template for implementing \c PointerSumType. It provides fast
  /// compile-time lookup of the member from a particular tag value, along with
  /// useful constants and compile time checking infrastructure..
  template <typename TagT, typename... MemberTs>
  struct PointerSumTypeHelper : MemberTs... {
    // First we use a trick to allow quickly looking up information about
    // a particular member of the sum type. This works because we arranged to
    // have this type derive from all of the member type templates. We can select
    // the matching member for a tag using type deduction during overload
    // resolution.
    template <TagT N, typename PointerT, typename TraitsT>
    static PointerSumTypeMember<N, PointerT, TraitsT>
    LookupOverload(PointerSumTypeMember<N, PointerT, TraitsT> *);
    template <TagT N> static void LookupOverload(...);
    template <TagT N> struct Lookup {
      // Compute a particular member type by resolving the lookup helper overload.
      using MemberT = decltype(
          LookupOverload<N>(static_cast<PointerSumTypeHelper *>(nullptr)));
  
      /// The Nth member's pointer type.
      using PointerT = typename MemberT::PointerT;
  
      /// The Nth member's traits type.
      using TraitsT = typename MemberT::TraitsT;
    };
  
    // Next we need to compute the number of bits available for the discriminant
    // by taking the min of the bits available for each member. Much of this
    // would be amazingly easier with good constexpr support.
    template <uintptr_t V, uintptr_t... Vs>
    struct Min : std::integral_constant<
                     uintptr_t, (V < Min<Vs...>::value ? V : Min<Vs...>::value)> {
    };
    template <uintptr_t V>
    struct Min<V> : std::integral_constant<uintptr_t, V> {};
    enum { NumTagBits = Min<MemberTs::TraitsT::NumLowBitsAvailable...>::value };
  
    // Also compute the smallest discriminant and various masks for convenience.
    constexpr static TagT MinTag =
        static_cast<TagT>(Min<MemberTs::Tag...>::value);
    enum : uint64_t {
      PointerMask = static_cast<uint64_t>(-1) << NumTagBits,
      TagMask = ~PointerMask
    };
  
    // Finally we need a recursive template to do static checks of each
    // member.
    template <typename MemberT, typename... InnerMemberTs>
    struct Checker : Checker<InnerMemberTs...> {
      static_assert(MemberT::Tag < (1 << NumTagBits),
                    "This discriminant value requires too many bits!");
    };
    template <typename MemberT> struct Checker<MemberT> : std::true_type {
      static_assert(MemberT::Tag < (1 << NumTagBits),
                    "This discriminant value requires too many bits!");
    };
    static_assert(Checker<MemberTs...>::value,
                  "Each member must pass the checker.");
  };
  
  } // end namespace detail
  
  // Teach DenseMap how to use PointerSumTypes as keys.
  template <typename TagT, typename... MemberTs>
  struct DenseMapInfo<PointerSumType<TagT, MemberTs...>> {
    using SumType = PointerSumType<TagT, MemberTs...>;
    using HelperT = detail::PointerSumTypeHelper<TagT, MemberTs...>;
    enum { SomeTag = HelperT::MinTag };
    using SomePointerT =
        typename HelperT::template Lookup<HelperT::MinTag>::PointerT;
    using SomePointerInfo = DenseMapInfo<SomePointerT>;
  
    static inline SumType getEmptyKey() {
  -    return SumType::create<SomeTag>(SomePointerInfo::getEmptyKey());
  +    return SumType::template create<SomeTag>(SomePointerInfo::getEmptyKey());
     }
   
     static inline SumType getTombstoneKey() {
  -    return SumType::create<SomeTag>(SomePointerInfo::getTombstoneKey());
  +    return SumType::template create<SomeTag>(
  +        SomePointerInfo::getTombstoneKey());
    }
  
    static unsigned getHashValue(const SumType &Arg) {
      uintptr_t OpaqueValue = Arg.getOpaqueValue();
      return DenseMapInfo<uintptr_t>::getHashValue(OpaqueValue);
    }
  
    static bool isEqual(const SumType &LHS, const SumType &RHS) {
      return LHS == RHS;
    }
  };
  
  } // end namespace llvm
  
  #endif // LLVM_ADT_POINTERSUMTYPE_H


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D121047/new/

https://reviews.llvm.org/D121047



More information about the llvm-commits mailing list