Skip to content

Commit 9479bb4

Browse files
committed
Add support for WIT fallible constructors
This implements: WebAssembly/component-model#550
1 parent c2fc211 commit 9479bb4

File tree

36 files changed

+621
-164
lines changed

36 files changed

+621
-164
lines changed

crates/wasmparser/src/validator/component.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4410,8 +4410,9 @@ impl ComponentNameContext {
44104410
| ComponentNameKind::Dependency(_)
44114411
| ComponentNameKind::Hash(_) => {}
44124412

4413-
// Constructors must return `(own $resource)` and the `$resource`
4414-
// must be named within this context to match `rname`
4413+
// Constructors must return `(own $resource)` or
4414+
// `(result (own $Tresource))` and the `$resource` must be named
4415+
// within this context to match `rname`.
44154416
ComponentNameKind::Constructor(rname) => {
44164417
let ty = func()?;
44174418
let ty = match ty.result {
@@ -4422,12 +4423,19 @@ impl ComponentNameContext {
44224423
ComponentValType::Primitive(_) => None,
44234424
ComponentValType::Type(ty) => match &types[ty] {
44244425
ComponentDefinedType::Own(id) => Some(id),
4426+
ComponentDefinedType::Result {
4427+
ok: Some(ComponentValType::Type(ok)),
4428+
..
4429+
} => match &types[*ok] {
4430+
ComponentDefinedType::Own(id) => Some(id),
4431+
_ => None,
4432+
},
44254433
_ => None,
44264434
},
44274435
};
44284436
let resource = match resource {
44294437
Some(id) => id,
4430-
None => bail!(offset, "function should return `(own $T)`"),
4438+
None => bail!(offset, "function should return `(own $T)` or `(result (own $T))`"),
44314439
};
44324440
self.validate_resource_name(*resource, rname, offset)?;
44334441
}

crates/wit-component/src/printing.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,15 +371,30 @@ impl<O: Output> WitPrinter<O> {
371371
}
372372
self.output.str(")");
373373

374-
// constructors don't have their results printed
375-
if let FunctionKind::Constructor(_) = func.kind {
376-
return Ok(());
374+
self.print_function_result(resolve, func)?;
375+
376+
Ok(())
377+
}
378+
379+
fn print_function_result(&mut self, resolve: &Resolve, func: &Function) -> Result<()> {
380+
// Don't print the return type for constructors that return an owned
381+
// handle of themselves:
382+
if let FunctionKind::Constructor(containing_res_id) = func.kind {
383+
if let Some(Type::Id(id)) = &func.result {
384+
let ty = &resolve.types[*id];
385+
if let TypeDefKind::Handle(Handle::Own(return_res_id)) = ty.kind {
386+
if containing_res_id == return_res_id {
387+
return Ok(());
388+
}
389+
}
390+
}
377391
}
378392

379393
if let Some(ty) = &func.result {
380394
self.output.str(" -> ");
381395
self.print_type_name(resolve, ty)?;
382396
}
397+
383398
Ok(())
384399
}
385400

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
(component
2+
(type (;0;)
3+
(instance
4+
(export (;0;) "a" (type (sub resource)))
5+
(type (;1;) (own 0))
6+
(type (;2;) (result 1 (error u32)))
7+
(type (;3;) (func (result 2)))
8+
(export (;0;) "[constructor]a" (func (type 3)))
9+
)
10+
)
11+
(import "foo" (instance (;0;) (type 0)))
12+
(core module (;0;)
13+
(type (;0;) (func (param i32)))
14+
(import "foo" "[constructor]a" (func (;0;) (type 0)))
15+
(import "foo" "[resource-drop]a" (func (;1;) (type 0)))
16+
(memory (;0;) 1)
17+
(export "memory" (memory 0))
18+
(@producers
19+
(processed-by "wit-component" "$CARGO_PKG_VERSION")
20+
(processed-by "my-fake-bindgen" "123.45")
21+
)
22+
)
23+
(core module (;1;)
24+
(type (;0;) (func (param i32)))
25+
(table (;0;) 1 1 funcref)
26+
(export "0" (func $"indirect-foo-[constructor]a"))
27+
(export "$imports" (table 0))
28+
(func $"indirect-foo-[constructor]a" (;0;) (type 0) (param i32)
29+
local.get 0
30+
i32.const 0
31+
call_indirect (type 0)
32+
)
33+
(@producers
34+
(processed-by "wit-component" "$CARGO_PKG_VERSION")
35+
)
36+
)
37+
(core module (;2;)
38+
(type (;0;) (func (param i32)))
39+
(import "" "0" (func (;0;) (type 0)))
40+
(import "" "$imports" (table (;0;) 1 1 funcref))
41+
(elem (;0;) (i32.const 0) func 0)
42+
(@producers
43+
(processed-by "wit-component" "$CARGO_PKG_VERSION")
44+
)
45+
)
46+
(core instance (;0;) (instantiate 1))
47+
(alias core export 0 "0" (core func (;0;)))
48+
(alias export 0 "a" (type (;1;)))
49+
(core func (;1;) (canon resource.drop 1))
50+
(core instance (;1;)
51+
(export "[constructor]a" (func 0))
52+
(export "[resource-drop]a" (func 1))
53+
)
54+
(core instance (;2;) (instantiate 0
55+
(with "foo" (instance 1))
56+
)
57+
)
58+
(alias core export 2 "memory" (core memory (;0;)))
59+
(alias core export 0 "$imports" (core table (;0;)))
60+
(alias export 0 "[constructor]a" (func (;0;)))
61+
(core func (;2;) (canon lower (func 0) (memory 0)))
62+
(core instance (;3;)
63+
(export "$imports" (table 0))
64+
(export "0" (func 2))
65+
)
66+
(core instance (;4;) (instantiate 2
67+
(with "" (instance 3))
68+
)
69+
)
70+
(@producers
71+
(processed-by "wit-component" "$CARGO_PKG_VERSION")
72+
)
73+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package root:component;
2+
3+
world root {
4+
import foo: interface {
5+
resource a {
6+
constructor() -> result<a, u32>;
7+
}
8+
}
9+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(module
2+
(import "foo" "[constructor]a" (func (param i32)))
3+
(import "foo" "[resource-drop]a" (func (param i32)))
4+
(memory (export "memory") 1)
5+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package foo:bar;
2+
3+
world module {
4+
import foo: interface {
5+
resource a {
6+
constructor() -> result<a, u32>;
7+
}
8+
}
9+
}

crates/wit-component/tests/interfaces/resources.wat

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,18 @@
152152
(alias export 2 "bar" (type (;5;)))
153153
(import "bar" (type (;6;) (eq 5)))
154154
(import "a" (type (;7;) (sub resource)))
155-
(type (;8;) (own 7))
156-
(type (;9;) (func (result 8)))
157-
(import "[constructor]a" (func (;0;) (type 9)))
158-
(export (;1;) "x" (func (type 9)))
159-
(type (;10;) (own 6))
160-
(type (;11;) (func (result 10)))
161-
(export (;2;) "y" (func (type 11)))
155+
(import "b" (type (;8;) (sub resource)))
156+
(type (;9;) (own 7))
157+
(type (;10;) (func (result 9)))
158+
(import "[constructor]a" (func (;0;) (type 10)))
159+
(type (;11;) (own 8))
160+
(type (;12;) (result 11 (error string)))
161+
(type (;13;) (func (result 12)))
162+
(import "[constructor]b" (func (;1;) (type 13)))
163+
(export (;2;) "x" (func (type 10)))
164+
(type (;14;) (own 6))
165+
(type (;15;) (func (result 14)))
166+
(export (;3;) "y" (func (type 15)))
162167
)
163168
)
164169
(export (;0;) "foo:bar/some-world" (component (type 0)))

crates/wit-component/tests/interfaces/resources.wit

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ world some-world {
2727
constructor();
2828
}
2929

30+
resource b {
31+
constructor() -> result<b, string>;
32+
}
33+
3034
export x: func() -> a;
3135
export y: func() -> bar;
3236

crates/wit-component/tests/interfaces/resources.wit.print

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ world some-world {
6565
constructor();
6666
}
6767

68+
resource b {
69+
constructor() -> result<b, string>;
70+
}
71+
6872
export x: func() -> a;
6973
export y: func() -> bar;
7074
}

crates/wit-encoder/src/from_parser.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -397,8 +397,6 @@ impl<'a> Converter<'a> {
397397
) -> Option<ResourceFunc> {
398398
// skip first argument for methods, as they're just `self`.
399399
let mut skip_first_param = false;
400-
// constructors can't return anything
401-
let mut with_returns = true;
402400
let mut method = match func.kind {
403401
wit_parser::FunctionKind::Freestanding
404402
| wit_parser::FunctionKind::AsyncFreestanding => return None,
@@ -423,7 +421,6 @@ impl<'a> Converter<'a> {
423421
if id != resource_id {
424422
return None;
425423
}
426-
with_returns = false;
427424
ResourceFunc::constructor()
428425
}
429426
};
@@ -433,9 +430,7 @@ impl<'a> Converter<'a> {
433430
method.params_mut().items_mut().remove(0);
434431
}
435432

436-
if with_returns {
437-
method.set_result(func.result.as_ref().map(|ty| self.convert_type(ty)));
438-
}
433+
method.set_result(func.result.as_ref().map(|ty| self.convert_type(ty)));
439434

440435
Some(method)
441436
}

0 commit comments

Comments
 (0)