Repository: bytecodealliance/wasm-tools
Pull request #2182 (2025-05-07 20:23:43)

Add the GC canonical option
Disallow using the `core-type` option without `gc`.


Patch:
diff --git a/crates/wasm-encoder/src/component/canonicals.rs b/crates/wasm-encoder/src/component/canonicals.rs
index 5bec980d..867db047 100644
--- a/crates/wasm-encoder/src/component/canonicals.rs
+++ b/crates/wasm-encoder/src/component/canonicals.rs
@@ -29,6 +29,8 @@ pub enum CanonicalOption {
     Callback(u32),
     /// The core function type to lower a component function into.
     CoreType(u32),
+    /// Use the GC variant of the canonical ABI.
+    Gc,
 }
 
 impl Encode for CanonicalOption {
@@ -60,6 +62,9 @@ impl Encode for CanonicalOption {
                 sink.push(0x08);
                 idx.encode(sink);
             }
+            Self::Gc => {
+                sink.push(0x09);
+            }
         }
     }
 }
diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs
index baf7a2ce..718528c7 100644
--- a/crates/wasm-encoder/src/reencode/component.rs
+++ b/crates/wasm-encoder/src/reencode/component.rs
@@ -1393,6 +1393,7 @@ pub mod component_utils {
             wasmparser::CanonicalOption::CoreType(u) => {
                 crate::component::CanonicalOption::CoreType(reencoder.type_index(u)?)
             }
+            wasmparser::CanonicalOption::Gc => crate::component::CanonicalOption::Gc,
         })
     }
 
diff --git a/crates/wasmparser/src/readers/component/canonicals.rs b/crates/wasmparser/src/readers/component/canonicals.rs
index 6e578938..61fb2a95 100644
--- a/crates/wasmparser/src/readers/component/canonicals.rs
+++ b/crates/wasmparser/src/readers/component/canonicals.rs
@@ -30,6 +30,8 @@ pub enum CanonicalOption {
     Callback(u32),
     /// The core function type to lower this component function to.
     CoreType(u32),
+    /// Use the GC version of the canonical ABI.
+    Gc,
 }
 
 /// Represents a canonical function in a WebAssembly component.
@@ -404,6 +406,7 @@ impl<'a> FromReader<'a> for CanonicalOption {
             0x06 => CanonicalOption::Async,
             0x07 => CanonicalOption::Callback(reader.read_var_u32()?),
             0x08 => CanonicalOption::CoreType(reader.read_var_u32()?),
+            0x09 => CanonicalOption::Gc,
             x => return reader.invalid_leading_byte(x, "canonical option"),
         })
     }
diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs
index 174c4b7a..3799be94 100644
--- a/crates/wasmparser/src/validator/component.rs
+++ b/crates/wasmparser/src/validator/component.rs
@@ -239,6 +239,7 @@ pub(crate) struct CanonicalOptions {
     pub(crate) post_return: Option<u32>,
     pub(crate) concurrency: Concurrency,
     pub(crate) core_type: Option<CoreTypeId>,
+    pub(crate) gc: bool,
 }
 
 impl CanonicalOptions {
@@ -304,6 +305,13 @@ impl CanonicalOptions {
             );
         }
 
+        if self.gc && self.core_type.is_none() {
+            bail!(
+                offset,
+                "cannot specify `gc` without also specifying a `core-type` for lowerings"
+            )
+        }
+
         Ok(self)
     }
 
@@ -2334,7 +2342,8 @@ impl ComponentState {
                 CanonicalOption::PostReturn(_) => "post-return",
                 CanonicalOption::Async => "async",
                 CanonicalOption::Callback(_) => "callback",
-                CanonicalOption::CoreType(_) => "core type",
+                CanonicalOption::CoreType(_) => "core-type",
+                CanonicalOption::Gc => "gc",
             }
         }
 
@@ -2345,6 +2354,7 @@ impl ComponentState {
         let mut is_async = false;
         let mut callback = None;
         let mut core_type = None;
+        let mut gc = false;
 
         for option in options {
             match option {
@@ -2475,6 +2485,21 @@ impl ComponentState {
                         }
                     };
                 }
+                CanonicalOption::Gc => {
+                    if gc {
+                        return Err(BinaryReaderError::new(
+                            "canonical option `gc` is specified more than once",
+                            offset,
+                        ));
+                    }
+                    if !self.features.cm_gc() {
+                        return Err(BinaryReaderError::new(
+                            "canonical option `gc` requires the `cm-gc` feature",
+                            offset,
+                        ));
+                    }
+                    gc = true;
+                }
             }
         }
 
