about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2025-08-26 13:53:11 -0700
committerAlex Crichton <alex@alexcrichton.com>2025-08-28 15:07:15 -0700
commit5d81f033a5ab2fce07898a22ad8c607380812cf7 (patch)
tree322dfd2dac61c80c3445a7bd6ff241269416286d
parentaa27cca79e7888deff3bafed84ea16a5a89cb7c7 (diff)
downloadrust-5d81f033a5ab2fce07898a22ad8c607380812cf7.tar.gz
rust-5d81f033a5ab2fce07898a22ad8c607380812cf7.zip
std: Start supporting WASIp2 natively
This commit is the start of an effort to support WASIp2 natively in the
standard library. Before this commit the `wasm32-wasip2` target behaved
exactly like `wasm32-wasip1` target by importing APIs from the core wasm
module `wasi_snapshot_preview1`. These APIs are satisfied by the
`wasm-component-ld` target by using an [adapter] which implements WASIp1
in terms of WASIp2. This adapter comes at a cost, however, in terms of
runtime indirection and instantiation cost, so ideally the adapter would
be removed entirely. The purpose of this adapter was to provide a
smoother on-ramp from WASIp1 to WASIp2 when it was originally created.

The `wasm32-wasip2` target has been around for long enough now that it's
much more established. Additionally the only thing historically blocking
using WASIp2 directly was implementation effort. Work is now underway to
migrate wasi-libc itself to using WASIp2 directly and now seems as good
a time as any to migrate the Rust standard library too.

Implementation-wise the milestones here are:

* The `wasm32-wasip2` target now also depends on the `wasi` crate at
  version 0.14.* in addition to the preexisting dependency of 0.11.*.
  The 0.14.* release series binds WASIp2 APIs instead of WASIp1 APIs.
* Some preexisting naming around `mod wasi` or `wasi.rs` was renamed to
  `wasip1` where appropriate. For example `std::sys::pal::wasi` is now
  called `std::sys::pal::wasip1`.
* More platform-specific WASI modules are now split between WASIp1 and
  WASIp2. For example getting the current time, randomness, and
  process arguments now use WASIp2 APIs directly instead of using WASIp1
  APIs that require an adapter.

It's worth pointing out that this PR does not migrate the entire
standard library away from using WASIp1 APIs on the `wasm32-wasip2`
target. Everything related to file descriptors and filesystem APIs is
still using WASIp1. Migrating that is left for a future PR. In the
meantime the goal of this change is to lay the groundwork necessary for
migrating in the future. Eventually the goal is to drop the `wasi`
0.11.* dependency on the `wasm32-wasip2` target (the `wasm32-wasip1`
target will continue to retain this dependency).

[adapter]: https://github.com/bytecodealliance/wasmtime/blob/main/crates/wasi-preview1-component-adapter/README.md
-rw-r--r--library/Cargo.lock24
-rw-r--r--library/std/Cargo.toml5
-rw-r--r--library/std/src/sys/args/mod.rs10
-rw-r--r--library/std/src/sys/args/wasip1.rs (renamed from library/std/src/sys/args/wasi.rs)0
-rw-r--r--library/std/src/sys/args/wasip2.rs6
-rw-r--r--library/std/src/sys/pal/mod.rs6
-rw-r--r--library/std/src/sys/pal/wasip1/helpers.rs (renamed from library/std/src/sys/pal/wasi/helpers.rs)0
-rw-r--r--library/std/src/sys/pal/wasip1/mod.rs (renamed from library/std/src/sys/pal/wasi/mod.rs)0
-rw-r--r--library/std/src/sys/pal/wasip1/os.rs (renamed from library/std/src/sys/pal/wasi/os.rs)0
-rw-r--r--library/std/src/sys/pal/wasip1/thread.rs (renamed from library/std/src/sys/pal/wasi/thread.rs)0
-rw-r--r--library/std/src/sys/pal/wasip1/time.rs (renamed from library/std/src/sys/pal/wasi/time.rs)0
-rw-r--r--library/std/src/sys/pal/wasip2/mod.rs6
-rw-r--r--library/std/src/sys/pal/wasip2/thread.rs73
-rw-r--r--library/std/src/sys/pal/wasip2/time.rs69
-rw-r--r--library/std/src/sys/random/mod.rs11
-rw-r--r--library/std/src/sys/random/wasip1.rs (renamed from library/std/src/sys/random/wasi.rs)0
-rw-r--r--library/std/src/sys/random/wasip2.rs9
-rw-r--r--src/tools/tidy/src/deps.rs6
-rw-r--r--triagebot.toml3
19 files changed, 212 insertions, 16 deletions
diff --git a/library/Cargo.lock b/library/Cargo.lock
index 8b860f69492..c16bb3eafbd 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -327,7 +327,8 @@ dependencies = [
  "rustc-demangle",
  "std_detect",
  "unwind",
- "wasi",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+ "wasi 0.14.3+wasi-0.2.4",
  "windows-targets 0.0.0",
 ]
 
