Repository: bytecodealliance/wasm-tools (rust)

Issue:
wasm-tools compose creates invalid instances within the composed component
After I run the `wasm-tools compose` [example](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-compose/example), I cannot run `wasm-tools component wit` on the composed component. I get the following error:

```
Error: failed to decode WIT document

Caused by:
    0: failed to decode WIT from import `streams`
    1: unexpected unnamed type
```

The issue seems to be that the composed component has incorrectly encoded instances. You can see this when looking at one of the instances before and after composition happen. Note: the instance in question is not involved in the composition beyond being a shared dependency of the two components being merged into one composed component:

**Before composition**

```wat
(component
 (type (;0;)
    (instance
      (type (;0;) u32)
      (export (;1;) "wall-clock" (type (eq 0)))
      (type (;2;) (record (field "seconds" u64) (field "nanoseconds" u32)))
      (export (;3;) "datetime" (type (eq 2)))
      (type (;4;) (func (param "this" 1) (result 3))) ;; NOTE: the result points to the "datetime" export
      (export (;0;) "now" (func (type 4)))
    )
  )
;; ...
```

**After composition**

```wat
(component
  (type (;0;)
    (instance
      (type (;0;) u32)
      (export (;1;) "wall-clock" (type (eq 0)))
      (type (;2;) (record (field "seconds" u64) (field "nanoseconds" u32)))
      (export (;3;) "datetime" (type (eq 2)))
      (type (;4;) (func (param "this" 0) (result 2))) ;; NOTE: the result type points directly to the record
      (export (;0;) "now" (func (type 4)))
    )
  )
;; ...
```

At some point this instance seems to have one of its functions changed so that its result type points directly to a record field (which is illegal) instead of pointing to a type.

@alexcrichton noted the following [on Zulip](https://bytecodealliance.zulipchat.com/#narrow/stream/206238-general/topic/wasm-tools.20compose.20example/near/348479284):

> I suspect that this may be an issue with both the decoder and encoder perhaps? The constructs used here were added after the initial implementation of wasm-compose so it hasn't been updated to use exports in the same way. The decoder probably needs an update to read the structure of use-the-exported-item and the encoder needs to be updated to use items as they're exported. Shouldn't be too hard ideally as wit-component deals with similar problems as well, but if you'd like I think opening an issue for this would be good

Pull request:
Encode instance exports properly
Fixes #977 

Instance exports were not being reflected in the TypeMap meaning that future declarations would refer to the direct type definition instead of the export definition. This updates that handling so that the type map is updated with the index of the export. 

Big thanks to @peterhuene for the helpful hints!

> *Note*: I also updated some error handling to give better errors to help with a new error I'm seeing. Let me know if you'd rather I separate that out into a new PR.

Patch:
diff --git a/crates/wasm-compose/Cargo.toml b/crates/wasm-compose/Cargo.toml
index c599fc17..c2600365 100644
--- a/crates/wasm-compose/Cargo.toml
+++ b/crates/wasm-compose/Cargo.toml
@@ -32,3 +32,4 @@ cli = ["clap"]
 glob = "0.3.0"
 pretty_assertions = "1.2.1"
 wasmprinter = { workspace = true }
+wit-component = { workspace = true }
diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs
index 1d00a51e..423f8e05 100644
--- a/crates/wasm-compose/src/encoding.rs
+++ b/crates/wasm-compose/src/encoding.rs
@@ -143,8 +143,8 @@ impl<'a> TypeEncoder<'a> {
         }
 
         for (name, url, ty) in exports {
-            let ty = self.component_entity_type(&mut encoded, &mut types, ty);
-            encoded.export(name, url, ty);
+            let export = self.export(ty, &mut encoded, &mut types);
+            encoded.export(name, url, export);
         }
 
         encoded
@@ -158,8 +158,8 @@ impl<'a> TypeEncoder<'a> {
         let mut types = TypeMap::new();
 
         for (name, url, ty) in exports {
-            let ty = self.component_entity_type(&mut encoded, &mut types, ty);
-            encoded.export(name, url, ty);
+            let export = self.export(ty, &mut encoded, &mut types);
+            encoded.export(name, url, export);
         }
 
         encoded
@@ -666,6 +666,27 @@ impl<'a> TypeEncoder<'a> {
         encodable.ty().defined_type().result(ok, err);
         index
     }
+
+    fn export(
+        &self,
+        export: ComponentEntityType,
+        encoded: &mut impl Encodable,
+        types: &mut TypeMap<'a>,
+    ) -> ComponentTypeRef {
+        // Check if the export is a type; if so, we need to update the index of the
+        // type to point to the export instead of the original definition
+        let id = match export {
+            ComponentEntityType::Type { created: id, .. } => Some(id),
+            _ => None,
+        };
+        let export = self.component_entity_type(encoded, types, export);
+        if let Some(id) = id {
+            // Update the index in the type map to point to this export
+            let ty = self.0.type_from_id(id).unwrap();
+            types.insert(PtrKey(ty), encoded.type_count());
+        }
+        export
+    }
 }
 
 /// Represents an instance index in a composition graph.
@@ -1142,8 +1163,8 @@ impl<'a> CompositionGraphEncoder<'a> {
         let mut types = TypeMap::new();
         for (name, (url, component, ty)) in exports {
             let encoder = TypeEncoder::new(&component.types);
-            let ty = encoder.component_entity_type(&mut instance_type, &mut types, ty);
-            instance_type.export(name, url, ty);
+            let export = encoder.export(ty, &mut instance_type, &mut types);
+            instance_type.export(name, url, export);
         }
 
         let index = self.types;
diff --git a/crates/wit-component/src/decoding.rs b/crates/wit-component/src/decoding.rs
index 936b41b1..54bcc40b 100644
--- a/crates/wit-component/src/decoding.rs
+++ b/crates/wit-component/src/decoding.rs
@@ -207,9 +207,8 @@ impl<'a> ComponentInfo<'a> {
                                 })?;
                             WorldItem::Type(id)
                         }
