Skip to content

Commit 4b96400

Browse files
authored
[clang][bytecode] Allow placement-new in std functions pre-C++26 (#109753)
1 parent 14ab619 commit 4b96400

File tree

7 files changed

+93
-10
lines changed

7 files changed

+93
-10
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3117,21 +3117,22 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
31173117
if (!this->discard(Arg1))
31183118
return false;
31193119
IsNoThrow = true;
3120-
} else if (Ctx.getLangOpts().CPlusPlus26 &&
3121-
OperatorNew->isReservedGlobalPlacementOperator()) {
3120+
} else {
3121+
// Invalid unless we have C++26 or are in a std:: function.
3122+
if (!this->emitInvalidNewDeleteExpr(E, E))
3123+
return false;
3124+
31223125
// If we have a placement-new destination, we'll later use that instead
31233126
// of allocating.
3124-
PlacementDest = Arg1;
3125-
} else {
3126-
return this->emitInvalidNewDeleteExpr(E, E);
3127+
if (OperatorNew->isReservedGlobalPlacementOperator())
3128+
PlacementDest = Arg1;
31273129
}
3128-
31293130
} else {
3131+
// Always invalid.
31303132
return this->emitInvalid(E);
31313133
}
3132-
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
3134+
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction())
31333135
return this->emitInvalidNewDeleteExpr(E, E);
3134-
}
31353136

31363137
const Descriptor *Desc;
31373138
if (!PlacementDest) {

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,13 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
12931293
if (!CheckStore(S, OpPC, Ptr))
12941294
return false;
12951295

1296+
if (!InvalidNewDeleteExpr(S, OpPC, E))
1297+
return false;
1298+
1299+
// Assume proper types in std functions.
1300+
if (S.Current->isStdFunction())
1301+
return true;
1302+
12961303
const auto *NewExpr = cast<CXXNewExpr>(E);
12971304
QualType StorageType = Ptr.getType();
12981305

@@ -1334,10 +1341,16 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
13341341
assert(E);
13351342
const auto &Loc = S.Current->getSource(OpPC);
13361343

1344+
if (S.getLangOpts().CPlusPlus26)
1345+
return true;
1346+
13371347
if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) {
13381348
const FunctionDecl *OperatorNew = NewExpr->getOperatorNew();
13391349

13401350
if (!S.getLangOpts().CPlusPlus26 && NewExpr->getNumPlacementArgs() > 0) {
1351+
// This is allowed pre-C++26, but only an std function.
1352+
if (S.Current->isStdFunction())
1353+
return true;
13411354
S.FFDiag(Loc, diag::note_constexpr_new_placement)
13421355
<< /*C++26 feature*/ 1 << E->getSourceRange();
13431356
} else if (NewExpr->getNumPlacementArgs() == 1 &&

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,8 +1345,7 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
13451345
assert(!ElemT);
13461346
// Structs etc.
13471347
const Descriptor *Desc = S.P.createDescriptor(
1348-
Call, ElemType.getTypePtr(),
1349-
NumElems.ule(1) ? std::nullopt : Descriptor::InlineDescMD,
1348+
Call, ElemType.getTypePtr(), Descriptor::InlineDescMD,
13501349
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false,
13511350
/*Init=*/nullptr);
13521351

clang/lib/AST/ByteCode/InterpFrame.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,13 @@ SourceRange InterpFrame::getRange(CodePtr PC) const {
257257

258258
return S.getRange(Func, PC);
259259
}
260+
261+
bool InterpFrame::isStdFunction() const {
262+
if (!Func)
263+
return false;
264+
for (const DeclContext *DC = Func->getDecl(); DC; DC = DC->getParent())
265+
if (DC->isStdNamespace())
266+
return true;
267+
268+
return false;
269+
}

clang/lib/AST/ByteCode/InterpFrame.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ class InterpFrame final : public Frame {
117117

118118
unsigned getDepth() const { return Depth; }
119119

120+
bool isStdFunction() const;
121+
120122
void dump() const { dump(llvm::errs(), 0); }
121123
void dump(llvm::raw_ostream &OS, unsigned Indent = 0) const;
122124

clang/test/AST/ByteCode/new-delete.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,10 @@ namespace std {
594594
// both-note {{used to delete a null pointer}}
595595
}
596596
};
597+
template<typename T, typename ...Args>
598+
constexpr void construct_at(void *p, Args &&...args) { // #construct
599+
new (p) T((Args&&)args...);
600+
}
597601
}
598602

599603
/// Specialization for float, using operator new/delete.
@@ -762,6 +766,16 @@ namespace Placement {
762766
}
763767
static_assert(ok1()); // both-error {{not an integral constant expression}} \
764768
// both-note {{in call to}}
769+
770+
/// placement-new should be supported before C++26 in std functions.
771+
constexpr int ok2() {
772+
int *I = new int;
773+
std::construct_at<int>(I);
774+
int r = *I;
775+
delete I;
776+
return r;
777+
}
778+
static_assert(ok2()== 0);
765779
}
766780

767781
#else

clang/test/AST/ByteCode/placement-new.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33

44
namespace std {
55
using size_t = decltype(sizeof(0));
6+
template<typename T> struct allocator {
7+
constexpr T *allocate(size_t N) {
8+
return (T*)operator new(sizeof(T) * N);
9+
}
10+
constexpr void deallocate(void *p) {
11+
operator delete(p);
12+
}
13+
};
14+
template<typename T, typename ...Args>
15+
constexpr void construct_at(void *p, Args &&...args) {
16+
new (p) T((Args&&)args...); // both-note {{in call to}}
17+
}
618
}
719

820
void *operator new(std::size_t, void *p) { return p; }
@@ -217,3 +229,35 @@ namespace records {
217229
}
218230
static_assert(foo() == 0);
219231
}
232+
233+
namespace ConstructAt {
234+
struct S {
235+
int a = 10;
236+
float b = 1.0;
237+
};
238+
239+
constexpr bool ok1() {
240+
S s;
241+
242+
std::construct_at<S>(&s);
243+
return s.a == 10 && s.b == 1.0;
244+
}
245+
static_assert(ok1());
246+
247+
struct S2 {
248+
constexpr S2() {
249+
(void)(1/0); // both-note {{division by zero}} \
250+
// both-warning {{division by zero is undefined}}
251+
}
252+
};
253+
254+
constexpr bool ctorFail() { //
255+
S2 *s = std::allocator<S2>().allocate(1);
256+
std::construct_at<S2>(s); // both-note {{in call to}}
257+
258+
return true;
259+
}
260+
static_assert(ctorFail()); // both-error {{not an integral constant expression}} \
261+
// both-note {{in call to 'ctorFail()'}}
262+
263+
}

0 commit comments

Comments
 (0)