Skip to content

Commit d4dfeb3

Browse files
authored
[clang][Interp] Compile field+base destruction into class dtor func (#102871)
1 parent 7c36411 commit d4dfeb3

File tree

6 files changed

+93
-68
lines changed

6 files changed

+93
-68
lines changed

clang/lib/AST/Interp/Compiler.cpp

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4856,13 +4856,59 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
48564856
return this->emitRetVoid(SourceInfo{});
48574857
}
48584858

4859+
template <class Emitter>
4860+
bool Compiler<Emitter>::compileDestructor(const CXXDestructorDecl *Dtor) {
4861+
const RecordDecl *RD = Dtor->getParent();
4862+
const Record *R = this->getRecord(RD);
4863+
if (!R)
4864+
return false;
4865+
4866+
if (!Dtor->isTrivial() && Dtor->getBody()) {
4867+
if (!this->visitStmt(Dtor->getBody()))
4868+
return false;
4869+
}
4870+
4871+
if (!this->emitThis(Dtor))
4872+
return false;
4873+
4874+
assert(R);
4875+
if (!R->isUnion()) {
4876+
// First, destroy all fields.
4877+
for (const Record::Field &Field : llvm::reverse(R->fields())) {
4878+
const Descriptor *D = Field.Desc;
4879+
if (!D->isPrimitive() && !D->isPrimitiveArray()) {
4880+
if (!this->emitGetPtrField(Field.Offset, SourceInfo{}))
4881+
return false;
4882+
if (!this->emitDestruction(D))
4883+
return false;
4884+
if (!this->emitPopPtr(SourceInfo{}))
4885+
return false;
4886+
}
4887+
}
4888+
}
4889+
4890+
for (const Record::Base &Base : llvm::reverse(R->bases())) {
4891+
if (!this->emitGetPtrBase(Base.Offset, SourceInfo{}))
4892+
return false;
4893+
if (!this->emitRecordDestruction(Base.R))
4894+
return false;
4895+
if (!this->emitPopPtr(SourceInfo{}))
4896+
return false;
4897+
}
4898+
4899+
// FIXME: Virtual bases.
4900+
return this->emitPopPtr(Dtor) && this->emitRetVoid(Dtor);
4901+
}
4902+
48594903
template <class Emitter>
48604904
bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
48614905
// Classify the return type.
48624906
ReturnType = this->classify(F->getReturnType());
48634907

48644908
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(F))
48654909
return this->compileConstructor(Ctor);
4910+
if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(F))
4911+
return this->compileDestructor(Dtor);
48664912

48674913
// Emit custom code if this is a lambda static invoker.
48684914
if (const auto *MD = dyn_cast<CXXMethodDecl>(F);
@@ -5573,46 +5619,19 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS,
55735619
template <class Emitter>
55745620
bool Compiler<Emitter>::emitRecordDestruction(const Record *R) {
55755621
assert(R);
5576-
if (!R->isUnion()) {
5577-
// First, destroy all fields.
5578-
for (const Record::Field &Field : llvm::reverse(R->fields())) {
5579-
const Descriptor *D = Field.Desc;
5580-
if (!D->isPrimitive() && !D->isPrimitiveArray()) {
5581-
if (!this->emitGetPtrField(Field.Offset, SourceInfo{}))
5582-
return false;
5583-
if (!this->emitDestruction(D))
5584-
return false;
5585-
if (!this->emitPopPtr(SourceInfo{}))
5586-
return false;
5587-
}
5588-
}
5589-
}
5590-
5591-
// Now emit the destructor and recurse into base classes.
5592-
if (const CXXDestructorDecl *Dtor = R->getDestructor();
5593-
Dtor && !Dtor->isTrivial()) {
5594-
const Function *DtorFunc = getFunction(Dtor);
5595-
if (!DtorFunc)
5596-
return false;
5597-
assert(DtorFunc->hasThisPointer());
5598-
assert(DtorFunc->getNumParams() == 1);
5599-
if (!this->emitDupPtr(SourceInfo{}))
5600-
return false;
5601-
if (!this->emitCall(DtorFunc, 0, SourceInfo{}))
5602-
return false;
5603-
}
5604-
5605-
for (const Record::Base &Base : llvm::reverse(R->bases())) {
5606-
if (!this->emitGetPtrBase(Base.Offset, SourceInfo{}))
5607-
return false;
5608-
if (!this->emitRecordDestruction(Base.R))
5609-
return false;
5610-
if (!this->emitPopPtr(SourceInfo{}))
5611-
return false;
5612-
}
5622+
const CXXDestructorDecl *Dtor = R->getDestructor();
5623+
if (!Dtor || Dtor->isTrivial())
5624+
return true;
56135625

5614-
// FIXME: Virtual bases.
5615-
return true;
5626+
assert(Dtor);
5627+
const Function *DtorFunc = getFunction(Dtor);
5628+
if (!DtorFunc)
5629+
return false;
5630+
assert(DtorFunc->hasThisPointer());
5631+
assert(DtorFunc->getNumParams() == 1);
5632+
if (!this->emitDupPtr(SourceInfo{}))
5633+
return false;
5634+
return this->emitCall(DtorFunc, 0, SourceInfo{});
56165635
}
56175636
/// When calling this, we have a pointer of the local-to-destroy
56185637
/// on the stack.

clang/lib/AST/Interp/Compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
358358
const QualType DerivedType);
359359
bool emitLambdaStaticInvokerBody(const CXXMethodDecl *MD);
360360
bool compileConstructor(const CXXConstructorDecl *Ctor);
361+
bool compileDestructor(const CXXDestructorDecl *Dtor);
361362

