Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions crates/wasmparser/src/validator/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4410,8 +4410,9 @@ impl ComponentNameContext {
| ComponentNameKind::Dependency(_)
| ComponentNameKind::Hash(_) => {}

// Constructors must return `(own $resource)` and the `$resource`
// must be named within this context to match `rname`
// Constructors must return `(own $resource)` or
// `(result (own $Tresource))` and the `$resource` must be named
// within this context to match `rname`.
ComponentNameKind::Constructor(rname) => {
let ty = func()?;
let ty = match ty.result {
Expand All @@ -4422,12 +4423,22 @@ impl ComponentNameContext {
ComponentValType::Primitive(_) => None,
ComponentValType::Type(ty) => match &types[ty] {
ComponentDefinedType::Own(id) => Some(id),
ComponentDefinedType::Result {
ok: Some(ComponentValType::Type(ok)),
..
} => match &types[*ok] {
ComponentDefinedType::Own(id) => Some(id),
_ => None,
},
_ => None,
},
};
let resource = match resource {
Some(id) => id,
None => bail!(offset, "function should return `(own $T)`"),
None => bail!(
offset,
"function should return `(own $T)` or `(result (own $T))`"
),
};
self.validate_resource_name(*resource, rname, offset)?;
}
Expand Down
4 changes: 2 additions & 2 deletions crates/wit-component/src/printing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,8 @@ impl<O: Output> WitPrinter<O> {
}
self.output.str(")");

// constructors don't have their results printed
if let FunctionKind::Constructor(_) = func.kind {
// shorthand constructors don't have their results printed
if func.is_constructor_shorthand(resolve) {
return Ok(());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
(component
(type (;0;)
(instance
(export (;0;) "a" (type (sub resource)))
(type (;1;) (own 0))
(type (;2;) (result 1 (error u32)))
(type (;3;) (func (result 2)))
(export (;0;) "[constructor]a" (func (type 3)))
)
)
(import "foo" (instance (;0;) (type 0)))
(core module (;0;)
(type (;0;) (func (param i32)))
(import "foo" "[constructor]a" (func (;0;) (type 0)))
(import "foo" "[resource-drop]a" (func (;1;) (type 0)))
(memory (;0;) 1)
(export "memory" (memory 0))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module (;1;)
(type (;0;) (func (param i32)))
(table (;0;) 1 1 funcref)
(export "0" (func $"indirect-foo-[constructor]a"))
(export "$imports" (table 0))
(func $"indirect-foo-[constructor]a" (;0;) (type 0) (param i32)
local.get 0
i32.const 0
call_indirect (type 0)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module (;2;)
(type (;0;) (func (param i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "$imports" (table (;0;) 1 1 funcref))
(elem (;0;) (i32.const 0) func 0)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance (;0;) (instantiate 1))
(alias core export 0 "0" (core func (;0;)))
(alias export 0 "a" (type (;1;)))
(core func (;1;) (canon resource.drop 1))
(core instance (;1;)
(export "[constructor]a" (func 0))
(export "[resource-drop]a" (func 1))
)
(core instance (;2;) (instantiate 0
(with "foo" (instance 1))
)
)
(alias core export 2 "memory" (core memory (;0;)))
(alias core export 0 "$imports" (core table (;0;)))
(alias export 0 "[constructor]a" (func (;0;)))
(core func (;2;) (canon lower (func 0) (memory 0)))
(core instance (;3;)
(export "$imports" (table 0))
(export "0" (func 2))
)
(core instance (;4;) (instantiate 2
(with "" (instance 3))
)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package root:component;

world root {
import foo: interface {
resource a {
constructor() -> result<a, u32>;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(module
(import "foo" "[constructor]a" (func (param i32)))
(import "foo" "[resource-drop]a" (func (param i32)))
(memory (export "memory") 1)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package foo:bar;

world module {
import foo: interface {
resource a {
constructor() -> result<a, u32>;
}
}
}
19 changes: 12 additions & 7 deletions crates/wit-component/tests/interfaces/resources.wat
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,18 @@
(alias export 2 "bar" (type (;5;)))
(import "bar" (type (;6;) (eq 5)))
(import "a" (type (;7;) (sub resource)))
(type (;8;) (own 7))
(type (;9;) (func (result 8)))
(import "[constructor]a" (func (;0;) (type 9)))
(export (;1;) "x" (func (type 9)))
(type (;10;) (own 6))
(type (;11;) (func (result 10)))
(export (;2;) "y" (func (type 11)))
(import "b" (type (;8;) (sub resource)))
(type (;9;) (own 7))
(type (;10;) (func (result 9)))
(import "[constructor]a" (func (;0;) (type 10)))
(type (;11;) (own 8))
(type (;12;) (result 11 (error string)))
(type (;13;) (func (result 12)))
(import "[constructor]b" (func (;1;) (type 13)))
(export (;2;) "x" (func (type 10)))
(type (;14;) (own 6))
(type (;15;) (func (result 14)))
(export (;3;) "y" (func (type 15)))
)
)
(export (;0;) "foo:bar/some-world" (component (type 0)))
Expand Down
4 changes: 4 additions & 0 deletions crates/wit-component/tests/interfaces/resources.wit
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ world some-world {
constructor();
}

resource b {
constructor() -> result<b, string>;
}

export x: func() -> a;
export y: func() -> bar;

Expand Down
4 changes: 4 additions & 0 deletions crates/wit-component/tests/interfaces/resources.wit.print
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ world some-world {
constructor();
}

resource b {
constructor() -> result<b, string>;
}

export x: func() -> a;
export y: func() -> bar;
}
Expand Down
3 changes: 1 addition & 2 deletions crates/wit-encoder/src/from_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,6 @@ impl<'a> Converter<'a> {
) -> Option<ResourceFunc> {
// skip first argument for methods, as they're just `self`.
let mut skip_first_param = false;
// constructors can't return anything
let mut with_returns = true;
let mut method = match func.kind {
wit_parser::FunctionKind::Freestanding
Expand All @@ -423,7 +422,7 @@ impl<'a> Converter<'a> {
if id != resource_id {
return None;
}
with_returns = false;
with_returns = !func.is_constructor_shorthand(self.resolve);
ResourceFunc::constructor()
}
};
Expand Down
24 changes: 12 additions & 12 deletions crates/wit-encoder/src/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct ResourceFunc {
pub enum ResourceFuncKind {
Method(Ident, bool, Option<Type>),
Static(Ident, bool, Option<Type>),
Constructor,
Constructor(Option<Type>),
}

impl ResourceFunc {
Expand All @@ -62,7 +62,7 @@ impl ResourceFunc {

pub fn constructor() -> Self {
Self {
kind: ResourceFuncKind::Constructor,
kind: ResourceFuncKind::Constructor(None),
params: Params::empty(),
docs: None,
}
Expand All @@ -76,7 +76,7 @@ impl ResourceFunc {
ResourceFuncKind::Static(n, ..) => {
*n = name.into();
}
ResourceFuncKind::Constructor => panic!("constructors cannot have a name"),
ResourceFuncKind::Constructor(..) => panic!("constructors cannot have a name"),
}
}

Expand All @@ -97,22 +97,22 @@ impl ResourceFunc {
}

pub fn set_result(&mut self, result: Option<Type>) {
*self.result_mut().expect("constructors cannot have results") = result;
*self.result_mut() = result;
}

pub fn result(&self) -> Option<&Option<Type>> {
pub fn result(&self) -> &Option<Type> {
match &self.kind {
ResourceFuncKind::Method(.., result) => Some(result),
ResourceFuncKind::Static(.., result) => Some(result),
ResourceFuncKind::Constructor => None,
ResourceFuncKind::Method(.., result) => result,
ResourceFuncKind::Static(.., result) => result,
ResourceFuncKind::Constructor(result) => result,
}
}

pub fn result_mut(&mut self) -> Option<&mut Option<Type>> {
pub fn result_mut(&mut self) -> &mut Option<Type> {
match &mut self.kind {
ResourceFuncKind::Method(.., result) => Some(result),
ResourceFuncKind::Static(.., result) => Some(result),
ResourceFuncKind::Constructor => None,
ResourceFuncKind::Method(.., result) => result,
ResourceFuncKind::Static(.., result) => result,
ResourceFuncKind::Constructor(result) => result,
}
}

Expand Down
8 changes: 6 additions & 2 deletions crates/wit-encoder/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,12 @@ impl Render for TypeDef {
}
write!(f, ";\n")?;
}
crate::ResourceFuncKind::Constructor => {
write!(f, "{}constructor({});\n", opts.spaces(), func.params)?;
crate::ResourceFuncKind::Constructor(result) => {
write!(f, "{}constructor({})", opts.spaces(), func.params)?;
if let Some(ty) = result {
write!(f, " -> {ty}")?;
}
write!(f, ";\n")?;
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion crates/wit-parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,12 @@ impl<'a> ResourceFunc<'a> {
let ty = Type::parse(tokens)?;
Ok((name, ty))
})?;
let result = if tokens.eat(Token::RArrow)? {
let ty = Type::parse(tokens)?;
Some(ty)
} else {
None
};
tokens.expect_semicolon()?;
Ok(ResourceFunc::Constructor(NamedFunc {
docs,
Expand All @@ -821,7 +827,7 @@ impl<'a> ResourceFunc<'a> {
span,
async_: false,
params,
result: None,
result,
},
}))
}
Expand Down
52 changes: 46 additions & 6 deletions crates/wit-parser/src/ast/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1658,12 +1658,52 @@ impl<'a> Resolver<'a> {
None => Ok(None),
},

// Constructors are alwys parsed as 0 returned types but they're
// automatically translated as a single return type of the type that
// it's a constructor for.
FunctionKind::Constructor(id) => {
assert!(result.is_none());
Ok(Some(Type::Id(id)))
FunctionKind::Constructor(id) => match result {
// When constructors don't define a return type, they're
// implicitly assumed to return an owned handle to the type
// they construct.
None => Ok(Some(Type::Id(id))),

// If a constructor does define a return type, it must be in the
// form of `-> result<R, E?>` where `R` is the resource being
// constructed and `E` is an optional error type.
Some(ty) => Ok(Some(self.resolve_constructor_result(id, ty)?)),
},
}
}

fn resolve_constructor_result(
&mut self,
resource_id: TypeId,
result_ast: &ast::Type<'_>,
) -> Result<Type> {
let result = self.resolve_type(result_ast, &Stability::Unknown)?;
let ok_type = match result {
Type::Id(id) => match &self.types[id].kind {
TypeDefKind::Result(r) => Some(r.ok),
_ => None,
},
_ => None,
};
let Some(ok_type) = ok_type else {
bail!(Error::new(
result_ast.span(),
"if a constructor return type is declared it must be a `result`",
));
};
match ok_type {
Some(Type::Id(ok_id)) if resource_id == ok_id => Ok(result),
_ => {
let ok_span =
if let ast::Type::Result(ast::Result_ { ok: Some(ok), .. }) = result_ast {
ok.span()
} else {
result_ast.span()
};
bail!(Error::new(
ok_span,
"the `ok` type must be the resource being constructed",
));
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions crates/wit-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,24 @@ impl Function {
}
results
}

/// Check if this function is a resource constructor in shorthand form.
/// I.e. without an explicit return type annotation.
pub fn is_constructor_shorthand(&self, resolve: &Resolve) -> bool {
let FunctionKind::Constructor(containing_resource_id) = self.kind else {
return false;
};

let Some(Type::Id(id)) = &self.result else {
return false;
};

let TypeDefKind::Handle(Handle::Own(returned_resource_id)) = resolve.types[*id].kind else {
return false;
};

return containing_resource_id == returned_resource_id;
}
}

fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec<TypeId>) {
Expand Down
7 changes: 7 additions & 0 deletions crates/wit-parser/tests/ui/parse-fail/bad-resource16.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package foo:bar;

interface foo {
resource a {
constructor() -> result<_, string>;
}
}
Loading
Loading