[clang] [Clang] Implement P2747 constexpr placement new (PR #104586)

via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 16 08:19:41 PDT 2024


https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/104586

>From 36eb12a12db45ca345fe9a3945431c19eff2a6cf 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/2] [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                   |  5 +-
 .../include/clang/Basic/DiagnosticASTKinds.td |  2 +-
 clang/lib/AST/ExprConstant.cpp                | 55 +++++++------
 clang/lib/Frontend/InitPreprocessor.cpp       |  2 +-
 clang/test/AST/Interp/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, 151 insertions(+), 34 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 114e742f3561b7..296bb85f5e72b2 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 ffdd063ec99037..22864062fba7fd 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -73,7 +73,7 @@ C++ Specific Potentially Breaking Changes
     template <> // error: extraneous template head
     template <typename T>
     void f();
-    
+
 ABI Changes in This Version
 ---------------------------
 
@@ -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 09edbb6641650a..20c1fd6747d582 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/Interp/new-delete.cpp b/clang/test/AST/Interp/new-delete.cpp
index 6bb30bc19f110c..0c823e7000f853 100644
--- a/clang/test/AST/Interp/new-delete.cpp
+++ b/clang/test/AST/Interp/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 c901e1ce0e15b3..de84d94c20a152 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 073e2e5878d3c7cdbd54a5d2f32e8fb59e732d0e 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/2] 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/Interp/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 296bb85f5e72b2..114e742f3561b7 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 20c1fd6747d582..138269043c1790 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/Interp/new-delete.cpp b/clang/test/AST/Interp/new-delete.cpp
index 0c823e7000f853..7f135d94919cc6 100644
--- a/clang/test/AST/Interp/new-delete.cpp
+++ b/clang/test/AST/Interp/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}}



More information about the cfe-commits mailing list