@@ -400,6 +401,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "wasi"
+version = "0.14.3+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
+dependencies = [
+ "rustc-std-workspace-alloc",
+ "rustc-std-workspace-core",
+ "wit-bindgen",
+]
+
+[[package]]
 name = "windows-sys"
 version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -475,3 +487,13 @@ name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "wit-bindgen"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
+dependencies = [
+ "rustc-std-workspace-alloc",
+ "rustc-std-workspace-core",
+]
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index eff2562f031..8b78fe53a39 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -80,6 +80,11 @@ wasi = { version = "0.11.0", features = [
     'rustc-dep-of-std',
 ], default-features = false }
 
+[target.'cfg(all(target_os = "wasi", target_env = "p2"))'.dependencies]
+wasip2 = { version = '0.14.3', 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'] }
diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs
index c9627322276..e11e8e5430f 100644
--- a/library/std/src/sys/args/mod.rs
+++ b/library/std/src/sys/args/mod.rs
@@ -32,9 +32,13 @@ cfg_select! {
         mod uefi;
         pub use uefi::*;
     }
-    target_os = "wasi" => {
-        mod wasi;
-        pub use wasi::*;
+    all(target_os = "wasi", target_env = "p1") => {
+        mod wasip1;
+        pub use wasip1::*;
+    }
+    all(target_os = "wasi", target_env = "p2") => {
+        mod wasip2;
+        pub use wasip2::*;
     }
     target_os = "xous" => {
         mod xous;
diff --git a/library/std/src/sys/args/wasi.rs b/library/std/src/sys/args/wasip1.rs
index 72063a87dc9..72063a87dc9 100644
--- a/library/std/src/sys/args/wasi.rs
+++ b/library/std/src/sys/args/wasip1.rs
diff --git a/library/std/src/sys/args/wasip2.rs b/library/std/src/sys/args/wasip2.rs
new file mode 100644
index 00000000000..a57e4b97786
--- /dev/null
+++ b/library/std/src/sys/args/wasip2.rs
@@ -0,0 +1,6 @@
+pub use super::common::Args;
+
+/// Returns the command line arguments
+pub fn args() -> Args {
+    Args::new(wasip2::cli::environment::get_arguments().into_iter().map(|arg| arg.into()).collect())
+}
diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs
index 9376f5249f1..513121c6d30 100644
--- a/library/std/src/sys/pal/mod.rs
+++ b/library/std/src/sys/pal/mod.rs
@@ -49,9 +49,9 @@ cfg_select! {
         mod wasip2;
         pub use self::wasip2::*;
     }
-    target_os = "wasi" => {
-        mod wasi;
-        pub use self::wasi::*;
+    all(target_os = "wasi", target_env = "p1") => {
+        mod wasip1;
+        pub use self::wasip1::*;
     }
     target_family = "wasm" => {
         mod wasm;
diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasip1/helpers.rs
index 404747f0dc7..404747f0dc7 100644
--- a/library/std/src/sys/pal/wasi/helpers.rs
+++ b/library/std/src/sys/pal/wasip1/helpers.rs
diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasip1/mod.rs
index 61dd1c3f98b..61dd1c3f98b 100644
--- a/library/std/src/sys/pal/wasi/mod.rs
+++ b/library/std/src/sys/pal/wasip1/mod.rs
diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasip1/os.rs
index 151ba254ec4..151ba254ec4 100644
--- a/library/std/src/sys/pal/wasi/os.rs
+++ b/library/std/src/sys/pal/wasip1/os.rs
diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasip1/thread.rs
index e062b49bd7a..e062b49bd7a 100644
--- a/library/std/src/sys/pal/wasi/thread.rs
+++ b/library/std/src/sys/pal/wasip1/thread.rs
diff --git a/library/std/src/sys/pal/wasi/time.rs b/library/std/src/sys/pal/wasip1/time.rs
index 892661b312b..892661b312b 100644
--- a/library/std/src/sys/pal/wasi/time.rs
+++ b/library/std/src/sys/pal/wasip1/time.rs
diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs
index 47fe3221c90..5f3fb6d6ddf 100644
--- a/library/std/src/sys/pal/wasip2/mod.rs
+++ b/library/std/src/sys/pal/wasip2/mod.rs
@@ -10,13 +10,11 @@
 #[path = "../wasm/atomics/futex.rs"]
 pub mod futex;
 
-#[path = "../wasi/os.rs"]
+#[path = "../wasip1/os.rs"]
 pub mod os;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
-#[path = "../wasi/thread.rs"]
 pub mod thread;
-#[path = "../wasi/time.rs"]
 pub mod time;
 
 #[path = "../unsupported/common.rs"]
@@ -26,7 +24,7 @@ mod common;
 
 pub use common::*;
 
-#[path = "../wasi/helpers.rs"]
+#[path = "../wasip1/helpers.rs"]
 mod helpers;
 
 // The following exports are listed individually to work around Rust's glob
diff --git a/library/std/src/sys/pal/wasip2/thread.rs b/library/std/src/sys/pal/wasip2/thread.rs
new file mode 100644
index 00000000000..ad52918f15a
--- /dev/null
+++ b/library/std/src/sys/pal/wasip2/thread.rs
@@ -0,0 +1,73 @@
+use crate::ffi::CStr;
+use crate::io;
+use crate::num::NonZero;
+use crate::time::{Duration, Instant};
+
+pub struct Thread(!);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
+
+impl Thread {
+    pub unsafe fn new(
+        _stack: usize,
+        _name: Option<&str>,
+        _p: Box<dyn FnOnce()>,
+    ) -> io::Result<Thread> {
+        // Note that unlike WASIp1 even if the wasm `atomics` feature is enabled
+        // there is no support for threads, not even experimentally, not even in
+        // wasi-libc. Thus this is unconditionally unsupported.
+        crate::sys::unsupported()
+    }
+
+    pub fn yield_now() {
+        // no API for this in WASIp2, but there's also no threads, so that's
+        // sort of expected.
+    }
+
+    pub fn set_name(_name: &CStr) {
+        // nope
+    }
+
+    pub fn sleep(dur: Duration) {
+        // Sleep in increments of `u64::MAX` nanoseconds until the `dur` is
+        // entirely drained.
+        let mut remaining = dur.as_nanos();
+        while remaining > 0 {
+            let amt = u64::try_from(remaining).unwrap_or(u64::MAX);
+            wasip2::clocks::monotonic_clock::subscribe_duration(amt).block();
+            remaining -= u128::from(amt);
+        }
+    }
+
+    pub fn sleep_until(deadline: Instant) {
+        match u64::try_from(deadline.into_inner().as_duration().as_nanos()) {
+            // If the point in time we're sleeping to fits within a 64-bit
+            // number of nanoseconds then directly use `subscribe_instant`.
+            Ok(deadline) => {
+                wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block();
+            }
+            // ... otherwise we're sleeping for 500+ years relative to the
+            // "start" of what the system is using as a clock so speed/accuracy
+            // is not so much of a concern. Use `sleep` instead.
+            Err(_) => {
+                let now = Instant::now();
+
+                if let Some(delay) = deadline.checked_duration_since(now) {
+                    Self::sleep(delay);
+                }
+            }
+        }
+    }
+
+    pub fn join(self) {
+        self.0
+    }
+}
+
+pub(crate) fn current_os_id() -> Option<u64> {
+    None
+}
+
+pub fn available_parallelism() -> io::Result<NonZero<usize>> {
+    crate::sys::unsupported()
+}
diff --git a/library/std/src/sys/pal/wasip2/time.rs b/library/std/src/sys/pal/wasip2/time.rs
new file mode 100644
index 00000000000..f1f6839774b
--- /dev/null
+++ b/library/std/src/sys/pal/wasip2/time.rs
@@ -0,0 +1,69 @@
+use crate::time::Duration;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(Duration);
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(Duration);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
+
+impl Instant {
+    pub fn now() -> Instant {
+        Instant(Duration::from_nanos(wasip2::clocks::monotonic_clock::now()))
+    }
+
+    pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+        self.0.checked_sub(other.0)
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+        Some(Instant(self.0.checked_add(*other)?))
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+        Some(Instant(self.0.checked_sub(*other)?))
+    }
+
+    pub(super) fn as_duration(&self) -> &Duration {
+        &self.0
+    }
+}
+
+impl SystemTime {
+    pub fn now() -> SystemTime {
+        let now = wasip2::clocks::wall_clock::now();
+        SystemTime(Duration::new(now.seconds, now.nanoseconds))
+    }
+
+    #[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
+    pub const fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime {
+        SystemTime(Duration::from_nanos(ts))
+    }
+
+    #[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
+    pub const fn to_wasi_timestamp(&self) -> Option<wasi::Timestamp> {
+        // FIXME: const TryInto
+        let ns = self.0.as_nanos();
+        if ns <= u64::MAX as u128 { Some(ns as u64) } else { None }
+    }
+
+    #[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
+    pub const fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+        // FIXME: ok_or_else with const closures
+        match self.0.checked_sub(other.0) {
+            Some(duration) => Ok(duration),
+            None => Err(other.0 - self.0),
+        }
+    }
+
+    #[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
+    pub const fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_add(*other)?))
+    }
+
+    #[rustc_const_unstable(feature = "const_system_time", issue = "144517")]
+    pub const fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_sub(*other)?))
+    }
+}
diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs
index a7fbae86099..1e0eec07b50 100644
--- a/library/std/src/sys/random/mod.rs
+++ b/library/std/src/sys/random/mod.rs
@@ -86,9 +86,13 @@ cfg_select! {
         mod vxworks;
         pub use vxworks::fill_bytes;
     }
