diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index f056c8d8f3..16bac6a852 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -465,8 +465,12 @@ pub fn decode_world(wasm: &[u8]) -> Result<(Resolve, WorldId)> { let mut interfaces = IndexMap::default(); let mut worlds = IndexMap::default(); let ty = &types[world]; - assert_eq!(ty.imports.len(), 0); - assert_eq!(ty.exports.len(), 1); + if ty.imports.len() != 0 || ty.exports.len() != 1 { + bail!( + "cannot represent this component in WIT: expected an exported component type with \ + no imports and one export" + ); + } let name = ty.exports.keys().nth(0).unwrap(); let name = decoder.decode_world( name, @@ -613,7 +617,7 @@ impl WitPackageDecoder<'_> { ) -> Result { let ty = match item.ty { ComponentEntityType::Component(ty) => &self.types[ty], - _ => unreachable!(), + _ => bail!("cannot represent this component in WIT: export `{name}` is not a world"), }; let kebab_name = self .parse_component_name(name) @@ -788,7 +792,10 @@ impl WitPackageDecoder<'_> { self.register_defined(id, &self.types[ty])?; } ComponentAnyTypeId::Resource(_) => {} - _ => unreachable!(), + _ => bail!( + "cannot represent this component in WIT: type `{name}` is a \ + component, instance or function type" + ), } let prev = self.type_map.insert(created, id); assert!(prev.is_none()); @@ -1028,6 +1035,13 @@ impl WitPackageDecoder<'_> { item: &ComponentItem, owner: TypeOwner, ) -> Result { + // A WIT type has a plain name, not one like `a:b/c` or `[method]r.f`. + match self.parse_component_name(name)?.kind() { + ComponentNameKind::Plain(n) if n.is_bare() => {} + _ => bail!( + "cannot represent this component in WIT: type `{name}` doesn't have a plain name" + ), + } let (referenced, created) = match item.ty { ComponentEntityType::Type { referenced, @@ -1069,7 +1083,10 @@ impl WitPackageDecoder<'_> { .convert_defined(&self.types[ty]) .context("failed to convert unaliased type")?, ComponentAnyTypeId::Resource(_) => TypeDefKind::Resource, - _ => unreachable!(), + _ => bail!( + "cannot represent this component in WIT: type `{name}` is a component, \ + instance or function type" + ), } } }; @@ -1212,64 +1229,85 @@ impl WitPackageDecoder<'_> { kind: match name.kind() { ComponentNameKind::Plain(plain) => { if plain.accessor.is_some() && ty.async_ { - panic!("function `{name}` is an async accessor (should be impossible)"); + bail!("function `{name}` is an async accessor (should be impossible)"); } - let resource = plain - .resource() - .map(|resource| self.resources[&owner][resource.as_str()]); - match (plain.resource_func, plain.accessor) { - (None, None) => { - if ty.async_ { - FunctionKind::AsyncFreestanding - } else { - FunctionKind::Freestanding + match plain.resource_func { + None => match plain.accessor { + None => { + if ty.async_ { + FunctionKind::AsyncFreestanding + } else { + FunctionKind::Freestanding + } } - } - (None, Some(ComponentAccessorKind::Get)) => FunctionKind::Getter, - (None, Some(ComponentAccessorKind::Set)) => FunctionKind::Setter, + Some(ComponentAccessorKind::Get) => FunctionKind::Getter, + Some(ComponentAccessorKind::Set) => FunctionKind::Setter, + }, - (Some(ResourceFuncKind::Constructor), None) => { - FunctionKind::Constructor(resource.unwrap()) - } - (Some(ResourceFuncKind::Constructor), Some(_)) => { - panic!( - "function `{name}` is a constructor with an accessor (should be impossible)" - ); - } + Some(resource_func) => { + let Some(resource) = plain.resource() else { + bail!("function `{name}` has no resource (should be impossible)"); + }; + let Some(&resource) = self + .resources + .get(&owner) + .and_then(|resources| resources.get(resource.as_str())) + else { + bail!( + "cannot represent this component in WIT: function `{name}` \ + is not next to the definition of its resource `{resource}`" + ); + }; + + match (resource_func, plain.accessor) { + (ResourceFuncKind::Constructor, None) => { + FunctionKind::Constructor(resource) + } + (ResourceFuncKind::Constructor, Some(_)) => { + bail!( + "function `{name}` is a constructor with an accessor \ + (should be impossible)" + ); + } - (Some(ResourceFuncKind::Method), None) => { - if ty.async_ { - FunctionKind::AsyncMethod(resource.unwrap()) - } else { - FunctionKind::Method(resource.unwrap()) - } - } - (Some(ResourceFuncKind::Method), Some(ComponentAccessorKind::Get)) => { - FunctionKind::MethodGetter(resource.unwrap()) - } - (Some(ResourceFuncKind::Method), Some(ComponentAccessorKind::Set)) => { - FunctionKind::MethodSetter(resource.unwrap()) - } + (ResourceFuncKind::Method, None) => { + if ty.async_ { + FunctionKind::AsyncMethod(resource) + } else { + FunctionKind::Method(resource) + } + } + (ResourceFuncKind::Method, Some(ComponentAccessorKind::Get)) => { + FunctionKind::MethodGetter(resource) + } + (ResourceFuncKind::Method, Some(ComponentAccessorKind::Set)) => { + FunctionKind::MethodSetter(resource) + } - (Some(ResourceFuncKind::Static), None) => { - if ty.async_ { - FunctionKind::AsyncStatic(resource.unwrap()) - } else { - FunctionKind::Static(resource.unwrap()) + (ResourceFuncKind::Static, None) => { + if ty.async_ { + FunctionKind::AsyncStatic(resource) + } else { + FunctionKind::Static(resource) + } + } + (ResourceFuncKind::Static, Some(ComponentAccessorKind::Get)) => { + FunctionKind::StaticGetter(resource) + } + (ResourceFuncKind::Static, Some(ComponentAccessorKind::Set)) => { + FunctionKind::StaticSetter(resource) + } } } - (Some(ResourceFuncKind::Static), Some(ComponentAccessorKind::Get)) => { - FunctionKind::StaticGetter(resource.unwrap()) - } - (Some(ResourceFuncKind::Static), Some(ComponentAccessorKind::Set)) => { - FunctionKind::StaticSetter(resource.unwrap()) - } } } - // Functions shouldn't have ID-based names at this time. - ComponentNameKind::Interface(_) => unreachable!(), + // Functions can't have ID-based names in WIT. + ComponentNameKind::Interface(_) => bail!( + "cannot represent this component in WIT: function `{name}` has an interface's \ + name" + ), }, // Note that this name includes "name mangling" such as diff --git a/tests/cli/component-wit-custom-section-world-exports-instance.wat b/tests/cli/component-wit-custom-section-world-exports-instance.wat new file mode 100644 index 0000000000..14d9084643 --- /dev/null +++ b/tests/cli/component-wit-custom-section-world-exports-instance.wat @@ -0,0 +1,9 @@ +;; FAIL: component wit % +;; +;; The custom section holds the binary of: +;; +;; (component (type $c (component (export "a:b/w" (instance)))) (export "w" (type $c))) + +(module + (@custom "component-type:x" "\00\61\73\6d\0d\00\01\00\07\10\01\41\02\01\42\00\04\00\05\61\3a\62\2f\77\05\00\0b\07\01\00\01\77\03\00\00\00\16\0e\63\6f\6d\70\6f\6e\65\6e\74\2d\6e\61\6d\65\01\05\03\01\00\01\63") +) diff --git a/tests/cli/component-wit-custom-section-world-exports-instance.wat.stderr b/tests/cli/component-wit-custom-section-world-exports-instance.wat.stderr new file mode 100644 index 0000000000..046db9b6dd --- /dev/null +++ b/tests/cli/component-wit-custom-section-world-exports-instance.wat.stderr @@ -0,0 +1,4 @@ +error: decoding custom section component-type:x + +Caused by: + 0: cannot represent this component in WIT: export `a:b/w` is not a world diff --git a/tests/cli/component-wit-custom-section-world-imports.wat b/tests/cli/component-wit-custom-section-world-imports.wat new file mode 100644 index 0000000000..24bc273b80 --- /dev/null +++ b/tests/cli/component-wit-custom-section-world-imports.wat @@ -0,0 +1,9 @@ +;; FAIL: component wit % +;; +;; The custom section holds the binary of: +;; +;; (component (type $c (component (import "a:b/c" (instance)) (export "a:b/w" (component)))) (export "w" (type $c))) + +(module + (@custom "component-type:x" "\00\61\73\6d\0d\00\01\00\07\1d\01\41\04\01\42\00\03\00\05\61\3a\62\2f\63\05\00\01\41\00\04\00\05\61\3a\62\2f\77\04\01\0b\07\01\00\01\77\03\00\00\00\16\0e\63\6f\6d\70\6f\6e\65\6e\74\2d\6e\61\6d\65\01\05\03\01\00\01\63") +) diff --git a/tests/cli/component-wit-custom-section-world-imports.wat.stderr b/tests/cli/component-wit-custom-section-world-imports.wat.stderr new file mode 100644 index 0000000000..135818cbd0 --- /dev/null +++ b/tests/cli/component-wit-custom-section-world-imports.wat.stderr @@ -0,0 +1,4 @@ +error: decoding custom section component-type:x + +Caused by: + 0: cannot represent this component in WIT: expected an exported component type with no imports and one export diff --git a/tests/cli/component-wit-custom-section-world-no-export.wat b/tests/cli/component-wit-custom-section-world-no-export.wat new file mode 100644 index 0000000000..d8e6f116e0 --- /dev/null +++ b/tests/cli/component-wit-custom-section-world-no-export.wat @@ -0,0 +1,9 @@ +;; FAIL: component wit % +;; +;; The custom section holds the binary of: +;; +;; (component (type $c (component)) (export "x" (type $c))) + +(module + (@custom "component-type:x" "\00\61\73\6d\0d\00\01\00\07\03\01\41\00\0b\07\01\00\01\78\03\00\00\00\16\0e\63\6f\6d\70\6f\6e\65\6e\74\2d\6e\61\6d\65\01\05\03\01\00\01\63") +) diff --git a/tests/cli/component-wit-custom-section-world-no-export.wat.stderr b/tests/cli/component-wit-custom-section-world-no-export.wat.stderr new file mode 100644 index 0000000000..135818cbd0 --- /dev/null +++ b/tests/cli/component-wit-custom-section-world-no-export.wat.stderr @@ -0,0 +1,4 @@ +error: decoding custom section component-type:x + +Caused by: + 0: cannot represent this component in WIT: expected an exported component type with no imports and one export diff --git a/tests/cli/component-wit-func-with-interface-name.wat b/tests/cli/component-wit-func-with-interface-name.wat new file mode 100644 index 0000000000..4cab34b860 --- /dev/null +++ b/tests/cli/component-wit-func-with-interface-name.wat @@ -0,0 +1,5 @@ +;; FAIL: component wit % + +(component + (import "a:b/c" (func)) +) diff --git a/tests/cli/component-wit-func-with-interface-name.wat.stderr b/tests/cli/component-wit-func-with-interface-name.wat.stderr new file mode 100644 index 0000000000..14bd0f3d9a --- /dev/null +++ b/tests/cli/component-wit-func-with-interface-name.wat.stderr @@ -0,0 +1,4 @@ +error: failed to decode function from import `a:b/c` + +Caused by: + 0: cannot represent this component in WIT: function `a:b/c` has an interface's name diff --git a/tests/cli/component-wit-implements-redefines-type-as-instance-type.wat b/tests/cli/component-wit-implements-redefines-type-as-instance-type.wat new file mode 100644 index 0000000000..2fc9e8614c --- /dev/null +++ b/tests/cli/component-wit-implements-redefines-type-as-instance-type.wat @@ -0,0 +1,12 @@ +;; FAIL: component wit % + +(component + (import "x" (implements "a:b/c") (instance + (type $t u32) + (export "t" (type (eq $t))) + )) + (import "y" (implements "a:b/c") (instance + (type $i (instance)) + (export "t" (type (eq $i))) + )) +) diff --git a/tests/cli/component-wit-implements-redefines-type-as-instance-type.wat.stderr b/tests/cli/component-wit-implements-redefines-type-as-instance-type.wat.stderr new file mode 100644 index 0000000000..e8aac07149 --- /dev/null +++ b/tests/cli/component-wit-implements-redefines-type-as-instance-type.wat.stderr @@ -0,0 +1,4 @@ +error: failed to decode WIT from import `y` + +Caused by: + 0: cannot represent this component in WIT: type `t` is a component, instance or function type diff --git a/tests/cli/component-wit-import-component-type.wat b/tests/cli/component-wit-import-component-type.wat new file mode 100644 index 0000000000..92803cf7d2 --- /dev/null +++ b/tests/cli/component-wit-import-component-type.wat @@ -0,0 +1,6 @@ +;; FAIL: component wit % + +(component + (type $c (component)) + (import "t" (type (eq $c))) +) diff --git a/tests/cli/component-wit-import-component-type.wat.stderr b/tests/cli/component-wit-import-component-type.wat.stderr new file mode 100644 index 0000000000..5c32fdffef --- /dev/null +++ b/tests/cli/component-wit-import-component-type.wat.stderr @@ -0,0 +1,4 @@ +error: failed to decode type from export `t` + +Caused by: + 0: cannot represent this component in WIT: type `t` is a component, instance or function type diff --git a/tests/cli/component-wit-instance-exports-func-type.wat b/tests/cli/component-wit-instance-exports-func-type.wat new file mode 100644 index 0000000000..5c8b0868a5 --- /dev/null +++ b/tests/cli/component-wit-instance-exports-func-type.wat @@ -0,0 +1,8 @@ +;; FAIL: component wit % + +(component + (import "i" (instance + (type $f (func)) + (export "t" (type (eq $f))) + )) +) diff --git a/tests/cli/component-wit-instance-exports-func-type.wat.stderr b/tests/cli/component-wit-instance-exports-func-type.wat.stderr new file mode 100644 index 0000000000..9e26828071 --- /dev/null +++ b/tests/cli/component-wit-instance-exports-func-type.wat.stderr @@ -0,0 +1,5 @@ +error: failed to decode WIT from import `i` + +Caused by: + 0: failed to register type export 't' + 1: cannot represent this component in WIT: type `t` is a component, instance or function type diff --git a/tests/cli/component-wit-instance-type-with-interface-name.wat b/tests/cli/component-wit-instance-type-with-interface-name.wat new file mode 100644 index 0000000000..8a1dba6ae7 --- /dev/null +++ b/tests/cli/component-wit-instance-type-with-interface-name.wat @@ -0,0 +1,8 @@ +;; FAIL: component wit % + +(component + (import "i" (instance + (type (list u8)) + (export "a:b/c@1.0.0" (type (eq 0))) + )) +) diff --git a/tests/cli/component-wit-instance-type-with-interface-name.wat.stderr b/tests/cli/component-wit-instance-type-with-interface-name.wat.stderr new file mode 100644 index 0000000000..77195df3ed --- /dev/null +++ b/tests/cli/component-wit-instance-type-with-interface-name.wat.stderr @@ -0,0 +1,5 @@ +error: failed to decode WIT from import `i` + +Caused by: + 0: failed to register type export 'a:b/c@1.0.0' + 1: cannot represent this component in WIT: type `a:b/c@1.0.0` doesn't have a plain name diff --git a/tests/cli/component-wit-method-on-used-resource.wat b/tests/cli/component-wit-method-on-used-resource.wat new file mode 100644 index 0000000000..3644e7690b --- /dev/null +++ b/tests/cli/component-wit-method-on-used-resource.wat @@ -0,0 +1,12 @@ +;; FAIL: component wit % + +(component + (import "a:b/d" (instance $d + (export "r" (type (sub resource))) + )) + (alias export $d "r" (type $r)) + (import "a:b/e" (instance + (export "r" (type $x (eq $r))) + (export "[method]r.f" (func (param "self" (borrow $x)))) + )) +) diff --git a/tests/cli/component-wit-method-on-used-resource.wat.stderr b/tests/cli/component-wit-method-on-used-resource.wat.stderr new file mode 100644 index 0000000000..656ac88f75 --- /dev/null +++ b/tests/cli/component-wit-method-on-used-resource.wat.stderr @@ -0,0 +1,4 @@ +error: failed to decode WIT from import `a:b/e` + +Caused by: + 0: cannot represent this component in WIT: function `[method]r.f` is not next to the definition of its resource `r` diff --git a/tests/cli/component-wit-type-with-interface-name.wat b/tests/cli/component-wit-type-with-interface-name.wat new file mode 100644 index 0000000000..196ec6564f --- /dev/null +++ b/tests/cli/component-wit-type-with-interface-name.wat @@ -0,0 +1,6 @@ +;; FAIL: component wit % + +(component + (type (list u8)) + (import "a:b/c" (type (eq 0))) +) diff --git a/tests/cli/component-wit-type-with-interface-name.wat.stderr b/tests/cli/component-wit-type-with-interface-name.wat.stderr new file mode 100644 index 0000000000..fb9810f929 --- /dev/null +++ b/tests/cli/component-wit-type-with-interface-name.wat.stderr @@ -0,0 +1,4 @@ +error: failed to decode type from export `a:b/c` + +Caused by: + 0: cannot represent this component in WIT: type `a:b/c` doesn't have a plain name