@@ -2489,12 +2514,17 @@ impl ComponentState {
             (true, callback, false) => Concurrency::Async { callback },
         };
 
+        if !gc && core_type.is_some() {
+            bail!(offset, "cannot specify `core-type` without `gc`")
+        }
+
         Ok(CanonicalOptions {
             memory,
             realloc,
             post_return,
             concurrency,
             core_type,
+            gc,
         })
     }
 
diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs
index eee550a7..1aa35ce8 100644
--- a/crates/wasmprinter/src/component.rs
+++ b/crates/wasmprinter/src/component.rs
@@ -835,6 +835,7 @@ impl Printer<'_, '_> {
                     self.print_idx(&state.core.type_names, *idx)?;
                     self.end_group()?;
                 }
+                CanonicalOption::Gc => self.result.write_str("gc")?,
             }
         }
         Ok(())
diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs
index 21313521..caf59e0b 100644
--- a/crates/wast/src/component/binary.rs
+++ b/crates/wast/src/component/binary.rs
@@ -976,6 +976,7 @@ impl From<&CanonOpt<'_>> for wasm_encoder::CanonicalOption {
             CanonOpt::Async => Self::Async,
             CanonOpt::Callback(f) => Self::Callback(f.idx.into()),
             CanonOpt::CoreType(t) => Self::CoreType(t.idx.into()),
+            CanonOpt::Gc => Self::Gc,
         }
     }
 }
diff --git a/crates/wast/src/component/func.rs b/crates/wast/src/component/func.rs
index 900fd151..6d1fa031 100644
--- a/crates/wast/src/component/func.rs
+++ b/crates/wast/src/component/func.rs
@@ -951,6 +951,8 @@ pub enum CanonOpt<'a> {
     Callback(CoreItemRef<'a, kw::func>),
     /// Lower this component function into the specified core function type.
     CoreType(CoreItemRef<'a, kw::r#type>),
+    /// Use the GC variant of the canonical ABI.
+    Gc,
 }
 
 impl Default for kw::r#type {
@@ -974,6 +976,9 @@ impl<'a> Parse<'a> for CanonOpt<'a> {
         } else if l.peek::<kw::r#async>()? {
             parser.parse::<kw::r#async>()?;
             Ok(Self::Async)
