[clang] 4b96400 - [clang][bytecode] Allow placement-new in std functions pre-C++26 (#109753)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 24 01:28:57 PDT 2024
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()'}}
+
+}
More information about the cfe-commits
mailing list