[clang] e5f8998 - [clang][bytecode] Explicitly start variable lifetimes via placement new (#140221)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 16 03:48:25 PDT 2025
Author: Timm Baeder
Date: 2025-05-16T12:48:22+02:00
New Revision: e5f8998ac86f3cbbc763f0a1a9e23824e70b4af7
URL: https://github.com/llvm/llvm-project/commit/e5f8998ac86f3cbbc763f0a1a9e23824e70b4af7
DIFF: https://github.com/llvm/llvm-project/commit/e5f8998ac86f3cbbc763f0a1a9e23824e70b4af7.diff
LOG: [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.
Added:
clang/test/AST/ByteCode/lifetimes26.cpp
Modified:
clang/lib/AST/ByteCode/Compiler.cpp
clang/lib/AST/ByteCode/Interp.h
clang/lib/AST/ByteCode/Opcodes.td
clang/lib/AST/ByteCode/Pointer.h
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 2580fb17ce5e3..5017c9b76e6d1 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3478,6 +3478,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
if (PlacementDest) {
if (!this->visit(PlacementDest))
return false;
+ if (!this->emitStartLifetime(E))
+ return false;
if (!this->emitGetLocal(SizeT, ArrayLen, E))
return false;
if (!this->emitCheckNewTypeMismatchArray(SizeT, E, E))
@@ -3617,6 +3619,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
if (PlacementDest) {
if (!this->visit(PlacementDest))
return false;
+ if (!this->emitStartLifetime(E))
+ return false;
if (!this->emitCheckNewTypeMismatch(E, E))
return false;
} else {
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 35d97167135f7..9f1a6302eb856 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1326,6 +1326,14 @@ static inline bool Kill(InterpState &S, CodePtr OpPC) {
return true;
}
+static inline bool StartLifetime(InterpState &S, CodePtr OpPC) {
+ const auto &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckDummy(S, OpPC, Ptr, AK_Destroy))
+ return false;
+ Ptr.startLifetime();
+ return true;
+}
+
/// 1) Pops the value from the stack.
/// 2) Writes the value to the local variable with the
/// given offset.
@@ -1855,10 +1863,8 @@ template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Init(InterpState &S, CodePtr OpPC) {
const T &Value = S.Stk.pop<T>();
const Pointer &Ptr = S.Stk.peek<Pointer>();
- if (!CheckInit(S, OpPC, Ptr)) {
- assert(false);
+ if (!CheckInit(S, OpPC, Ptr))
return false;
- }
Ptr.activate();
Ptr.initialize();
new (&Ptr.deref<T>()) T(Value);
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 65a9a0cdad022..9dddcced8ca38 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -395,10 +395,8 @@ def GetLocal : AccessOpcode { let HasCustomEval = 1; }
// [] -> [Pointer]
def SetLocal : AccessOpcode { let HasCustomEval = 1; }
-def Kill : Opcode {
- let Types = [];
- let Args = [];
-}
+def Kill : Opcode;
+def StartLifetime : Opcode;
def CheckDecl : Opcode {
let Args = [ArgVarDecl];
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 19770aa3b97bc..479da09004685 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -722,6 +722,14 @@ class Pointer {
getInlineDesc()->LifeState = Lifetime::Ended;
}
+ void startLifetime() const {
+ if (!isBlockPointer())
+ return;
+ if (asBlockPointer().Base < sizeof(InlineDescriptor))
+ return;
+ getInlineDesc()->LifeState = Lifetime::Started;
+ }
+
/// Compare two pointers.
ComparisonCategoryResult compare(const Pointer &Other) const {
if (!hasSameBase(*this, Other))
diff --git a/clang/test/AST/ByteCode/lifetimes26.cpp b/clang/test/AST/ByteCode/lifetimes26.cpp
new file mode 100644
index 0000000000000..a5203ae77bc13
--- /dev/null
+++ b/clang/test/AST/ByteCode/lifetimes26.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -verify=expected,both -std=c++26 %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -verify=ref,both -std=c++26 %s
+
+// both-no-diagnostics
+
+namespace std {
+ struct type_info;
+ struct destroying_delete_t {
+ explicit destroying_delete_t() = default;
+ } inline constexpr destroying_delete{};
+ struct nothrow_t {
+ explicit nothrow_t() = default;
+ } inline constexpr nothrow{};
+ using size_t = decltype(sizeof(0));
+ enum class align_val_t : size_t {};
+};
+
+constexpr void *operator new(std::size_t, void *p) { return p; }
+namespace std {
+ template<typename T> constexpr T *construct(T *p) { return new (p) T; }
+ template<typename T> constexpr void destroy(T *p) { p->~T(); }
+}
+
+constexpr bool foo() {
+ using T = bool;
+ bool b = true;
+ b.~T();
+ new (&b) bool(false);
+ return b;
+}
+static_assert(!foo());
+
+struct S {};
+constexpr bool foo2() {
+ S s;
+ s.~S();
+ new (&s) S{};
+ return true;
+}
+static_assert(foo2());
+
+constexpr void destroy_pointer() {
+ using T = int*;
+ T p;
+ p.~T();
+ std::construct(&p);
+}
+static_assert((destroy_pointer(), true));
+
More information about the cfe-commits
mailing list