-                        _ => {
-                            bail!("component import `{name}` was not a function, instance, or type")
-                        }
+                        // All other imports do not form part of the component's world
+                        _ => continue,
                     };
                     decoder.resolve.worlds[world]
                         .imports
@@ -636,12 +635,14 @@ impl WitPackageDecoder<'_> {
                     referenced,
                     created,
                 } => {
-                    let ty = self.register_type_export(
-                        name,
-                        TypeOwner::Interface(self.resolve.interfaces.next_id()),
-                        referenced,
-                        created,
-                    )?;
+                    let ty = self
+                        .register_type_export(
+                            name,
+                            TypeOwner::Interface(self.resolve.interfaces.next_id()),
+                            referenced,
+                            created,
+                        )
+                        .with_context(|| format!("failed to register type export '{name}'"))?;
                     let prev = interface.types.insert(name.to_string(), ty);
                     assert!(prev.is_none());
                 }
@@ -682,7 +683,9 @@ impl WitPackageDecoder<'_> {
 
             // ... or this `TypeId`'s source definition has never
             // been seen before, so declare the full type.
-            None => self.convert_defined(ty)?,
+            None => self
+                .convert_defined(ty)
+                .context("failed to convert unaliased type")?,
         };
         let ty = self.resolve.types.alloc(TypeDef {
             name: Some(name.to_string()),
@@ -924,7 +927,9 @@ impl WitPackageDecoder<'_> {
                     .map(|(name, ty)| {
                         Ok(Field {
                             name: name.to_string(),
-                            ty: self.convert_valtype(ty)?,
+                            ty: self.convert_valtype(ty).with_context(|| {
+                                format!("failed to convert record field '{name}'")
+                            })?,
                             docs: Default::default(),
                         })
                     })


Test patch:
diff --git a/crates/wasm-compose/tests/compose.rs b/crates/wasm-compose/tests/compose.rs
index 6e30626f..831d0b1b 100644
--- a/crates/wasm-compose/tests/compose.rs
+++ b/crates/wasm-compose/tests/compose.rs
@@ -75,6 +75,13 @@ fn component_composing() -> Result<()> {
                 )
             })?;
 
