Skip to content

Conversation

kuhar
Copy link
Member

@kuhar kuhar commented Oct 4, 2025

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.

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.
@llvmbot
Copy link
Member

llvmbot commented Oct 4, 2025

@llvm/pr-subscribers-llvm-adt

Author: Jakub Kuderski (kuhar)

Changes

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 --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
+}

Copy link
Contributor

@kazutakahirata kazutakahirata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks!

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I was looking for this recently and surprised it doesn't exist...

@kuhar kuhar merged commit 85c7cea into llvm:main Oct 4, 2025
9 checks passed
kuhar added a commit to kuhar/llvm-project that referenced this pull request Oct 4, 2025
Similar to TypeSwitch (llvm#161970), allow for explicit unreachable default
case with a custom error message on unhandled cases.

StringSwitch already allowed for checking if any of the cases matched
with the conversion operator, but `DefaultUnreachable` is more explicit
and allows for a custom message.
kuhar added a commit that referenced this pull request Oct 5, 2025
Similar to TypeSwitch (#161970), allow for explicit unreachable default
case with a custom error message on unhandled cases.

StringSwitch already allowed for checking if any of the cases matched
with the conversion operator, but `DefaultUnreachable` is more explicit
and allows for a custom message.
kuhar added a commit to kuhar/llvm-project that referenced this pull request Oct 5, 2025
kuhar added a commit to kuhar/llvm-project that referenced this pull request Oct 6, 2025
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 6, 2025
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.
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 6, 2025
Similar to TypeSwitch (llvm#161970), allow for explicit unreachable default
case with a custom error message on unhandled cases.

StringSwitch already allowed for checking if any of the cases matched
with the conversion operator, but `DefaultUnreachable` is more explicit
and allows for a custom message.
kuhar added a commit that referenced this pull request Oct 6, 2025
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Oct 6, 2025
kuhar added a commit that referenced this pull request Oct 6, 2025
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Oct 6, 2025
bump-llvm bot pushed a commit to makslevental/python_bindings_fork that referenced this pull request Oct 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants