Repository: bytecodealliance/wasm-tools (rust)

Issue:
duplicate export name `57` already defined when importing multiple semver versions
Using the latest wasi-sdk pacakge built with the fix from https://github.com/bytecodealliance/wasm-tools/issues/1774 (https://github.com/WebAssembly/wasi-sdk/actions/runs/11129514597) I still get an error when building a wasm component with `wasi:io/poll@0.2.0` and `wasi:io/poll@0.2.1` I get the following error:

```
-- Build files have been written to: /home/jstur/projects/runtime/artifacts/bin/native/net10.0-wasi-Debug-wasm
  [ 14%] Linking C executable dotnet.wasm
EXEC : error : failed to encode component [/home/jstur/projects/runtime/src/mono/wasi/wasi.proj]

  Caused by:
      0: failed to validate component output
      1: duplicate export name `57` already defined (at offset 0xdf82f5)
```

```
cat /home/jstur/wasi-sdk-nightly/VERSION
24.9gcec5cf4f6cf3
wasi-libc: 1b19fc65ad84
llvm: a4bf6cd7cfb1
llvm-version: 19.1.0
config: f992bcc08219
```

Pull request description:
Fix generating shims with duplicate names
This fixes an issue with the semver-merging logic of `wit-component` where during the componentization process due to
insertion-with-replacement behavior the same name could be assigned to two different shims (as shim names are based on the map length which wouldn't change if a replacement was inserted). The fix in this commit is to avoid replacement and prefer the original name when a shim is inserted.

Closes #1850

Patch:
diff --git a/crates/wit-component/Cargo.toml b/crates/wit-component/Cargo.toml
index 13577e69..87bb2854 100644
--- a/crates/wit-component/Cargo.toml
+++ b/crates/wit-component/Cargo.toml
@@ -35,11 +35,11 @@ serde_derive = { workspace = true }
 serde_json = { workspace = true }
 
 [dev-dependencies]
-wasmprinter = { workspace = true }
+wasmprinter = { workspace = true, features = ['component-model'] }
 glob = "0.3.0"
 pretty_assertions = "1.3.0"
 env_logger = { workspace = true }
-wat = { workspace = true }
+wat = { workspace = true, features = ['component-model'] }
 libtest-mimic = { workspace = true }
 
 [target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs
index 51a45cff..5fb025b2 100644
--- a/crates/wit-component/src/encoding.rs
+++ b/crates/wit-component/src/encoding.rs
@@ -1827,7 +1827,12 @@ impl<'a> Shims<'a> {
     }
 
     fn push(&mut self, shim: Shim<'a>) {
-        self.shims.insert(shim.kind.clone(), shim);
+        // Only one shim per `ShimKind` is retained, so if it's already present
+        // don't overwrite it. If it's not present though go ahead and insert
+        // it.
+        if !self.shims.contains_key(&shim.kind) {
+            self.shims.insert(shim.kind.clone(), shim);
+        }
     }
 }
 


Test patch:
diff --git a/crates/wit-component/tests/components/merge-import-versions/component.wat b/crates/wit-component/tests/components/merge-import-versions/component.wat
index aaafd1f0..71207d37 100644
--- a/crates/wit-component/tests/components/merge-import-versions/component.wat
+++ b/crates/wit-component/tests/components/merge-import-versions/component.wat
@@ -10,8 +10,9 @@
       (export (;1;) "[method]r.x" (func (type 4)))
       (type (;5;) (func (param "x" string)))
       (export (;2;) "x" (func (type 5)))
+      (export (;3;) "x2" (func (type 5)))
       (type (;6;) (func))
-      (export (;3;) "y" (func (type 6)))
+      (export (;4;) "y" (func (type 6)))
     )
   )
   (import "a:b/c@0.1.1" (instance (;0;) (type 0)))
@@ -23,11 +24,13 @@
     (import "a:b/c@0.1.0" "[constructor]r" (func (;0;) (type 0)))
     (import "a:b/c@0.1.0" "[resource-drop]r" (func (;1;) (type 1)))
     (import "a:b/c@0.1.0" "x" (func (;2;) (type 2)))
