[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