Repository: bytecodealliance/wasm-tools
Pull request #2230 (2025-06-10 01:05:16)

wasmparser(CM+GC): Fill in `Options::core_type` during `Options::check_lift`
We need the core type in order to check that the core and component signatures match.

Before this commit, the new test cases would panic at runtime.


Patch:
diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs
index 78bf499b..40bebf46 100644
--- a/crates/wasmparser/src/validator/component.rs
+++ b/crates/wasmparser/src/validator/component.rs
@@ -336,15 +336,23 @@ impl CanonicalOptions {
     }
 
     pub(crate) fn check_lift(
-        &self,
+        &mut self,
         types: &TypeList,
         state: &ComponentState,
-        core_ty: &FuncType,
+        core_ty_id: CoreTypeId,
         offset: usize,
     ) -> Result<&Self> {
+        debug_assert!(matches!(
+            types[core_ty_id].composite_type.inner,
+            CompositeInnerType::Func(_)
+        ));
+
         if let Some(idx) = self.post_return {
-            let func_ty = types[state.core_function_at(idx, offset)?].unwrap_func();
-            if func_ty.params() != core_ty.results() || !func_ty.results().is_empty() {
+            let post_return_func_ty = types[state.core_function_at(idx, offset)?].unwrap_func();
+            let core_ty = types[core_ty_id].unwrap_func();
+            if post_return_func_ty.params() != core_ty.results()
+                || !post_return_func_ty.results().is_empty()
+            {
                 bail!(
                     offset,
                     "canonical option `post-return` uses a core function with an incorrect signature"
@@ -379,6 +387,7 @@ impl CanonicalOptions {
                 "canonical option `core-type` is not allowed in `canon lift`"
             )
         }
+        self.core_type = Some(core_ty_id);
 
         Ok(self)
     }
@@ -1252,14 +1261,12 @@ impl ComponentState {
     ) -> Result<()> {
         let ty = self.function_type_at(type_index, types, offset)?;
         let core_ty_id = self.core_function_at(core_func_index, offset)?;
-        let core_ty = types[core_ty_id].unwrap_func();
 
         // Lifting a function is for an export, so match the expected canonical ABI
         // export signature
-        let options = self.check_options(types, options, offset)?;
-        options.check_lift(types, self, core_ty, offset)?;
+        let mut options = self.check_options(types, options, offset)?;
+        options.check_lift(types, self, core_ty_id, offset)?;
         let func_ty = ty.lower(types, &options, Abi::Lift, offset)?;
-        debug_assert!(options.core_type.is_none());
         let lowered_core_ty_id = func_ty.intern(types, offset);
 
         if core_ty_id == lowered_core_ty_id {
diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs
index a8987698..ff5ae291 100644
--- a/crates/wasmparser/src/validator/core.rs
+++ b/crates/wasmparser/src/validator/core.rs
@@ -1174,13 +1174,11 @@ impl WasmModuleResources for ValidatorResources {
 }
 
 const _: () = {
-    fn assert_send<T: Send>() {}
+    const fn assert_send<T: Send>() {}
 
     // Assert that `ValidatorResources` is Send so function validation
     // can be parallelizable
-    fn assert() {
-        assert_send::<ValidatorResources>();
-    }
+    assert_send::<ValidatorResources>();
 };
 
 mod arc {


Test patch:
diff --git a/tests/cli/component-model-gc/lift-func-many-params.wast b/tests/cli/component-model-gc/lift-func-many-params.wast
new file mode 100644
index 00000000..ce6363a3
--- /dev/null
+++ b/tests/cli/component-model-gc/lift-func-many-params.wast
@@ -0,0 +1,73 @@
+;; RUN: wast --assert default --snapshot tests/snapshots % -f gc,cm-gc
+
+(component
+  (core module $m
+    (type $string (array i8))
+    (type $list (array i64))
+    (type $record (struct (field i32) (field f32)))
+    (type $tuple (struct (field i8) (field i16)))
+
+    (func (export "f") (param i32           ;; bool
+                              i32           ;; s8
+                              i32           ;; u8
+                              i32           ;; s16
+                              i32           ;; u16
+                              i32           ;; s32
+                              i32           ;; u32
+                              i64           ;; s64
+                              i64           ;; u64
+                              f32           ;; f32
+                              f64           ;; f64
+                              (ref $string) ;; string
+                              i32           ;; enum
+                              i32           ;; flags
+                              (ref $list)   ;; list
+                              (ref $record) ;; record
+                              (ref extern)  ;; (own resource)
+                              (ref extern)  ;; (borrow resource)
+                              (ref $tuple)  ;; tuple
+                              ))
+  )
+
+  (core instance $i (instantiate $m))
+
+  (type $enum' (enum "a" "b" "c"))
+  (export $enum "enum" (type $enum'))
+
+  (type $flags' (flags "d" "e" "f"))
+  (export $flags "flags" (type $flags'))
+
+  (type $list' (list u64))
+  (export $list "list" (type $list'))
+
+  (type $record' (record (field "g" u32) (field "h" f32)))
+  (export $record "record" (type $record'))
+
+  (type $resource' (resource (rep i32)))
+  (export $resource "resource" (type $resource'))
+
+  (type $tuple' (tuple u8 u16))
+  (export $tuple "tuple" (type $tuple'))
+
+  (func (export "f") (param "p0" bool)
+                     (param "p1" s8)
+                     (param "p2" u8)
+                     (param "p3" s16)
+                     (param "p4" u16)
+                     (param "p5" s32)
+                     (param "p6" u32)
+                     (param "p7" s64)
+                     (param "p8" u64)
+                     (param "p9" f32)
+                     (param "p10" f64)
+                     (param "p11" string)
+                     (param "p12" $enum)
+                     (param "p13" $flags)
+                     (param "p14" $list)
+                     (param "p15" $record)
+                     (param "p16" (own $resource))
+                     (param "p17" (borrow $resource))
+                     (param "p18" $tuple)
+    (canon lift (core func $i "f") gc)
+  )
+)
diff --git a/tests/cli/component-model-gc/lift-func-no-params.wast b/tests/cli/component-model-gc/lift-func-no-params.wast
new file mode 100644
index 00000000..c78bb12f
--- /dev/null
+++ b/tests/cli/component-model-gc/lift-func-no-params.wast
@@ -0,0 +1,13 @@
+;; RUN: wast --assert default --snapshot tests/snapshots % -f gc,cm-gc
+
+(component $A
+  (core module $m
+    (func (export "f"))
+  )
+
+  (core instance $i (instantiate $m))
+
+  (func (export "f")
+    (canon lift (core func $i "f") gc)
+  )
+)
diff --git a/tests/snapshots/cli/component-model-gc/lift-func-many-params.wast.json b/tests/snapshots/cli/component-model-gc/lift-func-many-params.wast.json
new file mode 100644
index 00000000..4119fa1e
--- /dev/null
+++ b/tests/snapshots/cli/component-model-gc/lift-func-many-params.wast.json
@@ -0,0 +1,11 @@
+{
+  "source_filename": "tests/cli/component-model-gc/lift-func-many-params.wast",
+  "commands": [
+    {
+      "type": "module",
+      "line": 3,
+      "filename": "lift-func-many-params.0.wasm",
+      "module_type": "binary"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/cli/component-model-gc/lift-func-many-params.wast/0.print b/tests/snapshots/cli/component-model-gc/lift-func-many-params.wast/0.print
new file mode 100644
index 00000000..b3406949
--- /dev/null
+++ b/tests/snapshots/cli/component-model-gc/lift-func-many-params.wast/0.print
@@ -0,0 +1,30 @@
+(component
+  (core module $m (;0;)
+    (type $string (;0;) (array i8))
+    (type $list (;1;) (array i64))
+    (type $record (;2;) (struct (field i32) (field f32)))
+    (type $tuple (;3;) (struct (field i8) (field i16)))
+    (type (;4;) (func (param i32 i32 i32 i32 i32 i32 i32 i64 i64 f32 f64 (ref $string) i32 i32 (ref $list) (ref $record) (ref extern) (ref extern) (ref $tuple))))
+    (export "f" (func 0))
+    (func (;0;) (type 4) (param i32 i32 i32 i32 i32 i32 i32 i64 i64 f32 f64 (ref $string) i32 i32 (ref $list) (ref $record) (ref extern) (ref extern) (ref $tuple)))
+  )
+  (core instance $i (;0;) (instantiate $m))
+  (type $enum' (;0;) (enum "a" "b" "c"))
+  (export $enum (;1;) "enum" (type $enum'))
+  (type $flags' (;2;) (flags "d" "e" "f"))
+  (export $flags (;3;) "flags" (type $flags'))
+  (type $list' (;4;) (list u64))
+  (export $list (;5;) "list" (type $list'))
+  (type $record' (;6;) (record (field "g" u32) (field "h" f32)))
+  (export $record (;7;) "record" (type $record'))
+  (type $resource' (;8;) (resource (rep i32)))
+  (export $resource (;9;) "resource" (type $resource'))
+  (type $tuple' (;10;) (tuple u8 u16))
+  (export $tuple (;11;) "tuple" (type $tuple'))
+  (type (;12;) (own $resource))
+  (type (;13;) (borrow $resource))
+  (type (;14;) (func (param "p0" bool) (param "p1" s8) (param "p2" u8) (param "p3" s16) (param "p4" u16) (param "p5" s32) (param "p6" u32) (param "p7" s64) (param "p8" u64) (param "p9" f32) (param "p10" f64) (param "p11" string) (param "p12" $enum) (param "p13" $flags) (param "p14" $list) (param "p15" $record) (param "p16" 12) (param "p17" 13) (param "p18" $tuple)))
+  (alias core export $i "f" (core func (;0;)))
+  (func (;0;) (type 14) (canon lift (core func 0) gc))
+  (export (;1;) "f" (func 0))
+)
diff --git a/tests/snapshots/cli/component-model-gc/lift-func-no-params.wast.json b/tests/snapshots/cli/component-model-gc/lift-func-no-params.wast.json
new file mode 100644
index 00000000..a137a49c
--- /dev/null
+++ b/tests/snapshots/cli/component-model-gc/lift-func-no-params.wast.json
@@ -0,0 +1,12 @@
+{
+  "source_filename": "tests/cli/component-model-gc/lift-func-no-params.wast",
+  "commands": [
+    {
+      "type": "module",
+      "line": 3,
+      "name": "A",
+      "filename": "lift-func-no-params.0.wasm",
+      "module_type": "binary"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/cli/component-model-gc/lift-func-no-params.wast/0.print b/tests/snapshots/cli/component-model-gc/lift-func-no-params.wast/0.print
new file mode 100644
index 00000000..f9eba369
--- /dev/null
+++ b/tests/snapshots/cli/component-model-gc/lift-func-no-params.wast/0.print
@@ -0,0 +1,12 @@
+(component $A
+  (core module $m (;0;)
+    (type (;0;) (func))
+    (export "f" (func 0))
+    (func (;0;) (type 0))
+  )
+  (core instance $i (;0;) (instantiate $m))
+  (type (;0;) (func))
+  (alias core export $i "f" (core func (;0;)))
+  (func (;0;) (type 0) (canon lift (core func 0) gc))
+  (export (;1;) "f" (func 0))
+)