[llvm] 0029815 - [ADT] Support `.Default` with `nullptr` and `nullopt` values in TypeSwitch (#165724)

via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 30 10:27:45 PDT 2025


Author: Jakub Kuderski
Date: 2025-10-30T13:27:41-04:00
New Revision: 0029815af7b8335054345e2f5aacf68ef9d18e81

URL: https://github.com/llvm/llvm-project/commit/0029815af7b8335054345e2f5aacf68ef9d18e81
DIFF: https://github.com/llvm/llvm-project/commit/0029815af7b8335054345e2f5aacf68ef9d18e81.diff

LOG: [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.

Added: 
    

Modified: 
    llvm/include/llvm/ADT/TypeSwitch.h
    llvm/unittests/ADT/TypeSwitchTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/TypeSwitch.h b/llvm/include/llvm/ADT/TypeSwitch.h
index 5657303b0a1f2..50ca1d5a6b5b6 100644
--- a/llvm/include/llvm/ADT/TypeSwitch.h
+++ b/llvm/include/llvm/ADT/TypeSwitch.h
@@ -111,6 +111,7 @@ class TypeSwitch : public detail::TypeSwitchBase<TypeSwitch<T, ResultT>, T> {
       return std::move(*result);
     return defaultFn(this->value);
   }
+
   /// As a default, return the given value.
   [[nodiscard]] ResultT Default(ResultT defaultResult) {
     if (result)
@@ -118,6 +119,22 @@ class TypeSwitch : public detail::TypeSwitchBase<TypeSwitch<T, ResultT>, T> {
     return defaultResult;
   }
 
+  /// Default for pointer-like results types that accept `nullptr`.
+  template <typename ArgT = ResultT,
+            typename =
+                std::enable_if_t<std::is_constructible_v<ArgT, std::nullptr_t>>>
+  [[nodiscard]] ResultT Default(std::nullptr_t) {
+    return Default(ResultT(nullptr));
+  }
+
+  /// Default for optional results types that accept `std::nullopt`.
+  template <typename ArgT = ResultT,
+            typename =
+                std::enable_if_t<std::is_constructible_v<ArgT, std::nullopt_t>>>
+  [[nodiscard]] ResultT Default(std::nullopt_t) {
+    return Default(ResultT(std::nullopt));
+  }
+
   /// Declare default as unreachable, making sure that all cases were handled.
   [[nodiscard]] ResultT DefaultUnreachable(
       const char *message = "Fell off the end of a type-switch") {

diff  --git a/llvm/unittests/ADT/TypeSwitchTest.cpp b/llvm/unittests/ADT/TypeSwitchTest.cpp
index a7d934265c5f0..b80122837c1ad 100644
--- a/llvm/unittests/ADT/TypeSwitchTest.cpp
+++ b/llvm/unittests/ADT/TypeSwitchTest.cpp
@@ -142,3 +142,44 @@ TEST(TypeSwitchTest, DefaultUnreachableWithVoid) {
   EXPECT_DEATH((void)translate(DerivedD()), "Unhandled type");
 #endif
 }
+
+TEST(TypeSwitchTest, DefaultNullopt) {
+  auto translate = [](auto value) {
+    return TypeSwitch<Base *, std::optional<int>>(&value)
+        .Case([](DerivedA *) { return 0; })
+        .Default(std::nullopt);
+  };
+  EXPECT_EQ(0, translate(DerivedA()));
+  EXPECT_EQ(std::nullopt, translate(DerivedD()));
+}
+
+TEST(TypeSwitchTest, DefaultNullptr) {
+  float foo = 0.0f;
+  auto translate = [&](auto value) {
+    return TypeSwitch<Base *, float *>(&value)
+        .Case([&](DerivedA *) { return &foo; })
+        .Default(nullptr);
+  };
+  EXPECT_EQ(&foo, translate(DerivedA()));
+  EXPECT_EQ(nullptr, translate(DerivedD()));
+}
+
+TEST(TypeSwitchTest, DefaultNullptrForPointerLike) {
+  struct Value {
+    void *ptr;
+    Value(const Value &other) : ptr(other.ptr) {}
+    Value(std::nullptr_t) : ptr(nullptr) {}
+    Value() : Value(nullptr) {}
+  };
+
+  float foo = 0.0f;
+  Value fooVal;
+  fooVal.ptr = &foo;
+  auto translate = [&](auto value) {
+    return TypeSwitch<Base *, Value>(&value)
+        .Case([&](DerivedA *) { return fooVal; })
+        .Default(nullptr);
+  };
+  EXPECT_EQ(&foo, translate(DerivedA()).ptr);
+  EXPECT_EQ(nullptr, translate(DerivedD()).ptr);
+}


        


More information about the llvm-commits mailing list