Skip to content

Commit 1c6c582

Browse files
committed
[CIR][CIRGen] Support a defined pure virtual destructor
This is permitted by the language, and IRGen emits traps for destructors other than the base object destructor. Make CIRGen follow suit.
1 parent d167034 commit 1c6c582

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,7 +1109,17 @@ void CIRGenFunction::buildDestructorBody(FunctionArgList &Args) {
11091109
// in fact emit references to them from other compilations, so emit them
11101110
// as functions containing a trap instruction.
11111111
if (DtorType != Dtor_Base && Dtor->getParent()->isAbstract()) {
1112-
llvm_unreachable("NYI");
1112+
builder.create<mlir::cir::TrapOp>(getLoc(Dtor->getLocation()));
1113+
// cir.trap is a terminator. The corresponding IRGen code does this:
1114+
// builder.clearInsertionPoint();
1115+
// However, CIRGenFunction::generateCode performs the following check, which
1116+
// fails if we do the same thing here:
1117+
// assert(builder.getInsertionBlock() && "Should be valid");
1118+
// IRGen has no equivalent assert, but for now we create a block to have a
1119+
// valid insertion point.
1120+
// TODO(cir): Should the CIRGenFunction::generateCode assert be relaxed?
1121+
builder.createBlock(builder.getBlock()->getParent());
1122+
return;
11131123
}
11141124

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

17111721
if (constantCount.use_empty())
17121722
constantCount.erase();
1713-
}
1723+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
// Pure virtual functions are allowed to be defined, but the vtable should still
5+
// point to __cxa_pure_virtual instead of the definition. For destructors, the
6+
// base object destructor (which is not included in the vtable) should be
7+
// defined as usual. The complete object destructors and deleting destructors
8+
// should contain a trap, and the vtable entries for them should point to
9+
// __cxa_pure_virtual.
10+
class C {
11+
C();
12+
virtual ~C() = 0;
13+
virtual void pure() = 0;
14+
};
15+
16+
C::C() = default;
17+
C::~C() = default;
18+
void C::pure() {}
19+
20+
// CHECK: @_ZTV1C = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1C> : !cir.ptr<!u8i>
21+
// complete object destructor (D1)
22+
// CHECK-SAME: #cir.global_view<@__cxa_pure_virtual> : !cir.ptr<!u8i>,
23+
// deleting destructor (D0)
24+
// CHECK-SAME: #cir.global_view<@__cxa_pure_virtual> : !cir.ptr<!u8i>,
25+
// C::pure
26+
// CHECK-SAME: #cir.global_view<@__cxa_pure_virtual> : !cir.ptr<!u8i>]>
27+
28+
// The base object destructor should be emitted as normal.
29+
// CHECK-LABEL: cir.func @_ZN1CD2Ev(%arg0: !cir.ptr<!ty_22C22> loc({{[^)]+}})) {{.*}} {
30+
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>, ["this", init] {alignment = 8 : i64}
31+
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>
32+
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22C22>>, !cir.ptr<!ty_22C22>
33+
// CHECK-NEXT: cir.return
34+
// CHECK-NEXT: }
35+
36+
// The complete object destructor should trap.
37+
// CHECK-LABEL: cir.func @_ZN1CD1Ev(%arg0: !cir.ptr<!ty_22C22> loc({{[^)]+}})) {{.*}} {
38+
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>, ["this", init] {alignment = 8 : i64}
39+
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>
40+
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22C22>>, !cir.ptr<!ty_22C22>
41+
// CHECK-NEXT: cir.trap
42+
// CHECK-NEXT: }
43+
44+
// The deleting destructor should trap.
45+
// CHECK-LABEL: cir.func @_ZN1CD0Ev(%arg0: !cir.ptr<!ty_22C22> loc({{[^)]+}})) {{.*}} {
46+
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>, ["this", init] {alignment = 8 : i64}
47+
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>
48+
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22C22>>, !cir.ptr<!ty_22C22>
49+
// CHECK-NEXT: cir.trap
50+
// CHECK-NEXT: }
51+
52+
// C::pure should be emitted as normal.
53+
// CHECK-LABEL: cir.func @_ZN1C4pureEv(%arg0: !cir.ptr<!ty_22C22> loc({{[^)]+}})) {{.*}} {
54+
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>, ["this", init] {alignment = 8 : i64}
55+
// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr<!ty_22C22>, !cir.ptr<!cir.ptr<!ty_22C22>>
56+
// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr<!cir.ptr<!ty_22C22>>, !cir.ptr<!ty_22C22>
57+
// CHECK-NEXT: cir.return
58+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)