+            wit_component::decode("component", &bytes).with_context(|| {
+                format!(
+                    "failed to decode WIT from component bytes for test case `{}`",
+                    test_case
+                )
+            })?;
+
             (
                 wasmprinter::print_bytes(&bytes).with_context(|| {
                     format!(
diff --git a/crates/wasm-compose/tests/compositions/complex-import/composed.wat b/crates/wasm-compose/tests/compositions/complex-import/composed.wat
index c63ee59e..7e71a617 100644
--- a/crates/wasm-compose/tests/compositions/complex-import/composed.wat
+++ b/crates/wasm-compose/tests/compositions/complex-import/composed.wat
@@ -47,32 +47,32 @@
           (export (;12;) "m" (func (type 12)))
           (type (;13;) (record (field "a" s8) (field "b" u8) (field "c" s16) (field "d" u16) (field "e" s32) (field "f" u32) (field "g" s64) (field "h" u64) (field "i" float32) (field "j" float64) (field "k" bool) (field "l" string)))
           (export (;14;) "record1" (type (eq 13)))
-          (type (;15;) (func (param "x" 13)))
+          (type (;15;) (func (param "x" 14)))
           (export (;13;) "n" (func (type 15)))
-          (type (;16;) (list 13))
+          (type (;16;) (list 14))
           (type (;17;) (func (param "x" 16)))
           (export (;14;) "o" (func (type 17)))
-          (type (;18;) (tuple 13 string))
+          (type (;18;) (tuple 14 string))
           (type (;19;) (func (param "x" 18)))
           (export (;15;) "p" (func (type 19)))
           (type (;20;) (flags "a" "b" "c"))
           (export (;21;) "flags1" (type (eq 20)))
-          (type (;22;) (func (param "x" 20)))
+          (type (;22;) (func (param "x" 21)))
           (export (;16;) "q" (func (type 22)))
           (type (;23;) (enum "a" "b" "c"))
           (export (;24;) "enum1" (type (eq 23)))
-          (type (;25;) (func (param "x" 23)))
+          (type (;25;) (func (param "x" 24)))
           (export (;17;) "r" (func (type 25)))
-          (type (;26;) (union s8 string 13))
+          (type (;26;) (union s8 string 14))
           (export (;27;) "union1" (type (eq 26)))
-          (type (;28;) (func (param "x" 26)))
+          (type (;28;) (func (param "x" 27)))
           (export (;18;) "s" (func (type 28)))
-          (type (;29;) (variant (case "a" s8) (case "b" u8) (case "c" s16) (case "d" u16) (case "e" s32) (case "f" u32) (case "g" s64) (case "h" u64) (case "i" float32) (case "j" float64) (case "k" bool) (case "l" string) (case "m" 13)))
+          (type (;29;) (variant (case "a" s8) (case "b" u8) (case "c" s16) (case "d" u16) (case "e" s32) (case "f" u32) (case "g" s64) (case "h" u64) (case "i" float32) (case "j" float64) (case "k" bool) (case "l" string) (case "m" 14)))
           (export (;30;) "variant1" (type (eq 29)))
-          (type (;31;) (option 29))
+          (type (;31;) (option 30))
           (type (;32;) (func (param "x" 31)))
           (export (;19;) "t" (func (type 32)))
-          (type (;33;) (result 13 (error string)))
+          (type (;33;) (result 14 (error string)))
           (type (;34;) (func (result 33)))
           (export (;20;) "u" (func (type 34)))
         )
@@ -96,9 +96,9 @@
       (export (;3;) "flags1" (type (eq 2)))
       (type (;4;) (enum "a" "b" "c"))
       (export (;5;) "enum1" (type (eq 4)))
-      (type (;6;) (union s8 string 0))
+      (type (;6;) (union s8 string 1))
       (export (;7;) "union1" (type (eq 6)))
-      (type (;8;) (variant (case "a" s8) (case "b" u8) (case "c" s16) (case "d" u16) (case "e" s32) (case "f" u32) (case "g" s64) (case "h" u64) (case "i" float32) (case "j" float64) (case "k" bool) (case "l" string) (case "m" 0)))
+      (type (;8;) (variant (case "a" s8) (case "b" u8) (case "c" s16) (case "d" u16) (case "e" s32) (case "f" u32) (case "g" s64) (case "h" u64) (case "i" float32) (case "j" float64) (case "k" bool) (case "l" string) (case "m" 1)))
       (export (;9;) "variant1" (type (eq 8)))
       (type (;10;) (func))
       (export (;0;) "a" (func (type 10)))
@@ -126,24 +126,24 @@
       (export (;11;) "l" (func (type 21)))
       (type (;22;) (func (param "x" string)))
       (export (;12;) "m" (func (type 22)))
-      (type (;23;) (func (param "x" 0)))
+      (type (;23;) (func (param "x" 1)))
       (export (;13;) "n" (func (type 23)))
-      (type (;24;) (list 0))
+      (type (;24;) (list 1))
       (type (;25;) (func (param "x" 24)))
       (export (;14;) "o" (func (type 25)))
-      (type (;26;) (tuple 0 string))
+      (type (;26;) (tuple 1 string))
       (type (;27;) (func (param "x" 26)))
       (export (;15;) "p" (func (type 27)))
-      (type (;28;) (func (param "x" 2)))
+      (type (;28;) (func (param "x" 3)))
       (export (;16;) "q" (func (type 28)))
-      (type (;29;) (func (param "x" 4)))
+      (type (;29;) (func (param "x" 5)))
       (export (;17;) "r" (func (type 29)))
-      (type (;30;) (func (param "x" 6)))
+      (type (;30;) (func (param "x" 7)))
       (export (;18;) "s" (func (type 30)))
-      (type (;31;) (option 8))
+      (type (;31;) (option 9))
       (type (;32;) (func (param "x" 31)))
       (export (;19;) "t" (func (type 32)))
-      (type (;33;) (result 0 (error string)))
+      (type (;33;) (result 1 (error string)))
       (type (;34;) (func (result 33)))
       (export (;20;) "u" (func (type 34)))
     )