+        } else if l.peek::<kw::gc>()? {
+            parser.parse::<kw::gc>()?;
+            Ok(Self::Gc)
         } else if l.peek::<LParen>()? {
             parser.parens(|parser| {
                 let mut l = parser.lookahead1();
@@ -1015,6 +1020,7 @@ impl Peek for CanonOpt<'_> {
             || kw::string_utf16::peek(cursor)?
             || kw::string_latin1_utf16::peek(cursor)?
             || kw::r#async::peek(cursor)?
+            || kw::gc::peek(cursor)?
             || match cursor.lparen()? {
                 Some(next) => {
                     kw::memory::peek(next)?
diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs
index 67b5cf65..13c4bd30 100644
--- a/crates/wast/src/component/resolve.rs
+++ b/crates/wast/src/component/resolve.rs
@@ -481,7 +481,8 @@ impl<'a> Resolver<'a> {
                 CanonOpt::StringUtf8
                 | CanonOpt::StringUtf16
                 | CanonOpt::StringLatin1Utf16
-                | CanonOpt::Async => {}
+                | CanonOpt::Async
+                | CanonOpt::Gc => {}
                 CanonOpt::Memory(r) => self.core_item_ref(r)?,
                 CanonOpt::Realloc(r) | CanonOpt::PostReturn(r) | CanonOpt::Callback(r) => {
                     self.core_item_ref(r)?
diff --git a/crates/wast/src/lib.rs b/crates/wast/src/lib.rs
index 366c3847..29ad9e29 100644
--- a/crates/wast/src/lib.rs
+++ b/crates/wast/src/lib.rs
@@ -545,6 +545,7 @@ pub mod kw {
     custom_keyword!(with);
     custom_keyword!(core);
     custom_keyword!(core_type = "core-type");
+    custom_keyword!(gc);
     custom_keyword!(true_ = "true");
     custom_keyword!(false_ = "false");
     custom_keyword!(language);


Test patch:
diff --git a/tests/cli/component-model-gc/core-type.wat b/tests/cli/component-model-gc/core-type.wat
index d1923e57..70e97f62 100644
--- a/tests/cli/component-model-gc/core-type.wat
+++ b/tests/cli/component-model-gc/core-type.wat
@@ -1,194 +1,223 @@
 ;; RUN: wast --assert default --snapshot tests/snapshots % -f gc,cm-gc
 
-(component
-  (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
-  (core type $ty (func (param i32 i32) (result i32)))
-  (core func (canon lower (func $f) (core-type $ty)))
-)
-
 (assert_invalid
   (component
-    (core module $m
-      (memory (export "memory") 1)
-      (func (export "f") (result i32) unreachable)
-    )
-    (core instance $i (instantiate $m))
-
-    (core type $ty (func (result i32)))
-    (func (export "f") (result u32)
-      (canon lift (core func $i "f") (core-type $ty))
-    )
+   (import "f" (func $f (param "x" u32) (result u32)))
+   (core func (canon lower (func $f) gc))
   )
-  "canonical option `core-type` is not allowed in `canon lift`"
+  "cannot specify `gc` without also specifying a `core-type` for lowerings"
 )
 
 (assert_invalid
   (component
-    (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
-    (core type $ty (func (param i64 i32) (result i32)))
-    (core func (canon lower (func $f) (core-type $ty)))
+    (import "f" (func $f (param "x" u32) (result u32)))
+    (core type $ty (func (param i32) (result i32)))
+    (core func (canon lower (func $f) gc gc (core-type $ty)))
   )
-  "declared core type has `[I64, I32]` parameter types, but actual lowering has `[I32, I32]` parameter types"
+  "canonical option `gc` is specified more than once"
 )
 
 (assert_invalid
   (component
-    (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
-    (core type $ty (func (param i32 i32) (result i64)))
+    (import "f" (func $f (param "x" u32) (result u32)))
+    (core type $ty (func (param i32) (result i32)))
     (core func (canon lower (func $f) (core-type $ty)))
   )
-  "declared core type has `[I64]` result types, but actual lowering has `[I32]` result types"
-)
-
-(component
-  (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
-
-  (core rec
-    (type (func))
-    (type $ty (func (param i32 i32) (result i32)))
-  )
-
-  (core func $f (canon lower (func $f) (core-type $ty)))
-
-  (core module $m
-    (rec
-      (type (func))
-      (type $ty (func (param i32 i32) (result i32)))
-    )
-    (import "a" "b" (func (type $ty)))
-  )
-
-  (core instance (instantiate $m (with "a" (instance (export "b" (func $f))))))
-)
-
-;; Satisfying an import with the "same" type but from the wrong rec group 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
-      (rec
-        (type (func))
-        (type $ty (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`"
-)
-
-;; 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))))))
+  "cannot specify `core-type` without `gc`"
 )
 
-;; 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`"
-)
+;; TODO: Rewrite and reenable the following tests once we actually support GC
+;; lifting/lowering.
+
+;; (component
+;;   (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+;;   (core type $ty (func (param i32 i32) (result i32)))
+;;   (core func (canon lower (func $f) (core-type $ty)))
+;; )
+
+;; (assert_invalid
+;;   (component
+;;     (core module $m
+;;       (memory (export "memory") 1)
+;;       (func (export "f") (result i32) unreachable)
+;;     )
+;;     (core instance $i (instantiate $m))
+
+;;     (core type $ty (func (result i32)))
+;;     (func (export "f") (result u32)
+;;       (canon lift (core func $i "f") (core-type $ty))
+;;     )
+;;   )
+;;   "canonical option `core-type` is not allowed in `canon lift`"
+;; )
+
+;; (assert_invalid
+;;   (component
+;;     (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+;;     (core type $ty (func (param i64 i32) (result i32)))
+;;     (core func (canon lower (func $f) (core-type $ty)))
+;;   )
+;;   "declared core type has `[I64, I32]` parameter types, but actual lowering has `[I32, I32]` parameter types"
+;; )
+
+;; (assert_invalid
+;;   (component
+;;     (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+;;     (core type $ty (func (param i32 i32) (result i64)))
+;;     (core func (canon lower (func $f) (core-type $ty)))
+;;   )
+;;   "declared core type has `[I64]` result types, but actual lowering has `[I32]` result types"
+;; )
+
+;; (component
+;;   (import "f" (func $f (param "x" u32) (param "y" u32) (result u32)))
+
+;;   (core rec
+;;     (type (func))
+;;     (type $ty (func (param i32 i32) (result i32)))
+;;   )
+
+;;   (core func $f (canon lower (func $f) (core-type $ty)))
+
+;;   (core module $m
+;;     (rec
+;;       (type (func))
+;;       (type $ty (func (param i32 i32) (result i32)))
+;;     )
+;;     (import "a" "b" (func (type $ty)))
+;;   )
+
+;;   (core instance (instantiate $m (with "a" (instance (export "b" (func $f))))))
+;; )
+
+;; ;; Satisfying an import with the "same" type but from the wrong rec group 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
+;;       (rec
+;;         (type (func))
+;;         (type $ty (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`"
+;; )
+
+;; ;; 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 e2b33aeb..ad809fde 100644
--- a/tests/snapshots/cli/component-model-gc/core-type.wat.json
+++ b/tests/snapshots/cli/component-model-gc/core-type.wat.json
@@ -2,84 +2,25 @@
   "source_filename": "tests/cli/component-model-gc/core-type.wat",
   "commands": [
     {
-      "type": "module",
-      "line": 3,
+      "type": "assert_invalid",
+      "line": 4,
       "filename": "core-type.0.wasm",
-      "module_type": "binary"
+      "module_type": "binary",
+      "text": "cannot specify `gc` without also specifying a `core-type` for lowerings"
     },
     {
       "type": "assert_invalid",
-      "line": 10,
+      "line": 12,
       "filename": "core-type.1.wasm",
       "module_type": "binary",
-      "text": "canonical option `core-type` is not allowed in `canon lift`"
+      "text": "canonical option `gc` is specified more than once"
     },
     {
       "type": "assert_invalid",
-      "line": 26,
+      "line": 21,
       "filename": "core-type.2.wasm",
       "module_type": "binary",
-      "text": "declared core type has `[I64, I32]` parameter types, but actual lowering has `[I32, I32]` parameter types"
-    },
-    {
-      "type": "assert_invalid",
-      "line": 35,
-      "filename": "core-type.3.wasm",
-      "module_type": "binary",
-      "text": "declared core type has `[I64]` result types, but actual lowering has `[I32]` result types"
-    },
-    {
-      "type": "module",
-      "line": 43,
-      "filename": "core-type.4.wasm",
-      "module_type": "binary"
-    },
-    {
-      "type": "assert_invalid",
-      "line": 67,
-      "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`"
+      "text": "cannot specify `core-type` without `gc`"
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/snapshots/cli/component-model-gc/core-type.wat/0.print b/tests/snapshots/cli/component-model-gc/core-type.wat/0.print
deleted file mode 100644
index b38ce886..00000000
--- a/tests/snapshots/cli/component-model-gc/core-type.wat/0.print
+++ /dev/null
@@ -1,6 +0,0 @@
-(component
-  (type (;0;) (func (param "x" u32) (param "y" u32) (result u32)))
-  (import "f" (func $f (;0;) (type 0)))
-  (core type $ty (;0;) (func (param i32 i32) (result i32)))
-  (core func (;0;) (canon lower (func $f) (core-type $ty)))
-)
diff --git a/tests/snapshots/cli/component-model-gc/core-type.wat/4.print b/tests/snapshots/cli/component-model-gc/core-type.wat/4.print
deleted file mode 100644
index a2de79d0..00000000
--- a/tests/snapshots/cli/component-model-gc/core-type.wat/4.print
+++ /dev/null
@@ -1,23 +0,0 @@
-(component
-  (type (;0;) (func (param "x" u32) (param "y" u32) (result u32)))
-  (import "f" (func $f (;0;) (type 0)))
-  (core rec
-    (type (;0;) (func))
-    (type $ty (;1;) (func (param i32 i32) (result i32)))
-  )
-  (core func $f (;0;) (canon lower (func $f) (core-type $ty)))
-  (core module $m (;0;)
-    (rec
-      (type (;0;) (func))
-      (type $ty (;1;) (func (param i32 i32) (result i32)))
-    )
-    (import "a" "b" (func (;0;) (type $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/6.print b/tests/snapshots/cli/component-model-gc/core-type.wat/6.print
deleted file mode 100644
index f6092884..00000000
--- a/tests/snapshots/cli/component-model-gc/core-type.wat/6.print
+++ /dev/null
@@ -1,19 +0,0 @@
-(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
deleted file mode 100644
index 6ebd058e..00000000
--- a/tests/snapshots/cli/component-model-gc/core-type.wat/7.print
+++ /dev/null
@@ -1,18 +0,0 @@
-(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))
-    )
-  )
-)