// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both %s // RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s // RUN: %clang_cc1 -triple=i686-linux-gnu -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s // RUN: %clang_cc1 -verify=ref,both %s // RUN: %clang_cc1 -std=c++20 -verify=ref,both %s // RUN: %clang_cc1 -triple=i686-linux-gnu -std=c++20 -verify=ref,both %s #if __cplusplus >= 202002L constexpr int *Global = new int(12); // both-error {{must be initialized by a constant expression}} \ // both-note {{pointer to heap-allocated object}} \ // both-note {{heap allocation performed here}} static_assert(*(new int(12)) == 12); // both-error {{not an integral constant expression}} \ // both-note {{allocation performed here was not deallocated}} constexpr int a() { new int(12); // both-note {{allocation performed here was not deallocated}} return 1; } static_assert(a() == 1, ""); // both-error {{not an integral constant expression}} constexpr int b() { int *i = new int(12); int m = *i; delete(i); return m; } static_assert(b() == 12, ""); struct S { int a; int b; static constexpr S *create(int a, int b) { return new S(a, b); } }; constexpr int c() { S *s = new S(12, 13); int i = s->a; delete s; return i; } static_assert(c() == 12, ""); /// Dynamic allocation in function ::create(), freed in function d(). constexpr int d() { S* s = S::create(12, 14); int sum = s->a + s->b; delete s; return sum; } static_assert(d() == 26); /// Test we emit the right diagnostic for several allocations done on /// the same site. constexpr int loop() { for (int i = 0; i < 10; ++i) { int *a = new int[10]; // both-note {{not deallocated (along with 9 other memory leaks)}} } return 1; } static_assert(loop() == 1, ""); // both-error {{not an integral constant expression}} /// No initializer. constexpr int noInit() { int *i = new int; delete i; return 0; } static_assert(noInit() == 0, ""); /// Try to delete a pointer that hasn't been heap allocated. constexpr int notHeapAllocated() { // both-error {{never produces a constant expression}} int A = 0; // both-note 2{{declared here}} delete &A; // ref-note 2{{delete of pointer '&A' that does not point to a heap-allocated object}} \ // expected-note 2{{delete of pointer '&A' that does not point to a heap-allocated object}} return 1; } static_assert(notHeapAllocated() == 1, ""); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'notHeapAllocated()'}} consteval int deleteNull() { int *A = nullptr; delete A; return 1; } static_assert(deleteNull() == 1, ""); consteval int doubleDelete() { // both-error {{never produces a constant expression}} int *A = new int; delete A; delete A; // both-note 2{{delete of pointer that has already been deleted}} return 1; } static_assert(doubleDelete() == 1); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'doubleDelete()'}} constexpr int AutoArray() { auto array = new int[]{0, 1, 2, 3}; int ret = array[3]; delete [] array; return ret; } static_assert(AutoArray() == 3); #if 0 consteval int largeArray1(bool b) { if (b) { int *a = new int[1ull<<32]; // both-note {{cannot allocate array; evaluated array bound 4294967296 is too large}} delete[] a; } return 1; } static_assert(largeArray1(false) == 1, ""); static_assert(largeArray1(true) == 1, ""); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'largeArray1(true)'}} consteval int largeArray2(bool b) { if (b) { S *a = new S[1ull<<32]; // both-note {{cannot allocate array; evaluated array bound 4294967296 is too large}} delete[] a; } return 1; } static_assert(largeArray2(false) == 1, ""); static_assert(largeArray2(true) == 1, ""); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'largeArray2(true)'}} #endif namespace Arrays { constexpr int d() { int *Arr = new int[12]; Arr[0] = 1; Arr[1] = 5; int sum = Arr[0] + Arr[1]; delete[] Arr; return sum; } static_assert(d() == 6); constexpr int mismatch1() { // both-error {{never produces a constant expression}} int *i = new int(12); // both-note {{allocated with 'new' here}} \ // both-note 2{{heap allocation performed here}} delete[] i; // both-warning {{'delete[]' applied to a pointer that was allocated with 'new'}} \ // both-note 2{{array delete used to delete pointer to non-array object of type 'int'}} return 6; } static_assert(mismatch1() == 6); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'mismatch1()'}} constexpr int mismatch2() { // both-error {{never produces a constant expression}} int *i = new int[12]; // both-note {{allocated with 'new[]' here}} \ // both-note 2{{heap allocation performed here}} delete i; // both-warning {{'delete' applied to a pointer that was allocated with 'new[]'}} \ // both-note 2{{non-array delete used to delete pointer to array object of type 'int[12]'}} return 6; } static_assert(mismatch2() == 6); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'mismatch2()'}} /// Array of composite elements. constexpr int foo() { S *ss = new S[12]; ss[0].a = 12; int m = ss[0].a; delete[] ss; return m; } static_assert(foo() == 12); constexpr int ArrayInit() { auto array = new int[4]{0, 1, 2, 3}; int ret = array[0]; delete [] array; return ret; } static_assert(ArrayInit() == 0, ""); struct S { float F; }; constexpr float ArrayInit2() { auto array = new S[4]{}; float ret = array[0].F; delete [] array; return ret; } static_assert(ArrayInit2() == 0.0f, ""); } 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; struct placement_new_arg {}; void *operator new(std::size_t, placement_new_arg); void operator delete(void*, placement_new_arg); 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 PlacementNew { constexpr int foo() { // both-error {{never produces a constant expression}} char c[sizeof(int)]; new (c) int{12}; // both-note {{this placement new expression is not supported in constant expressions before C++2c}} return 0; } } namespace NowThrowNew { constexpr bool erroneous_array_bound_nothrow(long long n) { int *p = new (std::nothrow) int[n]; bool result = p != nullptr; 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) == 0); static_assert(!erroneous_array_bound_nothrow(1LL << 62)); struct S { int a; }; constexpr bool erroneous_array_bound_nothrow2(long long n) { S *p = new (std::nothrow) S[n]; bool result = p != nullptr; delete[] p; return result; } /// This needs support for CXXConstrucExprs with non-constant array sizes. static_assert(erroneous_array_bound_nothrow2(3)); // expected-error {{not an integral constant expression}} static_assert(erroneous_array_bound_nothrow2(0));// expected-error {{not an integral constant expression}} static_assert(erroneous_array_bound_nothrow2(-1) == 0);// expected-error {{not an integral constant expression}} static_assert(!erroneous_array_bound_nothrow2(1LL << 62));// expected-error {{not an integral constant expression}} constexpr bool evaluate_nothrow_arg() { bool ok = false; delete new ((ok = true, std::nothrow)) int; return ok; } static_assert(evaluate_nothrow_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; // both-note {{this placement new expression is not supported in constant expressions}} break; case 1: delete new ClassSpecificNew; // both-note {{call to class-specific 'operator new'}} break; case 2: delete new ClassSpecificDelete; // both-note {{call to class-specific 'operator delete'}} break; case 3: delete new DestroyingDelete; // both-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; // both-note {{this placement new expression is not supported in constant expressions}} break; } return true; } static_assert(bad(0)); // both-error {{constant expression}} \ // both-note {{in call}} static_assert(bad(1)); // both-error {{constant expression}} both-note {{in call}} static_assert(bad(2)); // both-error {{constant expression}} both-note {{in call}} static_assert(bad(3)); // both-error {{constant expression}} both-note {{in call}} static_assert(bad(4)); // both-error {{constant expression}} \ // both-note {{in call}} } namespace delete_random_things { static_assert((delete new int, true)); static_assert((delete (int*)0, true)); int n; // both-note {{declared here}} static_assert((delete &n, true)); // both-error {{}} \ // both-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)); // both-error {{}} \ // both-note {{delete of pointer to subobject }} static_assert((delete (new int + 1), true)); // both-error {{}} \ // ref-note {{delete of pointer '&{*new int#0} + 1' that does not point to complete object}} \ // expected-note {{delete of pointer '&{*new int#1} + 1' that does not point to complete object}} static_assert((delete[] (new int[3] + 1), true)); // both-error {{}} \ // both-note {{delete of pointer to subobject}} static_assert((delete &(int&)(int&&)0, true)); // both-error {{}} \ // both-note {{delete of pointer '&0' that does not point to a heap-allocated object}} \ // both-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)); // both-error {{}} both-note {{allocation performed here was not deallocated}} constexpr bool *f() { return new bool(true); } // both-note {{allocation performed here was not deallocated}} static_assert(*f()); // both-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 } /// From test/SemaCXX/cxx2a-consteval.cpp namespace std { template struct remove_reference { using type = T; }; template struct remove_reference { using type = T; }; template struct remove_reference { using type = T; }; template constexpr typename std::remove_reference::type&& move(T &&t) noexcept { return static_cast::type &&>(t); } } namespace cxx2a { struct A { int* p = new int(42); // both-note 3{{heap allocation performed here}} consteval int ret_i() const { return p ? *p : 0; } consteval A ret_a() const { return A{}; } constexpr ~A() { delete p; } }; consteval int by_value_a(A a) { return a.ret_i(); } consteval int const_a_ref(const A &a) { return a.ret_i(); } consteval int rvalue_ref(const A &&a) { return a.ret_i(); } consteval const A &to_lvalue_ref(const A &&a) { return a; } void test() { constexpr A a{ nullptr }; { int k = A().ret_i(); } { A k = A().ret_a(); } // both-error {{'cxx2a::A::ret_a' is not a constant expression}} \ // both-note {{heap-allocated object is not a constant expression}} { A k = to_lvalue_ref(A()); } // both-error {{'cxx2a::to_lvalue_ref' is not a constant expression}} \ // both-note {{reference to temporary is not a constant expression}} \ // both-note {{temporary created here}} { A k = to_lvalue_ref(A().ret_a()); } // both-error {{'cxx2a::to_lvalue_ref' is not a constant expression}} \ // both-note {{reference to temporary is not a constant expression}} \ // both-note {{temporary created here}} { int k = A().ret_a().ret_i(); } // both-error {{'cxx2a::A::ret_a' is not a constant expression}} \ // both-note {{heap-allocated object is not a constant expression}} { int k = by_value_a(A()); } { int k = const_a_ref(A()); } { int k = const_a_ref(a); } { int k = rvalue_ref(A()); } { int k = rvalue_ref(std::move(a)); } { int k = const_a_ref(A().ret_a()); } { int k = const_a_ref(to_lvalue_ref(A().ret_a())); } { int k = const_a_ref(to_lvalue_ref(std::move(a))); } { int k = by_value_a(A().ret_a()); } { int k = by_value_a(to_lvalue_ref(static_cast(a))); } { int k = (A().ret_a(), A().ret_i()); } // both-error {{'cxx2a::A::ret_a' is not a constant expression}} \ // both-note {{is not a constant expression}} \ // both-warning {{left operand of comma operator has no effect}} { int k = (const_a_ref(A().ret_a()), A().ret_i()); } // both-warning {{left operand of comma operator has no effect}} } } constexpr int *const &p = new int; // both-error {{must be initialized by a constant expression}} \ // both-note {{pointer to heap-allocated object}} \ // both-note {{allocation performed here}} constexpr const int *A[] = {nullptr, nullptr, new int{12}}; // both-error {{must be initialized by a constant expression}} \ // both-note {{pointer to heap-allocated object}} \ // both-note {{allocation performed here}} struct Sp { const int *p; }; constexpr Sp ss[] = {Sp{new int{154}}}; // both-error {{must be initialized by a constant expression}} \ // both-note {{pointer to heap-allocated object}} \ // both-note {{allocation performed here}} namespace DeleteRunsDtors { struct InnerFoo { int *mem; constexpr ~InnerFoo() { delete mem; } }; struct Foo { int *a; InnerFoo IF; constexpr Foo() { a = new int(13); IF.mem = new int(100); } constexpr ~Foo() { delete a; } }; constexpr int abc() { Foo *F = new Foo(); int n = *F->a; delete F; return n; } static_assert(abc() == 13); constexpr int abc2() { Foo *f = new Foo[3]; delete[] f; return 1; } static_assert(abc2() == 1); } /// FIXME: There is a slight difference in diagnostics here. namespace FaultyDtorCalledByDelete { struct InnerFoo { int *mem; constexpr ~InnerFoo() { if (mem) { (void)(1/0); // both-warning {{division by zero is undefined}} \ // both-note {{division by zero}} } delete mem; } }; struct Foo { int *a; InnerFoo IF; constexpr Foo() { a = new int(13); IF.mem = new int(100); } constexpr ~Foo() { delete a; } // expected-note {{in call to}} }; constexpr int abc() { Foo *F = new Foo(); int n = *F->a; delete F; // both-note {{in call to}} \ // ref-note {{in call to}} return n; } static_assert(abc() == 13); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'abc()'}} } namespace DeleteThis { constexpr bool super_secret_double_delete() { struct A { constexpr ~A() { delete this; } // both-note {{destruction of object that is already being destroyed}} \ // ref-note {{in call to}} }; delete new A; // both-note {{in call to}} return true; } static_assert(super_secret_double_delete()); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'super_secret_double_delete()'}} } namespace CastedDelete { 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; }; constexpr int vdtor_1() { int a; delete (S*)new T(&a); return a; } static_assert(vdtor_1() == 1); } constexpr void use_after_free_2() { // both-error {{never produces a constant expression}} struct X { constexpr void f() {} }; X *p = new X; delete p; p->f(); // both-note {{member call on heap allocated object that has been deleted}} } /// std::allocator definition namespace std { using size_t = decltype(sizeof(0)); template struct allocator { constexpr T *allocate(size_t N) { return (T*)__builtin_operator_new(sizeof(T) * N); // both-note 2{{allocation performed here}} \ // #alloc } constexpr void deallocate(void *p) { __builtin_operator_delete(p); // both-note 2{{std::allocator<...>::deallocate' used to delete pointer to object allocated with 'new'}} \ // both-note {{used to delete a null pointer}} } }; template constexpr void construct_at(void *p, Args &&...args) { // #construct new (p) T((Args&&)args...); } } /// Specialization for float, using operator new/delete. namespace std { using size_t = decltype(sizeof(0)); template<> struct allocator { constexpr float *allocate(size_t N) { return (float*)operator new (sizeof(float) * N); } constexpr void deallocate(void *p) { operator delete(p); } }; } namespace OperatorNewDelete { constexpr bool mismatched(int alloc_kind, int dealloc_kind) { int *p; switch (alloc_kind) { case 0: p = new int; // both-note {{heap allocation performed here}} break; case 1: p = new int[1]; // both-note {{heap allocation performed here}} break; case 2: p = std::allocator().allocate(1); break; } switch (dealloc_kind) { case 0: delete p; // both-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}} break; case 1: delete[] p; // both-note {{'delete' used to delete pointer to object allocated with 'std::allocator<...>::allocate'}} break; case 2: std::allocator().deallocate(p); // both-note 2{{in call}} break; } return true; } static_assert(mismatched(0, 2)); // both-error {{constant expression}} \ // both-note {{in call to}} static_assert(mismatched(1, 2)); // both-error {{constant expression}} \ // both-note {{in call to}} static_assert(mismatched(2, 0)); // both-error {{constant expression}} \ // both-note {{in call}} static_assert(mismatched(2, 1)); // both-error {{constant expression}} \ // both-note {{in call}} static_assert(mismatched(2, 2)); constexpr bool zeroAlloc() { int *F = std::allocator().allocate(0); std::allocator().deallocate(F); return true; } static_assert(zeroAlloc()); /// FIXME: This is broken in the current interpreter. constexpr int arrayAlloc() { int *F = std::allocator().allocate(2); F[0] = 10; // ref-note {{assignment to object outside its lifetime is not allowed in a constant expression}} F[1] = 13; int Res = F[1] + F[0]; std::allocator().deallocate(F); return Res; } static_assert(arrayAlloc() == 23); // ref-error {{not an integral constant expression}} \ // ref-note {{in call to}} struct S { int i; constexpr S(int i) : i(i) {} constexpr ~S() { } }; /// FIXME: This is broken in the current interpreter. constexpr bool structAlloc() { S *s = std::allocator().allocate(1); s->i = 12; // ref-note {{assignment to object outside its lifetime is not allowed in a constant expression}} bool Res = (s->i == 12); std::allocator().deallocate(s); return Res; } static_assert(structAlloc()); // ref-error {{not an integral constant expression}} \ // ref-note {{in call to}} constexpr bool structAllocArray() { S *s = std::allocator().allocate(9); s[2].i = 12; // ref-note {{assignment to object outside its lifetime is not allowed in a constant expression}} bool Res = (s[2].i == 12); std::allocator().deallocate(s); return Res; } static_assert(structAllocArray()); // ref-error {{not an integral constant expression}} \ // ref-note {{in call to}} constexpr bool alloc_from_user_code() { void *p = __builtin_operator_new(sizeof(int)); // both-note {{cannot allocate untyped memory in a constant expression; use 'std::allocator::allocate'}} __builtin_operator_delete(p); return true; } static_assert(alloc_from_user_code()); // both-error {{constant expression}} \ // both-note {{in call to}} constexpr int no_deallocate_nullptr = (std::allocator().deallocate(nullptr), 1); // both-error {{constant expression}} \ // both-note {{in call}} static_assert((std::allocator().deallocate(std::allocator().allocate(10)), 1) == 1); } namespace Limits { template constexpr T dynarray(int elems, int i) { T *p; if constexpr (sizeof(T) == 1) p = new T[elems]{"fox"}; else p = new T[elems]{1, 2, 3}; T n = p[i]; delete [] p; return n; } static_assert(dynarray(5, 0) == 'f'); #if __LP64__ template struct S { constexpr S(unsigned long long N) : data(nullptr){ data = alloc.allocate(N); // both-note {{in call to 'this->alloc.allocate(18446744073709551615)}} } constexpr T operator[](std::size_t i) const { return data[i]; } constexpr ~S() { alloc.deallocate(data); } std::allocator alloc; T* data; }; constexpr std::size_t s = S(~0UL)[42]; // both-error {{constexpr variable 's' must be initialized by a constant expression}} \ // both-note@#alloc {{cannot allocate array; evaluated array bound 2305843009213693951 is too large}} \ // both-note {{in call to}} #endif } /// Just test that we reject placement-new expressions before C++2c. /// Tests for successful expressions are in placement-new.cpp namespace Placement { consteval auto ok1() { // both-error {{never produces a constant expression}} bool b; new (&b) bool(true); // both-note 2{{this placement new expression is not supported in constant expressions before C++2c}} return b; } static_assert(ok1()); // both-error {{not an integral constant expression}} \ // both-note {{in call to}} /// placement-new should be supported before C++26 in std functions. constexpr int ok2() { int *I = new int; std::construct_at(I); int r = *I; delete I; return r; } static_assert(ok2()== 0); } 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; // both-note {{call to class-specific 'operator delete'}} return true; } static_assert(virt_delete(true)); static_assert(virt_delete(false)); // both-error {{not an integral constant expression}} \ // both-note {{in call to}} #else /// Make sure we reject this prior to C++20 constexpr int a() { // both-error {{never produces a constant expression}} delete new int(12); // both-note 2{{dynamic memory allocation is not permitted in constant expressions until C++20}} return 1; } static_assert(a() == 1, ""); // both-error {{not an integral constant expression}} \ // both-note {{in call to 'a()'}} static_assert(true ? *new int : 4, ""); // both-error {{expression is not an integral constant expression}} \ // both-note {{read of uninitialized object is not allowed in a constant expression}} #endif