diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index df3623476..0bb5e06a1 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -427,13 +427,31 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::FutureLift { payload, .. } => { let async_support = self.r#gen.r#gen.async_support_path(); let op = &operands[0]; - let name = payload + let mut name = payload .as_ref() .map(|ty| { self.r#gen .type_name_owned_with_id(ty, Identifier::StreamOrFuturePayload) }) .unwrap_or_else(|| "()".into()); + + // If the payload type corresponds to a Rust type alias, + // use the path to the canonical type alias for which the + // single FuturePayload implementation was generated, as the + // key to future_payloads + if let Some(Type::Id(id)) = payload { + let dealiased_id = dealias(resolve, *id); + if dealiased_id != *id { + name = self + .r#gen + .r#gen + .aliased_future_payloads + .get(&dealiased_id) + .unwrap() + .clone(); + } + }; + let ordinal = self .r#gen .r#gen @@ -455,13 +473,31 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::StreamLift { payload, .. } => { let async_support = self.r#gen.r#gen.async_support_path(); let op = &operands[0]; - let name = payload + let mut name = payload .as_ref() .map(|ty| { self.r#gen .type_name_owned_with_id(ty, Identifier::StreamOrFuturePayload) }) .unwrap_or_else(|| "()".into()); + + // If the payload type corresponds to a Rust type alias, + // use the path to the canonical type alias for which the + // single StreamPayload implementation was generated, as the + // key to stream_payloads + if let Some(Type::Id(id)) = payload { + let dealiased_id = dealias(resolve, *id); + if dealiased_id != *id { + name = self + .r#gen + .r#gen + .aliased_stream_payloads + .get(&dealiased_id) + .unwrap() + .clone(); + } + }; + let ordinal = self .r#gen .r#gen diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index c9a55eb14..c04215037 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -528,6 +528,27 @@ macro_rules! {macro_name} {{ } else { "()".into() }; + + let dealiased_id = match payload_type { + Some(Type::Id(id)) => Some(dealias(self.resolve, *id)), + _ => None, + }; + + // If the payload type corresponds to a Rust type alias, + // only one StreamPayload/FuturePayload implementation should be + // generated, therefore if one already exists, return + if let Some(dealiased_id) = dealiased_id + && payload_type != Some(&Type::Id(dealiased_id)) + { + let alias_map = match payload_for { + PayloadFor::Future => &mut self.r#gen.aliased_future_payloads, + PayloadFor::Stream => &mut self.r#gen.aliased_stream_payloads, + }; + if alias_map.contains_key(&dealiased_id) { + return; + } + } + let map = match payload_for { PayloadFor::Future => &mut self.r#gen.future_payloads, PayloadFor::Stream => &mut self.r#gen.stream_payloads, @@ -682,7 +703,21 @@ pub mod vtable{ordinal} {{ PayloadFor::Future => &mut self.r#gen.future_payloads, PayloadFor::Stream => &mut self.r#gen.stream_payloads, }; - map.insert(name, code); + map.insert(name.clone(), code); + + // If the payload type corresponds to a Rust type alias, + // record the module path/type name under which the + // StreamPayload/FuturePayload implementation was generated, + // so equivalent types can reuse the implementation + if let Some(dealiased_id) = dealiased_id + && payload_type != Some(&Type::Id(dealiased_id)) + { + let alias_map = match payload_for { + PayloadFor::Future => &mut self.r#gen.aliased_future_payloads, + PayloadFor::Stream => &mut self.r#gen.aliased_stream_payloads, + }; + alias_map.insert(dealiased_id, name); + } } fn generate_guest_import(&mut self, func: &Function, interface: Option<&WorldKey>) { diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 6d2139e4c..057748935 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -53,6 +53,13 @@ pub struct RustWasm { future_payloads: IndexMap, stream_payloads: IndexMap, + + // Stores the canonical type names with module paths for WIT types which generate Rust type aliases + // (and should therefore only generate a single StreamPayload or FuturePayload implementation) + // + // The values of these maps are used as keys to the future_payloads, and stream_payloads maps + aliased_future_payloads: IndexMap, + aliased_stream_payloads: IndexMap, } #[derive(Default)] diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index e2a0dd973..99a8131e9 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -49,6 +49,8 @@ impl LanguageMethods for Cpp { "async-trait-function.wit" | "error-context.wit" | "futures.wit" + | "import-export-future.wit" + | "import-export-stream.wit" | "resources-with-futures.wit" | "resources-with-streams.wit" | "streams.wit" => true, diff --git a/crates/test/src/csharp.rs b/crates/test/src/csharp.rs index aacb87422..6b66ff945 100644 --- a/crates/test/src/csharp.rs +++ b/crates/test/src/csharp.rs @@ -49,6 +49,8 @@ impl LanguageMethods for Csharp { | "streams.wit" | "error-context.wit" | "resource-fallible-constructor.wit" + | "import-export-future.wit" + | "import-export-stream.wit" ) } diff --git a/tests/codegen/import-export-future.wit b/tests/codegen/import-export-future.wit new file mode 100644 index 000000000..0f84e77af --- /dev/null +++ b/tests/codegen/import-export-future.wit @@ -0,0 +1,19 @@ +//@ async = true + +package a:b; + +world w { + import i; + export i; +} + +interface i { + use t.{r}; + f: func(p: future); +} + +interface t { + record r { + x: u32, + } +} \ No newline at end of file diff --git a/tests/codegen/import-export-stream.wit b/tests/codegen/import-export-stream.wit new file mode 100644 index 000000000..ee45bfdb1 --- /dev/null +++ b/tests/codegen/import-export-stream.wit @@ -0,0 +1,19 @@ +//@ async = true + +package a:b; + +world w { + import i; + export i; +} + +interface i { + use t.{r}; + f: func(p: stream); +} + +interface t { + record r { + x: u32, + } +} \ No newline at end of file