[clang] [clang][bytecode] Support compile-time exceptions (PR #189410)

Yanzuo Liu via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 4 07:48:32 PDT 2026


================
@@ -0,0 +1,1501 @@
+// RUN: %clang_cc1 -fcxx-exceptions -std=c++26 -fexperimental-new-constant-interpreter -verify %s
+
+namespace std {
+  class exception {
+  public:
+    constexpr exception() noexcept {};
+    // constexpr exception(const exception&) noexcept;
+    // constexpr exception& operator=(const exception&) noexcept;
+    constexpr virtual ~exception() {};
+    // constexpr virtual const char* what() const noexcept;
+  };
+
+  template <typename T> struct remove_reference { using type = T; };
+  template <typename T> struct remove_reference<T &> { using type = T; };
+  template <typename T> struct remove_reference<T &&> { using type = T; };
+  template <typename T>
+  constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
+    return static_cast<typename std::remove_reference<T>::type &&>(t);
+  }
+};
+
+
+class Bad : std::exception {};
+
+namespace Simple {
+  constexpr int a() {
+    try {
+    } catch(int e){
+      return 12;
+    }
+    return -2;
+  }
+  static_assert(a() == -2);
+
+  constexpr int b() {
+    try {
+      throw 12;
+    } catch(int e){
+      return 12;
+    }
+    return -2;
+  }
+  static_assert(b() == 12);
+
+  constexpr int c() {
+    int m = 12;
+    try {
+      throw 12;
+    } catch(int e){
+      m = 140;
+    }
+    return m;
+  }
+  static_assert(c() == 140);
+
+  constexpr int d() {
+    int m = 12;
+    try {
+      throw 12;
+    } catch(int e){
+      m = 140;
+    } catch (float f) {
+      m = 15;
+    }
+    return m;
+  }
+  static_assert(d() == 140);
+
+  constexpr int e() {
+    int m = 12;
+    try {
+      throw 12;
+    } catch(int e){
+      m = e + 2;
+    }
+    return m;
+  }
+  static_assert(e() == 14);
+
+  constexpr int f() {
+    int m = 12;
+    try {
+      throw 12;
+    } catch(...){
+      m = 100;
+    }
+    return m;
+  }
+  static_assert(f() == 100);
+
+  constexpr int g() {
+    int m = 12;
+    try {
+      throw Bad();
+    } catch(Bad &B){
+      m = 100;
+    }
+    return m;
+  }
+  static_assert(g() == 100);
+
+  constexpr int h() {
+    int m = 12;
+    try {
+      throw ++m;
+    } catch(...){
+    }
+    return m;
+  }
+  static_assert(h() == 13);
+
+  constexpr int i(bool b) {
+    try {
+      if (b)
+        throw 12;
+      else
+        throw 14.0f;
+    } catch (int) {
+      return 100;
+    } catch (float) {
+      return 200;
+    }
+    return 0;
+  }
+  static_assert(i(true) == 100);
+  static_assert(i(false) == 200);
+}
+
+namespace Uncaught {
+
+  constexpr int a() {
+    throw 12; // expected-note {{uncaught exception of type 'int': '12'}}
+    return 0;
+  }
+  static_assert(a() == 13); // expected-error {{not an integral constant expression}}
+}
+
+namespace NoFrame {
+  static_assert((1, throw 2, 3) == 1); // expected-error {{not an integral constant expression}} \
+                                       // expected-note {{uncaught exception of type 'int': '2'}} \
+                                       // expected-warning {{left operand of comma operator has no effect}}
+
+}
+
+namespace CleanupAfterThrowingCall {
+  constexpr int a() {
+    throw 12;
+    return -12;
+  }
+  constexpr int test() {
+    try {
+      a();
+    } catch (int i) {
+      return 26;
+    }
+
+    return 120;
+  }
+  static_assert(test() == 26);
+
+  constexpr int b2() {
+    throw 1.0;
+    return 1;
+  }
+  constexpr int a2() {
+    b2();
+    throw 12;
+    return -12;
+  }
+  constexpr int test2() {
+
+    try {
+      a2();
+    } catch (int i) {
+      return 26;
+    } catch (double d ){
+      return (int)(d * 2);
+    }
+
+    return 120;
+  }
+  static_assert(test2() == 2);
+}
+
+namespace Dtors {
+  class Inc {
+    public:
+    int &m;
+    constexpr Inc(int &m) : m(m) {}
+    constexpr ~Inc() { ++m; }
+  };
+
+  constexpr int test1() {
+    int m = 10;
+    Inc _(m);
+
+    try {
+      throw 12;
+    } catch (int) {
+      return m;
+    }
+  }
+  static_assert(test1() == 10);
+
+  constexpr int test2() {
+    int m = 10;
+    try {
+      Inc _(m);
+      throw 12;
+    } catch (int) {
+    }
+    return m;
+  }
+  static_assert(test2() == 11);
+
+  struct checker {
+    int & counter;
+    constexpr ~checker() {
+      ++counter;
+    }
+  };
+
+  constexpr int test() {
+    int counter = 0;
+    {
+      try {
+        auto c1 = checker{counter};
+        throw 42;
+      } catch (...) {
+        return counter * 7;
+      }
+    }
+    return counter * 3;
+  }
+
+  constexpr int destruction_counter = test();
+  static_assert(destruction_counter == 7);
+}
+
+namespace CatchArray {
+  template <typename T> consteval T test(T head, auto... tail) {
+    const T array[] = {head, tail...};
+    try {
+      throw array;
+    } catch (const T (&arr)[5]) {
+      return -2;
+    } catch (const T * ptr) {
+      return *ptr;
+    } catch (...) {
+      return -1;
+    }
+  }
+
+  constexpr auto r0 = test(1,2,3,4,5,6);
+  static_assert(r0 == 1);
+
+  constexpr auto r1 = test(1,2,3,4,5);
+  static_assert(r1 == 1);
+
+  constexpr auto r2 = test(1,2,3,4);
+  static_assert(r2 == 1);
+
+  constexpr auto r3 = test(7,1,2,3,4,5,6);
+  static_assert(r3 == 7);
+
+  constexpr auto r4 = test(8,1,2,3,4,5);
+  static_assert(r4 == 8);
+
+  constexpr auto r5 = test(9,1,2,3,4);
+  static_assert(r5 == 9);
+}
+
+namespace CatchVoidPtr {
+  consteval int test() {
+    int p = 3;
+    try {
+      throw &p;
+    } catch (void * ptr) {
+      return *static_cast<int *>(ptr);
+    }
+  }
+  static_assert(test() == 3);
+
+  constexpr void t() {
+    int p = 3; // expected-note {{declared here}}
+    throw &p;
+  }
+  consteval int test2() {
+    try {
+      t();
+    } catch (void * ptr) {
+      return *static_cast<int *>(ptr); // expected-note {{read of object outside its lifetime is not allowed in a constant expression}}
+    }
+  }
+  static_assert(test2() == 3); // expected-error {{not an integral constant expression}} \
+                               // expected-note {{in call to}}
+}
+
+namespace Nullptr {
+  consteval int test_nullptr() {
+    try {
+      throw nullptr;
+    } catch (const int * ex) {
+      return true;
+    } catch (...) {
+      return false;
+    }
+  }
+  static_assert(test_nullptr());
+
+  consteval int test_zero() {
+    try {
+      throw 0;
+    } catch (const int * ex) {
+      return false;
+    } catch (...) {
+      return true;
+    }
+  }
+  static_assert(test_zero());
+
+
+  constexpr int reference() {
+    try {
+      throw nullptr;
+    } catch (const int *&a) {
+      return 20;
+    }
+    return -1;
+  }
+  static_assert(reference() == 20);
+}
+
+namespace CatchAll {
+  template <typename T, typename... Args> consteval int test(Args && ... args) {
+    try {
+      throw T{args...};
+    } catch (unsigned v) {
+      return static_cast<int>(v) * 2;
+    } catch (int v) {
+      return v * 3;
+    } catch (bool v) {
+      return static_cast<int>(v) * 5;
+    } catch (...) {
+      return -1;
+    }
+    return 0;
+  }
+
+  static_assert(test<unsigned>(42u) == 84);
+  static_assert(test<int>(13) == 39);
+  static_assert(test<bool>(true) == 5);
+  static_assert(test<long>(42) == -1);
+}
+
+namespace Copy {
+  class Child {};
+  constexpr int test() {
+
+    try {
+      throw Child{};
+    } catch (Child C) {
+      return 20;
+    }
+    return 30;
+  }
+  static_assert(test() == 20);
+
+  constexpr int a(){
+      throw Child{};
+  }
+  constexpr int test2() {
+
+    try {
+      a();
+    } catch (Child C) {
+      return 20;
+    }
+    return 30;
+  }
+  static_assert(test2() == 20);
+}
+
+namespace Inheritance {
+  class Parent2 {
+  public:
+    int F = 5;
+    constexpr int getFive() { return F; }
+  };
+  class Parent : public Parent2{
+  };
+  class Child : public Parent {};
+
+  constexpr int foo() {
+    try {
+      throw Child{};
+    } catch (Parent2 P) {
+      return P.getFive() + 9;
+    }
+    return 0;
+  }
+  static_assert(foo() == 14);
+
+  constexpr int foo2() {
+    Child C{};
+    try {
+      throw &C;
+    } catch (Parent2 *P) {
+      return P->getFive() + 12;
+    }
+    return 0;
+  }
+  static_assert(foo2() == 17);
+
+  constexpr int a() {
+    throw Child{};
+  };
+  constexpr int foo3() {
+    try {
+      a();
+    } catch (Parent2 P) {
+      return P.getFive();
+    }
+    return 0;
+  }
+  static_assert(foo3() == 5);
+
+  constexpr int b(Child *C) {
+    throw C;
+  };
+
+  constexpr int foo4() {
+    Child C{};
+    try {
+      b(&C);
+    } catch (Parent *P) {
+      return P->getFive();
+    }
+    return 0;
+  }
+  static_assert(foo4() == 5);
+
+  constexpr int doublePointer() {
+    auto s = Child{};
+    auto *ss = &s;
+
+    try {
+      throw &ss;;
+    } catch (Parent**) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+    return -1;
+  }
+  static_assert(doublePointer() == 1);
+
+
+
+  class Base {};
+  class S : private Base {};
+  constexpr int foo5() {
+    try {
+      throw S{}; // expected-note {{uncaught exception of type 'S': '&S{}'}}
+    } catch (Base) {
+      return 10;
+    }
+    return -1;
+  }
+  static_assert(foo5() == -1); // expected-error {{not an integral constant expression}}
+
+  constexpr int foo6() {
+    auto s = Child{};
+    try {
+      throw &s;
+    } catch (Child*) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo6() == 2);
+
+  constexpr int foo7() {
+    try {
+      throw Child{};
+    } catch (Child*) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo7() == 1);
+
+  constexpr int foo8() {
+    auto s = Child{};
+    try {
+      throw &s;
+    } catch (Parent&) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo8() == 1);
+
+  constexpr int foo9() {
+    auto s = Child{};
+    try {
+      throw (Child&)s;
+    } catch (Parent&) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo9() == 2);
+
+  constexpr int foo10() {
+    auto s = Child{};
+    try {
+      throw (Child&)s;
+    } catch (Parent) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo10() == 2);
+
+  constexpr int foo11() {
+    auto s = Child{};
+    try {
+      throw (Child&&)s;
+    } catch (Parent2) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo11() == 2);
+
+
+  constexpr int foo12() {
+    auto s = Child{};
+    try {
+      throw s;
+    } catch (Parent&&) { // expected-error {{cannot catch exceptions by rvalue reference}}
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo12() == 2);
+}
+
+namespace Pointer {
+  static constexpr auto via_const_catch = 2;
+  static constexpr auto via_childs_get_value = 3;
+  static constexpr auto via_special_child_catch = 5;
+
+  struct parent {
+    int value;
+    explicit constexpr parent(int v) noexcept: value{v} { }
+    constexpr virtual int get_value() const noexcept {
+      return value;
+    }
+    constexpr virtual ~parent() = default;
+  };
+
+  struct modifying_child: parent {
+    explicit constexpr modifying_child(int v) noexcept: parent{v} { }
+    constexpr int get_value() const noexcept override {
+      return value * via_childs_get_value;
+    }
+  };
+
+  struct ordinary_child: parent {
+    explicit constexpr ordinary_child(int v) noexcept: parent{v} { }
+  };
+
+  struct special_child: parent {
+    explicit constexpr special_child(int v) noexcept: parent{v} { }
+  };
+
+  consteval int test(void (*fnc)()) {
+    int result = 0;
+    try {
+      fnc();
+    } catch (special_child * sch) {
+      result = sch->get_value() * via_special_child_catch;
+      delete sch;
+    } catch (const special_child * sch) {
+      result = sch->get_value() * via_special_child_catch * via_const_catch;
+      delete sch;
+    } catch (parent * exc) {
+      result = exc->get_value();
+      delete exc;
+    } catch (const parent * exc) {
+      result = exc->get_value() * via_const_catch;
+      delete exc;
+    }
+    return result;
+  }
+
+  constexpr auto r1 = test([] { throw new parent{1}; });
+  static_assert(r1 == 1);
+
+  constexpr auto r2 = test([] { throw new modifying_child{3}; });
+  static_assert(r2 == 3 * via_childs_get_value);
+
+  constexpr auto r3 = test([] { throw new ordinary_child{5}; });
+  static_assert(r3 == 5);
+
+  constexpr auto r4 = test([] { throw new special_child{17}; });
+  static_assert(r4 == 17 * via_special_child_catch);
+
+  constexpr int pointeeCatch() {
+    int x = 10;
+    try {
+      throw &x; // expected-note {{uncaught exception of type 'int *': '&x'}}
+    } catch (int ) {
+      return 10;
+    }
+    return 1;
+  }
+
+  static_assert(pointeeCatch() == 1); // expected-error {{not an integral constant expression}}
+}
+
+namespace References1 {
+  class Parent {
+  public:
+    int F = 10;
+    constexpr int getTen() { return F; }
+  };
+
+  class Child : public Parent {
+    public:
+    int F = 5;
+    constexpr int getFive() { return F; }
+
+  };
+
+  constexpr int foo() {
+    try {
+      throw Child{};
+    } catch (Child &C) {
+      return C.getFive();
+    }
+    return 0;
+  }
+  static_assert(foo() == 5);
+
+  constexpr int nested() {
+    throw Child{};
+    return 1;
+  };
+  constexpr int foo2() {
+    try {
+      nested();
+    } catch (Child &C) {
+      return C.getFive();
+    }
+    return 0;
+  }
+  static_assert(foo2() == 5);
+
+  constexpr int foo3() {
+    try {
+      throw Child{};
+    } catch (Parent &P) {
+      return P.getTen();
+    }
+    return 0;
+  }
+  static_assert(foo3() == 10);
+
+  constexpr int foo4() {
+    try {
+      nested();
+    } catch (Parent &P) {
+      return P.getTen();
+    }
+    return 0;
+  }
+  static_assert(foo4() == 10);
+
+  constexpr int foo5() {
+    try {
+      throw 13;
+    } catch (const int &a) {
+      return 25;
+    }
+    return -1;
+  }
+  static_assert(foo5() == 25);
+
+  consteval bool reference_test(const int & ref) {
----------------
zwuis wrote:

```suggestion
  consteval int reference_test(const int & ref) {
```


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


More information about the cfe-commits mailing list