Skip to content

Commit 7d8ecfc

Browse files
committed
[interop] trap on uncaught C++ destructor exceptions
1 parent 56ee7c5 commit 7d8ecfc

File tree

4 files changed

+76
-2
lines changed

4 files changed

+76
-2
lines changed

lib/IRGen/GenStruct.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,21 @@ namespace {
641641
IGF.IGM.DataLayout);
642642
args.push_back(implicitParam);
643643
}
644+
bool canThrow = false;
645+
if (auto *fpt =
646+
destructor->getType()->getAs<clang::FunctionProtoType>()) {
647+
if (!fpt->isNothrow())
648+
canThrow = true;
649+
}
650+
if (canThrow) {
651+
auto *invokeNormalDest = IGF.createBasicBlock("invoke.cont");
652+
auto *invokeUnwindDest = IGF.createExceptionUnwindBlock();
653+
IGF.Builder.createInvoke(destructorFnAddr->getFunctionType(),
654+
destructorFnAddr, args, invokeNormalDest,
655+
invokeUnwindDest);
656+
IGF.Builder.emitBlock(invokeNormalDest);
657+
return;
658+
}
644659

645660
IGF.Builder.CreateCall(destructorFnAddr->getFunctionType(),
646661
destructorFnAddr, args);

lib/IRGen/IRBuilder.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class IRBuilder : public IRBuilderBase {
5252
// Set calling convention of the call instruction using
5353
// the same calling convention as the callee function.
5454
// This ensures that they are always compatible.
55-
void setCallingConvUsingCallee(llvm::CallInst *Call) {
55+
void setCallingConvUsingCallee(llvm::CallBase *Call) {
5656
auto CalleeFn = Call->getCalledFunction();
5757
if (CalleeFn) {
5858
auto CC = CalleeFn->getCallingConv();
@@ -337,6 +337,17 @@ class IRBuilder : public IRBuilderBase {
337337
return Call;
338338
}
339339

340+
llvm::InvokeInst *
341+
createInvoke(llvm::FunctionType *fTy, llvm::Constant *callee,
342+
ArrayRef<llvm::Value *> args, llvm::BasicBlock *invokeNormalDest,
343+
llvm::BasicBlock *invokeUnwindDest, const Twine &name = "") {
344+
assert((!DebugInfo || getCurrentDebugLocation()) && "no debugloc on call");
345+
auto call = IRBuilderBase::CreateInvoke(fTy, callee, invokeNormalDest,
346+
invokeUnwindDest, args, name);
347+
setCallingConvUsingCallee(call);
348+
return call;
349+
}
350+
340351
llvm::CallBase *CreateCallOrInvoke(const FunctionPointer &fn,
341352
ArrayRef<llvm::Value *> args,
342353
llvm::BasicBlock *invokeNormalDest,

test/Interop/Cxx/exceptions/trap-on-exception-execution.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,9 @@ TrapOnExecutionTestSuite.test("TestClassWithSubscript") {
8888
let _ = v[1]
8989
}
9090

91+
TrapOnExecutionTestSuite.test("TestClassWithThrowingDestructor") {
92+
expectCrashLater()
93+
let _ = ClassWithThrowingDestructor()
94+
}
95+
9196
runAllTests()

test/Interop/Cxx/exceptions/trap-on-exception-irgen-itanium.swift

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,22 @@ public:
8989
}
9090
};
9191

92+
class ClassWithDestructor {
93+
int m = 0;
94+
public:
95+
inline ~ClassWithDestructor() {
96+
(void)freeFunctionNoThrow(0);
97+
}
98+
};
99+
100+
class ClassWithThrowingDestructor {
101+
int m = 0;
102+
public:
103+
inline ~ClassWithThrowingDestructor() noexcept(false) {
104+
throw 2;
105+
}
106+
};
107+
92108
//--- test.swift
93109

94110
import CxxModule
@@ -155,6 +171,14 @@ func testSubscriptThunkInvoke() -> CInt {
155171
return v[0]
156172
}
157173

174+
func testClassWithDestructor() {
175+
let _ = ClassWithDestructor()
176+
}
177+
178+
func testClassWithThrowingDestructor() {
179+
let _ = ClassWithThrowingDestructor()
180+
}
181+
158182
let _ = testFreeFunctionNoThrowOnly()
159183
let _ = testFreeFunctionCalls()
160184
let _ = testMethodCalls()
@@ -163,6 +187,8 @@ testFuncPtrCall()
163187
testCFuncPtrCall()
164188
testProtocolConformanceThunkInvoke()
165189
let _ = testSubscriptThunkInvoke()
190+
testClassWithDestructor()
191+
testClassWithThrowingDestructor()
166192

167193
// CHECK: define {{.*}} @"$s4test0A23FreeFunctionNoThrowOnlys5Int32VyF"() #[[#SWIFTMETA:]] {
168194
// CHECK-NEXT: :
@@ -257,7 +283,7 @@ let _ = testSubscriptThunkInvoke()
257283
// CHECK-NEXT: ret i32
258284
// CHECK-EMPTY:
259285
// CHECK-NEXT: [[UNWIND30]]:
260-
// CHECK-NEXT: %4 = landingpad { i8*, i32 }
286+
// CHECK-NEXT: landingpad { i8*, i32 }
261287
// CHECK-NEXT: catch i8* null
262288
// CHECK-NEXT: call void @llvm.trap()
263289
// CHECK-NEXT: unreachable
@@ -270,6 +296,23 @@ let _ = testSubscriptThunkInvoke()
270296
// CHECK: define {{.*}} @"$s4test0A20SubscriptThunkInvokes5Int32VyF"() #[[#SWIFTUWMETA]]
271297
// CHECK: invoke i32 @_ZNK18ClassWithSubscriptixEi
272298

299+
// CHECK: define {{.*}} @"$s4test0A19ClassWithDestructoryyF"() #[[#SWIFTMETA]]
300+
// CHECK-NOT: invoke
301+
// CHECK: }
302+
303+
// CHECK: define {{.*}} @"$s4test0A27ClassWithThrowingDestructoryyF"() #[[#SWIFTUWMETA]]
304+
// CHECK: invoke {{.*}} @_ZN27ClassWithThrowingDestructorD{{.*}}(
305+
// CHECK-NEXT: to label %[[CONT40:.*]] unwind label %[[UNWIND40:.*]]
306+
// CHECK: [[CONT40]]:
307+
// CHECK: ret void
308+
// CHECK-EMPTY:
309+
// CHECK-NEXT: [[UNWIND40]]:
310+
// CHECK-NEXT: landingpad { i8*, i32 }
311+
// CHECK-NEXT: catch i8* null
312+
// CHECK-NEXT: call void @llvm.trap()
313+
// CHECK-NEXT: unreachable
314+
// CHECK-NEXT: }
315+
273316
// CHECK: i32 @__gxx_personality_v0(...)
274317

275318
// CHECK: attributes #[[#SWIFTMETA]] = {

0 commit comments

Comments
 (0)