Skip to content

Commit

Permalink
[CIR][CIRGen] Support a defined pure virtual destructor
Browse files Browse the repository at this point in the history
This is permitted by the language, and IRGen emits traps for destructors
other than the base object destructor. Make CIRGen follow suit.
  • Loading branch information
smeenai committed Sep 6, 2024
1 parent d167034 commit 1c6c582
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 2 deletions.
14 changes: 12 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,17 @@ void CIRGenFunction::buildDestructorBody(FunctionArgList &Args) {
// in fact emit references to them from other compilations, so emit them
// as functions containing a trap instruction.
if (DtorType != Dtor_Base && Dtor->getParent()->isAbstract()) {
llvm_unreachable("NYI");
builder.create<mlir::cir::TrapOp>(getLoc(Dtor->getLocation()));
// cir.trap is a terminator. The corresponding IRGen code does this:
// builder.clearInsertionPoint();
// However, CIRGenFunction::generateCode performs the following check, which
// fails if we do the same thing here:
// assert(builder.getInsertionBlock() && "Should be valid");
// IRGen has no equivalent assert, but for now we create a block to have a
// valid insertion point.
// TODO(cir): Should the CIRGenFunction::generateCode assert be relaxed?
builder.createBlock(builder.getBlock()->getParent());
return;
}

Stmt *Body = Dtor->getBody();
Expand Down Expand Up @@ -1710,4 +1720,4 @@ void CIRGenFunction::buildCXXAggrConstructorCall(

if (constantCount.use_empty())
constantCount.erase();
}
}
58 changes: 58 additions & 0 deletions clang/test/CIR/CodeGen/defined-pure-virtual-func.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s

// Pure virtual functions are allowed to be defined, but the vtable should still
// point to __cxa_pure_virtual instead of the definition. For destructors, the
// base object destructor (which is not included in the vtable) should be
// defined as usual. The complete object destructors and deleting destructors
// should contain a trap, and the vtable entries for them should point to
// __cxa_pure_virtual.
class C {
C();
virtual ~C() = 0;
virtual void pure() = 0;
};

C::C() = default;
C::~C() = default;
void C::pure() {}

// CHECK: @_ZTV1C = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1C> : !cir.ptr<!u8i>
// complete object destructor (D1)
// CHECK-SAME: #cir.global_view<@__cxa_pure_virtual> : !cir.ptr<!u8i>,
// deleting destructor (D0)
// CHECK-SAME: #cir.global_view<@__cxa_pure_virtual> : !cir.ptr<!u8i>,
// C::pure
// CHECK-SAME: #cir.global_view<@__cxa_pure_virtual> : !cir.ptr<!u8i>]>

// The base object destructor should be emitted as normal.
// CHECK-LABEL: cir.func @_ZN1CD2Ev(%arg0: !cir.ptr<!ty_22C22> loc({{[^)]+}})) {{.*}} {
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>, ["this", init] {alignment = 8 : i64}
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22C22>>, !cir.ptr<!ty_22C22>
// CHECK-NEXT: cir.return
// CHECK-NEXT: }

// The complete object destructor should trap.
// CHECK-LABEL: cir.func @_ZN1CD1Ev(%arg0: !cir.ptr<!ty_22C22> loc({{[^)]+}})) {{.*}} {
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>, ["this", init] {alignment = 8 : i64}
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22C22>>, !cir.ptr<!ty_22C22>
// CHECK-NEXT: cir.trap
// CHECK-NEXT: }

// The deleting destructor should trap.
// CHECK-LABEL: cir.func @_ZN1CD0Ev(%arg0: !cir.ptr<!ty_22C22> loc({{[^)]+}})) {{.*}} {
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>, ["this", init] {alignment = 8 : i64}
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22C22>>, !cir.ptr<!ty_22C22>
// CHECK-NEXT: cir.trap
// CHECK-NEXT: }

// C::pure should be emitted as normal.
// CHECK-LABEL: cir.func @_ZN1C4pureEv(%arg0: !cir.ptr<!ty_22C22> loc({{[^)]+}})) {{.*}} {
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>, ["this", init] {alignment = 8 : i64}
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22C22>>, !cir.ptr<!ty_22C22>
// CHECK-NEXT: cir.return
// CHECK-NEXT: }

0 comments on commit 1c6c582

Please sign in to comment.