362363
bool checkLiteralType(const Expr *E);
363364

clang/lib/AST/Interp/Interp.cpp

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -871,23 +871,6 @@ static bool runRecordDestructor(InterpState &S, CodePtr OpPC,
871871
return false;
872872
}
873873

874-
// Fields.
875-
for (const Record::Field &Field : llvm::reverse(R->fields())) {
876-
const Descriptor *D = Field.Desc;
877-
if (D->isRecord()) {
878-
if (!runRecordDestructor(S, OpPC, BasePtr.atField(Field.Offset), D))
879-
return false;
880-
} else if (D->isCompositeArray()) {
881-
const Descriptor *ElemDesc = Desc->ElemDesc;
882-
assert(ElemDesc->isRecord());
883-
for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
884-
if (!runRecordDestructor(S, OpPC, BasePtr.atIndex(I).narrow(),
885-
ElemDesc))
886-
return false;
887-
}
888-
}
889-
}
890-
891874
// Destructor of this record.
892875
if (const CXXDestructorDecl *Dtor = R->getDestructor();
893876
Dtor && !Dtor->isTrivial()) {
@@ -899,13 +882,6 @@ static bool runRecordDestructor(InterpState &S, CodePtr OpPC,
899882
if (!Call(S, OpPC, DtorFunc, 0))
900883
return false;
901884
}
902-
903-
// Bases.
904-
for (const Record::Base &Base : llvm::reverse(R->bases())) {
905-
if (!runRecordDestructor(S, OpPC, BasePtr.atField(Base.Offset), Base.Desc))
906-
return false;
907-
}
908-
909885
return true;
910886
}
911887

clang/test/AST/Interp/cxx20.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,6 @@ namespace DefinitionLoc {
859859
// both-note {{non-constexpr constructor}}
860860
}
861861

862-
/// FIXME: Call base dtors when explicitly calling dtor.
863862
namespace VirtDtor {
864863
class B {
865864
public:
@@ -900,5 +899,5 @@ namespace VirtDtor {
900899
return buff[0] == a && buff[1] == b;
901900
}
902901

903-
static_assert(test('C', 'B')); // expected-error {{failed}}
902+
static_assert(test('C', 'B'));
904903
}

clang/test/AST/Interp/new-delete.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -514,8 +514,7 @@ namespace DeleteRunsDtors {
514514
static_assert(abc2() == 1);
515515
}
516516

517-
/// FIXME: There is a slight difference in diagnostics here, because we don't
518-
/// create a new frame when we delete record fields or bases at all.
517+
/// FIXME: There is a slight difference in diagnostics here.
519518
namespace FaultyDtorCalledByDelete {
520519
struct InnerFoo {
521520
int *mem;
@@ -536,7 +535,7 @@ namespace FaultyDtorCalledByDelete {
536535
a = new int(13);
537536
IF.mem = new int(100);
538537
}
539-
constexpr ~Foo() { delete a; }
538+
constexpr ~Foo() { delete a; } // expected-note {{in call to}}
540539
};
541540

542541
constexpr int abc() {

clang/test/AST/Interp/records.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1595,4 +1595,35 @@ namespace VirtDtor {
15951595
}
15961596
static_assert(virt_dtor(0, "ZYX"));
15971597
}
1598+
1599+
namespace DtorDestroysFieldsAfterSelf {
1600+
struct S {
1601+
int a = 10;
1602+
constexpr ~S() {
1603+
a = 0;
1604+
}
1605+
1606+
};
1607+
struct F {
1608+
S s;
1609+
int a;
1610+
int &b;
1611+
constexpr F(int a, int &b) : a(a), b(b) {}
1612+
constexpr ~F() {
1613+
b += s.a;
1614+
}
1615+
};
1616+
1617+
constexpr int foo() {
1618+
int a = 10;
1619+
int b = 5;
1620+
{
1621+
F f(a, b);
1622+
}
1623+
1624+
return b;
1625+
}
1626+
1627+
static_assert(foo() == 15);
1628+
}
15981629
#endif

0 commit comments

Comments
 (0)