Skip to content

Commit e5f8998

Browse files
authored
[clang][bytecode] Explicitly start variable lifetimes via placement new (#140221)
placement new /std::construct{,_at} can resurrect a variable after it's destructor has been called.
1 parent 711d72e commit e5f8998

File tree

5 files changed

+72
-7
lines changed

5 files changed

+72
-7
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3478,6 +3478,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
34783478
if (PlacementDest) {
34793479
if (!this->visit(PlacementDest))
34803480
return false;
3481+
if (!this->emitStartLifetime(E))
3482+
return false;
34813483
if (!this->emitGetLocal(SizeT, ArrayLen, E))
34823484
return false;
34833485
if (!this->emitCheckNewTypeMismatchArray(SizeT, E, E))
@@ -3617,6 +3619,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
36173619
if (PlacementDest) {
36183620
if (!this->visit(PlacementDest))
36193621
return false;
3622+
if (!this->emitStartLifetime(E))
3623+
return false;
36203624
if (!this->emitCheckNewTypeMismatch(E, E))
36213625
return false;
36223626
} else {

clang/lib/AST/ByteCode/Interp.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,14 @@ static inline bool Kill(InterpState &S, CodePtr OpPC) {
13261326
return true;
13271327
}
13281328

1329+
static inline bool StartLifetime(InterpState &S, CodePtr OpPC) {
1330+
const auto &Ptr = S.Stk.peek<Pointer>();
1331+
if (!CheckDummy(S, OpPC, Ptr, AK_Destroy))
1332+
return false;
1333+
Ptr.startLifetime();
1334+
return true;
1335+
}
1336+
13291337
/// 1) Pops the value from the stack.
13301338
/// 2) Writes the value to the local variable with the
13311339
/// given offset.
@@ -1855,10 +1863,8 @@ template <PrimType Name, class T = typename PrimConv<Name>::T>
18551863
bool Init(InterpState &S, CodePtr OpPC) {
18561864
const T &Value = S.Stk.pop<T>();
18571865
const Pointer &Ptr = S.Stk.peek<Pointer>();
1858-
if (!CheckInit(S, OpPC, Ptr)) {
1859-
assert(false);
1866+
if (!CheckInit(S, OpPC, Ptr))
18601867
return false;
1861-
}
18621868
Ptr.activate();
18631869
Ptr.initialize();
18641870
new (&Ptr.deref<T>()) T(Value);

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -395,10 +395,8 @@ def GetLocal : AccessOpcode { let HasCustomEval = 1; }
395395
// [] -> [Pointer]
396396
def SetLocal : AccessOpcode { let HasCustomEval = 1; }
397397

398-
def Kill : Opcode {
399-
let Types = [];
400-
let Args = [];
401-
}
398+
def Kill : Opcode;
399+
def StartLifetime : Opcode;
402400

403401
def CheckDecl : Opcode {
404402
let Args = [ArgVarDecl];

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,14 @@ class Pointer {
722722
getInlineDesc()->LifeState = Lifetime::Ended;
723723
}
724724

725+
void startLifetime() const {
726+
if (!isBlockPointer())
727+
return;
728+
if (asBlockPointer().Base < sizeof(InlineDescriptor))
729+
return;
730+
getInlineDesc()->LifeState = Lifetime::Started;
731+
}
732+
725733
/// Compare two pointers.
726734
ComparisonCategoryResult compare(const Pointer &Other) const {
727735
if (!hasSameBase(*this, Other))
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %clang_cc1 -verify=expected,both -std=c++26 %s -fexperimental-new-constant-interpreter
2+
// RUN: %clang_cc1 -verify=ref,both -std=c++26 %s
3+
4+
// both-no-diagnostics
5+
6+
namespace std {
7+
struct type_info;
8+
struct destroying_delete_t {
9+
explicit destroying_delete_t() = default;
10+
} inline constexpr destroying_delete{};
11+
struct nothrow_t {
12+
explicit nothrow_t() = default;
13+
} inline constexpr nothrow{};
14+
using size_t = decltype(sizeof(0));
15+
enum class align_val_t : size_t {};
16+
};
17+
18+
constexpr void *operator new(std::size_t, void *p) { return p; }
19+
namespace std {
20+
template<typename T> constexpr T *construct(T *p) { return new (p) T; }
21+
template<typename T> constexpr void destroy(T *p) { p->~T(); }
22+
}
23+
24+
constexpr bool foo() {
25+
using T = bool;
26+
bool b = true;
27+
b.~T();
28+
new (&b) bool(false);
29+
return b;
30+
}
31+
static_assert(!foo());
32+
33+
struct S {};
34+
constexpr bool foo2() {
35+
S s;
36+
s.~S();
37+
new (&s) S{};
38+
return true;
39+
}
40+
static_assert(foo2());
41+
42+
constexpr void destroy_pointer() {
43+
using T = int*;
44+
T p;
45+
p.~T();
46+
std::construct(&p);
47+
}
48+
static_assert((destroy_pointer(), true));
49+

0 commit comments

Comments
 (0)