Skip to content

Commit e4d6bee

Browse files
authored
Merge pull request #71400 from DougGregor/will-throw-typed
Implement `swift_willThrow` variant for typed throws.
2 parents 7b012d8 + 89f5b70 commit e4d6bee

File tree

11 files changed

+171
-24
lines changed

11 files changed

+171
-24
lines changed

include/swift/AST/KnownDecls.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ FUNC_DECL(BridgeAnyObjectToAny,
6464

6565
FUNC_DECL(ConvertToAnyHashable, "_convertToAnyHashable")
6666

67+
FUNC_DECL(WillThrowTyped, "_willThrowTyped")
68+
6769
FUNC_DECL(DiagnoseUnexpectedError, "_unexpectedError")
6870
FUNC_DECL(DiagnoseUnexpectedErrorTyped, "_unexpectedErrorTyped")
6971
FUNC_DECL(ErrorInMainTyped, "_errorInMainTyped")

include/swift/Runtime/Error.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ SWIFT_RUNTIME_STDLIB_API void
7171
swift_willThrow(SWIFT_CONTEXT void *unused,
7272
SWIFT_ERROR_RESULT SwiftError **object);
7373

74+
/// Called when throwing a typed error. Serves as a breakpoint hook
75+
/// for debuggers.
76+
SWIFT_CC(swift)
77+
SWIFT_RUNTIME_STDLIB_API void
78+
swift_willThrowTypedImpl(OpaqueValue *value,
79+
const Metadata *type,
80+
const WitnessTable *errorConformance);
81+
7482
/// Called when an error is thrown out of the top level of a script.
7583
SWIFT_CC(swift)
7684
SWIFT_RUNTIME_STDLIB_API SWIFT_NORETURN void

lib/SILGen/SILGenStmt.cpp

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,24 +1560,57 @@ void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV,
15601560

15611561
SILValue exn;
15621562
if (!exnMV.isInContext()) {
1563-
// Claim the exception value. If we need to handle throwing
1564-
// cleanups, the correct thing to do here is to recreate the
1565-
// exception's cleanup when emitting each cleanup we branch through.
1566-
// But for now we aren't bothering.
1567-
exn = exnMV.forward(*this);
1568-
15691563
// Whether the thrown exception is already an Error existential box.
15701564
SILType existentialBoxType = SILType::getExceptionType(getASTContext());
1571-
bool isExistentialBox = exn->getType() == existentialBoxType;
1572-
1573-
// FIXME: Right now, we suppress emission of the willThrow builtin if the
1574-
// error isn't already the error existential, because swift_willThrow expects
1575-
// the existential box.
1576-
if (emitWillThrow && isExistentialBox) {
1577-
// Generate a call to the 'swift_willThrow' runtime function to allow the
1578-
// debugger to catch the throw event.
1579-
B.createBuiltin(loc, SGM.getASTContext().getIdentifier("willThrow"),
1580-
SGM.Types.getEmptyTupleType(), {}, {exn});
1565+
bool isExistentialBox = exnMV.getType() == existentialBoxType;
1566+
1567+
// If we are supposed to emit a call to swift_willThrow(Typed), do so now.
1568+
if (emitWillThrow) {
1569+
ASTContext &ctx = SGM.getASTContext();
1570+
if (isExistentialBox) {
1571+
// Generate a call to the 'swift_willThrow' runtime function to allow the
1572+
// debugger to catch the throw event.
1573+
1574+
// Claim the exception value.
1575+
exn = exnMV.forward(*this);
1576+
1577+
B.createBuiltin(loc,
1578+
ctx.getIdentifier("willThrow"),
1579+
SGM.Types.getEmptyTupleType(), {}, {exn});
1580+
} else {
1581+
// Call the _willThrowTyped entrypoint, which handles
1582+
// arbitrary error types.
1583+
SILValue tmpBuffer;
1584+
SILValue error;
1585+
1586+
FuncDecl *entrypoint = ctx.getWillThrowTyped();
1587+
auto genericSig = entrypoint->getGenericSignature();
1588+
SubstitutionMap subMap = SubstitutionMap::get(
1589+
genericSig, [&](SubstitutableType *dependentType) {
1590+
return exnMV.getType().getASTType();
1591+
}, LookUpConformanceInModule(getModule().getSwiftModule()));
1592+
1593+
// Generic errors are passed indirectly.
1594+
if (!exnMV.getType().isAddress()) {
1595+
// Materialize the error so we can pass the address down to the
1596+
// swift_willThrowTyped.
1597+
exnMV = exnMV.materialize(*this, loc);
1598+
error = exnMV.getValue();
1599+
exn = exnMV.forward(*this);
1600+
} else {
1601+
// Claim the exception value.
1602+
exn = exnMV.forward(*this);
1603+
error = exn;
1604+
}
1605+
1606+
emitApplyOfLibraryIntrinsic(
1607+
loc, entrypoint, subMap,
1608+
{ ManagedValue::forForwardedRValue(*this, error) },
1609+
SGFContext());
1610+
}
1611+
} else {
1612+
// Claim the exception value.
1613+
exn = exnMV.forward(*this);
15811614
}
15821615
}
15831616

stdlib/public/core/EmbeddedRuntime.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ public func swift_deletedMethodError() -> Never {
302302
public func swift_willThrow() throws {
303303
}
304304

305+
/// Called when a typed error will be thrown.
306+
@_silgen_name("swift_willThrowTyped")
307+
public func _willThrowTyped<E: Error>(_ error: E) {
308+
}
309+
305310
@_extern(c, "arc4random_buf")
306311
func arc4random_buf(buf: UnsafeMutableRawPointer, nbytes: Int)
307312

stdlib/public/core/ErrorType.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,30 @@ internal func _getErrorDefaultUserInfo<T: Error>(_ error: T) -> AnyObject?
177177
public func _bridgeErrorToNSError(_ error: __owned Error) -> AnyObject
178178
#endif
179179

180+
/// Called to indicate that a typed error will be thrown.
181+
@_silgen_name("swift_willThrowTypedImpl")
182+
@available(SwiftStdlib 5.11, *)
183+
@usableFromInline
184+
func _willThrowTypedImpl<E: Error>(_ error: E)
185+
186+
#if !$Embedded
187+
/// Called when a typed error will be thrown.
188+
///
189+
/// On new-enough platforms, this will call through to the runtime to invoke
190+
/// the thrown error handler (if one is set).
191+
///
192+
/// On older platforms, the error will not be passed into the runtime, because
193+
/// doing so would require memory allocation (to create the 'any Error').
194+
@inlinable
195+
@_alwaysEmitIntoClient
196+
@_silgen_name("swift_willThrowTyped")
197+
public func _willThrowTyped<E: Error>(_ error: E) {
198+
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
199+
_willThrowTypedImpl(error)
200+
}
201+
}
202+
#endif
203+
180204
/// Invoked by the compiler when the subexpression of a `try!` expression
181205
/// throws an error.
182206
@_silgen_name("swift_unexpectedError")

stdlib/public/runtime/ErrorObjectCommon.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ void swift::_swift_setWillThrowHandler(void (* handler)(SwiftError *error)) {
3030
_swift_willThrow.store(handler, std::memory_order_release);
3131
}
3232

33-
/// Breakpoint hook for debuggers, and calls _swift_willThrow if set.
33+
/// Breakpoint hook for debuggers that is called for untyped throws, and
34+
/// calls _swift_willThrow if set.
3435
SWIFT_CC(swift) void
3536
swift::swift_willThrow(SWIFT_CONTEXT void *unused,
3637
SWIFT_ERROR_RESULT SwiftError **error) {
@@ -41,3 +42,27 @@ swift::swift_willThrow(SWIFT_CONTEXT void *unused,
4142
(* handler)(*error);
4243
}
4344
}
45+
46+
/// Breakpoint hook for debuggers that is called for typed throws, and calls
47+
/// _swift_willThrow if set. This implicitly boxes the typed error in an
48+
/// any Error for the call.
49+
SWIFT_CC(swift) void
50+
swift::swift_willThrowTypedImpl(OpaqueValue *value,
51+
const Metadata *type,
52+
const WitnessTable *errorConformance) {
53+
// Cheap check to bail out early, since we expect there to be no callbacks
54+
// the vast majority of the time.
55+
auto handler = _swift_willThrow.load(std::memory_order_acquire);
56+
if (SWIFT_UNLIKELY(handler)) {
57+
// Form an error box containing the error.
58+
BoxPair boxedError = swift_allocError(
59+
type, errorConformance, value, /*isTake=*/false);
60+
61+
// Hand the boxed error off to the handler.
62+
auto errorBox = reinterpret_cast<SwiftError *>(boxedError.object);
63+
(* handler)(errorBox);
64+
65+
// Release the error box.
66+
swift_errorRelease(errorBox);
67+
}
68+
}

test/SILGen/typed_throws.swift

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,45 @@ func doesNotThrowConcrete() throws(MyError) { }
1717
// CHECK-LABEL: sil hidden [ossa] @$s12typed_throws0B8ConcreteyyAA7MyErrorOYKF : $@convention(thin) () -> @error MyError
1818
func throwsConcrete() throws(MyError) {
1919
// CHECK: [[ERROR:%[0-9]+]] = enum $MyError, #MyError.fail!enumelt
20-
// CHECK-NOT: builtin "willThrow"
21-
// CHECK: throw [[ERROR]] : $MyError
20+
// CHECK: [[ERROR_ALLOC:%.*]] = alloc_stack $MyError
21+
// CHECK: store [[ERROR]] to [trivial] [[ERROR_ALLOC]] : $*MyError
22+
// CHECK: [[FN:%.*]] = function_ref @swift_willThrowTyped : $@convention(thin) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> ()
23+
// CHECK: apply [[FN]]<MyError>([[ERROR_ALLOC]]) : $@convention(thin) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> ()
24+
// CHECK: [[ERROR_RELOAD:%.*]] = load [trivial] [[ERROR_ALLOC]]
25+
// CHECK: dealloc_stack [[ERROR_ALLOC]] : $*MyError
26+
// CHECK: throw [[ERROR_RELOAD]] : $MyError
2227
throw .fail
2328
}
2429

30+
class ClassError: Error { }
31+
32+
// CHECK-LABEL: sil hidden [ossa] @$s12typed_throws0B10ClassErroryyAA0cD0CYKF : $@convention(thin) () -> @error ClassError
33+
// CHECK: [[META:%.*]] = metatype $@thick ClassError.Type
34+
// CHECK: [[INIT:%.*]] = function_ref @$s12typed_throws10ClassErrorCACycfC
35+
// CHECK: [[ERROR:%.*]] = apply [[INIT]]([[META]]) : $@convention(method) (@thick ClassError.Type) -> @owned ClassError
36+
// CHECK: [[ERROR_ALLOC:%.*]] = alloc_stack $ClassError
37+
// CHECK: store [[ERROR]] to [init] [[ERROR_ALLOC]] : $*ClassError
38+
// CHECK: [[FN:%.*]] = function_ref @swift_willThrowTyped : $@convention(thin) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> ()
39+
// CHECK: apply [[FN]]<ClassError>([[ERROR_ALLOC]]) : $@convention(thin) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> ()
40+
// CHECK: [[ERROR_RELOAD:%.*]] = load [take] [[ERROR_ALLOC]] : $*ClassError
41+
// CHECK: dealloc_stack [[ERROR_ALLOC]] : $*ClassError
42+
// CHECK: throw [[ERROR_RELOAD]] : $ClassError
43+
func throwsClassError() throws(ClassError) {
44+
throw ClassError()
45+
}
46+
47+
// CHECK-LABEL: sil hidden [ossa] @$s12typed_throws0B13IndirectErroryyxxYKs0D0RzlF : $@convention(thin) <E where E : Error> (@in_guaranteed E) -> @error_indirect E
48+
// CHECK: [[ERROR_ALLOC:%.*]] = alloc_stack $E
49+
// CHECK: copy_addr %1 to [init] [[ERROR_ALLOC]] : $*E
50+
// CHECK: [[FN:%.*]] = function_ref @swift_willThrowTyped : $@convention(thin) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> ()
51+
// CHECK: apply [[FN]]<E>([[ERROR_ALLOC]]) : $@convention(thin) <τ_0_0 where τ_0_0 : Error> (@in_guaranteed τ_0_0) -> ()
52+
// CHECK: copy_addr [take] [[ERROR_ALLOC]] to [init] %0 : $*E
53+
// CHECK: dealloc_stack [[ERROR_ALLOC]] : $*E
54+
// CHECK-NEXT: throw_addr
55+
func throwsIndirectError<E: Error>(_ error: E) throws(E) {
56+
throw error
57+
}
58+
2559
// CHECK-LABEL: sil hidden [ossa] @$s12typed_throws15rethrowConcreteyyAA7MyErrorOYKF
2660
func rethrowConcrete() throws(MyError) {
2761
// CHECK: try_apply [[FN:%[0-9]+]]() : $@convention(thin) () -> @error MyError, normal [[NORMALBB:bb[0-9]+]], error [[ERRORBB:bb[0-9]+]]

test/SILOptimizer/existential_box_elimination.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public struct TestError: Error {
2323

2424
@inline(never)
2525
@_optimize(none)
26-
internal func internalImplementation(somethingGood: Bool) -> Result<Int, TestError> {
26+
internal func internalImplementation(somethingGood: Bool) -> Result<Int, any Error> {
2727
return somethingGood ? .success(27) : .failure(TestError(errno:123))
2828
}
2929

@@ -32,12 +32,11 @@ public func publicWrapper(somethingGood: Bool) throws -> Int {
3232
}
3333

3434
// CHECK-LABEL: sil [noinline] @$s4test0A13WithForceCast13somethingGoodSiSb_tF
35-
// CHECK: [[F:%[0-9]+]] = function_ref @$s4test22internalImplementation13somethingGoods6ResultOySiAA9TestErrorVGSb_tF
35+
// CHECK: [[F:%[0-9]+]] = function_ref @$s4test22internalImplementation13somethingGoods6ResultOySis5Error_pGSb_tF
3636
// CHECK: apply [[F]]
3737
// CHECK: switch_enum
3838
// CHECK: bb1:
3939
// CHECK-NOT: alloc_existential_box
40-
// CHECK-NOT: apply
4140
// CHECK: } // end sil function '$s4test0A13WithForceCast13somethingGoodSiSb_tF'
4241
@inline(never)
4342
public func testWithForceCast(somethingGood: Bool) -> Int {
@@ -49,12 +48,11 @@ public func testWithForceCast(somethingGood: Bool) -> Int {
4948
}
5049

5150
// CHECK-LABEL: sil [noinline] @$s4test0A19WithMultipleCatches13somethingGoodSiSb_tF
52-
// CHECK: [[F:%[0-9]+]] = function_ref @$s4test22internalImplementation13somethingGoods6ResultOySiAA9TestErrorVGSb_tF
51+
// CHECK: [[F:%[0-9]+]] = function_ref @$s4test22internalImplementation13somethingGoods6ResultOySis5Error_pGSb_tF
5352
// CHECK: apply [[F]]
5453
// CHECK: switch_enum
5554
// CHECK: bb1:
5655
// CHECK-NOT: alloc_existential_box
57-
// CHECK-NOT: apply
5856
// CHECK: } // end sil function '$s4test0A19WithMultipleCatches13somethingGoodSiSb_tF'
5957
@inline(never)
6058
public func testWithMultipleCatches(somethingGood: Bool) -> Int {

test/abi/macOS/arm64/stdlib.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,4 @@ Added: __swift_pod_destroy
254254
Added: __swift_pod_direct_initializeBufferWithCopyOfBuffer
255255
Added: __swift_pod_indirect_initializeBufferWithCopyOfBuffer
256256
Added: __swift_validatePrespecializedMetadata
257+
Added: _swift_willThrowTypedImpl

test/abi/macOS/x86_64/stdlib.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,4 @@ Added: __swift_pod_destroy
254254
Added: __swift_pod_direct_initializeBufferWithCopyOfBuffer
255255
Added: __swift_pod_indirect_initializeBufferWithCopyOfBuffer
256256
Added: __swift_validatePrespecializedMetadata
257+
Added: _swift_willThrowTypedImpl

0 commit comments

Comments
 (0)