Skip to content

Commit 5a7561e

Browse files
committed
Add support for async/streams/futures to Rust generator
This adds support for generating bindings which use the [Async ABI](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Async.md) along with the [`stream`, `future`, and `error-context`](WebAssembly/component-model#405) types. By default, normal synchronous bindings are generated, but the user may opt-in to async bindings for all or some of the imported and/or exported functions in the target world and interfaces -- provided the default-enabled `async` feature is enabled. In addition, we generate `StreamPayload` and/or `FuturePayload` trait implementations for any types appearing as the `T` in `stream<T>` or `future<T>` in the WIT files, respectively. That enables user code to call `new_stream` or `new_future` to create `stream`s or `future`s with those payload types, then write to them, read from them, and/or pass the readable end as a parameter to a component import or return value of a component export. Note that I've added new `core::abi::Instruction` enum variants to handle async lifting and lowering, but they're currently tailored to the Rust generator and will probably change somewhat as we add support for other languages. This does not include any new tests; I'll add those in a follow-up commit. Signed-off-by: Joel Dice <[email protected]> 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 <[email protected]> add codegen tests for futures, streams, and error-contexts Signed-off-by: Joel Dice <[email protected]>
1 parent 563956d commit 5a7561e

38 files changed

+2954
-275
lines changed

Cargo.toml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ clap = { version = "4.3.19", features = ["derive"] }
3131
indexmap = "2.0.0"
3232
prettyplease = "0.2.20"
3333
syn = { version = "2.0", features = ["printing"] }
34+
futures = "0.3.31"
3435

35-
wasmparser = "0.220.0"
36-
wasm-encoder = "0.220.0"
37-
wasm-metadata = "0.220.0"
38-
wit-parser = "0.220.0"
39-
wit-component = "0.220.0"
36+
wasmparser = { git = "https://github.com/dicej/wasm-tools", branch = "async" }
37+
wasm-encoder = { git = "https://github.com/dicej/wasm-tools", branch = "async" }
38+
wasm-metadata = { git = "https://github.com/dicej/wasm-tools", branch = "async" }
39+
wit-parser = { git = "https://github.com/dicej/wasm-tools", branch = "async" }
40+
wit-component = { git = "https://github.com/dicej/wasm-tools", branch = "async" }
4041

4142
wit-bindgen-core = { path = 'crates/core', version = '0.35.0' }
4243
wit-bindgen-c = { path = 'crates/c', version = '0.35.0' }
@@ -74,6 +75,7 @@ default = [
7475
'go',
7576
'csharp',
7677
'moonbit',
78+
'async',
7779
]
7880
c = ['dep:wit-bindgen-c']
7981
rust = ['dep:wit-bindgen-rust']
@@ -83,6 +85,7 @@ go = ['dep:wit-bindgen-go']
8385
csharp = ['dep:wit-bindgen-csharp']
8486
csharp-mono = ['csharp']
8587
moonbit = ['dep:wit-bindgen-moonbit']
88+
async = ["wit-bindgen-rust/async"]
8689

8790
[dev-dependencies]
8891
heck = { workspace = true }

crates/c/src/lib.rs

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ fn is_prim_type_id(resolve: &Resolve, id: TypeId) -> bool {
718718
| TypeDefKind::Result(_)
719719
| TypeDefKind::Future(_)
720720
| TypeDefKind::Stream(_)
721+
| TypeDefKind::ErrorContext
721722
| TypeDefKind::Unknown => false,
722723
}
723724
}
@@ -779,8 +780,9 @@ pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) {
779780
src.push_str("list_");
780781
push_ty_name(resolve, ty, src);
781782
}
782-
TypeDefKind::Future(_) => unimplemented!(),
783-
TypeDefKind::Stream(_) => unimplemented!(),
783+
TypeDefKind::Future(_) => todo!(),
784+
TypeDefKind::Stream(_) => todo!(),
785+
TypeDefKind::ErrorContext => todo!(),
784786
TypeDefKind::Handle(Handle::Own(resource)) => {
785787
src.push_str("own_");
786788
push_ty_name(resolve, &Type::Id(*resource), src);
@@ -992,6 +994,7 @@ impl Return {
992994

993995
TypeDefKind::Future(_) => todo!("return_single for future"),
994996
TypeDefKind::Stream(_) => todo!("return_single for stream"),
997+
TypeDefKind::ErrorContext => todo!("return_single for error-context"),
995998
TypeDefKind::Resource => todo!("return_single for resource"),
996999
TypeDefKind::Unknown => unreachable!(),
9971000
}
@@ -1339,6 +1342,21 @@ void __wasm_export_{ns}_{snake}_dtor({ns}_{snake}_t* arg) {{
13391342
self.finish_typedef_struct(id);
13401343
}
13411344

1345+
fn type_future(&mut self, id: TypeId, name: &str, ty: &Option<Type>, docs: &Docs) {
1346+
_ = (id, name, ty, docs);
1347+
todo!()
1348+
}
1349+
1350+
fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) {
1351+
_ = (id, name, ty, docs);
1352+
todo!()
1353+
}
1354+
1355+
fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs) {
1356+
_ = (id, name, docs);
1357+
todo!()
1358+
}
1359+
13421360
fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) {
13431361
let _ = (id, name, ty, docs);
13441362
}
@@ -1427,12 +1445,16 @@ impl<'a> wit_bindgen_core::AnonymousTypeGenerator<'a> for InterfaceGenerator<'a>
14271445
todo!("print_anonymous_type for future");
14281446
}
14291447

