Skip to content

[stdlib] Make stdin/stdout/stderr Sendable on all platforms - #89891

Merged
ktoso merged 2 commits into
swiftlang:mainfrom
JacobHearst:sendable-stdio
Jul 13, 2026
Merged

ktoso merged 2 commits into
swiftlang:mainfrom
JacobHearst:sendable-stdio

Conversation

@JacobHearst

Copy link
Copy Markdown
Contributor

Resolves #75601.

Extends the existing OpenBSD shim functions in LibcOverlayShims.h to cover Linux and WASI, and re-exports them as nonisolated(unsafe) computed vars in the Glibc, Musl, Android, and WASILibc overlays.

@JacobHearst
JacobHearst requested a review from a team as a code owner June 11, 2026 22:51
@finagolfin

Copy link
Copy Markdown
Member

@swift-ci test

@finagolfin

Copy link
Copy Markdown
Member

@swift-ci test android

@JacobHearst

Copy link
Copy Markdown
Contributor Author

Thanks for triggering those @finagolfin I'll take a look at the failures

@finagolfin

Copy link
Copy Markdown
Member

@kateinoigakukun, @3405691582, and @al45tair, please take a look

@swift-ci test

@finagolfin

Copy link
Copy Markdown
Member

@swift-ci test android

Comment thread stdlib/public/Platform/Glibc.swift.gyb Outdated
@JacobHearst

Copy link
Copy Markdown
Contributor Author

I made the wrappers optional in my latest push because the first iteration of this change broke swift-log's usage of stderr and I (maybe incorrectly) thought that I should be able to achieve my goal without breaking existing usages. After the latest builds, I'm a bit confused.

swift-log force-unwraps stderr in this block (Logging.swift line 1254):

#elseif canImport(Glibc)
let systemStderr = Glibc.stderr!
let systemStdout = Glibc.stdout!
#elseif canImport(Musl)
let systemStderr = Musl.stderr!
let systemStdout = Musl.stdout!
#elseif canImport(WASILibc)
let systemStderr = WASILibc.stderr!
let systemStdout = WASILibc.stdout!

But the backtrace utils use stdout and stderr without force unwrapping (source).

I think what this means is that the original return type of the _swift_stdlib_stdxxx() functions was UnsafeMutableRawPointer! which would let callers use stderr or stderr!. Does this mean I should just make the wrappers return UnsafeMutablePointer<FILE>!?

@finagolfin

Copy link
Copy Markdown
Member

@ahoppen, @grynspan, @weissi, would be good to get your feedback here.

@weissi weissi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, this looks good to me.

In Swift 6 mode these were diagnosed as shared mutable global state
(swiftlang#75601). Mark them nonisolated(unsafe) across the libc overlays.

Glibc/Musl/Android/WASI define them as computed vars backed by the
_swift_stdlib_std* shims; Platform.swift covers OpenBSD, FreeBSD/PS4,
and Windows. They are OpaquePointer where FILE is opaque (Musl, WASI,
Android, OpenBSD) and UnsafeMutablePointer<FILE> elsewhere.

Adds the StdioSendable regression test.
@JacobHearst

Copy link
Copy Markdown
Contributor Author

@finagolfin Could you trigger the tests again on the latest changes?

@finagolfin

Copy link
Copy Markdown
Member

Hold on, Windows CI may be broken, will try in an hour or so if it isn't.

@finagolfin

Copy link
Copy Markdown
Member

@kateinoigakukun and @al45tair, would be good to get your sign-off here, plus from any of @swiftlang/android-workgroup who have input.

@finagolfin

Copy link
Copy Markdown
Member

Windows CI should be good now.

@swift-ci smoke test

@finagolfin

Copy link
Copy Markdown
Member

@swift-ci test android

@JacobHearst

Copy link
Copy Markdown
Contributor Author

Anything else needed from me to merge?

@finagolfin

Copy link
Copy Markdown
Member

Just waiting to see if anyone else has input, think you've done your part. @ktoso, wdyt?

@finagolfin

Copy link
Copy Markdown
Member

You'll need to rebase, some merge conflict.

@ktoso ktoso left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Long overdue, thanks for the PR! Looks good to me and enough other folks chimed in as well

@ktoso

ktoso commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

I took a stab at resolving the conflict, kicking off CI and we can merge if it passes.

@ktoso

ktoso commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

@swift-ci please test

@ktoso
ktoso enabled auto-merge July 13, 2026 05:27
@ktoso
ktoso merged commit dc600dc into swiftlang:main Jul 13, 2026
5 of 6 checks passed
ktoso added a commit to ktoso/swift that referenced this pull request Jul 15, 2026
swift-backtrace was using `setvbuf(stdout, nil, _IOLBF, 0)` which failed under `x86_64-swift-linux-musl` which broke after the recent changes in swiftlang#89891

We need to match the way those funcs are imported, so those variables must be `UnsafeMutablePointer<FILE>!`

Also adds a test actually using those globals so we won't regress randomly.

Resolves rdar://182172802
ktoso added a commit to ktoso/swift that referenced this pull request Jul 15, 2026
swift-backtrace was using `setvbuf(stdout, nil, _IOLBF, 0)` which failed under `x86_64-swift-linux-musl` which broke after the recent changes in swiftlang#89891

We need to match the way those funcs are imported, so those variables must be `UnsafeMutablePointer<FILE>!`

Also adds a test actually using those globals so we won't regress randomly.

Resolves rdar://182172802
ktoso added a commit that referenced this pull request Jul 15, 2026
swift-backtrace was using `setvbuf(stdout, nil, _IOLBF, 0)` which failed under `x86_64-swift-linux-musl` which broke after the recent changes in #89891

We need to match the way those funcs are imported, so those variables must be `UnsafeMutablePointer<FILE>!`

Also adds a test actually using those globals so we won't regress randomly.

Resolves rdar://182172802
ktoso added a commit that referenced this pull request Jul 15, 2026
swift-backtrace was using `setvbuf(stdout, nil, _IOLBF, 0)` which failed under
`x86_64-swift-linux-musl` after the recent changes in
#89891.

Musl's <stdio.h> forward-declares `struct _IO_FILE`, so Clang imports FILE
as a nominal Swift type and every stdio function in the same overlay takes
`UnsafeMutablePointer<FILE>`. Match that signature.

Android (bionic) and WASI don't expose FILE as a Swift-nameable type at all,
so their stdio functions come in typed with OpaquePointer. Keep those two
overlays as OpaquePointer (initial fix attempt got that wrong -- flagged by
finagolfin during review, ref swift-inspect String+Extensions.swift for the
recommended `typealias File = OpaquePointer` bridging pattern under
`#if os(Android)`).

Also extends test/stdlib/StdioSendable.swift to actually pass the globals to
stdio functions on the platforms where FILE is nominal (guards Android/WASI
out).

Resolves rdar://182172802
finagolfin pushed a commit that referenced this pull request Jul 21, 2026
swift-backtrace was using `setvbuf(stdout, nil, _IOLBF, 0)` which failed
under `x86_64-swift-linux-musl` after the recent changes in
#89891

We need to match the way those funcs are imported, so those variables
must be `UnsafeMutablePointer<FILE>!`

Also adds a test actually using those globals so we won't regress
randomly.

Resolves rdar://182172802
Teemperor pushed a commit to Teemperor/swift-mono that referenced this pull request Jul 21, 2026
…ssue/PR 90679)

swift-backtrace was using `setvbuf(stdout, nil, _IOLBF, 0)` which failed
under `x86_64-swift-linux-musl` after the recent changes in
swiftlang/swift#89891

We need to match the way those funcs are imported, so those variables
must be `UnsafeMutablePointer<FILE>!`

Also adds a test actually using those globals so we won't regress
randomly.

Resolves rdar://182172802
Paul-Passeron pushed a commit to Paul-Passeron/swift that referenced this pull request Jul 23, 2026
)

swift-backtrace was using `setvbuf(stdout, nil, _IOLBF, 0)` which failed
under `x86_64-swift-linux-musl` after the recent changes in
swiftlang#89891

We need to match the way those funcs are imported, so those variables
must be `UnsafeMutablePointer<FILE>!`

Also adds a test actually using those globals so we won't regress
randomly.

Resolves rdar://182172802
finagolfin added a commit to finagolfin/swift that referenced this pull request Jul 29, 2026
Recent pulls swiftlang#89891 and swiftlang#90679 tried to improve the async typing for Musl
but broke the static linux SDK CI, so temporarily reverting.
finagolfin added a commit that referenced this pull request Jul 30, 2026
Recent pulls #89891 and #90679 tried to improve the async typing for
Musl but broke the static linux SDK CI, so temporarily reverting.
kateinoigakukun added a commit to swiftwasm/JavaScriptKit that referenced this pull request Aug 18, 2026
```
$ echo "@preconcurrency import var Foundation.stderr" | swiftly run +main-snapshot-2026-08-11 swiftc  - -o /dev/null
<stdin>:1:28: error: ambiguous name 'stderr' in module 'Foundation'
1 | @preconcurrency import var Foundation.stderr
  |                            `- error: ambiguous name 'stderr' in module 'Foundation'
2 |

Glibc.stderr:1:32: note: found this candidate
1 | nonisolated(unsafe) public var stderr: UnsafeMutablePointer<FILE>! { get }
  |                                `- note: found this candidate

/usr/include/stdio.h:151:14: note: found this candidate
149 | extern FILE *stdin;               /* Standard input stream.  */
150 | extern FILE *stdout;              /* Standard output stream.  */
151 | extern FILE *stderr;              /* Standard error output stream.  */
    |              `- note: found this candidate
