From 646d3b4be2b0b02cfdace6fa0614c504ed53c801 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Tue, 9 Jun 2026 09:59:31 -0400 Subject: [PATCH 01/13] feat(rust): Expose merge_structurally_equal_types (#1633) The generate! macro now exposes `merge_structurally_equal_types`. This functionality was previously only available via the `--merge-structurally-equal-types` CLI flag. The underlying functionality is unchanged. Signed-off-by: Scott Andrews --- crates/guest-rust/macro/src/lib.rs | 9 ++++ crates/guest-rust/src/lib.rs | 9 ++++ crates/rust/tests/codegen.rs | 66 ++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index e5ae0b0ef..acf97ad53 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -168,6 +168,9 @@ impl Parse for Config { Opt::EnableMethodChaining(enable) => { opts.enable_method_chaining = enable.value(); } + Opt::MergeStructurallyEqualTypes(enable) => { + opts.merge_structurally_equal_types = Some(Some(enable.value())) + } } } } else { @@ -322,6 +325,7 @@ mod kw { syn::custom_keyword!(imports); syn::custom_keyword!(debug); syn::custom_keyword!(enable_method_chaining); + syn::custom_keyword!(merge_structurally_equal_types); } #[derive(Clone)] @@ -403,6 +407,7 @@ enum Opt { Async(AsyncFilterSet, Span), Debug(syn::LitBool), EnableMethodChaining(syn::LitBool), + MergeStructurallyEqualTypes(syn::LitBool), } impl Parse for Opt { @@ -586,6 +591,10 @@ impl Parse for Opt { } Ok(Opt::Async(set, span)) } + } else if l.peek(kw::merge_structurally_equal_types) { + input.parse::()?; + input.parse::()?; + Ok(Opt::MergeStructurallyEqualTypes(input.parse()?)) } else { Err(l.error()) } diff --git a/crates/guest-rust/src/lib.rs b/crates/guest-rust/src/lib.rs index a98cc7a0b..1be14bead 100644 --- a/crates/guest-rust/src/lib.rs +++ b/crates/guest-rust/src/lib.rs @@ -865,6 +865,15 @@ extern crate std; /// // returning `-> &Self`, to permit method chaining (e.g. for builder). /// // This expectation is also imposed on exports. /// enable_method_chaining: true, +/// +/// // Find all structurally equal types and only generate one type +/// // definition for each equivalence class. +/// // +/// // Other types in the same class will be type aliases to the generated +/// // type. This avoids clone when converting between types that are +/// // structurally equal, which is useful when import and export the same +/// // interface. +/// merge_structurally_equal_types: true, /// }); /// ``` /// diff --git a/crates/rust/tests/codegen.rs b/crates/rust/tests/codegen.rs index a6df22cf9..acc068a96 100644 --- a/crates/rust/tests/codegen.rs +++ b/crates/rust/tests/codegen.rs @@ -235,3 +235,69 @@ mod method_chaining { enable_method_chaining: true }); } + +#[allow(unused, reason = "testing codegen, not functionality")] +mod merge_structurally_equal_types { + wit_bindgen::generate!({ + inline: r#" + package test:merge-structurally-equal-types; + + interface blag { + variant kind1 { a, b(u64), c } + variant kind2 { a, b(u64), c } + record kind3 { a: input-stream } + record kind4 { a: input-stream } + record tree { l: t1, r: t1 } + record t1 { l: t2, r: t2 } + record t2 { l: t3, r: t3 } + record t3 { l: kind1, r: kind2 } + record t-stream { tree: tree, %stream: option> } + resource input-stream { + read: func(len: u64) -> list; + } + f: func(x: kind1) -> kind2; + g: func(x: kind3) -> kind4; + h: func(x: t-stream) -> tree; + } + + interface blah { + use blag.{input-stream, kind4, t-stream}; + variant kind5 { a, b(u64), c } + variant kind6 { a, c, b(u64) } + record kind7 { a: borrow } + record tt { l: t2, r: t2 } + record t1 { l: t3, r: t3 } + record t2 { l: t1, r: t1 } + record t3 { l: kind5, r: kind5 } + variant custom-result { ok(tt), err } + f: func(x: kind6) -> kind5; + g: func(x: kind7) -> kind4; + h: func(x: t-stream) -> custom-result; + + record r1 { a: u8 } + type a1 = u8; + record r2 { a: a1 } + alias-type: func(x: r1) -> r2; + } + + interface resources { + resource r1; + type r2 = r1; + + record t1 { a: r1 } + record t2 { a: r2 } + alias-own: func(x: t1) -> t2; + alias-aggregate: func(x: option) -> option; + } + + world proxy { + import blag; + export blag; + import blah; + export blah; + } + "#, + generate_all, + merge_structurally_equal_types: true + }); +} From 7a6dd1ae491255198b2f273376bff2a4ae81e143 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 12 Jun 2026 14:55:29 -0500 Subject: [PATCH 02/13] Update to wasm-tools 252 (#1634) Keepin up-to-date --- Cargo.lock | 84 +++++++++++++++++++++++++----------------------------- Cargo.toml | 14 ++++----- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8252e180b..2e6530677 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1024,7 +1024,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.251.0", + "wasm-encoder 0.252.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1129,12 +1129,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "url" version = "2.5.8" @@ -1227,9 +1221,9 @@ checksum = "02ed52111afc004605074d1a3fad77ba3ac85c0c0b8ad028919275a46fec8cfd" [[package]] name = "wasm-compose" -version = "0.251.0" +version = "0.252.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b089037d7eb453ed57b560fe7833de0707411c8b9fdc429745ced77e2a1bacb9" +checksum = "d59b710751a35d54732a63851cdfacbfb2266b7160ce174faf269d3f4e84e31b" dependencies = [ "anyhow", "heck", @@ -1240,8 +1234,8 @@ dependencies = [ "serde_derive", "serde_yaml2", "smallvec", - "wasm-encoder 0.251.0", - "wasmparser 0.251.0", + "wasm-encoder 0.252.0", + "wasmparser 0.252.0", "wat", ] @@ -1257,12 +1251,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.251.0" +version = "0.252.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a879a421bd17c528b74721b2abf4c62e8f1d1889c2ba8c3c50d02deaf2ce395" +checksum = "8185ae345fa5687c054626ff9a50e7089797a343d9904d1dc9820eb4c4d3196f" dependencies = [ "leb128fmt", - "wasmparser 0.251.0", + "wasmparser 0.252.0", ] [[package]] @@ -1286,14 +1280,14 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.251.0" +version = "0.252.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f998ccc6e012f7b86865eb2a106c8a0422017a1a88977ce01a69f2244be2e57" +checksum = "2b7e08e02a3cd55bf778009d4cd6faae50da011f293644daf78a531a32d6d142" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.251.0", - "wasmparser 0.251.0", + "wasm-encoder 0.252.0", + "wasmparser 0.252.0", ] [[package]] @@ -1311,9 +1305,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.251.0" +version = "0.252.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437970b35b1a85cfde9c74b2398352d8d653f3bd8e3a3db0c063ea8f5b4b36ff" +checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c" dependencies = [ "bitflags", "hashbrown 0.17.1", @@ -1324,22 +1318,22 @@ dependencies = [ [[package]] name = "wast" -version = "251.0.0" +version = "252.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc7467dda0a96142eb2c980329dfb62480b1e1d3622fdeb1a44e2bca6ceed74" +checksum = "942a3449d6a593fccc111a6241c8df52bda168af30e40bf9580d4394d7374c65" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.251.0", + "wasm-encoder 0.252.0", ] [[package]] name = "wat" -version = "1.251.0" +version = "1.252.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b1086c9e85b95bd6a229a928bc6c6d0662e42af0250c88d067b418831ea4d4" +checksum = "c72a4ba7088f7bac94cf516e49882bdf97068904a563768cf249efc839ec42cb" dependencies = [ "wast", ] @@ -1384,8 +1378,8 @@ dependencies = [ "clap", "heck", "indexmap", - "wasm-encoder 0.251.0", - "wasm-metadata 0.251.0", + "wasm-encoder 0.252.0", + "wasm-metadata 0.252.0", "wit-bindgen-core", "wit-component", ] @@ -1397,7 +1391,7 @@ dependencies = [ "anyhow", "clap", "env_logger", - "wasm-encoder 0.251.0", + "wasm-encoder 0.252.0", "wit-bindgen-c", "wit-bindgen-core", "wit-bindgen-cpp", @@ -1430,8 +1424,8 @@ dependencies = [ "heck", "indexmap", "test-helpers", - "wasm-encoder 0.251.0", - "wasm-metadata 0.251.0", + "wasm-encoder 0.252.0", + "wasm-metadata 0.252.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", @@ -1447,7 +1441,7 @@ dependencies = [ "heck", "indexmap", "regex", - "wasm-metadata 0.251.0", + "wasm-metadata 0.252.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1460,8 +1454,8 @@ dependencies = [ "anyhow", "clap", "heck", - "wasm-encoder 0.251.0", - "wasm-metadata 0.251.0", + "wasm-encoder 0.252.0", + "wasm-metadata 0.252.0", "wit-bindgen-core", "wit-component", ] @@ -1502,7 +1496,7 @@ dependencies = [ "serde_json", "syn", "test-helpers", - "wasm-metadata 0.251.0", + "wasm-metadata 0.252.0", "wit-bindgen", "wit-bindgen-core", "wit-component", @@ -1540,8 +1534,8 @@ dependencies = [ "wac-types", "wasi-preview1-component-adapter-provider", "wasm-compose", - "wasm-encoder 0.251.0", - "wasmparser 0.251.0", + "wasm-encoder 0.252.0", + "wasmparser 0.252.0", "wat", "wit-bindgen-csharp", "wit-component", @@ -1550,9 +1544,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.251.0" +version = "0.252.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a5e60173c413659c689f0581b0cf5d1a2404077568f9ffdce748a9eb2fc913" +checksum = "76db0662b590f45d33d0e363fa13539a5a1eecd35d5a12fe208c335461c1053d" dependencies = [ "anyhow", "bitflags", @@ -1561,18 +1555,18 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.251.0", - "wasm-metadata 0.251.0", - "wasmparser 0.251.0", + "wasm-encoder 0.252.0", + "wasm-metadata 0.252.0", + "wasmparser 0.252.0", "wat", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.251.0" +version = "0.252.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e960732e824fab95099971a09e638979347c94ca48568d3c854c945729196947" +checksum = "4266bea110371c620ccf3201c5023676046bc4556e5c7cfb5d500bda5ebc162d" dependencies = [ "anyhow", "hashbrown 0.17.1", @@ -1583,8 +1577,8 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "unicode-xid", - "wasmparser 0.251.0", + "unicode-ident", + "wasmparser 0.252.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4bd288573..5bf23e8ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,13 +47,13 @@ syn = { version = "2.0.89", features = ["printing"] } futures = "0.3.31" macro-string = "0.2.0" -wat = "1.251.0" -wasmparser = "0.251.0" -wasm-encoder = "0.251.0" -wasm-metadata = { version = "0.251.0", default-features = false } -wit-parser = "0.251.0" -wit-component = "0.251.0" -wasm-compose = "0.251.0" +wat = "1.252.0" +wasmparser = "0.252.0" +wasm-encoder = "0.252.0" +wasm-metadata = { version = "0.252.0", default-features = false } +wit-parser = "0.252.0" +wit-component = "0.252.0" +wasm-compose = "0.252.0" wit-bindgen-core = { path = 'crates/core', version = '0.58.0' } wit-bindgen-c = { path = 'crates/c', version = '0.58.0' } From 08416e210591f8d9f4098c94529de14394375a02 Mon Sep 17 00:00:00 2001 From: Christof Petig <33882057+cpetig@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:48:56 +0200 Subject: [PATCH 03/13] Rust: Provide a trait method to (optionally) control resource allocation (#1625) * Resource allocation override option * custom resource allocation * cut the dirty tricks out of the arena * hide that it is an Option (typedef), put underscore to the end * Some review comments of mine and refactorings * Fix doc example --------- Co-authored-by: Alex Crichton --- .../src/examples/_4_exported_resources.rs | 1 + crates/guest-rust/src/lib.rs | 2 + crates/guest-rust/src/resource.rs | 57 +++++++++++ crates/guest-rust/src/rt/mod.rs | 2 + crates/rust/src/bindgen.rs | 4 +- crates/rust/src/interface.rs | 95 +++++++++++++----- .../rust/arena-allocated-resources/runner.rs | 16 ++++ .../rust/arena-allocated-resources/test.rs | 96 +++++++++++++++++++ .../rust/arena-allocated-resources/test.wit | 18 ++++ 9 files changed, 267 insertions(+), 24 deletions(-) create mode 100644 crates/guest-rust/src/resource.rs create mode 100644 tests/runtime/rust/arena-allocated-resources/runner.rs create mode 100644 tests/runtime/rust/arena-allocated-resources/test.rs create mode 100644 tests/runtime/rust/arena-allocated-resources/test.wit diff --git a/crates/guest-rust/src/examples/_4_exported_resources.rs b/crates/guest-rust/src/examples/_4_exported_resources.rs index 5d1281be5..e47914d4c 100644 --- a/crates/guest-rust/src/examples/_4_exported_resources.rs +++ b/crates/guest-rust/src/examples/_4_exported_resources.rs @@ -23,4 +23,5 @@ crate::generate!({ } } "#, + runtime_path: "crate::rt", // only needed for this in-crate example. }); diff --git a/crates/guest-rust/src/lib.rs b/crates/guest-rust/src/lib.rs index 1be14bead..34aaea0df 100644 --- a/crates/guest-rust/src/lib.rs +++ b/crates/guest-rust/src/lib.rs @@ -887,6 +887,8 @@ pub mod examples; #[doc(hidden)] pub mod rt; +pub mod resource; + #[cfg(feature = "inter-task-wakeup")] pub use rt::async_support::UnitStreamOps; #[cfg(feature = "async-spawn")] diff --git a/crates/guest-rust/src/resource.rs b/crates/guest-rust/src/resource.rs new file mode 100644 index 000000000..075502fad --- /dev/null +++ b/crates/guest-rust/src/resource.rs @@ -0,0 +1,57 @@ +//! Helper traits, types, and utilities for managing resources in the component +//! model. + +/// A trait implemented by all resources that a component might export. +/// +/// This is an implementation detail primarily for the code generated by +/// exported resources. The primary purpose of this trait is to serve as an +/// abstraction for the in-memory storage of a resource. +pub trait Resource: Sized + 'static { + /// The type which is actually stored in-memory for this resource. + /// + /// By default this is `Option`. + type Rep: ResourceRep; +} + +impl Resource for T { + type Rep = Option; +} + +/// A trait used to define how to access the underlying data `T` from an +/// in-memory representation. +/// +/// This is used as a bound on the [`Resource::Rep`] associated type which is in +/// turn used to access data within a resources. +pub unsafe trait ResourceRep { + /// Creates a new instance of `Self` which wraps the provided data. + fn rep_new(inner: T) -> Self; + + /// Acquires `&T` from a raw pointer to `Self`. + unsafe fn rep_as_ref<'a>(ptr: *const Self) -> &'a T; + + /// Acquires `&mut T` from a raw pointer to `Self`. + unsafe fn rep_as_mut<'a>(ptr: *mut Self) -> &'a mut T; + + /// Takes the value out of `Self` at the provided pointer. + /// + /// Note that `ptr` will later be deallocated meaning that it must not run + /// the destructor of `T` after this method is called. This is guaranteed to + /// be called at most once, however. Additionally after calling this method + /// it's guaranteed that the `rep_as_*` method above will not be called. + unsafe fn rep_take<'a>(ptr: *mut Self) -> T; +} + +unsafe impl ResourceRep for Option { + fn rep_new(inner: T) -> Option { + Some(inner) + } + unsafe fn rep_as_ref<'a>(ptr: *const Option) -> &'a T { + unsafe { (*ptr).as_ref().unwrap() } + } + unsafe fn rep_as_mut<'a>(ptr: *mut Option) -> &'a mut T { + unsafe { (*ptr).as_mut().unwrap() } + } + unsafe fn rep_take(ptr: *mut Option) -> T { + unsafe { (*ptr).take().unwrap() } + } +} diff --git a/crates/guest-rust/src/rt/mod.rs b/crates/guest-rust/src/rt/mod.rs index 40736f6ef..e62c52640 100644 --- a/crates/guest-rust/src/rt/mod.rs +++ b/crates/guest-rust/src/rt/mod.rs @@ -1,6 +1,8 @@ use core::alloc::Layout; use core::ptr::{self, NonNull}; +pub use crate::resource::{Resource, ResourceRep}; + // Re-export `bitflags` so that we can reference it from macros. #[cfg(feature = "bitflags")] pub use bitflags; diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index bee880167..767beb6a9 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -406,7 +406,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { let result = if is_own { format!("{name}::from_handle({op} as u32)") } else if self.r#gen.is_exported_resource(*resource) { - format!("{name}Borrow::lift({op} as u32 as usize)") + format!( + "{name}Borrow::lift(core::ptr::with_exposed_provenance({op} as u32 as usize))" + ) } else { let tmp = format!("handle{}", self.tmp()); self.handle_decls.push(format!("let {tmp};")); diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index c81c3271e..148249be0 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -196,9 +196,10 @@ impl<'i> InterfaceGenerator<'i> { .map(|(resource, (trait_name, ..))| (resource.unwrap(), trait_name.as_str())), ) } + let rt = self.r#gen.runtime_path().to_string(); for (resource, (trait_name, methods)) in traits.iter() { - uwriteln!(self.src, "pub trait {trait_name}: 'static {{"); + uwriteln!(self.src, "pub trait {trait_name}: {rt}::Resource {{"); let resource = resource.unwrap(); let resource_name = self.resolve.types[resource].name.as_ref().unwrap(); let (_, interface_name) = interface.unwrap(); @@ -239,6 +240,55 @@ fn _resource_rep(handle: u32) -> *mut u8 "# ); + let box_path = self.path_to_box(); + uwriteln!( + self.src, + r#" +/// Place this resource's representation into a location with a stable pointer, +/// returning a pointer to that location. +/// +/// This method is used to place `val` on the heap, for example, or possibly in +/// an arena. The returned pointer must remain valid for the lifetime of this +/// resource. The default implementation of this metho will place `val` onto the +/// heap with `Box`. The returned pointer will be deallocated by the sibling +/// `resource_from_raw_` function. +/// +/// # Safety +/// +/// Note that this method is not `unsafe` to call, but it is `unsafe` to +/// define. When overriding this method you must additionally override the +/// `resource_from_raw_` to insert an appropriate deallocation for the returned +/// pointer. In the future this might become a default associated trait bound, +/// but that's not stable in Rust right now. +#[doc(hidden)] +unsafe fn resource_into_raw_(val: Self::Rep) -> *mut Self::Rep +{{ + {box_path}::into_raw({box_path}::new(val)) +}} + +/// Consumes and deallocates a pointer previously returned by +/// `resource_into_raw_`. +/// +/// This function is used to deallocate resources and allocations associated +/// with the allocation routine when creating a resource. The default +/// implementation of this method will read `handle`'s value and deallocate it +/// as a `Box`. +/// +/// Note that when overriding this method you'll almost surely want to override +/// `resource_into_raw_` as well. +/// +/// # Safety +/// +/// This method is only safe to call with pointers previously created by +/// `resource_into_raw_`. For more information see `Box::from_raw` for examples. +#[doc(hidden)] +unsafe fn resource_from_raw_(handle: *mut Self::Rep) -> Self::Rep +{{ + *unsafe {{ {box_path}::from_raw(handle) }} +}} + + "# + ); for method in methods { self.src.push_str(method); } @@ -2716,7 +2766,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { Identifier::World(_) => unimplemented!("resource exports from worlds"), Identifier::StreamOrFuturePayload => unreachable!(), }; - let box_path = self.path_to_box(); + let rt = self.r#gen.runtime_path(); uwriteln!( self.src, r#" @@ -2726,8 +2776,6 @@ pub struct {camel} {{ handle: {resource}<{camel}>, }} -type _{camel}Rep = Option; - impl {camel} {{ /// Creates a new resource from the specified representation. /// @@ -2736,31 +2784,30 @@ impl {camel} {{ /// create a handle. The owned handle is then returned as `{camel}`. pub fn new(val: T) -> Self {{ Self::type_guard::(); - let val: _{camel}Rep = Some(val); - let ptr: *mut _{camel}Rep = - {box_path}::into_raw({box_path}::new(val)); + let rep = >::rep_new(val); unsafe {{ + let ptr = T::resource_into_raw_(rep); Self::from_handle(T::_resource_new(ptr.cast())) }} }} /// Gets access to the underlying `T` which represents this resource. pub fn get(&self) -> &T {{ - let ptr = unsafe {{ &*self.as_ptr::() }}; - ptr.as_ref().unwrap() + let ptr = self.as_ptr::(); + unsafe {{ >::rep_as_ref(ptr) }} }} /// Gets mutable access to the underlying `T` which represents this /// resource. pub fn get_mut(&mut self) -> &mut T {{ - let ptr = unsafe {{ &mut *self.as_ptr::() }}; - ptr.as_mut().unwrap() + let ptr = self.as_ptr::(); + unsafe {{ >::rep_as_mut(ptr) }} }} /// Consumes this resource and returns the underlying `T`. pub fn into_inner(self) -> T {{ - let ptr = unsafe {{ &mut *self.as_ptr::() }}; - ptr.take().unwrap() + let ptr = self.as_ptr::(); + unsafe {{ >::rep_take(ptr) }} }} #[doc(hidden)] @@ -2783,7 +2830,7 @@ impl {camel} {{ // It's theoretically possible to implement the `Guest{camel}` trait twice // so guard against using it with two different types here. #[doc(hidden)] - fn type_guard() {{ + fn type_guard() {{ use core::any::TypeId; static mut LAST_TYPE: Option = None; unsafe {{ @@ -2797,12 +2844,14 @@ impl {camel} {{ }} #[doc(hidden)] - pub unsafe fn dtor(handle: *mut u8) {{ + pub unsafe fn dtor(handle: *mut u8) {{ Self::type_guard::(); - let _ = unsafe {{ {box_path}::from_raw(handle as *mut _{camel}Rep) }}; + unsafe {{ + let _rep = T::resource_from_raw_(handle.cast()); + }} }} - fn as_ptr(&self) -> *mut _{camel}Rep {{ + fn as_ptr(&self) -> *mut T::Rep {{ {camel}::type_guard::(); T::_resource_rep(self.handle()).cast() }} @@ -2813,29 +2862,29 @@ impl {camel} {{ #[derive(Debug)] #[repr(transparent)] pub struct {camel}Borrow<'a> {{ - rep: *mut u8, + rep: *const u8, _marker: core::marker::PhantomData<&'a {camel}>, }} impl<'a> {camel}Borrow<'a>{{ #[doc(hidden)] - pub unsafe fn lift(rep: usize) -> Self {{ + pub unsafe fn lift(rep: *const u8) -> Self {{ Self {{ - rep: rep as *mut u8, + rep, _marker: core::marker::PhantomData, }} }} /// Gets access to the underlying `T` in this resource. pub fn get(&self) -> &'a T {{ - let ptr = unsafe {{ &mut *self.as_ptr::() }}; - ptr.as_ref().unwrap() + let ptr = self.as_ptr::(); + unsafe {{ >::rep_as_ref(ptr) }} }} // NB: mutable access is not allowed due to the component model allowing // multiple borrows of the same resource. - fn as_ptr(&self) -> *mut _{camel}Rep {{ + fn as_ptr(&self) -> *const T::Rep {{ {camel}::type_guard::(); self.rep.cast() }} diff --git a/tests/runtime/rust/arena-allocated-resources/runner.rs b/tests/runtime/rust/arena-allocated-resources/runner.rs new file mode 100644 index 000000000..665a07632 --- /dev/null +++ b/tests/runtime/rust/arena-allocated-resources/runner.rs @@ -0,0 +1,16 @@ +include!(env!("BINDINGS")); + +use crate::test::arena_allocated_resources::to_test::Thing; + +struct Component; + +export!(Component); + +impl Guest for Component { + fn run() { + let thing1 = Thing::new(3); + let thing2 = Thing::new(5); + assert_eq!(3, thing1.get()); + assert_eq!(5, thing2.get()); + } +} diff --git a/tests/runtime/rust/arena-allocated-resources/test.rs b/tests/runtime/rust/arena-allocated-resources/test.rs new file mode 100644 index 000000000..71fb684e0 --- /dev/null +++ b/tests/runtime/rust/arena-allocated-resources/test.rs @@ -0,0 +1,96 @@ +include!(env!("BINDINGS")); + +use crate::exports::test::arena_allocated_resources::to_test::{Guest, GuestThing}; + +export!(Component); + +struct Component; + +impl Guest for Component { + type Thing = MyThing; +} + +mod arena { + + use core::sync::atomic::{AtomicUsize, Ordering}; + use core::{cell::UnsafeCell, mem::MaybeUninit}; + + /// A simple no_std arena allocator for fixed-size allocations. + /// + /// The arena allocates items of type T sequentially from a pre-allocated buffer + /// and does not support individual deallocation. Memory is reclaimed + /// only when the entire arena is reset. + pub struct Arena { + buffer: [UnsafeCell>; SIZE], + offset: AtomicUsize, + } + + // Element allocation is atomic and elements are exclusively handed out after allocation, + // so the arena can be send to other threads and simultaneosly accessed by multiple threads + unsafe impl Sync for Arena {} + unsafe impl Send for Arena {} + + impl Arena { + pub const fn new() -> Self { + Self { + buffer: [const { UnsafeCell::new(MaybeUninit::uninit()) }; SIZE], + offset: AtomicUsize::new(0), + } + } + + /// Allocates space for a single item of type T. + /// Returns a mutable reference to the allocated memory, or None if there's insufficient space. + pub fn alloc_one(&self) -> Option<&mut T> { + // short circuit the exhausted state (don't increment if full) + if self.offset.load(Ordering::Relaxed) >= SIZE { + None + } else { + // now try to allocate for real + let pos = self.offset.fetch_add(1, Ordering::Acquire); + if pos >= SIZE { + // now self.offset is already beyond SIZE, reduce our increment and return none + self.offset.fetch_sub(1, Ordering::Release); + None + } else { + let ptr = self.buffer[pos].get(); + // SAFETY: we demand exclusive ownership of the item in the arena + let uninit = unsafe { &mut *ptr }; + Some(uninit.write(Default::default())) + } + } + } + } +} + +use arena::Arena; + +#[derive(Clone)] +struct MyThing { + contents: u32, +} + +static ARENA: Arena, 4> = Arena::new(); + +impl GuestThing for MyThing { + fn new(v: u32) -> MyThing { + MyThing { contents: v } + } + + fn get(&self) -> u32 { + self.contents + } + + unsafe fn resource_into_raw_(val: Self::Rep) -> *mut Self::Rep { + val.and_then(|v| { + ARENA.alloc_one().map(|x| { + *x = Some(v); + x as *mut _ + }) + }) + .unwrap_or(core::ptr::null_mut()) + } + + unsafe fn resource_from_raw_(handle: *mut Self::Rep) -> Self::Rep { + unsafe { &mut *handle }.take() + } +} diff --git a/tests/runtime/rust/arena-allocated-resources/test.wit b/tests/runtime/rust/arena-allocated-resources/test.wit new file mode 100644 index 000000000..9a2ffc934 --- /dev/null +++ b/tests/runtime/rust/arena-allocated-resources/test.wit @@ -0,0 +1,18 @@ +package test:arena-allocated-resources; + +interface to-test { + resource thing { + constructor(v: u32); + get: func() -> u32; + } +} + +world test { + export to-test; +} + +world runner { + import to-test; + + export run: func(); +} From cfd7b77145e96055a51a8940751a3607b4178b1b Mon Sep 17 00:00:00 2001 From: Sano Suguru <43309177+sano-suguru@users.noreply.github.com> Date: Wed, 17 Jun 2026 04:51:21 +0900 Subject: [PATCH 04/13] csharp: omit version namespace segment when package is unambiguous (#1635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port the `pkg_has_multiple_versions` guard from the C generator (crates/c/src/lib.rs, `interface_identifier`) to the C# backend. Previously the C# generator unconditionally appended a version segment to the namespace of every versioned package (e.g. `my.dep.v0_1_0`). Now the segment is emitted only when the same package namespace+name appears at more than one version in the Resolve — i.e. when omitting it would cause a name collision. The guard logic is identical to the C backend's. The mangling format intentionally differs: C# emits `v{major}_{minor}_{patch}.` while C uses a trailing-underscore snake-case segment. Tests: - Unit tests on `interface_name` assert the namespace directly: the segment is dropped for a single-version package and kept when two versions coexist (verified to fail if the guard is reverted). - A `tests/codegen/single-version-package` fixture (one versioned import, single version) builds the drop path across all backends, alongside the existing `multiversion` fixture (two versions). Closes #1078 Co-authored-by: sanosuguru --- crates/csharp/src/world_generator.rs | 97 ++++++++++++++++++- .../wit/deps/dep/root.wit | 5 + .../single-version-package/wit/root.wit | 5 + 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 tests/codegen/single-version-package/wit/deps/dep/root.wit create mode 100644 tests/codegen/single-version-package/wit/root.wit diff --git a/crates/csharp/src/world_generator.rs b/crates/csharp/src/world_generator.rs index 685c69076..fb7d2450b 100644 --- a/crates/csharp/src/world_generator.rs +++ b/crates/csharp/src/world_generator.rs @@ -1049,8 +1049,18 @@ fn interface_name( ); if let Some(version) = &name.version { - let v = version.to_string().replace(['.', '-', '+'], "_"); - ns = format!("{}v{}.", ns, &v); + // Only include the version segment when the same package name exists at + // multiple versions in this Resolve; omit it when unambiguous. + // Mirrors the equivalent guard in the C generator (crates/c/src/lib.rs). + let pkg_has_multiple_versions = resolve.packages.iter().any(|(_, p)| { + p.name.namespace == name.namespace + && p.name.name == name.name + && p.name.version != name.version + }); + if pkg_has_multiple_versions { + let v = version.to_string().replace(['.', '-', '+'], "_"); + ns = format!("{}v{}.", ns, &v); + } } ns } @@ -1106,3 +1116,86 @@ fn by_resource<'a>( } by_resource } + +#[cfg(test)] +mod tests { + use super::*; + + /// Build a `Resolve` from inline WIT and return the generated C# namespace + /// for every imported interface in `world`. + fn imported_interface_namespaces(wit: &[(&str, &str)], world: &str) -> Vec { + let mut resolve = Resolve::default(); + for (path, contents) in wit { + resolve.push_str(path, contents).unwrap(); + } + let (world_id, _) = resolve + .worlds + .iter() + .find(|(_, w)| w.name == world) + .unwrap(); + let keys: Vec = resolve.worlds[world_id] + .imports + .keys() + .filter(|k| matches!(k, WorldKey::Interface(_))) + .cloned() + .collect(); + let mut csharp = CSharp::default(); + keys.iter() + .map(|k| interface_name(&mut csharp, &resolve, k, Direction::Import)) + .collect() + } + + #[test] + fn version_segment_omitted_when_package_is_unambiguous() { + let names = imported_interface_namespaces( + &[ + ( + "dep.wit", + "package my:dep@0.1.0;\ninterface a { x: func(); }", + ), + ( + "root.wit", + "package foo:bar;\nworld the-world { import my:dep/a@0.1.0; }", + ), + ], + "the-world", + ); + assert!(!names.is_empty()); + for n in &names { + assert!(n.contains("my.dep"), "expected package segment: {n}"); + assert!( + !n.contains("v0_1_0"), + "version segment should be omitted: {n}" + ); + } + } + + #[test] + fn version_segment_kept_when_multiple_versions_coexist() { + let names = imported_interface_namespaces( + &[ + ( + "v1.wit", + "package my:dep@0.1.0;\ninterface a { x: func(); }", + ), + ( + "v2.wit", + "package my:dep@0.2.0;\ninterface a { x: func(); }", + ), + ( + "root.wit", + "package foo:bar;\nworld the-world { import my:dep/a@0.1.0; import my:dep/a@0.2.0; }", + ), + ], + "the-world", + ); + assert!( + names.iter().any(|n| n.contains("v0_1_0")), + "expected a v0_1_0 segment: {names:?}" + ); + assert!( + names.iter().any(|n| n.contains("v0_2_0")), + "expected a v0_2_0 segment: {names:?}" + ); + } +} diff --git a/tests/codegen/single-version-package/wit/deps/dep/root.wit b/tests/codegen/single-version-package/wit/deps/dep/root.wit new file mode 100644 index 000000000..cdda0a38b --- /dev/null +++ b/tests/codegen/single-version-package/wit/deps/dep/root.wit @@ -0,0 +1,5 @@ +package my:dep@0.1.0; + +interface a { + x: func(); +} diff --git a/tests/codegen/single-version-package/wit/root.wit b/tests/codegen/single-version-package/wit/root.wit new file mode 100644 index 000000000..82df7fd45 --- /dev/null +++ b/tests/codegen/single-version-package/wit/root.wit @@ -0,0 +1,5 @@ +package foo:bar; + +world the-world { + import my:dep/a@0.1.0; +} From 154003e80c52e523a941ac79e44db6e087de453e Mon Sep 17 00:00:00 2001 From: Saicharan Ramineni <84414237+GodlyDonuts@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:32:17 -0400 Subject: [PATCH 05/13] async: remove inter-task wakeup stream from its waitable set before cancelling (#1638) `cancel_inter_task_stream_read` issued a synchronous `stream.cancel-read` on the inter-task wakeup stream while that stream was still a member of the task's waitable set, only calling `remove_waitable` afterwards. Per the Component Model (component-model#647), a synchronous `{stream,future}.cancel-{read,write}` traps if the waitable is still in a waitable set, for the same reason synchronous reads/writes do. Runtimes that enforce this trap (e.g. recent Wasmtime) therefore fault here during ordinary async operation. Reorder so the stream leaves the waitable set before the synchronous cancel, matching the unregister-then-cancel ordering already used by the general `WaitableOperation::cancel` path. The cancel result is discarded as before, so behavior is otherwise unchanged. --- crates/guest-rust/src/rt/async_support/inter_task_wakeup.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/guest-rust/src/rt/async_support/inter_task_wakeup.rs b/crates/guest-rust/src/rt/async_support/inter_task_wakeup.rs index d36d34bd6..31a84ea15 100644 --- a/crates/guest-rust/src/rt/async_support/inter_task_wakeup.rs +++ b/crates/guest-rust/src/rt/async_support/inter_task_wakeup.rs @@ -57,13 +57,17 @@ impl FutureState<'_> { } self.inter_task_wakeup.stream_reading = false; let handle = self.inter_task_wakeup.stream.as_mut().unwrap().handle(); + // Remove the stream from our waitable set before cancelling. A + // synchronous `stream.cancel-read` traps if the stream is still a member + // of a waitable set, so this must happen first. This matches the + // unregister-then-cancel ordering in `WaitableOperation::cancel`. + self.remove_waitable(handle); // Note that the return code here is discarded. No matter what the read // is cancelled, and whether we actually read something or whether we // cancelled doesn't matter. unsafe { UnitStreamOps.cancel_read(handle); } - self.remove_waitable(handle); } } From 4b1ba9a0019b0e8ae662cc8c034310042d211f32 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 29 Jun 2026 09:06:46 -0500 Subject: [PATCH 06/13] Evolve the wasip3 async C ABI for tasks (#1637) * Evolve the wasip3 async C ABI for tasks This commit is an evolution of the C ABI used to managed task-related infrastructure in WASIp3 with the goal of solving #1618. The basic problem of #1618 is that waitables in Rust aren't guaranteed to be polled within the context of the original task. For example by mixing an `async` Rust export and `block_on` it's possible to "cross the wires" and poll in one context while dropping/completing in another context. This can lead to buggy situations where a waitable is left in a set, not added to an appropriate set, or generally mis-managed. The solution here is to enhance the current C ABI of task management with clone/drop operations. Notably this enables waitables to retain a strong reference to the task state as opposed to always consulting what the current task in. This fixes a few situations such as: * When dropping a half-finished waitable it no longer needs to be dropped in the context of the original task. Dropping will unregister the waitable from a task that it was originally registered with. * When a waitable is moved from one task to another it needs to implicitly de-register with the previous task, and this was not previously done. Now with a retained strong reference it's able to clear out previous state upon re-registering with a new task. This change requires some finesse as this needs to be ABI-stable to work with previous versions of the `wit-bindgen` crate. The runtime support additionally can't assume that the new ABI bits are available and instead needs to handle the previous ABI as well. Not too too bad, in the end, though. This additionally did some refactoring of the state associated with async tasks to juggle things around and better represent the raw pointers/`Arc`/etc from before. Closes #1618 * Document the C ABI * Fix disabled compile * Just one version marker --- crates/guest-rust/src/rt/async_support.rs | 271 +++++++++++------- .../guest-rust/src/rt/async_support/cabi.rs | 69 +++++ .../src/rt/async_support/inter_task_wakeup.rs | 12 +- .../inter_task_wakeup_disabled.rs | 4 +- .../src/rt/async_support/waitable.rs | 167 ++++++++--- .../async/rust-async-and-block-on/runner.rs | 49 ++++ .../async/rust-async-and-block-on/test.rs | 13 + .../async/rust-async-and-block-on/test.wit | 15 + 8 files changed, 453 insertions(+), 147 deletions(-) create mode 100644 tests/runtime-async/async/rust-async-and-block-on/runner.rs create mode 100644 tests/runtime-async/async/rust-async-and-block-on/test.rs create mode 100644 tests/runtime-async/async/rust-async-and-block-on/test.wit diff --git a/crates/guest-rust/src/rt/async_support.rs b/crates/guest-rust/src/rt/async_support.rs index 2a5218ed1..680e5f9ab 100644 --- a/crates/guest-rust/src/rt/async_support.rs +++ b/crates/guest-rust/src/rt/async_support.rs @@ -1,12 +1,13 @@ #![deny(missing_docs)] +use self::try_lock::TryLock; use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::sync::Arc; use alloc::task::Wake; use core::ffi::c_void; use core::future::Future; -use core::mem; +use core::mem::{self, ManuallyDrop}; use core::pin::Pin; use core::ptr; use core::sync::atomic::{AtomicU32, Ordering}; @@ -104,15 +105,28 @@ use spawn_disabled as spawn; /// Represents a task created by either a call to an async-lifted export or a /// future run using `block_on` or `start_task`. -struct FutureState<'a> { +struct TaskState<'a> { /// Remaining work to do (if any) before this task can be considered "done". /// /// Note that we won't tell the host the task is done until this is drained /// and `waitables` is empty. tasks: spawn::Tasks<'a>, - /// The waitable set containing waitables created by this task, if any. - waitable_set: Option, + /// Dual-mode rust-level Waker and C ABI level "task" for wasip3 + /// integration. + shared: Arc, + + /// Clone of `shared` field, but represented as `std::task::Waker`. + waker: Waker, + + /// State related to supporting inter-task wakeup scenarios. + inter_task_wakeup: inter_task_wakeup::State, +} + +struct SharedTaskState { + /// One of `SLEEP_STATE_*` indicating the current status. + sleep_state: AtomicU32, + inter_task_stream: inter_task_wakeup::WakerState, /// State of all waitables in `waitable_set`, and the ptr/callback they're /// associated with. @@ -123,56 +137,44 @@ struct FutureState<'a> { // the `wasi_snapshot_preview1` adapter when targeting `wasm32-wasip2` and // later, and that's expensive enough that we'd prefer to avoid it for apps // which otherwise make no use of the adapter. - waitables: BTreeMap, - - /// Raw structure used to pass to `cabi::wasip3_task_set` - wasip3_task: cabi::wasip3_task, - - /// Rust-level state for the waker, notably a bool as to whether this has - /// been woken. - waker: Arc, + // + // Also note that the `TryLock` here should never be contended, but it's + // used for interior mutability. + waitables: TryLock>, - /// Clone of `waker` field, but represented as `std::task::Waker`. - waker_clone: Waker, + /// The waitable set containing waitables created by this task, if any. + // + // Note the `TryLock` is the same as `waitables` above, it's serving the + // purpose of interior mutability. + waitable_set: TryLock>, +} - /// State related to supporting inter-task wakeup scenarios. - inter_task_wakeup: inter_task_wakeup::State, +/// An entry of `SharedTaskState::waitables` which is added through the C ABI. +struct CabiWaitable { + callback: unsafe extern "C" fn(*mut c_void, u32), + callback_ptr: *mut c_void, } -impl FutureState<'_> { - fn new(future: BoxFuture<'_>) -> FutureState<'_> { - let waker = Arc::new(FutureWaker::default()); - FutureState { - waker_clone: waker.clone().into(), - waker, +unsafe impl Send for CabiWaitable {} + +impl TaskState<'_> { + fn new(future: BoxFuture<'_>) -> TaskState<'_> { + let shared = Arc::new(SharedTaskState { + sleep_state: AtomicU32::new(0), + inter_task_stream: Default::default(), + waitables: Default::default(), + waitable_set: Default::default(), + }); + TaskState { + waker: shared.clone().into(), + shared, tasks: spawn::Tasks::new(future), - waitable_set: None, - waitables: BTreeMap::new(), - wasip3_task: cabi::wasip3_task { - // This pointer is filled in before calling `wasip3_task_set`. - ptr: ptr::null_mut(), - version: cabi::WASIP3_TASK_V1, - waitable_register, - waitable_unregister, - }, inter_task_wakeup: Default::default(), } } - fn get_or_create_waitable_set(&mut self) -> &WaitableSet { - self.waitable_set.get_or_insert_with(WaitableSet::new) - } - - fn add_waitable(&mut self, waitable: u32) { - self.get_or_create_waitable_set().join(waitable) - } - - fn remove_waitable(&mut self, waitable: u32) { - WaitableSet::remove_waitable_from_all_sets(waitable) - } - fn remaining_work(&self) -> bool { - !self.waitables.is_empty() + !self.shared.waitables.try_lock().unwrap().is_empty() } /// Handles the `event{0,1,2}` event codes and returns a corresponding @@ -190,7 +192,7 @@ impl FutureState<'_> { // Cancellation is mapped to destruction in Rust, so return a // code/bool indicating we're done. The caller will then - // appropriately deallocate this `FutureState` which will + // appropriately deallocate this `TaskState` which will // transitively run all destructors. return CallbackCode::Exit; } @@ -200,7 +202,7 @@ impl FutureState<'_> { self.with_p3_task_set(|me| { // Transition our sleep state to ensure that the inter-task stream // isn't used since there's no need to use that here. - me.waker + me.shared .sleep_state .store(SLEEP_STATE_WOKEN, Ordering::Relaxed); @@ -221,13 +223,13 @@ impl FutureState<'_> { me.cancel_inter_task_stream_read(); loop { - let mut context = Context::from_waker(&me.waker_clone); + let mut context = Context::from_waker(&me.waker); // On each turn of this loop reset the state to "polling" // which clears out any pending wakeup if one was sent. This // in theory helps minimize wakeups from previous iterations // happening in this iteration. - me.waker + me.shared .sleep_state .store(SLEEP_STATE_POLLING, Ordering::Relaxed); @@ -242,7 +244,8 @@ impl FutureState<'_> { Poll::Ready(()) => { assert!(me.tasks.is_empty()); if me.remaining_work() { - let waitable = me.waitable_set.as_ref().unwrap().as_raw(); + let set = me.shared.waitable_set.try_lock().unwrap(); + let waitable = set.as_ref().unwrap().as_raw(); break CallbackCode::Wait(waitable); } else { break CallbackCode::Exit; @@ -255,10 +258,12 @@ impl FutureState<'_> { // something. Poll::Pending => { assert!(!me.tasks.is_empty()); - if me.waker.sleep_state.load(Ordering::Relaxed) == SLEEP_STATE_WOKEN { + if me.shared.sleep_state.load(Ordering::Relaxed) == SLEEP_STATE_WOKEN { if me.remaining_work() { - let (event0, event1, event2) = - me.waitable_set.as_ref().unwrap().poll(); + let (event0, event1, event2) = { + let set = me.shared.waitable_set.try_lock().unwrap(); + set.as_ref().unwrap().poll() + }; if event0 != EVENT_NONE { me.deliver_waitable_event(event1, event2); continue; @@ -270,11 +275,12 @@ impl FutureState<'_> { // Transition our state to "sleeping" so wakeup // notifications know that they need to signal the // inter-task stream. - me.waker + me.shared .sleep_state .store(SLEEP_STATE_SLEEPING, Ordering::Relaxed); me.read_inter_task_stream(); - let waitable = me.waitable_set.as_ref().unwrap().as_raw(); + let set = me.shared.waitable_set.try_lock().unwrap(); + let waitable = set.as_ref().unwrap().as_raw(); break CallbackCode::Wait(waitable); } } @@ -286,7 +292,7 @@ impl FutureState<'_> { /// waitable should be present because it's part of the waitable set which /// is kept in-sync with our map. fn deliver_waitable_event(&mut self, waitable: u32, code: u32) { - self.remove_waitable(waitable); + WaitableSet::remove_waitable_from_all_sets(waitable); if self .inter_task_wakeup @@ -295,17 +301,19 @@ impl FutureState<'_> { return; } - let (ptr, callback) = self.waitables.remove(&waitable).unwrap(); + let c = { + let mut waitables = self.shared.waitables.try_lock().unwrap(); + waitables.remove(&waitable).unwrap() + }; unsafe { - callback(ptr, code); + (c.callback)(c.callback_ptr, code); } } fn with_p3_task_set(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { - // Finish our `wasip3_task` by initializing its self-referential pointer, - // and then register it for the duration of this function with - // `wasip3_task_set`. The previous value of `wasip3_task_set` will get - // restored when this function returns. + // Initialize a temporary `wasip3_task` structure on the stack and + // inform `wasip3_task_set` that we're now within that task. Note the + // RAII guard to reset the task back to its previous contents. struct ResetTask(*mut cabi::wasip3_task); impl Drop for ResetTask { fn drop(&mut self) { @@ -314,16 +322,32 @@ impl FutureState<'_> { } } } - let self_raw = self as *mut FutureState<'_>; - self.wasip3_task.ptr = self_raw.cast(); - let prev = unsafe { cabi::wasip3_task_set(&mut self.wasip3_task) }; + // The `ptr` field of `wasip3_task` is to `SharedTaskState` which is + // what's cloned/handed out/etc. + let shared_raw: *const SharedTaskState = &*self.shared; + let mut wasip3_task = cabi::wasip3_task_v2 { + v1: cabi::wasip3_task { + ptr: shared_raw.cast_mut().cast(), + version: cabi::WASIP3_TASK_V2, + waitable_register: SharedTaskState::CABI_VTABLE.waitable_register, + waitable_unregister: SharedTaskState::CABI_VTABLE.waitable_unregister, + }, + vtable: &SharedTaskState::CABI_VTABLE, + }; + + // Explicitly take a mutable borrow on the entire `wasip3_task` + // structure, and then cast its raw pointer to the "smaller" historical + // version, ensuring the final pointer has provenace over the entire + // structure. + let wasip3_task: *mut cabi::wasip3_task_v2 = &mut wasip3_task; + let prev = unsafe { cabi::wasip3_task_set(wasip3_task.cast::()) }; let _reset = ResetTask(prev); f(self) } } -impl Drop for FutureState<'_> { +impl Drop for TaskState<'_> { fn drop(&mut self) { // If there's an active read of the inter-task stream, go ahead and // cancel it, since we're about to drop the stream anyway. @@ -342,33 +366,79 @@ impl Drop for FutureState<'_> { } } -unsafe extern "C" fn waitable_register( - ptr: *mut c_void, - waitable: u32, - callback: unsafe extern "C" fn(*mut c_void, u32), - callback_ptr: *mut c_void, -) -> *mut c_void { - let ptr = ptr.cast::>(); - assert!(!ptr.is_null()); - unsafe { - (*ptr).add_waitable(waitable); - match (*ptr).waitables.insert(waitable, (callback_ptr, callback)) { - Some((prev, _)) => prev, +impl SharedTaskState { + const CABI_VTABLE: cabi::wasip3_task_vtable = cabi::wasip3_task_vtable { + waitable_register: Self::cabi_waitable_register, + waitable_unregister: Self::cabi_waitable_unregister, + drop: Self::cabi_drop, + clone: Self::cabi_clone, + }; + + /// Adds the `waitable` provided to this task's waitable set. + fn add_waitable(&self, waitable: u32) { + let mut set = self.waitable_set.try_lock().unwrap(); + set.get_or_insert_with(WaitableSet::new).join(waitable); + } + + /// Implementation of the CABI `waitable_register` function. + fn waitable_register( + &self, + waitable: u32, + callback: unsafe extern "C" fn(*mut c_void, u32), + callback_ptr: *mut c_void, + ) -> *mut c_void { + self.add_waitable(waitable); + let mut waitables = self.waitables.try_lock().unwrap(); + let c = CabiWaitable { + callback, + callback_ptr, + }; + match waitables.insert(waitable, c) { + Some(prev) => prev.callback_ptr, None => ptr::null_mut(), } } -} -unsafe extern "C" fn waitable_unregister(ptr: *mut c_void, waitable: u32) -> *mut c_void { - let ptr = ptr.cast::>(); - assert!(!ptr.is_null()); - unsafe { - (*ptr).remove_waitable(waitable); - match (*ptr).waitables.remove(&waitable) { - Some((prev, _)) => prev, + /// Implementation of the CABI `waitable_unregister` function. + fn waitable_unregister(&self, waitable: u32) -> *mut c_void { + WaitableSet::remove_waitable_from_all_sets(waitable); + let mut waitables = self.waitables.try_lock().unwrap(); + match waitables.remove(&waitable) { + Some(prev) => prev.callback_ptr, None => ptr::null_mut(), } } + + /// Helper to go from a raw `c_void` FFI pointer to a typed + /// self-representation. + unsafe fn cabi_to_self(ptr: *mut c_void) -> ManuallyDrop> { + unsafe { ManuallyDrop::new(Arc::from_raw(ptr.cast::())) } + } + + unsafe extern "C" fn cabi_waitable_register( + ptr: *mut c_void, + waitable: u32, + callback: unsafe extern "C" fn(*mut c_void, u32), + callback_ptr: *mut c_void, + ) -> *mut c_void { + let me = unsafe { Self::cabi_to_self(ptr) }; + me.waitable_register(waitable, callback, callback_ptr) + } + + unsafe extern "C" fn cabi_waitable_unregister(ptr: *mut c_void, waitable: u32) -> *mut c_void { + let me = unsafe { Self::cabi_to_self(ptr) }; + me.waitable_unregister(waitable) + } + + unsafe extern "C" fn cabi_clone(ptr: *mut c_void) -> *mut c_void { + let me = unsafe { Self::cabi_to_self(ptr) }; + Arc::into_raw(Arc::clone(&me)).cast_mut().cast() + } + + unsafe extern "C" fn cabi_drop(ptr: *mut c_void) { + let mut me = unsafe { Self::cabi_to_self(ptr) }; + unsafe { ManuallyDrop::drop(&mut me) } + } } /// Status for "this task is actively being polled" @@ -380,14 +450,7 @@ const SLEEP_STATE_WOKEN: u32 = 1; /// Wakeups on this status signal the inter-task stream. const SLEEP_STATE_SLEEPING: u32 = 2; -#[derive(Default)] -struct FutureWaker { - /// One of `SLEEP_STATE_*` indicating the current status. - sleep_state: AtomicU32, - inter_task_stream: inter_task_wakeup::WakerState, -} - -impl Wake for FutureWaker { +impl Wake for SharedTaskState { fn wake(self: Arc) { Self::wake_by_ref(&self) } @@ -483,11 +546,11 @@ impl ReturnCode { /// immediately with its result. #[doc(hidden)] pub fn start_task(task: impl Future + 'static) -> i32 { - // Allocate a new `FutureState` which will track all state necessary for + // Allocate a new `TaskState` which will track all state necessary for // our exported task. - let state = Box::into_raw(Box::new(FutureState::new(Box::pin(task)))); + let state = Box::into_raw(Box::new(TaskState::new(Box::pin(task)))); - // Store our `FutureState` into our context-local-storage slot and then + // Store our `TaskState` into our context-local-storage slot and then // pretend we got EVENT_NONE to kick off everything. // // SAFETY: we should own `context.set` as we're the root level exported @@ -505,13 +568,13 @@ pub fn start_task(task: impl Future + 'static) -> i32 { /// /// # Unsafety /// -/// This function assumes that `context_get()` returns a `FutureState`. +/// This function assumes that `context_get()` returns a `TaskState`. #[doc(hidden)] pub unsafe fn callback(event0: u32, event1: u32, event2: u32) -> u32 { // Acquire our context-local state, assert it's not-null, and then reset // the state to null while we're running to help prevent any unintended // usage. - let state = context_get().cast::>(); + let state = context_get().cast::>(); assert!(!state.is_null()); unsafe { context_set(ptr::null_mut()); @@ -540,7 +603,7 @@ pub unsafe fn callback(event0: u32, event1: u32, event2: u32) -> u32 { // TODO: refactor so `'static` bounds aren't necessary pub fn block_on(future: impl Future) -> T { let mut result = None; - let mut state = FutureState::new(Box::pin(async { + let mut state = TaskState::new(Box::pin(async { result = Some(future.await); })); let mut event = (EVENT_NONE, 0, 0); @@ -550,8 +613,14 @@ pub fn block_on(future: impl Future) -> T { drop(state); break result.unwrap(); } - CallbackCode::Yield => event = state.waitable_set.as_ref().unwrap().poll(), - CallbackCode::Wait(_) => event = state.waitable_set.as_ref().unwrap().wait(), + CallbackCode::Yield => { + let set = state.shared.waitable_set.try_lock().unwrap(); + event = set.as_ref().unwrap().poll() + } + CallbackCode::Wait(_) => { + let set = state.shared.waitable_set.try_lock().unwrap(); + event = set.as_ref().unwrap().wait() + } } } } diff --git a/crates/guest-rust/src/rt/async_support/cabi.rs b/crates/guest-rust/src/rt/async_support/cabi.rs index d696d1e55..b03f5da92 100644 --- a/crates/guest-rust/src/rt/async_support/cabi.rs +++ b/crates/guest-rust/src/rt/async_support/cabi.rs @@ -43,6 +43,29 @@ //! //! Additionally for now this file is serving as documentation of this //! interface. +//! +//! # Revisions +//! +//! This interface is intended to be evolvable over time if needed. Notably the +//! original task structure, `wasip3_task`, has a `version` field where certain +//! version levels imply the existence of certain fields. The historical +//! revisions are: +//! +//! ### V1 +//! +//! This was the original version. This is the original specification of +//! `wasip3_task_set` and `wasip3_task`. +//! +//! ### V2 +//! +//! This was added 2026-06-17 in response to #1618. This added +//! `wasip3_task_v2` and `wasip3_task_vtable`. This version enables cloning a +//! task to create a strong reference to it independent of the stack lifetime +//! that `wasip3_task_set` is required to uphold. This necessitated introducing +//! `clone` and `drop` callbacks to manage the lifetime of this reference. +//! While doing this everything was moved into a vtable structure instead of +//! inline in `wasip3_task` to make it easier to add more function pointers +//! in the future if necessary. use core::ffi::c_void; @@ -66,6 +89,7 @@ extern_wasm! { /// The first version of `wasip3_task` which implies the existence of the /// fields `ptr`, `waitable_register`, and `waitable_unregister`. pub const WASIP3_TASK_V1: u32 = 1; +pub const WASIP3_TASK_V2: u32 = 2; /// Indirect "vtable" used to connect imported functions and exported tasks. /// Executors (e.g. exported functions) define and manage this while imports @@ -80,6 +104,37 @@ pub struct wasip3_task { /// below as the first argument. pub ptr: *mut c_void, + /// See `wasip3_task_vtable::waitable_register`. + pub waitable_register: unsafe extern "C" fn( + ptr: *mut c_void, + waitable: u32, + callback: unsafe extern "C" fn(callback_ptr: *mut c_void, code: u32), + callback_ptr: *mut c_void, + ) -> *mut c_void, + + /// See `wasip3_task_vtable::waitable_unregister`. + pub waitable_unregister: unsafe extern "C" fn(ptr: *mut c_void, waitable: u32) -> *mut c_void, +} + +unsafe impl Send for wasip3_task {} +unsafe impl Sync for wasip3_task {} + +/// Representation when `wasip3_task::version` is `WASIP3_TASK_V2`. +#[repr(C)] +pub struct wasip3_task_v2 { + /// The original task structure. + pub v1: wasip3_task, + + /// An always-valid pointer to a list of function pointers, described + /// below. + pub vtable: &'static wasip3_task_vtable, +} + +/// Function pointer operations that can operate on `wasip3_task::ptr`. +/// +/// This was introduced in the "v2" ABI and is a member of `wasip3_task_v2`. +#[repr(C)] +pub struct wasip3_task_vtable { /// Register a new `waitable` for this exported task. /// /// This exported task will add `waitable` to its `waitable-set`. When it @@ -104,4 +159,18 @@ pub struct wasip3_task { /// Returns the `callback_ptr` passed to `waitable_register` if present, or /// `NULL` if it's not present. pub waitable_unregister: unsafe extern "C" fn(ptr: *mut c_void, waitable: u32) -> *mut c_void, + + /// Clones this task's pointer to create a separately owned pointer which + /// can be persisted outside the stack frame that this is being used + /// within. + /// + /// Cloned values must be dropped/deallocated with `drop` below. + pub clone: unsafe extern "C" fn(ptr: *mut c_void) -> *mut c_void, + + /// Drops and deallocates the provided pointer previously created by a + /// call to the `clone` callback above. + /// + /// This must not be called on the `ptr` value within `wasip3_task::ptr` as + /// that's not managed with this lifetime. + pub drop: unsafe extern "C" fn(ptr: *mut c_void), } diff --git a/crates/guest-rust/src/rt/async_support/inter_task_wakeup.rs b/crates/guest-rust/src/rt/async_support/inter_task_wakeup.rs index 31a84ea15..96ee57308 100644 --- a/crates/guest-rust/src/rt/async_support/inter_task_wakeup.rs +++ b/crates/guest-rust/src/rt/async_support/inter_task_wakeup.rs @@ -1,6 +1,6 @@ -use super::FutureState; +use super::TaskState; use crate::rt::async_support::try_lock::TryLock; -use crate::rt::async_support::{BLOCKED, COMPLETED}; +use crate::rt::async_support::{BLOCKED, COMPLETED, WaitableSet}; use crate::{RawStreamReader, RawStreamWriter, StreamOps, UnitStreamOps}; use core::ptr; @@ -18,7 +18,7 @@ pub struct State { stream_reading: bool, } -impl FutureState<'_> { +impl TaskState<'_> { pub(super) fn read_inter_task_stream(&mut self) { // Lazily allocate the inter-task stream now that we're actually going // to sleep. We don't know where the wakeup notification will come from @@ -27,7 +27,7 @@ impl FutureState<'_> { assert!(!self.inter_task_wakeup.stream_reading); let (writer, reader) = UnitStreamOps::new(); self.inter_task_wakeup.stream = Some(reader); - let mut waker_stream = self.waker.inter_task_stream.lock.try_lock().unwrap(); + let mut waker_stream = self.shared.inter_task_stream.lock.try_lock().unwrap(); assert!(waker_stream.is_none()); *waker_stream = Some(writer); } @@ -44,7 +44,7 @@ impl FutureState<'_> { let rc = unsafe { UnitStreamOps.start_read(handle, ptr::null_mut(), 1) }; assert_eq!(rc, BLOCKED); self.inter_task_wakeup.stream_reading = true; - self.add_waitable(handle); + self.shared.add_waitable(handle); } } @@ -61,7 +61,7 @@ impl FutureState<'_> { // synchronous `stream.cancel-read` traps if the stream is still a member // of a waitable set, so this must happen first. This matches the // unregister-then-cancel ordering in `WaitableOperation::cancel`. - self.remove_waitable(handle); + WaitableSet::remove_waitable_from_all_sets(handle); // Note that the return code here is discarded. No matter what the read // is cancelled, and whether we actually read something or whether we // cancelled doesn't matter. diff --git a/crates/guest-rust/src/rt/async_support/inter_task_wakeup_disabled.rs b/crates/guest-rust/src/rt/async_support/inter_task_wakeup_disabled.rs index 3f7462cfb..6cd1fd140 100644 --- a/crates/guest-rust/src/rt/async_support/inter_task_wakeup_disabled.rs +++ b/crates/guest-rust/src/rt/async_support/inter_task_wakeup_disabled.rs @@ -1,9 +1,9 @@ -use super::FutureState; +use super::TaskState; #[derive(Default)] pub struct State; -impl FutureState<'_> { +impl TaskState<'_> { pub(super) fn read_inter_task_stream(&mut self) { assert!( self.remaining_work(), diff --git a/crates/guest-rust/src/rt/async_support/waitable.rs b/crates/guest-rust/src/rt/async_support/waitable.rs index 4e45f911c..d0b8c97f0 100644 --- a/crates/guest-rust/src/rt/async_support/waitable.rs +++ b/crates/guest-rust/src/rt/async_support/waitable.rs @@ -19,11 +19,59 @@ use core::task::{Context, Poll, Waker}; pub struct WaitableOperation { op: S, state: WaitableOperationState, + task: Option, /// Storage for the final result of this asynchronous operation, if it's /// completed asynchronously. completion_status: CompletionStatus, } +struct CabiTask { + ptr: *mut c_void, + registered: Option, + vtable: &'static cabi::wasip3_task_vtable, +} + +impl CabiTask { + /// Creates a new task from the raw C ABI representation provided. + /// + /// # Safety + /// + /// The `task` provided must be valid and adhere to C ABI conventions. + unsafe fn new(task: *mut cabi::wasip3_task_v2) -> CabiTask { + // SAFETY: the validity of `task` is up to the caller. + unsafe { + CabiTask { + ptr: ((*task).vtable.clone)((*task).v1.ptr), + registered: None, + vtable: (*task).vtable, + } + } + } + + fn unregister(&mut self, waitable: u32) -> *mut c_void { + self.registered = None; + // SAFETY: this was created from a valid task, so this should be safe + // to invoke. + unsafe { (self.vtable.waitable_unregister)(self.ptr, waitable) } + } +} + +unsafe impl Send for CabiTask {} +unsafe impl Sync for CabiTask {} + +impl Drop for CabiTask { + fn drop(&mut self) { + if let Some(waitable) = self.registered { + self.unregister(waitable); + } + // SAFETY: this was created from a valid atask, so this should be safe + // to invoke. + unsafe { + (self.vtable.drop)(self.ptr); + } + } +} + /// Structure used to store the `u32` return code from the canonical ABI about /// an asynchronous operation. /// @@ -140,6 +188,7 @@ where WaitableOperation { op, state: WaitableOperationState::Start(state), + task: None, completion_status: CompletionStatus { code: None, waker: None, @@ -153,6 +202,7 @@ where ) -> ( &mut S, &mut WaitableOperationState, + &mut Option, Pin<&mut CompletionStatus>, ) { // SAFETY: this is the one method used to project from `Pin<&mut Self>` @@ -165,6 +215,7 @@ where ( &mut me.op, &mut me.state, + &mut me.task, Pin::new_unchecked(&mut me.completion_status), ) } @@ -175,7 +226,7 @@ where /// * Fill in `completion_status` with the result of a completion event. /// * Call `cx.waker().wake()`. pub fn register_waker(self: Pin<&mut Self>, waitable: u32, cx: &mut Context) { - let (_, _, mut completion_status) = self.pin_project(); + let (_, _, last_task, mut completion_status) = self.pin_project(); debug_assert!(completion_status.as_mut().code_mut().is_none()); *completion_status.as_mut().waker_mut() = Some(cx.waker().clone()); @@ -193,13 +244,33 @@ where assert!(!task.is_null()); assert!((*task).version >= cabi::WASIP3_TASK_V1); let ptr: *mut CompletionStatus = completion_status.get_unchecked_mut(); + + // For the v2+ ABI clone the task structure to store internally + // within this waitable operation, if we're not already storing + // this task. This ensures that `unregister_waker` below works + // correctly in cross-task situations. + // + // Note that this must happen before the `waitable_register` below + // to ensure we're fully removed from the previous task, if + // applicable, before registering with another task. + if (*task).version >= cabi::WASIP3_TASK_V2 { + let task = task.cast::(); + let last_task = match last_task { + Some(prev) if prev.ptr == (*task).v1.ptr => prev, + _ => last_task.insert(CabiTask::new(task)), + }; + last_task.registered = Some(waitable); + } + let prev = ((*task).waitable_register)((*task).ptr, waitable, cabi_wake, ptr.cast()); + // We might be inserting a waker for the first time or overwriting // the previous waker. Only assert the expected value here if the // previous value was non-null. if !prev.is_null() { assert_eq!(ptr, prev.cast()); } + cabi::wasip3_task_set(task); } @@ -216,42 +287,59 @@ where /// This relinquishes control of the original `completion_status` pointer /// passed to `register_waker` after this call has completed. pub fn unregister_waker(self: Pin<&mut Self>, waitable: u32) { - // SAFETY: the contract of `wasip3_task_set` is that the returned - // pointer is valid for the lifetime of our entire task, so it's valid - // for this stack frame. Additionally we assert it's non-null to - // double-check it's initialized and additionally check the version for - // the fields that we access. - // - // Otherwise the `waitable_unregister` callback should be safe because: - // - // * We're fulfilling the contract where the first argument must be - // `(*task).ptr` - // * We own the `waitable` that we're passing in, so we're fulfilling - // the contract that arbitrary waitables for other units of work - // aren't being manipulated. - unsafe { - let task = cabi::wasip3_task_set(ptr::null_mut()); - assert!(!task.is_null()); - assert!((*task).version >= cabi::WASIP3_TASK_V1); - let prev = ((*task).waitable_unregister)((*task).ptr, waitable); - - // Note that `_prev` here is not guaranteed to be either `NULL` or - // not. A racy completion notification may have come in and - // removed our waitable from the map even though we're in the - // `InProgress` state, meaning it may not be present. + let (_, _, task, completion) = self.pin_project(); + + let prev = match task { + // Note that in this case we leave `task` as-is to avoid re-cloning + // it in the future if we're re-registered with it, so this only + // unregisters. + Some(prev) => prev.unregister(waitable), + + // If we don't have a previous task listed then that means that we + // are registered with a "v1" task that can't be cloned out. Assume + // blindly that we're still under the same task and unregister from + // it. // - // The main thing is that after this method is called the - // internal `completion_status` is guaranteed to no longer be in - // `task`. + // SAFETY: the contract of `wasip3_task_set` is that the returned + // pointer is valid for the lifetime of our entire task, so it's + // valid for this stack frame. Additionally we assert it's non-null + // to double-check it's initialized and additionally check the + // version for the fields that we access. // - // Note, though, that if present this must be our `CompletionStatus` - // pointer. - if !prev.is_null() { - let ptr: *mut CompletionStatus = self.pin_project().2.get_unchecked_mut(); - assert_eq!(ptr, prev.cast()); - } + // Otherwise the `waitable_unregister` callback should be safe + // because: + // + // * We're fulfilling the contract where the first argument must be + // `(*task).ptr` + // * We own the `waitable` that we're passing in, so we're + // fulfilling the contract that arbitrary waitables for other + // units of work aren't being manipulated. + None => unsafe { + let task = cabi::wasip3_task_set(ptr::null_mut()); + assert!(!task.is_null()); + assert!((*task).version >= cabi::WASIP3_TASK_V1); + let prev = ((*task).waitable_unregister)((*task).ptr, waitable); + cabi::wasip3_task_set(task); + prev + }, + }; - cabi::wasip3_task_set(task); + // Note that `prev` here may be null or may be valid. A racy completion + // notification may have come in and removed our waitable from the map + // even though we're in the `InProgress` state, meaning it may not be + // present. + // + // The main thing is that after this method is called the + // internal `completion_status` is guaranteed to no longer be in + // `task`. + // + // Note, though, that if present this must be our `CompletionStatus` + // pointer. If it's not then something's been corrupted and this is + // intended to catch that early. + if !prev.is_null() { + // SAFETY: only used for a comparison, not mutated. + let ptr: *mut CompletionStatus = unsafe { completion.get_unchecked_mut() }; + assert_eq!(ptr, prev.cast()); } } @@ -261,7 +349,7 @@ where pub fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { use WaitableOperationState::*; - let (op, state, completion_status) = self.as_mut().pin_project(); + let (op, state, _task, completion_status) = self.as_mut().pin_project(); // First up, determine the completion status, if any, that's available. let optional_code = match state { @@ -305,7 +393,7 @@ where ) -> Poll { use WaitableOperationState::*; - let (op, state, _completion_status) = self.as_mut().pin_project(); + let (op, state, task, _completion_status) = self.as_mut().pin_project(); // If a status code is provided, then extract the in-progress state and // see what it thinks about this code. If we're done, yay! If not then @@ -315,6 +403,9 @@ where // If no status code is available then that means we were polled before // the status came back, so just re-register the waker. if let Some(code) = optional_code { + if let Some(task) = task { + task.registered = None; + } let InProgress(in_progress) = mem::replace(state, Done) else { unreachable!() }; @@ -354,7 +445,7 @@ where pub fn cancel(mut self: Pin<&mut Self>) -> S::Cancel { use WaitableOperationState::*; - let (op, state, mut completion_status) = self.as_mut().pin_project(); + let (op, state, _task, mut completion_status) = self.as_mut().pin_project(); let in_progress = match state { // This operation was never actually started, so there's no need to // cancel anything, just pull out the value and return it. @@ -432,7 +523,7 @@ where // this to be sound. Rust doesn't currently have linear types or async // destructors for example to ensure otherwise that if this were to // proceed asynchronously that we could rely on it being invoked. - let (op, InProgress(in_progress), _) = self.as_mut().pin_project() else { + let (op, InProgress(in_progress), _, _) = self.as_mut().pin_project() else { unreachable!() }; let code = op.in_progress_cancel(in_progress); diff --git a/tests/runtime-async/async/rust-async-and-block-on/runner.rs b/tests/runtime-async/async/rust-async-and-block-on/runner.rs new file mode 100644 index 000000000..e9347009a --- /dev/null +++ b/tests/runtime-async/async/rust-async-and-block-on/runner.rs @@ -0,0 +1,49 @@ +//@ wasmtime-flags = '-Wcomponent-model-async' + +include!(env!("BINDINGS")); + +use std::future::Future; +use std::pin::pin; +use std::task::{Context, Poll, Waker}; +use wit_bindgen::block_on; + +struct Component; + +export!(Component); + +impl Guest for Component { + async fn run() { + let (writer, reader) = wit_stream::new::(); + let reader = a::b::i::launder(reader); + let noop_cx = &mut Context::from_waker(Waker::noop()); + + let mut w1 = pin!(async { + let mut w = writer; + let _ = w.write(vec![1u8]).await; + w + }); + + // Step 1 — register &w1.completion_status in export_task.waitables[H]. + assert!(matches!(w1.as_mut().poll(noop_cx), Poll::Pending)); + + // Step 2 — block_on completes w1; export_task.waitables[H] goes stale. + // _reader must stay alive so the step-3 write blocks (Dropped skips + // register_waker and hides the bug). + let (writer, _reader) = block_on(async move { + let mut reader = reader; + let (w, _) = futures::join!(w1, reader.read(Vec::with_capacity(1))); + (w, reader) + }); + + // Step 3 — register &w2.completion_status; gets freed + // &w1.completion_status back as prev → assert_eq!(ptr, prev.cast()) + // panics at waitable.rs:201. + let mut w2 = pin!(async move { + let mut w = writer; + let pad = [0u64; 16]; + let _ = w.write(vec![2u8]).await; + let _ = pad; // explicit use after await keeps pad in the state machine + }); + let _ = w2.as_mut().poll(noop_cx); // panics + } +} diff --git a/tests/runtime-async/async/rust-async-and-block-on/test.rs b/tests/runtime-async/async/rust-async-and-block-on/test.rs new file mode 100644 index 000000000..2011d1ae0 --- /dev/null +++ b/tests/runtime-async/async/rust-async-and-block-on/test.rs @@ -0,0 +1,13 @@ +include!(env!("BINDINGS")); + +use wit_bindgen::StreamReader; + +struct Component; + +export!(Component); + +impl crate::exports::a::b::i::Guest for Component { + fn launder(x: StreamReader) -> StreamReader { + x + } +} diff --git a/tests/runtime-async/async/rust-async-and-block-on/test.wit b/tests/runtime-async/async/rust-async-and-block-on/test.wit new file mode 100644 index 000000000..3e66f4289 --- /dev/null +++ b/tests/runtime-async/async/rust-async-and-block-on/test.wit @@ -0,0 +1,15 @@ +package a:b; + +interface i { + launder: func(x: stream) -> stream; +} + +world test { + export i; +} + +world runner { + import i; + + export run: async func(); +} From f1e55577645c5e0caad85e9e31ff069272359171 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Mon, 29 Jun 2026 11:29:41 -0400 Subject: [PATCH 07/13] feat(cpp): add map type support (#1590) * feat(cpp): add map type support * fix(cpp): add operator< to wit::string for std::map key support and fix rustfmt wit::string lacked comparison operators, causing compilation failures when used as a std::map key. Also fixes rustfmt formatting issues. * fix(cpp): const_cast map keys during lowering for ownership transfer std::map keys are const, but the ABI lowering needs to call leak() on string keys to transfer ownership to the flat buffer. Use const_cast since the map is consumed during the lowering operation. * fix(cpp): copy map keys by value instead of const_cast during lowering const_cast fails when the map key type differs between contexts (e.g. std::string_view in imports vs wit::string in exports). Copying by value works universally and is safe since the map is consumed. * fix(cpp): remove map runtime test until wasi-sdk supports map types wasm-component-ld bundled with wasi-sdk 30 doesn't support the map type encoding (0x63) in the component model binary format. The C++ map codegen is still validated by the codegen test. The runtime test can be re-added when wasi-sdk ships a compatible wasm-component-ld. * refactor(cpp): use wit::map instead of std::map for map types std::map requires per-entry rb-tree allocation during lift; wit::map mirrors the canonical ABI layout (flat pair array) so lift is a single allocation, matching the existing wit::vector / wit::string pattern. Signed-off-by: Yordis Prieto * refactor(cpp): drop redundant size_t cast in MapLower wit::map::size() and std::span::size() already return size_t, so the C-style cast was a no-op. Signed-off-by: Yordis Prieto * refactor(cpp): skip guest-dealloc loop when body is empty Avoids an unused `base` local and the `(void) base;` suppression when the element has no nested heap-owned fields (e.g. list, map). Signed-off-by: Yordis Prieto * refactor(cpp): use static_cast for MapLower realloc result The cast converts void* (from cabi_realloc) to uint8_t*, which is a well-defined static_cast and doesn't need a C-style cast. Signed-off-by: Yordis Prieto * fix(cpp): zero length when moving wit::map Leaves the moved-from map in a fully consistent state so any later inspection of size()/empty() reflects the empty invariant, not just the destructor's short-circuit on a null data_ pointer. Signed-off-by: Yordis Prieto * style(cpp): break long single-line methods in wit::map Signed-off-by: Yordis Prieto * refactor(cpp): drop wit::map operator[] Index-based subscript on a map is a footgun: integral keys would silently compile while doing positional access. Iteration via range-for and pointer access via data() are sufficient for codegen and don't carry the same expectation mismatch. Signed-off-by: Yordis Prieto * refactor(cpp): drop std::span accessors from wit::map A span of pairs is a vector-shaped view that doesn't fit a map abstraction; codegen sites that need a flat pair span build it explicitly from data() and size() at the call site. Signed-off-by: Yordis Prieto * feat(cpp): introduce wit::map_view for borrowed map arguments Borrowed map arguments were surfacing as std::span const>, which carries vector-shaped affordances (positional indexing, span conversions) on what is conceptually a map. wit::map_view is a borrow- only counterpart to wit::map that mimics map semantics rather than vector semantics. Signed-off-by: Yordis Prieto * refactor(cpp): rename wit::map to wit::unordered_map Component-model map carries no ordering or hashing guarantee, and the type's API surface deliberately mirrors std::unordered_map (size, empty, begin/end) plus bindings-construction primitives, so the name should reflect the unordered semantics rather than std::map's ordered ones. Companion borrow type renamed to wit::unordered_map_view. Signed-off-by: Yordis Prieto * fix(cpp): align map iteration variable with renamed _base Adjacent block bodies (string/list/option dealloc) now reference _base after main's rename, so MapLower / MapLift / GuestDeallocateMap must declare the per-entry pointer under the same name to compile. Adds the matching (void) _base; suppression so loops still compile when the inner body doesn't reference it. Signed-off-by: Yordis Prieto * refactor(cpp): remove unordered_map_view and update map handling The `unordered_map_view` class has been removed to streamline the API, as it was a borrow-only handle that mimicked map semantics. The code now directly utilizes `std::span` for borrowed map arguments, ensuring a clearer distinction between map and vector semantics. Additionally, the handling of dependencies has been updated to include `` where necessary. Signed-off-by: Yordis Prieto --------- Signed-off-by: Yordis Prieto --- crates/cpp/helper-types/wit.h | 74 ++++++++++++++ crates/cpp/src/lib.rs | 184 ++++++++++++++++++++++++++++++---- crates/test/src/cpp.rs | 2 +- 3 files changed, 239 insertions(+), 21 deletions(-) diff --git a/crates/cpp/helper-types/wit.h b/crates/cpp/helper-types/wit.h index 8a79db32c..e643a5262 100644 --- a/crates/cpp/helper-types/wit.h +++ b/crates/cpp/helper-types/wit.h @@ -13,6 +13,7 @@ #include // free #include #include +#include // pair namespace wit { /// @brief Helper class to map between IDs and resources @@ -170,6 +171,79 @@ template class vector { } }; +/// @brief A map stored as a contiguous array of key-value pairs in linear +/// memory, freed unconditionally using free. +/// +/// Mirrors the canonical ABI representation of `map` (`list>`) +/// to enable lift without per-entry tree allocation. The container has no +/// ordering or hashing guarantees and exposes only the subset of the +/// `std::unordered_map` API that's meaningful over a flat pair buffer plus +/// the bindings-construction primitives (`allocate`, `initialize`, `leak`, +/// `drop_raw`, `data`). +template class unordered_map { +public: + using entry_type = std::pair; + +private: + entry_type *data_; + size_t length; + + static entry_type* empty_ptr() { return (entry_type*)alignof(entry_type); } + +public: + unordered_map(unordered_map const &) = delete; + unordered_map(unordered_map &&b) : data_(b.data_), length(b.length) { + b.data_ = nullptr; + b.length = 0; + } + unordered_map &operator=(unordered_map const &) = delete; + unordered_map &operator=(unordered_map &&b) { + if (data_ && length > 0) { + for (unsigned i = 0; i < length; ++i) { data_[i].~entry_type(); } + free(data_); + } + data_ = b.data_; + length = b.length; + b.data_ = nullptr; + b.length = 0; + return *this; + } + unordered_map(entry_type *d, size_t l) : data_(d), length(l) {} + unordered_map() : data_(empty_ptr()), length() {} + entry_type const *data() const { return data_; } + entry_type *data() { return data_; } + size_t size() const { return length; } + bool empty() const { return !length; } + ~unordered_map() { + if (data_ && length > 0) { + for (unsigned i = 0; i < length; ++i) { data_[i].~entry_type(); } + free((void*)data_); + } + } + // WARNING: unordered_map contains uninitialized entries; caller must + // construct them via `initialize` before the map is observed or destroyed. + static unordered_map allocate(size_t len) { + if (!len) return unordered_map(empty_ptr(), 0); + return unordered_map( + (entry_type*)malloc(sizeof(entry_type) * len), len); + } + void initialize(size_t n, entry_type&& entry) { + new ((void*)(data_ + n)) entry_type(std::move(entry)); + } + entry_type *leak() { + entry_type *result = data_; + data_ = nullptr; + return result; + } + static void drop_raw(void *ptr) { + if (ptr != empty_ptr()) free(ptr); + } + entry_type *begin() { return data_; } + entry_type *end() { return data_ + length; } + entry_type const *begin() const { return data_; } + entry_type const *end() const { return data_ + length; } +}; + /// @brief A Resource defined within the guest (guest side) /// /// It registers with the host and should remain in a static location. diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 468aa2b17..e672feb10 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -425,6 +425,9 @@ impl Cpp { if self.dependencies.needs_tuple { self.include(""); } + if self.dependencies.needs_span { + self.include(""); + } if self.dependencies.needs_wit { self.include("\"wit.h\""); } @@ -906,7 +909,7 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Stream(_) => todo!("generate for stream"), TypeDefKind::Handle(_) => todo!("generate for handle"), TypeDefKind::FixedLengthList(_, _) => todo!(), - TypeDefKind::Map(_, _) => todo!(), + TypeDefKind::Map(k, v) => self.type_map(id, name, k, v, &ty.docs), TypeDefKind::Unknown => unreachable!(), } } @@ -1733,7 +1736,30 @@ impl CppInterfaceGenerator<'_> { self.type_name(ty, from_namespace, flavor) ) } - TypeDefKind::Map(_, _) => todo!(), + TypeDefKind::Map(key, value) => { + let borrowed = match flavor { + Flavor::BorrowedArgument => true, + Flavor::Argument(var) => { + matches!(var, AbiVariant::GuestImport) + || self.r#gen.opts.api_style == APIStyle::Symmetric + } + _ => false, + }; + let element_flavor = if borrowed { + Flavor::BorrowedArgument + } else { + Flavor::InStruct + }; + let k = self.type_name(key, from_namespace, element_flavor); + let v = self.type_name(value, from_namespace, element_flavor); + if borrowed { + self.r#gen.dependencies.needs_span = true; + format!("std::span const>") + } else { + self.r#gen.dependencies.needs_wit = true; + format!("wit::unordered_map<{k}, {v}>") + } + } TypeDefKind::Unknown => todo!(), }, Type::ErrorContext => todo!(), @@ -2258,7 +2284,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> _value: &wit_bindgen_core::wit_parser::Type, _docs: &wit_bindgen_core::wit_parser::Docs, ) { - todo!("map types are not yet supported in the C++ backend") + // nothing to do here } fn type_builtin( @@ -3483,17 +3509,19 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let len = self.tempname("_len", tmp); uwriteln!(self.src, "uint8_t* {ptr} = {};", operands[0]); uwriteln!(self.src, "size_t {len} = {};", operands[1]); - let i = self.tempname("i", tmp); - uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{"); - let size = self.r#gen.sizes.size(element); - uwriteln!( - self.src, - "uint8_t* _base = {ptr} + {i} * {size};", - size = size.format(POINTER_SIZE_EXPRESSION) - ); - uwriteln!(self.src, "(void) _base;"); - uwrite!(self.src, "{body}"); - uwriteln!(self.src, "}}"); + if !body.trim().is_empty() { + let i = self.tempname("i", tmp); + uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{"); + let size = self.r#gen.sizes.size(element); + uwriteln!( + self.src, + "uint8_t* _base = {ptr} + {i} * {size};", + size = size.format(POINTER_SIZE_EXPRESSION) + ); + uwriteln!(self.src, "(void) _base;"); + uwrite!(self.src, "{body}"); + uwriteln!(self.src, "}}"); + } uwriteln!(self.src, "if ({len} > 0) {{"); uwriteln!(self.src, "free((void*) ({ptr}));"); uwriteln!(self.src, "}}"); @@ -3541,12 +3569,128 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } abi::Instruction::AsyncTaskReturn { .. } => todo!(), abi::Instruction::DropHandle { .. } => todo!(), - abi::Instruction::MapLower { .. } - | abi::Instruction::MapLift { .. } - | abi::Instruction::IterMapKey { .. } - | abi::Instruction::IterMapValue { .. } - | abi::Instruction::GuestDeallocateMap { .. } => { - todo!("map types are not yet supported in this backend") + abi::Instruction::MapLower { + key, + value, + realloc, + } => { + let tmp = self.tmp(); + let body = self.blocks.pop().unwrap(); + let val = format!("map{tmp}"); + let ptr = format!("ptr{tmp}"); + let len = format!("len{tmp}"); + let entry = self.r#gen.sizes.record([*key, *value]); + let size = entry.size.format(POINTER_SIZE_EXPRESSION); + let align = entry.align.format(POINTER_SIZE_EXPRESSION); + // The canonical ABI entry layout can differ from the C++ entry + // layout (see wit-bindgen#1592), so always allocate a fresh ABI + // buffer rather than reusing the source map's storage. + self.push_str(&format!("auto&& {val} = {};\n", operands[0])); + self.push_str(&format!("auto {len} = {val}.size();\n")); + uwriteln!( + self.src, + "auto {ptr} = static_cast<{ptr_type}>({len} > 0 ? cabi_realloc(nullptr, 0, {align}, {len} * {size}) : nullptr);", + ptr_type = self.r#gen.r#gen.opts.ptr_type() + ); + uwriteln!(self.src, "for (size_t i = 0; i < {len}; ++i) {{"); + uwriteln!(self.src, "auto _base = {ptr} + i * {size};"); + uwriteln!(self.src, "(void) _base;"); + uwriteln!(self.src, "auto&& iter_entry = {val}.data()[i];"); + uwriteln!(self.src, "auto&& iter_map_key = iter_entry.first;"); + uwriteln!(self.src, "auto&& iter_map_value = iter_entry.second;"); + uwrite!(self.src, "{}", body.0); + uwriteln!(self.src, "}}"); + if realloc.is_some() { + uwriteln!(self.src, "{}.leak();", operands[0]); + } + results.push(ptr); + results.push(len); + } + abi::Instruction::MapLift { key, value, .. } => { + let body = self.blocks.pop().unwrap(); + let tmp = self.tmp(); + let entry = self.r#gen.sizes.record([*key, *value]); + let size = entry.size.format(POINTER_SIZE_EXPRESSION); + let flavor = if self.r#gen.r#gen.opts.api_style == APIStyle::Symmetric + && matches!(self.variant, AbiVariant::GuestExport) + { + Flavor::BorrowedArgument + } else { + Flavor::InStruct + }; + let key_type = self.r#gen.type_name(key, &self.namespace, flavor); + let value_type = self.r#gen.type_name(value, &self.namespace, flavor); + let len = format!("len{tmp}"); + let base = format!("base{tmp}"); + let result = format!("result{tmp}"); + uwriteln!(self.src, "auto {base} = {};", operands[0]); + uwriteln!(self.src, "auto {len} = {};", operands[1]); + uwriteln!( + self.src, + "auto {result} = wit::unordered_map<{key_type}, {value_type}>::allocate({len});" + ); + if self.r#gen.r#gen.opts.api_style == APIStyle::Symmetric + && matches!(self.variant, AbiVariant::GuestExport) + { + assert!(self.needs_dealloc); + uwriteln!(self.src, "if ({len}>0) _deallocate.push_back({base});"); + } + uwriteln!(self.src, "for (unsigned i=0; i<{len}; ++i) {{"); + uwriteln!(self.src, "auto _base = {base} + i * {size};"); + uwriteln!(self.src, "(void) _base;"); + uwrite!(self.src, "{}", body.0); + let body_key = &body.1[0]; + let body_value = &body.1[1]; + uwriteln!( + self.src, + "{result}.initialize(i, std::make_pair({}, {}));", + move_if_necessary(body_key), + move_if_necessary(body_value) + ); + uwriteln!(self.src, "}}"); + + if self.r#gen.r#gen.opts.api_style == APIStyle::Symmetric + && matches!(self.variant, AbiVariant::GuestExport) + { + self.r#gen.r#gen.dependencies.needs_wit = true; + self.r#gen.r#gen.dependencies.needs_span = true; + results.push(format!( + "std::span const>({result}.data(), {result}.size())" + )); + self.leak_on_insertion.replace(format!( + "if ({len}>0) _deallocate.push_back((void*){result}.leak());\n" + )); + } else { + results.push(move_if_necessary(&result)); + } + } + abi::Instruction::IterMapKey { .. } => { + results.push("iter_map_key".to_string()); + } + abi::Instruction::IterMapValue { .. } => { + results.push("iter_map_value".to_string()); + } + abi::Instruction::GuestDeallocateMap { key, value } => { + let (body, results) = self.blocks.pop().unwrap(); + assert!(results.is_empty()); + let tmp = self.tmp(); + let ptr = self.tempname("_ptr", tmp); + let len = self.tempname("_len", tmp); + uwriteln!(self.src, "uint8_t* {ptr} = {};", operands[0]); + uwriteln!(self.src, "size_t {len} = {};", operands[1]); + if !body.trim().is_empty() { + let i = self.tempname("i", tmp); + uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{"); + let entry = self.r#gen.sizes.record([*key, *value]); + let size = entry.size.format(POINTER_SIZE_EXPRESSION); + uwriteln!(self.src, "uint8_t* _base = {ptr} + {i} * {size};"); + uwriteln!(self.src, "(void) _base;"); + uwrite!(self.src, "{body}"); + uwriteln!(self.src, "}}"); + } + uwriteln!(self.src, "if ({len} > 0) {{"); + uwriteln!(self.src, "free((void*) ({ptr}));"); + uwriteln!(self.src, "}}"); } } } diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 4946a1fef..bf49ad3a8 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -50,7 +50,7 @@ impl LanguageMethods for Cpp { return false; } return match name { - "issue1514-6.wit" | "named-fixed-length-list.wit" | "map.wit" => true, + "issue1514-6.wit" | "named-fixed-length-list.wit" => true, _ => false, } || config.async_; } From 1ccc7c72cf6824b1b3541eef175d5280e12a9037 Mon Sep 17 00:00:00 2001 From: EvianZhang Date: Mon, 6 Jul 2026 23:26:00 +0800 Subject: [PATCH 08/13] Fix exception safety violation in AbiBuffer::advance (#1649) * Fix exception safety violation in AbiBuffer::advance * Add more explanation --- crates/guest-rust/src/rt/async_support/abi_buffer.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/guest-rust/src/rt/async_support/abi_buffer.rs b/crates/guest-rust/src/rt/async_support/abi_buffer.rs index 9b0841999..fe97b687e 100644 --- a/crates/guest-rust/src/rt/async_support/abi_buffer.rs +++ b/crates/guest-rust/src/rt/async_support/abi_buffer.rs @@ -133,6 +133,12 @@ impl AbiBuffer { let (mut ptr, len) = self.abi_ptr_and_len(); assert!(amt <= len); for _ in 0..amt { + // Update self.cursor incrementally for exception safety. + // When `self.ops.dealloc_lists` panics (which is a user-provided + // callback), we can make sure any item before (including the + // panic one) will not be dealloced again, and the remaining items + // can still get advanced properly. + self.cursor += 1; // SAFETY: we're managing the pointer passed to `dealloc_lists` and // it was initialized with a `lower`, and then the pointer // arithmetic should all be in-bounds. @@ -141,7 +147,6 @@ impl AbiBuffer { ptr = ptr.add(self.ops.elem_layout().size()); } } - self.cursor += amt; } fn take_vec(&mut self) -> Vec { From 4642b6bcc4283361b5d85eb00b00addf618458a9 Mon Sep 17 00:00:00 2001 From: Andrew Steurer <94206073+asteurer@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:10:52 -0500 Subject: [PATCH 09/13] chore: bump go-pkg version (#1645) Signed-off-by: Andrew Steurer <94206073+asteurer@users.noreply.github.com> --- crates/go/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/go/src/lib.rs b/crates/go/src/lib.rs index 4caf53a04..d72b2ae9d 100644 --- a/crates/go/src/lib.rs +++ b/crates/go/src/lib.rs @@ -45,7 +45,7 @@ fn remote_pkg(name: &str) -> String { } /// The version of github.com/bytecodealliance/go-pkg that's being used -const REMOTE_PKG_VERSION: &str = "v0.2.1"; +const REMOTE_PKG_VERSION: &str = "v0.2.2"; /// If a user specifies the `pkg_name` flag, the required version for the /// shared remote package isn't recorded. This enables downstream users to retrieve the version programmatically. From 893636588c2b106ec7136bef6c19f984f49dc696 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 6 Jul 2026 17:26:28 -0500 Subject: [PATCH 10/13] Propagate errors in `preprocess` instead of unwrapping (#1648) Closes #1647 --- crates/c/src/lib.rs | 3 ++- crates/core/src/lib.rs | 5 +++-- crates/cpp/src/lib.rs | 3 ++- crates/csharp/src/world_generator.rs | 3 ++- crates/go/src/lib.rs | 3 ++- crates/markdown/src/lib.rs | 3 ++- crates/moonbit/src/lib.rs | 3 ++- crates/rust/src/lib.rs | 7 ++++--- 8 files changed, 19 insertions(+), 11 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index d28908057..970d34aa2 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -173,7 +173,7 @@ enum Scalar { } impl WorldGenerator for C { - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) -> Result<()> { self.world = self .opts .rename_world @@ -204,6 +204,7 @@ impl WorldGenerator for C { } } } + Ok(()) } fn import_interface( diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index b85754160..ee5a63b30 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -28,7 +28,7 @@ pub trait WorldGenerator { resolve.generate_nominal_type_ids(id); } let world = &resolve.worlds[id]; - self.preprocess(resolve, id); + self.preprocess(resolve, id)?; fn unwrap_name(key: &WorldKey) -> &str { match key { @@ -95,8 +95,9 @@ pub trait WorldGenerator { let _ = (resolve, world, files); } - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) -> Result<()> { let _ = (resolve, world); + Ok(()) } fn import_interface( diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e672feb10..52e13ccab 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -487,7 +487,7 @@ struct FileContext { } impl WorldGenerator for Cpp { - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) -> anyhow::Result<()> { let name = &resolve.worlds[world].name; self.world = name.to_string(); self.types.analyze(resolve); @@ -511,6 +511,7 @@ impl WorldGenerator for Cpp { "#, self.world.to_snake_case(), ); + Ok(()) } fn import_interface( diff --git a/crates/csharp/src/world_generator.rs b/crates/csharp/src/world_generator.rs index fb7d2450b..dcfb6b9bc 100644 --- a/crates/csharp/src/world_generator.rs +++ b/crates/csharp/src/world_generator.rs @@ -144,7 +144,7 @@ impl CSharp { } impl WorldGenerator for CSharp { - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) -> anyhow::Result<()> { let name = &resolve.worlds[world].name; self.types.analyze(resolve); self.types.collect_equal_types(resolve, world, &|a| { @@ -171,6 +171,7 @@ impl WorldGenerator for CSharp { }); self.name = name.to_string(); self.sizes.fill(resolve); + Ok(()) } fn import_interface( diff --git a/crates/go/src/lib.rs b/crates/go/src/lib.rs index d72b2ae9d..a6a99d4ba 100644 --- a/crates/go/src/lib.rs +++ b/crates/go/src/lib.rs @@ -724,10 +724,11 @@ impl WorldGenerator for Go { false } - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) -> Result<()> { _ = world; self.sizes.fill(resolve); self.imports.insert(remote_pkg("runtime")); + Ok(()) } fn import_interface( diff --git a/crates/markdown/src/lib.rs b/crates/markdown/src/lib.rs index a1db52cf2..706023c95 100644 --- a/crates/markdown/src/lib.rs +++ b/crates/markdown/src/lib.rs @@ -37,7 +37,7 @@ impl Opts { } impl WorldGenerator for Markdown { - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) -> Result<()> { self.sizes.fill(resolve); let world = &resolve.worlds[world]; @@ -109,6 +109,7 @@ impl WorldGenerator for Markdown { } } r#gen.push_str("\n"); + Ok(()) } fn import_interface( diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index 4aa29852f..7939d6146 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -244,7 +244,7 @@ impl MoonBit { /// - Inline FFI helpers and builtins are collected and written once into the /// final export FFI module. Async helpers are emitted when required. impl WorldGenerator for MoonBit { - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) -> Result<()> { self.pkg_resolver.resolve = resolve.clone(); self.project_name = self .opts @@ -256,6 +256,7 @@ impl WorldGenerator for MoonBit { })) .unwrap_or("generated".into()); self.sizes.fill(resolve); + Ok(()) } fn import_interface( diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 7241f2d09..e7f4a36ed 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -1069,7 +1069,7 @@ macro_rules! __export_{world_name}_impl {{ } impl WorldGenerator for RustWasm { - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) -> Result<()> { wit_bindgen_core::generated_preamble(&mut self.src_preamble, env!("CARGO_PKG_VERSION")); // Render some generator options to assist with debugging and/or to help @@ -1239,14 +1239,15 @@ impl WorldGenerator for RustWasm { self.with.generate_by_default = self.opts.generate_all; for (key, item) in world.imports.iter() { if let WorldItem::Interface { id, .. } = item { - self.name_interface(resolve, *id, &key, false).unwrap(); + self.name_interface(resolve, *id, &key, false)?; } } for (key, item) in world.exports.iter() { if let WorldItem::Interface { id, .. } = item { - self.name_interface(resolve, *id, &key, true).unwrap(); + self.name_interface(resolve, *id, &key, true)?; } } + Ok(()) } fn import_interface( From 2943424b605bec47acf2e229f28e28fce1e8cdea Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Jul 2026 11:32:16 -0500 Subject: [PATCH 11/13] Move all async tests to general folder (#1651) * Move all async tests to general folder WASIp3 and component-model-async have shipped and are stable, so these are no longer expected to fail. * Try to fix CI failure * Try to fix Go in CI * Fix codegen test expectation * Another round of attempting ignores * Disable broken tests I'm tired of trying to work around them, just disable them. * Tweak CI configuration --- .github/actions/install-wasi-sdk/action.yml | 4 +- .github/workflows/main.yml | 37 +++---- Cargo.lock | 4 +- crates/test/Cargo.toml | 2 +- crates/test/src/c.rs | 1 + crates/test/src/cpp.rs | 1 + crates/test/src/csharp.rs | 1 + crates/test/src/custom.rs | 1 + crates/test/src/go.rs | 98 +++++++++++++++++-- crates/test/src/lib.rs | 36 ++++++- crates/test/src/moonbit.rs | 1 + crates/test/src/rust.rs | 1 + crates/test/src/wat.rs | 1 + .../async => runtime}/cancel-import/runner.c | 0 .../async => runtime}/cancel-import/runner.rs | 0 .../async => runtime}/cancel-import/test.c | 0 .../async => runtime}/cancel-import/test.rs | 0 .../async => runtime}/cancel-import/test.wit | 1 + .../future-cancel-read/disabled}/runner.cs | 0 .../future-cancel-read/disabled}/test.cs | 0 .../future-cancel-read/runner.c | 0 .../future-cancel-read/runner.rs | 0 .../future-cancel-read/test.c | 0 .../future-cancel-read/test.mbt | 0 .../future-cancel-read/test.rs | 0 .../future-cancel-read/test.wit | 1 + .../future-cancel-write-then-read/runner.rs | 0 .../future-cancel-write-then-read/test.rs | 0 .../future-cancel-write-then-read/test.wit | 1 + .../future-cancel-write/runner.c | 0 .../future-cancel-write/runner.rs | 0 .../future-cancel-write/test.c | 0 .../future-cancel-write/test.mbt | 0 .../future-cancel-write/test.rs | 0 .../future-cancel-write/test.wit | 1 + .../future-close-after-coming-back/runner.rs | 0 .../future-close-after-coming-back/test.mbt | 0 .../future-close-after-coming-back/test.rs | 0 .../future-close-after-coming-back}/test.wit | 1 + .../future-close-then-receive-read/runner.rs | 0 .../future-close-then-receive-read/test.rs | 0 .../future-close-then-receive-read/test.wit | 1 + .../future-closes-with-error/runner.rs | 0 .../future-closes-with-error/test.rs | 0 .../future-closes-with-error/test.wit | 1 + .../runner.rs | 0 .../future-write-then-read-comes-back/test.rs | 0 .../test.wit | 1 + .../future-write-then-read-remote/runner.rs | 0 .../future-write-then-read-remote/runner2.rs | 0 .../future-write-then-read-remote/test.rs | 0 .../future-write-then-read-remote/test.wit | 1 + .../incomplete-writes/leaf.go | 0 .../incomplete-writes/runner.go | 0 .../incomplete-writes/test.go | 0 .../incomplete-writes/test.wit | 1 + .../async => runtime}/pending-import/runner.c | 0 .../pending-import/runner.cs | 0 .../pending-import/runner.rs | 0 .../async => runtime}/pending-import/test.c | 0 .../async => runtime}/pending-import/test.cs | 0 .../async => runtime}/pending-import/test.rs | 0 .../async => runtime}/pending-import/test.wit | 1 + .../ping-pong/disabled}/runner.cs | 0 .../ping-pong/disabled}/test.cs | 0 .../async => runtime}/ping-pong/runner.c | 0 .../async => runtime}/ping-pong/runner.go | 0 .../async => runtime}/ping-pong/runner.rs | 0 .../async => runtime}/ping-pong/test.c | 0 .../async => runtime}/ping-pong/test.go | 0 .../async => runtime}/ping-pong/test.rs | 0 .../async => runtime}/ping-pong/test.wit | 1 + .../async => runtime}/return-string/runner.go | 0 .../async => runtime}/return-string/test.go | 0 .../async => runtime}/return-string/test.wit | 1 + .../rust-async-and-block-on/runner.rs | 0 .../rust-async-and-block-on/test.rs | 0 .../rust-async-and-block-on/test.wit | 1 + .../rust-cross-task-wakeup/runner.rs | 0 .../rust-cross-task-wakeup/test.rs | 0 .../rust-cross-task-wakeup/test.wit | 1 + .../rust-lowered-send/runner.rs | 0 .../rust-lowered-send/test.rs | 0 .../rust-lowered-send/test.wit | 1 + .../simple-call-import/runner.c | 0 .../simple-call-import/runner.go | 0 .../simple-call-import/runner.rs | 0 .../simple-call-import/test.c | 0 .../simple-call-import/test.go | 0 .../simple-call-import/test.rs | 0 .../simple-call-import}/test.wit | 1 + .../simple-future/runner-nostd.rs | 0 .../async => runtime}/simple-future/runner.c | 0 .../async => runtime}/simple-future/runner.cs | 0 .../async => runtime}/simple-future/runner.go | 0 .../async => runtime}/simple-future/runner.rs | 0 .../async => runtime}/simple-future/test.c | 0 .../async => runtime}/simple-future/test.cs | 0 .../async => runtime}/simple-future/test.go | 0 .../async => runtime}/simple-future/test.mbt | 0 .../async => runtime}/simple-future/test.rs | 0 .../async => runtime}/simple-future/test.wit | 1 + .../simple-import-params-results/runner.c | 0 .../simple-import-params-results/runner.cs | 0 .../simple-import-params-results/runner.go | 0 .../simple-import-params-results/runner.rs | 0 .../simple-import-params-results/test.c | 0 .../simple-import-params-results/test.cs | 0 .../simple-import-params-results/test.go | 0 .../simple-import-params-results/test.mbt | 0 .../simple-import-params-results/test.rs | 0 .../simple-import-params-results/test.wit | 1 + .../simple-pending-import/runner.c | 0 .../simple-pending-import/runner.go | 0 .../simple-pending-import/runner.rs | 0 .../simple-pending-import/test.c | 0 .../simple-pending-import/test.go | 0 .../simple-pending-import/test.rs | 0 .../simple-pending-import/test.wit | 1 + .../simple-stream-payload/runner.c | 0 .../simple-stream-payload/runner.cs | 0 .../simple-stream-payload/runner.go | 0 .../simple-stream-payload/runner.rs | 0 .../simple-stream-payload/test.c | 0 .../simple-stream-payload/test.cs | 0 .../simple-stream-payload/test.go | 0 .../simple-stream-payload/test.mbt | 0 .../simple-stream-payload/test.rs | 0 .../simple-stream-payload/test.wit | 1 + .../simple-stream/runner-nostd.rs | 0 .../async => runtime}/simple-stream/runner.c | 0 .../async => runtime}/simple-stream/runner.go | 0 .../async => runtime}/simple-stream/runner.rs | 0 .../async => runtime}/simple-stream/test.c | 0 .../async => runtime}/simple-stream/test.go | 0 .../async => runtime}/simple-stream/test.mbt | 0 .../async => runtime}/simple-stream/test.wit | 1 + .../simple-yield/runner-nostd.rs | 0 .../async => runtime}/simple-yield/runner.c | 0 .../async => runtime}/simple-yield/runner.rs | 0 .../async => runtime}/simple-yield/test.c | 0 .../async => runtime}/simple-yield/test.rs | 0 .../simple-yield}/test.wit | 1 + .../stream-to-futures-stream/runner.rs | 0 .../stream-to-futures-stream/test.rs | 0 .../stream-to-futures-stream/test.wit | 1 + .../runtime/strings/{ => disabled}/runner.go | 0 tests/runtime/strings/{ => disabled}/test.go | 0 .../yield-loop-receives-events/leaf.rs | 0 .../yield-loop-receives-events/middle.rs | 0 .../yield-loop-receives-events/runner.rs | 0 .../yield-loop-receives-events/test.wit | 1 + .../threading-builtins/runner.c | 0 .../threading-builtins/test.c | 0 .../threading-builtins}/test.wit | 0 155 files changed, 175 insertions(+), 38 deletions(-) rename tests/{runtime-async/async => runtime}/cancel-import/runner.c (100%) rename tests/{runtime-async/async => runtime}/cancel-import/runner.rs (100%) rename tests/{runtime-async/async => runtime}/cancel-import/test.c (100%) rename tests/{runtime-async/async => runtime}/cancel-import/test.rs (100%) rename tests/{runtime-async/async => runtime}/cancel-import/test.wit (92%) rename tests/{runtime-async/async/future-cancel-read => runtime/future-cancel-read/disabled}/runner.cs (100%) rename tests/{runtime-async/async/future-cancel-read => runtime/future-cancel-read/disabled}/test.cs (100%) rename tests/{runtime-async/async => runtime}/future-cancel-read/runner.c (100%) rename tests/{runtime-async/async => runtime}/future-cancel-read/runner.rs (100%) rename tests/{runtime-async/async => runtime}/future-cancel-read/test.c (100%) rename tests/{runtime-async/async => runtime}/future-cancel-read/test.mbt (100%) rename tests/{runtime-async/async => runtime}/future-cancel-read/test.rs (100%) rename tests/{runtime-async/async => runtime}/future-cancel-read/test.wit (94%) rename tests/{runtime-async/async => runtime}/future-cancel-write-then-read/runner.rs (100%) rename tests/{runtime-async/async => runtime}/future-cancel-write-then-read/test.rs (100%) rename tests/{runtime-async/async => runtime}/future-cancel-write-then-read/test.wit (91%) rename tests/{runtime-async/async => runtime}/future-cancel-write/runner.c (100%) rename tests/{runtime-async/async => runtime}/future-cancel-write/runner.rs (100%) rename tests/{runtime-async/async => runtime}/future-cancel-write/test.c (100%) rename tests/{runtime-async/async => runtime}/future-cancel-write/test.mbt (100%) rename tests/{runtime-async/async => runtime}/future-cancel-write/test.rs (100%) rename tests/{runtime-async/async => runtime}/future-cancel-write/test.wit (92%) rename tests/{runtime-async/async => runtime}/future-close-after-coming-back/runner.rs (100%) rename tests/{runtime-async/async => runtime}/future-close-after-coming-back/test.mbt (100%) rename tests/{runtime-async/async => runtime}/future-close-after-coming-back/test.rs (100%) rename tests/{runtime-async/async/future-write-then-read-comes-back => runtime/future-close-after-coming-back}/test.wit (91%) rename tests/{runtime-async/async => runtime}/future-close-then-receive-read/runner.rs (100%) rename tests/{runtime-async/async => runtime}/future-close-then-receive-read/test.rs (100%) rename tests/{runtime-async/async => runtime}/future-close-then-receive-read/test.wit (91%) rename tests/{runtime-async/async => runtime}/future-closes-with-error/runner.rs (100%) rename tests/{runtime-async/async => runtime}/future-closes-with-error/test.rs (100%) rename tests/{runtime-async/async => runtime}/future-closes-with-error/test.wit (90%) rename tests/{runtime-async/async => runtime}/future-write-then-read-comes-back/runner.rs (100%) rename tests/{runtime-async/async => runtime}/future-write-then-read-comes-back/test.rs (100%) rename tests/{runtime-async/async/future-close-after-coming-back => runtime/future-write-then-read-comes-back}/test.wit (91%) rename tests/{runtime-async/async => runtime}/future-write-then-read-remote/runner.rs (100%) rename tests/{runtime-async/async => runtime}/future-write-then-read-remote/runner2.rs (100%) rename tests/{runtime-async/async => runtime}/future-write-then-read-remote/test.rs (100%) rename tests/{runtime-async/async => runtime}/future-write-then-read-remote/test.wit (90%) rename tests/{runtime-async/async => runtime}/incomplete-writes/leaf.go (100%) rename tests/{runtime-async/async => runtime}/incomplete-writes/runner.go (100%) rename tests/{runtime-async/async => runtime}/incomplete-writes/test.go (100%) rename tests/{runtime-async/async => runtime}/incomplete-writes/test.wit (98%) rename tests/{runtime-async/async => runtime}/pending-import/runner.c (100%) rename tests/{runtime-async/async => runtime}/pending-import/runner.cs (100%) rename tests/{runtime-async/async => runtime}/pending-import/runner.rs (100%) rename tests/{runtime-async/async => runtime}/pending-import/test.c (100%) rename tests/{runtime-async/async => runtime}/pending-import/test.cs (100%) rename tests/{runtime-async/async => runtime}/pending-import/test.rs (100%) rename tests/{runtime-async/async => runtime}/pending-import/test.wit (90%) rename tests/{runtime-async/async/ping-pong => runtime/ping-pong/disabled}/runner.cs (100%) rename tests/{runtime-async/async/ping-pong => runtime/ping-pong/disabled}/test.cs (100%) rename tests/{runtime-async/async => runtime}/ping-pong/runner.c (100%) rename tests/{runtime-async/async => runtime}/ping-pong/runner.go (100%) rename tests/{runtime-async/async => runtime}/ping-pong/runner.rs (100%) rename tests/{runtime-async/async => runtime}/ping-pong/test.c (100%) rename tests/{runtime-async/async => runtime}/ping-pong/test.go (100%) rename tests/{runtime-async/async => runtime}/ping-pong/test.rs (100%) rename tests/{runtime-async/async => runtime}/ping-pong/test.wit (93%) rename tests/{runtime-async/async => runtime}/return-string/runner.go (100%) rename tests/{runtime-async/async => runtime}/return-string/test.go (100%) rename tests/{runtime-async/async => runtime}/return-string/test.wit (90%) rename tests/{runtime-async/async => runtime}/rust-async-and-block-on/runner.rs (100%) rename tests/{runtime-async/async => runtime}/rust-async-and-block-on/test.rs (100%) rename tests/{runtime-async/async => runtime}/rust-async-and-block-on/test.wit (90%) rename tests/{runtime-async/async => runtime}/rust-cross-task-wakeup/runner.rs (100%) rename tests/{runtime-async/async => runtime}/rust-cross-task-wakeup/test.rs (100%) rename tests/{runtime-async/async => runtime}/rust-cross-task-wakeup/test.wit (91%) rename tests/{runtime-async/async => runtime}/rust-lowered-send/runner.rs (100%) rename tests/{runtime-async/async => runtime}/rust-lowered-send/test.rs (100%) rename tests/{runtime-async/async => runtime}/rust-lowered-send/test.wit (90%) rename tests/{runtime-async/async => runtime}/simple-call-import/runner.c (100%) rename tests/{runtime-async/async => runtime}/simple-call-import/runner.go (100%) rename tests/{runtime-async/async => runtime}/simple-call-import/runner.rs (100%) rename tests/{runtime-async/async => runtime}/simple-call-import/test.c (100%) rename tests/{runtime-async/async => runtime}/simple-call-import/test.go (100%) rename tests/{runtime-async/async => runtime}/simple-call-import/test.rs (100%) rename tests/{runtime-async/async/simple-yield => runtime/simple-call-import}/test.wit (88%) rename tests/{runtime-async/async => runtime}/simple-future/runner-nostd.rs (100%) rename tests/{runtime-async/async => runtime}/simple-future/runner.c (100%) rename tests/{runtime-async/async => runtime}/simple-future/runner.cs (100%) rename tests/{runtime-async/async => runtime}/simple-future/runner.go (100%) rename tests/{runtime-async/async => runtime}/simple-future/runner.rs (100%) rename tests/{runtime-async/async => runtime}/simple-future/test.c (100%) rename tests/{runtime-async/async => runtime}/simple-future/test.cs (100%) rename tests/{runtime-async/async => runtime}/simple-future/test.go (100%) rename tests/{runtime-async/async => runtime}/simple-future/test.mbt (100%) rename tests/{runtime-async/async => runtime}/simple-future/test.rs (100%) rename tests/{runtime-async/async => runtime}/simple-future/test.wit (92%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/runner.c (100%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/runner.cs (100%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/runner.go (100%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/runner.rs (100%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/test.c (100%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/test.cs (100%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/test.go (100%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/test.mbt (100%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/test.rs (100%) rename tests/{runtime-async/async => runtime}/simple-import-params-results/test.wit (95%) rename tests/{runtime-async/async => runtime}/simple-pending-import/runner.c (100%) rename tests/{runtime-async/async => runtime}/simple-pending-import/runner.go (100%) rename tests/{runtime-async/async => runtime}/simple-pending-import/runner.rs (100%) rename tests/{runtime-async/async => runtime}/simple-pending-import/test.c (100%) rename tests/{runtime-async/async => runtime}/simple-pending-import/test.go (100%) rename tests/{runtime-async/async => runtime}/simple-pending-import/test.rs (100%) rename tests/{runtime-async/async => runtime}/simple-pending-import/test.wit (88%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/runner.c (100%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/runner.cs (100%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/runner.go (100%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/runner.rs (100%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/test.c (100%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/test.cs (100%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/test.go (100%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/test.mbt (100%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/test.rs (100%) rename tests/{runtime-async/async => runtime}/simple-stream-payload/test.wit (90%) rename tests/{runtime-async/async => runtime}/simple-stream/runner-nostd.rs (100%) rename tests/{runtime-async/async => runtime}/simple-stream/runner.c (100%) rename tests/{runtime-async/async => runtime}/simple-stream/runner.go (100%) rename tests/{runtime-async/async => runtime}/simple-stream/runner.rs (100%) rename tests/{runtime-async/async => runtime}/simple-stream/test.c (100%) rename tests/{runtime-async/async => runtime}/simple-stream/test.go (100%) rename tests/{runtime-async/async => runtime}/simple-stream/test.mbt (100%) rename tests/{runtime-async/async => runtime}/simple-stream/test.wit (90%) rename tests/{runtime-async/async => runtime}/simple-yield/runner-nostd.rs (100%) rename tests/{runtime-async/async => runtime}/simple-yield/runner.c (100%) rename tests/{runtime-async/async => runtime}/simple-yield/runner.rs (100%) rename tests/{runtime-async/async => runtime}/simple-yield/test.c (100%) rename tests/{runtime-async/async => runtime}/simple-yield/test.rs (100%) rename tests/{runtime-async/async/threading-builtins => runtime/simple-yield}/test.wit (88%) rename tests/{runtime-async/async => runtime}/stream-to-futures-stream/runner.rs (100%) rename tests/{runtime-async/async => runtime}/stream-to-futures-stream/test.rs (100%) rename tests/{runtime-async/async => runtime}/stream-to-futures-stream/test.wit (90%) rename tests/runtime/strings/{ => disabled}/runner.go (100%) rename tests/runtime/strings/{ => disabled}/test.go (100%) rename tests/{runtime-async/async => runtime}/yield-loop-receives-events/leaf.rs (100%) rename tests/{runtime-async/async => runtime}/yield-loop-receives-events/middle.rs (100%) rename tests/{runtime-async/async => runtime}/yield-loop-receives-events/runner.rs (100%) rename tests/{runtime-async/async => runtime}/yield-loop-receives-events/test.wit (94%) rename tests/{runtime-async/async => threading}/threading-builtins/runner.c (100%) rename tests/{runtime-async/async => threading}/threading-builtins/test.c (100%) rename tests/{runtime-async/async/simple-call-import => threading/threading-builtins}/test.wit (100%) diff --git a/.github/actions/install-wasi-sdk/action.yml b/.github/actions/install-wasi-sdk/action.yml index 169f68c93..5b0798a59 100644 --- a/.github/actions/install-wasi-sdk/action.yml +++ b/.github/actions/install-wasi-sdk/action.yml @@ -34,8 +34,8 @@ runs: - name: Setup `wasm-tools` uses: bytecodealliance/actions/wasm-tools/setup@v1 with: - version: "1.247.0" + version: "1.252.0" - name: Setup `wasmtime` uses: bytecodealliance/actions/wasmtime/setup@v1 with: - version: "44.0.0" + version: "46.0.1" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c22eee340..3a86dcb49 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -104,11 +104,22 @@ jobs: dotnet-version: '9.x' if: matrix.lang == 'csharp' + # As of this writing async tests require [a patched build of + # Go](https://github.com/dicej/go/releases/tag/go1.25.5-wasi-on-idle). + # Install this on Linux to get coverage, but don't install it on + # macOS/Windows to also get coverage for test-without-a-patched-toolchain. + - name: Install Patched Go + run: | + curl -OL https://github.com/dicej/go/releases/download/go1.25.5-wasi-on-idle/go-linux-amd64-bootstrap.tbz + tar xf go-linux-amd64-bootstrap.tbz + echo "$(pwd)/go-linux-amd64-bootstrap/bin" >> $GITHUB_PATH + if: matrix.lang == 'go' && matrix.os == 'ubuntu-latest' + - name: Setup Go uses: actions/setup-go@v5 with: go-version: 1.25.4 - if: matrix.lang == 'go' + if: matrix.lang == 'go' && matrix.os != 'ubuntu-latest' # Hacky work-around for https://github.com/dotnet/runtime/issues/80619 - run: dotnet new console -o /tmp/foo @@ -130,17 +141,17 @@ jobs: --artifacts target/artifacts \ --rust-wit-bindgen-path ./crates/guest-rust - # Run all runtime tests for this language, and also enable Rust in case this - # language only implements either the runner or test component + # Run all runtime tests for this language, and also enable Rust & C in case + # this language only implements either the runner or test component - run: | - cargo run test --languages rust,${{ matrix.lang }} tests/runtime \ + cargo run test --languages rust,c,${{ matrix.lang }} tests/runtime \ --artifacts target/artifacts \ --rust-wit-bindgen-path ./crates/guest-rust - # While async is off-by-default and toolchains are percolating this is a + # While threading is off-by-default and toolchains are percolating this is a # separate job to get configured slightly differently. - async: - name: Test Async (allowed to fail) + threading: + name: Test WASIp3 Threading (allowed to fail) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -151,18 +162,9 @@ jobs: run: rustup update stable --no-self-update && rustup default stable - run: rustup target add wasm32-wasip2 - # As of this writing, we need [a patched build of - # Go](https://github.com/dicej/go/releases/tag/go1.25.5-wasi-on-idle) to - # support async. - - name: Install Patched Go - run: | - curl -OL https://github.com/dicej/go/releases/download/go1.25.5-wasi-on-idle/go-linux-amd64-bootstrap.tbz - tar xf go-linux-amd64-bootstrap.tbz - echo "$(pwd)/go-linux-amd64-bootstrap/bin" >> $GITHUB_PATH - - uses: ./.github/actions/install-wasi-sdk - run: | - cargo run test --languages rust,c,go tests/runtime-async \ + cargo run test --languages rust,c,go tests/threading \ --artifacts target/artifacts \ --rust-wit-bindgen-path ./crates/guest-rust @@ -287,7 +289,6 @@ jobs: - verify-publish - check - msrv - # - async if: always() steps: diff --git a/Cargo.lock b/Cargo.lock index 2e6530677..63a642d15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1215,9 +1215,9 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter-provider" -version = "45.0.1" +version = "46.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02ed52111afc004605074d1a3fad77ba3ac85c0c0b8ad028919275a46fec8cfd" +checksum = "1b6c48003fe59c201c97a7786ff55feabe6b6f83b598aa9ff5bcc4f94d940bf3" [[package]] name = "wasm-compose" diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml index 4100f71a5..14c80e0d9 100644 --- a/crates/test/Cargo.toml +++ b/crates/test/Cargo.toml @@ -26,7 +26,7 @@ log = "0.4.26" regex = "1.11.1" serde = { workspace = true } toml = "1.1.2" -wasi-preview1-component-adapter-provider = "45.0.1" +wasi-preview1-component-adapter-provider = "46.0.1" wac-parser = "0.10.0" wac-types = "0.10.0" wac-graph = "0.10.0" diff --git a/crates/test/src/c.rs b/crates/test/src/c.rs index a34f2fc55..a04dbf967 100644 --- a/crates/test/src/c.rs +++ b/crates/test/src/c.rs @@ -52,6 +52,7 @@ impl LanguageMethods for C { fn should_fail_verify( &self, + _runner: &Runner, name: &str, config: &crate::config::WitConfig, _args: &[String], diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index bf49ad3a8..d33cefa11 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -41,6 +41,7 @@ impl LanguageMethods for Cpp { fn should_fail_verify( &self, + _runner: &Runner, name: &str, config: &crate::config::WitConfig, _args: &[String], diff --git a/crates/test/src/csharp.rs b/crates/test/src/csharp.rs index 8e11ca7f7..6327ca029 100644 --- a/crates/test/src/csharp.rs +++ b/crates/test/src/csharp.rs @@ -36,6 +36,7 @@ impl LanguageMethods for Csharp { fn should_fail_verify( &self, + _runner: &Runner, name: &str, _config: &crate::config::WitConfig, _args: &[String], diff --git a/crates/test/src/custom.rs b/crates/test/src/custom.rs index 402fe65f4..8a56032d9 100644 --- a/crates/test/src/custom.rs +++ b/crates/test/src/custom.rs @@ -101,6 +101,7 @@ impl LanguageMethods for Language { fn should_fail_verify( &self, + _runner: &Runner, _name: &str, _config: &crate::config::WitConfig, _args: &[String], diff --git a/crates/test/src/go.rs b/crates/test/src/go.rs index afe651951..7655b26df 100644 --- a/crates/test/src/go.rs +++ b/crates/test/src/go.rs @@ -7,6 +7,22 @@ use std::process::Command; pub struct Go; +/// Go-specific state, stored in `Runner`, detected during `prepare`. +pub struct State { + /// Whether the Go toolchain in use supports the `runtime.wasiOnIdle` hook + /// required for component model async support. + async_supported: bool, +} + +impl Runner { + fn go_async_supported(&self) -> bool { + self.go_state + .as_ref() + .map(|s| s.async_supported) + .unwrap_or(false) + } +} + impl LanguageMethods for Go { fn display(&self) -> &str { "go" @@ -18,19 +34,43 @@ impl LanguageMethods for Go { fn should_fail_verify( &self, + runner: &Runner, name: &str, config: &crate::config::WitConfig, _args: &[String], ) -> bool { - // TODO: We _do_ support async, but only with a build of Go that has - // [this - // patch](https://github.com/dicej/go/commit/40fc123d5bce6448fc4e4601fd33bad4250b36a5). - // Once we upstream something equivalent, we can remove the ` || name == - // "async-trait-function.wit"` here. - config.error_context - || name == "async-trait-function.wit" - || name == "named-fixed-length-list.wit" - || name == "issue-1598.wit" + if config.error_context { + return true; + } + if name == "named-fixed-length-list.wit" { + return true; + } + if !runner.go_async_supported() { + return name == "async-trait-function.wit" || name == "issue-1598.wit"; + } + + false + } + + fn should_fail_compile( + &self, + runner: &Runner, + path: &Path, + config: &crate::config::WitConfig, + ) -> bool { + // This test, even though it's part of async, compiles on any + // toolchain. + if path.ends_with("incomplete-writes/leaf.go") { + return false; + } + + // Bindings for async tests rely on `runtime.wasiOnIdle` (see `prepare` + // below) and fail to link without a toolchain that provides it. + if !runner.go_async_supported() { + return config.async_; + } + + false } fn default_bindgen_args_for_codegen(&self) -> &[&str] { @@ -56,7 +96,45 @@ impl LanguageMethods for Go { .arg("build") .arg("-buildmode=c-shared") .arg("-ldflags=-checklinkname=0"), - ) + )?; + + // Component model async support requires a `runtime.wasiOnIdle` hook + // which, as of the time of this writing, is only available in a + // patched build of Go. Detect whether the toolchain in use has + // this hook by building a program that links against it, and if not + // then async tests are expected to fail to build. + println!("Testing if `go` supports `runtime.wasiOnIdle`..."); + let probe_dir = dir.join("wasi-on-idle-probe"); + super::write_if_different( + &probe_dir.join("main.go"), + r#"package main + +import _ "unsafe" + +//go:linkname wasiOnIdle runtime.wasiOnIdle +func wasiOnIdle(callback func() bool) +func init() { defer wasiOnIdle(func() bool { return false }) } +func main() {} +"#, + )?; + super::write_if_different(&probe_dir.join("go.mod"), "module probe\n\ngo 1.25")?; + let async_supported = runner + .run_command( + Command::new("go") + .current_dir(&probe_dir) + .env("GOOS", "wasip1") + .env("GOARCH", "wasm") + .arg("build") + .arg("-o") + .arg("probe.wasm") + .arg("-buildmode=c-shared") + .arg("-ldflags=-checklinkname=0"), + ) + .is_ok(); + println!("`runtime.wasiOnIdle` supported: {async_supported}"); + runner.go_state = Some(State { async_supported }); + + Ok(()) } fn compile(&self, runner: &Runner, compile: &Compile<'_>) -> Result<()> { diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 615fb9ce0..7a54f4612 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -137,6 +137,7 @@ impl Opts { Runner { opts: self.clone(), rust_state: None, + go_state: None, wit_bindgen: wit_bindgen.to_path_buf(), test_runner: runner::TestRunner::new(&self.runner)?, } @@ -254,6 +255,7 @@ struct Verify<'a> { struct Runner { opts: Opts, rust_state: Option, + go_state: Option, wit_bindgen: PathBuf, test_runner: runner::TestRunner, } @@ -578,7 +580,7 @@ impl Runner { let me = self.clone(); let should_fail = language .obj() - .should_fail_verify(&args_kind, &config, &args); + .should_fail_verify(self, &args_kind, &config, &args); let name = format!("{language} {args_kind} {test:?}"); Trial::test(&name, move || { @@ -703,17 +705,23 @@ impl Runner { let compilations = compilations.clone(); let test = test.clone(); let component = component.clone(); + let should_fail = component.language.obj().should_fail_compile( + self, + &component.path, + &component.bindgen.wit_config, + ); Trial::test(&component.path.display().to_string(), move || { let result = me.compile_component(&test, &component).with_context(|| { format!("failed to compile component {:?}", component.path) }); match result { - Ok(path) => { + Ok(path) if !should_fail => { compilations.lock().unwrap().push((test, component, path)); Ok(()) } - Err(e) => me.render_error( - StepResult::new(Err(e)) + other => me.render_error( + StepResult::new(other.map(|_| ())) + .should_fail(should_fail) .metadata("component", &component.name) .metadata("path", component.path.display()), ), @@ -1280,7 +1288,25 @@ trait LanguageMethods { /// Returns whether this language is supposed to fail this codegen tests /// given the `config` and `args` for the test. - fn should_fail_verify(&self, name: &str, config: &config::WitConfig, args: &[String]) -> bool; + fn should_fail_verify( + &self, + runner: &Runner, + name: &str, + config: &config::WitConfig, + args: &[String], + ) -> bool; + + /// Returns whether this language is expected to fail to compile the + /// runtime test component described by `config`. + fn should_fail_compile( + &self, + runner: &Runner, + path: &Path, + config: &config::WitConfig, + ) -> bool { + let _ = (runner, path, config); + false + } /// Performs a "check" or a verify that the generated bindings described by /// `Verify` are indeed valid. diff --git a/crates/test/src/moonbit.rs b/crates/test/src/moonbit.rs index a837ae93e..8811314f5 100644 --- a/crates/test/src/moonbit.rs +++ b/crates/test/src/moonbit.rs @@ -120,6 +120,7 @@ impl LanguageMethods for MoonBit { fn should_fail_verify( &self, + _runner: &Runner, name: &str, config: &crate::config::WitConfig, _args: &[String], diff --git a/crates/test/src/rust.rs b/crates/test/src/rust.rs index fe492d60e..53484c29c 100644 --- a/crates/test/src/rust.rs +++ b/crates/test/src/rust.rs @@ -56,6 +56,7 @@ impl LanguageMethods for Rust { fn should_fail_verify( &self, + _runner: &Runner, name: &str, _config: &crate::config::WitConfig, _args: &[String], diff --git a/crates/test/src/wat.rs b/crates/test/src/wat.rs index 1cd59cdce..8278ade8e 100644 --- a/crates/test/src/wat.rs +++ b/crates/test/src/wat.rs @@ -14,6 +14,7 @@ impl LanguageMethods for Wat { fn should_fail_verify( &self, + _runner: &Runner, _name: &str, _config: &crate::config::WitConfig, _args: &[String], diff --git a/tests/runtime-async/async/cancel-import/runner.c b/tests/runtime/cancel-import/runner.c similarity index 100% rename from tests/runtime-async/async/cancel-import/runner.c rename to tests/runtime/cancel-import/runner.c diff --git a/tests/runtime-async/async/cancel-import/runner.rs b/tests/runtime/cancel-import/runner.rs similarity index 100% rename from tests/runtime-async/async/cancel-import/runner.rs rename to tests/runtime/cancel-import/runner.rs diff --git a/tests/runtime-async/async/cancel-import/test.c b/tests/runtime/cancel-import/test.c similarity index 100% rename from tests/runtime-async/async/cancel-import/test.c rename to tests/runtime/cancel-import/test.c diff --git a/tests/runtime-async/async/cancel-import/test.rs b/tests/runtime/cancel-import/test.rs similarity index 100% rename from tests/runtime-async/async/cancel-import/test.rs rename to tests/runtime/cancel-import/test.rs diff --git a/tests/runtime-async/async/cancel-import/test.wit b/tests/runtime/cancel-import/test.wit similarity index 92% rename from tests/runtime-async/async/cancel-import/test.wit rename to tests/runtime/cancel-import/test.wit index 78e57ec13..66a8c365e 100644 --- a/tests/runtime-async/async/cancel-import/test.wit +++ b/tests/runtime/cancel-import/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/future-cancel-read/runner.cs b/tests/runtime/future-cancel-read/disabled/runner.cs similarity index 100% rename from tests/runtime-async/async/future-cancel-read/runner.cs rename to tests/runtime/future-cancel-read/disabled/runner.cs diff --git a/tests/runtime-async/async/future-cancel-read/test.cs b/tests/runtime/future-cancel-read/disabled/test.cs similarity index 100% rename from tests/runtime-async/async/future-cancel-read/test.cs rename to tests/runtime/future-cancel-read/disabled/test.cs diff --git a/tests/runtime-async/async/future-cancel-read/runner.c b/tests/runtime/future-cancel-read/runner.c similarity index 100% rename from tests/runtime-async/async/future-cancel-read/runner.c rename to tests/runtime/future-cancel-read/runner.c diff --git a/tests/runtime-async/async/future-cancel-read/runner.rs b/tests/runtime/future-cancel-read/runner.rs similarity index 100% rename from tests/runtime-async/async/future-cancel-read/runner.rs rename to tests/runtime/future-cancel-read/runner.rs diff --git a/tests/runtime-async/async/future-cancel-read/test.c b/tests/runtime/future-cancel-read/test.c similarity index 100% rename from tests/runtime-async/async/future-cancel-read/test.c rename to tests/runtime/future-cancel-read/test.c diff --git a/tests/runtime-async/async/future-cancel-read/test.mbt b/tests/runtime/future-cancel-read/test.mbt similarity index 100% rename from tests/runtime-async/async/future-cancel-read/test.mbt rename to tests/runtime/future-cancel-read/test.mbt diff --git a/tests/runtime-async/async/future-cancel-read/test.rs b/tests/runtime/future-cancel-read/test.rs similarity index 100% rename from tests/runtime-async/async/future-cancel-read/test.rs rename to tests/runtime/future-cancel-read/test.rs diff --git a/tests/runtime-async/async/future-cancel-read/test.wit b/tests/runtime/future-cancel-read/test.wit similarity index 94% rename from tests/runtime-async/async/future-cancel-read/test.wit rename to tests/runtime/future-cancel-read/test.wit index 52bbcd51c..df8e3e30c 100644 --- a/tests/runtime-async/async/future-cancel-read/test.wit +++ b/tests/runtime/future-cancel-read/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/future-cancel-write-then-read/runner.rs b/tests/runtime/future-cancel-write-then-read/runner.rs similarity index 100% rename from tests/runtime-async/async/future-cancel-write-then-read/runner.rs rename to tests/runtime/future-cancel-write-then-read/runner.rs diff --git a/tests/runtime-async/async/future-cancel-write-then-read/test.rs b/tests/runtime/future-cancel-write-then-read/test.rs similarity index 100% rename from tests/runtime-async/async/future-cancel-write-then-read/test.rs rename to tests/runtime/future-cancel-write-then-read/test.rs diff --git a/tests/runtime-async/async/future-cancel-write-then-read/test.wit b/tests/runtime/future-cancel-write-then-read/test.wit similarity index 91% rename from tests/runtime-async/async/future-cancel-write-then-read/test.wit rename to tests/runtime/future-cancel-write-then-read/test.wit index 1995d936a..78780f2db 100644 --- a/tests/runtime-async/async/future-cancel-write-then-read/test.wit +++ b/tests/runtime/future-cancel-write-then-read/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface the-test { diff --git a/tests/runtime-async/async/future-cancel-write/runner.c b/tests/runtime/future-cancel-write/runner.c similarity index 100% rename from tests/runtime-async/async/future-cancel-write/runner.c rename to tests/runtime/future-cancel-write/runner.c diff --git a/tests/runtime-async/async/future-cancel-write/runner.rs b/tests/runtime/future-cancel-write/runner.rs similarity index 100% rename from tests/runtime-async/async/future-cancel-write/runner.rs rename to tests/runtime/future-cancel-write/runner.rs diff --git a/tests/runtime-async/async/future-cancel-write/test.c b/tests/runtime/future-cancel-write/test.c similarity index 100% rename from tests/runtime-async/async/future-cancel-write/test.c rename to tests/runtime/future-cancel-write/test.c diff --git a/tests/runtime-async/async/future-cancel-write/test.mbt b/tests/runtime/future-cancel-write/test.mbt similarity index 100% rename from tests/runtime-async/async/future-cancel-write/test.mbt rename to tests/runtime/future-cancel-write/test.mbt diff --git a/tests/runtime-async/async/future-cancel-write/test.rs b/tests/runtime/future-cancel-write/test.rs similarity index 100% rename from tests/runtime-async/async/future-cancel-write/test.rs rename to tests/runtime/future-cancel-write/test.rs diff --git a/tests/runtime-async/async/future-cancel-write/test.wit b/tests/runtime/future-cancel-write/test.wit similarity index 92% rename from tests/runtime-async/async/future-cancel-write/test.wit rename to tests/runtime/future-cancel-write/test.wit index 3b4dc207a..4d71ca6b9 100644 --- a/tests/runtime-async/async/future-cancel-write/test.wit +++ b/tests/runtime/future-cancel-write/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/future-close-after-coming-back/runner.rs b/tests/runtime/future-close-after-coming-back/runner.rs similarity index 100% rename from tests/runtime-async/async/future-close-after-coming-back/runner.rs rename to tests/runtime/future-close-after-coming-back/runner.rs diff --git a/tests/runtime-async/async/future-close-after-coming-back/test.mbt b/tests/runtime/future-close-after-coming-back/test.mbt similarity index 100% rename from tests/runtime-async/async/future-close-after-coming-back/test.mbt rename to tests/runtime/future-close-after-coming-back/test.mbt diff --git a/tests/runtime-async/async/future-close-after-coming-back/test.rs b/tests/runtime/future-close-after-coming-back/test.rs similarity index 100% rename from tests/runtime-async/async/future-close-after-coming-back/test.rs rename to tests/runtime/future-close-after-coming-back/test.rs diff --git a/tests/runtime-async/async/future-write-then-read-comes-back/test.wit b/tests/runtime/future-close-after-coming-back/test.wit similarity index 91% rename from tests/runtime-async/async/future-write-then-read-comes-back/test.wit rename to tests/runtime/future-close-after-coming-back/test.wit index 12083a297..fb7865ef6 100644 --- a/tests/runtime-async/async/future-write-then-read-comes-back/test.wit +++ b/tests/runtime/future-close-after-coming-back/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface the-test { diff --git a/tests/runtime-async/async/future-close-then-receive-read/runner.rs b/tests/runtime/future-close-then-receive-read/runner.rs similarity index 100% rename from tests/runtime-async/async/future-close-then-receive-read/runner.rs rename to tests/runtime/future-close-then-receive-read/runner.rs diff --git a/tests/runtime-async/async/future-close-then-receive-read/test.rs b/tests/runtime/future-close-then-receive-read/test.rs similarity index 100% rename from tests/runtime-async/async/future-close-then-receive-read/test.rs rename to tests/runtime/future-close-then-receive-read/test.rs diff --git a/tests/runtime-async/async/future-close-then-receive-read/test.wit b/tests/runtime/future-close-then-receive-read/test.wit similarity index 91% rename from tests/runtime-async/async/future-close-then-receive-read/test.wit rename to tests/runtime/future-close-then-receive-read/test.wit index cc7f391e1..ec970d290 100644 --- a/tests/runtime-async/async/future-close-then-receive-read/test.wit +++ b/tests/runtime/future-close-then-receive-read/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface the-test { diff --git a/tests/runtime-async/async/future-closes-with-error/runner.rs b/tests/runtime/future-closes-with-error/runner.rs similarity index 100% rename from tests/runtime-async/async/future-closes-with-error/runner.rs rename to tests/runtime/future-closes-with-error/runner.rs diff --git a/tests/runtime-async/async/future-closes-with-error/test.rs b/tests/runtime/future-closes-with-error/test.rs similarity index 100% rename from tests/runtime-async/async/future-closes-with-error/test.rs rename to tests/runtime/future-closes-with-error/test.rs diff --git a/tests/runtime-async/async/future-closes-with-error/test.wit b/tests/runtime/future-closes-with-error/test.wit similarity index 90% rename from tests/runtime-async/async/future-closes-with-error/test.wit rename to tests/runtime/future-closes-with-error/test.wit index 58211d988..634fcae53 100644 --- a/tests/runtime-async/async/future-closes-with-error/test.wit +++ b/tests/runtime/future-closes-with-error/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface the-test { diff --git a/tests/runtime-async/async/future-write-then-read-comes-back/runner.rs b/tests/runtime/future-write-then-read-comes-back/runner.rs similarity index 100% rename from tests/runtime-async/async/future-write-then-read-comes-back/runner.rs rename to tests/runtime/future-write-then-read-comes-back/runner.rs diff --git a/tests/runtime-async/async/future-write-then-read-comes-back/test.rs b/tests/runtime/future-write-then-read-comes-back/test.rs similarity index 100% rename from tests/runtime-async/async/future-write-then-read-comes-back/test.rs rename to tests/runtime/future-write-then-read-comes-back/test.rs diff --git a/tests/runtime-async/async/future-close-after-coming-back/test.wit b/tests/runtime/future-write-then-read-comes-back/test.wit similarity index 91% rename from tests/runtime-async/async/future-close-after-coming-back/test.wit rename to tests/runtime/future-write-then-read-comes-back/test.wit index 12083a297..fb7865ef6 100644 --- a/tests/runtime-async/async/future-close-after-coming-back/test.wit +++ b/tests/runtime/future-write-then-read-comes-back/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface the-test { diff --git a/tests/runtime-async/async/future-write-then-read-remote/runner.rs b/tests/runtime/future-write-then-read-remote/runner.rs similarity index 100% rename from tests/runtime-async/async/future-write-then-read-remote/runner.rs rename to tests/runtime/future-write-then-read-remote/runner.rs diff --git a/tests/runtime-async/async/future-write-then-read-remote/runner2.rs b/tests/runtime/future-write-then-read-remote/runner2.rs similarity index 100% rename from tests/runtime-async/async/future-write-then-read-remote/runner2.rs rename to tests/runtime/future-write-then-read-remote/runner2.rs diff --git a/tests/runtime-async/async/future-write-then-read-remote/test.rs b/tests/runtime/future-write-then-read-remote/test.rs similarity index 100% rename from tests/runtime-async/async/future-write-then-read-remote/test.rs rename to tests/runtime/future-write-then-read-remote/test.rs diff --git a/tests/runtime-async/async/future-write-then-read-remote/test.wit b/tests/runtime/future-write-then-read-remote/test.wit similarity index 90% rename from tests/runtime-async/async/future-write-then-read-remote/test.wit rename to tests/runtime/future-write-then-read-remote/test.wit index 58211d988..634fcae53 100644 --- a/tests/runtime-async/async/future-write-then-read-remote/test.wit +++ b/tests/runtime/future-write-then-read-remote/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface the-test { diff --git a/tests/runtime-async/async/incomplete-writes/leaf.go b/tests/runtime/incomplete-writes/leaf.go similarity index 100% rename from tests/runtime-async/async/incomplete-writes/leaf.go rename to tests/runtime/incomplete-writes/leaf.go diff --git a/tests/runtime-async/async/incomplete-writes/runner.go b/tests/runtime/incomplete-writes/runner.go similarity index 100% rename from tests/runtime-async/async/incomplete-writes/runner.go rename to tests/runtime/incomplete-writes/runner.go diff --git a/tests/runtime-async/async/incomplete-writes/test.go b/tests/runtime/incomplete-writes/test.go similarity index 100% rename from tests/runtime-async/async/incomplete-writes/test.go rename to tests/runtime/incomplete-writes/test.go diff --git a/tests/runtime-async/async/incomplete-writes/test.wit b/tests/runtime/incomplete-writes/test.wit similarity index 98% rename from tests/runtime-async/async/incomplete-writes/test.wit rename to tests/runtime/incomplete-writes/test.wit index 6e3604133..6498e3a90 100644 --- a/tests/runtime-async/async/incomplete-writes/test.wit +++ b/tests/runtime/incomplete-writes/test.wit @@ -1,3 +1,4 @@ +//@ async = true //@ dependencies = ['test', 'leaf'] package my:test; diff --git a/tests/runtime-async/async/pending-import/runner.c b/tests/runtime/pending-import/runner.c similarity index 100% rename from tests/runtime-async/async/pending-import/runner.c rename to tests/runtime/pending-import/runner.c diff --git a/tests/runtime-async/async/pending-import/runner.cs b/tests/runtime/pending-import/runner.cs similarity index 100% rename from tests/runtime-async/async/pending-import/runner.cs rename to tests/runtime/pending-import/runner.cs diff --git a/tests/runtime-async/async/pending-import/runner.rs b/tests/runtime/pending-import/runner.rs similarity index 100% rename from tests/runtime-async/async/pending-import/runner.rs rename to tests/runtime/pending-import/runner.rs diff --git a/tests/runtime-async/async/pending-import/test.c b/tests/runtime/pending-import/test.c similarity index 100% rename from tests/runtime-async/async/pending-import/test.c rename to tests/runtime/pending-import/test.c diff --git a/tests/runtime-async/async/pending-import/test.cs b/tests/runtime/pending-import/test.cs similarity index 100% rename from tests/runtime-async/async/pending-import/test.cs rename to tests/runtime/pending-import/test.cs diff --git a/tests/runtime-async/async/pending-import/test.rs b/tests/runtime/pending-import/test.rs similarity index 100% rename from tests/runtime-async/async/pending-import/test.rs rename to tests/runtime/pending-import/test.rs diff --git a/tests/runtime-async/async/pending-import/test.wit b/tests/runtime/pending-import/test.wit similarity index 90% rename from tests/runtime-async/async/pending-import/test.wit rename to tests/runtime/pending-import/test.wit index a5ab30cda..0a586a2d3 100644 --- a/tests/runtime-async/async/pending-import/test.wit +++ b/tests/runtime/pending-import/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/ping-pong/runner.cs b/tests/runtime/ping-pong/disabled/runner.cs similarity index 100% rename from tests/runtime-async/async/ping-pong/runner.cs rename to tests/runtime/ping-pong/disabled/runner.cs diff --git a/tests/runtime-async/async/ping-pong/test.cs b/tests/runtime/ping-pong/disabled/test.cs similarity index 100% rename from tests/runtime-async/async/ping-pong/test.cs rename to tests/runtime/ping-pong/disabled/test.cs diff --git a/tests/runtime-async/async/ping-pong/runner.c b/tests/runtime/ping-pong/runner.c similarity index 100% rename from tests/runtime-async/async/ping-pong/runner.c rename to tests/runtime/ping-pong/runner.c diff --git a/tests/runtime-async/async/ping-pong/runner.go b/tests/runtime/ping-pong/runner.go similarity index 100% rename from tests/runtime-async/async/ping-pong/runner.go rename to tests/runtime/ping-pong/runner.go diff --git a/tests/runtime-async/async/ping-pong/runner.rs b/tests/runtime/ping-pong/runner.rs similarity index 100% rename from tests/runtime-async/async/ping-pong/runner.rs rename to tests/runtime/ping-pong/runner.rs diff --git a/tests/runtime-async/async/ping-pong/test.c b/tests/runtime/ping-pong/test.c similarity index 100% rename from tests/runtime-async/async/ping-pong/test.c rename to tests/runtime/ping-pong/test.c diff --git a/tests/runtime-async/async/ping-pong/test.go b/tests/runtime/ping-pong/test.go similarity index 100% rename from tests/runtime-async/async/ping-pong/test.go rename to tests/runtime/ping-pong/test.go diff --git a/tests/runtime-async/async/ping-pong/test.rs b/tests/runtime/ping-pong/test.rs similarity index 100% rename from tests/runtime-async/async/ping-pong/test.rs rename to tests/runtime/ping-pong/test.rs diff --git a/tests/runtime-async/async/ping-pong/test.wit b/tests/runtime/ping-pong/test.wit similarity index 93% rename from tests/runtime-async/async/ping-pong/test.wit rename to tests/runtime/ping-pong/test.wit index cdfa4d43d..41468b2c8 100644 --- a/tests/runtime-async/async/ping-pong/test.wit +++ b/tests/runtime/ping-pong/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/return-string/runner.go b/tests/runtime/return-string/runner.go similarity index 100% rename from tests/runtime-async/async/return-string/runner.go rename to tests/runtime/return-string/runner.go diff --git a/tests/runtime-async/async/return-string/test.go b/tests/runtime/return-string/test.go similarity index 100% rename from tests/runtime-async/async/return-string/test.go rename to tests/runtime/return-string/test.go diff --git a/tests/runtime-async/async/return-string/test.wit b/tests/runtime/return-string/test.wit similarity index 90% rename from tests/runtime-async/async/return-string/test.wit rename to tests/runtime/return-string/test.wit index 943d8cea7..c8b23cdaf 100644 --- a/tests/runtime-async/async/return-string/test.wit +++ b/tests/runtime/return-string/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/rust-async-and-block-on/runner.rs b/tests/runtime/rust-async-and-block-on/runner.rs similarity index 100% rename from tests/runtime-async/async/rust-async-and-block-on/runner.rs rename to tests/runtime/rust-async-and-block-on/runner.rs diff --git a/tests/runtime-async/async/rust-async-and-block-on/test.rs b/tests/runtime/rust-async-and-block-on/test.rs similarity index 100% rename from tests/runtime-async/async/rust-async-and-block-on/test.rs rename to tests/runtime/rust-async-and-block-on/test.rs diff --git a/tests/runtime-async/async/rust-async-and-block-on/test.wit b/tests/runtime/rust-async-and-block-on/test.wit similarity index 90% rename from tests/runtime-async/async/rust-async-and-block-on/test.wit rename to tests/runtime/rust-async-and-block-on/test.wit index 3e66f4289..94ebaa738 100644 --- a/tests/runtime-async/async/rust-async-and-block-on/test.wit +++ b/tests/runtime/rust-async-and-block-on/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface i { diff --git a/tests/runtime-async/async/rust-cross-task-wakeup/runner.rs b/tests/runtime/rust-cross-task-wakeup/runner.rs similarity index 100% rename from tests/runtime-async/async/rust-cross-task-wakeup/runner.rs rename to tests/runtime/rust-cross-task-wakeup/runner.rs diff --git a/tests/runtime-async/async/rust-cross-task-wakeup/test.rs b/tests/runtime/rust-cross-task-wakeup/test.rs similarity index 100% rename from tests/runtime-async/async/rust-cross-task-wakeup/test.rs rename to tests/runtime/rust-cross-task-wakeup/test.rs diff --git a/tests/runtime-async/async/rust-cross-task-wakeup/test.wit b/tests/runtime/rust-cross-task-wakeup/test.wit similarity index 91% rename from tests/runtime-async/async/rust-cross-task-wakeup/test.wit rename to tests/runtime/rust-cross-task-wakeup/test.wit index ae07023b3..35e8f2dfe 100644 --- a/tests/runtime-async/async/rust-cross-task-wakeup/test.wit +++ b/tests/runtime/rust-cross-task-wakeup/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/rust-lowered-send/runner.rs b/tests/runtime/rust-lowered-send/runner.rs similarity index 100% rename from tests/runtime-async/async/rust-lowered-send/runner.rs rename to tests/runtime/rust-lowered-send/runner.rs diff --git a/tests/runtime-async/async/rust-lowered-send/test.rs b/tests/runtime/rust-lowered-send/test.rs similarity index 100% rename from tests/runtime-async/async/rust-lowered-send/test.rs rename to tests/runtime/rust-lowered-send/test.rs diff --git a/tests/runtime-async/async/rust-lowered-send/test.wit b/tests/runtime/rust-lowered-send/test.wit similarity index 90% rename from tests/runtime-async/async/rust-lowered-send/test.wit rename to tests/runtime/rust-lowered-send/test.wit index 6ad54cc50..692778822 100644 --- a/tests/runtime-async/async/rust-lowered-send/test.wit +++ b/tests/runtime/rust-lowered-send/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface i { diff --git a/tests/runtime-async/async/simple-call-import/runner.c b/tests/runtime/simple-call-import/runner.c similarity index 100% rename from tests/runtime-async/async/simple-call-import/runner.c rename to tests/runtime/simple-call-import/runner.c diff --git a/tests/runtime-async/async/simple-call-import/runner.go b/tests/runtime/simple-call-import/runner.go similarity index 100% rename from tests/runtime-async/async/simple-call-import/runner.go rename to tests/runtime/simple-call-import/runner.go diff --git a/tests/runtime-async/async/simple-call-import/runner.rs b/tests/runtime/simple-call-import/runner.rs similarity index 100% rename from tests/runtime-async/async/simple-call-import/runner.rs rename to tests/runtime/simple-call-import/runner.rs diff --git a/tests/runtime-async/async/simple-call-import/test.c b/tests/runtime/simple-call-import/test.c similarity index 100% rename from tests/runtime-async/async/simple-call-import/test.c rename to tests/runtime/simple-call-import/test.c diff --git a/tests/runtime-async/async/simple-call-import/test.go b/tests/runtime/simple-call-import/test.go similarity index 100% rename from tests/runtime-async/async/simple-call-import/test.go rename to tests/runtime/simple-call-import/test.go diff --git a/tests/runtime-async/async/simple-call-import/test.rs b/tests/runtime/simple-call-import/test.rs similarity index 100% rename from tests/runtime-async/async/simple-call-import/test.rs rename to tests/runtime/simple-call-import/test.rs diff --git a/tests/runtime-async/async/simple-yield/test.wit b/tests/runtime/simple-call-import/test.wit similarity index 88% rename from tests/runtime-async/async/simple-yield/test.wit rename to tests/runtime/simple-call-import/test.wit index 378d915eb..7635d9993 100644 --- a/tests/runtime-async/async/simple-yield/test.wit +++ b/tests/runtime/simple-call-import/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface i { diff --git a/tests/runtime-async/async/simple-future/runner-nostd.rs b/tests/runtime/simple-future/runner-nostd.rs similarity index 100% rename from tests/runtime-async/async/simple-future/runner-nostd.rs rename to tests/runtime/simple-future/runner-nostd.rs diff --git a/tests/runtime-async/async/simple-future/runner.c b/tests/runtime/simple-future/runner.c similarity index 100% rename from tests/runtime-async/async/simple-future/runner.c rename to tests/runtime/simple-future/runner.c diff --git a/tests/runtime-async/async/simple-future/runner.cs b/tests/runtime/simple-future/runner.cs similarity index 100% rename from tests/runtime-async/async/simple-future/runner.cs rename to tests/runtime/simple-future/runner.cs diff --git a/tests/runtime-async/async/simple-future/runner.go b/tests/runtime/simple-future/runner.go similarity index 100% rename from tests/runtime-async/async/simple-future/runner.go rename to tests/runtime/simple-future/runner.go diff --git a/tests/runtime-async/async/simple-future/runner.rs b/tests/runtime/simple-future/runner.rs similarity index 100% rename from tests/runtime-async/async/simple-future/runner.rs rename to tests/runtime/simple-future/runner.rs diff --git a/tests/runtime-async/async/simple-future/test.c b/tests/runtime/simple-future/test.c similarity index 100% rename from tests/runtime-async/async/simple-future/test.c rename to tests/runtime/simple-future/test.c diff --git a/tests/runtime-async/async/simple-future/test.cs b/tests/runtime/simple-future/test.cs similarity index 100% rename from tests/runtime-async/async/simple-future/test.cs rename to tests/runtime/simple-future/test.cs diff --git a/tests/runtime-async/async/simple-future/test.go b/tests/runtime/simple-future/test.go similarity index 100% rename from tests/runtime-async/async/simple-future/test.go rename to tests/runtime/simple-future/test.go diff --git a/tests/runtime-async/async/simple-future/test.mbt b/tests/runtime/simple-future/test.mbt similarity index 100% rename from tests/runtime-async/async/simple-future/test.mbt rename to tests/runtime/simple-future/test.mbt diff --git a/tests/runtime-async/async/simple-future/test.rs b/tests/runtime/simple-future/test.rs similarity index 100% rename from tests/runtime-async/async/simple-future/test.rs rename to tests/runtime/simple-future/test.rs diff --git a/tests/runtime-async/async/simple-future/test.wit b/tests/runtime/simple-future/test.wit similarity index 92% rename from tests/runtime-async/async/simple-future/test.wit rename to tests/runtime/simple-future/test.wit index 6a1a7d1fa..45d6517b9 100644 --- a/tests/runtime-async/async/simple-future/test.wit +++ b/tests/runtime/simple-future/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/simple-import-params-results/runner.c b/tests/runtime/simple-import-params-results/runner.c similarity index 100% rename from tests/runtime-async/async/simple-import-params-results/runner.c rename to tests/runtime/simple-import-params-results/runner.c diff --git a/tests/runtime-async/async/simple-import-params-results/runner.cs b/tests/runtime/simple-import-params-results/runner.cs similarity index 100% rename from tests/runtime-async/async/simple-import-params-results/runner.cs rename to tests/runtime/simple-import-params-results/runner.cs diff --git a/tests/runtime-async/async/simple-import-params-results/runner.go b/tests/runtime/simple-import-params-results/runner.go similarity index 100% rename from tests/runtime-async/async/simple-import-params-results/runner.go rename to tests/runtime/simple-import-params-results/runner.go diff --git a/tests/runtime-async/async/simple-import-params-results/runner.rs b/tests/runtime/simple-import-params-results/runner.rs similarity index 100% rename from tests/runtime-async/async/simple-import-params-results/runner.rs rename to tests/runtime/simple-import-params-results/runner.rs diff --git a/tests/runtime-async/async/simple-import-params-results/test.c b/tests/runtime/simple-import-params-results/test.c similarity index 100% rename from tests/runtime-async/async/simple-import-params-results/test.c rename to tests/runtime/simple-import-params-results/test.c diff --git a/tests/runtime-async/async/simple-import-params-results/test.cs b/tests/runtime/simple-import-params-results/test.cs similarity index 100% rename from tests/runtime-async/async/simple-import-params-results/test.cs rename to tests/runtime/simple-import-params-results/test.cs diff --git a/tests/runtime-async/async/simple-import-params-results/test.go b/tests/runtime/simple-import-params-results/test.go similarity index 100% rename from tests/runtime-async/async/simple-import-params-results/test.go rename to tests/runtime/simple-import-params-results/test.go diff --git a/tests/runtime-async/async/simple-import-params-results/test.mbt b/tests/runtime/simple-import-params-results/test.mbt similarity index 100% rename from tests/runtime-async/async/simple-import-params-results/test.mbt rename to tests/runtime/simple-import-params-results/test.mbt diff --git a/tests/runtime-async/async/simple-import-params-results/test.rs b/tests/runtime/simple-import-params-results/test.rs similarity index 100% rename from tests/runtime-async/async/simple-import-params-results/test.rs rename to tests/runtime/simple-import-params-results/test.rs diff --git a/tests/runtime-async/async/simple-import-params-results/test.wit b/tests/runtime/simple-import-params-results/test.wit similarity index 95% rename from tests/runtime-async/async/simple-import-params-results/test.wit rename to tests/runtime/simple-import-params-results/test.wit index 567040647..828a1e14b 100644 --- a/tests/runtime-async/async/simple-import-params-results/test.wit +++ b/tests/runtime/simple-import-params-results/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface i { diff --git a/tests/runtime-async/async/simple-pending-import/runner.c b/tests/runtime/simple-pending-import/runner.c similarity index 100% rename from tests/runtime-async/async/simple-pending-import/runner.c rename to tests/runtime/simple-pending-import/runner.c diff --git a/tests/runtime-async/async/simple-pending-import/runner.go b/tests/runtime/simple-pending-import/runner.go similarity index 100% rename from tests/runtime-async/async/simple-pending-import/runner.go rename to tests/runtime/simple-pending-import/runner.go diff --git a/tests/runtime-async/async/simple-pending-import/runner.rs b/tests/runtime/simple-pending-import/runner.rs similarity index 100% rename from tests/runtime-async/async/simple-pending-import/runner.rs rename to tests/runtime/simple-pending-import/runner.rs diff --git a/tests/runtime-async/async/simple-pending-import/test.c b/tests/runtime/simple-pending-import/test.c similarity index 100% rename from tests/runtime-async/async/simple-pending-import/test.c rename to tests/runtime/simple-pending-import/test.c diff --git a/tests/runtime-async/async/simple-pending-import/test.go b/tests/runtime/simple-pending-import/test.go similarity index 100% rename from tests/runtime-async/async/simple-pending-import/test.go rename to tests/runtime/simple-pending-import/test.go diff --git a/tests/runtime-async/async/simple-pending-import/test.rs b/tests/runtime/simple-pending-import/test.rs similarity index 100% rename from tests/runtime-async/async/simple-pending-import/test.rs rename to tests/runtime/simple-pending-import/test.rs diff --git a/tests/runtime-async/async/simple-pending-import/test.wit b/tests/runtime/simple-pending-import/test.wit similarity index 88% rename from tests/runtime-async/async/simple-pending-import/test.wit rename to tests/runtime/simple-pending-import/test.wit index 378d915eb..7635d9993 100644 --- a/tests/runtime-async/async/simple-pending-import/test.wit +++ b/tests/runtime/simple-pending-import/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface i { diff --git a/tests/runtime-async/async/simple-stream-payload/runner.c b/tests/runtime/simple-stream-payload/runner.c similarity index 100% rename from tests/runtime-async/async/simple-stream-payload/runner.c rename to tests/runtime/simple-stream-payload/runner.c diff --git a/tests/runtime-async/async/simple-stream-payload/runner.cs b/tests/runtime/simple-stream-payload/runner.cs similarity index 100% rename from tests/runtime-async/async/simple-stream-payload/runner.cs rename to tests/runtime/simple-stream-payload/runner.cs diff --git a/tests/runtime-async/async/simple-stream-payload/runner.go b/tests/runtime/simple-stream-payload/runner.go similarity index 100% rename from tests/runtime-async/async/simple-stream-payload/runner.go rename to tests/runtime/simple-stream-payload/runner.go diff --git a/tests/runtime-async/async/simple-stream-payload/runner.rs b/tests/runtime/simple-stream-payload/runner.rs similarity index 100% rename from tests/runtime-async/async/simple-stream-payload/runner.rs rename to tests/runtime/simple-stream-payload/runner.rs diff --git a/tests/runtime-async/async/simple-stream-payload/test.c b/tests/runtime/simple-stream-payload/test.c similarity index 100% rename from tests/runtime-async/async/simple-stream-payload/test.c rename to tests/runtime/simple-stream-payload/test.c diff --git a/tests/runtime-async/async/simple-stream-payload/test.cs b/tests/runtime/simple-stream-payload/test.cs similarity index 100% rename from tests/runtime-async/async/simple-stream-payload/test.cs rename to tests/runtime/simple-stream-payload/test.cs diff --git a/tests/runtime-async/async/simple-stream-payload/test.go b/tests/runtime/simple-stream-payload/test.go similarity index 100% rename from tests/runtime-async/async/simple-stream-payload/test.go rename to tests/runtime/simple-stream-payload/test.go diff --git a/tests/runtime-async/async/simple-stream-payload/test.mbt b/tests/runtime/simple-stream-payload/test.mbt similarity index 100% rename from tests/runtime-async/async/simple-stream-payload/test.mbt rename to tests/runtime/simple-stream-payload/test.mbt diff --git a/tests/runtime-async/async/simple-stream-payload/test.rs b/tests/runtime/simple-stream-payload/test.rs similarity index 100% rename from tests/runtime-async/async/simple-stream-payload/test.rs rename to tests/runtime/simple-stream-payload/test.rs diff --git a/tests/runtime-async/async/simple-stream-payload/test.wit b/tests/runtime/simple-stream-payload/test.wit similarity index 90% rename from tests/runtime-async/async/simple-stream-payload/test.wit rename to tests/runtime/simple-stream-payload/test.wit index b776823fd..aa882a185 100644 --- a/tests/runtime-async/async/simple-stream-payload/test.wit +++ b/tests/runtime/simple-stream-payload/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/simple-stream/runner-nostd.rs b/tests/runtime/simple-stream/runner-nostd.rs similarity index 100% rename from tests/runtime-async/async/simple-stream/runner-nostd.rs rename to tests/runtime/simple-stream/runner-nostd.rs diff --git a/tests/runtime-async/async/simple-stream/runner.c b/tests/runtime/simple-stream/runner.c similarity index 100% rename from tests/runtime-async/async/simple-stream/runner.c rename to tests/runtime/simple-stream/runner.c diff --git a/tests/runtime-async/async/simple-stream/runner.go b/tests/runtime/simple-stream/runner.go similarity index 100% rename from tests/runtime-async/async/simple-stream/runner.go rename to tests/runtime/simple-stream/runner.go diff --git a/tests/runtime-async/async/simple-stream/runner.rs b/tests/runtime/simple-stream/runner.rs similarity index 100% rename from tests/runtime-async/async/simple-stream/runner.rs rename to tests/runtime/simple-stream/runner.rs diff --git a/tests/runtime-async/async/simple-stream/test.c b/tests/runtime/simple-stream/test.c similarity index 100% rename from tests/runtime-async/async/simple-stream/test.c rename to tests/runtime/simple-stream/test.c diff --git a/tests/runtime-async/async/simple-stream/test.go b/tests/runtime/simple-stream/test.go similarity index 100% rename from tests/runtime-async/async/simple-stream/test.go rename to tests/runtime/simple-stream/test.go diff --git a/tests/runtime-async/async/simple-stream/test.mbt b/tests/runtime/simple-stream/test.mbt similarity index 100% rename from tests/runtime-async/async/simple-stream/test.mbt rename to tests/runtime/simple-stream/test.mbt diff --git a/tests/runtime-async/async/simple-stream/test.wit b/tests/runtime/simple-stream/test.wit similarity index 90% rename from tests/runtime-async/async/simple-stream/test.wit rename to tests/runtime/simple-stream/test.wit index bdf2bc03b..4281d2826 100644 --- a/tests/runtime-async/async/simple-stream/test.wit +++ b/tests/runtime/simple-stream/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime-async/async/simple-yield/runner-nostd.rs b/tests/runtime/simple-yield/runner-nostd.rs similarity index 100% rename from tests/runtime-async/async/simple-yield/runner-nostd.rs rename to tests/runtime/simple-yield/runner-nostd.rs diff --git a/tests/runtime-async/async/simple-yield/runner.c b/tests/runtime/simple-yield/runner.c similarity index 100% rename from tests/runtime-async/async/simple-yield/runner.c rename to tests/runtime/simple-yield/runner.c diff --git a/tests/runtime-async/async/simple-yield/runner.rs b/tests/runtime/simple-yield/runner.rs similarity index 100% rename from tests/runtime-async/async/simple-yield/runner.rs rename to tests/runtime/simple-yield/runner.rs diff --git a/tests/runtime-async/async/simple-yield/test.c b/tests/runtime/simple-yield/test.c similarity index 100% rename from tests/runtime-async/async/simple-yield/test.c rename to tests/runtime/simple-yield/test.c diff --git a/tests/runtime-async/async/simple-yield/test.rs b/tests/runtime/simple-yield/test.rs similarity index 100% rename from tests/runtime-async/async/simple-yield/test.rs rename to tests/runtime/simple-yield/test.rs diff --git a/tests/runtime-async/async/threading-builtins/test.wit b/tests/runtime/simple-yield/test.wit similarity index 88% rename from tests/runtime-async/async/threading-builtins/test.wit rename to tests/runtime/simple-yield/test.wit index 378d915eb..7635d9993 100644 --- a/tests/runtime-async/async/threading-builtins/test.wit +++ b/tests/runtime/simple-yield/test.wit @@ -1,3 +1,4 @@ +//@ async = true package a:b; interface i { diff --git a/tests/runtime-async/async/stream-to-futures-stream/runner.rs b/tests/runtime/stream-to-futures-stream/runner.rs similarity index 100% rename from tests/runtime-async/async/stream-to-futures-stream/runner.rs rename to tests/runtime/stream-to-futures-stream/runner.rs diff --git a/tests/runtime-async/async/stream-to-futures-stream/test.rs b/tests/runtime/stream-to-futures-stream/test.rs similarity index 100% rename from tests/runtime-async/async/stream-to-futures-stream/test.rs rename to tests/runtime/stream-to-futures-stream/test.rs diff --git a/tests/runtime-async/async/stream-to-futures-stream/test.wit b/tests/runtime/stream-to-futures-stream/test.wit similarity index 90% rename from tests/runtime-async/async/stream-to-futures-stream/test.wit rename to tests/runtime/stream-to-futures-stream/test.wit index b776823fd..aa882a185 100644 --- a/tests/runtime-async/async/stream-to-futures-stream/test.wit +++ b/tests/runtime/stream-to-futures-stream/test.wit @@ -1,3 +1,4 @@ +//@ async = true package my:test; interface i { diff --git a/tests/runtime/strings/runner.go b/tests/runtime/strings/disabled/runner.go similarity index 100% rename from tests/runtime/strings/runner.go rename to tests/runtime/strings/disabled/runner.go diff --git a/tests/runtime/strings/test.go b/tests/runtime/strings/disabled/test.go similarity index 100% rename from tests/runtime/strings/test.go rename to tests/runtime/strings/disabled/test.go diff --git a/tests/runtime-async/async/yield-loop-receives-events/leaf.rs b/tests/runtime/yield-loop-receives-events/leaf.rs similarity index 100% rename from tests/runtime-async/async/yield-loop-receives-events/leaf.rs rename to tests/runtime/yield-loop-receives-events/leaf.rs diff --git a/tests/runtime-async/async/yield-loop-receives-events/middle.rs b/tests/runtime/yield-loop-receives-events/middle.rs similarity index 100% rename from tests/runtime-async/async/yield-loop-receives-events/middle.rs rename to tests/runtime/yield-loop-receives-events/middle.rs diff --git a/tests/runtime-async/async/yield-loop-receives-events/runner.rs b/tests/runtime/yield-loop-receives-events/runner.rs similarity index 100% rename from tests/runtime-async/async/yield-loop-receives-events/runner.rs rename to tests/runtime/yield-loop-receives-events/runner.rs diff --git a/tests/runtime-async/async/yield-loop-receives-events/test.wit b/tests/runtime/yield-loop-receives-events/test.wit similarity index 94% rename from tests/runtime-async/async/yield-loop-receives-events/test.wit rename to tests/runtime/yield-loop-receives-events/test.wit index 343a5294d..6136b613b 100644 --- a/tests/runtime-async/async/yield-loop-receives-events/test.wit +++ b/tests/runtime/yield-loop-receives-events/test.wit @@ -1,3 +1,4 @@ +//@ async = true //@ dependencies = ['middle', 'leaf'] package test:common; diff --git a/tests/runtime-async/async/threading-builtins/runner.c b/tests/threading/threading-builtins/runner.c similarity index 100% rename from tests/runtime-async/async/threading-builtins/runner.c rename to tests/threading/threading-builtins/runner.c diff --git a/tests/runtime-async/async/threading-builtins/test.c b/tests/threading/threading-builtins/test.c similarity index 100% rename from tests/runtime-async/async/threading-builtins/test.c rename to tests/threading/threading-builtins/test.c diff --git a/tests/runtime-async/async/simple-call-import/test.wit b/tests/threading/threading-builtins/test.wit similarity index 100% rename from tests/runtime-async/async/simple-call-import/test.wit rename to tests/threading/threading-builtins/test.wit From 6f156096b24bd75d511afd46a03ba42107389cb1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Jul 2026 15:43:30 -0500 Subject: [PATCH 12/13] Update wasm-tools dependencies (#1654) * Update wasm-tools dependencies Additionally update C intrinsics for threading as they've been renamed slightly. * Remove unused import --- Cargo.lock | 76 ++++++------ Cargo.toml | 14 +-- crates/c/src/lib.rs | 137 +++++----------------- crates/cpp/src/lib.rs | 5 + crates/guest-rust/macro/src/lib.rs | 4 +- tests/threading/threading-builtins/test.c | 40 ++++--- 6 files changed, 105 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63a642d15..885e23beb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1024,7 +1024,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.252.0", + "wasm-encoder 0.253.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1221,9 +1221,9 @@ checksum = "1b6c48003fe59c201c97a7786ff55feabe6b6f83b598aa9ff5bcc4f94d940bf3" [[package]] name = "wasm-compose" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59b710751a35d54732a63851cdfacbfb2266b7160ce174faf269d3f4e84e31b" +checksum = "a00572a13a43b4754066ddeb46aec65091cd4cdb5b632768295a4236574da28a" dependencies = [ "anyhow", "heck", @@ -1234,8 +1234,8 @@ dependencies = [ "serde_derive", "serde_yaml2", "smallvec", - "wasm-encoder 0.252.0", - "wasmparser 0.252.0", + "wasm-encoder 0.253.0", + "wasmparser 0.253.0", "wat", ] @@ -1251,12 +1251,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8185ae345fa5687c054626ff9a50e7089797a343d9904d1dc9820eb4c4d3196f" +checksum = "59972d6cd272259de647b7c1f1912e45e289c75ffd4be04e10695507cd7e1b59" dependencies = [ "leb128fmt", - "wasmparser 0.252.0", + "wasmparser 0.253.0", ] [[package]] @@ -1280,14 +1280,14 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7e08e02a3cd55bf778009d4cd6faae50da011f293644daf78a531a32d6d142" +checksum = "b3f45816ef616806f48498bcd831377de578c4fa51db0c83ab8ceb78cc13523b" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.252.0", - "wasmparser 0.252.0", + "wasm-encoder 0.253.0", + "wasmparser 0.253.0", ] [[package]] @@ -1305,9 +1305,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c" +checksum = "19db11f87d2486580e1e8b6f494c54df7e0566b87d0b599db843c24019667339" dependencies = [ "bitflags", "hashbrown 0.17.1", @@ -1318,22 +1318,22 @@ dependencies = [ [[package]] name = "wast" -version = "252.0.0" +version = "253.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942a3449d6a593fccc111a6241c8df52bda168af30e40bf9580d4394d7374c65" +checksum = "d3264542f8965c5d84fb1085d924bfba9a6314bb228eff13a2de14d7627664d0" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.252.0", + "wasm-encoder 0.253.0", ] [[package]] name = "wat" -version = "1.252.0" +version = "1.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c72a4ba7088f7bac94cf516e49882bdf97068904a563768cf249efc839ec42cb" +checksum = "4bfc5ce906144200c972ec617470aa35bd847472e170b26dde3e80541c674055" dependencies = [ "wast", ] @@ -1378,8 +1378,8 @@ dependencies = [ "clap", "heck", "indexmap", - "wasm-encoder 0.252.0", - "wasm-metadata 0.252.0", + "wasm-encoder 0.253.0", + "wasm-metadata 0.253.0", "wit-bindgen-core", "wit-component", ] @@ -1391,7 +1391,7 @@ dependencies = [ "anyhow", "clap", "env_logger", - "wasm-encoder 0.252.0", + "wasm-encoder 0.253.0", "wit-bindgen-c", "wit-bindgen-core", "wit-bindgen-cpp", @@ -1424,8 +1424,8 @@ dependencies = [ "heck", "indexmap", "test-helpers", - "wasm-encoder 0.252.0", - "wasm-metadata 0.252.0", + "wasm-encoder 0.253.0", + "wasm-metadata 0.253.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", @@ -1441,7 +1441,7 @@ dependencies = [ "heck", "indexmap", "regex", - "wasm-metadata 0.252.0", + "wasm-metadata 0.253.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1454,8 +1454,8 @@ dependencies = [ "anyhow", "clap", "heck", - "wasm-encoder 0.252.0", - "wasm-metadata 0.252.0", + "wasm-encoder 0.253.0", + "wasm-metadata 0.253.0", "wit-bindgen-core", "wit-component", ] @@ -1496,7 +1496,7 @@ dependencies = [ "serde_json", "syn", "test-helpers", - "wasm-metadata 0.252.0", + "wasm-metadata 0.253.0", "wit-bindgen", "wit-bindgen-core", "wit-component", @@ -1534,8 +1534,8 @@ dependencies = [ "wac-types", "wasi-preview1-component-adapter-provider", "wasm-compose", - "wasm-encoder 0.252.0", - "wasmparser 0.252.0", + "wasm-encoder 0.253.0", + "wasmparser 0.253.0", "wat", "wit-bindgen-csharp", "wit-component", @@ -1544,9 +1544,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76db0662b590f45d33d0e363fa13539a5a1eecd35d5a12fe208c335461c1053d" +checksum = "dbbd2500ac3488489ee8c6e59b79d7e47e6da5bfb019efd35d5dca57b78af624" dependencies = [ "anyhow", "bitflags", @@ -1555,18 +1555,18 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.252.0", - "wasm-metadata 0.252.0", - "wasmparser 0.252.0", + "wasm-encoder 0.253.0", + "wasm-metadata 0.253.0", + "wasmparser 0.253.0", "wat", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4266bea110371c620ccf3201c5023676046bc4556e5c7cfb5d500bda5ebc162d" +checksum = "4d997b8e5920fcbeec742b58e583325d6419a6aca617ae8075c406a61c65ba8a" dependencies = [ "anyhow", "hashbrown 0.17.1", @@ -1578,7 +1578,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-ident", - "wasmparser 0.252.0", + "wasmparser 0.253.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5bf23e8ae..98e1378a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,13 +47,13 @@ syn = { version = "2.0.89", features = ["printing"] } futures = "0.3.31" macro-string = "0.2.0" -wat = "1.252.0" -wasmparser = "0.252.0" -wasm-encoder = "0.252.0" -wasm-metadata = { version = "0.252.0", default-features = false } -wit-parser = "0.252.0" -wit-component = "0.252.0" -wasm-compose = "0.252.0" +wat = "1.253.0" +wasmparser = "0.253.0" +wasm-encoder = "0.253.0" +wasm-metadata = { version = "0.253.0", default-features = false } +wit-parser = "0.253.0" +wit-component = "0.253.0" +wasm-compose = "0.253.0" wit-bindgen-core = { path = 'crates/core', version = '0.58.0' } wit-bindgen-c = { path = 'crates/c', version = '0.58.0' } diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 970d34aa2..91d30b14e 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -728,123 +728,48 @@ impl C { let snake = self.world.to_snake_case(); uwriteln!( self.src.h_async, - " -void* {snake}_context_get_1(void); -void {snake}_context_set_1(void* value); -uint32_t {snake}_thread_yield_cancellable(void); -uint32_t {snake}_thread_index(void); -uint32_t {snake}_thread_new_indirect(void (*start_function)(void*), void* arg); -void {snake}_thread_suspend_to(uint32_t thread); -uint32_t {snake}_thread_suspend_to_cancellable(uint32_t thread); -void {snake}_thread_suspend_to_suspended(uint32_t thread); -uint32_t {snake}_thread_suspend_to_suspended_cancellable(uint32_t thread); -void {snake}_thread_unsuspend(uint32_t thread); -void {snake}_thread_yield_to_suspended(uint32_t thread); -uint32_t {snake}_thread_yield_to_suspended_cancellable(uint32_t thread); -void {snake}_thread_suspend(void); -uint32_t {snake}_thread_suspend_cancellable(void); - " - ); - uwriteln!( - self.src.c_async, r#" __attribute__((__import_module__("$root"), __import_name__("[context-get-1]"))) -extern void* __context_get_1(void); - -void* {snake}_context_get_1(void) {{ - return __context_get_1(); -}} +extern void* {snake}_context_get_1(void); __attribute__((__import_module__("$root"), __import_name__("[context-set-1]"))) -extern void __context_set_1(void*); - -void {snake}_context_set_1(void* value) {{ - __context_set_1(value); -}} - -__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-yield]"))) -extern uint32_t __thread_yield_cancellable(void); - -uint32_t {snake}_thread_yield_cancellable(void) {{ - return __thread_yield_cancellable(); -}} +extern void {snake}_context_set_1(void* value); __attribute__((__import_module__("$root"), __import_name__("[thread-index]"))) -extern uint32_t __thread_index(void); - -uint32_t {snake}_thread_index(void) {{ - return __thread_index(); -}} +extern uint32_t {snake}_thread_index(void); __attribute__((__import_module__("$root"), __import_name__("[thread-new-indirect-v0]"))) -extern uint32_t __thread_new_indirect(uint32_t, void*); - -uint32_t {snake}_thread_new_indirect(void (*start_function)(void*), void* arg) {{ - return __thread_new_indirect((uint32_t)(uintptr_t)start_function, arg -); -}} - -__attribute__((__import_module__("$root"), __import_name__("[thread-suspend-to-suspended]"))) -extern uint32_t __thread_suspend_to_suspended(uint32_t); - -void {snake}_thread_suspend_to_suspended(uint32_t thread) {{ - __thread_suspend_to_suspended(thread); -}} - -__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-suspend-to-suspended]"))) -extern uint32_t __thread_suspend_to_suspended_cancellable(uint32_t); - -uint32_t {snake}_thread_suspend_to_suspended_cancellable(uint32_t thread) {{ - return __thread_suspend_to_suspended_cancellable(thread); -}} - -__attribute__((__import_module__("$root"), __import_name__("[thread-suspend-to]"))) -extern uint32_t __thread_suspend_to(uint32_t); - -void {snake}_thread_suspend_to(uint32_t thread) {{ - __thread_suspend_to(thread); -}} - -__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-suspend-to]"))) -extern uint32_t __thread_suspend_to_cancellable(uint32_t); - -uint32_t {snake}_thread_suspend_to_cancellable(uint32_t thread) {{ - return __thread_suspend_to_cancellable(thread); -}} - -__attribute__((__import_module__("$root"), __import_name__("[thread-unsuspend]"))) -extern void __thread_unsuspend(uint32_t); - -void {snake}_thread_unsuspend(uint32_t thread) {{ - __thread_unsuspend(thread); -}} - -__attribute__((__import_module__("$root"), __import_name__("[thread-yield-to-suspended]"))) -extern uint32_t __thread_yield_to_suspended(uint32_t); - -void {snake}_thread_yield_to_suspended(uint32_t thread) {{ - __thread_yield_to_suspended(thread); -}} - -__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-yield-to-suspended]"))) -extern uint32_t __thread_yield_to_suspended_cancellable(uint32_t); - -uint32_t {snake}_thread_yield_to_suspended_cancellable(uint32_t thread) {{ - return __thread_yield_to_suspended_cancellable(thread); -}} +extern uint32_t {snake}_thread_new_indirect(void (*start_function)(void*), void* arg); +__attribute__((__import_module__("$root"), __import_name__("[thread-resume-later]"))) +extern void {snake}_thread_resume_later(uint32_t thread); __attribute__((__import_module__("$root"), __import_name__("[thread-suspend]"))) -extern uint32_t __thread_suspend(void); - -void {snake}_thread_suspend(void) {{ - __thread_suspend(); -}} - +extern uint32_t {snake}_thread_suspend(void); __attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-suspend]"))) -extern uint32_t __thread_suspend_cancellable(void); -uint32_t {snake}_thread_suspend_cancellable(void) {{ - return __thread_suspend_cancellable(); -}} +extern uint32_t {snake}_thread_suspend_cancellable(void); + +__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-yield]"))) +extern uint32_t {snake}_thread_yield_cancellable(void); + +__attribute__((__import_module__("$root"), __import_name__("[thread-suspend-then-resume]"))) +extern uint32_t {snake}_thread_suspend_then_resume(uint32_t thread); +__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-suspend-then-resume]"))) +extern uint32_t {snake}_thread_suspend_then_resume_cancellable(uint32_t thread); + +__attribute__((__import_module__("$root"), __import_name__("[thread-yield-then-resume]"))) +extern uint32_t {snake}_thread_yield_then_resume(uint32_t thread); +__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-yield-then-resume]"))) +extern uint32_t {snake}_thread_yield_then_resume_cancellable(uint32_t thread); + +__attribute__((__import_module__("$root"), __import_name__("[thread-suspend-then-promote]"))) +extern uint32_t {snake}_thread_suspend_then_promote(uint32_t thread); +__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-suspend-then-promote]"))) +extern uint32_t {snake}_thread_suspend_then_promote_cancellable(uint32_t thread); + +__attribute__((__import_module__("$root"), __import_name__("[thread-yield-then-promote]"))) +extern uint32_t {snake}_thread_yield_then_promote(uint32_t thread); +__attribute__((__import_module__("$root"), __import_name__("[cancellable][thread-yield-then-promote]"))) +extern uint32_t {snake}_thread_yield_then_promote_cancellable(uint32_t thread); "# ); } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 52e13ccab..8cd392616 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1976,6 +1976,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: Docs::default(), stability: Stability::Unknown, span: Default::default(), + external_id: None, }; self.generate_function(&func, &TypeOwner::Interface(intf), variant); } @@ -2010,6 +2011,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: Docs::default(), stability: Stability::Unknown, span: Default::default(), + external_id: None, }; self.generate_function(&func2, &TypeOwner::Interface(intf), variant); } @@ -2044,6 +2046,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: Docs::default(), stability: Stability::Unknown, span: Default::default(), + external_id: None, }; self.generate_function(&func, &TypeOwner::Interface(intf), variant); @@ -2059,6 +2062,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: Docs::default(), stability: Stability::Unknown, span: Default::default(), + external_id: None, }; self.generate_function(&func1, &TypeOwner::Interface(intf), variant); @@ -2074,6 +2078,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: Docs::default(), stability: Stability::Unknown, span: Default::default(), + external_id: None, }; self.generate_function(&func2, &TypeOwner::Interface(intf), variant); } diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index acf97ad53..d0393a834 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -8,7 +8,7 @@ use syn::punctuated::Punctuated; use syn::{Token, braced, token}; use wit_bindgen_core::AsyncFilterSet; use wit_bindgen_core::WorldGenerator; -use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId}; +use wit_bindgen_core::wit_parser::{PackageId, Resolve, WorldId}; use wit_bindgen_rust::{Opts, Ownership, WithOption}; #[proc_macro] @@ -238,7 +238,7 @@ fn parse_source( } } pkgs.truncate(0); - pkgs.push(resolve.push_group(UnresolvedPackageGroup::parse("macro-input", s)?)?); + pkgs.push(resolve.push_str("macro-input", s)?); } Some(Source::Paths(p)) => parse(p)?, None => parse(&[default])?, diff --git a/tests/threading/threading-builtins/test.c b/tests/threading/threading-builtins/test.c index 7a23d610a..b9baa2092 100644 --- a/tests/threading/threading-builtins/test.c +++ b/tests/threading/threading-builtins/test.c @@ -19,13 +19,15 @@ void thread_start(void *arg) { test_thread_yield_cancellable(); test_thread_suspend(); test_thread_suspend_cancellable(); - test_thread_yield_to_suspended(main_tid); - test_thread_yield_to_suspended_cancellable(main_tid); - test_thread_suspend_to_suspended(main_tid); - test_thread_suspend_to_suspended_cancellable(main_tid); - test_thread_suspend_to(main_tid); - test_thread_suspend_to_cancellable(main_tid); - test_thread_unsuspend(main_tid); + test_thread_yield_then_resume(main_tid); + test_thread_yield_then_resume_cancellable(main_tid); + test_thread_yield_then_promote(main_tid); + test_thread_yield_then_promote_cancellable(main_tid); + test_thread_suspend_then_resume(main_tid); + test_thread_suspend_then_resume_cancellable(main_tid); + test_thread_suspend_then_promote(main_tid); + test_thread_suspend_then_promote_cancellable(main_tid); + test_thread_resume_later(main_tid); } test_subtask_status_t exports_test_f_callback(test_event_t *event) { @@ -36,17 +38,19 @@ test_subtask_status_t exports_test_f_callback(test_event_t *event) { spawned_tid = test_thread_new_indirect(thread_start, &main_tid); // Now drive the other thread to completion by switching/yielding to it - test_thread_yield_to_suspended(spawned_tid); // other yields - test_thread_yield(); // other yields - test_thread_yield(); // other suspends - test_thread_yield_to_suspended(spawned_tid); // other suspends - test_thread_suspend_to_suspended(spawned_tid); // other yields to me - test_thread_suspend(); // other yields to me - test_thread_suspend(); // other suspends to me - test_thread_suspend_to_suspended(spawned_tid); // other suspends to me - test_thread_suspend_to_suspended(spawned_tid); // other suspends to me - test_thread_suspend_to_suspended(spawned_tid); // other suspends to me - test_thread_suspend_to_suspended( + test_thread_yield_then_resume(spawned_tid); // other yields + test_thread_yield(); // other yields + test_thread_yield(); // other suspends + test_thread_yield_then_resume(spawned_tid); // other suspends + test_thread_suspend_then_resume(spawned_tid); // other yields to me + test_thread_suspend(); // other yields to me + test_thread_suspend(); // other yields to me + test_thread_suspend(); // other yields to me + test_thread_suspend(); // other suspends to me + test_thread_suspend_then_resume(spawned_tid); // other suspends to me + test_thread_suspend_then_resume(spawned_tid); // other suspends to me + test_thread_suspend_then_resume(spawned_tid); // other suspends to me + test_thread_suspend_then_resume( spawned_tid); // other unsuspends me and terminates exports_test_f_return(); return TEST_CALLBACK_CODE_EXIT; From 93a5f4e3a91c3d4bf73b48e9f09ca090335194cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:06:48 -0500 Subject: [PATCH 13/13] Release wit-bindgen 0.59.0 (#1655) [automatically-tag-and-release-this-commit] Co-authored-by: Auto Release Process --- Cargo.lock | 24 +++++++++--------- Cargo.toml | 22 ++++++++-------- crates/cpp/Cargo.toml | 2 +- crates/guest-rust/Cargo.toml | 2 +- .../guest-rust/src/rt/libwit_bindgen_cabi.a | Bin 858 -> 858 bytes .../src/rt/wit_bindgen_cabi_realloc.c | 4 +-- .../src/rt/wit_bindgen_cabi_realloc.o | Bin 251 -> 251 bytes .../src/rt/wit_bindgen_cabi_realloc.rs | 2 +- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 885e23beb..231411c6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1361,7 +1361,7 @@ checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" [[package]] name = "wit-bindgen" -version = "0.58.0" +version = "0.59.0" dependencies = [ "bitflags", "futures", @@ -1372,7 +1372,7 @@ dependencies = [ [[package]] name = "wit-bindgen-c" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "clap", @@ -1386,7 +1386,7 @@ dependencies = [ [[package]] name = "wit-bindgen-cli" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "clap", @@ -1406,7 +1406,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "clap", @@ -1417,7 +1417,7 @@ dependencies = [ [[package]] name = "wit-bindgen-cpp" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "clap", @@ -1434,7 +1434,7 @@ dependencies = [ [[package]] name = "wit-bindgen-csharp" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "clap", @@ -1449,7 +1449,7 @@ dependencies = [ [[package]] name = "wit-bindgen-go" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "clap", @@ -1462,7 +1462,7 @@ dependencies = [ [[package]] name = "wit-bindgen-markdown" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "clap", @@ -1473,7 +1473,7 @@ dependencies = [ [[package]] name = "wit-bindgen-moonbit" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "clap", @@ -1483,7 +1483,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "bytes", @@ -1504,7 +1504,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "macro-string", @@ -1518,7 +1518,7 @@ dependencies = [ [[package]] name = "wit-bindgen-test" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "clap", diff --git a/Cargo.toml b/Cargo.toml index 98e1378a2..d7fabf202 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ resolver = "2" [workspace.package] edition = "2024" -version = "0.58.0" +version = "0.59.0" license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" repository = "https://github.com/bytecodealliance/wit-bindgen" rust-version = "1.88.0" @@ -55,16 +55,16 @@ wit-parser = "0.253.0" wit-component = "0.253.0" wasm-compose = "0.253.0" -wit-bindgen-core = { path = 'crates/core', version = '0.58.0' } -wit-bindgen-c = { path = 'crates/c', version = '0.58.0' } -wit-bindgen-cpp = { path = 'crates/cpp', version = '0.58.0' } -wit-bindgen-rust = { path = "crates/rust", version = "0.58.0" } -wit-bindgen-csharp = { path = 'crates/csharp', version = '0.58.0' } -wit-bindgen-markdown = { path = 'crates/markdown', version = '0.58.0' } -wit-bindgen-moonbit = { path = 'crates/moonbit', version = '0.58.0' } -wit-bindgen-go = { path = 'crates/go', version = '0.58.0' } -wit-bindgen = { path = 'crates/guest-rust', version = '0.58.0', default-features = false } -wit-bindgen-test = { path = 'crates/test', version = '0.58.0' } +wit-bindgen-core = { path = 'crates/core', version = '0.59.0' } +wit-bindgen-c = { path = 'crates/c', version = '0.59.0' } +wit-bindgen-cpp = { path = 'crates/cpp', version = '0.59.0' } +wit-bindgen-rust = { path = "crates/rust", version = "0.59.0" } +wit-bindgen-csharp = { path = 'crates/csharp', version = '0.59.0' } +wit-bindgen-markdown = { path = 'crates/markdown', version = '0.59.0' } +wit-bindgen-moonbit = { path = 'crates/moonbit', version = '0.59.0' } +wit-bindgen-go = { path = 'crates/go', version = '0.59.0' } +wit-bindgen = { path = 'crates/guest-rust', version = '0.59.0', default-features = false } +wit-bindgen-test = { path = 'crates/test', version = '0.59.0' } [workspace.lints.clippy] # The default set of lints in Clippy is viewed as "too noisy" right now so diff --git a/crates/cpp/Cargo.toml b/crates/cpp/Cargo.toml index f03cfe57a..e7a8e098a 100644 --- a/crates/cpp/Cargo.toml +++ b/crates/cpp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "wit-bindgen-cpp" authors = ["Christof Petig "] -version = "0.58.0" +version = "0.59.0" edition.workspace = true rust-version.workspace = true repository = 'https://github.com/cpetig/wit-bindgen' diff --git a/crates/guest-rust/Cargo.toml b/crates/guest-rust/Cargo.toml index efccac493..ef0ec9220 100644 --- a/crates/guest-rust/Cargo.toml +++ b/crates/guest-rust/Cargo.toml @@ -25,7 +25,7 @@ workspace = true all-features = true [dependencies] -wit-bindgen-rust-macro = { path = "./macro", optional = true, default-features = false, version = "0.58.0" } +wit-bindgen-rust-macro = { path = "./macro", optional = true, default-features = false, version = "0.59.0" } bitflags = { workspace = true, optional = true } futures = { version = "0.3.30", optional = true, default-features = false, features = ["alloc"] } diff --git a/crates/guest-rust/src/rt/libwit_bindgen_cabi.a b/crates/guest-rust/src/rt/libwit_bindgen_cabi.a index 1864d5525a1edb6c049ec589f356157dcdad76fc..6a023dd49bc01e212bc922e5e49d70dbc43dace4 100644 GIT binary patch delta 14 Vcmcb`c8hI84kM%G=3K_Ni~uR*1t$Oi delta 14 Vcmcb`c8hI84kM$*=3K_Ni~uR#1ttIh diff --git a/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.c b/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.c index e958945c8..5dd719ed3 100644 --- a/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.c +++ b/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.c @@ -2,9 +2,9 @@ #include -extern void *cabi_realloc_wit_bindgen_0_58_0(void *ptr, size_t old_size, size_t align, size_t new_size); +extern void *cabi_realloc_wit_bindgen_0_59_0(void *ptr, size_t old_size, size_t align, size_t new_size); __attribute__((__weak__, __export_name__("cabi_realloc"))) void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { - return cabi_realloc_wit_bindgen_0_58_0(ptr, old_size, align, new_size); + return cabi_realloc_wit_bindgen_0_59_0(ptr, old_size, align, new_size); } diff --git a/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.o b/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.o index cd87964d408840d4d451e33bd53ec338f00e6ac1..8c230259420896a77676376c10725b73c92e0f27 100644 GIT binary patch delta 11 Scmey(_?vNpAEV_&|1|&|V+5T5 delta 11 Scmey(_?vNpAEU)Y|1|&|Uj&>0 diff --git a/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.rs b/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.rs index 65431605b..1c0a8894d 100644 --- a/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.rs +++ b/crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.rs @@ -1,7 +1,7 @@ // This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh #[unsafe(no_mangle)] -pub unsafe extern "C" fn cabi_realloc_wit_bindgen_0_58_0( +pub unsafe extern "C" fn cabi_realloc_wit_bindgen_0_59_0( old_ptr: *mut u8, old_len: usize, align: usize,