Author: Timm Baeder Date: 2024-09-24T10:28:54+02:00 New Revision: 4b964002403a9b9be934174391ff5b698691a26b
URL: https://github.com/llvm/llvm-project/commit/4b964002403a9b9be934174391ff5b698691a26b DIFF: https://github.com/llvm/llvm-project/commit/4b964002403a9b9be934174391ff5b698691a26b.diff LOG: [clang][bytecode] Allow placement-new in std functions pre-C++26 (#109753) Added: Modified: clang/lib/AST/ByteCode/Compiler.cpp clang/lib/AST/ByteCode/Interp.cpp clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/lib/AST/ByteCode/InterpFrame.cpp clang/lib/AST/ByteCode/InterpFrame.h clang/test/AST/ByteCode/new-delete.cpp clang/test/AST/ByteCode/placement-new.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 939a3aa43ff8dd..754cd0db9868b7 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -3117,21 +3117,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