[clang] [clang][bytecode] Recursively start lifetimes as well (PR #141742)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Wed May 28 03:49:27 PDT 2025
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/141742
The constructor starts the lifetime of all the subobjects.
>From 11f8d2e4c3282a9b49f9cf08b7d16f6c32e08134 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 28 May 2025 11:27:39 +0200
Subject: [PATCH] [clang][bytecode] Recursively start lifetimes as well
The constructor starts the lifetime of all the subobjects.
---
clang/lib/AST/ByteCode/DynamicAllocator.cpp | 8 ++--
clang/lib/AST/ByteCode/Interp.cpp | 37 ++++++++++++++----
clang/test/AST/ByteCode/placement-new.cpp | 42 +++++++++++++++++++++
3 files changed, 77 insertions(+), 10 deletions(-)
diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.cpp b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
index 733984508ed79..4f0f511fb6164 100644
--- a/clang/lib/AST/ByteCode/DynamicAllocator.cpp
+++ b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
@@ -86,9 +86,11 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
ID->IsInitialized = false;
ID->IsVolatile = false;
- ID->LifeState =
- AllocForm == Form::Operator ? Lifetime::Ended : Lifetime::Started;
- ;
+ if (D->isCompositeArray())
+ ID->LifeState = Lifetime::Started;
+ else
+ ID->LifeState =
+ AllocForm == Form::Operator ? Lifetime::Ended : Lifetime::Started;
B->IsDynamic = true;
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index e454d9e3bc218..a8286dd75f09a 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1699,20 +1699,38 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
return Call(S, OpPC, F, VarArgSize);
}
+static void startLifetimeRecurse(const Pointer &Ptr) {
+ if (const Record *R = Ptr.getRecord()) {
+ Ptr.startLifetime();
+ for (const Record::Field &Fi : R->fields())
+ startLifetimeRecurse(Ptr.atField(Fi.Offset));
+ return;
+ }
+
+ if (const Descriptor *FieldDesc = Ptr.getFieldDesc();
+ FieldDesc->isCompositeArray()) {
+ assert(Ptr.getLifetime() == Lifetime::Started);
+ for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I)
+ startLifetimeRecurse(Ptr.atIndex(I).narrow());
+ return;
+ }
+
+ Ptr.startLifetime();
+}
+
bool StartLifetime(InterpState &S, CodePtr OpPC) {
const auto &Ptr = S.Stk.peek<Pointer>();
if (!CheckDummy(S, OpPC, Ptr, AK_Destroy))
return false;
-
- Ptr.startLifetime();
+ startLifetimeRecurse(Ptr.narrow());
return true;
}
// FIXME: It might be better to the recursing as part of the generated code for
// a destructor?
static void endLifetimeRecurse(const Pointer &Ptr) {
- Ptr.endLifetime();
if (const Record *R = Ptr.getRecord()) {
+ Ptr.endLifetime();
for (const Record::Field &Fi : R->fields())
endLifetimeRecurse(Ptr.atField(Fi.Offset));
return;
@@ -1720,9 +1738,14 @@ static void endLifetimeRecurse(const Pointer &Ptr) {
if (const Descriptor *FieldDesc = Ptr.getFieldDesc();
FieldDesc->isCompositeArray()) {
+ // No endLifetime() for array roots.
+ assert(Ptr.getLifetime() == Lifetime::Started);
for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I)
endLifetimeRecurse(Ptr.atIndex(I).narrow());
+ return;
}
+
+ Ptr.endLifetime();
}
/// Ends the lifetime of the peek'd pointer.
@@ -1730,7 +1753,7 @@ bool EndLifetime(InterpState &S, CodePtr OpPC) {
const auto &Ptr = S.Stk.peek<Pointer>();
if (!CheckDummy(S, OpPC, Ptr, AK_Destroy))
return false;
- endLifetimeRecurse(Ptr);
+ endLifetimeRecurse(Ptr.narrow());
return true;
}
@@ -1739,7 +1762,7 @@ bool EndLifetimePop(InterpState &S, CodePtr OpPC) {
const auto &Ptr = S.Stk.pop<Pointer>();
if (!CheckDummy(S, OpPC, Ptr, AK_Destroy))
return false;
- endLifetimeRecurse(Ptr);
+ endLifetimeRecurse(Ptr.narrow());
return true;
}
@@ -1758,9 +1781,9 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
// CheckLifetime for this and all base pointers.
for (Pointer P = Ptr;;) {
- if (!CheckLifetime(S, OpPC, P, AK_Construct)) {
+ if (!CheckLifetime(S, OpPC, P, AK_Construct))
return false;
- }
+
if (P.isRoot())
break;
P = P.getBase();
diff --git a/clang/test/AST/ByteCode/placement-new.cpp b/clang/test/AST/ByteCode/placement-new.cpp
index a301c96739c83..f23d71510602c 100644
--- a/clang/test/AST/ByteCode/placement-new.cpp
+++ b/clang/test/AST/ByteCode/placement-new.cpp
@@ -423,3 +423,45 @@ namespace SubObj {
}
static_assert(construct_after_lifetime_2()); // both-error {{}} both-note {{in call}}
}
+
+namespace RecursiveLifetimeStart {
+ struct B {
+ int b;
+ };
+
+ struct A {
+ B b;
+ int a;
+ };
+
+ constexpr int foo() {
+ A a;
+ a.~A();
+
+ new (&a) A();
+ a.a = 10;
+ a.b.b = 12;
+ return a.a;
+ }
+ static_assert(foo() == 10);
+}
+
+namespace ArrayRoot {
+ struct S {
+ int a;
+ };
+ constexpr int foo() {
+ S* ss = std::allocator<S>().allocate(2);
+ new (ss) S{};
+ new (ss + 1) S{};
+
+ S* ps = &ss[2];
+ ps = ss;
+ ps->~S();
+
+ std::allocator<S>().deallocate(ss);
+ return 0;
+ }
+
+ static_assert(foo() == 0);
+}
More information about the cfe-commits
mailing list