From f0c24bdd04d43b28fa84e586203246cd35c8c546 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Fri, 5 Jul 2024 16:46:39 +0200 Subject: [PATCH] chore(transport): document and test `Send` async rustc bug Signed-off-by: Roman Volosatovs --- Cargo.lock | 11 ++++-- Cargo.toml | 3 +- crates/transport/Cargo.toml | 3 +- crates/transport/src/lib.rs | 72 ++++++++++++++++++++++++++++++++++++- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 803201fd..0fc8372b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2077,6 +2077,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +[[package]] +name = "send-future" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224e328af6e080cddbab3c770b1cf50f0351ba0577091ef2410c3951d835ff87" + [[package]] name = "serde" version = "1.0.203" @@ -3671,7 +3677,7 @@ dependencies = [ [[package]] name = "wrpc" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "async-nats-wrpc", @@ -3735,11 +3741,12 @@ dependencies = [ [[package]] name = "wrpc-transport" -version = "0.26.1" +version = "0.26.2" dependencies = [ "anyhow", "bytes", "futures", + "send-future", "test-log", "tokio", "tokio-stream", diff --git a/Cargo.toml b/Cargo.toml index 83eb77f9..a8f682af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wrpc" -version = "0.2.0" +version = "0.2.1" description = "WebAssembly component-native RPC framework based on WIT" authors.workspace = true @@ -109,6 +109,7 @@ quote = { version = "1", default-features = false } rcgen = { version = "0.13", default-features = false } reqwest = { version = "0.11", default-features = false } rustls = { version = "0.23", default-features = false } +send-future = { version = "0.1", default-features = false } serde = { version = "1", default-features = false } serde_json = { version = "1", default-features = false } syn = { version = "2", default-features = false, features = ["printing"] } diff --git a/crates/transport/Cargo.toml b/crates/transport/Cargo.toml index fb28e9f4..730e0b69 100644 --- a/crates/transport/Cargo.toml +++ b/crates/transport/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wrpc-transport" -version = "0.26.1" +version = "0.26.2" description = "wRPC core transport functionality" authors.workspace = true @@ -24,6 +24,7 @@ tokio = { workspace = true, features = ["macros", "rt"] } tokio-stream = { workspace = true } tokio-util = { workspace = true, features = ["codec", "io"] } tracing = { workspace = true, features = ["attributes"] } +send-future = { workspace = true } wasm-tokio = { workspace = true, features = ["tracing"] } [dev-dependencies] diff --git a/crates/transport/src/lib.rs b/crates/transport/src/lib.rs index ab055a84..2d8ff4f7 100644 --- a/crates/transport/src/lib.rs +++ b/crates/transport/src/lib.rs @@ -7,6 +7,7 @@ mod value; #[cfg(feature = "frame")] pub use frame::{Decoder as FrameDecoder, Encoder as FrameEncoder, FrameRef}; +pub use send_future::SendFuture; pub use value::*; use core::future::Future; @@ -48,6 +49,45 @@ pub trait Invoke: Send + Sync + 'static { type Incoming: AsyncRead + Index + Send + Sync + Unpin + 'static; /// Invoke function `func` on instance `instance` + /// + /// Note, that compilation of code calling methods on [`Invoke`] implementations within [`Send`] async functions + /// may fail with hard-to-debug errors due to a compiler bug: + /// [https://github.com/rust-lang/rust/issues/96865](https://github.com/rust-lang/rust/issues/96865) + /// + /// The following fails to compile with rustc 1.78.0: + /// + /// ```compile_fail + /// use core::future::Future; + /// + /// fn invoke_send() -> impl Future> + Send + /// where + /// T: wrpc_transport::Invoke + Default, + /// { + /// async { T::default().invoke((), "compiler-bug", "free", "since".into(), [[Some(2024)].as_slice(); 0]).await } + /// } + /// ``` + /// + /// ```text + /// async { T::default().invoke((), "compiler-bug", "free", "since".into(), [[Some(2024)].as_slice(); 0]).await } + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `AsRef` is not general enough + /// | + /// = note: `[&'0 [Option]; 0]` must implement `AsRef<[&'1 [Option]]>`, for any two lifetimes `'0` and `'1`... + /// = note: ...but it actually implements `AsRef<[&[Option]]>` + /// ``` + /// + /// The fix is to call [`send`](SendFuture::send) provided by [`send_future::SendFuture`], re-exported by this crate, on the future before awaiting: + /// ``` + /// use core::future::Future; + /// use wrpc_transport::SendFuture as _; + /// + /// fn invoke_send() -> impl Future> + Send + /// where + /// T: wrpc_transport::Invoke + Default, + /// { + /// async { T::default().invoke((), "compiler-bug", "free", "since".into(), [[Some(2024)].as_slice(); 0]).send().await } + /// } + /// ``` + fn invoke

( &self, cx: Self::Context, @@ -325,6 +365,36 @@ mod tests { use super::*; + fn invoke_values_send() -> impl Future< + Output = anyhow::Result<( + Pin< + Box< + dyn Stream + Send + Sync>>>> + + Send + + Sync, + >, + >, + )>, + > + Send + where + T: Invoke + Default, + { + async { + let wrpc = T::default(); + let ((r0,), _) = wrpc + .invoke_values( + (), + "wrpc-test:integration/async", + "with-streams", + (), + [[None].as_slice()], + ) + .send() + .await?; + Ok(r0) + } + } + async fn call_invoke( i: &T, cx: T::Context, @@ -333,7 +403,7 @@ mod tests { i.invoke(cx, "foo", "bar", Bytes::default(), &paths).await } - pub async fn call_invoke_async( + async fn call_invoke_async( ) -> anyhow::Result<(Pin + Send + Sync>>,)> where T: Invoke + Default,