Skip to content

Commit 2f37f79

Browse files
committed
add async: true case to Rust codegen_tests
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>
1 parent 8ebfc1e commit 2f37f79

File tree

7 files changed

+72
-19
lines changed

7 files changed

+72
-19
lines changed

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/core/src/abi.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
10301030
// `self.return_pointer`) so we use that to read
10311031
// the result of the function from memory.
10321032
AbiVariant::GuestImport => {
1033-
assert!(sig.results.is_empty());
1033+
assert!(sig.results.is_empty() || self.async_);
10341034
self.return_pointer.take().unwrap()
10351035
}
10361036

crates/guest-rust/rt/src/async_support.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![deny(missing_docs)]
2+
#![allow(static_mut_refs)]
23

34
use {
45
futures::{
@@ -21,6 +22,8 @@ use {
2122
},
2223
};
2324

25+
pub use futures;
26+
2427
type BoxFuture = Pin<Box<dyn Future<Output = ()> + 'static>>;
2528

2629
/// Represents a task created by either a call to an async-lifted export or a

crates/rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ prettyplease = { workspace = true }
2828

2929
[dev-dependencies]
3030
wit-bindgen = { path = '../guest-rust' }
31+
wit-bindgen-rt = { path = '../guest-rust/rt' }
3132
test-helpers = { path = '../test-helpers' }
3233
# For use with the custom attributes test
3334
serde = { version = "1.0", features = ["derive"] }

crates/rust/src/bindgen.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -859,24 +859,31 @@ impl Bindgen for FunctionBindgen<'_, '_> {
859859
} else {
860860
self.let_results(func.results.len(), results);
861861
};
862-
match &func.kind {
862+
let constructor_type = match &func.kind {
863863
FunctionKind::Freestanding => {
864864
self.push_str(&format!("T::{}", to_rust_ident(&func.name)));
865+
None
865866
}
866867
FunctionKind::Method(_) | FunctionKind::Static(_) => {
867868
self.push_str(&format!("T::{}", to_rust_ident(func.item_name())));
869+
None
868870
}
869871
FunctionKind::Constructor(ty) => {
870-
self.push_str(&format!(
871-
"{}::new(T::new",
872-
resolve.types[*ty]
873-
.name
874-
.as_deref()
875-
.unwrap()
876-
.to_upper_camel_case()
877-
));
872+
let ty = resolve.types[*ty]
873+
.name
874+
.as_deref()
875+
.unwrap()
876+
.to_upper_camel_case();
877+
let call = if self.async_ {
878+
let async_support = self.gen.path_to_async_support();
879+
format!("{async_support}::futures::FutureExt::map(T::new")
880+
} else {
881+
format!("{ty}::new(T::new",)
882+
};
883+
self.push_str(&call);
884+
Some(ty)
878885
}
879-
}
886+
};
880887
self.push_str("(");
881888
for (i, operand) in operands.iter().enumerate() {
882889
if i > 0 {
@@ -893,8 +900,12 @@ impl Bindgen for FunctionBindgen<'_, '_> {
893900
}
894901
}
895902
self.push_str(")");
896-
if let FunctionKind::Constructor(_) = &func.kind {
897-
self.push_str(")");
903+
if let Some(ty) = constructor_type {
904+
self.push_str(&if self.async_ {
905+
format!(", {ty}::new)")
906+
} else {
907+
")".into()
908+
});
898909
}
899910
self.push_str(";\n");
900911
}

crates/rust/src/interface.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,7 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
11691169

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

11821182
if !root_methods.is_empty() || !extra_trait_items.is_empty() {
1183-
self.generate_stub_impl(&guest_trait, &extra_trait_items, &root_methods);
1183+
self.generate_stub_impl(&guest_trait, &extra_trait_items, &root_methods, interface);
11841184
}
11851185
}
11861186

@@ -1189,6 +1189,7 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
11891189
trait_name: &str,
11901190
extra_trait_items: &str,
11911191
funcs: &[&Function],
1192+
interface: Option<(InterfaceId, &WorldKey)>,
11921193
) {
11931194
uwriteln!(self.src, "impl {trait_name} for Stub {{");
11941195
self.src.push_str(extra_trait_items);
@@ -1197,7 +1198,19 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
11971198
if self.gen.skip.contains(&func.name) {
11981199
continue;
11991200
}
1201+
let async_ = match &self.gen.opts.async_ {
1202+
AsyncConfig::None => false,
1203+
AsyncConfig::All => true,
1204+
AsyncConfig::Some { exports, .. } => {
1205+
exports.contains(&if let Some((_, key)) = interface {
1206+
format!("{}#{}", self.resolve.name_world_key(key), func.name)
1207+
} else {
1208+
func.name.clone()
1209+
})
1210+
}
1211+
};
12001212
let mut sig = FnSig {
1213+
async_,
12011214
use_item_name: true,
12021215
private: true,
12031216
..Default::default()
@@ -1206,8 +1219,14 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
12061219
sig.self_arg = Some("&self".into());
12071220
sig.self_is_first_param = true;
12081221
}
1209-
self.print_signature(func, true, &sig, true);
1210-
self.src.push_str("{ unreachable!() }\n");
1222+
self.print_signature(func, true, &sig, false);
1223+
let call = if async_ {
1224+
let async_support = self.path_to_async_support();
1225+
format!("{{ #[allow(unreachable_code)]{async_support}::futures::future::ready(unreachable!()) }}\n")
1226+
} else {
1227+
"{ unreachable!() }\n".into()
1228+
};
1229+
self.src.push_str(&call);
12111230
}
12121231

12131232
self.src.push_str("}\n");
@@ -1273,7 +1292,11 @@ impl {stream_and_future_support}::StreamPayload for {name} {{
12731292
) -> Vec<String> {
12741293
let params = self.print_docs_and_params(func, params_owned, sig, use_async_sugar);
12751294
if let FunctionKind::Constructor(_) = &func.kind {
1276-
self.push_str(" -> Self")
1295+
self.push_str(if sig.async_ && !use_async_sugar {
1296+
" -> impl ::core::future::Future<Output = Self>"
1297+
} else {
1298+
" -> Self"
1299+
})
12771300
} else {
12781301
self.print_results(&func.results, sig.async_ && !use_async_sugar);
12791302
}

crates/rust/tests/codegen.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ mod codegen_tests {
5151
#[test]
5252
fn works() {}
5353
}
54+
55+
#[cfg(feature = "async")]
56+
mod async_ {
57+
wit_bindgen::generate!({
58+
path: $test,
59+
stubs,
60+
export_prefix: "[async]",
61+
generate_all,
62+
async: true
63+
});
64+
65+
#[test]
66+
fn works() {}
67+
}
5468
}
5569

5670
};

0 commit comments

Comments
 (0)