Repository: bytecodealliance/wasm-tools
Pull request #2173 (2025-04-30 23:33:25)

wasmparser(CM+GC): start doing subtyping checking when validating components' core function linking
Builds on top of https://github.com/bytecodealliance/wasm-tools/pull/2172


Patch:
diff --git a/crates/wasmparser/src/validator/component_types.rs b/crates/wasmparser/src/validator/component_types.rs
index 4e78cd1a..159fb84c 100644
--- a/crates/wasmparser/src/validator/component_types.rs
+++ b/crates/wasmparser/src/validator/component_types.rs
@@ -3093,7 +3093,7 @@ impl<'a> SubtypeCx<'a> {
     fn core_func_type(&self, a: CoreTypeId, b: CoreTypeId, offset: usize) -> Result<()> {
         debug_assert!(self.a.get(a).is_some());
         debug_assert!(self.b.get(b).is_some());
-        if a == b {
+        if self.a.id_is_subtype(a, b) {
             debug_assert!(self.a.get(b).is_some());
             debug_assert!(self.b.get(a).is_some());
             Ok(())
@@ -3368,6 +3368,18 @@ impl<'a> SubtypeArena<'a> {
             self.list.get(temp_id)
         }
     }
+
+    /// Is `a == b` or was `a` declared (potentially transitively) to be a
+    /// subtype of `b`?
+    fn id_is_subtype(&self, a: CoreTypeId, b: CoreTypeId) -> bool {
+        self.get(a).is_some() && self.get(b).is_some() && {
+            // NB: we can query `self.types.id_is_subtype` directly, and ignore
+            // `self.list`, because `self.list` should never contain core types.
+            debug_assert!(a.index() < CoreTypeId::list(self.types).len());
+            debug_assert!(b.index() < CoreTypeId::list(self.types).len());
+            self.types.id_is_subtype(a, b)
+        }
+    }
 }
 
 impl<T> Index<T> for SubtypeArena<'_>


Test patch:
diff --git a/tests/cli/component-model-gc/core-type.wat b/tests/cli/component-model-gc/core-type.wat
index d3b7276d..d1923e57 100644
--- a/tests/cli/component-model-gc/core-type.wat
+++ b/tests/cli/component-model-gc/core-type.wat
@@ -82,3 +82,113 @@
   )
   "type mismatch for export `b` of module instantiation argument `a`"
 )
