Skip to content

Commit 410d0ce

Browse files
committed
Add Rust support for fallible resource constructors.
And C support came along for free. See: WebAssembly/component-model#550 C++, C# & Moonbit are not supported yet.
1 parent eaf42f6 commit 410d0ce

File tree

8 files changed

+161
-59
lines changed

8 files changed

+161
-59
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ prettyplease = "0.2.20"
3131
syn = { version = "2.0.89", features = ["printing"] }
3232
futures = "0.3.31"
3333

34-
wat = "1.236.0"
35-
wasmparser = "0.236.0"
36-
wasm-encoder = "0.236.0"
37-
wasm-metadata = { version = "0.236.0", default-features = false }
38-
wit-parser = "0.236.0"
39-
wit-component = "0.236.0"
40-
wasm-compose = "0.236.0"
34+
wat = "1.237.0"
35+
wasmparser = "0.237.0"
36+
wasm-encoder = "0.237.0"
37+
wasm-metadata = { version = "0.237.0", default-features = false }
38+
wit-parser = "0.237.0"
39+
wit-component = "0.237.0"
40+
wasm-compose = "0.237.0"
4141

4242
wit-bindgen-core = { path = 'crates/core', version = '0.44.0' }
4343
wit-bindgen-c = { path = 'crates/c', version = '0.44.0' }

