[clang] [Clang] Implement P2747 constexpr placement new (PR #104586)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 23 06:35:25 PDT 2024
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/104586
>From 67201d1d2e4d092cf8712b1e1b63f2eceaf8d57c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 9 Jul 2024 08:37:18 +0200
Subject: [PATCH 1/6] [Clang] Implement P2747 constexpr placement new
In C++26 and as an extension in C++20
---
clang/docs/LanguageExtensions.rst | 1 +
clang/docs/ReleaseNotes.rst | 3 +
.../include/clang/Basic/DiagnosticASTKinds.td | 2 +-
clang/lib/AST/ExprConstant.cpp | 55 +++++++------
clang/lib/Frontend/InitPreprocessor.cpp | 2 +-
clang/test/AST/ByteCode/new-delete.cpp | 4 +-
clang/test/CXX/drs/cwg29xx.cpp | 26 ++++++
clang/test/Lexer/cxx-features.cpp | 2 +-
.../SemaCXX/constant-expression-cxx2a.cpp | 2 +-
.../test/SemaCXX/cxx2a-constexpr-dynalloc.cpp | 82 ++++++++++++++++++-
clang/www/cxx_dr_status.html | 2 +-
clang/www/cxx_status.html | 2 +-
12 files changed, 150 insertions(+), 33 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 62903fc3744cad..23fe23208763b8 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1506,6 +1506,7 @@ Attributes on Structured Bindings __cpp_structured_bindings C+
Pack Indexing __cpp_pack_indexing C++26 C++03
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
Variadic Friends __cpp_variadic_friend C++26 C++03
+``constexpr`` placement new __cpp_constexpr C++26 C++20
-------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8f98167dff31ef..0852ac91ba4947 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -131,6 +131,9 @@ C++2c Feature Support
- Implemented `P2893R3 Variadic Friends <https://wg21.link/P2893>`_
+- Implemented `P2747R2 constexpr placement new <https://wg21.link/P2747R2>`_.
+
+
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index f317c5ac44f32b..569d2cc20a526c 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -335,7 +335,7 @@ def note_constexpr_new_non_replaceable : Note<
def note_constexpr_new_placement : Note<
"this placement new expression is not yet supported in constant expressions">;
def note_constexpr_placement_new_wrong_type : Note<
- "placement new would change type of storage from %0 to %1">;
+ "placement new would change type of storage from %0 to %1">;
def note_constexpr_new_negative : Note<
"cannot allocate array; evaluated array bound %0 is negative">;
def note_constexpr_new_too_large : Note<
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 5540f58b526705..3df963c554a995 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -6691,7 +6691,7 @@ static bool HandleDestructionImpl(EvalInfo &Info, SourceRange CallRange,
if (Size && Size > Value.getArrayInitializedElts())
expandArray(Value, Value.getArraySize() - 1);
- for (; Size != 0; --Size) {
+ for (Size = Value.getArraySize(); Size != 0; --Size) {
APValue &Elem = Value.getArrayInitializedElt(Size - 1);
if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1) ||
!HandleDestructionImpl(Info, CallRange, ElemLV, Elem, ElemT))
@@ -10003,23 +10003,14 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
return false;
FunctionDecl *OperatorNew = E->getOperatorNew();
+ QualType AllocType = E->getAllocatedType();
+ QualType TargetType = AllocType;
bool IsNothrow = false;
bool IsPlacement = false;
- if (OperatorNew->isReservedGlobalPlacementOperator() &&
- Info.CurrentCall->isStdFunction() && !E->isArray()) {
- // FIXME Support array placement new.
- assert(E->getNumPlacementArgs() == 1);
- if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
- return false;
- if (Result.Designator.Invalid)
- return false;
- IsPlacement = true;
- } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
- Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
- << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
- return false;
- } else if (E->getNumPlacementArgs()) {
+
+ if (E->getNumPlacementArgs() && E->getNumPlacementArgs() == 1 &&
+ E->getPlacementArg(0)->getType()->isNothrowT()) {
// 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
@@ -10030,14 +10021,25 @@ bool PointerExprEvaluator::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 (E->getNumPlacementArgs() != 1 ||
- !E->getPlacementArg(0)->getType()->isNothrowT())
- return Error(E, diag::note_constexpr_new_placement);
-
LValue Nothrow;
if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info))
return false;
IsNothrow = true;
+ } else if (OperatorNew->isReservedGlobalPlacementOperator()) {
+ if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
+ return false;
+ if (Result.Designator.Invalid)
+ return false;
+ /// if(!lifetimeStartedInEvaluation(Info, Result.getLValueBase()))
+ // return false;
+ TargetType = E->getPlacementArg(0)->getType();
+ IsPlacement = true;
+ } else if (E->getNumPlacementArgs()) {
+ return Error(E, diag::note_constexpr_new_placement);
+ } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
+ Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
+ << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
+ return false;
}
const Expr *Init = E->getInitializer();
@@ -10045,7 +10047,6 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
const CXXConstructExpr *ResizedArrayCCE = nullptr;
bool ValueInit = false;
- QualType AllocType = E->getAllocatedType();
if (std::optional<const Expr *> ArraySize = E->getArraySize()) {
const Expr *Stripped = *ArraySize;
for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped);
@@ -10139,9 +10140,17 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
bool found(APValue &Subobj, QualType SubobjType) {
// FIXME: Reject the cases where [basic.life]p8 would not permit the
// old name of the object to be used to name the new object.
- if (!Info.Ctx.hasSameUnqualifiedType(SubobjType, AllocType)) {
- Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type) <<
- SubobjType << AllocType;
+ unsigned SubobjectSize = 1;
+ unsigned AllocSize = 1;
+ if (auto *CAT = dyn_cast<ConstantArrayType>(AllocType))
+ AllocSize = CAT->getZExtSize();
+ if (auto *CAT = dyn_cast<ConstantArrayType>(SubobjType))
+ SubobjectSize = CAT->getZExtSize();
+ if (SubobjectSize < AllocSize ||
+ !Info.Ctx.hasSimilarType(Info.Ctx.getBaseElementType(SubobjType),
+ Info.Ctx.getBaseElementType(AllocType))) {
+ Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type)
+ << SubobjType << AllocType;
return false;
}
Value = &Subobj;
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 4f2856dd2247f8..61260a3379828d 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -660,7 +660,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_unicode_literals", "200710L");
Builder.defineMacro("__cpp_user_defined_literals", "200809L");
Builder.defineMacro("__cpp_lambdas", "200907L");
- Builder.defineMacro("__cpp_constexpr", LangOpts.CPlusPlus26 ? "202306L"
+ Builder.defineMacro("__cpp_constexpr", LangOpts.CPlusPlus26 ? "202406L"
: LangOpts.CPlusPlus23 ? "202211L"
: LangOpts.CPlusPlus20 ? "201907L"
: LangOpts.CPlusPlus17 ? "201603L"
diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index 6bb30bc19f110c..0c823e7000f853 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -245,7 +245,7 @@ namespace std {
namespace PlacementNew {
constexpr int foo() { // both-error {{never produces a constant expression}}
char c[sizeof(int)];
- new (c) int{12}; // ref-note {{call to placement 'operator new'}} \
+ new (c) int{12}; // ref-note {{placement new would change type of storage from 'char' to 'int'}} \
// expected-note {{subexpression not valid in a constant expression}}
return 0;
}
@@ -309,7 +309,7 @@ namespace placement_new_delete {
constexpr bool bad(int which) {
switch (which) {
case 0:
- delete new (placement_new_arg{}) int; // ref-note {{call to placement 'operator new'}} \
+ delete new (placement_new_arg{}) int; // ref-note {{this placement new expression is not yet supported in constant expression}} \
// expected-note {{subexpression not valid in a constant expression}}
break;
diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp
index 8cac9f283980b6..7e04aa0c3a4942 100644
--- a/clang/test/CXX/drs/cwg29xx.cpp
+++ b/clang/test/CXX/drs/cwg29xx.cpp
@@ -23,3 +23,29 @@ struct S {
friend class C<Ts>::Nested...; // expected-error {{friend declaration expands pack 'Ts' that is declared it its own template parameter list}}
};
} // namespace cwg2917
+
+#if __cplusplus >= 202002
+
+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;}
+
+
+namespace cwg2922 { // cwg2922: 20 open 2024-07-10
+union U { int a, b; };
+constexpr U nondeterministic(bool i) {
+ if(i) {
+ U u;
+ new (&u) int();
+ // expected-note at -1 {{placement new would change type of storage from 'U' to 'int'}}
+ return u;
+ }
+ return {};
+}
+constexpr U _ = nondeterministic(true);
+// expected-error at -1 {{constexpr variable '_' must be initialized by a constant expression}} \
+// expected-note at -1 {{in call to 'nondeterministic(true)'}}
+}
+#endif
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 1c51013ca06f77..4a06d29ae9dbc6 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -317,7 +317,7 @@
#error "wrong value for __cpp_lambdas"
#endif
-#if check(constexpr, 0, 200704, 201304, 201603, 201907, 202211, 202306)
+#if check(constexpr, 0, 200704, 201304, 201603, 201907, 202211, 202406L)
#error "wrong value for __cpp_constexpr"
#endif
diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp
index e4d97dcb73562d..b8cbf723a96a76 100644
--- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp
@@ -994,7 +994,7 @@ namespace placement_new_delete {
constexpr bool bad(int which) {
switch (which) {
case 0:
- delete new (placement_new_arg{}) int; // expected-note {{call to placement 'operator new'}}
+ delete new (placement_new_arg{}) int; // expected-note {{this placement new expression is not yet supported in constant expressions}}
break;
case 1:
diff --git a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
index 357dc67bd5ad22..b02c90b88d1ac6 100644
--- a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
+++ b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
@@ -90,9 +90,10 @@ constexpr int no_deallocate_nonalloc = (std::allocator<int>().deallocate((int*)&
// expected-note at -2 {{declared here}}
void *operator new(std::size_t, void *p) { return p; }
-constexpr bool no_placement_new_in_user_code() { // expected-error {{never produces a constant expression}}
+void* operator new[] (std::size_t, void* p) {return p;}
+constexpr bool no_placement_new_in_user_code() {
int a;
- new (&a) int(42); // expected-note {{call to placement 'operator new'}}
+ new (&a) int(42);
return a == 42;
}
@@ -239,3 +240,80 @@ void f() {
}
}
+
+namespace placement_new {
+
+template <typename T, typename U>
+constexpr void f(T* t, U u) {
+ new (t) U(u);
+}
+
+consteval int ok() {
+ int i;
+ new (&i) int(0);
+ new (&i) int[1]{1};
+ new (static_cast<void*>(&i)) int(0);
+ return 0;
+}
+
+consteval int conversion() { // expected-error {{consteval function never produces a constant expression}}
+ int i;
+ new (static_cast<void*>(&i)) float(0);
+ // expected-note at -1 {{placement new would change type of storage from 'int' to 'float'}}
+ return 0;
+}
+
+consteval int indeterminate() {
+ int * indeterminate;
+ new (indeterminate) int(0);
+ // expected-note at -1 {{read of uninitialized object is not allowed in a constant expression}}
+ return 0;
+}
+
+consteval int array1() {
+ int i[2];
+ new (&i) int[]{1,2};
+ new (&i) int[]{1};
+ new (&i) int(0);
+ new (static_cast<void*>(&i)) int[]{1,2};
+ new (static_cast<void*>(&i)) int[]{1};
+ return 0;
+}
+
+consteval int array2() { // expected-error {{consteval function never produces a constant expression}}
+ int i[1];
+ new (&i) int[2];
+ //expected-note at -1 {{placement new would change type of storage from 'int[1]' to 'int[2]'}}
+ return 0;
+}
+
+struct S{
+ int* i;
+ constexpr S() : i(new int(42)) {} // expected-note {{allocation performed here was not deallocated}}
+ constexpr ~S() {delete i;}
+};
+
+consteval void alloc() {
+ S* s = new S();
+ s->~S();
+ new (s) S();
+ delete s;
+}
+
+
+consteval void alloc_err() {
+ S* s = new S();
+ new (s) S();
+ delete s;
+}
+
+
+
+int a = ok();
+int c = indeterminate(); // expected-error {{call to consteval function 'placement_new::indeterminate' is not a constant expression}} \
+ // expected-note {{in call to 'indeterminate()'}}
+int d = array1();
+int alloc1 = (alloc(), 0);
+int alloc2 = (alloc_err(), 0); // expected-error {{call to consteval function 'placement_new::alloc_err' is not a constant expression}}
+
+}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index a8d2d813d0f536..395b5d3bff49a6 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -17348,7 +17348,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2922.html">2922</a></td>
<td>open</td>
<td>constexpr placement-new is too permissive</td>
- <td align="center">Not resolved</td>
+ <td title="Clang 20 implements 2024-07-10 resolution" align="center">Not Resolved*</td>
</tr></table>
</div>
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index faee8b578b6242..58bbb12a76dd75 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -208,7 +208,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
<tr>
<td><tt>constexpr</tt> placement new</td>
<td><a href="https://wg21.link/P2747R2">P2747R2</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 20</td>
</tr>
<tr>
<td>Deleting a Pointer to an Incomplete Type Should be Ill-formed</td>
>From 0042fbfd7b8152ffc880f7890fdd67db92857bf4 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 16 Aug 2024 17:18:51 +0200
Subject: [PATCH 2/6] Do not make it an extension as there is no good way to
introduce a warning for it
---
clang/docs/LanguageExtensions.rst | 1 -
clang/lib/AST/ExprConstant.cpp | 4 +-
clang/test/AST/ByteCode/new-delete.cpp | 2 +-
clang/test/CXX/drs/cwg29xx.cpp | 2 +-
.../test/SemaCXX/cxx2a-constexpr-dynalloc.cpp | 88 ++-----------------
.../SemaCXX/cxx2c-constexpr-placement-new.cpp | 78 ++++++++++++++++
6 files changed, 89 insertions(+), 86 deletions(-)
create mode 100644 clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 23fe23208763b8..62903fc3744cad 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1506,7 +1506,6 @@ Attributes on Structured Bindings __cpp_structured_bindings C+
Pack Indexing __cpp_pack_indexing C++26 C++03
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
Variadic Friends __cpp_variadic_friend C++26 C++03
-``constexpr`` placement new __cpp_constexpr C++26 C++20
-------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3df963c554a995..3a84011e79649d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10025,7 +10025,9 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info))
return false;
IsNothrow = true;
- } else if (OperatorNew->isReservedGlobalPlacementOperator()) {
+ } else if (OperatorNew->isReservedGlobalPlacementOperator() &&
+ (Info.CurrentCall->isStdFunction() ||
+ Info.getLangOpts().CPlusPlus26)) {
if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
return false;
if (Result.Designator.Invalid)
diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index 0c823e7000f853..7f135d94919cc6 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -245,7 +245,7 @@ namespace std {
namespace PlacementNew {
constexpr int foo() { // both-error {{never produces a constant expression}}
char c[sizeof(int)];
- new (c) int{12}; // ref-note {{placement new would change type of storage from 'char' to 'int'}} \
+ new (c) int{12}; // ref-note {{this placement new expression is not yet supported in constant expressions}} \
// expected-note {{subexpression not valid in a constant expression}}
return 0;
}
diff --git a/clang/test/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp
index 7e04aa0c3a4942..2515785f47bf19 100644
--- a/clang/test/CXX/drs/cwg29xx.cpp
+++ b/clang/test/CXX/drs/cwg29xx.cpp
@@ -24,7 +24,7 @@ struct S {
};
} // namespace cwg2917
-#if __cplusplus >= 202002
+#if __cplusplus >= 202400L
namespace std {
using size_t = decltype(sizeof(0));
diff --git a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
index b02c90b88d1ac6..c84678137479fc 100644
--- a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
+++ b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
@@ -1,6 +1,7 @@
-// RUN: %clang_cc1 -std=c++2a -verify %s -DNEW=__builtin_operator_new -DDELETE=__builtin_operator_delete
-// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=operator new" "-DDELETE=operator delete"
-// RUN: %clang_cc1 -std=c++2a -verify %s "-DNEW=::operator new" "-DDELETE=::operator delete"
+// RUN: %clang_cc1 -std=c++2a -verify=expected,cxx20 %s -DNEW=__builtin_operator_new -DDELETE=__builtin_operator_delete
+// RUN: %clang_cc1 -std=c++2a -verify=expected,cxx20 %s "-DNEW=operator new" "-DDELETE=operator delete"
+// RUN: %clang_cc1 -std=c++2a -verify=expected,cxx20 %s "-DNEW=::operator new" "-DDELETE=::operator delete"
+// RUN: %clang_cc1 -std=c++2c -verify=expected,cxx26 %s "-DNEW=::operator new" "-DDELETE=::operator delete"
constexpr bool alloc_from_user_code() {
void *p = NEW(sizeof(int)); // expected-note {{cannot allocate untyped memory in a constant expression; use 'std::allocator<T>::allocate'}}
@@ -91,9 +92,9 @@ constexpr int no_deallocate_nonalloc = (std::allocator<int>().deallocate((int*)&
void *operator new(std::size_t, void *p) { return p; }
void* operator new[] (std::size_t, void* p) {return p;}
-constexpr bool no_placement_new_in_user_code() {
+constexpr bool no_placement_new_in_user_code() { // cxx20-error {{constexpr function never produces a constant expression}}
int a;
- new (&a) int(42);
+ new (&a) int(42); // cxx20-note {{this placement new expression is not yet supported in constant expressions}}
return a == 42;
}
@@ -240,80 +241,3 @@ void f() {
}
}
-
-namespace placement_new {
-
-template <typename T, typename U>
-constexpr void f(T* t, U u) {
- new (t) U(u);
-}
-
-consteval int ok() {
- int i;
- new (&i) int(0);
- new (&i) int[1]{1};
- new (static_cast<void*>(&i)) int(0);
- return 0;
-}
-
-consteval int conversion() { // expected-error {{consteval function never produces a constant expression}}
- int i;
- new (static_cast<void*>(&i)) float(0);
- // expected-note at -1 {{placement new would change type of storage from 'int' to 'float'}}
- return 0;
-}
-
-consteval int indeterminate() {
- int * indeterminate;
- new (indeterminate) int(0);
- // expected-note at -1 {{read of uninitialized object is not allowed in a constant expression}}
- return 0;
-}
-
-consteval int array1() {
- int i[2];
- new (&i) int[]{1,2};
- new (&i) int[]{1};
- new (&i) int(0);
- new (static_cast<void*>(&i)) int[]{1,2};
- new (static_cast<void*>(&i)) int[]{1};
- return 0;
-}
-
-consteval int array2() { // expected-error {{consteval function never produces a constant expression}}
- int i[1];
- new (&i) int[2];
- //expected-note at -1 {{placement new would change type of storage from 'int[1]' to 'int[2]'}}
- return 0;
-}
-
-struct S{
- int* i;
- constexpr S() : i(new int(42)) {} // expected-note {{allocation performed here was not deallocated}}
- constexpr ~S() {delete i;}
-};
-
-consteval void alloc() {
- S* s = new S();
- s->~S();
- new (s) S();
- delete s;
-}
-
-
-consteval void alloc_err() {
- S* s = new S();
- new (s) S();
- delete s;
-}
-
-
-
-int a = ok();
-int c = indeterminate(); // expected-error {{call to consteval function 'placement_new::indeterminate' is not a constant expression}} \
- // expected-note {{in call to 'indeterminate()'}}
-int d = array1();
-int alloc1 = (alloc(), 0);
-int alloc2 = (alloc_err(), 0); // expected-error {{call to consteval function 'placement_new::alloc_err' is not a constant expression}}
-
-}
diff --git a/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
new file mode 100644
index 00000000000000..55ebc37b700983
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
@@ -0,0 +1,78 @@
+// RUN: %clang_cc1 -std=c++2c -verify %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 int ok() {
+ int i;
+ new (&i) int(0);
+ new (&i) int[1]{1};
+ new (static_cast<void*>(&i)) int(0);
+ return 0;
+}
+
+consteval int conversion() { // expected-error {{consteval function never produces a constant expression}}
+ int i;
+ new (static_cast<void*>(&i)) float(0);
+ // expected-note at -1 {{placement new would change type of storage from 'int' to 'float'}}
+ return 0;
+}
+
+consteval int indeterminate() {
+ int * indeterminate;
+ new (indeterminate) int(0);
+ // expected-note at -1 {{read of uninitialized object is not allowed in a constant expression}}
+ return 0;
+}
+
+consteval int array1() {
+ int i[2];
+ new (&i) int[]{1,2};
+ new (&i) int[]{1};
+ new (&i) int(0);
+ new (static_cast<void*>(&i)) int[]{1,2};
+ new (static_cast<void*>(&i)) int[]{1};
+ return 0;
+}
+
+consteval int array2() { // expected-error {{consteval function never produces a constant expression}}
+ int i[1];
+ new (&i) int[2];
+ //expected-note at -1 {{placement new would change type of storage from 'int[1]' to 'int[2]'}}
+ return 0;
+}
+
+struct S{
+ int* i;
+ constexpr S() : i(new int(42)) {} // expected-note {{allocation performed here was not deallocated}}
+ constexpr ~S() {delete i;}
+};
+
+consteval void alloc() {
+ S* s = new S();
+ s->~S();
+ new (s) S();
+ delete s;
+}
+
+
+consteval void alloc_err() {
+ S* s = new S();
+ new (s) S();
+ delete s;
+}
+
+
+
+int a = ok();
+int c = indeterminate(); // expected-error {{call to consteval function 'placement_new::indeterminate' is not a constant expression}} \
+ // expected-note {{in call to 'indeterminate()'}}
+int d = array1();
+int alloc1 = (alloc(), 0);
+int alloc2 = (alloc_err(), 0); // expected-error {{call to consteval function 'placement_new::alloc_err' is not a constant expression}}
>From f8a15eb3d901166dc1f49e16b033f3665ff4db64 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 16 Aug 2024 18:52:33 +0200
Subject: [PATCH 3/6] remove commented code
---
clang/lib/AST/ExprConstant.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3a84011e79649d..009e5d0074114e 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10032,8 +10032,6 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
return false;
if (Result.Designator.Invalid)
return false;
- /// if(!lifetimeStartedInEvaluation(Info, Result.getLValueBase()))
- // return false;
TargetType = E->getPlacementArg(0)->getType();
IsPlacement = true;
} else if (E->getNumPlacementArgs()) {
>From 5c4225be413b3b56c0ae7d05d493d1e93b77de75 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 21 Aug 2024 16:49:36 +0200
Subject: [PATCH 4/6] Address Aarons's feedback
---
.../include/clang/Basic/DiagnosticASTKinds.td | 5 ++-
clang/lib/AST/ExprConstant.cpp | 26 ++++++-----
clang/test/AST/ByteCode/new-delete.cpp | 6 +--
.../SemaCXX/constant-expression-cxx2a.cpp | 4 +-
.../test/SemaCXX/cxx2a-constexpr-dynalloc.cpp | 2 +-
.../SemaCXX/cxx2c-constexpr-placement-new.cpp | 45 +++++++++++++++++--
6 files changed, 66 insertions(+), 22 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 569d2cc20a526c..45ad84831589b1 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -333,9 +333,10 @@ def note_constexpr_new : Note<
def note_constexpr_new_non_replaceable : Note<
"call to %select{placement|class-specific}0 %1">;
def note_constexpr_new_placement : Note<
- "this placement new expression is not yet supported in constant expressions">;
+ "this placement new expression is not supported in constant expressions "
+ "%select{|before C++2c}0">;
def note_constexpr_placement_new_wrong_type : Note<
- "placement new would change type of storage from %0 to %1">;
+ "placement new would change type of storage from %0 to %1">;
def note_constexpr_new_negative : Note<
"cannot allocate array; evaluated array bound %0 is negative">;
def note_constexpr_new_too_large : Note<
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 009e5d0074114e..83610b3b2101d3 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10009,7 +10009,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
bool IsNothrow = false;
bool IsPlacement = false;
- if (E->getNumPlacementArgs() && E->getNumPlacementArgs() == 1 &&
+ if (E->getNumPlacementArgs() == 1 &&
E->getPlacementArg(0)->getType()->isNothrowT()) {
// The only new-placement list we support is of the form (std::nothrow).
//
@@ -10025,17 +10025,23 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info))
return false;
IsNothrow = true;
- } else if (OperatorNew->isReservedGlobalPlacementOperator() &&
- (Info.CurrentCall->isStdFunction() ||
- Info.getLangOpts().CPlusPlus26)) {
- if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
- return false;
- if (Result.Designator.Invalid)
+ } else if (OperatorNew->isReservedGlobalPlacementOperator()) {
+ if (Info.CurrentCall->isStdFunction() || Info.getLangOpts().CPlusPlus26) {
+ if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
+ return false;
+ if (Result.Designator.Invalid)
+ return false;
+ TargetType = E->getPlacementArg(0)->getType();
+ IsPlacement = true;
+ } else {
+ Info.FFDiag(E, diag::note_constexpr_new_placement)
+ << /*C++26 feature*/ 1 << E->getSourceRange();
return false;
- TargetType = E->getPlacementArg(0)->getType();
- IsPlacement = true;
+ }
} else if (E->getNumPlacementArgs()) {
- return Error(E, diag::note_constexpr_new_placement);
+ Info.FFDiag(E, diag::note_constexpr_new_placement)
+ << /*Unsupported*/ 0 << E->getSourceRange();
+ return false;
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
<< isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index 7f135d94919cc6..cb398b38bc8d72 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -245,7 +245,7 @@ namespace std {
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 yet supported in constant expressions}} \
+ new (c) int{12}; // ref-note {{this placement new expression is not supported in constant expressions}} \
// expected-note {{subexpression not valid in a constant expression}}
return 0;
}
@@ -309,7 +309,7 @@ namespace placement_new_delete {
constexpr bool bad(int which) {
switch (which) {
case 0:
- delete new (placement_new_arg{}) int; // ref-note {{this placement new expression is not yet supported in constant expression}} \
+ 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}}
break;
@@ -328,7 +328,7 @@ namespace placement_new_delete {
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 {{placement new expression is not yet supported}} \
+ 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}}
break;
}
diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp
index b8cbf723a96a76..36d4d25c48471b 100644
--- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp
@@ -994,7 +994,7 @@ namespace placement_new_delete {
constexpr bool bad(int which) {
switch (which) {
case 0:
- delete new (placement_new_arg{}) int; // expected-note {{this placement new expression is not yet supported in constant expressions}}
+ delete new (placement_new_arg{}) int; // expected-note {{this placement new expression is not supported in constant expressions}}
break;
case 1:
@@ -1012,7 +1012,7 @@ namespace placement_new_delete {
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}}
+ delete new (std::align_val_t{64}) Overaligned; // expected-note {{this placement new expression is not supported in constant expressions}}
break;
}
diff --git a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
index c84678137479fc..6d9c0b607d8a67 100644
--- a/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
+++ b/clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
@@ -94,7 +94,7 @@ void *operator new(std::size_t, void *p) { return p; }
void* operator new[] (std::size_t, void* p) {return p;}
constexpr bool no_placement_new_in_user_code() { // cxx20-error {{constexpr function never produces a constant expression}}
int a;
- new (&a) int(42); // cxx20-note {{this placement new expression is not yet supported in constant expressions}}
+ new (&a) int(42); // cxx20-note {{this placement new expression is not supported in constant expressions before C++2c}}
return a == 42;
}
diff --git a/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
index 55ebc37b700983..450d4a4d690526 100644
--- a/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
+++ b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
@@ -17,7 +17,7 @@ consteval int ok() {
return 0;
}
-consteval int conversion() { // expected-error {{consteval function never produces a constant expression}}
+consteval int conversion() {
int i;
new (static_cast<void*>(&i)) float(0);
// expected-note at -1 {{placement new would change type of storage from 'int' to 'float'}}
@@ -41,7 +41,7 @@ consteval int array1() {
return 0;
}
-consteval int array2() { // expected-error {{consteval function never produces a constant expression}}
+consteval int array2() {
int i[1];
new (&i) int[2];
//expected-note at -1 {{placement new would change type of storage from 'int[1]' to 'int[2]'}}
@@ -71,8 +71,45 @@ consteval void alloc_err() {
int a = ok();
-int c = indeterminate(); // expected-error {{call to consteval function 'placement_new::indeterminate' is not a constant expression}} \
+int b = conversion(); // expected-error {{call to consteval function 'conversion' is not a constant expression}} \
+ // expected-note {{in call to 'conversion()'}}
+int c = indeterminate(); // expected-error {{call to consteval function 'indeterminate' is not a constant expression}} \
// expected-note {{in call to 'indeterminate()'}}
int d = array1();
+int e = array2(); // expected-error {{call to consteval function 'array2' is not a constant expression}} \
+ // expected-note {{in call to 'array2()'}}
int alloc1 = (alloc(), 0);
-int alloc2 = (alloc_err(), 0); // expected-error {{call to consteval function 'placement_new::alloc_err' is not a constant expression}}
+int alloc2 = (alloc_err(), 0); // expected-error {{call to consteval function 'alloc_err' is not a constant expression}}
+
+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 }; // expected-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()); // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to 'blah()'}}
+
+constexpr int *get_indeterminate() {
+ int *evil;
+ return evil; // expected-note {{read of uninitialized object is not allowed in a constant expression}}
+}
+
+constexpr bool bleh() {
+ int *ptr = new (get_indeterminate()) int; // expected-note {{in call to 'get_indeterminate()'}}
+ return true;
+}
+static_assert(bleh()); // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to 'bleh()'}}
>From ab02d7ff16e8662295069d0481fc10fc86f151a9 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 22 Aug 2024 12:46:59 +0200
Subject: [PATCH 5/6] Address Aarons's remaining feedback
---
clang/lib/AST/ExprConstant.cpp | 2 ++
clang/test/AST/ByteCode/new-delete.cpp | 4 ++--
clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp | 3 ++-
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 83610b3b2101d3..826cc5f58bdf51 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -6691,6 +6691,8 @@ static bool HandleDestructionImpl(EvalInfo &Info, SourceRange CallRange,
if (Size && Size > Value.getArrayInitializedElts())
expandArray(Value, Value.getArraySize() - 1);
+ // The size of the array might have been reduced by
+ // a placement new.
for (Size = Value.getArraySize(); Size != 0; --Size) {
APValue &Elem = Value.getArrayInitializedElt(Size - 1);
if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1) ||
diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index cb398b38bc8d72..a7be4102fd0a05 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -245,7 +245,7 @@ namespace std {
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}} \
+ 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}}
return 0;
}
@@ -328,7 +328,7 @@ namespace placement_new_delete {
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 }} \
+ 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}}
break;
}
diff --git a/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
index 450d4a4d690526..031fa4df8d8a9f 100644
--- a/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
+++ b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
@@ -50,7 +50,7 @@ consteval int array2() {
struct S{
int* i;
- constexpr S() : i(new int(42)) {} // expected-note {{allocation performed here was not deallocated}}
+ constexpr S() : i(new int(42)) {} // #no-deallocation
constexpr ~S() {delete i;}
};
@@ -80,6 +80,7 @@ int e = array2(); // expected-error {{call to consteval function 'array2' is not
// expected-note {{in call to 'array2()'}}
int alloc1 = (alloc(), 0);
int alloc2 = (alloc_err(), 0); // expected-error {{call to consteval function 'alloc_err' is not a constant expression}}
+ // expected-note@#no-deallocation {{allocation performed here was not deallocated}}
constexpr int *intptr() {
return new int;
>From f0dbe73efad67142ff1178b2f712ee6cc3e95763 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 22 Aug 2024 13:45:58 +0200
Subject: [PATCH 6/6] cleanup test
---
clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
index 031fa4df8d8a9f..a29fb981cedbf0 100644
--- a/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
+++ b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
@@ -72,9 +72,9 @@ consteval void alloc_err() {
int a = ok();
int b = conversion(); // expected-error {{call to consteval function 'conversion' is not a constant expression}} \
- // expected-note {{in call to 'conversion()'}}
+ // expected-note {{in call to 'conversion()'}}
int c = indeterminate(); // expected-error {{call to consteval function 'indeterminate' is not a constant expression}} \
- // expected-note {{in call to 'indeterminate()'}}
+ // expected-note {{in call to 'indeterminate()'}}
int d = array1();
int e = array2(); // expected-error {{call to consteval function 'array2' is not a constant expression}} \
// expected-note {{in call to 'array2()'}}
More information about the cfe-commits
mailing list