Simplify traits, add CastableCommonBase & IsCastable

Use `static constexpr bool` instead of `std::integral_constant` for `IsTypeOrDerived`. This is often cleaner to use.

Add `IsCastable`, a helper for determining if all the template types derive from `CastableBase`.

Add `CastableCommonBase`, some template magic for determinine the most derived, common base type for all castable types.

Change-Id: Ia3d33548424750f8260f518ecd63d39949e4a826
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/81105
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
Ben Clayton 2022-02-18 22:05:53 +00:00 committed by Tint LUCI CQ
parent 911944cc8d
commit 473b6087ac
5 changed files with 158 additions and 46 deletions

View File

@ -48,12 +48,23 @@ namespace tint {
// Forward declaration // Forward declaration
class CastableBase; class CastableBase;
/// Ignore is used as a special type used for skipping over types for trait
/// helper functions.
class Ignore {};
namespace detail { namespace detail {
template <typename T> template <typename T>
struct TypeInfoOf; struct TypeInfoOf;
} // namespace detail } // namespace detail
/// True if all template types that are not Ignore derive from CastableBase
template <typename... TYPES>
static constexpr bool IsCastable =
((traits::IsTypeOrDerived<TYPES, CastableBase> ||
std::is_same_v<TYPES, Ignore>)&&...) &&
!(std::is_same_v<TYPES, Ignore> && ...);
/// Helper macro to instantiate the TypeInfo<T> template for `CLASS`. /// Helper macro to instantiate the TypeInfo<T> template for `CLASS`.
#define TINT_INSTANTIATE_TYPEINFO(CLASS) \ #define TINT_INSTANTIATE_TYPEINFO(CLASS) \
TINT_CASTABLE_PUSH_DISABLE_WARNINGS(); \ TINT_CASTABLE_PUSH_DISABLE_WARNINGS(); \
@ -145,8 +156,7 @@ struct TypeInfo {
/// multiple hashcodes are bitwise-or'd together. /// multiple hashcodes are bitwise-or'd together.
template <typename T> template <typename T>
static constexpr HashCode HashCodeOf() { static constexpr HashCode HashCodeOf() {
static_assert(traits::IsTypeOrDerived<T, CastableBase>::value, static_assert(IsCastable<T>, "T is not Castable");
"T is not Castable");
static_assert( static_assert(
std::is_same_v<T, std::remove_cv_t<T>>, std::is_same_v<T, std::remove_cv_t<T>>,
"Strip const / volatile decorations before calling HashCodeOf"); "Strip const / volatile decorations before calling HashCodeOf");
@ -455,6 +465,68 @@ class Castable : public BASE {
} }
}; };
namespace detail {
/// <code>typename CastableCommonBaseImpl<TYPES>::type</code> resolves to the
/// common base class for all of TYPES.
template <typename... TYPES>
struct CastableCommonBaseImpl {};
/// Alias to typename CastableCommonBaseImpl<TYPES>::type
template <typename... TYPES>
using CastableCommonBase =
typename detail::CastableCommonBaseImpl<TYPES...>::type;
/// CastableCommonBaseImpl template specialization for a single type
template <typename T>
struct CastableCommonBaseImpl<T> {
/// Common base class of a single type is itself
using type = T;
};
/// CastableCommonBaseImpl A <-> CastableBase specialization
template <typename A>
struct CastableCommonBaseImpl<A, CastableBase> {
/// Common base class for A and CastableBase is CastableBase
using type = CastableBase;
};
/// CastableCommonBaseImpl T <-> Ignore specialization
template <typename T>
struct CastableCommonBaseImpl<T, Ignore> {
/// Resolves to T as the other type is ignored
using type = T;
};
/// CastableCommonBaseImpl Ignore <-> T specialization
template <typename T>
struct CastableCommonBaseImpl<Ignore, T> {
/// Resolves to T as the other type is ignored
using type = T;
};
/// CastableCommonBaseImpl A <-> B specialization
template <typename A, typename B>
struct CastableCommonBaseImpl<A, B> {
/// The common base class for A, B and OTHERS
using type = std::conditional_t<traits::IsTypeOrDerived<A, B>,
B, // A derives from B
CastableCommonBase<A, typename B::TrueBase>>;
};
/// CastableCommonBaseImpl 3+ types specialization
template <typename A, typename B, typename... OTHERS>
struct CastableCommonBaseImpl<A, B, OTHERS...> {
/// The common base class for A, B and OTHERS
using type = CastableCommonBase<CastableCommonBase<A, B>, OTHERS...>;
};
} // namespace detail
/// Resolves to the common most derived type that each of the types in `TYPES`
/// derives from.
template <typename... TYPES>
using CastableCommonBase = detail::CastableCommonBase<TYPES...>;
/// Default can be used as the default case for a Switch(), when all previous /// Default can be used as the default case for a Switch(), when all previous
/// cases failed to match. /// cases failed to match.
/// ///

View File

@ -22,34 +22,15 @@
namespace tint { namespace tint {
namespace { namespace {
struct Animal : public tint::Castable<Animal> { struct Animal : public tint::Castable<Animal> {};
explicit Animal(std::string n) : name(n) {} struct Amphibian : public tint::Castable<Amphibian, Animal> {};
const std::string name; struct Mammal : public tint::Castable<Mammal, Animal> {};
}; struct Reptile : public tint::Castable<Reptile, Animal> {};
struct Frog : public tint::Castable<Frog, Amphibian> {};
struct Amphibian : public tint::Castable<Amphibian, Animal> { struct Bear : public tint::Castable<Bear, Mammal> {};
explicit Amphibian(std::string n) : Base(n) {} struct Lizard : public tint::Castable<Lizard, Reptile> {};
}; struct Gecko : public tint::Castable<Gecko, Lizard> {};
struct Iguana : public tint::Castable<Iguana, Lizard> {};
struct Mammal : public tint::Castable<Mammal, Animal> {
explicit Mammal(std::string n) : Base(n) {}
};
struct Reptile : public tint::Castable<Reptile, Animal> {
explicit Reptile(std::string n) : Base(n) {}
};
struct Frog : public tint::Castable<Frog, Amphibian> {
Frog() : Base("Frog") {}
};
struct Bear : public tint::Castable<Bear, Mammal> {
Bear() : Base("Bear") {}
};
struct Gecko : public tint::Castable<Gecko, Reptile> {
Gecko() : Base("Gecko") {}
};
TEST(CastableBase, Is) { TEST(CastableBase, Is) {
std::unique_ptr<CastableBase> frog = std::make_unique<Frog>(); std::unique_ptr<CastableBase> frog = std::make_unique<Frog>();
@ -417,6 +398,65 @@ TEST(Castable, SwitchNullNoDefault) {
EXPECT_TRUE(default_called); EXPECT_TRUE(default_called);
} }
// IsCastable static tests
static_assert(IsCastable<CastableBase>);
static_assert(IsCastable<Animal>);
static_assert(IsCastable<Ignore, Frog, Bear>);
static_assert(IsCastable<Mammal, Ignore, Amphibian, Gecko>);
static_assert(!IsCastable<Mammal, int, Amphibian, Ignore, Gecko>);
static_assert(!IsCastable<bool>);
static_assert(!IsCastable<int, float>);
static_assert(!IsCastable<Ignore>);
// CastableCommonBase static tests
static_assert(std::is_same_v<Animal, CastableCommonBase<Animal>>);
static_assert(std::is_same_v<Amphibian, CastableCommonBase<Amphibian>>);
static_assert(std::is_same_v<Mammal, CastableCommonBase<Mammal>>);
static_assert(std::is_same_v<Reptile, CastableCommonBase<Reptile>>);
static_assert(std::is_same_v<Frog, CastableCommonBase<Frog>>);
static_assert(std::is_same_v<Bear, CastableCommonBase<Bear>>);
static_assert(std::is_same_v<Lizard, CastableCommonBase<Lizard>>);
static_assert(std::is_same_v<Gecko, CastableCommonBase<Gecko>>);
static_assert(std::is_same_v<Iguana, CastableCommonBase<Iguana>>);
static_assert(std::is_same_v<Animal, CastableCommonBase<Animal, Animal>>);
static_assert(
std::is_same_v<Amphibian, CastableCommonBase<Amphibian, Amphibian>>);
static_assert(std::is_same_v<Mammal, CastableCommonBase<Mammal, Mammal>>);
static_assert(std::is_same_v<Reptile, CastableCommonBase<Reptile, Reptile>>);
static_assert(std::is_same_v<Frog, CastableCommonBase<Frog, Frog>>);
static_assert(std::is_same_v<Bear, CastableCommonBase<Bear, Bear>>);
static_assert(std::is_same_v<Lizard, CastableCommonBase<Lizard, Lizard>>);
static_assert(std::is_same_v<Gecko, CastableCommonBase<Gecko, Gecko>>);
static_assert(std::is_same_v<Iguana, CastableCommonBase<Iguana, Iguana>>);
static_assert(
std::is_same_v<CastableBase, CastableCommonBase<CastableBase, Animal>>);
static_assert(
std::is_same_v<CastableBase, CastableCommonBase<Animal, CastableBase>>);
static_assert(std::is_same_v<Amphibian, CastableCommonBase<Amphibian, Frog>>);
static_assert(std::is_same_v<Amphibian, CastableCommonBase<Frog, Amphibian>>);
static_assert(std::is_same_v<Animal, CastableCommonBase<Reptile, Frog>>);
static_assert(std::is_same_v<Animal, CastableCommonBase<Frog, Reptile>>);
static_assert(std::is_same_v<Animal, CastableCommonBase<Bear, Frog>>);
static_assert(std::is_same_v<Animal, CastableCommonBase<Frog, Bear>>);
static_assert(std::is_same_v<Lizard, CastableCommonBase<Gecko, Iguana>>);
static_assert(std::is_same_v<Animal, CastableCommonBase<Bear, Frog, Iguana>>);
static_assert(
std::is_same_v<Lizard, CastableCommonBase<Lizard, Gecko, Iguana>>);
static_assert(
std::is_same_v<Lizard, CastableCommonBase<Gecko, Iguana, Lizard>>);
static_assert(
std::is_same_v<Lizard, CastableCommonBase<Gecko, Lizard, Iguana>>);
static_assert(std::is_same_v<Animal, CastableCommonBase<Frog, Gecko, Iguana>>);
static_assert(std::is_same_v<Animal, CastableCommonBase<Gecko, Iguana, Frog>>);
static_assert(std::is_same_v<Animal, CastableCommonBase<Gecko, Frog, Iguana>>);
static_assert(
std::is_same_v<CastableBase,
CastableCommonBase<Bear, Frog, Iguana, CastableBase>>);
} // namespace } // namespace
TINT_INSTANTIATE_TYPEINFO(Animal); TINT_INSTANTIATE_TYPEINFO(Animal);
@ -425,6 +465,7 @@ TINT_INSTANTIATE_TYPEINFO(Mammal);
TINT_INSTANTIATE_TYPEINFO(Reptile); TINT_INSTANTIATE_TYPEINFO(Reptile);
TINT_INSTANTIATE_TYPEINFO(Frog); TINT_INSTANTIATE_TYPEINFO(Frog);
TINT_INSTANTIATE_TYPEINFO(Bear); TINT_INSTANTIATE_TYPEINFO(Bear);
TINT_INSTANTIATE_TYPEINFO(Lizard);
TINT_INSTANTIATE_TYPEINFO(Gecko); TINT_INSTANTIATE_TYPEINFO(Gecko);
} // namespace tint } // namespace tint

View File

@ -58,10 +58,10 @@ inline ProgramID ProgramIDOf(const Cloneable*) {
/// CloneContext holds the state used while cloning AST nodes. /// CloneContext holds the state used while cloning AST nodes.
class CloneContext { class CloneContext {
/// ParamTypeIsPtrOf<F, T>::value is true iff the first parameter of /// ParamTypeIsPtrOf<F, T> is true iff the first parameter of
/// F is a pointer of (or derives from) type T. /// F is a pointer of (or derives from) type T.
template <typename F, typename T> template <typename F, typename T>
using ParamTypeIsPtrOf = traits::IsTypeOrDerived< static constexpr bool ParamTypeIsPtrOf = traits::IsTypeOrDerived<
typename std::remove_pointer<traits::ParameterType<F, 0>>::type, typename std::remove_pointer<traits::ParameterType<F, 0>>::type,
T>; T>;
@ -295,8 +295,8 @@ class CloneContext {
/// `T* (T*)`, where `T` derives from Cloneable /// `T* (T*)`, where `T` derives from Cloneable
/// @returns this CloneContext so calls can be chained /// @returns this CloneContext so calls can be chained
template <typename F> template <typename F>
traits::EnableIf<ParamTypeIsPtrOf<F, Cloneable>::value, CloneContext>& traits::EnableIf<ParamTypeIsPtrOf<F, Cloneable>, CloneContext>& ReplaceAll(
ReplaceAll(F&& replacer) { F&& replacer) {
using TPtr = traits::ParameterType<F, 0>; using TPtr = traits::ParameterType<F, 0>;
using T = typename std::remove_pointer<TPtr>::type; using T = typename std::remove_pointer<TPtr>::type;
for (auto& transform : transforms_) { for (auto& transform : transforms_) {

View File

@ -331,8 +331,8 @@ class ProgramBuilder {
/// @returns the node pointer /// @returns the node pointer
template <typename T, typename ARG0, typename... ARGS> template <typename T, typename ARG0, typename... ARGS>
traits::EnableIf</* T is ast::Node and ARG0 is not Source */ traits::EnableIf</* T is ast::Node and ARG0 is not Source */
traits::IsTypeOrDerived<T, ast::Node>::value && traits::IsTypeOrDerived<T, ast::Node> &&
!traits::IsTypeOrDerived<ARG0, Source>::value, !traits::IsTypeOrDerived<ARG0, Source>,
T>* T>*
create(ARG0&& arg0, ARGS&&... args) { create(ARG0&& arg0, ARGS&&... args) {
AssertNotMoved(); AssertNotMoved();
@ -346,8 +346,8 @@ class ProgramBuilder {
/// @param args the arguments to pass to the type constructor /// @param args the arguments to pass to the type constructor
/// @returns the node pointer /// @returns the node pointer
template <typename T, typename... ARGS> template <typename T, typename... ARGS>
traits::EnableIf<traits::IsTypeOrDerived<T, sem::Node>::value && traits::EnableIf<traits::IsTypeOrDerived<T, sem::Node> &&
!traits::IsTypeOrDerived<T, sem::Type>::value, !traits::IsTypeOrDerived<T, sem::Type>,
T>* T>*
create(ARGS&&... args) { create(ARGS&&... args) {
AssertNotMoved(); AssertNotMoved();

View File

@ -86,13 +86,12 @@ using ParameterType = typename SignatureOfT<F>::template parameter<N>;
template <typename F> template <typename F>
using ReturnType = typename SignatureOfT<F>::ret; using ReturnType = typename SignatureOfT<F>::ret;
/// `IsTypeOrDerived<T, BASE>::value` is true iff `T` is of type `BASE`, or /// IsTypeOrDerived<T, BASE> is true iff `T` is of type `BASE`, or derives from
/// derives from `BASE`. /// `BASE`.
template <typename T, typename BASE> template <typename T, typename BASE>
using IsTypeOrDerived = static constexpr bool IsTypeOrDerived =
std::integral_constant<bool, std::is_base_of<BASE, Decay<T>>::value ||
std::is_base_of<BASE, Decay<T>>::value || std::is_same<BASE, Decay<T>>::value;
std::is_same<BASE, Decay<T>>::value>;
/// If `CONDITION` is true then EnableIf resolves to type T, otherwise an /// If `CONDITION` is true then EnableIf resolves to type T, otherwise an
/// invalid type. /// invalid type.
@ -102,12 +101,12 @@ using EnableIf = typename std::enable_if<CONDITION, T>::type;
/// If `T` is of type `BASE`, or derives from `BASE`, then EnableIfIsType /// If `T` is of type `BASE`, or derives from `BASE`, then EnableIfIsType
/// resolves to type `T`, otherwise an invalid type. /// resolves to type `T`, otherwise an invalid type.
template <typename T, typename BASE> template <typename T, typename BASE>
using EnableIfIsType = EnableIf<IsTypeOrDerived<T, BASE>::value, T>; using EnableIfIsType = EnableIf<IsTypeOrDerived<T, BASE>, T>;
/// If `T` is not of type `BASE`, or does not derive from `BASE`, then /// If `T` is not of type `BASE`, or does not derive from `BASE`, then
/// EnableIfIsNotType resolves to type `T`, otherwise an invalid type. /// EnableIfIsNotType resolves to type `T`, otherwise an invalid type.
template <typename T, typename BASE> template <typename T, typename BASE>
using EnableIfIsNotType = EnableIf<!IsTypeOrDerived<T, BASE>::value, T>; using EnableIfIsNotType = EnableIf<!IsTypeOrDerived<T, BASE>, T>;
/// @returns the std::index_sequence with all the indices shifted by OFFSET. /// @returns the std::index_sequence with all the indices shifted by OFFSET.
template <std::size_t OFFSET, std::size_t... INDICES> template <std::size_t OFFSET, std::size_t... INDICES>