+
+;; Satisfying an import with an exact subtype should succeed.
+(component
+  (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+
+  (core type $super_ty (sub (func (param i32 i32) (result i32))))
+  (core type $sub_ty (sub $super_ty (func (param i32 i32) (result i32))))
+
+  (core func $f (canon lower (func $f) (core-type $sub_ty)))
+
+  (core module $m
+    (type $super_ty (sub (func (param i32 i32) (result i32))))
+    (type $sub_ty (sub $super_ty (func (param i32 i32) (result i32))))
+    (import "a" "b" (func (type $sub_ty)))
+  )
+
+  (core instance (instantiate $m (with "a" (instance (export "b" (func $f))))))
+)
+
+;; Satisfying an import with a subtype should succeed.
+(component
+  (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+
+  (core type $super_ty (sub (func (param i32 i32) (result i32))))
+  (core type $sub_ty (sub $super_ty (func (param i32 i32) (result i32))))
+
+  (core func $f (canon lower (func $f) (core-type $sub_ty)))
+
+  (core module $m
+    (type $super_ty (sub (func (param i32 i32) (result i32))))
+    (import "a" "b" (func (type $super_ty)))
+  )
+
+  (core instance (instantiate $m (with "a" (instance (export "b" (func $f))))))
+)
+
+;; Satisfying an import with the "same" type but from a different subtyping
+;; hierarchy should fail.
+(assert_invalid
+  (component
+    (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+
+    (core type $ty (func (param i32 i32) (result i32)))
+    (core func $f (canon lower (func $f) (core-type $ty)))
+
+    (core module $m
+      (type $super_ty (sub (func (param i32 i32) (result i32))))
+      (type $sub_ty (sub $super_ty (func (param i32 i32) (result i32))))
+      (import "a" "b" (func (type $sub_ty)))
+    )
+
+    (core instance (instantiate $m (with "a" (instance (export "b" (func $f))))))
+  )
+  "type mismatch for export `b` of module instantiation argument `a`"
+)
+
+;; Satisfying an import with the "same" type but is a supertype, rather than a
+;; subtype, should fail.
+(assert_invalid
+  (component
+    (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+
+    (core type $super_ty (sub (func (param i32 i32) (result i32))))
+    (core func $f (canon lower (func $f) (core-type $super_ty)))
+
+    (core module $m
+      (type $super_ty (sub (func (param i32 i32) (result i32))))
+      (type $sub_ty (sub $super_ty (func (param i32 i32) (result i32))))
+      (import "a" "b" (func (type $sub_ty)))
+    )
+
+    (core instance (instantiate $m (with "a" (instance (export "b" (func $f))))))
+  )
+  "type mismatch for export `b` of module instantiation argument `a`"
+)
+
+;; Satisfying an import with the "same" type but with the wrong finality should
+;; fail.
+(assert_invalid
+  (component
+    (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+
+    (core type $ty (sub final (func (param i32 i32) (result i32))))
+    (core func $f (canon lower (func $f) (core-type $ty)))
+
+    (core module $m
+      (type $ty (sub (func (param i32 i32) (result i32))))
+      (import "a" "b" (func (type $ty)))
+    )
+
+    (core instance (instantiate $m (with "a" (instance (export "b" (func $f))))))
+  )
+  "type mismatch for export `b` of module instantiation argument `a`"
+)
+(assert_invalid
+  (component
+    (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+
+    (core type $ty (sub (func (param i32 i32) (result i32))))
+    (core func $f (canon lower (func $f) (core-type $ty)))
+
+    (core module $m
+      (type $ty (sub final (func (param i32 i32) (result i32))))
+      (import "a" "b" (func (type $ty)))
+    )
+
+    (core instance (instantiate $m (with "a" (instance (export "b" (func $f))))))
+  )
+  "type mismatch for export `b` of module instantiation argument `a`"
+)
diff --git a/tests/snapshots/cli/component-model-gc/core-type.wat.json b/tests/snapshots/cli/component-model-gc/core-type.wat.json
index 42068df4..e2b33aeb 100644
--- a/tests/snapshots/cli/component-model-gc/core-type.wat.json
+++ b/tests/snapshots/cli/component-model-gc/core-type.wat.json
@@ -40,6 +40,46 @@
       "filename": "core-type.5.wasm",
       "module_type": "binary",
       "text": "type mismatch for export `b` of module instantiation argument `a`"
+    },
+    {
+      "type": "module",
+      "line": 87,
+      "filename": "core-type.6.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 105,
+      "filename": "core-type.7.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 124,
+      "filename": "core-type.8.wasm",
+      "module_type": "binary",
+      "text": "type mismatch for export `b` of module instantiation argument `a`"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 144,
+      "filename": "core-type.9.wasm",
+      "module_type": "binary",
+      "text": "type mismatch for export `b` of module instantiation argument `a`"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 164,
+      "filename": "core-type.10.wasm",
+      "module_type": "binary",
+      "text": "type mismatch for export `b` of module instantiation argument `a`"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 180,
+      "filename": "core-type.11.wasm",
+      "module_type": "binary",
+      "text": "type mismatch for export `b` of module instantiation argument `a`"
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/snapshots/cli/component-model-gc/core-type.wat/6.print b/tests/snapshots/cli/component-model-gc/core-type.wat/6.print
new file mode 100644
index 00000000..f6092884
--- /dev/null
+++ b/tests/snapshots/cli/component-model-gc/core-type.wat/6.print
@@ -0,0 +1,19 @@
+(component
+  (type (;0;) (func (param "x" u32) (param "y" u32) (result u32)))
+  (import "f" (func $f (;0;) (type 0)))
+  (core type $super_ty (;0;) (sub (func (param i32 i32) (result i32))))
+  (core type $sub_ty (;1;) (sub $super_ty (func (param i32 i32) (result i32))))
+  (core func $f (;0;) (canon lower (func $f) (core-type $sub_ty)))
+  (core module $m (;0;)
+    (type $super_ty (;0;) (sub (func (param i32 i32) (result i32))))
+    (type $sub_ty (;1;) (sub $super_ty (func (param i32 i32) (result i32))))
+    (import "a" "b" (func (;0;) (type $sub_ty)))
+  )
+  (core instance (;0;)
+    (export "b" (func $f))
+  )
+  (core instance (;1;) (instantiate $m
+      (with "a" (instance 0))
+    )
+  )
+)
diff --git a/tests/snapshots/cli/component-model-gc/core-type.wat/7.print b/tests/snapshots/cli/component-model-gc/core-type.wat/7.print
new file mode 100644
index 00000000..6ebd058e
--- /dev/null
+++ b/tests/snapshots/cli/component-model-gc/core-type.wat/7.print
@@ -0,0 +1,18 @@
+(component
+  (type (;0;) (func (param "x" u32) (param "y" u32) (result u32)))
+  (import "f" (func $f (;0;) (type 0)))
+  (core type $super_ty (;0;) (sub (func (param i32 i32) (result i32))))
+  (core type $sub_ty (;1;) (sub $super_ty (func (param i32 i32) (result i32))))
+  (core func $f (;0;) (canon lower (func $f) (core-type $sub_ty)))
+  (core module $m (;0;)
+    (type $super_ty (;0;) (sub (func (param i32 i32) (result i32))))
+    (import "a" "b" (func (;0;) (type $super_ty)))
+  )
+  (core instance (;0;)
+    (export "b" (func $f))
+  )
+  (core instance (;1;) (instantiate $m
+      (with "a" (instance 0))
+    )
+  )
+)