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
54 changes: 25 additions & 29 deletions crates/environ/src/component/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ impl<'a, 'data> Translator<'a, 'data> {
for import in s {
let import = import?;
let ty = self.types.component_type_ref(&import.ty);
self.result.push_typedef(ty);
self.push_typedef(ty);
self.result
.initializers
.push(LocalInitializer::Import(import.name, ty));
Expand Down Expand Up @@ -687,6 +687,28 @@ impl<'a, 'data> Translator<'a, 'data> {
Ok(Action::KeepGoing)
}

fn push_typedef(&mut self, ty: TypeDef) {
match ty {
TypeDef::ComponentInstance(idx) => {
self.result
.component_instances
.push(ComponentInstanceType::Index(idx));
}
TypeDef::ComponentFunc(idx) => {
self.result.component_funcs.push(idx);
}
TypeDef::Component(idx) => {
self.result.components.push(ComponentType::Index(idx));
}
TypeDef::Interface(_) => {
self.types.push_component_typedef(ty);
}

// not processed here
TypeDef::CoreFunc(_) | TypeDef::Module(_) => {}
}
}

fn instantiate_module(
&mut self,
module: ModuleIndex,
Expand Down Expand Up @@ -852,21 +874,15 @@ impl<'a, 'data> Translator<'a, 'data> {
// the aliased item is directly available from the instance type.
ComponentInstanceType::Index(ty) => {
let (_url, ty) = &self.types[ty].exports[name];
self.result.push_typedef(*ty);
if let TypeDef::Interface(_) = ty {
self.types.push_component_typedef(*ty);
}
self.push_typedef(*ty);
}

// An imported component was instantiated so the type of the aliased
// export is available through the type of the export on the
// original component.
ComponentInstanceType::InstantiatedIndex(ty) => {
let (_, ty) = self.types[ty].exports[name];
self.result.push_typedef(ty);
if let TypeDef::Interface(_) = ty {
self.types.push_component_typedef(ty);
}
self.push_typedef(ty);
}

// A static nested component was instantiated which means that the
Expand Down Expand Up @@ -1025,23 +1041,3 @@ impl<'a, 'data> Translator<'a, 'data> {
return ret;
}
}

impl Translation<'_> {
fn push_typedef(&mut self, ty: TypeDef) {
match ty {
TypeDef::ComponentInstance(idx) => {
self.component_instances
.push(ComponentInstanceType::Index(idx));
}
TypeDef::ComponentFunc(idx) => {
self.component_funcs.push(idx);
}
TypeDef::Component(idx) => {
self.components.push(ComponentType::Index(idx));
}

// not processed here
TypeDef::Interface(_) | TypeDef::CoreFunc(_) | TypeDef::Module(_) => {}
}
}
}
20 changes: 13 additions & 7 deletions crates/environ/src/component/translate/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ pub(super) fn run(
let mut args = HashMap::with_capacity(result.exports.len());
for init in result.initializers.iter() {
let (name, ty) = match *init {
// Imports of types (which are currently always equality-bounded)
// are not required to be specified by the host since it's just for
// type information within the component.
LocalInitializer::Import(_, TypeDef::Interface(_)) => continue,
LocalInitializer::Import(name, ty) => (name, ty),
_ => continue,
};
Expand Down Expand Up @@ -355,6 +359,13 @@ impl<'a> Inliner<'a> {
use LocalInitializer::*;

match initializer {
// Importing a type into a component is ignored. All type imports
// are equality-bound right now which means that it's purely
// informational name about the type such as a name to assign it.
// Otherwise type imports have no effect on runtime or such, so skip
// them.
Import(_, TypeDef::Interface(_)) => {}

// When a component imports an item the actual definition of the
// item is looked up here (not at runtime) via its name. The
// arguments provided in our `InlinerFrame` describe how each
Expand All @@ -378,12 +389,7 @@ impl<'a> Inliner<'a> {
ComponentItemDef::Func(i) => {
frame.component_funcs.push(i.clone());
}

// The type structure of a component does not change depending
// on how it is instantiated at this time so importing a type
// does not affect the component being instantiated so it's
// ignored.
ComponentItemDef::Type(_ty) => {}
ComponentItemDef::Type(_ty) => unreachable!(),
},

// Lowering a component function to a core wasm function is
Expand Down Expand Up @@ -1045,7 +1051,7 @@ impl<'a> ComponentItemDef<'a> {
// FIXME(#4283) should commit one way or another to how this
// should be treated.
TypeDef::Component(_ty) => bail!("root-level component imports are not supported"),
TypeDef::Interface(_ty) => unimplemented!("import of a type"),
TypeDef::Interface(ty) => ComponentItemDef::Type(TypeDef::Interface(ty)),
TypeDef::CoreFunc(_ty) => unreachable!(),
};
Ok(item)
Expand Down
3 changes: 1 addition & 2 deletions crates/wasmtime/src/component/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,7 @@ impl<T> Linker<T> {
let import = self
.strings
.lookup(name)
.and_then(|name| self.map.get(&name))
.ok_or_else(|| anyhow!("import `{name}` not defined"))?;
.and_then(|name| self.map.get(&name));
cx.definition(ty, import)
.with_context(|| format!("import `{name}` has the wrong type"))?;
}
Expand Down
31 changes: 19 additions & 12 deletions crates/wasmtime/src/component/matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@ pub struct TypeChecker<'a> {
}

impl TypeChecker<'_> {
pub fn definition(&self, expected: &TypeDef, actual: &Definition) -> Result<()> {
pub fn definition(&self, expected: &TypeDef, actual: Option<&Definition>) -> Result<()> {
match *expected {
TypeDef::Module(t) => match actual {
Definition::Module(actual) => self.module(&self.types[t], actual),
_ => bail!("expected module found {}", actual.desc()),
Some(Definition::Module(actual)) => self.module(&self.types[t], actual),
_ => bail!("expected module found {}", desc(actual)),
},
TypeDef::ComponentInstance(t) => match actual {
Definition::Instance(actual) => self.instance(&self.types[t], actual),
_ => bail!("expected instance found {}", actual.desc()),
Some(Definition::Instance(actual)) => self.instance(&self.types[t], Some(actual)),
None => self.instance(&self.types[t], None),
_ => bail!("expected instance found {}", desc(actual)),
},
TypeDef::ComponentFunc(t) => match actual {
Definition::Func(actual) => self.func(t, actual),
_ => bail!("expected func found {}", actual.desc()),
Some(Definition::Func(actual)) => self.func(t, actual),
_ => bail!("expected func found {}", desc(actual)),
},
TypeDef::Component(_) => bail!("expected component found {}", actual.desc()),
TypeDef::Interface(_) => bail!("expected type found {}", actual.desc()),
TypeDef::Component(_) => bail!("expected component found {}", desc(actual)),
TypeDef::Interface(_) => bail!("expected type found {}", desc(actual)),

// not possible for valid components to import
TypeDef::CoreFunc(_) => unreachable!(),
Expand Down Expand Up @@ -67,7 +68,7 @@ impl TypeChecker<'_> {
Ok(())
}

fn instance(&self, expected: &TypeComponentInstance, actual: &NameMap) -> Result<()> {
fn instance(&self, expected: &TypeComponentInstance, actual: Option<&NameMap>) -> Result<()> {
// Like modules, every export in the expected type must be present in
// the actual type. It's ok, though, to have extra exports in the actual
// type.
Expand All @@ -81,8 +82,7 @@ impl TypeChecker<'_> {
let actual = self
.strings
.lookup(name)
.and_then(|name| actual.get(&name))
.ok_or_else(|| anyhow!("instance export `{name}` not defined"))?;
.and_then(|name| actual?.get(&name));
self.definition(expected, actual)
.with_context(|| format!("instance export `{name}` has the wrong type"))?;
}
Expand All @@ -94,6 +94,13 @@ impl TypeChecker<'_> {
}
}

fn desc(def: Option<&Definition>) -> &'static str {
match def {
Some(def) => def.desc(),
None => "nothing",
}
}

impl Definition {
fn desc(&self) -> &'static str {
match self {
Expand Down
15 changes: 15 additions & 0 deletions tests/misc_testsuite/component-model/import.wast
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,18 @@
(import "host-return-two" (func $f (result u32)))
(export "x" (func $f)))
"component export `x` is a reexport of an imported function which is not implemented")

(assert_invalid
(component
(import "host-return-two" (instance))
)
"expected instance found func")

;; empty instances don't need to be supplied by the host, even recursively
;; empty instances.
(component
(import "not-provided-by-the-host" (instance))
(import "not-provided-by-the-host2" (instance
(export "x" (instance))
))
)
4 changes: 2 additions & 2 deletions tests/misc_testsuite/component-model/linking.wast
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
(component
(import "undefined-name" (core module))
)
"import `undefined-name` not defined")
"expected module found nothing")
(component $i)
(component
(import "i" (instance))
Expand All @@ -15,4 +15,4 @@
"expected func found instance")
(assert_unlinkable
(component (import "i" (instance (export "x" (func)))))
"export `x` not defined")
"expected func found nothing")
14 changes: 14 additions & 0 deletions tests/misc_testsuite/component-model/types.wast
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,17 @@

(instance $c (instantiate $c (with "x" (component $x))))
)

(component
(type $t1 u64)
(import "a" (type $t2 (eq $t1)))
(import "b" (type $t3 (eq $t2)))
)

(component
(import "a" (instance
(type $t1 u64)
(export $t2 "a" (type (eq $t1)))
(export "b" (type (eq $t2)))
))
)