mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 12:26:07 +00:00
394 lines
9.2 KiB
C++
394 lines
9.2 KiB
C++
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -fexperimental-new-constant-interpreter -verify=expected,both %s -DBYTECODE
|
|
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -verify=ref,both %s
|
|
|
|
namespace std {
|
|
using size_t = decltype(sizeof(0));
|
|
template<typename T> struct allocator {
|
|
constexpr T *allocate(size_t N) {
|
|
return (T*)operator new(sizeof(T) * N);
|
|
}
|
|
constexpr void deallocate(void *p) {
|
|
operator delete(p);
|
|
}
|
|
};
|
|
template<typename T, typename ...Args>
|
|
constexpr void construct_at(void *p, Args &&...args) {
|
|
new (p) T((Args&&)args...); // both-note {{in call to}} \
|
|
// both-note {{placement new would change type of storage from 'int' to 'float'}} \
|
|
// both-note {{construction of subobject of member 'x' of union with active member 'a' is not allowed in a constant expression}}
|
|
|
|
}
|
|
}
|
|
|
|
void *operator new(std::size_t, void *p) { return p; }
|
|
void* operator new[] (std::size_t, void* p) {return p;}
|
|
|
|
|
|
consteval auto ok1() {
|
|
bool b;
|
|
new (&b) bool(true);
|
|
return b;
|
|
}
|
|
static_assert(ok1());
|
|
|
|
consteval auto ok2() {
|
|
int b;
|
|
new (&b) int(12);
|
|
return b;
|
|
}
|
|
static_assert(ok2() == 12);
|
|
|
|
|
|
consteval auto ok3() {
|
|
float b;
|
|
new (&b) float(12.0);
|
|
return b;
|
|
}
|
|
static_assert(ok3() == 12.0);
|
|
|
|
|
|
consteval auto ok4() {
|
|
_BitInt(11) b;
|
|
new (&b) _BitInt(11)(37);
|
|
return b;
|
|
}
|
|
static_assert(ok4() == 37);
|
|
|
|
consteval int ok5() {
|
|
int i;
|
|
new (&i) int[1]{1};
|
|
|
|
struct S {
|
|
int a; int b;
|
|
} s;
|
|
new (&s) S[1]{{12, 13}};
|
|
|
|
return 25;
|
|
// return s.a + s.b; FIXME: Broken in the current interpreter.
|
|
}
|
|
static_assert(ok5() == 25);
|
|
|
|
/// FIXME: Broken in both interpreters.
|
|
#if 0
|
|
consteval int ok5() {
|
|
int i;
|
|
new (&i) int[1]{1}; // expected-note {{assignment to dereferenced one-past-the-end pointer}}
|
|
return i;
|
|
}
|
|
static_assert(ok5() == 1); // expected-error {{not an integral constant expression}} \
|
|
// expected-note {{in call to}}
|
|
#endif
|
|
|
|
/// FIXME: Crashes the current interpreter.
|
|
#if 0
|
|
consteval int ok6() {
|
|
int i[2];
|
|
new (&i) int(100);
|
|
return i[0];
|
|
}
|
|
static_assert(ok6() == 100);
|
|
#endif
|
|
|
|
consteval int ok6() {
|
|
int i[2];
|
|
new (i) int(100);
|
|
new (i + 1) int(200);
|
|
return i[0] + i[1];
|
|
}
|
|
static_assert(ok6() == 300);
|
|
|
|
|
|
consteval auto fail1() {
|
|
int b;
|
|
new (&b) float(1.0); // both-note {{placement new would change type of storage from 'int' to 'float'}}
|
|
return b;
|
|
}
|
|
static_assert(fail1() == 0); // both-error {{not an integral constant expression}} \
|
|
// both-note {{in call to}}
|
|
|
|
consteval int fail2() {
|
|
int i;
|
|
new (static_cast<void*>(&i)) float(0); // both-note {{placement new would change type of storage from 'int' to 'float'}}
|
|
return 0;
|
|
}
|
|
static_assert(fail2() == 0); // both-error {{not an integral constant expression}} \
|
|
// both-note {{in call to}}
|
|
|
|
consteval int indeterminate() {
|
|
int * indeterminate;
|
|
new (indeterminate) int(0); // both-note {{read of uninitialized object is not allowed in a constant expression}}
|
|
return 0;
|
|
}
|
|
static_assert(indeterminate() == 0); // both-error {{not an integral constant expression}} \
|
|
// both-note {{in call to}}
|
|
|
|
consteval int array1() {
|
|
int i[2];
|
|
new (&i) int[]{1,2};
|
|
return i[0] + i[1];
|
|
}
|
|
static_assert(array1() == 3);
|
|
|
|
consteval int array2() {
|
|
int i[2];
|
|
new (static_cast<void*>(&i)) int[]{1,2};
|
|
return i[0] + i[1];
|
|
}
|
|
static_assert(array2() == 3);
|
|
|
|
consteval int array3() {
|
|
int i[1];
|
|
new (&i) int[2]; // both-note {{placement new would change type of storage from 'int[1]' to 'int[2]'}}
|
|
return 0;
|
|
}
|
|
static_assert(array3() == 0); // both-error {{not an integral constant expression}} \
|
|
// both-note {{in call to}}
|
|
|
|
consteval int array4() {
|
|
int i[2];
|
|
new (&i) int[]{12};
|
|
return i[0];
|
|
}
|
|
static_assert(array4() == 12);
|
|
|
|
constexpr int *intptr() {
|
|
return new int;
|
|
}
|
|
constexpr bool yay() {
|
|
int *ptr = new (intptr()) int(42);
|
|
bool ret = *ptr == 42;
|
|
delete ptr;
|
|
return ret;
|
|
}
|
|
static_assert(yay());
|
|
|
|
|
|
constexpr bool blah() {
|
|
int *ptr = new (intptr()) int[3]{ 1, 2, 3 }; // both-note {{placement new would change type of storage from 'int' to 'int[3]'}}
|
|
bool ret = ptr[0] == 1 && ptr[1] == 2 && ptr[2] == 3;
|
|
delete [] ptr;
|
|
return ret;
|
|
}
|
|
static_assert(blah()); // both-error {{not an integral constant expression}} \
|
|
// both-note {{in call to 'blah()'}}
|
|
|
|
|
|
constexpr int *get_indeterminate() {
|
|
int *evil;
|
|
return evil; // both-note {{read of uninitialized object is not allowed in a constant expression}}
|
|
}
|
|
|
|
constexpr bool bleh() {
|
|
int *ptr = new (get_indeterminate()) int; // both-note {{in call to 'get_indeterminate()'}}
|
|
return true;
|
|
}
|
|
static_assert(bleh()); // both-error {{not an integral constant expression}} \
|
|
// both-note {{in call to 'bleh()'}}
|
|
|
|
namespace records {
|
|
class S {
|
|
public:
|
|
float f;
|
|
};
|
|
|
|
constexpr bool record1() {
|
|
S s(13);
|
|
new (&s) S(42);
|
|
return s.f == 42;
|
|
}
|
|
static_assert(record1());
|
|
|
|
S GlobalS;
|
|
constexpr bool record2() {
|
|
new (&GlobalS) S(42); // both-note {{a constant expression cannot modify an object that is visible outside that expression}}
|
|
return GlobalS.f == 42;
|
|
}
|
|
static_assert(record2()); // both-error {{not an integral constant expression}} \
|
|
// both-note {{in call to}}
|
|
|
|
|
|
constexpr bool record3() {
|
|
S ss[3];
|
|
|
|
new (&ss) S[]{{1}, {2}, {3}};
|
|
|
|
return ss[0].f == 1 && ss[1].f == 2 && ss[2].f == 3;
|
|
}
|
|
static_assert(record3());
|
|
|
|
struct F {
|
|
float f;
|
|
};
|
|
struct R {
|
|
F f;
|
|
int a;
|
|
};
|
|
constexpr bool record4() {
|
|
R r;
|
|
new (&r.f) F{42.0};
|
|
new (&r.a) int(12);
|
|
|
|
return r.f.f == 42.0 && r.a == 12;
|
|
}
|
|
static_assert(record4());
|
|
|
|
/// Destructor is NOT called.
|
|
struct A {
|
|
bool b;
|
|
constexpr ~A() { if (b) throw; }
|
|
};
|
|
|
|
constexpr int foo() {
|
|
A a;
|
|
new (&a) A(true);
|
|
new (&a) A(false);
|
|
return 0;
|
|
}
|
|
static_assert(foo() == 0);
|
|
}
|
|
|
|
namespace ConstructAt {
|
|
struct S {
|
|
int a = 10;
|
|
float b = 1.0;
|
|
};
|
|
|
|
constexpr bool ok1() {
|
|
S s;
|
|
|
|
std::construct_at<S>(&s);
|
|
return s.a == 10 && s.b == 1.0;
|
|
}
|
|
static_assert(ok1());
|
|
|
|
struct S2 {
|
|
constexpr S2() {
|
|
(void)(1/0); // both-note {{division by zero}} \
|
|
// both-warning {{division by zero is undefined}}
|
|
}
|
|
};
|
|
|
|
constexpr bool ctorFail() { //
|
|
S2 *s = std::allocator<S2>().allocate(1);
|
|
std::construct_at<S2>(s); // both-note {{in call to}}
|
|
|
|
return true;
|
|
}
|
|
static_assert(ctorFail()); // both-error {{not an integral constant expression}} \
|
|
// both-note {{in call to 'ctorFail()'}}
|
|
|
|
|
|
constexpr bool bad_construct_at_type() {
|
|
int a;
|
|
std::construct_at<float>(&a, 1.0f); // both-note {{in call to}}
|
|
return true;
|
|
}
|
|
static_assert(bad_construct_at_type()); // both-error {{not an integral constant expression}} \
|
|
// both-note {{in call}}
|
|
|
|
constexpr bool bad_construct_at_subobject() {
|
|
struct X { int a, b; };
|
|
union A {
|
|
int a;
|
|
X x;
|
|
};
|
|
A a = {1};
|
|
std::construct_at<int>(&a.x.a, 1); // both-note {{in call}}
|
|
return true;
|
|
}
|
|
static_assert(bad_construct_at_subobject()); // both-error{{not an integral constant expression}} \
|
|
// both-note {{in call}}
|
|
}
|
|
|
|
namespace UsedToCrash {
|
|
struct S {
|
|
int* i;
|
|
constexpr S() : i(new int(42)) {} // #no-deallocation
|
|
constexpr ~S() {delete i;}
|
|
};
|
|
consteval void alloc() {
|
|
S* s = new S();
|
|
s->~S();
|
|
new (s) S();
|
|
delete s;
|
|
}
|
|
int alloc1 = (alloc(), 0);
|
|
}
|
|
|
|
constexpr bool change_union_member() {
|
|
union U {
|
|
int a;
|
|
int b;
|
|
};
|
|
U u = {.a = 1};
|
|
std::construct_at<int>(&u.b, 2);
|
|
return u.b == 2;
|
|
}
|
|
static_assert(change_union_member());
|
|
|
|
namespace PR48606 {
|
|
struct A { mutable int n = 0; };
|
|
|
|
constexpr bool f() {
|
|
A a;
|
|
A *p = &a;
|
|
p->~A();
|
|
std::construct_at<A>(p);
|
|
return true;
|
|
}
|
|
static_assert(f());
|
|
}
|
|
|
|
/// This used to crash because of an assertion in the implementation
|
|
/// of the This instruction.
|
|
namespace ExplicitThisOnArrayElement {
|
|
struct S {
|
|
int a = 12;
|
|
constexpr S(int a) {
|
|
this->a = a;
|
|
}
|
|
};
|
|
|
|
template <class _Tp, class... _Args>
|
|
constexpr void construct_at(_Tp *__location, _Args &&...__args) {
|
|
new (__location) _Tp(__args...);
|
|
}
|
|
|
|
constexpr bool foo() {
|
|
auto *M = std::allocator<S>().allocate(13); // both-note {{allocation performed here was not deallocated}}
|
|
construct_at(M, 12);
|
|
return true;
|
|
}
|
|
|
|
static_assert(foo()); // both-error {{not an integral constant expression}}
|
|
}
|
|
|
|
#ifdef BYTECODE
|
|
constexpr int N = [] // expected-error {{must be initialized by a constant expression}} \
|
|
// expected-note {{assignment to dereferenced one-past-the-end pointer is not allowed in a constant expression}} \
|
|
// expected-note {{in call to}}
|
|
{
|
|
struct S {
|
|
int a[1];
|
|
};
|
|
S s;
|
|
::new (s.a) int[1][2][3][4]();
|
|
return s.a[0];
|
|
}();
|
|
#endif
|
|
|
|
namespace MemMove {
|
|
constexpr int foo() {
|
|
int *a = std::allocator<int>{}.allocate(1);
|
|
new(a) int{123};
|
|
|
|
int b;
|
|
__builtin_memmove(&b, a, sizeof(int));
|
|
|
|
std::allocator<int>{}.deallocate(a);
|
|
return b;
|
|
}
|
|
|
|
static_assert(foo() == 123);
|
|
}
|