Skip to content

Commit

Permalink
add async: true case to Rust codegen_tests
Browse files Browse the repository at this point in the history
This ensures that all the codegen test WIT files produce compile-able bindings
with `async: true` (i.e. all imports lowered and all exports lifted using the
async ABI).  That revealed some issues involving resource methods and
constructors, as well as missing stub support, which I've resolved.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>
  • Loading branch information
dicej committed Nov 19, 2024
1 parent 8ebfc1e commit 2f37f79
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 19 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/core/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
// `self.return_pointer`) so we use that to read
// the result of the function from memory.
AbiVariant::GuestImport => {
assert!(sig.results.is_empty());
assert!(sig.results.is_empty() || self.async_);
self.return_pointer.take().unwrap()
}

Expand Down
3 changes: 3 additions & 0 deletions crates/guest-rust/rt/src/async_support.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![deny(missing_docs)]
#![allow(static_mut_refs)]

use {
futures::{
Expand All @@ -21,6 +22,8 @@ use {
},
};

pub use futures;

type BoxFuture = Pin<Box<dyn Future<Output = ()> + 'static>>;

/// Represents a task created by either a call to an async-lifted export or a
Expand Down
1 change: 1 addition & 0 deletions crates/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ prettyplease = { workspace = true }

[dev-dependencies]
wit-bindgen = { path = '../guest-rust' }
wit-bindgen-rt = { path = '../guest-rust/rt' }
test-helpers = { path = '../test-helpers' }
# For use with the custom attributes test
serde = { version = "1.0", features = ["derive"] }
Expand Down
35 changes: 23 additions & 12 deletions crates/rust/src/bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -859,24 +859,31 @@ impl Bindgen for FunctionBindgen<'_, '_> {
} else {
self.let_results(func.results.len(), results);
};
match &func.kind {
let constructor_type = match &func.kind {
FunctionKind::Freestanding => {
self.push_str(&format!("T::{}", to_rust_ident(&func.name)));
None
}
FunctionKind::Method(_) | FunctionKind::Static(_) => {
self.push_str(&format!("T::{}", to_rust_ident(func.item_name())));
None
}
FunctionKind::Constructor(ty) => {
self.push_str(&format!(
"{}::new(T::new",
resolve.types[*ty]
.name
.as_deref()
.unwrap()
.to_upper_camel_case()
));
let ty = resolve.types[*ty]
.name
.as_deref()
.unwrap()
.to_upper_camel_case();
let call = if self.async_ {
let async_support = self.gen.path_to_async_support();
format!("{async_support}::futures::FutureExt::map(T::new")
} else {
format!("{ty}::new(T::new",)
};
self.push_str(&call);
Some(ty)
}
}
};
self.push_str("(");
for (i, operand) in operands.iter().enumerate() {
if i > 0 {
Expand All @@ -893,8 +900,12 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}
}
self.push_str(")");
if let FunctionKind::Constructor(_) = &func.kind {
self.push_str(")");
if let Some(ty) = constructor_type {
self.push_str(&if self.async_ {
format!(", {ty}::new)")
} else {
")".into()
});
}
self.push_str(";\n");
}
Expand Down
33 changes: 28 additions & 5 deletions crates/rust/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ impl {stream_and_future_support}::StreamPayload for {name} {{

let resource_methods = funcs.remove(&Some(*id)).unwrap_or(Vec::new());
let trait_name = format!("{path}::Guest{camel}");
self.generate_stub_impl(&trait_name, "", &resource_methods);
self.generate_stub_impl(&trait_name, "", &resource_methods, interface);
}
format!("{path}::Guest")
}
Expand All @@ -1180,7 +1180,7 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
};

if !root_methods.is_empty() || !extra_trait_items.is_empty() {
self.generate_stub_impl(&guest_trait, &extra_trait_items, &root_methods);
self.generate_stub_impl(&guest_trait, &extra_trait_items, &root_methods, interface);
}
}

Expand All @@ -1189,6 +1189,7 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
trait_name: &str,
extra_trait_items: &str,
funcs: &[&Function],
interface: Option<(InterfaceId, &WorldKey)>,
) {
uwriteln!(self.src, "impl {trait_name} for Stub {{");
self.src.push_str(extra_trait_items);
Expand All @@ -1197,7 +1198,19 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
if self.gen.skip.contains(&func.name) {
continue;
}
let async_ = match &self.gen.opts.async_ {
AsyncConfig::None => false,
AsyncConfig::All => true,
AsyncConfig::Some { exports, .. } => {
exports.contains(&if let Some((_, key)) = interface {
format!("{}#{}", self.resolve.name_world_key(key), func.name)
} else {
func.name.clone()
})
}
};
let mut sig = FnSig {
async_,
use_item_name: true,
private: true,
..Default::default()
Expand All @@ -1206,8 +1219,14 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
sig.self_arg = Some("&self".into());
sig.self_is_first_param = true;
}
self.print_signature(func, true, &sig, true);
self.src.push_str("{ unreachable!() }\n");
self.print_signature(func, true, &sig, false);
let call = if async_ {
let async_support = self.path_to_async_support();
format!("{{ #[allow(unreachable_code)]{async_support}::futures::future::ready(unreachable!()) }}\n")
} else {
"{ unreachable!() }\n".into()
};
self.src.push_str(&call);
}

self.src.push_str("}\n");
Expand Down Expand Up @@ -1273,7 +1292,11 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
) -> Vec<String> {
let params = self.print_docs_and_params(func, params_owned, sig, use_async_sugar);
if let FunctionKind::Constructor(_) = &func.kind {
self.push_str(" -> Self")
self.push_str(if sig.async_ && !use_async_sugar {
" -> impl ::core::future::Future<Output = Self>"
} else {
" -> Self"
})
} else {
self.print_results(&func.results, sig.async_ && !use_async_sugar);
}
Expand Down
14 changes: 14 additions & 0 deletions crates/rust/tests/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ mod codegen_tests {
#[test]
fn works() {}
}

#[cfg(feature = "async")]
mod async_ {
wit_bindgen::generate!({
path: $test,
stubs,
export_prefix: "[async]",
generate_all,
async: true
});

#[test]
fn works() {}
}
}

};
Expand Down

0 comments on commit 2f37f79

Please sign in to comment.