Skip to content

Commit ee25a85

Browse files
authored
[clang][bytecode] Handle CXXPseudoDestructorExprs (llvm#125835)
Make lifetime management more explicit. We're only using this for CXXPseudoDestructorExprs for now but we need this to handle std::construct_at/placement-new after destructor calls later anyway.
1 parent baf2786 commit ee25a85

File tree

7 files changed

+93
-2
lines changed

7 files changed

+93
-2
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4715,6 +4715,14 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
47154715
} else if (!this->visit(MC->getImplicitObjectArgument())) {
47164716
return false;
47174717
}
4718+
} else if (const auto *PD =
4719+
dyn_cast<CXXPseudoDestructorExpr>(E->getCallee())) {
4720+
const Expr *Base = PD->getBase();
4721+
if (!Base->isGLValue())
4722+
return this->discard(Base);
4723+
if (!this->visit(Base))
4724+
return false;
4725+
return this->emitKill(E);
47184726
} else if (!FuncDecl) {
47194727
const Expr *Callee = E->getCallee();
47204728
CalleeOffset = this->allocateLocalPrimitive(Callee, PT_FnPtr, true, false);

clang/lib/AST/ByteCode/Descriptor.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ struct alignas(void *) GlobalInlineDescriptor {
6161
};
6262
static_assert(sizeof(GlobalInlineDescriptor) == sizeof(void *), "");
6363

64+
enum class Lifetime : uint8_t {
65+
Started,
66+
Ended,
67+
};
68+
6469
/// Inline descriptor embedded in structures and arrays.
6570
///
6671
/// Such descriptors precede all composite array elements and structure fields.
@@ -100,12 +105,14 @@ struct InlineDescriptor {
100105
LLVM_PREFERRED_TYPE(bool)
101106
unsigned IsArrayElement : 1;
102107

108+
Lifetime LifeState;
109+
103110
const Descriptor *Desc;
104111

105112
InlineDescriptor(const Descriptor *D)
106113
: Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false),
107114
IsBase(false), IsActive(false), IsFieldMutable(false),
108-
IsArrayElement(false), Desc(D) {}
115+
IsArrayElement(false), LifeState(Lifetime::Started), Desc(D) {}
109116

110117
void dump() const { dump(llvm::errs()); }
111118
void dump(llvm::raw_ostream &OS) const;

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,18 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
561561
return false;
562562
}
563563

564+
static bool CheckLifetime(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
565+
AccessKinds AK) {
566+
if (Ptr.getLifetime() == Lifetime::Started)
567+
return true;
568+
569+
if (!S.checkingPotentialConstantExpression()) {
570+
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_uninit)
571+
<< AK << /*uninitialized=*/false << S.Current->getRange(OpPC);
572+
}
573+
return false;
574+
}
575+
564576
bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
565577
if (Ptr.isInitialized())
566578
return true;
@@ -605,6 +617,8 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
605617
return false;
606618
if (!CheckActive(S, OpPC, Ptr, AK))
607619
return false;
620+
if (!CheckLifetime(S, OpPC, Ptr, AK))
621+
return false;
608622
if (!CheckInitialized(S, OpPC, Ptr, AK))
609623
return false;
610624
if (!CheckTemporary(S, OpPC, Ptr, AK))
@@ -634,6 +648,8 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
634648
return false;
635649
if (!CheckActive(S, OpPC, Ptr, AK_Read))
636650
return false;
651+
if (!CheckLifetime(S, OpPC, Ptr, AK_Read))
652+
return false;
637653
if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
638654
return false;
639655
if (!CheckTemporary(S, OpPC, Ptr, AK_Read))
@@ -650,6 +666,8 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
650666
return false;
651667
if (!CheckDummy(S, OpPC, Ptr, AK_Assign))
652668
return false;
669+
if (!CheckLifetime(S, OpPC, Ptr, AK_Assign))
670+
return false;
653671
if (!CheckExtern(S, OpPC, Ptr))
654672
return false;
655673
if (!CheckRange(S, OpPC, Ptr, AK_Assign))

clang/lib/AST/ByteCode/Interp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,12 @@ bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
12541254
return true;
12551255
}
12561256

1257+
static inline bool Kill(InterpState &S, CodePtr OpPC) {
1258+
const auto &Ptr = S.Stk.pop<Pointer>();
1259+
Ptr.endLifetime();
1260+
return true;
1261+
}
1262+
12571263
/// 1) Pops the value from the stack.
12581264
/// 2) Writes the value to the local variable with the
12591265
/// given offset.

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,11 @@ def GetLocal : AccessOpcode { let HasCustomEval = 1; }
394394
// [] -> [Pointer]
395395
def SetLocal : AccessOpcode { let HasCustomEval = 1; }
396396

397+
def Kill : Opcode {
398+
let Types = [];
399+
let Args = [];
400+
}
401+
397402
def CheckDecl : Opcode {
398403
let Args = [ArgVarDecl];
399404
}

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,22 @@ class Pointer {
687687
/// Deactivates an entire strurcutre.
688688
void deactivate() const;
689689

690+
Lifetime getLifetime() const {
691+
if (!isBlockPointer())
692+
return Lifetime::Started;
693+
if (asBlockPointer().Base < sizeof(InlineDescriptor))
694+
return Lifetime::Started;
695+
return getInlineDesc()->LifeState;
696+
}
697+
698+
void endLifetime() const {
699+
if (!isBlockPointer())
700+
return;
701+
if (asBlockPointer().Base < sizeof(InlineDescriptor))
702+
return;
703+
getInlineDesc()->LifeState = Lifetime::Ended;
704+
}
705+
690706
/// Compare two pointers.
691707
ComparisonCategoryResult compare(const Pointer &Other) const {
692708
if (!hasSameBase(*this, Other))

clang/test/AST/ByteCode/cxx20.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -fcxx-exceptions -fexperimental-new-constant-interpreter -std=c++20 -verify=both,expected -fcxx-exceptions %s
1+
// RUN: %clang_cc1 -fcxx-exceptions -fexperimental-new-constant-interpreter -std=c++20 -verify=both,expected -fcxx-exceptions %s -DNEW_INTERP
22
// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=both,ref -fcxx-exceptions %s
33

44
void test_alignas_operand() {
@@ -931,3 +931,34 @@ namespace LocalDestroy {
931931
}
932932
static_assert(f() == 1);
933933
}
934+
935+
namespace PseudoDtor {
936+
constexpr int f1() {
937+
using T = int;
938+
int a = 0;
939+
a.~T();
940+
return a; // both-note {{read of object outside its lifetime}}
941+
}
942+
static_assert(f1() == 0); // both-error {{not an integral constant expression}} \
943+
// both-note {{in call to}}
944+
945+
constexpr int f2() {
946+
using T = int;
947+
int a = 0;
948+
a.~T();
949+
a = 0; // both-note {{assignment to object outside its lifetime}}
950+
return a;
951+
}
952+
static_assert(f2() == 0); // both-error {{not an integral constant expression}} \
953+
// both-note {{in call to}}
954+
955+
#ifdef NEW_INTERP
956+
/// FIXME: Currently crashes with the current interpreter, see https://github.com/llvm/llvm-project/issues/53741
957+
constexpr int f3() {
958+
using T = int;
959+
0 .~T();
960+
return 0;
961+
}
962+
static_assert(f3() == 0);
963+
#endif
964+
}

0 commit comments

Comments
 (0)