From ba86dea28136147190907d436c1d2cab3efcd640 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Sun, 8 Dec 2024 13:22:52 +0100 Subject: [PATCH 1/6] Support typed throws --- Sources/ConcurrencyExtras/Result.swift | 4 +- .../ConcurrencyExtrasTests/ResultTests.swift | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 Tests/ConcurrencyExtrasTests/ResultTests.swift diff --git a/Sources/ConcurrencyExtras/Result.swift b/Sources/ConcurrencyExtras/Result.swift index 046066b..420fbef 100644 --- a/Sources/ConcurrencyExtras/Result.swift +++ b/Sources/ConcurrencyExtras/Result.swift @@ -1,10 +1,10 @@ -extension Result where Failure == Swift.Error { +extension Result where Failure: Swift.Error { /// Creates a new result by evaluating an async throwing closure, capturing the returned value as /// a success, or any thrown error as a failure. /// /// - Parameter body: A throwing closure to evaluate. @_transparent - public init(catching body: () async throws -> Success) async { + public init(catching body: () async throws(Failure) -> Success) async { do { self = .success(try await body()) } catch { diff --git a/Tests/ConcurrencyExtrasTests/ResultTests.swift b/Tests/ConcurrencyExtrasTests/ResultTests.swift new file mode 100644 index 0000000..7387b17 --- /dev/null +++ b/Tests/ConcurrencyExtrasTests/ResultTests.swift @@ -0,0 +1,49 @@ +import ConcurrencyExtras +import XCTest + +final class ResultTests: XCTestCase { + struct SomeError: Error, Equatable { } + func f(_ value: Int) async throws -> Int { + if value == 42 { + return 42 + } else { + throw SomeError() + } + } + + func testBasics() async throws { + do { + let res = await Result { try await f(42) } + XCTAssertEqual(try res.get(), 42) + } + do { + let res = await Result { try await f(0) } + do { + _ = try res.get() + } catch let error as SomeError { + XCTAssertEqual(error, SomeError()) + } catch { + XCTFail("unexpected error: \(error)") + } + } + } + + func g(_ value: Int) async throws(SomeError) -> Int { + if value == 42 { + return 42 + } else { + throw SomeError() + } + } + + func testTypedThrows() async throws { + do { + let res = await Result { () async throws(SomeError) -> Int in try await g(0) } + do { + _ = try res.get() + } catch { + XCTAssertEqual(error, SomeError()) + } + } + } +} From dbb8db5a15d5ed3218394e449049343642d094c5 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Mon, 9 Dec 2024 20:41:01 +0100 Subject: [PATCH 2/6] Update Sources/ConcurrencyExtras/Result.swift Co-authored-by: Stephen Celis --- Sources/ConcurrencyExtras/Result.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ConcurrencyExtras/Result.swift b/Sources/ConcurrencyExtras/Result.swift index 420fbef..0e40300 100644 --- a/Sources/ConcurrencyExtras/Result.swift +++ b/Sources/ConcurrencyExtras/Result.swift @@ -1,4 +1,4 @@ -extension Result where Failure: Swift.Error { +extension Result { /// Creates a new result by evaluating an async throwing closure, capturing the returned value as /// a success, or any thrown error as a failure. /// From 4708c9ff49cd7ab1ae7c110d15e32c254f427f89 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Mon, 9 Dec 2024 20:44:49 +0100 Subject: [PATCH 3/6] Constrain change to compiler(>=6) --- Sources/ConcurrencyExtras/Result.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Sources/ConcurrencyExtras/Result.swift b/Sources/ConcurrencyExtras/Result.swift index 0e40300..2e6fd11 100644 --- a/Sources/ConcurrencyExtras/Result.swift +++ b/Sources/ConcurrencyExtras/Result.swift @@ -1,3 +1,4 @@ +#if compiler(>=6) extension Result { /// Creates a new result by evaluating an async throwing closure, capturing the returned value as /// a success, or any thrown error as a failure. @@ -12,3 +13,19 @@ extension Result { } } } +#else +extension Result where Failure == Swift.Error { + /// Creates a new result by evaluating an async throwing closure, capturing the returned value as + /// a success, or any thrown error as a failure. + /// + /// - Parameter body: A throwing closure to evaluate. + @_transparent + public init(catching body: () async throws -> Success) async { + do { + self = .success(try await body()) + } catch { + self = .failure(error) + } + } +} +#endif From fc32317c121744206d783087d3a7880b2a9aa670 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Mon, 9 Dec 2024 20:46:19 +0100 Subject: [PATCH 4/6] Also gate the test to Swift 6 --- Tests/ConcurrencyExtrasTests/ResultTests.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/ConcurrencyExtrasTests/ResultTests.swift b/Tests/ConcurrencyExtrasTests/ResultTests.swift index 7387b17..77642ab 100644 --- a/Tests/ConcurrencyExtrasTests/ResultTests.swift +++ b/Tests/ConcurrencyExtrasTests/ResultTests.swift @@ -36,6 +36,7 @@ final class ResultTests: XCTestCase { } } +#if compiler(>=6) func testTypedThrows() async throws { do { let res = await Result { () async throws(SomeError) -> Int in try await g(0) } @@ -46,4 +47,5 @@ final class ResultTests: XCTestCase { } } } +#endif } From a30dcd5efff5a8c083f1620e273cec3347e96017 Mon Sep 17 00:00:00 2001 From: "Sven A. Schmidt" Date: Mon, 9 Dec 2024 20:46:52 +0100 Subject: [PATCH 5/6] Include func g in ifdef --- Tests/ConcurrencyExtrasTests/ResultTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/ConcurrencyExtrasTests/ResultTests.swift b/Tests/ConcurrencyExtrasTests/ResultTests.swift index 77642ab..c6c9437 100644 --- a/Tests/ConcurrencyExtrasTests/ResultTests.swift +++ b/Tests/ConcurrencyExtrasTests/ResultTests.swift @@ -28,6 +28,7 @@ final class ResultTests: XCTestCase { } } +#if compiler(>=6) func g(_ value: Int) async throws(SomeError) -> Int { if value == 42 { return 42 @@ -36,7 +37,6 @@ final class ResultTests: XCTestCase { } } -#if compiler(>=6) func testTypedThrows() async throws { do { let res = await Result { () async throws(SomeError) -> Int in try await g(0) } From 5904a97a90282c09f17aee78b3a47f5ab93cb07a Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Mon, 16 Dec 2024 10:41:37 -0800 Subject: [PATCH 6/6] wip --- Sources/ConcurrencyExtras/Result.swift | 44 +++++++++---------- .../ConcurrencyExtrasTests/ResultTests.swift | 35 ++++++++++----- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/Sources/ConcurrencyExtras/Result.swift b/Sources/ConcurrencyExtras/Result.swift index 2e6fd11..baae40f 100644 --- a/Sources/ConcurrencyExtras/Result.swift +++ b/Sources/ConcurrencyExtras/Result.swift @@ -1,31 +1,27 @@ #if compiler(>=6) -extension Result { - /// Creates a new result by evaluating an async throwing closure, capturing the returned value as - /// a success, or any thrown error as a failure. - /// - /// - Parameter body: A throwing closure to evaluate. - @_transparent - public init(catching body: () async throws(Failure) -> Success) async { - do { - self = .success(try await body()) - } catch { - self = .failure(error) + extension Result { + /// Creates a new result by evaluating an async throwing closure, capturing the returned value as + /// a success, or any thrown error as a failure. + /// + /// - Parameter body: A throwing closure to evaluate. + @_transparent + public init(catching body: () async throws(Failure) -> Success) async { + do { + self = .success(try await body()) + } catch { + self = .failure(error) + } } } -} #else -extension Result where Failure == Swift.Error { - /// Creates a new result by evaluating an async throwing closure, capturing the returned value as - /// a success, or any thrown error as a failure. - /// - /// - Parameter body: A throwing closure to evaluate. - @_transparent - public init(catching body: () async throws -> Success) async { - do { - self = .success(try await body()) - } catch { - self = .failure(error) + extension Result where Failure == Swift.Error { + @_transparent + public init(catching body: () async throws -> Success) async { + do { + self = .success(try await body()) + } catch { + self = .failure(error) + } } } -} #endif diff --git a/Tests/ConcurrencyExtrasTests/ResultTests.swift b/Tests/ConcurrencyExtrasTests/ResultTests.swift index c6c9437..6bd5984 100644 --- a/Tests/ConcurrencyExtrasTests/ResultTests.swift +++ b/Tests/ConcurrencyExtrasTests/ResultTests.swift @@ -11,7 +11,7 @@ final class ResultTests: XCTestCase { } } - func testBasics() async throws { + func testBasics() async { do { let res = await Result { try await f(42) } XCTAssertEqual(try res.get(), 42) @@ -28,17 +28,16 @@ final class ResultTests: XCTestCase { } } -#if compiler(>=6) - func g(_ value: Int) async throws(SomeError) -> Int { - if value == 42 { - return 42 - } else { - throw SomeError() + #if compiler(>=6) + func g(_ value: Int) async throws(SomeError) -> Int { + if value == 42 { + return 42 + } else { + throw SomeError() + } } - } - func testTypedThrows() async throws { - do { + func testTypedThrows() async { let res = await Result { () async throws(SomeError) -> Int in try await g(0) } do { _ = try res.get() @@ -46,6 +45,18 @@ final class ResultTests: XCTestCase { XCTAssertEqual(error, SomeError()) } } - } -#endif + + func h() async throws(SomeError) -> Int { + throw SomeError() + } + + func testTypedThrowsInference() async { + let res = await Result(catching: h) + do { + _ = try res.get() + } catch { + XCTAssertEqual(error, SomeError()) + } + } + #endif }