From d23e8e30ba76a86b8667299178928e62e20bbfd8 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 11 Dec 2025 19:49:09 +0100 Subject: [PATCH] fix(bindgen): futures and stream are not `Clone` Signed-off-by: Roman Volosatovs --- .../component-macro/tests/codegen/records.wit | 7 ++ .../component-macro/tests/expanded/records.rs | 87 +++++++++++++++++++ .../tests/expanded/records_async.rs | 87 +++++++++++++++++++ .../tests/expanded/records_concurrent.rs | 87 +++++++++++++++++++ .../tests/expanded/records_tracing_async.rs | 87 +++++++++++++++++++ crates/wit-bindgen/src/types.rs | 9 +- 6 files changed, 362 insertions(+), 2 deletions(-) diff --git a/crates/component-macro/tests/codegen/records.wit b/crates/component-macro/tests/codegen/records.wit index e47af0cbf61e..ea85dabac92a 100644 --- a/crates/component-macro/tests/codegen/records.wit +++ b/crates/component-macro/tests/codegen/records.wit @@ -53,6 +53,13 @@ interface records { type int-typedef = s32; type tuple-typedef2 = tuple; typedef-inout: func(e: tuple-typedef2) -> s32; + + record futures-and-streams { + a: future, + b: stream, + c: stream>, + d: future>, + } } world the-world { diff --git a/crates/component-macro/tests/expanded/records.rs b/crates/component-macro/tests/expanded/records.rs index fe82efb4a2bf..0096aad14f67 100644 --- a/crates/component-macro/tests/expanded/records.rs +++ b/crates/component-macro/tests/expanded/records.rs @@ -346,6 +346,48 @@ pub mod foo { 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::ALIGN32 ); }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct FuturesAndStreams { + #[component(name = "a")] + pub a: wasmtime::component::FutureReader, + #[component(name = "b")] + pub b: wasmtime::component::StreamReader, + #[component(name = "c")] + pub c: wasmtime::component::StreamReader< + wasmtime::component::FutureReader< + wasmtime::component::StreamReader<()>, + >, + >, + #[component(name = "d")] + pub d: wasmtime::component::FutureReader< + wasmtime::component::StreamReader< + wasmtime::component::FutureReader<()>, + >, + >, + } + impl core::fmt::Debug for FuturesAndStreams { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("FuturesAndStreams") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .finish() + } + } + const _: () = { + assert!( + 16 == < FuturesAndStreams as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < FuturesAndStreams as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; pub trait HostWithStore: wasmtime::component::HasData {} impl<_T: ?Sized> HostWithStore for _T where @@ -711,6 +753,51 @@ pub mod exports { >::ALIGN32 ); }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct FuturesAndStreams { + #[component(name = "a")] + pub a: wasmtime::component::FutureReader, + #[component(name = "b")] + pub b: wasmtime::component::StreamReader, + #[component(name = "c")] + pub c: wasmtime::component::StreamReader< + wasmtime::component::FutureReader< + wasmtime::component::StreamReader<()>, + >, + >, + #[component(name = "d")] + pub d: wasmtime::component::FutureReader< + wasmtime::component::StreamReader< + wasmtime::component::FutureReader<()>, + >, + >, + } + impl core::fmt::Debug for FuturesAndStreams { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("FuturesAndStreams") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .finish() + } + } + const _: () = { + assert!( + 16 == < FuturesAndStreams as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < FuturesAndStreams as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; #[derive(Clone)] pub struct Guest { tuple_arg: wasmtime::component::Func, diff --git a/crates/component-macro/tests/expanded/records_async.rs b/crates/component-macro/tests/expanded/records_async.rs index 4453813966db..cdbc698678ac 100644 --- a/crates/component-macro/tests/expanded/records_async.rs +++ b/crates/component-macro/tests/expanded/records_async.rs @@ -346,6 +346,48 @@ pub mod foo { 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::ALIGN32 ); }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct FuturesAndStreams { + #[component(name = "a")] + pub a: wasmtime::component::FutureReader, + #[component(name = "b")] + pub b: wasmtime::component::StreamReader, + #[component(name = "c")] + pub c: wasmtime::component::StreamReader< + wasmtime::component::FutureReader< + wasmtime::component::StreamReader<()>, + >, + >, + #[component(name = "d")] + pub d: wasmtime::component::FutureReader< + wasmtime::component::StreamReader< + wasmtime::component::FutureReader<()>, + >, + >, + } + impl core::fmt::Debug for FuturesAndStreams { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("FuturesAndStreams") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .finish() + } + } + const _: () = { + assert!( + 16 == < FuturesAndStreams as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < FuturesAndStreams as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; pub trait HostWithStore: wasmtime::component::HasData + Send {} impl<_T: ?Sized> HostWithStore for _T where @@ -789,6 +831,51 @@ pub mod exports { >::ALIGN32 ); }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct FuturesAndStreams { + #[component(name = "a")] + pub a: wasmtime::component::FutureReader, + #[component(name = "b")] + pub b: wasmtime::component::StreamReader, + #[component(name = "c")] + pub c: wasmtime::component::StreamReader< + wasmtime::component::FutureReader< + wasmtime::component::StreamReader<()>, + >, + >, + #[component(name = "d")] + pub d: wasmtime::component::FutureReader< + wasmtime::component::StreamReader< + wasmtime::component::FutureReader<()>, + >, + >, + } + impl core::fmt::Debug for FuturesAndStreams { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("FuturesAndStreams") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .finish() + } + } + const _: () = { + assert!( + 16 == < FuturesAndStreams as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < FuturesAndStreams as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; #[derive(Clone)] pub struct Guest { tuple_arg: wasmtime::component::Func, diff --git a/crates/component-macro/tests/expanded/records_concurrent.rs b/crates/component-macro/tests/expanded/records_concurrent.rs index dd97f8e22d48..ced782224e65 100644 --- a/crates/component-macro/tests/expanded/records_concurrent.rs +++ b/crates/component-macro/tests/expanded/records_concurrent.rs @@ -346,6 +346,48 @@ pub mod foo { 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::ALIGN32 ); }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct FuturesAndStreams { + #[component(name = "a")] + pub a: wasmtime::component::FutureReader, + #[component(name = "b")] + pub b: wasmtime::component::StreamReader, + #[component(name = "c")] + pub c: wasmtime::component::StreamReader< + wasmtime::component::FutureReader< + wasmtime::component::StreamReader<()>, + >, + >, + #[component(name = "d")] + pub d: wasmtime::component::FutureReader< + wasmtime::component::StreamReader< + wasmtime::component::FutureReader<()>, + >, + >, + } + impl core::fmt::Debug for FuturesAndStreams { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("FuturesAndStreams") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .finish() + } + } + const _: () = { + assert!( + 16 == < FuturesAndStreams as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < FuturesAndStreams as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; pub trait HostWithStore: wasmtime::component::HasData + Send { fn tuple_arg( accessor: &wasmtime::component::Accessor, @@ -722,6 +764,51 @@ pub mod exports { >::ALIGN32 ); }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct FuturesAndStreams { + #[component(name = "a")] + pub a: wasmtime::component::FutureReader, + #[component(name = "b")] + pub b: wasmtime::component::StreamReader, + #[component(name = "c")] + pub c: wasmtime::component::StreamReader< + wasmtime::component::FutureReader< + wasmtime::component::StreamReader<()>, + >, + >, + #[component(name = "d")] + pub d: wasmtime::component::FutureReader< + wasmtime::component::StreamReader< + wasmtime::component::FutureReader<()>, + >, + >, + } + impl core::fmt::Debug for FuturesAndStreams { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("FuturesAndStreams") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .finish() + } + } + const _: () = { + assert!( + 16 == < FuturesAndStreams as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < FuturesAndStreams as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; #[derive(Clone)] pub struct Guest { tuple_arg: wasmtime::component::Func, diff --git a/crates/component-macro/tests/expanded/records_tracing_async.rs b/crates/component-macro/tests/expanded/records_tracing_async.rs index dd8846d8e947..f86158bbc621 100644 --- a/crates/component-macro/tests/expanded/records_tracing_async.rs +++ b/crates/component-macro/tests/expanded/records_tracing_async.rs @@ -346,6 +346,48 @@ pub mod foo { 4 == < TupleTypedef2 as wasmtime::component::ComponentType >::ALIGN32 ); }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct FuturesAndStreams { + #[component(name = "a")] + pub a: wasmtime::component::FutureReader, + #[component(name = "b")] + pub b: wasmtime::component::StreamReader, + #[component(name = "c")] + pub c: wasmtime::component::StreamReader< + wasmtime::component::FutureReader< + wasmtime::component::StreamReader<()>, + >, + >, + #[component(name = "d")] + pub d: wasmtime::component::FutureReader< + wasmtime::component::StreamReader< + wasmtime::component::FutureReader<()>, + >, + >, + } + impl core::fmt::Debug for FuturesAndStreams { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("FuturesAndStreams") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .finish() + } + } + const _: () = { + assert!( + 16 == < FuturesAndStreams as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < FuturesAndStreams as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; pub trait HostWithStore: wasmtime::component::HasData + Send {} impl<_T: ?Sized> HostWithStore for _T where @@ -950,6 +992,51 @@ pub mod exports { >::ALIGN32 ); }; + #[derive(wasmtime::component::ComponentType)] + #[derive(wasmtime::component::Lift)] + #[derive(wasmtime::component::Lower)] + #[component(record)] + pub struct FuturesAndStreams { + #[component(name = "a")] + pub a: wasmtime::component::FutureReader, + #[component(name = "b")] + pub b: wasmtime::component::StreamReader, + #[component(name = "c")] + pub c: wasmtime::component::StreamReader< + wasmtime::component::FutureReader< + wasmtime::component::StreamReader<()>, + >, + >, + #[component(name = "d")] + pub d: wasmtime::component::FutureReader< + wasmtime::component::StreamReader< + wasmtime::component::FutureReader<()>, + >, + >, + } + impl core::fmt::Debug for FuturesAndStreams { + fn fmt( + &self, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + f.debug_struct("FuturesAndStreams") + .field("a", &self.a) + .field("b", &self.b) + .field("c", &self.c) + .field("d", &self.d) + .finish() + } + } + const _: () = { + assert!( + 16 == < FuturesAndStreams as wasmtime::component::ComponentType + >::SIZE32 + ); + assert!( + 4 == < FuturesAndStreams as wasmtime::component::ComponentType + >::ALIGN32 + ); + }; #[derive(Clone)] pub struct Guest { tuple_arg: wasmtime::component::Func, diff --git a/crates/wit-bindgen/src/types.rs b/crates/wit-bindgen/src/types.rs index b06c2950f7ba..200d13a82f93 100644 --- a/crates/wit-bindgen/src/types.rs +++ b/crates/wit-bindgen/src/types.rs @@ -26,6 +26,9 @@ pub struct TypeInfo { /// Whether or not this type (transitively) has a handle. pub has_handle: bool, + + /// Whether or not this type (transitively) has a future or a stream. + pub has_future_or_stream: bool, } impl std::ops::BitOrAssign for TypeInfo { @@ -35,6 +38,7 @@ impl std::ops::BitOrAssign for TypeInfo { self.error |= rhs.error; self.has_list |= rhs.has_list; self.has_handle |= rhs.has_handle; + self.has_future_or_stream |= rhs.has_future_or_stream; } } @@ -153,6 +157,7 @@ impl Types { } TypeDefKind::Future(ty) | TypeDefKind::Stream(ty) => { info = self.optional_type_info(resolve, ty.as_ref()); + info.has_future_or_stream = true; } TypeDefKind::Handle(_) => info.has_handle = true, TypeDefKind::Resource => {} @@ -184,10 +189,10 @@ impl Types { impl TypeInfo { pub fn is_copy(&self) -> bool { - !self.has_list && !self.has_handle + !self.has_list && !self.has_handle && !self.has_future_or_stream } pub fn is_clone(&self) -> bool { - !self.has_handle + !self.has_handle && !self.has_future_or_stream } }