[llvm] [ADT] Add `DefaultUnreachable("msg")` to TypeSwitch (PR #161970)

via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 4 12:47:25 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-adt

Author: Jakub Kuderski (kuhar)

<details>
<summary>Changes</summary>

This is to allow making it explicit that all the cases must be handled. The error message is customizable.

Something similar was already supported using the conversion operator for the typed case but less explicit. In the `void` case when `TypeSwitch` doesn't return anything, this was not possible without a custom lambda.

---
Full diff: https://github.com/llvm/llvm-project/pull/161970.diff


2 Files Affected:

- (modified) llvm/include/llvm/ADT/TypeSwitch.h (+16-3) 
- (modified) llvm/unittests/ADT/TypeSwitchTest.cpp (+28) 


``````````diff
diff --git a/llvm/include/llvm/ADT/TypeSwitch.h b/llvm/include/llvm/ADT/TypeSwitch.h
index 5bbbdf23b257e..5657303b0a1f2 100644
--- a/llvm/include/llvm/ADT/TypeSwitch.h
+++ b/llvm/include/llvm/ADT/TypeSwitch.h
@@ -17,6 +17,7 @@
 
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
 #include <optional>
 
 namespace llvm {
@@ -117,11 +118,16 @@ class TypeSwitch : public detail::TypeSwitchBase<TypeSwitch<T, ResultT>, T> {
     return defaultResult;
   }
 
-  [[nodiscard]] operator ResultT() {
-    assert(result && "Fell off the end of a type-switch");
-    return std::move(*result);
+  /// 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") {
+    if (result)
+      return std::move(*result);
+    llvm_unreachable(message);
   }
 
+  [[nodiscard]] operator ResultT() { return DefaultUnreachable(); }
+
 private:
   /// The pointer to the result of this switch statement, once known,
   /// null before that.
@@ -158,6 +164,13 @@ class TypeSwitch<T, void>
       defaultFn(this->value);
   }
 
+  /// Declare default as unreachable, making sure that all cases were handled.
+  void DefaultUnreachable(
+      const char *message = "Fell off the end of a type-switch") {
+    if (!foundMatch)
+      llvm_unreachable(message);
+  }
+
 private:
   /// A flag detailing if we have already found a match.
   bool foundMatch = false;
diff --git a/llvm/unittests/ADT/TypeSwitchTest.cpp b/llvm/unittests/ADT/TypeSwitchTest.cpp
index c54b7987edf7e..1778df73be5f4 100644
--- a/llvm/unittests/ADT/TypeSwitchTest.cpp
+++ b/llvm/unittests/ADT/TypeSwitchTest.cpp
@@ -114,3 +114,31 @@ TEST(TypeSwitchTest, CasesOptional) {
   EXPECT_EQ(std::nullopt, translate(DerivedC()));
   EXPECT_EQ(-1, translate(DerivedD()));
 }
+
+TEST(TypeSwitchTest, DefaultUnreachableWithValue) {
+  auto translate = [](auto value) {
+    return TypeSwitch<Base *, int>(&value)
+        .Case([](DerivedA *) { return 0; })
+        .DefaultUnreachable("Unhandled type");
+  };
+  EXPECT_EQ(0, translate(DerivedA()));
+
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+  EXPECT_DEATH((void)translate(DerivedD(DerivedD())), "Unhandled type");
+#endif
+}
+
+TEST(TypeSwitchTest, DefaultUnreachableWithVoid) {
+  auto translate = [](auto value) {
+    int result = -1;
+    TypeSwitch<Base *>(&value)
+        .Case([&result](DerivedA *) { result = 0; })
+        .DefaultUnreachable("Unhandled type");
+    return result;
+  };
+  EXPECT_EQ(0, translate(DerivedA()));
+
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+  EXPECT_DEATH((void)translate(DerivedD(DerivedD())), "Unhandled type");
+#endif
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/161970


More information about the llvm-commits mailing list