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 <corentinja...@gmail.com> Date: Tue, 9 Jul 2024 08:37:18 +0200 Subject: [PATCH 1/5] [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@-1 {{placement new would change type of storage from 'U' to 'int'}} + return u; + } + return {}; +} +constexpr U _ = nondeterministic(true); +// expected-error@-1 {{constexpr variable '_' must be initialized by a constant expression}} \ +// expected-note@-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@-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@-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@-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@-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 <corentinja...@gmail.com> Date: Fri, 16 Aug 2024 17:18:51 +0200 Subject: [PATCH 2/5] 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@-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@-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@-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@-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@-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@-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 <corentinja...@gmail.com> Date: Fri, 16 Aug 2024 18:52:33 +0200 Subject: [PATCH 3/5] 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 <corentinja...@gmail.com> Date: Wed, 21 Aug 2024 16:49:36 +0200 Subject: [PATCH 4/5] 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@-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@-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 <corentinja...@gmail.com> Date: Thu, 22 Aug 2024 12:46:59 +0200 Subject: [PATCH 5/5] 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; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits