Add a new wasm32-wasip3 target to Rust

This commit adds a new tier 3 target to rustc, `wasm32-wasip3`. This
follows in the footsteps of the previous `wasm32-wasip2` target and is
used to represent binding to the WASIp3 set of APIs managed by the WASI
subgroup to the WebAssembly Community Group.

As of now the WASIp3 set of APIs are not finalized nor standardized.
They're in the process of doing so and the current trajectory is to have
the APIs published in December of this year. The goal here is to get the
wheels turning in Rust to have the target in a
more-ready-than-nonexistent state by the time this happens in December.

For now the `wasm32-wasip3` target looks exactly the same as
`wasm32-wasip2` except that `target_env = "p3"` is specified. This
indicates to crates in the ecosystem that WASIp3 APIs should be used,
such as the [`wasip3` crate]. Over time this target will evolve as
implementation in guest toolchains progress, notably:

* The standard library will use WASIp3 APIs natively once they're
  finalized in the WASI subgroup.
* Support through `wasi-libc` will be updated to use WASIp3 natively
  which Rust will then transitively use.
* Longer-term, features such as cooperative multithreading will be added
  to the WASIp3-track of targets to enable using `std::thread`, for
  example, on this target.

These changes are all expected to be non-breaking changes for users of
this target. Runtimes supporting WASIp3, currently Wasmtime and Jco,
support WASIp2 APIs as well and will work with components whether or not
they import WASIp2, both WASIp2 and WASIp3, or just WASIp3 APIs. This
means that changing the internal implementation details of libstd over
time is expected to be a non-breaking change.

[`wasip3` crate]: https://crates.io/crates/wasip3
This commit is contained in:
Alex Crichton 2025-09-30 13:40:27 -07:00
parent 5c7ae0c7ed
commit ce2087692f
17 changed files with 135 additions and 11 deletions

View file

@ -1624,6 +1624,7 @@ supported_targets! {
("wasm32v1-none", wasm32v1_none),
("wasm32-wasip1", wasm32_wasip1),
("wasm32-wasip2", wasm32_wasip2),
("wasm32-wasip3", wasm32_wasip3),
("wasm32-wasip1-threads", wasm32_wasip1_threads),
("wasm32-wali-linux-musl", wasm32_wali_linux_musl),
("wasm64-unknown-unknown", wasm64_unknown_unknown),

View file

@ -0,0 +1,20 @@
//! The `wasm32-wasip3` target is the next in the chain of `wasm32-wasip1`, then
//! `wasm32-wasip2`, then WASIp3. The main feature of WASIp3 is native async
//! support in the component model itself.
//!
//! Like `wasm32-wasip2` this target produces a component by default. Support
//! for `wasm32-wasip3` is very early as of the time of this writing so
//! components produced will still import WASIp2 APIs, but that's ok since it's
//! all component-model-level imports anyway. Over time the imports of the
//! standard library will change to WASIp3.
use crate::spec::Target;
pub(crate) fn target() -> Target {
// As of now WASIp3 is a lightly edited wasip2 target, so start with that
// and this may grow over time as more features are supported.
let mut target = super::wasm32_wasip2::target();
target.llvm_target = "wasm32-wasip3".into();
target.options.env = "p3".into();
target
}

View file

@ -85,6 +85,11 @@ wasip2 = { version = '0.14.4', features = [
'rustc-dep-of-std',
], default-features = false, package = 'wasi' }
[target.'cfg(all(target_os = "wasi", target_env = "p3"))'.dependencies]
wasip2 = { version = '0.14.4', features = [
'rustc-dep-of-std',
], default-features = false, package = 'wasi' }
[target.'cfg(target_os = "uefi")'.dependencies]
r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] }
r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] }

View file

@ -103,7 +103,7 @@ pub mod linux;
all(target_vendor = "fortanix", target_env = "sgx")
)
)))]
#[cfg(any(target_os = "wasi", doc))]
#[cfg(any(target_os = "wasi", any(target_env = "p1", target_env = "p2"), doc))]
pub mod wasi;
#[cfg(any(all(target_os = "wasi", target_env = "p2"), doc))]

View file

