Skip to content

Commit 355f00c

Browse files
committed
[interop] ensure that call to a C++ constructor traps on uncaught exception
1 parent 8998f97 commit 355f00c

File tree

9 files changed

+113
-10
lines changed

9 files changed

+113
-10
lines changed

lib/IRGen/Callee.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,10 @@ namespace irgen {
335335
// function.
336336
bool isForeignNoThrow = false;
337337

338+
// True when this function pointer points to a foreign function that traps
339+
// on exception in the always_inline thunk.
340+
bool foreignCallCatchesExceptionInThunk = false;
341+
338342
explicit FunctionPointer(Kind kind, llvm::Value *value,
339343
const Signature &signature)
340344
: FunctionPointer(kind, value, PointerAuthInfo(), signature) {}
@@ -501,6 +505,18 @@ namespace irgen {
501505
bool canThrowForeignException() const {
502506
return getForeignInfo().canThrow && !isForeignNoThrow;
503507
}
508+
509+
void setForeignCallCatchesExceptionInThunk() {
510+
foreignCallCatchesExceptionInThunk = true;
511+
}
512+
513+
bool doesForeignCallCatchExceptionInThunk() {
514+
return foreignCallCatchesExceptionInThunk;
515+
}
516+
517+
bool shouldUseInvoke() const {
518+
return canThrowForeignException() && !foreignCallCatchesExceptionInThunk;
519+
}
504520
};
505521

506522
class Callee {

lib/IRGen/GenCall.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2928,8 +2928,11 @@ llvm::CallBase *CallEmission::emitCallSite() {
29282928
}
29292929

29302930
if (fn.canThrowForeignException()) {
2931-
invokeNormalDest = IGF.createBasicBlock("invoke.cont");
2932-
invokeUnwindDest = IGF.createExceptionUnwindBlock();
2931+
if (!fn.doesForeignCallCatchExceptionInThunk()) {
2932+
invokeNormalDest = IGF.createBasicBlock("invoke.cont");
2933+
invokeUnwindDest = IGF.createExceptionUnwindBlock();
2934+
} else
2935+
IGF.setCallsThunksWithForeignExceptionTraps();
29332936
}
29342937
auto call = createCall(fn, Args);
29352938
if (invokeNormalDest)
@@ -3012,7 +3015,7 @@ llvm::CallBase *IRBuilder::CreateCallOrInvoke(
30123015
assert(!isTrapIntrinsic(fn.getRawPointer()) && "Use CreateNonMergeableTrap");
30133016
auto fnTy = cast<llvm::FunctionType>(fn.getFunctionType());
30143017
llvm::CallBase *call;
3015-
if (!fn.canThrowForeignException())
3018+
if (!fn.shouldUseInvoke())
30163019
call = IRBuilderBase::CreateCall(fnTy, fn.getRawPointer(), args, bundles);
30173020
else
30183021
call =
@@ -3043,7 +3046,7 @@ llvm::CallBase *IRBuilder::CreateCallOrInvoke(
30433046

30443047
llvm::CallInst *IRBuilder::CreateCall(const FunctionPointer &fn,
30453048
ArrayRef<llvm::Value *> args) {
3046-
assert(!fn.canThrowForeignException());
3049+
assert(!fn.shouldUseInvoke());
30473050
return cast<llvm::CallInst>(CreateCallOrInvoke(
30483051
fn, args, /*invokeNormalDest=*/nullptr, /*invokeUnwindDest=*/nullptr));
30493052
}
@@ -4551,14 +4554,15 @@ void IRGenFunction::emitEpilogue() {
45514554
AllocaIP->eraseFromParent();
45524555
// Add exception unwind blocks and additional exception handling info
45534556
// if needed.
4554-
if (!ExceptionUnwindBlocks.empty()) {
4557+
if (!ExceptionUnwindBlocks.empty() ||
4558+
callsAnyAlwaysInlineThunksWithForeignExceptionTraps) {
45554559
// The function should have an unwind table when catching exceptions.
45564560
CurFn->addFnAttr(llvm::Attribute::getWithUWTableKind(
45574561
*IGM.LLVMContext, llvm::UWTableKind::Default));
45584562
CurFn->setPersonalityFn(IGM.getForeignExceptionHandlingPersonalityFunc());
4559-
for (auto *bb : ExceptionUnwindBlocks)
4560-
CurFn->getBasicBlockList().push_back(bb);
45614563
}
4564+
for (auto *bb : ExceptionUnwindBlocks)
4565+
CurFn->getBasicBlockList().push_back(bb);
45624566
}
45634567

45644568
std::pair<Address, Size>

lib/IRGen/GenDecl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3257,13 +3257,14 @@ llvm::Constant *swift::irgen::emitCXXConstructorThunkIfNeeded(
32573257
if (fpt->isNothrow())
32583258
canThrow = false;
32593259
}
3260-
if (canThrow)
3260+
if (canThrow) {
3261+
IGM.emittedForeignFunctionThunksWithExceptionTraps.insert(thunk);
32613262
subIGF.createExceptionTrapScope([&](llvm::BasicBlock *invokeNormalDest,
32623263
llvm::BasicBlock *invokeUnwindDest) {
32633264
subIGF.Builder.createInvoke(ctorFnType, ctorAddress, Args,
32643265
invokeNormalDest, invokeUnwindDest);
32653266
});
3266-
else
3267+
} else
32673268
subIGF.Builder.CreateCall(ctorFnType, ctorAddress, Args);
32683269

32693270
subIGF.Builder.CreateRetVoid();

lib/IRGen/IRGenFunction.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ class IRGenFunction {
105105

106106
llvm::BasicBlock *createExceptionUnwindBlock();
107107

108+
void setCallsThunksWithForeignExceptionTraps() {
109+
callsAnyAlwaysInlineThunksWithForeignExceptionTraps = true;
110+
}
111+
108112
void createExceptionTrapScope(
109113
llvm::function_ref<void(llvm::BasicBlock *, llvm::BasicBlock *)>
110114
invokeEmitter);
@@ -206,6 +210,10 @@ class IRGenFunction {
206210
/// in this function.
207211
llvm::SmallVector<llvm::BasicBlock *, 4> ExceptionUnwindBlocks;
208212

213+
/// True if this function calls any always inline thunks that have a foreign
214+
/// exception trap.
215+
bool callsAnyAlwaysInlineThunksWithForeignExceptionTraps = false;
216+
209217
public:
210218
void emitCoroutineOrAsyncExit();
211219

lib/IRGen/IRGenModule.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -959,9 +959,13 @@ class IRGenModule {
959959
/// module.
960960
llvm::Function *foreignExceptionHandlingPersonalityFunc = nullptr;
961961

962+
public:
963+
/// The set of emitted foreign function thunks that trap on exception in the
964+
/// underlying call that the thunk dispatches.
965+
llvm::SmallPtrSet<llvm::Function *, 4>
966+
emittedForeignFunctionThunksWithExceptionTraps;
962967

963968
//--- Types -----------------------------------------------------------------
964-
public:
965969
const ProtocolInfo &getProtocolInfo(ProtocolDecl *D, ProtocolInfoKind kind);
966970

967971
// Not strictly a type operation, but similar.

lib/IRGen/IRGenSIL.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2739,6 +2739,8 @@ void IRGenSILFunction::visitFunctionRefBaseInst(FunctionRefBaseInst *i) {
27392739
fp.setForeignNoThrow();
27402740
}
27412741
}
2742+
if (IGM.emittedForeignFunctionThunksWithExceptionTraps.count(fnPtr))
2743+
fp.setForeignCallCatchesExceptionInThunk();
27422744
}
27432745
// Store the function as a FunctionPointer so we can avoid bitcasting
27442746
// or thunking if we don't need to.

test/Interop/Cxx/exceptions/exceptions-disabled-exception-irgen.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ public:
3131
inline ClassWithThrowingCopyConstructor(const ClassWithThrowingCopyConstructor &) { (void)freeFunctionThrows(0); }
3232
};
3333

34+
class ClassWithThrowingConstructor {
35+
public:
36+
int m = 0;
37+
inline ClassWithThrowingConstructor() { }
38+
};
39+
3440
//--- test.swift
3541

3642
import CxxModule
@@ -56,9 +62,15 @@ func testClassWithThrowingCopyConstructor() -> CInt {
5662
return p2.m
5763
}
5864

65+
func testClassWithThrowingConstructor() -> CInt {
66+
let obj = ClassWithThrowingConstructor()
67+
return obj.m
68+
}
69+
5970
let _ = testFreeFunctionNoThrowOnly()
6071
let _ = testFreeFunctionCalls()
6172
let _ = testClassWithThrowingCopyConstructor()
73+
let _ = testClassWithThrowingConstructor()
6274

6375
// CHECK: define {{.*}} @"$s4test0A23FreeFunctionNoThrowOnlys5Int32VyF"() #[[#SWIFTMETA:]] {
6476
// CHECK-NEXT: :
@@ -78,3 +90,7 @@ let _ = testClassWithThrowingCopyConstructor()
7890
// CHECK: define {{.*}} @"$s4test0A32ClassWithThrowingCopyConstructors5Int32VyF"() #[[#SWIFTMETA]] {
7991
// CHECK-NOT: invoke
8092
// CHECK: }
93+
94+
// CHECK: define {{.*}} @"$s4test0A28ClassWithThrowingConstructors5Int32VyF"() #[[#SWIFTMETA]] {
95+
// CHECK-NOT: invoke
96+
// CHECK: }

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,14 @@ TrapOnExecutionTestSuite.test("TestClassWithThrowingCopyConstructor") {
103103
}
104104
#endif
105105

106+
TrapOnExecutionTestSuite.test("ClassWithThrowingConstructor") {
107+
expectCrashLater()
108+
let _ = ClassWithThrowingConstructor()
109+
}
110+
111+
TrapOnExecutionTestSuite.test("ClassWithNoThrowingConstructor") {
112+
let obj = ClassWithNoThrowingConstructor()
113+
expectEqual(obj.m, 0)
114+
}
115+
106116
runAllTests()

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ public:
119119
inline ClassWithThrowingCopyConstructor(const ClassWithThrowingCopyConstructor &) { throw 2; }
120120
};
121121

122+
class ClassWithThrowingConstructor {
123+
public:
124+
int m = 0;
125+
inline ClassWithThrowingConstructor() { throw 2; }
126+
};
127+
128+
class ClassWithNoThrowingConstructor {
129+
public:
130+
int m = 0;
131+
inline ClassWithNoThrowingConstructor() noexcept {}
132+
};
133+
122134
//--- test.swift
123135

124136
import CxxModule
@@ -205,6 +217,16 @@ func testClassWithThrowingCopyConstructor() -> CInt {
205217
return p2.m
206218
}
207219

220+
func testClassWithThrowingConstructor() -> CInt {
221+
let obj = ClassWithThrowingConstructor()
222+
return obj.m
223+
}
224+
225+
func testClassWithNoThrowingConstructor() -> CInt {
226+
let obj = ClassWithNoThrowingConstructor()
227+
return obj.m
228+
}
229+
208230
let _ = testFreeFunctionNoThrowOnly()
209231
let _ = testFreeFunctionCalls()
210232
let _ = testMethodCalls()
@@ -217,6 +239,8 @@ testClassWithDestructor()
217239
testClassWithThrowingDestructor()
218240
let _ = testClassWithCopyConstructor()
219241
let _ = testClassWithThrowingCopyConstructor()
242+
let _ = testClassWithThrowingConstructor()
243+
let _ = testClassWithNoThrowingConstructor()
220244

221245
// CHECK: define {{.*}} @"$s4test0A23FreeFunctionNoThrowOnlys5Int32VyF"() #[[#SWIFTMETA:]] {
222246
// CHECK-NEXT: :
@@ -359,6 +383,24 @@ let _ = testClassWithThrowingCopyConstructor()
359383
// CHECK: ret i32
360384
// CHECK-NEXT: }
361385

386+
// CHECK: define {{.*}} @"$s4test0A28ClassWithThrowingConstructors5Int32VyF"() #[[#SWIFTUWMETA]] personality
387+
// CHECK: invoke {{.*}} @_ZN28ClassWithThrowingConstructorC{{.*}}(
388+
// CHECK-NEXT: to label %[[CONT42:.*]] unwind label %[[UNWIND42:.*]]
389+
// CHECK-EMPTY:
390+
// CHECK-NEXT: [[UNWIND42]]:
391+
// CHECK-NEXT: landingpad { i8*, i32 }
392+
// CHECK-NEXT: catch i8* null
393+
// CHECK-NEXT: call void @llvm.trap()
394+
// CHECK-NEXT: unreachable
395+
// CHECK-EMPTY:
396+
// CHECK-NEXT: [[CONT42]]:
397+
// CHECK: ret i32
398+
// CHECK-NEXT: }
399+
400+
// CHECK: define {{.*}} @"$s4test0A30ClassWithNoThrowingConstructors5Int32VyF"() #[[#SWIFTMETA]]
401+
// CHECK-NOT: invoke
402+
// CHECK: }
403+
362404
// CHECK: i32 @__gxx_personality_v0(...)
363405

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

0 commit comments

Comments
 (0)