Castable: Have Switch() support nullptr objects.

Change-Id: I7635b01707e80c2d2e0b79148e07874f2fa2b5ff
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/79764
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@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-09 21:37:57 +00:00 committed by Tint LUCI CQ
parent 6839cba568
commit 88d1a433aa
2 changed files with 46 additions and 13 deletions

View File

@ -565,7 +565,6 @@ inline bool NonDefaultCases(T* object,
/// @see NonDefaultCases
template <typename T, typename RETURN_TYPE, typename... CASES>
inline void SwitchCases(T* object,
const TypeInfo* type,
RETURN_TYPE* result,
std::tuple<CASES...>&& cases) {
using Cases = std::tuple<CASES...>;
@ -575,19 +574,32 @@ inline void SwitchCases(T* object,
static constexpr bool kHasDefaultCase = kDefaultIndex >= 0;
static constexpr bool kHasReturnType = !std::is_same_v<RETURN_TYPE, void>;
if constexpr (kHasDefaultCase) {
// Evaluate non-default cases.
if (!detail::NonDefaultCases<T>(object, type, result,
traits::Slice<0, kDefaultIndex>(cases))) {
// Nothing matched. Evaluate default case.
if (object) {
auto* type = &object->TypeInfo();
if constexpr (kHasDefaultCase) {
// Evaluate non-default cases.
if (!detail::NonDefaultCases<T>(object, type, result,
traits::Slice<0, kDefaultIndex>(cases))) {
// Nothing matched. Evaluate default case.
if constexpr (kHasReturnType) {
*result = std::get<kDefaultIndex>(cases)({});
} else {
std::get<kDefaultIndex>(cases)({});
}
}
} else {
detail::NonDefaultCases<T>(object, type, result, std::move(cases));
}
} else {
// Object is nullptr, so no cases can match
if constexpr (kHasDefaultCase) {
// Evaluate default case.
if constexpr (kHasReturnType) {
*result = std::get<kDefaultIndex>(cases)({});
} else {
std::get<kDefaultIndex>(cases)({});
}
}
} else {
detail::NonDefaultCases<T>(object, type, result, std::move(cases));
}
}
@ -608,6 +620,10 @@ inline void SwitchCases(T* object,
/// used as the last case. This default case will be called if all previous
/// cases failed to match.
///
/// If `object` is nullptr and a default case is provided, then the default case
/// will be called. If `object` is nullptr and no default case is provided, then
/// no cases will be called.
///
/// Example:
/// ```
/// Switch(object,
@ -630,17 +646,14 @@ inline auto Switch(T* object, CASES&&... cases) {
using ReturnType = traits::ReturnType<std::tuple_element_t<0, Cases>>;
static constexpr bool kHasReturnType = !std::is_same_v<ReturnType, void>;
auto& type = object->TypeInfo();
if constexpr (kHasReturnType) {
ReturnType res = {};
detail::SwitchCases(object, &type, &res,
detail::SwitchCases(object, &res,
std::forward_as_tuple(std::forward<CASES>(cases)...));
return res;
} else {
detail::SwitchCases<T, void>(
object, &type, nullptr,
std::forward_as_tuple(std::forward<CASES>(cases)...));
object, nullptr, std::forward_as_tuple(std::forward<CASES>(cases)...));
}
}

View File

@ -371,6 +371,7 @@ TEST(Castable, SwitchDefault) {
EXPECT_TRUE(gecko_matched_default);
}
}
TEST(Castable, SwitchMatchFirst) {
std::unique_ptr<Animal> frog = std::make_unique<Frog>();
{
@ -397,6 +398,25 @@ TEST(Castable, SwitchMatchFirst) {
}
}
TEST(Castable, SwitchNull) {
Animal* null = nullptr;
Switch(
null, //
[&](Amphibian*) { FAIL() << "should not be called"; },
[&](Animal*) { FAIL() << "should not be called"; });
}
TEST(Castable, SwitchNullNoDefault) {
Animal* null = nullptr;
bool default_called = false;
Switch(
null, //
[&](Amphibian*) { FAIL() << "should not be called"; },
[&](Animal*) { FAIL() << "should not be called"; },
[&](Default) { default_called = true; });
EXPECT_TRUE(default_called);
}
} // namespace
TINT_INSTANTIATE_TYPEINFO(Animal);