@ -36,7 +36,7 @@ cfg_select! {
mod wasip1;
pub use wasip1::*;
}
all(target_os = "wasi", target_env = "p2") => {
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => {
mod wasip2;
pub use wasip2::*;
}

View file

@ -3,7 +3,7 @@ cfg_select! {
all(target_family = "unix", not(target_os = "l4re")),
target_os = "windows",
target_os = "hermit",
all(target_os = "wasi", target_env = "p2"),
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")),
target_os = "solid_asp3",
) => {
mod socket;

View file

@ -25,7 +25,7 @@ cfg_select! {
mod unix;
pub use unix::*;
}
all(target_os = "wasi", target_env = "p2") => {
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => {
mod wasip2;
pub use wasip2::*;
}

View file

@ -49,7 +49,7 @@ cfg_select! {
mod vexos;
pub use self::vexos::*;
}
all(target_os = "wasi", target_env = "p2") => {
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => {
mod wasip2;
pub use self::wasip2::*;
}

View file

@ -90,7 +90,7 @@ cfg_select! {
mod wasip1;
pub use wasip1::fill_bytes;
}
all(target_os = "wasi", target_env = "p2") => {
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => {
mod wasip2;
pub use wasip2::{fill_bytes, hashmap_random_keys};
}
@ -115,7 +115,7 @@ cfg_select! {
target_os = "linux",
target_os = "android",
all(target_family = "wasm", target_os = "unknown"),
all(target_os = "wasi", target_env = "p2"),
all(target_os = "wasi", not(target_env = "p1")),
target_os = "xous",
target_os = "vexos",
)))]

View file

@ -37,7 +37,7 @@ cfg_select! {
mod wasip1;
pub use wasip1::*;
}
all(target_os = "wasi", target_env = "p2") => {
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => {
mod wasip2;
pub use wasip2::*;
}

View file

@ -99,7 +99,7 @@ cfg_select! {
#[cfg(not(target_feature = "atomics"))]
pub use unsupported::{Thread, available_parallelism};
}
all(target_os = "wasi", target_env = "p2") => {
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => {
mod wasip2;
pub use wasip2::{sleep, sleep_until};
#[expect(dead_code)]
@ -146,7 +146,7 @@ cfg_select! {
target_os = "hurd",
target_os = "fuchsia",
target_os = "vxworks",
all(target_os = "wasi", target_env = "p2"),
all(target_os = "wasi", not(target_env = "p1")),
)))]
pub fn sleep_until(deadline: crate::time::Instant) {
use crate::time::Instant;

View file

@ -430,6 +430,16 @@ fn copy_self_contained_objects(
target.triple
)
});
// wasm32-wasip3 doesn't exist in wasi-libc yet, so instead use libs
// from the wasm32-wasip2 target. Once wasi-libc supports wasip3 this
// should be deleted and the native objects should be used.
let srcdir = if target == "wasm32-wasip3" {
assert!(!srcdir.exists(), "wasip3 support is in wasi-libc, this should be updated now");
builder.wasi_libdir(TargetSelection::from_user("wasm32-wasip2")).unwrap()
} else {
srcdir
};
for &obj in &["libc.a", "crt1-command.o", "crt1-reactor.o"] {
copy_and_stamp(
builder,

View file

@ -132,6 +132,7 @@
- [wasm32-wasip1](platform-support/wasm32-wasip1.md)
- [wasm32-wasip1-threads](platform-support/wasm32-wasip1-threads.md)
- [wasm32-wasip2](platform-support/wasm32-wasip2.md)
- [wasm32-wasip3](platform-support/wasm32-wasip3.md)
- [wasm32-wali-linux-musl](platform-support/wasm32-wali-linux.md)
- [wasm32-unknown-emscripten](platform-support/wasm32-unknown-emscripten.md)
- [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md)

View file

@ -198,6 +198,7 @@ target | std | notes
[`wasm32-wasip1`](platform-support/wasm32-wasip1.md) | ✓ | WebAssembly with WASIp1
[`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ | WebAssembly with WASI Preview 1 and threads
[`wasm32-wasip2`](platform-support/wasm32-wasip2.md) | ✓ | WebAssembly with WASIp2
[`wasm32-wasip3`](platform-support/wasm32-wasip3.md) | ✓ | WebAssembly with WASIp3
[`wasm32v1-none`](platform-support/wasm32v1-none.md) | * | WebAssembly limited to 1.0 features and no imports
[`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS
[`x86_64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on x86_64

View file

@ -0,0 +1,83 @@
# `wasm32-wasip3`
**Tier: 3**
The `wasm32-wasip3` target is the next stage of evolution of the
[`wasm32-wasip2`](./wasm32-wasip2.md) target. The `wasm32-wasip3` target enables
the Rust standard library to use WASIp3 APIs to implement various pieces of
functionality. WASIp3 brings native async support over WASIp2, which integrates
well with Rust's `async` ecosystem.
> **Note**: As of 2025-10-01 WASIp3 has not yet been approved by the WASI
> subgroup of the WebAssembly Community Group. Development is expected to
> conclude in late 2025 or early 2026. Until then the Rust standard library
> won't actually use WASIp3 APIs on the `wasm32-wasip3` target as they are not
> yet stable and would reduce the stability of this target. Once WASIp3 is
> approved, however, the standard library will update to use WASIp3 natively.
> **Note**: This target does not yet build as of 2025-10-01 due to and update
> needed in the `libc` crate. Using it will require a `[patch]` for now.
> **Note**: Until the standard library is fully migrated to use the `wasip3`
> crate then components produced for `wasm32-wasip3` may import WASIp2 APIs.
> This is considered a transitionary phase until fully support of libstd is
> implemented.
## Target maintainers
[@alexcrichton](https://github.com/alexcrichton)
## Requirements
This target is cross-compiled. The target supports `std` fully.
## Platform requirements
The WebAssembly runtime should support both WASIp2 and WASIp3. Runtimes also
are required to support components since this target outputs a component as
opposed to a core wasm module. Two example runtimes for WASIp3 are [Wasmtime]
and [Jco].
[Wasmtime]: https://wasmtime.dev/
[Jco]: https://github.com/bytecodealliance/jco
## Building the target
To build this target first acquire a copy of
[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk/). At this time version 22
is the minimum needed.
Next configure the `WASI_SDK_PATH` environment variable to point to where this
is installed. For example:
```text
export WASI_SDK_PATH=/path/to/wasi-sdk-22.0
```
Next be sure to enable LLD when building Rust from source as LLVM's `wasm-ld`
driver for LLD is required when linking WebAssembly code together. Rust's build
system will automatically pick up any necessary binaries and programs from
`WASI_SDK_PATH`.
## Testing
This target is not tested in CI at this time. Locally it can be tested with a
`wasmtime` binary in `PATH` like so:
```text
./x.py test --target wasm32-wasip3 tests/ui
```
## Conditionally compiling code
It's recommended to conditionally compile code for this target with:
```text
#[cfg(all(target_os = "wasi", target_env = "p3"))]
```
## Enabled WebAssembly features
The default set of WebAssembly features enabled for compilation is currently the
same as [`wasm32-unknown-unknown`](./wasm32-unknown-unknown.md). See the
documentation there for more information.

View file

@ -586,6 +586,9 @@
//@ revisions: wasm32_wasip2
//@ [wasm32_wasip2] compile-flags: --target wasm32-wasip2
//@ [wasm32_wasip2] needs-llvm-components: webassembly
//@ revisions: wasm32_wasip3
//@ [wasm32_wasip3] compile-flags: --target wasm32-wasip3
//@ [wasm32_wasip3] needs-llvm-components: webassembly
//@ revisions: wasm32_wali_linux_musl
//@ [wasm32_wali_linux_musl] compile-flags: --target wasm32-wali-linux-musl
//@ [wasm32_wali_linux_musl] needs-llvm-components: webassembly

View file

@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`
LL | target_env = "_UNEXPECTED_VALUE",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5`
= note: expected values for `target_env` are: ``, `gnu`, `macabi`, `mlibc`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `p3`, `relibc`, `sgx`, `sim`, `uclibc`, and `v5`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE`