Skip to content

Commit 36eb12a

Browse files
committed
[Clang] Implement P2747 constexpr placement new
In C++26 and as an extension in C++20
1 parent d867988 commit 36eb12a

12 files changed

+151
-34
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,7 @@ Attributes on Structured Bindings __cpp_structured_bindings C+
15061506
Pack Indexing __cpp_pack_indexing C++26 C++03
15071507
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
15081508
Variadic Friends __cpp_variadic_friend C++26 C++03
1509+
``constexpr`` placement new __cpp_constexpr C++26 C++20
15091510
-------------------------------------------- -------------------------------- ------------- -------------
15101511
Designated initializers (N494) C99 C89
15111512
Array & element qualification (N2607) C23 C89

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ C++ Specific Potentially Breaking Changes
7373
template <> // error: extraneous template head
7474
template <typename T>
7575
void f();
76-
76+
7777
ABI Changes in This Version
7878
---------------------------
7979

@@ -131,6 +131,9 @@ C++2c Feature Support
131131

132132
- Implemented `P2893R3 Variadic Friends <https://wg21.link/P2893>`_
133133

134+
- Implemented `P2747R2 constexpr placement new <https://wg21.link/P2747R2>`_.
135+
136+
134137
Resolutions to C++ Defect Reports
135138
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
136139

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ def note_constexpr_new_non_replaceable : Note<
335335
def note_constexpr_new_placement : Note<
336336
"this placement new expression is not yet supported in constant expressions">;
337337
def note_constexpr_placement_new_wrong_type : Note<
338-
"placement new would change type of storage from %0 to %1">;
338+
"placement new would change type of storage from %0 to %1">;
339339
def note_constexpr_new_negative : Note<
340340
"cannot allocate array; evaluated array bound %0 is negative">;
341341
def note_constexpr_new_too_large : Note<

clang/lib/AST/ExprConstant.cpp

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6691,7 +6691,7 @@ static bool HandleDestructionImpl(EvalInfo &Info, SourceRange CallRange,
66916691
if (Size && Size > Value.getArrayInitializedElts())
66926692
expandArray(Value, Value.getArraySize() - 1);
66936693

6694-
for (; Size != 0; --Size) {
6694+
for (Size = Value.getArraySize(); Size != 0; --Size) {
66956695
APValue &Elem = Value.getArrayInitializedElt(Size - 1);
66966696
if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1) ||
66976697
!HandleDestructionImpl(Info, CallRange, ElemLV, Elem, ElemT))
@@ -10003,23 +10003,14 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
1000310003
return false;
1000410004

1000510005
FunctionDecl *OperatorNew = E->getOperatorNew();
10006+
QualType AllocType = E->getAllocatedType();
10007+
QualType TargetType = AllocType;
1000610008

1000710009
bool IsNothrow = false;
1000810010
bool IsPlacement = false;
10009-
if (OperatorNew->isReservedGlobalPlacementOperator() &&
10010-
Info.CurrentCall->isStdFunction() && !E->isArray()) {
10011-
// FIXME Support array placement new.
10012-
assert(E->getNumPlacementArgs() == 1);
10013-
if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
10014-
return false;
10015-
if (Result.Designator.Invalid)
10016-
return false;
10017-
IsPlacement = true;
10018-
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
10019-
Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
10020-
<< isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
10021-
return false;
10022-
} else if (E->getNumPlacementArgs()) {
10011+
10012+
if (E->getNumPlacementArgs() && E->getNumPlacementArgs() == 1 &&
10013+
E->getPlacementArg(0)->getType()->isNothrowT()) {
1002310014
// The only new-placement list we support is of the form (std::nothrow).
1002410015
//
1002510016
// FIXME: There is no restriction on this, but it's not clear that any
@@ -10030,22 +10021,32 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
1003010021
// (which should presumably be valid only if N is a multiple of
1003110022
// alignof(int), and in any case can't be deallocated unless N is
1003210023
// alignof(X) and X has new-extended alignment).
10033-
if (E->getNumPlacementArgs() != 1 ||
10034-
!E->getPlacementArg(0)->getType()->isNothrowT())
10035-
return Error(E, diag::note_constexpr_new_placement);
10036-
1003710024
LValue Nothrow;
1003810025
if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info))
1003910026
return false;
1004010027
IsNothrow = true;
10028+
} else if (OperatorNew->isReservedGlobalPlacementOperator()) {
10029+
if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
10030+
return false;
10031+
if (Result.Designator.Invalid)
10032+
return false;
10033+
/// if(!lifetimeStartedInEvaluation(Info, Result.getLValueBase()))
10034+
// return false;
10035+
TargetType = E->getPlacementArg(0)->getType();
10036+
IsPlacement = true;
10037+
} else if (E->getNumPlacementArgs()) {
10038+
return Error(E, diag::note_constexpr_new_placement);
10039+
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
10040+
Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
10041+
<< isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
10042+
return false;
1004110043
}
1004210044

