From 1c6c5829e6ff8f802f9764639cb43eeb4f2c2d3e Mon Sep 17 00:00:00 2001 From: Shoaib Meenai Date: Fri, 6 Sep 2024 16:38:06 -0700 Subject: [PATCH] [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. --- clang/lib/CIR/CodeGen/CIRGenClass.cpp | 14 ++++- .../CIR/CodeGen/defined-pure-virtual-func.cpp | 58 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 clang/test/CIR/CodeGen/defined-pure-virtual-func.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index ca2a5bc22936..d932843e5b3d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -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(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(); @@ -1710,4 +1720,4 @@ void CIRGenFunction::buildCXXAggrConstructorCall( if (constantCount.use_empty()) constantCount.erase(); -} \ No newline at end of file +} diff --git a/clang/test/CIR/CodeGen/defined-pure-virtual-func.cpp b/clang/test/CIR/CodeGen/defined-pure-virtual-func.cpp new file mode 100644 index 000000000000..e8910af83319 --- /dev/null +++ b/clang/test/CIR/CodeGen/defined-pure-virtual-func.cpp @@ -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 : !cir.ptr, #cir.global_view<@_ZTI1C> : !cir.ptr +// complete object destructor (D1) +// CHECK-SAME: #cir.global_view<@__cxa_pure_virtual> : !cir.ptr, +// deleting destructor (D0) +// CHECK-SAME: #cir.global_view<@__cxa_pure_virtual> : !cir.ptr, +// C::pure +// CHECK-SAME: #cir.global_view<@__cxa_pure_virtual> : !cir.ptr]> + +// The base object destructor should be emitted as normal. +// CHECK-LABEL: cir.func @_ZN1CD2Ev(%arg0: !cir.ptr loc({{[^)]+}})) {{.*}} { +// CHECK-NEXT: %0 = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] {alignment = 8 : i64} +// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr, !cir.ptr> +// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr>, !cir.ptr +// CHECK-NEXT: cir.return +// CHECK-NEXT: } + +// The complete object destructor should trap. +// CHECK-LABEL: cir.func @_ZN1CD1Ev(%arg0: !cir.ptr loc({{[^)]+}})) {{.*}} { +// CHECK-NEXT: %0 = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] {alignment = 8 : i64} +// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr, !cir.ptr> +// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr>, !cir.ptr +// CHECK-NEXT: cir.trap +// CHECK-NEXT: } + +// The deleting destructor should trap. +// CHECK-LABEL: cir.func @_ZN1CD0Ev(%arg0: !cir.ptr loc({{[^)]+}})) {{.*}} { +// CHECK-NEXT: %0 = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] {alignment = 8 : i64} +// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr, !cir.ptr> +// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr>, !cir.ptr +// CHECK-NEXT: cir.trap +// CHECK-NEXT: } + +// C::pure should be emitted as normal. +// CHECK-LABEL: cir.func @_ZN1C4pureEv(%arg0: !cir.ptr loc({{[^)]+}})) {{.*}} { +// CHECK-NEXT: %0 = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] {alignment = 8 : i64} +// CHECK-NEXT: cir.store %arg0, %0 : !cir.ptr, !cir.ptr> +// CHECK-NEXT: %1 = cir.load %0 : !cir.ptr>, !cir.ptr +// CHECK-NEXT: cir.return +// CHECK-NEXT: }