-    target_os = "wasi" => {
-        mod wasi;
-        pub use wasi::fill_bytes;
+    all(target_os = "wasi", target_env = "p1") => {
+        mod wasip1;
+        pub use wasip1::fill_bytes;
+    }
+    all(target_os = "wasi", target_env = "p2") => {
+        mod wasip2;
+        pub use wasip2::{fill_bytes, hashmap_random_keys};
     }
     target_os = "zkvm" => {
         mod zkvm;
@@ -110,6 +114,7 @@ cfg_select! {
     target_os = "linux",
     target_os = "android",
     all(target_family = "wasm", target_os = "unknown"),
+    all(target_os = "wasi", target_env = "p2"),
     target_os = "xous",
 )))]
 pub fn hashmap_random_keys() -> (u64, u64) {
diff --git a/library/std/src/sys/random/wasi.rs b/library/std/src/sys/random/wasip1.rs
index d41da3751fc..d41da3751fc 100644
--- a/library/std/src/sys/random/wasi.rs
+++ b/library/std/src/sys/random/wasip1.rs
diff --git a/library/std/src/sys/random/wasip2.rs b/library/std/src/sys/random/wasip2.rs
new file mode 100644
index 00000000000..a67c8a6428d
--- /dev/null
+++ b/library/std/src/sys/random/wasip2.rs
@@ -0,0 +1,9 @@
+pub fn fill_bytes(bytes: &mut [u8]) {
+    bytes.copy_from_slice(&wasip2::random::random::get_random_bytes(
+        u64::try_from(bytes.len()).unwrap(),
+    ));
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+    wasip2::random::insecure_seed::insecure_seed()
+}
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 6974ede624a..560f11ecf50 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -490,6 +490,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
     "windows_x86_64_gnu",
     "windows_x86_64_gnullvm",
     "windows_x86_64_msvc",
+    "wit-bindgen",
     // tidy-alphabetical-end
 ];
 
@@ -798,7 +799,10 @@ fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, bad: &mut bool)
             continue;
         }
 
-        if !seen_pkgs.insert(&*pkg.name) {
+        // Skip the `wasi` crate here which the standard library explicitly
+        // depends on two version of (one for the `wasm32-wasip1` target and
+        // another for the `wasm32-wasip2` target).
+        if pkg.name.to_string() != "wasi" && !seen_pkgs.insert(&*pkg.name) {
             tidy_error!(
                 bad,
                 "duplicate package `{}` is not allowed for the standard library",
diff --git a/triagebot.toml b/triagebot.toml
index 3b7b216c0d2..cf2e95203fa 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -450,7 +450,8 @@ trigger_files = [
 
 [autolabel."O-wasi"]
 trigger_files = [
-    "library/std/src/sys/pal/wasi",
+    "library/std/src/sys/pal/wasip1",
+    "library/std/src/sys/pal/wasip2",
     "library/std/src/os/wasi"
 ]