https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/109753
>From 99a3e850f943393f18405d98601a08a91065d710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Mon, 23 Sep 2024 16:37:36 +0200 Subject: [PATCH] [clang][bytecode] Allow placement-new in std function pre-C++26 --- clang/lib/AST/ByteCode/Compiler.cpp | 17 ++++----- clang/lib/AST/ByteCode/Interp.cpp | 13 +++++++ clang/lib/AST/ByteCode/InterpBuiltin.cpp | 3 +- clang/lib/AST/ByteCode/InterpFrame.cpp | 10 ++++++ clang/lib/AST/ByteCode/InterpFrame.h | 2 ++ clang/test/AST/ByteCode/new-delete.cpp | 14 ++++++++ clang/test/AST/ByteCode/placement-new.cpp | 44 +++++++++++++++++++++++ 7 files changed, 93 insertions(+), 10 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index e89863a231bed4..04e4f5f8387ec5 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -3116,21 +3116,22 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) { if (!this->discard(Arg1)) return false; IsNoThrow = true; - } else if (Ctx.getLangOpts().CPlusPlus26 && - OperatorNew->isReservedGlobalPlacementOperator()) { + } else { + // Invalid unless we have C++26 or are in a std:: function. + if (!this->emitInvalidNewDeleteExpr(E, E)) + return false; + // If we have a placement-new destination, we'll later use that instead // of allocating. - PlacementDest = Arg1; - } else { - return this->emitInvalidNewDeleteExpr(E, E); + if (OperatorNew->isReservedGlobalPlacementOperator()) + PlacementDest = Arg1; } - } else { + // Always invalid. return this->emitInvalid(E); } - } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) { + } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) return this->emitInvalidNewDeleteExpr(E, E); - } const Descriptor *Desc; if (!PlacementDest) { diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 739f6d2d8a7e95..8b578ccbeb6792 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1293,6 +1293,13 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E, if (!CheckStore(S, OpPC, Ptr)) return false; + if (!InvalidNewDeleteExpr(S, OpPC, E)) + return false; + + // Assume proper types in std functions. + if (S.Current->isStdFunction()) + return true; + const auto *NewExpr = cast<CXXNewExpr>(E); QualType StorageType = Ptr.getType(); @@ -1334,10 +1341,16 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) { assert(E); const auto &Loc = S.Current->getSource(OpPC); + if (S.getLangOpts().CPlusPlus26) + return true; + if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) { const FunctionDecl *OperatorNew = NewExpr->getOperatorNew(); if (!S.getLangOpts().CPlusPlus26 && NewExpr->getNumPlacementArgs() > 0) { + // This is allowed pre-C++26, but only an std function. + if (S.Current->isStdFunction()) + return true; S.FFDiag(Loc, diag::note_constexpr_new_placement) << /*C++26 feature*/ 1 << E->getSourceRange(); } else if (NewExpr->getNumPlacementArgs() == 1 && diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 523f5cb993dbc7..68710f67be2003 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1345,8 +1345,7 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC, assert(!ElemT); // Structs etc. const Descriptor *Desc = S.P.createDescriptor( - Call, ElemType.getTypePtr(), - NumElems.ule(1) ? std::nullopt : Descriptor::InlineDescMD, + Call, ElemType.getTypePtr(), Descriptor::InlineDescMD, /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false, /*Init=*/nullptr); diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp b/clang/lib/AST/ByteCode/InterpFrame.cpp index 7c877a70fe6b97..7f02464a1c0f14 100644 --- a/clang/lib/AST/ByteCode/InterpFrame.cpp +++ b/clang/lib/AST/ByteCode/InterpFrame.cpp @@ -257,3 +257,13 @@ SourceRange InterpFrame::getRange(CodePtr PC) const { return S.getRange(Func, PC); } + +bool InterpFrame::isStdFunction() const { + if (!Func) + return false; + for (const DeclContext *DC = Func->getDecl(); DC; DC = DC->getParent()) + if (DC->isStdNamespace()) + return true; + + return false; +} diff --git a/clang/lib/AST/ByteCode/InterpFrame.h b/clang/lib/AST/ByteCode/InterpFrame.h index 802777a523d9b3..7cfc3ac68b4f3e 100644 --- a/clang/lib/AST/ByteCode/InterpFrame.h +++ b/clang/lib/AST/ByteCode/InterpFrame.h @@ -117,6 +117,8 @@ class InterpFrame final : public Frame { unsigned getDepth() const { return Depth; } + bool isStdFunction() const; + void dump() const { dump(llvm::errs(), 0); } void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const; diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp index d62f12b63eee8e..8c9d5d9c9b1d7c 100644 --- a/clang/test/AST/ByteCode/new-delete.cpp +++ b/clang/test/AST/ByteCode/new-delete.cpp @@ -594,6 +594,10 @@ namespace std { // both-note {{used to delete a null pointer}} } }; + template<typename T, typename ...Args> + constexpr void construct_at(void *p, Args &&...args) { // #construct + new (p) T((Args&&)args...); + } } /// Specialization for float, using operator new/delete. @@ -762,6 +766,16 @@ namespace Placement { } static_assert(ok1()); // both-error {{not an integral constant expression}} \ // both-note {{in call to}} + + /// placement-new should be supported before C++26 in std functions. + constexpr int ok2() { + int *I = new int; + std::construct_at<int>(I); + int r = *I; + delete I; + return r; + } + static_assert(ok2()== 0); } #else diff --git a/clang/test/AST/ByteCode/placement-new.cpp b/clang/test/AST/ByteCode/placement-new.cpp index 9e86217c5fbf36..7a562adae02a6f 100644 --- a/clang/test/AST/ByteCode/placement-new.cpp +++ b/clang/test/AST/ByteCode/placement-new.cpp @@ -3,6 +3,18 @@ namespace std { using size_t = decltype(sizeof(0)); + template<typename T> struct allocator { + constexpr T *allocate(size_t N) { + return (T*)operator new(sizeof(T) * N); + } + constexpr void deallocate(void *p) { + operator delete(p); + } + }; + template<typename T, typename ...Args> + constexpr void construct_at(void *p, Args &&...args) { + new (p) T((Args&&)args...); // both-note {{in call to}} + } } void *operator new(std::size_t, void *p) { return p; } @@ -217,3 +229,35 @@ namespace records { } static_assert(foo() == 0); } + +namespace ConstructAt { + struct S { + int a = 10; + float b = 1.0; + }; + + constexpr bool ok1() { + S s; + + std::construct_at<S>(&s); + return s.a == 10 && s.b == 1.0; + } + static_assert(ok1()); + + struct S2 { + constexpr S2() { + (void)(1/0); // both-note {{division by zero}} \ + // both-warning {{division by zero is undefined}} + } + }; + + constexpr bool ctorFail() { // + S2 *s = std::allocator<S2>().allocate(1); + std::construct_at<S2>(s); // both-note {{in call to}} + + return true; + } + static_assert(ctorFail()); // both-error {{not an integral constant expression}} \ + // both-note {{in call to 'ctorFail()'}} + +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits