Repository: bytecodealliance/wasmtime
Pull request #5591 (2023-01-18 23:16:11)

Improve handling of types and aliases in components
This commit fixes more cases from #5565 where `export` items introducing indices wasn't handled by accident. Additionally this fixes support for aliasing types from instances which largely wasn't working before. Most of the fixes here are about correctly maintaining Wasmtime's view of the type index spaces.

<!--

Please ensure that the following steps are all taken care of before submitting
the PR.

- [ ] This has been discussed in issue #..., or if not, please tell us why
  here.
- [ ] A short description of what this does, why it is needed; if the
  description becomes long, the matter should probably be discussed in an issue
  first.
- [ ] This PR contains test cases, if meaningful.
- [ ] A reviewer from the core maintainer team has been assigned for this PR.
  If you don't know who could review this, please indicate so. The list of
  suggested reviewers on the right can help you.

Please ensure all communication adheres to the [code of
conduct](https://github.com/bytecodealliance/wasmtime/blob/master/CODE_OF_CONDUCT.md).
-->



Patch:
diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs
index 678aa6306..ab538db42 100644
--- a/crates/environ/src/component/translate.rs
+++ b/crates/environ/src/component/translate.rs
@@ -266,6 +266,7 @@ enum ComponentItemType {
     Func(TypeFuncIndex),
     Component(ComponentType),
     Instance(ComponentInstanceType),
+    Type(TypeDef),
 }
 
 #[derive(Copy, Clone, PartialEq, Eq)]
@@ -621,6 +622,9 @@ impl<'a, 'data> Translator<'a, 'data> {
                     self.result
                         .initializers
                         .push(LocalInitializer::Export(item));
+                    if let ComponentItem::Type(ty) = item {
+                        self.types.push_component_typedef(ty);
+                    }
                 }
             }
 
@@ -775,7 +779,8 @@ impl<'a, 'data> Translator<'a, 'data> {
                 ComponentItem::ComponentInstance(i) => Some(ComponentItemType::Instance(
                     self.result.component_instances[i],
                 )),
-                ComponentItem::Module(_) | ComponentItem::Type(_) => None,
+                ComponentItem::Type(ty) => Some(ComponentItemType::Type(ty)),
+                ComponentItem::Module(_) => None,
             };
             map.insert(export.name, idx);
             if let Some(ty) = ty {
@@ -848,6 +853,9 @@ impl<'a, 'data> Translator<'a, 'data> {
             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);
+                }
             }
 
             // An imported component was instantiated so the type of the aliased
@@ -856,6 +864,9 @@ impl<'a, 'data> Translator<'a, 'data> {
             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);
+                }
             }
 
             // A static nested component was instantiated which means that the
@@ -902,6 +913,9 @@ impl<'a, 'data> Translator<'a, 'data> {
                     ComponentItemType::Instance(ty) => {
                         self.result.component_instances.push(ty);
                     }
+                    ComponentItemType::Type(ty) => {
+                        self.types.push_component_typedef(ty);
+                    }
                 }
             }
         }
diff --git a/crates/environ/src/component/translate/inline.rs b/crates/environ/src/component/translate/inline.rs
index 352f9883d..9d63bb4be 100644
--- a/crates/environ/src/component/translate/inline.rs
+++ b/crates/environ/src/component/translate/inline.rs
@@ -683,9 +683,10 @@ impl<'a> Inliner<'a> {
                             TypeDef::Component(_) => {
                                 unimplemented!("aliasing component export of component import")
                             }
-                            TypeDef::Interface(_) => {
-                                unimplemented!("aliasing type export of component import")
-                            }
+
+                            // This is handled during the initial translation
+                            // pass and doesn't need further handling here.
+                            TypeDef::Interface(_) => {}
 
                             // not possible with valid components
                             TypeDef::CoreFunc(_) => unreachable!(),
diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs
index b690b9ccf..4c78c2cbc 100644
--- a/crates/environ/src/component/types.rs
+++ b/crates/environ/src/component/types.rs
@@ -335,6 +335,7 @@ pub struct ComponentTypesBuilder {
 struct TypeScope {
     core: PrimaryMap<TypeIndex, TypeDef>,
     component: PrimaryMap<ComponentTypeIndex, TypeDef>,
+    instances: PrimaryMap<ComponentInstanceIndex, TypeComponentInstanceIndex>,
 }
 
 macro_rules! intern_and_fill_flat_types {
@@ -554,13 +555,13 @@ impl ComponentTypesBuilder {
                 ComponentTypeDeclaration::CoreType(ty) => self.type_declaration_core_type(ty)?,
                 ComponentTypeDeclaration::Alias(alias) => self.type_declaration_alias(alias)?,
                 ComponentTypeDeclaration::Export { name, url, ty } => {
-                    let ty = self.component_type_ref(ty);
+                    let ty = self.type_declaration_define(ty);
                     result
                         .exports
                         .insert(name.to_string(), (url.to_string(), ty));
                 }
                 ComponentTypeDeclaration::Import(import) => {
-                    let ty = self.component_type_ref(&import.ty);
+                    let ty = self.type_declaration_define(&import.ty);
                     result
                         .imports
                         .insert(import.name.to_string(), (import.url.to_string(), ty));
@@ -586,7 +587,7 @@ impl ComponentTypesBuilder {
                 InstanceTypeDeclaration::CoreType(ty) => self.type_declaration_core_type(ty)?,
                 InstanceTypeDeclaration::Alias(alias) => self.type_declaration_alias(alias)?,
                 InstanceTypeDeclaration::Export { name, url, ty } => {
-                    let ty = self.component_type_ref(ty);
+                    let ty = self.type_declaration_define(ty);
                     result
                         .exports
                         .insert(name.to_string(), (url.to_string(), ty));
@@ -629,11 +630,49 @@ impl ComponentTypesBuilder {
                 let ty = self.component_outer_type(*count, ComponentTypeIndex::from_u32(*index));
                 self.push_component_typedef(ty);
             }
+            ComponentAlias::InstanceExport {
+                kind: _,
+                instance_index,
+                name,
+            } => {
+                let ty = self.type_scopes.last().unwrap().instances
+                    [ComponentInstanceIndex::from_u32(*instance_index)];
+                let (_, ty) = self.component_types[ty].exports[*name];
+                self.push_component_typedef(ty);
+            }
             a => unreachable!("invalid alias {a:?}"),
         }
         Ok(())
     }
 
+    fn type_declaration_define(&mut self, ty: &wasmparser::ComponentTypeRef) -> TypeDef {
+        let ty = self.component_type_ref(ty);
+        let scope = self.type_scopes.last_mut().unwrap();
+        match ty {
+            // If an import or an export within a component or instance type
+            // references an interface type itself then that creates a new type
+            // which is effectively an alias, so push the type information here.
+            TypeDef::Interface(_) => {
+                self.push_component_typedef(ty);
+            }
+
+            // When an import or an export references a component instance then
+            // that creates a "pseudo-instance" which type information is
+            // maintained about. This is later used during the `InstanceExport`
+            // alias within a type declaration.
+            TypeDef::ComponentInstance(ty) => {
+                scope.instances.push(ty);
+            }
+
+            // All other valid types are ignored since we don't need to maintain
+            // metadata about them here as index spaces are modified that we're
+            // not interested in.
+            _ => {}
+        }
+
+        ty
+    }
+
     fn func_type(&mut self, ty: &wasmparser::ComponentFuncType<'_>) -> TypeFuncIndex {
         let ty = TypeFunc {
             params: ty


Test patch:
diff --git a/tests/misc_testsuite/component-model/types.wast b/tests/misc_testsuite/component-model/types.wast
index 2cd334a62..c4c93a803 100644
--- a/tests/misc_testsuite/component-model/types.wast
+++ b/tests/misc_testsuite/component-model/types.wast
@@ -246,3 +246,77 @@
     (type $t101 (list $t100))
   )
   "type nesting is too deep")
+
+(component
+  (type (instance
+    (export $x "x" (instance
+      (type $t u32)
+      (export "y" (type (eq $t)))
+    ))
+    (alias export $x "y" (type $t))
+    (export "my-y" (type (eq $t)))
+  ))
+
+  (type (component
+    (import "x" (instance $x
+      (type $t u32)
+      (export "y" (type (eq $t)))
+    ))
+    (alias export $x "y" (type $t))
+    (export "my-y" (type (eq $t)))
+  ))
+)
+
+(component
+  (type $t u32)
+  (export $t2 "t" (type $t))
+  (type $r (record (field "x" $t2)))
+  (export "r" (type $r))
+)
+
+(component
+  (component
+    (import "x" (instance $i
+      (type $i u32)
+      (export "i" (type (eq $i)))
+    ))
+    (alias export $i "i" (type $i))
+    (export "i" (type $i))
+  )
+)
+
+(component
+  (type $u u32)
+  (instance $i
+    (export "i" (type $u))
+  )
+  (alias export $i "i" (type $i))
+  (export "i" (type $i))
+)
+
+(component
+  (component $c
+    (type $t u32)
+    (export "t" (type $t))
+  )
+  (instance $c (instantiate $c))
+  (export "i" (type $c "t"))
+)
+
+(component
+  (component $c
+    (import "x" (component $c
+      (type $t u32)
+      (export "t" (type (eq $t)))
+    ))
+    (instance $c (instantiate $c))
+    (export "i" (type $c "t"))
+  )
+
+  (component $x
+    (type $t u32)
+    (export "t" (type $t))
+  )
+
+  (instance $c (instantiate $c (with "x" (component $x))))
+)