1430-
fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Stream, _docs: &Docs) {
1448+
fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) {
14311449
todo!("print_anonymous_type for stream");
14321450
}
14331451

1434-
fn anonymous_typ_type(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) {
1435-
todo!("print_anonymous_type for typ");
1452+
fn anonymous_type_error_context(&mut self) {
1453+
todo!("print_anonymous_type for error-context");
1454+
}
1455+
1456+
fn anonymous_type_type(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) {
1457+
todo!("print_anonymous_type for type");
14361458
}
14371459
}
14381460

@@ -1605,6 +1627,7 @@ impl InterfaceGenerator<'_> {
16051627
}
16061628
TypeDefKind::Future(_) => todo!("print_dtor for future"),
16071629
TypeDefKind::Stream(_) => todo!("print_dtor for stream"),
1630+
TypeDefKind::ErrorContext => todo!("print_dtor for error-context"),
16081631
TypeDefKind::Resource => {}
16091632
TypeDefKind::Handle(Handle::Borrow(id) | Handle::Own(id)) => {
16101633
self.free(&Type::Id(*id), "*ptr");
@@ -1750,6 +1773,7 @@ impl InterfaceGenerator<'_> {
17501773
LiftLower::LowerArgsLiftResults,
17511774
func,
17521775
&mut f,
1776+
false,
17531777
);
17541778

17551779
let FunctionBindgen {
@@ -1822,6 +1846,7 @@ impl InterfaceGenerator<'_> {
18221846
LiftLower::LiftArgsLowerResults,
18231847
func,
18241848
&mut f,
1849+
false,
18251850
);
18261851
let FunctionBindgen { src, .. } = f;
18271852
self.src.c_adapters(&src);
@@ -1852,7 +1877,7 @@ impl InterfaceGenerator<'_> {
18521877

18531878
let mut f = FunctionBindgen::new(self, c_sig, &import_name);
18541879
f.params = params;
1855-
abi::post_return(f.gen.resolve, func, &mut f);
1880+
abi::post_return(f.gen.resolve, func, &mut f, false);
18561881
let FunctionBindgen { src, .. } = f;
18571882
self.src.c_fns(&src);
18581883
self.src.c_fns("}\n");
@@ -2075,17 +2100,8 @@ impl InterfaceGenerator<'_> {
20752100

20762101
TypeDefKind::List(ty) => self.contains_droppable_borrow(ty),
20772102

2078-
TypeDefKind::Future(r) => r
2079-
.as_ref()
2080-
.map_or(false, |ty| self.contains_droppable_borrow(ty)),
2081-
2082-
TypeDefKind::Stream(s) => {
2083-
s.element
2084-
.as_ref()
2085-
.map_or(false, |ty| self.contains_droppable_borrow(ty))
2086-
|| s.end
2087-
.as_ref()
2088-
.map_or(false, |ty| self.contains_droppable_borrow(ty))
2103+
TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => {
2104+
false
20892105
}
20902106

20912107
TypeDefKind::Type(ty) => self.contains_droppable_borrow(ty),
@@ -2753,7 +2769,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
27532769
self.src.push_str(");\n");
27542770
}
27552771

2756-
Instruction::CallInterface { func } => {
2772+
Instruction::CallInterface { func, .. } => {
27572773
let mut args = String::new();
27582774
for (i, (op, (byref, _))) in operands.iter().zip(&self.sig.params).enumerate() {
27592775
if i > 0 {
@@ -3037,6 +3053,10 @@ impl Bindgen for FunctionBindgen<'_, '_> {
30373053
uwriteln!(self.src, "}}");
30383054
}
30393055

3056+
Instruction::Flush { amt } => {
3057+
results.extend(operands.iter().take(*amt).map(|v| v.clone()));
3058+
}
3059+
30403060
i => unimplemented!("{:?}", i),
30413061
}
30423062
}
@@ -3145,6 +3165,7 @@ pub fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool {
31453165
TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true,
31463166
TypeDefKind::Future(_) => todo!("is_arg_by_pointer for future"),
31473167
TypeDefKind::Stream(_) => todo!("is_arg_by_pointer for stream"),
3168+
TypeDefKind::ErrorContext => todo!("is_arg_by_pointer for error-context"),
31483169
TypeDefKind::Resource => todo!("is_arg_by_pointer for resource"),
31493170
TypeDefKind::Unknown => unreachable!(),
31503171
},

crates/c/tests/codegen.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ use std::process::Command;
66
use wit_parser::{Resolve, UnresolvedPackageGroup};
77

88
macro_rules! codegen_test {
9+
// TODO: implement support for stream, future, and error-context, and then
10+
// remove these lines:
11+
(streams $name:tt $test:tt) => {};
12+
(futures $name:tt $test:tt) => {};
13+
(resources_with_streams $name:tt $test:tt) => {};
14+
(resources_with_futures $name:tt $test:tt) => {};
15+
(error_context $name:tt $test:tt) => {};
16+
917
($id:ident $name:tt $test:tt) => {
1018
#[test]
1119
fn $id() {

crates/core/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ doctest = false
1818
wit-parser = { workspace = true }
1919
anyhow = { workspace = true }
2020
heck = { workspace = true }
21+
22+
[features]
23+
default = ["async"]
24+
async = []

0 commit comments

Comments
 (0)