Files
swift-mirror/test/stdlib/Result+asyncInit.swift
Max Desiatov 4c990823cd IRGen: fix async typed throws miscompiles on Wasm (#89715)
Follow up to currently-reverted https://github.com/swiftlang/swift/pull/89416.

On wasm32 the WebAssembly backend pads the `swiftself` and `swifterror` parameters, which left the indirect typed-error pointer and the `swiftself`/context slot at different LLVM-IR positions for Thin versus Thick async callers; caller and callee then disagreed on which trailing pointer held the typed-error slot and miscompiled async typed-throws calls (#89155: `Result.init(catching:) async` trapped in a value-witness copy on Wasm).

With this change `SignatureExpansion::expandAsyncEntryType` now emits `addIndirectThrowingResult` before the `swiftself`/context slot, so the trailing pair is always `[ind_error, swiftself]` and Thin and Thick agree on the position regardless of parameter padding. The result-shape decision moves behind one gate, `hasTrailingAsyncErrorContextPair` (`GenCall.h`), consumed by the three sites that read these slots positionally: `AsyncCallEmission::setFromCallee`, `AsyncPartialApplicationForwarderEmission`, and `AsyncNativeCCEntryPointArgumentEmission::mapAsyncParameters` (the last also pre-extracts the witness-method Self/witness-table that trail the pair).

Each consumer carries an assertion that fires if the gate ever diverges from the slots actually filled.

Fixes #89320.
2026-07-14 15:17:54 +01:00

70 lines
1.3 KiB
Swift

// RUN: %target-run-simple-swift(-target %target-swift-5.1-abi-triple)
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: concurrency_runtime
import StdlibUnittest
import Swift
let ResultAsyncInitTests = TestSuite("ResultAsyncInit")
fileprivate enum Err: Error, Equatable {
case err
case derr
}
fileprivate let string = "string"
fileprivate extension Result {
var success: Success? {
switch self {
case let .success(success):
return success
case .failure:
return nil
}
}
var failure: Failure? {
switch self {
case .success:
return nil
case let .failure(failure):
return failure
}
}
}
ResultAsyncInitTests.test("Async Initialization") {
func asyncThrowing() async throws -> String {
throw Err.err
}
func asyncNotThrowing() async throws -> String {
return string
}
let result1 = await Result { try await asyncThrowing() }
let result2 = await Result { try await asyncNotThrowing() }
expectEqual(result1.failure as? Err, Err.err)
expectEqual(result2.success, string)
do {
_ = try result1.get()
} catch let error as Err {
expectEqual(error, Err.err)
} catch {
expectUnreachable()
}
do {
let unwrapped = try result2.get()
expectEqual(unwrapped, string)
} catch {
expectUnreachable()
}
}
await runAllTestsAsync()