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)); + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits