mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 05:16:06 +00:00
[clang][bytecode] Implement placement-new (#107033)
If we have a placement-new destination already, use that instead of allocating a new one. Tests are partially based on `test/SemaCXX/cxx2c-constexpr-placement-new.cpp`.
This commit is contained in:
parent
26f272ebbd
commit
c712ab829b
@ -3097,12 +3097,11 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
|
||||
QualType ElementType = E->getAllocatedType();
|
||||
std::optional<PrimType> ElemT = classify(ElementType);
|
||||
unsigned PlacementArgs = E->getNumPlacementArgs();
|
||||
const FunctionDecl *OperatorNew = E->getOperatorNew();
|
||||
const Expr *PlacementDest = nullptr;
|
||||
bool IsNoThrow = false;
|
||||
|
||||
// FIXME: Better diagnostic. diag::note_constexpr_new_placement
|
||||
if (PlacementArgs != 0) {
|
||||
// The only new-placement list we support is of the form (std::nothrow).
|
||||
//
|
||||
// FIXME: There is no restriction on this, but it's not clear that any
|
||||
// other form makes any sense. We get here for cases such as:
|
||||
//
|
||||
@ -3111,27 +3110,43 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
|
||||
// (which should presumably be valid only if N is a multiple of
|
||||
// alignof(int), and in any case can't be deallocated unless N is
|
||||
// alignof(X) and X has new-extended alignment).
|
||||
if (PlacementArgs != 1 || !E->getPlacementArg(0)->getType()->isNothrowT())
|
||||
return this->emitInvalid(E);
|
||||
if (PlacementArgs == 1) {
|
||||
const Expr *Arg1 = E->getPlacementArg(0);
|
||||
if (Arg1->getType()->isNothrowT()) {
|
||||
if (!this->discard(Arg1))
|
||||
return false;
|
||||
IsNoThrow = true;
|
||||
} else if (Ctx.getLangOpts().CPlusPlus26 &&
|
||||
OperatorNew->isReservedGlobalPlacementOperator()) {
|
||||
// If we have a placement-new destination, we'll later use that instead
|
||||
// of allocating.
|
||||
PlacementDest = Arg1;
|
||||
} else {
|
||||
return this->emitInvalidNewDeleteExpr(E, E);
|
||||
}
|
||||
|
||||
if (!this->discard(E->getPlacementArg(0)))
|
||||
return false;
|
||||
IsNoThrow = true;
|
||||
} else {
|
||||
return this->emitInvalid(E);
|
||||
}
|
||||
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
|
||||
return this->emitInvalidNewDeleteExpr(E, E);
|
||||
}
|
||||
|
||||
const Descriptor *Desc;
|
||||
if (ElemT) {
|
||||
if (E->isArray())
|
||||
Desc = nullptr; // We're not going to use it in this case.
|
||||
else
|
||||
Desc = P.createDescriptor(E, *ElemT, Descriptor::InlineDescMD,
|
||||
/*IsConst=*/false, /*IsTemporary=*/false,
|
||||
/*IsMutable=*/false);
|
||||
} else {
|
||||
Desc = P.createDescriptor(
|
||||
E, ElementType.getTypePtr(),
|
||||
E->isArray() ? std::nullopt : Descriptor::InlineDescMD,
|
||||
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false, Init);
|
||||
if (!PlacementDest) {
|
||||
if (ElemT) {
|
||||
if (E->isArray())
|
||||
Desc = nullptr; // We're not going to use it in this case.
|
||||
else
|
||||
Desc = P.createDescriptor(E, *ElemT, Descriptor::InlineDescMD,
|
||||
/*IsConst=*/false, /*IsTemporary=*/false,
|
||||
/*IsMutable=*/false);
|
||||
} else {
|
||||
Desc = P.createDescriptor(
|
||||
E, ElementType.getTypePtr(),
|
||||
E->isArray() ? std::nullopt : Descriptor::InlineDescMD,
|
||||
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false, Init);
|
||||
}
|
||||
}
|
||||
|
||||
if (E->isArray()) {
|
||||
@ -3148,26 +3163,42 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
|
||||
|
||||
PrimType SizeT = classifyPrim(Stripped->getType());
|
||||
|
||||
if (!this->visit(Stripped))
|
||||
return false;
|
||||
|
||||
if (ElemT) {
|
||||
// N primitive elements.
|
||||
if (!this->emitAllocN(SizeT, *ElemT, E, IsNoThrow, E))
|
||||
if (PlacementDest) {
|
||||
if (!this->visit(PlacementDest))
|
||||
return false;
|
||||
if (!this->visit(Stripped))
|
||||
return false;
|
||||
if (!this->emitCheckNewTypeMismatchArray(SizeT, E, E))
|
||||
return false;
|
||||
} else {
|
||||
// N Composite elements.
|
||||
if (!this->emitAllocCN(SizeT, Desc, IsNoThrow, E))
|
||||
if (!this->visit(Stripped))
|
||||
return false;
|
||||
|
||||
if (ElemT) {
|
||||
// N primitive elements.
|
||||
if (!this->emitAllocN(SizeT, *ElemT, E, IsNoThrow, E))
|
||||
return false;
|
||||
} else {
|
||||
// N Composite elements.
|
||||
if (!this->emitAllocCN(SizeT, Desc, IsNoThrow, E))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Init && !this->visitInitializer(Init))
|
||||
return false;
|
||||
|
||||
} else {
|
||||
// Allocate just one element.
|
||||
if (!this->emitAlloc(Desc, E))
|
||||
return false;
|
||||
if (PlacementDest) {
|
||||
if (!this->visit(PlacementDest))
|
||||
return false;
|
||||
if (!this->emitCheckNewTypeMismatch(E, E))
|
||||
return false;
|
||||
} else {
|
||||
// Allocate just one element.
|
||||
if (!this->emitAlloc(Desc, E))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Init) {
|
||||
if (ElemT) {
|
||||
@ -3194,6 +3225,11 @@ template <class Emitter>
|
||||
bool Compiler<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
|
||||
const Expr *Arg = E->getArgument();
|
||||
|
||||
const FunctionDecl *OperatorDelete = E->getOperatorDelete();
|
||||
|
||||
if (!OperatorDelete->isReplaceableGlobalAllocationFunction())
|
||||
return this->emitInvalidNewDeleteExpr(E, E);
|
||||
|
||||
// Arg must be an lvalue.
|
||||
if (!this->visit(Arg))
|
||||
return false;
|
||||
|
@ -1286,6 +1286,80 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
|
||||
return Call(S, OpPC, F, VarArgSize);
|
||||
}
|
||||
|
||||
bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
|
||||
std::optional<uint64_t> ArraySize) {
|
||||
const Pointer &Ptr = S.Stk.peek<Pointer>();
|
||||
|
||||
if (!CheckStore(S, OpPC, Ptr))
|
||||
return false;
|
||||
|
||||
const auto *NewExpr = cast<CXXNewExpr>(E);
|
||||
QualType StorageType = Ptr.getType();
|
||||
|
||||
if (isa_and_nonnull<CXXNewExpr>(Ptr.getFieldDesc()->asExpr())) {
|
||||
// FIXME: Are there other cases where this is a problem?
|
||||
StorageType = StorageType->getPointeeType();
|
||||
}
|
||||
|
||||
const ASTContext &ASTCtx = S.getASTContext();
|
||||
QualType AllocType;
|
||||
if (ArraySize) {
|
||||
AllocType = ASTCtx.getConstantArrayType(
|
||||
NewExpr->getAllocatedType(),
|
||||
APInt(64, static_cast<uint64_t>(*ArraySize), false), nullptr,
|
||||
ArraySizeModifier::Normal, 0);
|
||||
} else {
|
||||
AllocType = NewExpr->getAllocatedType();
|
||||
}
|
||||
|
||||
unsigned StorageSize = 1;
|
||||
unsigned AllocSize = 1;
|
||||
if (const auto *CAT = dyn_cast<ConstantArrayType>(AllocType))
|
||||
AllocSize = CAT->getZExtSize();
|
||||
if (const auto *CAT = dyn_cast<ConstantArrayType>(StorageType))
|
||||
StorageSize = CAT->getZExtSize();
|
||||
|
||||
if (AllocSize > StorageSize ||
|
||||
!ASTCtx.hasSimilarType(ASTCtx.getBaseElementType(AllocType),
|
||||
ASTCtx.getBaseElementType(StorageType))) {
|
||||
S.FFDiag(S.Current->getLocation(OpPC),
|
||||
diag::note_constexpr_placement_new_wrong_type)
|
||||
<< StorageType << AllocType;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
|
||||
assert(E);
|
||||
const auto &Loc = S.Current->getSource(OpPC);
|
||||
|
||||
if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) {
|
||||
const FunctionDecl *OperatorNew = NewExpr->getOperatorNew();
|
||||
|
||||
if (!S.getLangOpts().CPlusPlus26 && NewExpr->getNumPlacementArgs() > 0) {
|
||||
S.FFDiag(Loc, diag::note_constexpr_new_placement)
|
||||
<< /*C++26 feature*/ 1 << E->getSourceRange();
|
||||
} else if (NewExpr->getNumPlacementArgs() == 1 &&
|
||||
!OperatorNew->isReservedGlobalPlacementOperator()) {
|
||||
S.FFDiag(Loc, diag::note_constexpr_new_placement)
|
||||
<< /*Unsupported*/ 0 << E->getSourceRange();
|
||||
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
|
||||
S.FFDiag(Loc, diag::note_constexpr_new_non_replaceable)
|
||||
<< isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
|
||||
}
|
||||
} else {
|
||||
const auto *DeleteExpr = cast<CXXDeleteExpr>(E);
|
||||
const FunctionDecl *OperatorDelete = DeleteExpr->getOperatorDelete();
|
||||
if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) {
|
||||
S.FFDiag(Loc, diag::note_constexpr_new_non_replaceable)
|
||||
<< isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Interpret(InterpState &S, APValue &Result) {
|
||||
// The current stack frame when we started Interpret().
|
||||
// This is being used by the ops to determine wheter
|
||||
|
@ -2947,6 +2947,17 @@ static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Check if the initializer and storage types of a placement-new expression
|
||||
/// match.
|
||||
bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
|
||||
std::optional<uint64_t> ArraySize = std::nullopt);
|
||||
|
||||
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
||||
bool CheckNewTypeMismatchArray(InterpState &S, CodePtr OpPC, const Expr *E) {
|
||||
const auto &Size = S.Stk.pop<T>();
|
||||
return CheckNewTypeMismatch(S, OpPC, E, static_cast<uint64_t>(Size));
|
||||
}
|
||||
bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E);
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Read opcode arguments
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -787,4 +787,18 @@ def Free : Opcode {
|
||||
let Args = [ArgBool];
|
||||
}
|
||||
|
||||
def CheckNewTypeMismatch : Opcode {
|
||||
let Args = [ArgExpr];
|
||||
}
|
||||
|
||||
def InvalidNewDeleteExpr : Opcode {
|
||||
let Args = [ArgExpr];
|
||||
}
|
||||
|
||||
def CheckNewTypeMismatchArray : Opcode {
|
||||
let Types = [IntegerTypeClass];
|
||||
let Args = [ArgExpr];
|
||||
let HasGroup = 1;
|
||||
}
|
||||
|
||||
def IsConstantContext: Opcode;
|
||||
|
@ -241,12 +241,10 @@ namespace std {
|
||||
|
||||
|
||||
|
||||
/// FIXME: The new interpreter produces the wrong diagnostic.
|
||||
namespace PlacementNew {
|
||||
constexpr int foo() { // both-error {{never produces a constant expression}}
|
||||
char c[sizeof(int)];
|
||||
new (c) int{12}; // ref-note {{this placement new expression is not supported in constant expressions before C++2c}} \
|
||||
// expected-note {{subexpression not valid in a constant expression}}
|
||||
new (c) int{12}; // both-note {{this placement new expression is not supported in constant expressions before C++2c}}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -305,31 +303,28 @@ namespace placement_new_delete {
|
||||
}
|
||||
static_assert(ok());
|
||||
|
||||
/// FIXME: Diagnosting placement new.
|
||||
constexpr bool bad(int which) {
|
||||
switch (which) {
|
||||
case 0:
|
||||
delete new (placement_new_arg{}) int; // ref-note {{this placement new expression is not supported in constant expressions}} \
|
||||
// expected-note {{subexpression not valid in a constant expression}}
|
||||
delete new (placement_new_arg{}) int; // both-note {{this placement new expression is not supported in constant expressions}}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
delete new ClassSpecificNew; // ref-note {{call to class-specific 'operator new'}}
|
||||
delete new ClassSpecificNew; // both-note {{call to class-specific 'operator new'}}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
delete new ClassSpecificDelete; // ref-note {{call to class-specific 'operator delete'}}
|
||||
delete new ClassSpecificDelete; // both-note {{call to class-specific 'operator delete'}}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
delete new DestroyingDelete; // ref-note {{call to class-specific 'operator delete'}}
|
||||
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; // ref-note {{this placement new expression is not supported in constant expressions}} \
|
||||
// expected-note {{subexpression not valid in a constant expression}}
|
||||
delete new (std::align_val_t{64}) Overaligned; // both-note {{this placement new expression is not supported in constant expressions}}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -337,9 +332,9 @@ namespace placement_new_delete {
|
||||
}
|
||||
static_assert(bad(0)); // both-error {{constant expression}} \
|
||||
// both-note {{in call}}
|
||||
static_assert(bad(1)); // ref-error {{constant expression}} ref-note {{in call}}
|
||||
static_assert(bad(2)); // ref-error {{constant expression}} ref-note {{in call}}
|
||||
static_assert(bad(3)); // ref-error {{constant expression}} ref-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}}
|
||||
}
|
||||
@ -586,7 +581,6 @@ constexpr void use_after_free_2() { // both-error {{never produces a constant ex
|
||||
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));
|
||||
@ -758,6 +752,18 @@ namespace Limits {
|
||||
#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}}
|
||||
}
|
||||
|
||||
#else
|
||||
/// Make sure we reject this prior to C++20
|
||||
constexpr int a() { // both-error {{never produces a constant expression}}
|
||||
|
219
clang/test/AST/ByteCode/placement-new.cpp
Normal file
219
clang/test/AST/ByteCode/placement-new.cpp
Normal file
@ -0,0 +1,219 @@
|
||||
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -fexperimental-new-constant-interpreter -verify=expected,both %s
|
||||
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -verify=ref,both %s
|
||||
|
||||
namespace std {
|
||||
using size_t = decltype(sizeof(0));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/// 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user