1004310045
const Expr *Init = E->getInitializer();
1004410046
const InitListExpr *ResizedArrayILE = nullptr;
1004510047
const CXXConstructExpr *ResizedArrayCCE = nullptr;
1004610048
bool ValueInit = false;
1004710049

10048-
QualType AllocType = E->getAllocatedType();
1004910050
if (std::optional<const Expr *> ArraySize = E->getArraySize()) {
1005010051
const Expr *Stripped = *ArraySize;
1005110052
for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped);
@@ -10139,9 +10140,17 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
1013910140
bool found(APValue &Subobj, QualType SubobjType) {
1014010141
// FIXME: Reject the cases where [basic.life]p8 would not permit the
1014110142
// old name of the object to be used to name the new object.
10142-
if (!Info.Ctx.hasSameUnqualifiedType(SubobjType, AllocType)) {
10143-
Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type) <<
10144-
SubobjType << AllocType;
10143+
unsigned SubobjectSize = 1;
10144+
unsigned AllocSize = 1;
10145+
if (auto *CAT = dyn_cast<ConstantArrayType>(AllocType))
10146+
AllocSize = CAT->getZExtSize();
10147+
if (auto *CAT = dyn_cast<ConstantArrayType>(SubobjType))
10148+
SubobjectSize = CAT->getZExtSize();
10149+
if (SubobjectSize < AllocSize ||
10150+
!Info.Ctx.hasSimilarType(Info.Ctx.getBaseElementType(SubobjType),
10151+
Info.Ctx.getBaseElementType(AllocType))) {
10152+
Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type)
10153+
<< SubobjType << AllocType;
1014510154
return false;
1014610155
}
1014710156
Value = &Subobj;

clang/lib/Frontend/InitPreprocessor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
660660
Builder.defineMacro("__cpp_unicode_literals", "200710L");
661661
Builder.defineMacro("__cpp_user_defined_literals", "200809L");
662662
Builder.defineMacro("__cpp_lambdas", "200907L");
663-
Builder.defineMacro("__cpp_constexpr", LangOpts.CPlusPlus26 ? "202306L"
663+
Builder.defineMacro("__cpp_constexpr", LangOpts.CPlusPlus26 ? "202406L"
664664
: LangOpts.CPlusPlus23 ? "202211L"
665665
: LangOpts.CPlusPlus20 ? "201907L"
666666
: LangOpts.CPlusPlus17 ? "201603L"

clang/test/AST/Interp/new-delete.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ namespace std {
245245
namespace PlacementNew {
246246
constexpr int foo() { // both-error {{never produces a constant expression}}
247247
char c[sizeof(int)];
248-
new (c) int{12}; // ref-note {{call to placement 'operator new'}} \
248+
new (c) int{12}; // ref-note {{placement new would change type of storage from 'char' to 'int'}} \
249249
// expected-note {{subexpression not valid in a constant expression}}
250250
return 0;
251251
}
@@ -309,7 +309,7 @@ namespace placement_new_delete {
309309
constexpr bool bad(int which) {
310310
switch (which) {
311311
case 0:
312-
delete new (placement_new_arg{}) int; // ref-note {{call to placement 'operator new'}} \
312+
delete new (placement_new_arg{}) int; // ref-note {{this placement new expression is not yet supported in constant expression}} \
313313
// expected-note {{subexpression not valid in a constant expression}}
314314
break;
315315

clang/test/CXX/drs/cwg29xx.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,29 @@ struct S {
2323
friend class C<Ts>::Nested...; // expected-error {{friend declaration expands pack 'Ts' that is declared it its own template parameter list}}
2424
};
2525
} // namespace cwg2917
26+
27+
#if __cplusplus >= 202002
28+
29+
namespace std {
30+
using size_t = decltype(sizeof(0));
31+
};
32+
void *operator new(std::size_t, void *p) { return p; }
33+
void* operator new[] (std::size_t, void* p) {return p;}
34+
35+
36+
namespace cwg2922 { // cwg2922: 20 open 2024-07-10
37+
union U { int a, b; };
38+
constexpr U nondeterministic(bool i) {
39+
if(i) {
40+
U u;
41+
new (&u) int();
42+
// expected-note@-1 {{placement new would change type of storage from 'U' to 'int'}}
43+
return u;
44+
}
45+
return {};
46+
}
47+
constexpr U _ = nondeterministic(true);
48+
// expected-error@-1 {{constexpr variable '_' must be initialized by a constant expression}} \
49+
// expected-note@-1 {{in call to 'nondeterministic(true)'}}
50+
}
51+
#endif

clang/test/Lexer/cxx-features.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@
317317
#error "wrong value for __cpp_lambdas"
318318
#endif
319319

320-
#if check(constexpr, 0, 200704, 201304, 201603, 201907, 202211, 202306)
320+
#if check(constexpr, 0, 200704, 201304, 201603, 201907, 202211, 202406L)
321321
#error "wrong value for __cpp_constexpr"
322322
#endif
323323

clang/test/SemaCXX/constant-expression-cxx2a.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,7 @@ namespace placement_new_delete {
994994
constexpr bool bad(int which) {
995995
switch (which) {
996996
case 0:
997-
delete new (placement_new_arg{}) int; // expected-note {{call to placement 'operator new'}}
997+
delete new (placement_new_arg{}) int; // expected-note {{this placement new expression is not yet supported in constant expressions}}
998998
break;
999999

10001000
case 1:

clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,10 @@ constexpr int no_deallocate_nonalloc = (std::allocator<int>().deallocate((int*)&
9090
// expected-note@-2 {{declared here}}
9191

9292
void *operator new(std::size_t, void *p) { return p; }
93-
constexpr bool no_placement_new_in_user_code() { // expected-error {{never produces a constant expression}}
93+
void* operator new[] (std::size_t, void* p) {return p;}
94+
constexpr bool no_placement_new_in_user_code() {
9495
int a;
95-
new (&a) int(42); // expected-note {{call to placement 'operator new'}}
96+
new (&a) int(42);
9697
return a == 42;
9798
}
9899

@@ -239,3 +240,80 @@ void f() {
239240
}
240241

241242
}
243+
244+
namespace placement_new {
245+
246+
template <typename T, typename U>
247+
constexpr void f(T* t, U u) {
248+
new (t) U(u);
249+
}
250+
251+
consteval int ok() {
252+
int i;
253+
new (&i) int(0);
254+
new (&i) int[1]{1};
255+
new (static_cast<void*>(&i)) int(0);
256+
return 0;
257+
}
258+
259+
consteval int conversion() { // expected-error {{consteval function never produces a constant expression}}
260+
int i;
261+
new (static_cast<void*>(&i)) float(0);
262+
// expected-note@-1 {{placement new would change type of storage from 'int' to 'float'}}
263+
return 0;
264+
}
265+
266+
consteval int indeterminate() {
267+
int * indeterminate;
268+
new (indeterminate) int(0);
269+
// expected-note@-1 {{read of uninitialized object is not allowed in a constant expression}}
270+
return 0;
271+
}
272+
273+
consteval int array1() {
274+
int i[2];
275+
new (&i) int[]{1,2};
276+
new (&i) int[]{1};
277+
new (&i) int(0);
278+
new (static_cast<void*>(&i)) int[]{1,2};
279+
new (static_cast<void*>(&i)) int[]{1};
280+
return 0;
281+
}
282+
283+
consteval int array2() { // expected-error {{consteval function never produces a constant expression}}
284+
int i[1];
285+
new (&i) int[2];
286+
//expected-note@-1 {{placement new would change type of storage from 'int[1]' to 'int[2]'}}
287+
return 0;
288+
}
289+
290+
struct S{
291+
int* i;
292+
constexpr S() : i(new int(42)) {} // expected-note {{allocation performed here was not deallocated}}
293+
constexpr ~S() {delete i;}
294+
};
295+
296+
consteval void alloc() {
297+
S* s = new S();
298+
s->~S();
299+
new (s) S();
300+
delete s;
301+
}
302+
303+
304+
consteval void alloc_err() {
305+
S* s = new S();
306+
new (s) S();
307+
delete s;
308+
}
309+
310+
311+
312+
int a = ok();
313+
int c = indeterminate(); // expected-error {{call to consteval function 'placement_new::indeterminate' is not a constant expression}} \
314+
// expected-note {{in call to 'indeterminate()'}}
315+
int d = array1();
316+
int alloc1 = (alloc(), 0);
317+
int alloc2 = (alloc_err(), 0); // expected-error {{call to consteval function 'placement_new::alloc_err' is not a constant expression}}
318+
319+
}

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17348,7 +17348,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1734817348
<td><a href="https://cplusplus.github.io/CWG/issues/2922.html">2922</a></td>
1734917349
<td>open</td>
1735017350
<td>constexpr placement-new is too permissive</td>
17351-
<td align="center">Not resolved</td>
17351+
<td title="Clang 20 implements 2024-07-10 resolution" align="center">Not Resolved*</td>
1735217352
</tr></table>
1735317353

1735417354
</div>

clang/www/cxx_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
208208
<tr>
209209
<td><tt>constexpr</tt> placement new</td>
210210
<td><a href="https://wg21.link/P2747R2">P2747R2</a></td>
211-
<td class="none" align="center">No</td>
211+
<td class="unreleased" align="center">Clang 20</td>
212212
</tr>
213213
<tr>
214214
<td>Deleting a Pointer to an Incomplete Type Should be Ill-formed</td>

0 commit comments

Comments
 (0)