Skip to content

Commit 0029815

Browse files
authored
[ADT] Support .Default with nullptr and nullopt values in TypeSwitch (#165724)
In the previous implementation, this would fail for cases like `TypeSwitch<T*, std::optional<U>>` because `std::nullopt` does not match `ResultT` exactly and the overload for callable types would be selected. Add new overloads that support `nullptr` and `std::nullopt`. These can be added alongside generic callables because we wouldn't want to call any 'null' function refs anyway. I selected the `nullptr` and `nullopt` specializations because how often they appear in the codebase -- currently, you will see lots of code like `.Default(std::optional<T>())` that can be simplified with this patch.
1 parent 3056727 commit 0029815

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

llvm/include/llvm/ADT/TypeSwitch.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,30 @@ class TypeSwitch : public detail::TypeSwitchBase<TypeSwitch<T, ResultT>, T> {
111111
return std::move(*result);
112112
return defaultFn(this->value);
113113
}
114+
114115
/// As a default, return the given value.
115116
[[nodiscard]] ResultT Default(ResultT defaultResult) {
116117
if (result)
117118
return std::move(*result);
118119
return defaultResult;
119120
}
120121

122+
/// Default for pointer-like results types that accept `nullptr`.
123+
template <typename ArgT = ResultT,
124+
typename =
125+
std::enable_if_t<std::is_constructible_v<ArgT, std::nullptr_t>>>
126+
[[nodiscard]] ResultT Default(std::nullptr_t) {
127+
return Default(ResultT(nullptr));
128+
}
129+
130+
/// Default for optional results types that accept `std::nullopt`.
131+
template <typename ArgT = ResultT,
132+
typename =
133+
std::enable_if_t<std::is_constructible_v<ArgT, std::nullopt_t>>>
134+
[[nodiscard]] ResultT Default(std::nullopt_t) {
135+
return Default(ResultT(std::nullopt));
136+
}
137+
121138
/// Declare default as unreachable, making sure that all cases were handled.
122139
[[nodiscard]] ResultT DefaultUnreachable(
123140
const char *message = "Fell off the end of a type-switch") {

llvm/unittests/ADT/TypeSwitchTest.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,44 @@ TEST(TypeSwitchTest, DefaultUnreachableWithVoid) {
142142
EXPECT_DEATH((void)translate(DerivedD()), "Unhandled type");
143143
#endif
144144
}
145+
146+
TEST(TypeSwitchTest, DefaultNullopt) {
147+
auto translate = [](auto value) {
148+
return TypeSwitch<Base *, std::optional<int>>(&value)
149+
.Case([](DerivedA *) { return 0; })
150+
.Default(std::nullopt);
151+
};
152+
EXPECT_EQ(0, translate(DerivedA()));
153+
EXPECT_EQ(std::nullopt, translate(DerivedD()));
154+
}
155+
156+
TEST(TypeSwitchTest, DefaultNullptr) {
157+
float foo = 0.0f;
158+
auto translate = [&](auto value) {
159+
return TypeSwitch<Base *, float *>(&value)
160+
.Case([&](DerivedA *) { return &foo; })
161+
.Default(nullptr);
162+
};
163+
EXPECT_EQ(&foo, translate(DerivedA()));
164+
EXPECT_EQ(nullptr, translate(DerivedD()));
165+
}
166+
167+
TEST(TypeSwitchTest, DefaultNullptrForPointerLike) {
168+
struct Value {
169+
void *ptr;
170+
Value(const Value &other) : ptr(other.ptr) {}
171+
Value(std::nullptr_t) : ptr(nullptr) {}
172+
Value() : Value(nullptr) {}
173+
};
174+
175+
float foo = 0.0f;
176+
Value fooVal;
177+
fooVal.ptr = &foo;
178+
auto translate = [&](auto value) {
179+
return TypeSwitch<Base *, Value>(&value)
180+
.Case([&](DerivedA *) { return fooVal; })
181+
.Default(nullptr);
182+
};
183+
EXPECT_EQ(&foo, translate(DerivedA()).ptr);
184+
EXPECT_EQ(nullptr, translate(DerivedD()).ptr);
185+
}

0 commit comments

Comments
 (0)