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>
diff --git a/src/castable.h b/src/castable.h
index 8977467..2906563 100644
--- a/src/castable.h
+++ b/src/castable.h
@@ -48,12 +48,23 @@
// 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 @@
/// 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 @@
}
};
+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.
///
diff --git a/src/castable_test.cc b/src/castable_test.cc
index a15b3d6..4975bef 100644
--- a/src/castable_test.cc
+++ b/src/castable_test.cc
@@ -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 @@
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(Reptile);
TINT_INSTANTIATE_TYPEINFO(Frog);
TINT_INSTANTIATE_TYPEINFO(Bear);
+TINT_INSTANTIATE_TYPEINFO(Lizard);
TINT_INSTANTIATE_TYPEINFO(Gecko);
} // namespace tint
diff --git a/src/clone_context.h b/src/clone_context.h
index 974ee90..4037e80 100644
--- a/src/clone_context.h
+++ b/src/clone_context.h
@@ -58,10 +58,10 @@
/// 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 @@
/// `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_) {
diff --git a/src/program_builder.h b/src/program_builder.h
index 0db8eea..787b64e 100644
--- a/src/program_builder.h
+++ b/src/program_builder.h
@@ -331,8 +331,8 @@
/// @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 @@
/// @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();
diff --git a/src/traits.h b/src/traits.h
index f8e52a6..ef702d6 100644
--- a/src/traits.h
+++ b/src/traits.h
@@ -86,13 +86,12 @@
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 @@
/// 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>