// RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu -Wno-mismatched-new-delete #include "Inputs/std-compare.h" namespace std { struct type_info; struct destroying_delete_t { explicit destroying_delete_t() = default; } inline constexpr destroying_delete{}; struct nothrow_t { explicit nothrow_t() = default; } inline constexpr nothrow{}; using size_t = decltype(sizeof(0)); enum class align_val_t : size_t {}; }; [[nodiscard]] void *operator new(std::size_t, const std::nothrow_t&) noexcept; [[nodiscard]] void *operator new(std::size_t, std::align_val_t, const std::nothrow_t&) noexcept; [[nodiscard]] void *operator new[](std::size_t, const std::nothrow_t&) noexcept; [[nodiscard]] void *operator new[](std::size_t, std::align_val_t, const std::nothrow_t&) noexcept; [[nodiscard]] void *operator new[](std::size_t, std::align_val_t); void operator delete(void*, const std::nothrow_t&) noexcept; void operator delete(void*, std::align_val_t, const std::nothrow_t&) noexcept; void operator delete[](void*, const std::nothrow_t&) noexcept; void operator delete[](void*, std::align_val_t, const std::nothrow_t&) noexcept; // Helper to print out values for debugging. constexpr void not_defined(); template constexpr void print(T) { not_defined(); } namespace ThreeWayComparison { struct A { int n; constexpr friend int operator<=>(const A &a, const A &b) { return a.n < b.n ? -1 : a.n > b.n ? 1 : 0; } }; static_assert(A{1} <=> A{2} < 0); static_assert(A{2} <=> A{1} > 0); static_assert(A{2} <=> A{2} == 0); static_assert(1 <=> 2 < 0); static_assert(2 <=> 1 > 0); static_assert(1 <=> 1 == 0); constexpr int k = (1 <=> 1, 0); // expected-warning@-1 {{three-way comparison result unused}} static_assert(std::strong_ordering::equal == 0); constexpr void f() { void(1 <=> 1); } struct MemPtr { void foo() {} void bar() {} int data; int data2; long data3; }; struct MemPtr2 { void foo() {} void bar() {} int data; int data2; long data3; }; using MemPtrT = void (MemPtr::*)(); using FnPtrT = void (*)(); void FnPtr1() {} void FnPtr2() {} #define CHECK(...) ((__VA_ARGS__) ? void() : throw "error") #define CHECK_TYPE(...) static_assert(__is_same(__VA_ARGS__)); constexpr bool test_constexpr_success = [] { { auto &EQ = std::strong_ordering::equal; auto &LESS = std::strong_ordering::less; auto &GREATER = std::strong_ordering::greater; using SO = std::strong_ordering; auto eq = (42 <=> 42); CHECK_TYPE(decltype(eq), SO); CHECK(eq.test_eq(EQ)); auto less = (-1 <=> 0); CHECK_TYPE(decltype(less), SO); CHECK(less.test_eq(LESS)); auto greater = (42l <=> 1u); CHECK_TYPE(decltype(greater), SO); CHECK(greater.test_eq(GREATER)); } { using PO = std::partial_ordering; auto EQUIV = PO::equivalent; auto LESS = PO::less; auto GREATER = PO::greater; auto eq = (42.0 <=> 42.0); CHECK_TYPE(decltype(eq), PO); CHECK(eq.test_eq(EQUIV)); auto less = (39.0 <=> 42.0); CHECK_TYPE(decltype(less), PO); CHECK(less.test_eq(LESS)); auto greater = (-10.123 <=> -101.1); CHECK_TYPE(decltype(greater), PO); CHECK(greater.test_eq(GREATER)); } return true; }(); int dummy = 42; int dummy2 = 101; constexpr bool tc9 = (&dummy <=> &dummy2) != 0; // expected-error {{constant expression}} expected-note {{unspecified}} template constexpr T makeComplex(R r, I i) { T res{r, i}; return res; }; } // namespace ThreeWayComparison constexpr bool for_range_init() { int k = 0; for (int arr[3] = {1, 2, 3}; int n : arr) k += n; return k == 6; } static_assert(for_range_init()); namespace Virtual { struct NonZeroOffset { int padding = 123; }; constexpr void assert(bool b) { if (!b) throw 0; } // Ensure that we pick the right final overrider during construction. struct A { virtual constexpr char f() const { return 'A'; } char a = f(); constexpr ~A() { assert(f() == 'A'); } }; struct NoOverrideA : A {}; struct B : NonZeroOffset, NoOverrideA { virtual constexpr char f() const { return 'B'; } char b = f(); constexpr ~B() { assert(f() == 'B'); } }; struct NoOverrideB : B {}; struct C : NonZeroOffset, A { virtual constexpr char f() const { return 'C'; } A *pba; char c = ((A*)this)->f(); char ba = pba->f(); constexpr C(A *pba) : pba(pba) {} constexpr ~C() { assert(f() == 'C'); } }; struct D : NonZeroOffset, NoOverrideB, C { // expected-warning {{inaccessible}} virtual constexpr char f() const { return 'D'; } char d = f(); constexpr D() : C((B*)this) {} constexpr ~D() { assert(f() == 'D'); } }; constexpr int n = (D(), 0); constexpr D d; static_assert(((B&)d).a == 'A'); static_assert(((C&)d).a == 'A'); static_assert(d.b == 'B'); static_assert(d.c == 'C'); // During the construction of C, the dynamic type of B's A is B. static_assert(d.ba == 'B'); static_assert(d.d == 'D'); static_assert(d.f() == 'D'); constexpr const A &a = (B&)d; constexpr const B &b = d; static_assert(a.f() == 'D'); static_assert(b.f() == 'D'); // FIXME: It is unclear whether this should be permitted. D d_not_constexpr; static_assert(d_not_constexpr.f() == 'D'); // expected-error {{constant expression}} expected-note {{virtual function called on object 'd_not_constexpr' whose dynamic type is not constant}} // Check that we apply a proper adjustment for a covariant return type. struct Covariant1 { D d; virtual const A *f() const; }; template struct Covariant2 : Covariant1 { virtual const T *f() const; }; template struct Covariant3 : Covariant2 { constexpr virtual const D *f() const { return &this->d; } }; constexpr Covariant3 cb; constexpr Covariant3 cc; constexpr const Covariant1 *cb1 = &cb; constexpr const Covariant2 *cb2 = &cb; static_assert(cb1->f()->a == 'A'); static_assert(cb1->f() == (B*)&cb.d); static_assert(cb1->f()->f() == 'D'); static_assert(cb2->f()->b == 'B'); static_assert(cb2->f() == &cb.d); static_assert(cb2->f()->f() == 'D'); constexpr const Covariant1 *cc1 = &cc; constexpr const Covariant2 *cc2 = &cc; static_assert(cc1->f()->a == 'A'); static_assert(cc1->f() == (C*)&cc.d); static_assert(cc1->f()->f() == 'D'); static_assert(cc2->f()->c == 'C'); static_assert(cc2->f() == &cc.d); static_assert(cc2->f()->f() == 'D'); static_assert(cb.f()->d == 'D'); static_assert(cc.f()->d == 'D'); struct Abstract { constexpr virtual void f() = 0; // expected-note {{declared here}} constexpr Abstract() { do_it(); } // expected-note {{in call to}} constexpr void do_it() { f(); } // expected-note {{pure virtual function 'Virtual::Abstract::f' called}} }; struct PureVirtualCall : Abstract { void f(); }; // expected-note {{in call to 'Abstract}} constexpr PureVirtualCall pure_virtual_call; // expected-error {{constant expression}} expected-note {{in call to 'PureVirtualCall}} } namespace DynamicCast { struct A2 { virtual void a2(); }; struct A : A2 { virtual void a(); }; struct B : A {}; struct C2 { virtual void c2(); }; struct C : A, C2 { A *c = dynamic_cast(static_cast(this)); }; struct D { virtual void d(); }; struct E { virtual void e(); }; struct F : B, C, D, private E { void *f = dynamic_cast(static_cast(this)); }; struct Padding { virtual void padding(); }; struct G : Padding, F {}; constexpr G g; // During construction of C, A is unambiguous subobject of dynamic type C. static_assert(g.c == (C*)&g); // ... but in the complete object, the same is not true, so the runtime fails. static_assert(dynamic_cast(static_cast(&g)) == nullptr); // dynamic_cast produces a pointer to the object of the dynamic type. static_assert(g.f == (void*)(F*)&g); static_assert(dynamic_cast(static_cast(&g)) == &g); // expected-note@+1 {{reference dynamic_cast failed: 'DynamicCast::A' is an ambiguous base class of dynamic type 'DynamicCast::G' of operand}} constexpr int d_a = (dynamic_cast(static_cast(g)), 0); // expected-error {{}} // Can navigate from A2 to its A... static_assert(&dynamic_cast((A2&)(B&)g) == &(A&)(B&)g); // ... and from B to its A ... static_assert(&dynamic_cast((B&)g) == &(A&)(B&)g); // ... but not from D. // expected-note@+1 {{reference dynamic_cast failed: 'DynamicCast::A' is an ambiguous base class of dynamic type 'DynamicCast::G' of operand}} static_assert(&dynamic_cast((D&)g) == &(A&)(B&)g); // expected-error {{}} // Can cast from A2 to sibling class D. static_assert(&dynamic_cast((A2&)(B&)g) == &(D&)g); // Cannot cast from private base E to derived class F. // expected-note@+1 {{reference dynamic_cast failed: static type 'DynamicCast::E' of operand is a non-public base class of dynamic type 'DynamicCast::G'}} constexpr int e_f = (dynamic_cast((E&)g), 0); // expected-error {{}} // Cannot cast from B to private sibling E. // expected-note@+1 {{reference dynamic_cast failed: 'DynamicCast::E' is a non-public base class of dynamic type 'DynamicCast::G' of operand}} constexpr int b_e = (dynamic_cast((B&)g), 0); // expected-error {{}} struct Unrelated { virtual void unrelated(); }; // expected-note@+1 {{reference dynamic_cast failed: dynamic type 'DynamicCast::G' of operand does not have a base class of type 'DynamicCast::Unrelated'}} constexpr int b_unrelated = (dynamic_cast((B&)g), 0); // expected-error {{}} // expected-note@+1 {{reference dynamic_cast failed: dynamic type 'DynamicCast::G' of operand does not have a base class of type 'DynamicCast::Unrelated'}} constexpr int e_unrelated = (dynamic_cast((E&)g), 0); // expected-error {{}} } namespace TypeId { struct A { const std::type_info &ti = typeid(*this); }; struct A2 : A {}; static_assert(&A().ti == &typeid(A)); static_assert(&typeid((A2())) == &typeid(A2)); extern A2 extern_a2; static_assert(&typeid(extern_a2) == &typeid(A2)); constexpr A2 a2; constexpr const A &a1 = a2; static_assert(&typeid(a1) == &typeid(A)); struct B { virtual void f(); const std::type_info &ti1 = typeid(*this); }; struct B2 : B { const std::type_info &ti2 = typeid(*this); }; static_assert(&B2().ti1 == &typeid(B)); static_assert(&B2().ti2 == &typeid(B2)); extern B2 extern_b2; // expected-note@+1 {{typeid applied to object 'extern_b2' whose dynamic type is not constant}} static_assert(&typeid(extern_b2) == &typeid(B2)); // expected-error {{constant expression}} constexpr B2 b2; constexpr const B &b1 = b2; static_assert(&typeid(b1) == &typeid(B2)); constexpr bool side_effects() { // Not polymorphic nor a glvalue. bool OK = true; (void)typeid(OK = false, A2()); // expected-warning {{has no effect}} if (!OK) return false; // Not polymorphic. A2 a2; (void)typeid(OK = false, a2); // expected-warning {{has no effect}} if (!OK) return false; // Not a glvalue. (void)typeid(OK = false, B2()); // expected-warning {{has no effect}} if (!OK) return false; // Polymorphic glvalue: operand evaluated. OK = false; B2 b2; (void)typeid(OK = true, b2); // expected-warning {{will be evaluated}} return OK; } static_assert(side_effects()); } namespace Union { struct Base { int y; // expected-note 2{{here}} }; struct A : Base { int x; int arr[3]; union { int p, q; }; }; union B { A a; int b; }; constexpr int read_wrong_member() { // expected-error {{never produces a constant}} B b = {.b = 1}; return b.a.x; // expected-note {{read of member 'a' of union with active member 'b'}} } constexpr int change_member() { B b = {.b = 1}; b.a.x = 1; return b.a.x; } static_assert(change_member() == 1); constexpr int change_member_then_read_wrong_member() { // expected-error {{never produces a constant}} B b = {.b = 1}; b.a.x = 1; return b.b; // expected-note {{read of member 'b' of union with active member 'a'}} } constexpr int read_wrong_member_indirect() { // expected-error {{never produces a constant}} B b = {.b = 1}; int *p = &b.a.y; return *p; // expected-note {{read of member 'a' of union with active member 'b'}} } constexpr int read_uninitialized() { B b = {.b = 1}; int *p = &b.a.y; b.a.x = 1; return *p; // expected-note {{read of uninitialized object}} } static_assert(read_uninitialized() == 0); // expected-error {{constant}} expected-note {{in call}} constexpr void write_wrong_member_indirect() { // expected-error {{never produces a constant}} B b = {.b = 1}; int *p = &b.a.y; *p = 1; // expected-note {{assignment to member 'a' of union with active member 'b'}} } constexpr int write_uninitialized() { B b = {.b = 1}; int *p = &b.a.y; b.a.x = 1; *p = 1; return *p; } static_assert(write_uninitialized() == 1); constexpr int change_member_indirectly() { B b = {.b = 1}; b.a.arr[1] = 1; int &r = b.a.y; r = 123; b.b = 2; b.a.y = 3; b.a.arr[2] = 4; return b.a.arr[2]; } static_assert(change_member_indirectly() == 4); constexpr B return_uninit() { B b = {.b = 1}; b.a.x = 2; return b; } constexpr B uninit = return_uninit(); // expected-error {{constant expression}} expected-note {{subobject of type 'int' is not initialized}} static_assert(return_uninit().a.x == 2); constexpr A return_uninit_struct() { B b = {.b = 1}; b.a.x = 2; return b.a; // expected-note {{in call to 'A(b.a)'}} expected-note {{subobject of type 'int' is not initialized}} } // Note that this is rejected even though return_uninit() is accepted, and // return_uninit() copies the same stuff wrapped in a union. // // Copying a B involves copying the object representation of the union, but // copying an A invokes a copy constructor that copies the object // elementwise, and reading from b.a.y is undefined. static_assert(return_uninit_struct().x == 2); // expected-error {{constant expression}} expected-note {{in call}} constexpr B return_init_all() { B b = {.b = 1}; b.a.x = 2; b.a.y = 3; b.a.arr[0] = 4; b.a.arr[1] = 5; b.a.arr[2] = 6; return b; } static_assert(return_init_all().a.x == 2); static_assert(return_init_all().a.y == 3); static_assert(return_init_all().a.arr[0] == 4); static_assert(return_init_all().a.arr[1] == 5); static_assert(return_init_all().a.arr[2] == 6); static_assert(return_init_all().a.p == 7); // expected-error {{}} expected-note {{read of member 'p' of union with no active member}} static_assert(return_init_all().a.q == 8); // expected-error {{}} expected-note {{read of member 'q' of union with no active member}} constexpr B init_all = return_init_all(); constexpr bool test_no_member_change = []{ union U { char dummy = {}; }; U u1; U u2; u1 = u2; return true; }(); struct S1 { int n; }; struct S2 : S1 {}; struct S3 : S2 {}; void f() { S3 s; s.n = 0; } union ref_member_1 { int a; int b; }; struct ref_member_2 { ref_member_1 &&r; }; union ref_member_3 { ref_member_2 a, b; }; constexpr int ref_member_test_1() { ref_member_3 r = {.a = {.r = {.a = 1}}}; r.a.r.b = 2; return r.a.r.b; } static_assert(ref_member_test_1() == 2); constexpr int ref_member_test_2() { // expected-error {{never produces a constant}} ref_member_3 r = {.a = {.r = {.a = 1}}}; // FIXME: This note isn't great. The 'read' here is reading the referent of the reference. r.b.r.b = 2; // expected-note {{read of member 'b' of union with active member 'a'}} return r.b.r.b; } namespace PR43762 { struct A { int x = 1; constexpr int f() { return 1; } }; struct B : A { int y = 1; constexpr int g() { return 2; } }; struct C { int x; constexpr virtual int f() = 0; }; struct D : C { int y; constexpr virtual int f() override { return 3; } }; union U { int n; B b; D d; }; constexpr int test(int which) { U u{.n = 5}; switch (which) { case 0: u.b.x = 10; // expected-note {{active member 'n'}} return u.b.f(); case 1: u.b.y = 10; // expected-note {{active member 'n'}} return u.b.g(); case 2: u.d.x = 10; // expected-note {{active member 'n'}} return u.d.f(); case 3: u.d.y = 10; // expected-note {{active member 'n'}} return u.d.f(); } } static_assert(test(0)); // expected-error {{}} expected-note {{in call}} static_assert(test(1)); // expected-error {{}} expected-note {{in call}} static_assert(test(2)); // expected-error {{}} expected-note {{in call}} static_assert(test(3)); // expected-error {{}} expected-note {{in call}} } } namespace TwosComplementShifts { using uint32 = __UINT32_TYPE__; using int32 = __INT32_TYPE__; static_assert(uint32(int32(0x1234) << 16) == 0x12340000); static_assert(uint32(int32(0x1234) << 19) == 0x91a00000); static_assert(uint32(int32(0x1234) << 20) == 0x23400000); // expected-warning {{requires 34 bits}} static_assert(uint32(int32(0x1234) << 24) == 0x34000000); // expected-warning {{requires 38 bits}} static_assert(uint32(int32(-1) << 31) == 0x80000000); static_assert(-1 >> 1 == -1); static_assert(-1 >> 31 == -1); static_assert(-2 >> 1 == -1); static_assert(-3 >> 1 == -2); static_assert(-4 >> 1 == -2); } namespace Uninit { constexpr int f(bool init) { int a; if (init) a = 1; return a; // expected-note {{read of uninitialized object}} } static_assert(f(true) == 1); static_assert(f(false) == 1); // expected-error {{constant expression}} expected-note {{in call}} struct X { int n; // expected-note {{declared here}} constexpr X(bool init) { if (init) n = 123; } }; constinit X x1(true); constinit X x2(false); // expected-error {{constant initializer}} expected-note {{constinit}} expected-note {{subobject of type 'int' is not initialized}} struct Y { struct Z { int n; }; // expected-note {{here}} Z z1; Z z2; Z z3; // OK: the lifetime of z1 (and its members) start before the initializer of // z2 runs. constexpr Y() : z2{ (z1.n = 1, z1.n + 1) } { z3.n = 3; } // Not OK: z3 is not in its lifetime when the initializer of z2 runs. constexpr Y(int) : z2{ (z3.n = 1, // expected-note {{assignment to object outside its lifetime}} z3.n + 1) // expected-warning {{uninitialized}} } { z1.n = 3; } constexpr Y(int, int) : z2{} {} }; // FIXME: This is working around clang not implementing DR2026. With that // fixed, we should be able to test this without the injected copy. constexpr Y copy(Y y) { return y; } // expected-note {{in call to 'Y(y)'}} expected-note {{subobject of type 'int' is not initialized}} constexpr Y y1 = copy(Y()); static_assert(y1.z1.n == 1 && y1.z2.n == 2 && y1.z3.n == 3); constexpr Y y2 = copy(Y(0)); // expected-error {{constant expression}} expected-note {{in call}} static_assert(Y(0,0).z2.n == 0); static_assert(Y(0,0).z1.n == 0); // expected-error {{constant expression}} expected-note {{read of uninitialized object}} static_assert(Y(0,0).z3.n == 0); // expected-error {{constant expression}} expected-note {{read of uninitialized object}} static_assert(copy(Y(0,0)).z2.n == 0); // expected-error {{constant expression}} expected-note {{in call}} constexpr unsigned char not_even_unsigned_char() { unsigned char c; return c; // expected-note {{read of uninitialized object}} } constexpr unsigned char x = not_even_unsigned_char(); // expected-error {{constant expression}} expected-note {{in call}} constexpr int switch_var(int n) { switch (n) { case 1: int a; a = n; return a; case 2: a = n; return a; } } constexpr int s1 = switch_var(1); constexpr int s2 = switch_var(2); static_assert(s1 == 1 && s2 == 2); constexpr bool switch_into_init_stmt() { switch (1) { if (int n; false) { for (int m; false;) { case 1: n = m = 1; return n == 1 && m == 1; } } } } static_assert(switch_into_init_stmt()); } namespace dtor { void lifetime_extension() { struct X { constexpr ~X() {} }; X &&a = X(); } template constexpr T &&ref(T &&t) { return (T&&)t; } struct Buf { char buf[64]; int n = 0; constexpr void operator+=(char c) { buf[n++] = c; } constexpr bool operator==(const char *str) const { return str[n] == 0 && __builtin_memcmp(str, buf, n) == 0; } constexpr bool operator!=(const char *str) const { return !operator==(str); } }; struct A { constexpr A(Buf &buf, char c) : buf(buf), c(c) { buf += c; } constexpr ~A() { buf += c; } constexpr operator bool() const { return true; } Buf &buf; char c; }; constexpr bool dtor_calls_dtor() { union U { constexpr U(Buf &buf) : u(buf, 'u') { buf += 'U'; } constexpr ~U() { u.buf += 'U'; } A u, v; }; struct B : A { A c, &&d, e; union { A f; }; U u; constexpr B(Buf &buf) : A(buf, 'a'), c(buf, 'c'), d(ref(A(buf, 'd'))), e(A(buf, 'e')), f(buf, 'f'), u(buf) { buf += 'b'; } constexpr ~B() { buf += 'b'; } }; Buf buf; { B b(buf); if (buf != "acddefuUb") return false; } if (buf != "acddefuUbbUeca") return false; return true; } static_assert(dtor_calls_dtor()); constexpr void abnormal_termination(Buf &buf) { struct Indestructible { constexpr ~Indestructible(); // not defined }; A a(buf, 'a'); A(buf, 'b'); int n = 0; for (A &&c = A(buf, 'c'); A d = A(buf, 'd'); A(buf, 'e')) { switch (A f(buf, 'f'); A g = A(buf, 'g')) { // expected-warning {{boolean}} case false: { A x(buf, 'x'); } case true: { A h(buf, 'h'); switch (n++) { case 0: break; case 1: continue; case 2: return; } break; } default: Indestructible indest; } A j = (A(buf, 'i'), A(buf, 'j')); } } constexpr bool check_abnormal_termination() { Buf buf = {}; abnormal_termination(buf); return buf == "abbc" "dfgh" /*break*/ "hgfijijeed" "dfgh" /*continue*/ "hgfeed" "dfgh" /*return*/ "hgfd" "ca"; } static_assert(check_abnormal_termination()); constexpr bool run_dtors_on_array_filler() { struct S { int times_destroyed = 0; constexpr ~S() { if (++times_destroyed != 1) throw "oops"; } }; S s[3]; return true; } static_assert(run_dtors_on_array_filler()); // Ensure that we can handle temporary cleanups for array temporaries. struct ArrElem { constexpr ~ArrElem() {} }; using Arr = ArrElem[3]; static_assert((Arr{}, true)); } namespace dynamic_alloc { constexpr int *p = // expected-error {{constant}} expected-note {{pointer to heap-allocated object is not a constant expression}} new int; // expected-note {{heap allocation performed here}} constexpr int f(int n) { int *p = new int[n]; for (int i = 0; i != n; ++i) { p[i] = i; } int k = 0; for (int i = 0; i != n; ++i) { k += p[i]; } delete[] p; return k; } static_assert(f(123) == 123 * 122 / 2); constexpr bool nvdtor() { // expected-error {{never produces a constant expression}} struct S { constexpr ~S() {} }; struct T : S {}; delete (S*)new T; // expected-note {{delete of object with dynamic type 'T' through pointer to base class type 'S' with non-virtual destructor}} return true; } constexpr int vdtor_1() { int a; struct S { constexpr S(int *p) : p(p) {} constexpr virtual ~S() { *p = 1; } int *p; }; struct T : S { // implicit destructor defined eagerly because it is constexpr and virtual using S::S; }; delete (S*)new T(&a); return a; } static_assert(vdtor_1() == 1); constexpr int vdtor_2() { int a = 0; struct S { constexpr virtual ~S() {} }; struct T : S { constexpr T(int *p) : p(p) {} constexpr ~T() { ++*p; } int *p; }; S *p = new T{&a}; delete p; return a; } static_assert(vdtor_2() == 1); constexpr int vdtor_3(int mode) { int a = 0; struct S { constexpr virtual ~S() {} }; struct T : S { constexpr T(int *p) : p(p) {} constexpr ~T() { ++*p; } int *p; }; S *p = new T[3]{&a, &a, &a}; // expected-note 2{{heap allocation}} switch (mode) { case 0: delete p; // expected-note {{non-array delete used to delete pointer to array object of type 'T [3]'}} break; case 1: // FIXME: This diagnosic isn't great; we should mention the cast to S* // somewhere in here. delete[] p; // expected-note {{delete of pointer to subobject '&{*new T [3]#0}[0]'}} break; case 2: delete (T*)p; // expected-note {{non-array delete used to delete pointer to array object of type 'T [3]'}} break; case 3: delete[] (T*)p; break; } return a; } static_assert(vdtor_3(0) == 3); // expected-error {{}} expected-note {{in call}} static_assert(vdtor_3(1) == 3); // expected-error {{}} expected-note {{in call}} static_assert(vdtor_3(2) == 3); // expected-error {{}} expected-note {{in call}} static_assert(vdtor_3(3) == 3); constexpr void delete_mismatch() { // expected-error {{never produces a constant expression}} delete[] // expected-note {{array delete used to delete pointer to non-array object of type 'int'}} new int; // expected-note {{allocation}} } template constexpr T dynarray(int elems, int i) { T *p; if constexpr (sizeof(T) == 1) p = new T[elems]{"fox"}; // expected-note {{evaluated array bound 3 is too small to hold 4 explicitly initialized elements}} else p = new T[elems]{1, 2, 3}; // expected-note {{evaluated array bound 2 is too small to hold 3 explicitly initialized elements}} T n = p[i]; // expected-note 4{{past-the-end}} delete [] p; return n; } static_assert(dynarray(4, 0) == 1); static_assert(dynarray(4, 1) == 2); static_assert(dynarray(4, 2) == 3); static_assert(dynarray(4, 3) == 0); static_assert(dynarray(4, 4) == 0); // expected-error {{constant expression}} expected-note {{in call}} static_assert(dynarray(3, 2) == 3); static_assert(dynarray(3, 3) == 0); // expected-error {{constant expression}} expected-note {{in call}} static_assert(dynarray(2, 1) == 0); // expected-error {{constant expression}} expected-note {{in call}} static_assert(dynarray(5, 0) == 'f'); static_assert(dynarray(5, 1) == 'o'); static_assert(dynarray(5, 2) == 'x'); static_assert(dynarray(5, 3) == 0); // (from string) static_assert(dynarray(5, 4) == 0); // (from filler) static_assert(dynarray(5, 5) == 0); // expected-error {{constant expression}} expected-note {{in call}} static_assert(dynarray(4, 0) == 'f'); static_assert(dynarray(4, 1) == 'o'); static_assert(dynarray(4, 2) == 'x'); static_assert(dynarray(4, 3) == 0); static_assert(dynarray(4, 4) == 0); // expected-error {{constant expression}} expected-note {{in call}} static_assert(dynarray(3, 2) == 'x'); // expected-error {{constant expression}} expected-note {{in call}} constexpr bool run_dtors_on_array_filler() { struct S { int times_destroyed = 0; constexpr ~S() { if (++times_destroyed != 1) throw "oops"; } }; delete[] new S[3]; return true; } static_assert(run_dtors_on_array_filler()); constexpr bool erroneous_array_bound(long long n) { delete[] new int[n]; // expected-note {{array bound -1 is negative}} expected-note {{array bound 4611686018427387904 is too large}} return true; } static_assert(erroneous_array_bound(3)); static_assert(erroneous_array_bound(0)); static_assert(erroneous_array_bound(-1)); // expected-error {{constant expression}} expected-note {{in call}} static_assert(erroneous_array_bound(1LL << 62)); // expected-error {{constant expression}} expected-note {{in call}} constexpr bool erroneous_array_bound_nothrow(long long n) { int *p = new (std::nothrow) int[n]; bool result = p != 0; delete[] p; return result; } static_assert(erroneous_array_bound_nothrow(3)); static_assert(erroneous_array_bound_nothrow(0)); static_assert(!erroneous_array_bound_nothrow(-1)); static_assert(!erroneous_array_bound_nothrow(1LL << 62)); constexpr bool evaluate_nothrow_arg() { bool ok = false; delete new ((ok = true, std::nothrow)) int; return ok; } static_assert(evaluate_nothrow_arg()); constexpr void double_delete() { // expected-error {{never produces a constant expression}} int *p = new int; delete p; delete p; // expected-note {{delete of pointer that has already been deleted}} } constexpr bool super_secret_double_delete() { struct A { constexpr ~A() { delete this; } // expected-note {{destruction of object that is already being destroyed}} expected-note {{in call}} }; delete new A; // expected-note {{in call}} return true; } static_assert(super_secret_double_delete()); // expected-error {{constant expression}} expected-note {{in call}} constexpr void use_after_free() { // expected-error {{never produces a constant expression}} int *p = new int; delete p; *p = 1; // expected-note {{assignment to heap allocated object that has been deleted}} } constexpr void use_after_free_2() { // expected-error {{never produces a constant expression}} struct X { constexpr void f() {} }; X *p = new X; delete p; p->f(); // expected-note {{member call on heap allocated object that has been deleted}} } template struct X { std::size_t n; char *p; void dependent(); }; template void X::dependent() { char *p; // Ensure that we don't try to evaluate these for overflow and crash. These // are all value-dependent expressions. p = new char[n]; p = new ((std::align_val_t)n) char[n]; p = new char(n); } namespace PR47143 { constexpr char *f(int n) { return new char[n](); } const char *p = f(3); constexpr bool test() { char *p = f(3); bool result = !p[0] && !p[1] && !p[2]; delete [] p; return result; } static_assert(test()); } } struct placement_new_arg {}; void *operator new(std::size_t, placement_new_arg); void operator delete(void*, placement_new_arg); namespace placement_new_delete { struct ClassSpecificNew { void *operator new(std::size_t); }; struct ClassSpecificDelete { void operator delete(void*); }; struct DestroyingDelete { void operator delete(DestroyingDelete*, std::destroying_delete_t); }; struct alignas(64) Overaligned {}; constexpr bool ok() { delete new Overaligned; delete ::new ClassSpecificNew; ::delete new ClassSpecificDelete; ::delete new DestroyingDelete; return true; } static_assert(ok()); constexpr bool bad(int which) { switch (which) { case 0: delete new (placement_new_arg{}) int; // expected-note {{call to placement 'operator new'}} break; case 1: delete new ClassSpecificNew; // expected-note {{call to class-specific 'operator new'}} break; case 2: delete new ClassSpecificDelete; // expected-note {{call to class-specific 'operator delete'}} break; case 3: delete new DestroyingDelete; // expected-note {{call to class-specific 'operator delete'}} break; case 4: // FIXME: This technically follows the standard's rules, but it seems // unreasonable to expect implementations to support this. delete new (std::align_val_t{64}) Overaligned; // expected-note {{placement new expression is not yet supported}} break; } return true; } static_assert(bad(0)); // expected-error {{constant expression}} expected-note {{in call}} static_assert(bad(1)); // expected-error {{constant expression}} expected-note {{in call}} static_assert(bad(2)); // expected-error {{constant expression}} expected-note {{in call}} static_assert(bad(3)); // expected-error {{constant expression}} expected-note {{in call}} static_assert(bad(4)); // expected-error {{constant expression}} expected-note {{in call}} } namespace delete_random_things { static_assert((delete new int, true)); static_assert((delete (int*)0, true)); int n; // expected-note {{declared here}} static_assert((delete &n, true)); // expected-error {{}} expected-note {{delete of pointer '&n' that does not point to a heap-allocated object}} struct A { int n; }; static_assert((delete &(new A)->n, true)); // expected-error {{}} expected-note {{delete of pointer to subobject '&{*new delete_random_things::A#0}.n'}} static_assert((delete (new int + 1), true)); // expected-error {{}} expected-note {{delete of pointer '&{*new int#0} + 1' that does not point to complete object}} static_assert((delete[] (new int[3] + 1), true)); // expected-error {{}} expected-note {{delete of pointer to subobject '&{*new int [3]#0}[1]'}} static_assert((delete &(int&)(int&&)0, true)); // expected-error {{}} expected-note {{delete of pointer '&0' that does not point to a heap-allocated object}} expected-note {{temporary created here}} } namespace value_dependent_delete { template void f(T *p) { int arr[(delete p, 0)]; } } namespace memory_leaks { static_assert(*new bool(true)); // expected-error {{}} expected-note {{allocation performed here was not deallocated}} constexpr bool *f() { return new bool(true); } // expected-note {{allocation performed here was not deallocated}} static_assert(*f()); // expected-error {{}} struct UP { bool *p; constexpr ~UP() { delete p; } constexpr bool &operator*() { return *p; } }; constexpr UP g() { return {new bool(true)}; } static_assert(*g()); // ok constexpr bool h(UP p) { return *p; } static_assert(h({new bool(true)})); // ok } constexpr void *operator new(std::size_t, void *p) { return p; } namespace std { template constexpr T *construct(T *p) { return new (p) T; } template constexpr void destroy(T *p) { p->~T(); } } namespace dtor_call { struct A { int n; }; constexpr void f() { // expected-error {{never produces a constant expression}} A a; // expected-note {{destroying object 'a' whose lifetime has already ended}} a.~A(); } union U { A a; }; constexpr void g() { U u; u.a.n = 3; u.a.~A(); // There's now effectively no active union member, but we model it as if // 'a' is still the active union member (but its lifetime has ended). u.a.n = 4; // Start lifetime of 'a' again. u.a.~A(); } static_assert((g(), true)); constexpr bool pseudo(bool read, bool recreate) { using T = bool; bool b = false; // expected-note {{lifetime has already ended}} // This evaluates the store to 'b'... (b = true).~T(); // ... and ends the lifetime of the object. return (read ? b // expected-note {{read of object outside its lifetime}} : true) + (recreate ? (std::construct(&b), true) : true); } static_assert(pseudo(false, false)); // expected-error {{constant expression}} expected-note {{in call}} static_assert(pseudo(true, false)); // expected-error {{constant expression}} expected-note {{in call}} static_assert(pseudo(false, true)); constexpr void use_after_destroy() { A a; a.~A(); A b = a; // expected-note {{in call}} expected-note {{read of object outside its lifetime}} } static_assert((use_after_destroy(), true)); // expected-error {{}} expected-note {{in call}} constexpr void double_destroy() { A a; a.~A(); a.~A(); // expected-note {{destruction of object outside its lifetime}} } static_assert((double_destroy(), true)); // expected-error {{}} expected-note {{in call}} struct X { char *p; constexpr ~X() { *p++ = 'X'; } }; struct Y : X { int y; virtual constexpr ~Y() { *p++ = 'Y'; } }; struct Z : Y { int z; constexpr ~Z() override { *p++ = 'Z'; } }; union VU { constexpr VU() : z() {} constexpr ~VU() {} Z z; }; constexpr bool virt_dtor(int mode, const char *expected) { char buff[4] = {}; VU vu; vu.z.p = buff; switch (mode) { case 0: vu.z.~Z(); break; case 1: ((Y&)vu.z).~Y(); break; case 2: ((X&)vu.z).~X(); break; case 3: ((Y&)vu.z).Y::~Y(); vu.z.z = 1; // ok, still have a Z (with no Y base class!) break; case 4: ((X&)vu.z).X::~X(); vu.z.y = 1; // ok, still have a Z and a Y (with no X base class!) break; } return __builtin_strcmp(expected, buff) == 0; } static_assert(virt_dtor(0, "ZYX")); static_assert(virt_dtor(1, "ZYX")); static_assert(virt_dtor(2, "X")); static_assert(virt_dtor(3, "YX")); static_assert(virt_dtor(4, "X")); constexpr bool virt_delete(bool global) { struct A { virtual constexpr ~A() {} }; struct B : A { void operator delete(void *); constexpr ~B() {} }; A *p = new B; if (global) ::delete p; else delete p; // expected-note {{call to class-specific 'operator delete'}} return true; } static_assert(virt_delete(true)); static_assert(virt_delete(false)); // expected-error {{}} expected-note {{in call}} constexpr void use_after_virt_destroy() { char buff[4] = {}; VU vu; vu.z.p = buff; ((Y&)vu.z).~Y(); ((Z&)vu.z).z = 1; // expected-note {{assignment to object outside its lifetime}} } static_assert((use_after_virt_destroy(), true)); // expected-error {{}} expected-note {{in call}} constexpr void destroy_after_lifetime() { A *p; { A a; p = &a; } p->~A(); // expected-note {{destruction of object outside its lifetime}} } static_assert((destroy_after_lifetime(), true)); // expected-error {{}} expected-note {{in call}} constexpr void destroy_after_lifetime2() { A *p = []{ A a; return &a; }(); // expected-warning {{}} expected-note {{declared here}} p->~A(); // expected-note {{destruction of variable whose lifetime has ended}} } static_assert((destroy_after_lifetime2(), true)); // expected-error {{}} expected-note {{in call}} constexpr void destroy_after_lifetime3() { A *p = []{ return &(A&)(A&&)A(); }(); // expected-warning {{}} expected-note {{temporary created here}} p->~A(); // expected-note {{destruction of temporary whose lifetime has ended}} } static_assert((destroy_after_lifetime3(), true)); // expected-error {{}} expected-note {{in call}} constexpr void destroy_after_lifetime4() { // expected-error {{never produces a constant expression}} A *p = new A; delete p; p->~A(); // expected-note {{destruction of heap allocated object that has been deleted}} } struct Extern { constexpr ~Extern() {} } extern e; constexpr void destroy_extern() { // expected-error {{never produces a constant expression}} e.~Extern(); // expected-note {{cannot modify an object that is visible outside}} } constexpr A &&a_ref = A(); // expected-note {{temporary created here}} constexpr void destroy_extern_2() { // expected-error {{never produces a constant expression}} a_ref.~A(); // expected-note {{destruction of temporary is not allowed in a constant expression outside the expression that created the temporary}} } struct S { constexpr S() { n = 1; } constexpr ~S() { n = 0; } int n; }; constexpr void destroy_volatile() { volatile S s; } static_assert((destroy_volatile(), true)); // ok, not volatile during construction and destruction constexpr void destroy_null() { // expected-error {{never produces a constant expression}} ((A*)nullptr)->~A(); // expected-note {{destruction of dereferenced null pointer}} } constexpr void destroy_past_end() { // expected-error {{never produces a constant expression}} A a; (&a+1)->~A(); // expected-note {{destruction of dereferenced one-past-the-end pointer}} } constexpr void destroy_past_end_array() { // expected-error {{never produces a constant expression}} A a[2]; a[2].~A(); // expected-note {{destruction of dereferenced one-past-the-end pointer}} } union As { A a, b; }; constexpr void destroy_no_active() { // expected-error {{never produces a constant expression}} As as; as.b.~A(); // expected-note {{destruction of member 'b' of union with no active member}} } constexpr void destroy_inactive() { // expected-error {{never produces a constant expression}} As as; as.a.n = 1; as.b.~A(); // expected-note {{destruction of member 'b' of union with active member 'a'}} } constexpr void destroy_no_active_2() { // expected-error {{never produces a constant expression}} As as; as.a.n = 1; as.a.~A(); // FIXME: This diagnostic is wrong; the union has no active member now. as.b.~A(); // expected-note {{destruction of member 'b' of union with active member 'a'}} } constexpr void destroy_pointer() { using T = int*; T p; // We used to think this was an -> member access because its left-hand side // is a pointer. Ensure we don't crash. p.~T(); // Put a T back so we can destroy it again. std::construct(&p); } static_assert((destroy_pointer(), true)); } namespace temp_dtor { void f(); struct A { bool b; constexpr ~A() { if (b) f(); } }; // We can't accept either of these unless we start actually registering the // destructors of the A temporaries to run on shutdown. It's unclear what the // intended standard behavior is so we reject this for now. constexpr A &&a = A{false}; // expected-error {{constant}} expected-note {{non-trivial destruction of lifetime-extended temporary}} void f() { a.b = true; } constexpr A &&b = A{true}; // expected-error {{constant}} expected-note {{non-trivial destruction of lifetime-extended temporary}} // FIXME: We could in prinicple accept this. constexpr const A &c = A{false}; // expected-error {{constant}} expected-note {{non-trivial destruction of lifetime-extended temporary}} } namespace value_dependent_init { struct A { constexpr ~A() {} }; template void f() { A a = T(); } } namespace mutable_subobjects { struct A { int m; mutable int n; // expected-note 2{{here}} constexpr int f() const { return m; } constexpr int g() const { return n; } // expected-note {{mutable}} }; constexpr A a = {1, 2}; static_assert(a.f() == 1); // OK (PR44958) static_assert(a.g() == 2); // expected-error {{constant}} expected-note {{in call}} constexpr A b = a; // expected-error {{constant}} expected-note {{read of mutable member 'n'}} expected-note {{in call}} auto &ti1 = typeid(a); auto &ti2 = typeid(a.m); auto &ti3 = typeid(a.n); constexpr void destroy1() { // expected-error {{constexpr}} a.~A(); // expected-note {{cannot modify an object that is visible outside}} } using T = int; constexpr void destroy2() { // expected-error {{constexpr}} a.m.~T(); // expected-note {{cannot modify an object that is visible outside}} } constexpr void destroy3() { // expected-error {{constexpr}} a.n.~T(); // expected-note {{cannot modify an object that is visible outside}} } struct X { mutable int n = 0; virtual constexpr ~X() {} }; struct Y : X { }; constexpr Y y; constexpr const X *p = &y; constexpr const Y *q = dynamic_cast(p); // FIXME: It's unclear whether this should be accepted. The dynamic_cast is // undefined after 'z.y.~Y()`, for example. We essentially assume that all // objects that the evaluator can reach have unbounded lifetimes. (We make // the same assumption when evaluating member function calls.) struct Z { mutable Y y; }; constexpr Z z; constexpr const X *pz = &z.y; constexpr const Y *qz = dynamic_cast(pz); auto &zti = typeid(z.y); static_assert(&zti == &typeid(Y)); } namespace PR45133 { struct A { long x; }; union U; constexpr A foo(U *up); union U { A a = foo(this); // expected-note {{in call to 'foo(&u)'}} int y; }; constexpr A foo(U *up) { up->y = 11; // expected-note {{assignment would change active union member during the initialization of a different member}} return {42}; } constinit U u = {}; // expected-error {{constant init}} expected-note {{constinit}} template struct X {}; union V { int a, b; constexpr V(X<0>) : a(a = 1) {} // ok constexpr V(X<1>) : a(b = 1) {} // expected-note {{assignment would change active union member during the initialization of a different member}} constexpr V(X<2>) : a() { b = 1; } // ok // This case (changing the active member then changing it back) is debatable, // but it seems appropriate to reject. constexpr V(X<3>) : a((b = 1, a = 1)) {} // expected-note {{assignment would change active union member during the initialization of a different member}} }; constinit V v0 = X<0>(); constinit V v1 = X<1>(); // expected-error {{constant init}} expected-note {{constinit}} expected-note {{in call}} constinit V v2 = X<2>(); constinit V v3 = X<3>(); // expected-error {{constant init}} expected-note {{constinit}} expected-note {{in call}} } namespace PR45350 { int q; struct V { int n; int *p = &n; constexpr ~V() { *p = *p * 10 + n; }}; constexpr int f(int n) { int k = 0; V *p = new V[n]; for (int i = 0; i != n; ++i) { if (p[i].p != &p[i].n) return -1; p[i].n = i; p[i].p = &k; } delete[] p; return k; } // [expr.delete]p6: // In the case of an array, the elements will be destroyed in order of // decreasing address static_assert(f(6) == 543210); } namespace PR47805 { struct A { bool bad = true; constexpr ~A() { if (bad) throw; } }; constexpr bool f(A a) { a.bad = false; return true; } constexpr bool b = f(A()); struct B { B *p = this; }; constexpr bool g(B b) { return &b == b.p; } static_assert(g({})); } constexpr bool destroy_at_test() { int n = 0; std::destroy(&n); std::construct(&n); return true; } static_assert(destroy_at_test()); namespace PR48582 { struct S { void *p = this; constexpr S() {} constexpr S(const S&) {} }; constexpr bool b = [a = S(), b = S()] { return a.p == b.p; }(); static_assert(!b); }