[clang] 6e78aef - [Clang] Implement P2747 constexpr placement new (#104586)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 23 08:24:11 PDT 2024
Author: cor3ntin
Date: 2024-08-23T17:24:08+02:00
New Revision: 6e78aef646c22b7087cbf7939c8016f4f59614a1
URL: https://github.com/llvm/llvm-project/commit/6e78aef646c22b7087cbf7939c8016f4f59614a1
DIFF: https://github.com/llvm/llvm-project/commit/6e78aef646c22b7087cbf7939c8016f4f59614a1.diff
LOG: [Clang] Implement P2747 constexpr placement new (#104586)
The implementation follows the resolution of CWG2922
Added:
clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticASTKinds.td
clang/lib/AST/ExprConstant.cpp
clang/lib/Frontend/InitPreprocessor.cpp
clang/test/AST/ByteCode/new-delete.cpp
clang/test/CXX/drs/cwg29xx.cpp
clang/test/Lexer/cxx-features.cpp
clang/test/SemaCXX/constant-expression-cxx2a.cpp
clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp
clang/www/cxx_dr_status.html
clang/www/cxx_status.html
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index fb3c2f699964c3..baedc3cd6f03fc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -118,6 +118,8 @@ C++2c Feature Support
- Implemented `P2893R3 Variadic Friends <https://wg21.link/P2893>`_
+- Implemented `P2747R2 constexpr placement new <https://wg21.link/P2747R2>`_.
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- Removed the restriction to literal types in constexpr functions in C++23 mode.
@@ -125,6 +127,7 @@ C++23 Feature Support
C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^
+
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index f317c5ac44f32b..45ad84831589b1 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -333,7 +333,8 @@ 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">;
def note_constexpr_new_negative : Note<
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 5540f58b526705..826cc5f58bdf51 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -6691,7 +6691,9 @@ static bool HandleDestructionImpl(EvalInfo &Info, SourceRange CallRange,
if (Size && Size > Value.getArrayInitializedElts())
expandArray(Value, Value.getArraySize() - 1);
- for (; Size != 0; --Size) {
+ // 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) ||
!HandleDestructionImpl(Info, CallRange, ElemLV, Elem, ElemT))
@@ -10003,23 +10005,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() == 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 +10023,31 @@ 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 (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;
+ }
+ } else if (E->getNumPlacementArgs()) {
+ 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;
+ return false;
}
const Expr *Init = E->getInitializer();
@@ -10045,7 +10055,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 +10148,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..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 {{call to placement 'operator new'}} \
+ 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;
}
@@ -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 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/CXX/drs/cwg29xx.cpp b/clang/test/CXX/drs/cwg29xx.cpp
index 8cac9f283980b6..2515785f47bf19 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 >= 202400L
+
+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..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 {{call to placement 'operator new'}}
+ 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 357dc67bd5ad22..6d9c0b607d8a67 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'}}
@@ -90,9 +91,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() { // cxx20-error {{constexpr function never produces a constant expression}}
int a;
- new (&a) int(42); // expected-note {{call to placement 'operator new'}}
+ 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
new file mode 100644
index 00000000000000..a29fb981cedbf0
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp
@@ -0,0 +1,116 @@
+// 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() {
+ 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() {
+ 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)) {} // #no-deallocation
+ 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 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 'alloc_err' is not a constant expression}}
+ // expected-note@#no-deallocation {{allocation performed here was not deallocated}}
+
+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()'}}
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>
More information about the cfe-commits
mailing list