-    (import "a:b/c@0.1.1" "[constructor]r" (func (;3;) (type 0)))
-    (import "a:b/c@0.1.1" "[resource-drop]r" (func (;4;) (type 1)))
-    (import "a:b/c@0.1.1" "[method]r.x" (func (;5;) (type 1)))
-    (import "a:b/c@0.1.1" "x" (func (;6;) (type 2)))
-    (import "a:b/c@0.1.1" "y" (func (;7;) (type 3)))
+    (import "a:b/c@0.1.0" "x2" (func (;3;) (type 2)))
+    (import "a:b/c@0.1.1" "[constructor]r" (func (;4;) (type 0)))
+    (import "a:b/c@0.1.1" "[resource-drop]r" (func (;5;) (type 1)))
+    (import "a:b/c@0.1.1" "[method]r.x" (func (;6;) (type 1)))
+    (import "a:b/c@0.1.1" "x" (func (;7;) (type 2)))
+    (import "a:b/c@0.1.1" "x2" (func (;8;) (type 2)))
+    (import "a:b/c@0.1.1" "y" (func (;9;) (type 3)))
     (memory (;0;) 1)
     (export "memory" (memory 0))
     (@producers
@@ -37,24 +40,32 @@
   )
   (core module (;1;)
     (type (;0;) (func (param i32 i32)))
-    (table (;0;) 1 1 funcref)
-    (export "1" (func $indirect-a:b/c@0.1.1-x))
+    (table (;0;) 2 2 funcref)
+    (export "0" (func $indirect-a:b/c@0.1.0-x))
+    (export "1" (func $indirect-a:b/c@0.1.0-x2))
     (export "$imports" (table 0))
-    (func $indirect-a:b/c@0.1.1-x (;0;) (type 0) (param i32 i32)
+    (func $indirect-a:b/c@0.1.0-x (;0;) (type 0) (param i32 i32)
       local.get 0
       local.get 1
       i32.const 0
       call_indirect (type 0)
     )
+    (func $indirect-a:b/c@0.1.0-x2 (;1;) (type 0) (param i32 i32)
+      local.get 0
+      local.get 1
+      i32.const 1
+      call_indirect (type 0)
+    )
     (@producers
       (processed-by "wit-component" "$CARGO_PKG_VERSION")
     )
   )
   (core module (;2;)
     (type (;0;) (func (param i32 i32)))
-    (import "" "1" (func (;0;) (type 0)))
-    (import "" "$imports" (table (;0;) 1 1 funcref))
-    (elem (;0;) (i32.const 0) func 0)
+    (import "" "0" (func (;0;) (type 0)))
+    (import "" "1" (func (;1;) (type 0)))
+    (import "" "$imports" (table (;0;) 2 2 funcref))
+    (elem (;0;) (i32.const 0) func 0 1)
     (@producers
       (processed-by "wit-component" "$CARGO_PKG_VERSION")
     )
@@ -64,26 +75,29 @@
   (core func (;0;) (canon lower (func 0)))
   (alias export 0 "r" (type (;1;)))
   (core func (;1;) (canon resource.drop 1))
-  (alias core export 0 "1" (core func (;2;)))
+  (alias core export 0 "0" (core func (;2;)))
+  (alias core export 0 "1" (core func (;3;)))
   (core instance (;1;)
     (export "[constructor]r" (func 0))
     (export "[resource-drop]r" (func 1))
     (export "x" (func 2))
+    (export "x2" (func 3))
   )
   (alias export 0 "[constructor]r" (func (;1;)))
-  (core func (;3;) (canon lower (func 1)))
+  (core func (;4;) (canon lower (func 1)))
   (alias export 0 "r" (type (;2;)))
-  (core func (;4;) (canon resource.drop 2))
+  (core func (;5;) (canon resource.drop 2))
   (alias export 0 "[method]r.x" (func (;2;)))
-  (core func (;5;) (canon lower (func 2)))
+  (core func (;6;) (canon lower (func 2)))
   (alias export 0 "y" (func (;3;)))
-  (core func (;6;) (canon lower (func 3)))
+  (core func (;7;) (canon lower (func 3)))
   (core instance (;2;)
-    (export "[constructor]r" (func 3))
-    (export "[resource-drop]r" (func 4))
-    (export "[method]r.x" (func 5))
+    (export "[constructor]r" (func 4))
+    (export "[resource-drop]r" (func 5))
+    (export "[method]r.x" (func 6))
     (export "x" (func 2))
-    (export "y" (func 6))
+    (export "x2" (func 3))
+    (export "y" (func 7))
   )
   (core instance (;3;) (instantiate 0
       (with "a:b/c@0.1.0" (instance 1))
@@ -93,10 +107,13 @@
   (alias core export 3 "memory" (core memory (;0;)))
   (alias core export 0 "$imports" (core table (;0;)))
   (alias export 0 "x" (func (;4;)))
-  (core func (;7;) (canon lower (func 4) (memory 0) string-encoding=utf8))
+  (core func (;8;) (canon lower (func 4) (memory 0) string-encoding=utf8))
+  (alias export 0 "x2" (func (;5;)))
+  (core func (;9;) (canon lower (func 5) (memory 0) string-encoding=utf8))
   (core instance (;4;)
     (export "$imports" (table 0))
-    (export "1" (func 7))
+    (export "0" (func 8))
+    (export "1" (func 9))
   )
   (core instance (;5;) (instantiate 2
       (with "" (instance 4))
diff --git a/crates/wit-component/tests/components/merge-import-versions/module.wat b/crates/wit-component/tests/components/merge-import-versions/module.wat
index ce158de3..b23e81cc 100644
--- a/crates/wit-component/tests/components/merge-import-versions/module.wat
+++ b/crates/wit-component/tests/components/merge-import-versions/module.wat
@@ -2,11 +2,13 @@
   (import "a:b/c@0.1.0" "[constructor]r" (func (result i32)))
   (import "a:b/c@0.1.0" "[resource-drop]r" (func (param i32)))
   (import "a:b/c@0.1.0" "x" (func (param i32 i32)))
+  (import "a:b/c@0.1.0" "x2" (func (param i32 i32)))
 
   (import "a:b/c@0.1.1" "[constructor]r" (func (result i32)))
   (import "a:b/c@0.1.1" "[resource-drop]r" (func (param i32)))
   (import "a:b/c@0.1.1" "[method]r.x" (func (param i32)))
   (import "a:b/c@0.1.1" "x" (func (param i32 i32)))
+  (import "a:b/c@0.1.1" "x2" (func (param i32 i32)))
   (import "a:b/c@0.1.1" "y" (func))
 
   (memory (export "memory") 1)
diff --git a/crates/wit-component/tests/components/merge-import-versions/module.wit b/crates/wit-component/tests/components/merge-import-versions/module.wit
index eb7234ec..63dacf41 100644
--- a/crates/wit-component/tests/components/merge-import-versions/module.wit
+++ b/crates/wit-component/tests/components/merge-import-versions/module.wit
@@ -11,12 +11,14 @@ package a:b@0.1.0 {
       constructor();
     }
     x: func(x: string);
+    x2: func(x: string);
   }
 }
 
 package a:b@0.1.1 {
   interface c {
     x: func(x: string);
+    x2: func(x: string);
     y: func();
 
     resource r {