Skip to content

Commit 7d74b3b

Browse files
committed
[SE-0413] Adopt typed throws in Result
Make `init(catching:)` and `get()` use typed throws. The former infers the `Failure` type from the closure provided (once full type inference is in place) and the latter only throws errors of the `Failure` type.
1 parent d19f082 commit 7d74b3b

File tree

4 files changed

+68
-9
lines changed

4 files changed

+68
-9
lines changed

stdlib/public/core/Result.swift

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,9 @@ public enum Result<Success, Failure: Error> {
161161
///
162162
/// - Returns: The success value, if the instance represents a success.
163163
/// - Throws: The failure value, if the instance represents a failure.
164+
@_alwaysEmitIntoClient
164165
@inlinable
165-
public func get() throws -> Success {
166+
public func get() throws(Failure) -> Success {
166167
switch self {
167168
case let .success(success):
168169
return success
@@ -172,13 +173,42 @@ public enum Result<Success, Failure: Error> {
172173
}
173174
}
174175

175-
extension Result where Failure == Swift.Error {
176+
extension Result {
176177
/// Creates a new result by evaluating a throwing closure, capturing the
177178
/// returned value as a success, or any thrown error as a failure.
178179
///
179-
/// - Parameter body: A throwing closure to evaluate.
180-
@_transparent
181-
public init(catching body: () throws -> Success) {
180+
/// - Parameter body: A potentially throwing closure to evaluate.
181+
@_alwaysEmitIntoClient
182+
@inlinable
183+
public init(catching body: () throws(Failure) -> Success) {
184+
do {
185+
self = .success(try body())
186+
} catch {
187+
self = .failure(error)
188+
}
189+
}
190+
}
191+
192+
extension Result {
193+
/// ABI: Historical get() throws
194+
@_silgen_name("$ss6ResultO3getxyKF")
195+
@usableFromInline
196+
func __abi_get() throws -> Success {
197+
switch self {
198+
case let .success(success):
199+
return success
200+
case let .failure(failure):
201+
throw failure
202+
}
203+
}
204+
205+
}
206+
207+
extension Result where Failure == Swift.Error {
208+
/// ABI: Historical init(catching:)
209+
@_silgen_name("$ss6ResultOss5Error_pRs_rlE8catchingAByxsAC_pGxyKXE_tcfCa")
210+
@usableFromInline
211+
init(__abi_catching body: () throws(Failure) -> Success) {
182212
do {
183213
self = .success(try body())
184214
} catch {

test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Func Unicode.UTF32.Parser.parseScalar(from:) has generic signature change from <
2626
Func Unicode.UTF32.decode(_:) has generic signature change from <I where I : Swift.IteratorProtocol, I.Element == Swift.Unicode.UTF32.CodeUnit> to <I where I : Swift.IteratorProtocol, I.Element == Swift.UInt32>
2727
Func Unicode.UTF8.decode(_:) has generic signature change from <I where I : Swift.IteratorProtocol, I.Element == Swift.Unicode.UTF8.CodeUnit> to <I where I : Swift.IteratorProtocol, I.Element == Swift.UInt8>
2828
Constructor Mirror.init(_:children:displayStyle:ancestorRepresentation:) has generic signature change from <Subject, C where C : Swift.Collection, C.Element == Swift.Mirror.Child> to <Subject, C where C : Swift.Collection, C.Element == (label: Swift.String?, value: Any)>
29+
30+
// Generalizations due to typed throws.
2931
Func AnyBidirectionalCollection.map(_:) has generic signature change from <Element, T> to <Element, T, E where E : Swift.Error>
3032
Func AnyBidirectionalCollection.map(_:) is now without @rethrows
3133
Func AnyCollection.map(_:) has generic signature change from <Element, T> to <Element, T, E where E : Swift.Error>
@@ -38,5 +40,6 @@ Func Collection.map(_:) has generic signature change from <Self, T where Self :
3840
Func Collection.map(_:) is now without @rethrows
3941
Func Sequence.map(_:) has generic signature change from <Self, T where Self : Swift.Sequence> to <Self, T, E where Self : Swift.Sequence, E : Swift.Error>
4042
Func Sequence.map(_:) is now without @rethrows
43+
Constructor Result.init(catching:) has generic signature change from <Success, Failure where Failure == any Swift.Error> to <Success, Failure where Failure : Swift.Error>
4144

4245
Protocol SIMDScalar has generic signature change from <Self == Self.SIMD16Storage.Scalar, Self.SIMD16Storage : Swift.SIMDStorage, Self.SIMD2Storage : Swift.SIMDStorage, Self.SIMD32Storage : Swift.SIMDStorage, Self.SIMD4Storage : Swift.SIMDStorage, Self.SIMD64Storage : Swift.SIMDStorage, Self.SIMD8Storage : Swift.SIMDStorage, Self.SIMDMaskScalar : Swift.FixedWidthInteger, Self.SIMDMaskScalar : Swift.SIMDScalar, Self.SIMDMaskScalar : Swift.SignedInteger, Self.SIMD16Storage.Scalar == Self.SIMD2Storage.Scalar, Self.SIMD2Storage.Scalar == Self.SIMD32Storage.Scalar, Self.SIMD32Storage.Scalar == Self.SIMD4Storage.Scalar, Self.SIMD4Storage.Scalar == Self.SIMD64Storage.Scalar, Self.SIMD64Storage.Scalar == Self.SIMD8Storage.Scalar> to <Self == Self.SIMD16Storage.Scalar, Self.SIMD16Storage : Swift.SIMDStorage, Self.SIMD2Storage : Swift.SIMDStorage, Self.SIMD32Storage : Swift.SIMDStorage, Self.SIMD4Storage : Swift.SIMDStorage, Self.SIMD64Storage : Swift.SIMDStorage, Self.SIMD8Storage : Swift.SIMDStorage, Self.SIMDMaskScalar : Swift.FixedWidthInteger, Self.SIMDMaskScalar : Swift.SIMDScalar, Self.SIMDMaskScalar : Swift.SignedInteger, Self.SIMDMaskScalar == Self.SIMDMaskScalar.SIMDMaskScalar, Self.SIMD16Storage.Scalar == Self.SIMD2Storage.Scalar, Self.SIMD2Storage.Scalar == Self.SIMD32Storage.Scalar, Self.SIMD32Storage.Scalar == Self.SIMD4Storage.Scalar, Self.SIMD4Storage.Scalar == Self.SIMD64Storage.Scalar, Self.SIMD64Storage.Scalar == Self.SIMD8Storage.Scalar>

test/api-digester/stability-stdlib-abi-without-asserts.test

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ Constructor _SmallString.init(taggedCocoa:) has return type change from Swift._S
7575
Enum Never has added a conformance to an existing protocol Decodable
7676
Enum Never has added a conformance to an existing protocol Encodable
7777
Enum Never has added a conformance to an existing protocol Identifiable
78+
79+
// These functions haven't actually changed ABI, but are using @_silgen_name tricks to maintain the old ABI while moving to typed throws.
7880
Func AnyBidirectionalCollection.map(_:) has been renamed to Func __rethrows_map(_:)
7981
Func AnyBidirectionalCollection.map(_:) has mangled name changing from 'Swift.AnyBidirectionalCollection.map<A>((A) throws -> A1) throws -> Swift.Array<A1>' to 'Swift.AnyBidirectionalCollection.__rethrows_map<A>((A) throws -> A1) throws -> Swift.Array<A1>'
8082
Func AnyBidirectionalCollection.map(_:) is now without @rethrows
@@ -87,14 +89,16 @@ Func AnyRandomAccessCollection.map(_:) is now without @rethrows
8789
Func AnySequence.map(_:) has been renamed to Func __rethrows_map(_:)
8890
Func AnySequence.map(_:) has mangled name changing from 'Swift.AnySequence.map<A>((A) throws -> A1) throws -> Swift.Array<A1>' to 'Swift.AnySequence.__rethrows_map<A>((A) throws -> A1) throws -> Swift.Array<A1>'
8991
Func AnySequence.map(_:) is now without @rethrows
90-
91-
// These are using
9292
Func Sequence.map(_:) has been renamed to Func __rethrows_map(_:)
9393
Func Sequence.map(_:) has mangled name changing from '(extension in Swift):Swift.Sequence.map<A>((A.Element) throws -> A1) throws -> Swift.Array<A1>' to '(extension in Swift):Swift.Sequence.__rethrows_map<A>((A.Element) throws -> A1) throws -> Swift.Array<A1>'
9494
Func Sequence.map(_:) is now without @rethrows
9595
Func Collection.map(_:) has been renamed to Func __rethrows_map(_:)
9696
Func Collection.map(_:) has mangled name changing from '(extension in Swift):Swift.Collection.map<A>((A.Element) throws -> A1) throws -> Swift.Array<A1>' to '(extension in Swift):Swift.Collection.__rethrows_map<A>((A.Element) throws -> A1) throws -> Swift.Array<A1>'
9797
Func Collection.map(_:) is now without @rethrows
98+
Constructor Result.init(__abi_catching:) is a new API without @available attribute
99+
Constructor Result.init(catching:) has been removed
100+
Func Result.get() has been renamed to Func __abi_get()
101+
Func Result.get() has mangled name changing from 'Swift.Result.get() throws -> A' to 'Swift.Result.__abi_get() throws -> A'
98102

99103
// These haven't actually been removed; they are simply marked unavailable.
100104
// This seems to be a false positive in the ABI checker. This is not an ABI

test/stdlib/Result.swift

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,16 @@ ResultTests.test("Throwing Initialization and Unwrapping") {
6767
func throwing() throws -> String {
6868
throw Err.err
6969
}
70-
70+
71+
func throwingTyped() throws(Err) -> String {
72+
throw .err
73+
}
74+
75+
func knownNotThrowing() -> String { return string }
76+
7177
let result1 = Result { try throwing() }
7278
let result2 = Result { try notThrowing() }
73-
79+
7480
expectEqual(result1.failure as? Err, Err.err)
7581
expectEqual(result2.success, string)
7682

@@ -98,6 +104,22 @@ ResultTests.test("Throwing Initialization and Unwrapping") {
98104
} catch {
99105
expectUnreachable()
100106
}
107+
108+
// Test strongly typed error via closure.
109+
// FIXME: Type inference should eliminate the need for the throws(Err)
110+
// annotations below.
111+
let result4 = Result { () throws(Err) in try throwingTyped() }
112+
let _: Result<String, Err> = result4 // check the type
113+
expectEqual(result4.failure, .err)
114+
do throws(Err) {
115+
_ = try result4.get()
116+
} catch let error {
117+
expectEqual(error, .err)
118+
}
119+
120+
let result5 = Result { knownNotThrowing() }
121+
let _: Result<String, Never> = result5 // check the type
122+
_ = result5.get() // no need for 'try'
101123
}
102124

103125
ResultTests.test("Functional Transforms") {

0 commit comments

Comments
 (0)