152 | /* C89/C99 say they're macros.  Make them happy.  */
153 | #define stdin stdin
```

swiftlang/swift#89891
kateinoigakukun added a commit to swiftwasm/JavaScriptKit that referenced this pull request Aug 18, 2026
```
$ echo "@preconcurrency import var Foundation.stderr" | swiftly run +main-snapshot-2026-08-11 swiftc  - -o /dev/null
<stdin>:1:28: error: ambiguous name 'stderr' in module 'Foundation'
1 | @preconcurrency import var Foundation.stderr
  |                            `- error: ambiguous name 'stderr' in module 'Foundation'
2 |

Glibc.stderr:1:32: note: found this candidate
1 | nonisolated(unsafe) public var stderr: UnsafeMutablePointer<FILE>! { get }
  |                                `- note: found this candidate

/usr/include/stdio.h:151:14: note: found this candidate
149 | extern FILE *stdin;               /* Standard input stream.  */
150 | extern FILE *stdout;              /* Standard output stream.  */
151 | extern FILE *stderr;              /* Standard error output stream.  */
    |              `- note: found this candidate
152 | /* C89/C99 say they're macros.  Make them happy.  */
153 | #define stdin stdin
```

swiftlang/swift#89891
kateinoigakukun added a commit to swiftwasm/JavaScriptKit that referenced this pull request Aug 22, 2026
…807)

* PackageToJS: Derive WASI and shared memory support from wasm imports

The SwiftBuild backend's build directory doesn't encode the target triple, so
detect the traits derived from it by parsing the imports of the product binary
instead. The imports are now parsed as a part of the packaging build graph.

* Avoid `import var Foundation.stderr`

```
$ echo "@preconcurrency import var Foundation.stderr" | swiftly run +main-snapshot-2026-08-11 swiftc  - -o /dev/null
<stdin>:1:28: error: ambiguous name 'stderr' in module 'Foundation'
1 | @preconcurrency import var Foundation.stderr
  |                            `- error: ambiguous name 'stderr' in module 'Foundation'
2 |

Glibc.stderr:1:32: note: found this candidate
1 | nonisolated(unsafe) public var stderr: UnsafeMutablePointer<FILE>! { get }
  |                                `- note: found this candidate

/usr/include/stdio.h:151:14: note: found this candidate
149 | extern FILE *stdin;               /* Standard input stream.  */
150 | extern FILE *stdout;              /* Standard output stream.  */
151 | extern FILE *stderr;              /* Standard error output stream.  */
    |              `- note: found this candidate
152 | /* C89/C99 say they're macros.  Make them happy.  */
153 | #define stdin stdin
```

swiftlang/swift#89891

* CI: Install Swift toolchain preserving its usr/bin layout

Flattening the tarball into /usr/local makes SwiftPM derive a bogus
toolchain root, so the swiftbuild build system cannot find swiftc.

* PackageToJS: Record every import kind while parsing wasm imports

Only memory imports were collected, so `WasmFeatures.isWASI` never saw
`wasi_snapshot_preview1` and the generated node.js dropped the WASI import
while still constructing a WASI instance.

* PackageToJS: run per-target test runners under the swiftbuild build system

`swift package js test` assumed the native build system's single combined
`<Package>PackageTests` binary: it looked for one `.wasm`/`.xctest` under
`.build/<config>/`, packaged it, and ran it.

SwiftBuild produces no combined test binary. It emits one
`<TestTarget>-test-runner.wasm` per test target under `.build/out/Products/`,
plus an aggregate target that only orchestrates building them, so the old
lookup failed with "Failed to find 'JavaScriptKitPackageTests.wasm'".

(cherry picked from commit c12c7e9)

* Install the JS event loop executor in async test targets

Because SwiftBuild mode produces a linked executable for each test
target.

(cherry picked from commit 0a1f926)

* BridgeJS: Skip type handle registration for unlinked modules

`js test` generates the glue from every test target's skeletons, but the
SwiftBuild build system links one binary per test target, so the eager
registration hit a missing export.

* PackageToJS: Share the linked JS modules with the aggregated glue

The per-runner `bridge-js.js` copied to the base output directory for test
preludes imports its JavaScript modules relative to itself, so copy the
`bridge-js-modules` directory next to it.

* BridgeJS: Require the imports object for static-only imported types

An imported type looked up from `getImports` contributes its static methods
too, but only a constructor marked the imports object as needed, so the glue
referenced an undeclared `imports`.

* PackageToJS: Generate each test bundle's glue from what its binary links in

`js test` generated the glue from every test target's skeletons and handed
the same copy to each SwiftBuild per-target runner, which described exports
those binaries don't have. Package the aggregated glue once into the shared
base directory, where preludes import it by a fixed path, and give each
runner glue scoped to the target it links in.

This supersedes the registration guard in the JS code generator.
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.

stderr is not marked as Sendable on Linux

7 participants