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
class CastableBase;
/// Ignore is used as a special type used for skipping over types for trait
/// helper functions.
class Ignore {};
namespace detail {
template <typename T>
struct TypeInfoOf;
} // 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`.
#define TINT_INSTANTIATE_TYPEINFO(CLASS) \
TINT_CASTABLE_PUSH_DISABLE_WARNINGS(); \
@ -145,8 +156,7 @@ struct TypeInfo {
/// multiple hashcodes are bitwise-or'd together.
template <typename T>
static constexpr HashCode HashCodeOf() {
static_assert(traits::IsTypeOrDerived<T, CastableBase>::value,
"T is not Castable");
static_assert(IsCastable<T>, "T is not Castable");
static_assert(
std::is_same_v<T, std::remove_cv_t<T>>,
"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
/// cases failed to match.
///

View File

@ -22,34 +22,15 @@
namespace tint {
namespace {
struct Animal : public tint::Castable<Animal> {
explicit Animal(std::string n) : name(n) {}
const std::string name;
};
struct Amphibian : public tint::Castable<Amphibian, Animal> {
explicit Amphibian(std::string n) : Base(n) {}
};
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") {}
};
struct Animal : public tint::Castable<Animal> {};
struct Amphibian : public tint::Castable<Amphibian, Animal> {};
struct Mammal : public tint::Castable<Mammal, Animal> {};
struct Reptile : public tint::Castable<Reptile, Animal> {};
struct Frog : public tint::Castable<Frog, Amphibian> {};
struct Bear : public tint::Castable<Bear, Mammal> {};
struct Lizard : public tint::Castable<Lizard, Reptile> {};
struct Gecko : public tint::Castable<Gecko, Lizard> {};
struct Iguana : public tint::Castable<Iguana, Lizard> {};
TEST(CastableBase, Is) {
std::unique_ptr<CastableBase> frog = std::make_unique<Frog>();
@ -417,6 +398,65 @@ TEST(Castable, SwitchNullNoDefault) {
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
TINT_INSTANTIATE_TYPEINFO(Animal);
@ -425,6 +465,7 @@ TINT_INSTANTIATE_TYPEINFO(Mammal);
TINT_INSTANTIATE_TYPEINFO(Reptile);
TINT_INSTANTIATE_TYPEINFO(Frog);
TINT_INSTANTIATE_TYPEINFO(Bear);
TINT_INSTANTIATE_TYPEINFO(Lizard);
TINT_INSTANTIATE_TYPEINFO(Gecko);
} // namespace tint

View File

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

View File

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

View File

@ -86,13 +86,12 @@ using ParameterType = typename SignatureOfT<F>::template parameter<N>;
template <typename F>
using ReturnType = typename SignatureOfT<F>::ret;
/// `IsTypeOrDerived<T, BASE>::value` is true iff `T` is of type `BASE`, or
/// derives from `BASE`.
/// IsTypeOrDerived<T, BASE> is true iff `T` is of type `BASE`, or derives from
/// `BASE`.
template <typename T, typename BASE>
using IsTypeOrDerived =
std::integral_constant<bool,
std::is_base_of<BASE, Decay<T>>::value ||
std::is_same<BASE, Decay<T>>::value>;
static constexpr bool IsTypeOrDerived =
std::is_base_of<BASE, Decay<T>>::value ||
std::is_same<BASE, Decay<T>>::value;
/// If `CONDITION` is true then EnableIf resolves to type T, otherwise an
/// 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
/// resolves to type `T`, otherwise an invalid type.
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
/// EnableIfIsNotType resolves to type `T`, otherwise an invalid type.
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.
template <std::size_t OFFSET, std::size_t... INDICES>