Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Tests: Reproducer for Swift 6.3 typed-throws async closure miscompile#12

Draft
krodak wants to merge 1 commit into
kr/closures-throws-asyncPassiveLogic/JavaScriptKit:kr/closures-throws-asyncfrom
kr/typed-throws-async-closure-reproPassiveLogic/JavaScriptKit:kr/typed-throws-async-closure-reproCopy head branch name to clipboard
Draft

Tests: Reproducer for Swift 6.3 typed-throws async closure miscompile#12
krodak wants to merge 1 commit into
kr/closures-throws-asyncPassiveLogic/JavaScriptKit:kr/closures-throws-asyncfrom
kr/typed-throws-async-closure-reproPassiveLogic/JavaScriptKit:kr/typed-throws-async-closure-reproCopy head branch name to clipboard

Conversation

@krodak

@krodak krodak commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Overview

A standalone, runnable reproducer for the Swift compiler bug that blocks the Swift-to-JS async throws reject path in the closures work (the "Known limitation" in the feature PR). This branch is stacked on top of that feature branch so reviewers see only the reproducer test in the diff.

Root cause identified: this is swiftlang/swift#89320 ("Crash with typed/untyped throwing async functions on Wasm"), with a fix in progress in swiftlang/swift#89715 (IRGen: fix async typed throws miscompiles on Wasm). On wasm32, IRGen places the indirect typed-error pointer after swiftself at thick call sites, but a captureless closure or function reference goes through thin_to_thick_function whose thin signature has no swiftself; LLVM's wasm swiftcc padding appends the missing parameters, producing equal-arity but permuted signatures. The callee writes the thrown error through the caller's null swiftself, and the caller reads a never-written slot.

The bug triggers only when both conditions hold:

  1. the typed error is too large for the direct error convention (roughly >16 bytes on wasm32; JSException is ~36 bytes), and
  2. the closure value was produced by thin_to_thick_function (a captureless closure literal or a function reference).

Tests/BridgeJSRuntimeTests/TypedThrowsAsyncClosureBugTests.swift demonstrates both conditions with labelled [REPRO] output. The suite stays green: the buggy cases are encoded as current-behaviour assertions with a comment to flip once swiftlang/swift#89715 lands (XCTExpectFailure is not available in the wasm XCTest runtime).

What it shows

[REPRO] jsexc / typed-throws async closure   object!=nil: false   <- payload lost
[REPRO] large / typed-throws async closure   garbage values       <- payload lost (no JavaScriptKit types involved)
[REPRO] jsexc / async function               object!=nil: true
[REPRO] jsexc / capturing typed-throws closure object!=nil: true  <- capturing closures are unaffected
[REPRO] jsexc / untyped async closure        object!=nil: true
[REPRO] value / typed-throws async closure   payload: ALIVE       <- small errors ride the direct convention
[REPRO] ref   / typed-throws async closure   payload: ALIVE
case error size callee shape result
JSException from captureless async closure ~36 B thin_to_thick LOST
32-byte plain-Swift error from captureless async closure 32 B thin_to_thick LOST
JSException from async function ~36 B direct preserved
JSException from capturing async closure ~36 B partial apply preserved
JSException from untyped async closure ~36 B untyped convention preserved
small struct / class-reference errors 4-12 B thin_to_thick preserved

Two additional observations that raise severity beyond the trap documented in swiftlang/swift#89320:

  • the corruption is silent: the caller observes a zeroed or stale-garbage error value instead of trapping;
  • the garbage is dangerous to even read: a corrupted error carrying a String field trapped with UnsafeBufferPointer has a nil start and nonzero count during interpolation, and releasing corrupted references crashes in swift_release.

Relationship to existing Swift issues

Purpose

Internal review artifact to confirm the characterization before coordinating with the maintainers on the preferred path (merge with the documented limitation, box JSException storage to fit the direct error convention, or wait for swiftlang/swift#89715). Run with make unittest.

@krodak krodak force-pushed the kr/closures-throws-async branch from dad4c01 to 746c781 Compare June 10, 2026 08:47
@krodak krodak force-pushed the kr/typed-throws-async-closure-repro branch from 24e91ea to c98c67f Compare June 10, 2026 08:47
@krodak krodak force-pushed the kr/closures-throws-async branch from 746c781 to 09bb61c Compare June 10, 2026 09:45
@krodak krodak force-pushed the kr/typed-throws-async-closure-repro branch from c98c67f to 351fd63 Compare June 10, 2026 09:45
@krodak krodak force-pushed the kr/closures-throws-async branch from 09bb61c to 30a9c43 Compare June 10, 2026 10:34
@krodak krodak force-pushed the kr/typed-throws-async-closure-repro branch from 351fd63 to a52271a Compare June 10, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Morty Proxy This is a proxified and sanitized view of the page, visit original site.