crates/rust/src/bindgen.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::{int_repr, to_rust_ident, Identifier, InterfaceGenerator, RustFlagsRepr};
1+
use crate::{
2+
classify_constructor_return_type, int_repr, to_rust_ident, ConstructorReturnType, Identifier,
3+
InterfaceGenerator, RustFlagsRepr,
4+
};
25
use heck::*;
36
use std::fmt::Write as _;
47
use std::mem;
@@ -876,14 +879,24 @@ impl Bindgen for FunctionBindgen<'_, '_> {
876879
None
877880
}
878881
FunctionKind::Constructor(ty) => {
882+
let return_type =
883+
classify_constructor_return_type(resolve, *ty, &func.result);
879884
let ty = resolve.types[*ty]
880885
.name
881886
.as_deref()
882887
.unwrap()
883888
.to_upper_camel_case();
884-
let call = format!("{ty}::new(T::new");
885-
self.push_str(&call);
886-
Some(ty)
889+
890+
match return_type {
891+
ConstructorReturnType::Self_ => {
892+
self.push_str(&format!("{ty}::new(T::new"));
893+
}
894+
ConstructorReturnType::Result { .. } => {
895+
self.push_str(&format!("T::new"));
896+
}
897+
}
898+
899+
Some((ty, return_type))
887900
}
888901
};
889902
self.push_str("(");
@@ -908,8 +921,12 @@ impl Bindgen for FunctionBindgen<'_, '_> {
908921
if *async_ {
909922
self.push_str(".await");
910923
}
911-
if constructor_type.is_some() {
912-
self.push_str(")");
924+
match constructor_type {
925+
Some((_, ConstructorReturnType::Self_)) => self.push_str(")"),
926+
Some((ty, ConstructorReturnType::Result { .. })) => {
927+
self.push_str(&format!(".map({ty}::new)"))
928+
}
929+
None => {}
913930
}
914931
self.push_str("\n};\n");
915932
}

crates/rust/src/interface.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::bindgen::{FunctionBindgen, POINTER_SIZE_EXPRESSION};
22
use crate::{
3-
full_wit_type_name, int_repr, to_rust_ident, to_upper_camel_case, wasm_type, FnSig, Identifier,
4-
InterfaceName, Ownership, RuntimeItem, RustFlagsRepr, RustWasm, TypeGeneration,
3+
classify_constructor_return_type, full_wit_type_name, int_repr, to_rust_ident,
4+
to_upper_camel_case, wasm_type, ConstructorReturnType, FnSig, Identifier, InterfaceName,
5+
Ownership, RuntimeItem, RustFlagsRepr, RustWasm, TypeGeneration,
56
};
67
use anyhow::Result;
78
use heck::*;
@@ -1363,10 +1364,20 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{
13631364

13641365
fn print_signature(&mut self, func: &Function, params_owned: bool, sig: &FnSig) -> Vec<String> {
13651366
let params = self.print_docs_and_params(func, params_owned, sig);
1366-
if let FunctionKind::Constructor(_) = &func.kind {
1367-
self.push_str(" -> Self")
1367+
self.push_str(" -> ");
1368+
if let FunctionKind::Constructor(resource_id) = &func.kind {
1369+
match classify_constructor_return_type(&self.resolve, *resource_id, &func.result) {
1370+
ConstructorReturnType::Self_ => {
1371+
self.push_str("Self");
1372+
}
1373+
ConstructorReturnType::Result { err } => {
1374+
self.push_str("Result<Self, ");
1375+
self.print_result_type(&err);
1376+
self.push_str("> where Self: Sized");
1377+
}
1378+
}
13681379
} else {
1369-
self.print_results(&func.result);
1380+
self.print_result_type(&func.result);
13701381
}
13711382
params
13721383
}
@@ -1482,9 +1493,7 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{
14821493
params
14831494
}
14841495

1485-
fn print_results(&mut self, result: &Option<Type>) {
1486-
self.push_str(" -> ");
1487-
1496+
fn print_result_type(&mut self, result: &Option<Type>) {
14881497
match result {
14891498
None => {
14901499
self.push_str("()");

crates/rust/src/lib.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,3 +1787,63 @@ fn full_wit_type_name(resolve: &Resolve, id: TypeId) -> String {
17871787
None => type_def.name.clone().unwrap(),
17881788
}
17891789
}
1790+
1791+
enum ConstructorReturnType {
1792+
/// Resource constructor is infallible. E.g.:
1793+
/// ```wit
1794+
/// resource R {
1795+
/// constructor(..);
1796+
/// }
1797+
/// ```
1798+
Self_,
1799+
1800+
/// Resource constructor is fallible. E.g.:
1801+
/// ```wit
1802+
/// resource R {
1803+
/// constructor(..) -> result<R, err>;
1804+
/// }
1805+
/// ```
1806+
Result { err: Option<Type> },
1807+
}
1808+
1809+
fn classify_constructor_return_type(
1810+
resolve: &Resolve,
1811+
resource_id: TypeId,
1812+
result: &Option<Type>,
1813+
) -> ConstructorReturnType {
1814+
fn classify(
1815+
resolve: &Resolve,
1816+
resource_id: TypeId,
1817+
result: &Option<Type>,
1818+
) -> Option<ConstructorReturnType> {
1819+
let resource_id = dealias(resolve, resource_id);
1820+
let typedef = match result.as_ref()? {
1821+
Type::Id(id) => &resolve.types[dealias(resolve, *id)],
1822+
_ => return None,
1823+
};
1824+
1825+
match &typedef.kind {
1826+
TypeDefKind::Handle(Handle::Own(id)) if dealias(resolve, *id) == resource_id => {
1827+
Some(ConstructorReturnType::Self_)
1828+
}
1829+
TypeDefKind::Result(Result_ { ok, err }) => {
1830+
let ok_typedef = match ok.as_ref()? {
1831+
Type::Id(id) => &resolve.types[dealias(resolve, *id)],
1832+
_ => return None,
1833+
};
1834+
1835+
match &ok_typedef.kind {
1836+
TypeDefKind::Handle(Handle::Own(id))
1837+
if dealias(resolve, *id) == resource_id =>
1838+
{
1839+
Some(ConstructorReturnType::Result { err: *err })
1840+
}
1841+
_ => None,
1842+
}
1843+
}
1844+
_ => None,
1845+
}
1846+
}
1847+
1848+
classify(resolve, resource_id, result).expect("invalid constructor")
1849+
}

0 commit comments

Comments
 (0)