Repository: bytecodealliance/wasm-tools
Pull request #2160 (2025-04-28 12:17:50)

wasm-smith: add support for `--exports` and `--available-imports` option
This PR fixes issue https://github.com/bytecodealliance/wasm-tools/issues/2110 (sorry for the lateness):

1. It allows wasm-smith to generate the corresponding tag, table, and memory exports as specified by the `--exports` option. Note that I have also modified the function signatures of **arbitrary_const_expr** and **globals_for_const_expr** and added a new parameter called *allow_defined_globals*. This is because the **required_exports** is called after **arbitrary_globals** in the **build** function, which results in wasm-smith potentially *picking up defined globals for table initialization expressions* and that is not allowed in the current Wasm standard;
2. It allows Wasm modules with GC types to appear in the `--available-imports` option. To achieve this, the current solution is to copy all the recursive groups in the provided module into the module being constructed. And the necessary trait implementation is also added (i.e., `From` trait);

Please feel free to share any suggestions/concerns you might have :).


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 37d28184..a540e3bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2105,6 +2105,7 @@ dependencies = [
  "env_logger",
  "libfuzzer-sys",
  "log",
+ "rand",
  "tempfile",
  "wasm-encoder 0.229.0",
  "wasm-mutate",
diff --git a/Cargo.toml b/Cargo.toml
index 7ce17299..b0b1736a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,6 +65,8 @@ rust-2024-incompatible-pat = 'warn'
 missing-unsafe-on-extern = 'warn'
 unsafe-op-in-unsafe-fn = 'warn'
 
+unexpected_cfgs = { level = 'warn', check-cfg = ['cfg(fuzzing)'] }
+
 [workspace.lints.clippy]
 # The default set of lints in Clippy is viewed as "too noisy" right now so
 # they're all turned off by default. Selective lints are then enabled below as
diff --git a/crates/wasm-compose/Cargo.toml b/crates/wasm-compose/Cargo.toml
index 50e25014..e77a0325 100644
--- a/crates/wasm-compose/Cargo.toml
+++ b/crates/wasm-compose/Cargo.toml
@@ -27,10 +27,10 @@ serde_yaml = "0.9.22"
 smallvec = "1.10.0"
 wasm-encoder = { workspace = true, features = ['std', 'wasmparser', 'component-model'] }
 wasmparser = { workspace = true, features = ['std', 'validate', 'component-model', 'features'] }
-wat = { workspace = true }
+wat = { workspace = true, features = ['component-model'] }
 
 [dev-dependencies]
 glob = "0.3.0"
 pretty_assertions = "1.2.1"
-wasmprinter = { workspace = true }
+wasmprinter = { workspace = true, features = ['component-model'] }
 wit-component = { workspace = true }
diff --git a/crates/wasm-compose/src/composer.rs b/crates/wasm-compose/src/composer.rs
index 84ce8339..d3c6e52a 100644
--- a/crates/wasm-compose/src/composer.rs
+++ b/crates/wasm-compose/src/composer.rs
@@ -14,7 +14,7 @@ use std::{collections::VecDeque, ffi::OsStr, path::Path};
 use wasmparser::{
     component_types::{ComponentEntityType, ComponentInstanceTypeId},
     types::TypesRef,
-    ComponentExternalKind, ComponentTypeRef,
+    ComponentExternalKind, ComponentTypeRef, Validator, WasmFeatures,
 };
 
 /// The root component name used in configuration.
@@ -69,12 +69,19 @@ struct CompositionGraphBuilder<'a> {
     instances: IndexMap<String, InstanceId>,
     /// The definition components in the graph.
     definitions: Vec<(ComponentId, Option<InstanceId>)>,
+    /// Wasm validator and shared type arenas.
+    validator: Validator,
 }
 
 impl<'a> CompositionGraphBuilder<'a> {
     fn new(root_path: &Path, config: &'a Config) -> Result<Self> {
         let mut graph = CompositionGraph::new();
-        graph.add_component(Component::from_file(ROOT_COMPONENT_NAME, root_path)?)?;
+        let mut validator = Validator::new_with_features(WasmFeatures::all());
+        graph.add_component(Component::from_file(
+            &mut validator,
+            ROOT_COMPONENT_NAME,
+            root_path,
+        )?)?;
 
         let definitions = config
             .definitions
@@ -87,7 +94,7 @@ impl<'a> CompositionGraphBuilder<'a> {
                     )
                 })?;
 
-                let component = Component::from_file(name, config.dir.join(path))?;
+                let component = Component::from_file(&mut validator, name, config.dir.join(path))?;
 
                 Ok((graph.add_component(component)?, None))
             })
@@ -98,6 +105,7 @@ impl<'a> CompositionGraphBuilder<'a> {
             graph,
             instances: Default::default(),
             definitions,
+            validator,
         })
     }
 
@@ -117,7 +125,7 @@ impl<'a> CompositionGraphBuilder<'a> {
     }
 
     /// Finds the component with the given name on disk.
-    fn find_component(&self, name: &str) -> Result<Option<Component<'a>>> {
+    fn find_component(&mut self, name: &str) -> Result<Option<Component<'a>>> {
         // Check the config for an explicit path (must be a valid component)
         if let Some(dep) = self.config.dependencies.get(name) {
             log::debug!(
@@ -125,6 +133,7 @@ impl<'a> CompositionGraphBuilder<'a> {
                 path = dep.path.display()
             );
             return Ok(Some(Component::from_file(
+                &mut self.validator,
                 name,
                 self.config.dir.join(&dep.path),
             )?));
@@ -133,7 +142,7 @@ impl<'a> CompositionGraphBuilder<'a> {
         // Otherwise, search the paths for a valid component with the same name
         log::info!("searching for a component with name `{name}`");
         for dir in std::iter::once(&self.config.dir).chain(self.config.search_paths.iter()) {
-            if let Some(component) = Self::parse_component(dir, name)? {
+            if let Some(component) = Self::parse_component(&mut self.validator, dir, name)? {
                 return Ok(Some(component));
             }
         }
@@ -144,7 +153,11 @@ impl<'a> CompositionGraphBuilder<'a> {
     /// Parses a component from the given directory, if it exists.
     ///
     /// Returns `Ok(None)` if the component does not exist.
-    fn parse_component(dir: &Path, name: &str) -> Result<Option<Component<'a>>> {
+    fn parse_component(
+        validator: &mut Validator,
+        dir: &Path,
+        name: &str,
+    ) -> Result<Option<Component<'a>>> {
         let mut path = dir.join(name);
 
         for ext in ["wasm", "wat"] {
@@ -154,7 +167,7 @@ impl<'a> CompositionGraphBuilder<'a> {
                 continue;
             }
 
-            return Ok(Some(Component::from_file(name, &path)?));
+            return Ok(Some(Component::from_file(validator, name, &path)?));
         }
 
         Ok(None)
diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs
index 87a8d473..8fcf875c 100644
--- a/crates/wasm-compose/src/encoding.rs
+++ b/crates/wasm-compose/src/encoding.rs
@@ -649,6 +649,9 @@ impl<'a> TypeEncoder<'a> {
             ComponentDefinedType::Record(r) => self.record(state, r),
             ComponentDefinedType::Variant(v) => self.variant(state, v),
             ComponentDefinedType::List(ty) => self.list(state, *ty),
+            ComponentDefinedType::FixedSizeList(ty, elements) => {
+                self.fixed_size_list(state, *ty, *elements)
+            }
             ComponentDefinedType::Tuple(t) => self.tuple(state, t),
             ComponentDefinedType::Flags(names) => Self::flags(&mut state.cur.encodable, names),
             ComponentDefinedType::Enum(cases) => Self::enum_type(&mut state.cur.encodable, cases),
@@ -709,6 +712,23 @@ impl<'a> TypeEncoder<'a> {
         index
     }
 
+    fn fixed_size_list(
+        &self,
+        state: &mut TypeState<'a>,
+        ty: ct::ComponentValType,
+        elements: u32,
+    ) -> u32 {
+        let ty = self.component_val_type(state, ty);
+        let index = state.cur.encodable.type_count();
+        state
+            .cur
+            .encodable
+            .ty()
+            .defined_type()
+            .fixed_size_list(ty, elements);
+        index
+    }
+
     fn tuple(&self, state: &mut TypeState<'a>, tuple: &TupleType) -> u32 {
         let types = tuple
             .types
@@ -1228,7 +1248,9 @@ impl DependencyRegistrar<'_, '_> {
             ComponentDefinedType::Primitive(_)
             | ComponentDefinedType::Enum(_)
             | ComponentDefinedType::Flags(_) => {}
-            ComponentDefinedType::List(t) | ComponentDefinedType::Option(t) => self.val_type(*t),
+            ComponentDefinedType::List(t)
+            | ComponentDefinedType::FixedSizeList(t, _)
+            | ComponentDefinedType::Option(t) => self.val_type(*t),
             ComponentDefinedType::Own(r) | ComponentDefinedType::Borrow(r) => {
                 self.ty(ComponentAnyTypeId::Resource(*r))
             }
diff --git a/crates/wasm-compose/src/graph.rs b/crates/wasm-compose/src/graph.rs
index 99f17aec..440e2930 100644
--- a/crates/wasm-compose/src/graph.rs
+++ b/crates/wasm-compose/src/graph.rs
@@ -18,7 +18,7 @@ use wasmparser::{
     names::ComponentName,
     types::{Types, TypesRef},
     Chunk, ComponentExternalKind, ComponentTypeRef, Encoding, Parser, Payload, ValidPayload,
-    Validator, WasmFeatures,
+    Validator,
 };
 
 pub(crate) fn type_desc(item: ComponentEntityType) -> &'static str {
@@ -50,10 +50,15 @@ pub struct Component<'a> {
 
 impl<'a> Component<'a> {
     /// Constructs a new component from reading the given file.
-    pub fn from_file(name: &str, path: impl AsRef<Path>) -> Result<Self> {
+    pub fn from_file(
+        validator: &mut Validator,
+        name: &str,
+        path: impl AsRef<Path>,
+    ) -> Result<Self> {
         let path = path.as_ref();
         log::info!("parsing WebAssembly component file `{}`", path.display());
         let component = Self::parse(
+            validator,
             ComponentName::new(name, 0)?.to_string(),
             Some(path.to_owned()),
             wat::parse_file(path)
@@ -73,7 +78,11 @@ impl<'a> Component<'a> {
     }
 
     /// Constructs a new component from the given bytes.
-    pub fn from_bytes(name: impl Into<String>, bytes: impl Into<Cow<'a, [u8]>>) -> Result<Self> {
+    pub fn from_bytes(
+        validator: &mut Validator,
+        name: impl Into<String>,
+        bytes: impl Into<Cow<'a, [u8]>>,
+    ) -> Result<Self> {
         let mut bytes = bytes.into();
 
         match wat::parse_bytes(bytes.as_ref()).context("failed to parse component")? {
@@ -85,6 +94,7 @@ impl<'a> Component<'a> {
 
         log::info!("parsing WebAssembly component from bytes");
         let component = Self::parse(
+            validator,
             ComponentName::new(&name.into(), 0)?.to_string(),
             None,
             bytes,
@@ -96,13 +106,19 @@ impl<'a> Component<'a> {
         Ok(component)
     }
 
-    fn parse(name: String, path: Option<PathBuf>, bytes: Cow<'a, [u8]>) -> Result<Self> {
+    fn parse(
+        validator: &mut Validator,
+        name: String,
+        path: Option<PathBuf>,
+        bytes: Cow<'a, [u8]>,
+    ) -> Result<Self> {
         let mut parser = Parser::new(0);
         let mut parsers = Vec::new();
-        let mut validator = Validator::new_with_features(WasmFeatures::all());
         let mut imports = IndexMap::new();
         let mut exports = IndexMap::new();
 
+        validator.reset();
+
         let mut cur = bytes.as_ref();
         loop {
             match parser.parse(cur, true)? {
@@ -1036,7 +1052,8 @@ mod test {
 
     #[test]
     fn it_rejects_modules() -> Result<()> {
-        match Component::from_bytes("a", b"(module)".as_ref()) {
+        let mut validator = Validator::new();
+        match Component::from_bytes(&mut validator, "a", b"(module)".as_ref()) {
             Ok(_) => panic!("expected a failure to parse"),
             Err(e) => assert_eq!(
                 format!("{e:#}"),
@@ -1049,7 +1066,8 @@ mod test {
 
     #[test]
     fn it_rejects_invalid_components() -> Result<()> {
-        match Component::from_bytes("a", b"(component (export \"x\" (func 0)))".as_ref()) {
+        let mut validator = Validator::new();
+        match Component::from_bytes(&mut validator, "a", b"(component (export \"x\" (func 0)))".as_ref()) {
             Ok(_) => panic!("expected a failure to parse"),
             Err(e) => assert_eq!(format!("{e:#}"), "failed to parse component: unknown function 0: function index out of bounds (at offset 0xb)"),
         }
@@ -1060,9 +1078,18 @@ mod test {
     #[test]
     fn it_ensures_unique_component_names() -> Result<()> {
         let mut graph = CompositionGraph::new();
-        graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
+        let mut validator = Validator::new();
+        graph.add_component(Component::from_bytes(
+            &mut validator,
+            "a",
+            b"(component)".as_ref(),
+        )?)?;
 
-        match graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?) {
+        match graph.add_component(Component::from_bytes(
+            &mut validator,
+            "a",
+            b"(component)".as_ref(),
+        )?) {
             Ok(_) => panic!("expected a failure to add component"),
             Err(e) => assert_eq!(format!("{e:#}"), "a component with name `a` already exists"),
         }
@@ -1084,7 +1111,12 @@ mod test {
     #[test]
     fn it_instantiates_a_component() -> Result<()> {
         let mut graph = CompositionGraph::new();
-        let id = graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
+        let mut validator = Validator::new();
+        let id = graph.add_component(Component::from_bytes(
+            &mut validator,
+            "a",
+            b"(component)".as_ref(),
+        )?)?;
         let id = graph.instantiate(id)?;
         assert_eq!(graph.get_component_of_instance(id).unwrap().1.name(), "a");
         Ok(())
@@ -1100,7 +1132,12 @@ mod test {
     #[test]
     fn it_gets_a_component() -> Result<()> {
         let mut graph = CompositionGraph::new();
-        let id = graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
+        let mut validator = Validator::new();
+        let id = graph.add_component(Component::from_bytes(
+            &mut validator,
+            "a",
+            b"(component)".as_ref(),
+        )?)?;
         assert_eq!(graph.get_component(id).unwrap().name(), "a");
         assert_eq!(graph.get_component_by_name("a").unwrap().1.name(), "a");
         Ok(())
@@ -1109,11 +1146,14 @@ mod test {
     #[test]
     fn it_removes_a_component() -> Result<()> {
         let mut graph = CompositionGraph::new();
+        let mut validator = Validator::new();
         let a = graph.add_component(Component::from_bytes(
+            &mut validator,
             "a",
             b"(component (import \"x\" (func)))".as_ref(),
         )?)?;
         let b = graph.add_component(Component::from_bytes(
+            &mut validator,
             "b",
             b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
         )?)?;
@@ -1142,11 +1182,14 @@ mod test {
     #[test]
     fn it_removes_a_connection() -> Result<()> {
         let mut graph = CompositionGraph::new();
+        let mut validator = Validator::new();
         let a = graph.add_component(Component::from_bytes(
+            &mut validator,
             "a",
             b"(component (import \"x\" (func)))".as_ref(),
         )?)?;
         let b = graph.add_component(Component::from_bytes(
+            &mut validator,
             "b",
             b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
         )?)?;
@@ -1167,11 +1210,14 @@ mod test {
     #[test]
     fn it_requires_source_to_disconnect() -> Result<()> {
         let mut graph = CompositionGraph::new();
+        let mut validator = Validator::new();
         let a = graph.add_component(Component::from_bytes(
+            &mut validator,
             "a",
             b"(component (import \"x\" (func)))".as_ref(),
         )?)?;
         let b = graph.add_component(Component::from_bytes(
+            &mut validator,
             "b",
             b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
         )?)?;
@@ -1193,11 +1239,14 @@ mod test {
     #[test]
     fn it_requires_a_target_to_disconnect() -> Result<()> {
         let mut graph = CompositionGraph::new();
+        let mut validator = Validator::new();
         let a = graph.add_component(Component::from_bytes(
+            &mut validator,
             "a",
             b"(component (import \"x\" (func)))".as_ref(),
         )?)?;
         let b = graph.add_component(Component::from_bytes(
+            &mut validator,
             "b",
             b"(component (import \"x\" (func)) (export \"y\" (func 0)))".as_ref(),
         )?)?;
@@ -1219,12 +1268,15 @@ mod test {
     #[test]
     fn it_validates_connections() -> Result<()> {
         let mut graph = CompositionGraph::new();
+        let mut validator = Validator::new();
         let a = graph.add_component(Component::from_bytes(
+            &mut validator,
             "a",
             b"(component (import \"i1\" (func)) (import \"i2\" (instance (export \"no\" (func)))))"
                 .as_ref(),
         )?)?;
         let b = graph.add_component(Component::from_bytes(
+            &mut validator,
             "b",
             b"(component (import \"i1\" (func)) (import \"i2\" (core module)) (export \"e1\" (func 0)) (export \"e2\" (core module 0)))".as_ref(),
         )?)?;
@@ -1300,11 +1352,14 @@ mod test {
     #[test]
     fn it_cannot_encode_a_cycle() -> Result<()> {
         let mut graph = CompositionGraph::new();
+        let mut validator = Validator::new();
         let a = graph.add_component(Component::from_bytes(
+            &mut validator,
             "a",
             b"(component (import \"i1\" (func)) (export \"e1\" (func 0)))".as_ref(),
         )?)?;
         let b = graph.add_component(Component::from_bytes(
+            &mut validator,
             "b",
             b"(component (import \"i1\" (func)) (export \"e1\" (func 0)))".as_ref(),
         )?)?;
@@ -1329,8 +1384,17 @@ mod test {
     #[test]
     fn it_encodes_an_empty_component() -> Result<()> {
         let mut graph = CompositionGraph::new();
-        graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
-        graph.add_component(Component::from_bytes("b", b"(component)".as_ref())?)?;
+        let mut validator = Validator::new();
+        graph.add_component(Component::from_bytes(
+            &mut validator,
+            "a",
+            b"(component)".as_ref(),
+        )?)?;
+        graph.add_component(Component::from_bytes(
+            &mut validator,
+            "b",
+            b"(component)".as_ref(),
+        )?)?;
 
         let encoded = graph.encode(EncodeOptions {
             define_components: false,
@@ -1351,9 +1415,18 @@ mod test {
     #[test]
     fn it_encodes_component_imports() -> Result<()> {
         let mut graph = CompositionGraph::new();
+        let mut validator = Validator::new();
         // Add a component that doesn't get instantiated (shouldn't be imported)
-        graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
-        let b = graph.add_component(Component::from_bytes("b", b"(component)".as_ref())?)?;
+        graph.add_component(Component::from_bytes(
+            &mut validator,
+            "a",
+            b"(component)".as_ref(),
+        )?)?;
+        let b = graph.add_component(Component::from_bytes(
+            &mut validator,
+            "b",
+            b"(component)".as_ref(),
+        )?)?;
         graph.instantiate(b)?;
 
         let encoded = graph.encode(EncodeOptions {
@@ -1381,9 +1454,18 @@ mod test {
     #[test]
     fn it_encodes_defined_components() -> Result<()> {
         let mut graph = CompositionGraph::new();
+        let mut validator = Validator::new();
         // Add a component that doesn't get instantiated (shouldn't be imported)
-        graph.add_component(Component::from_bytes("a", b"(component)".as_ref())?)?;
-        let b = graph.add_component(Component::from_bytes("b", b"(component)".as_ref())?)?;
+        graph.add_component(Component::from_bytes(
+            &mut validator,
+            "a",
+            b"(component)".as_ref(),
+        )?)?;
+        let b = graph.add_component(Component::from_bytes(
+            &mut validator,
+            "b",
+            b"(component)".as_ref(),
+        )?)?;
         graph.instantiate(b)?;
 
         let encoded = graph.encode(EncodeOptions {
@@ -1408,7 +1490,9 @@ mod test {
     #[test]
     fn it_encodes_a_simple_composition() -> Result<()> {
         let mut graph = CompositionGraph::new();
+        let mut validator = Validator::new();
         let a = graph.add_component(Component::from_bytes(
+            &mut validator,
             "a",
             b"(component
   (type (tuple u32 u32))
@@ -1426,6 +1510,7 @@ mod test {
             .as_ref(),
         )?)?;
         let b = graph.add_component(Component::from_bytes(
+            &mut validator,
             "b",
             b"(component
   (type (tuple u32 u32))
diff --git a/crates/wasm-encoder/src/component/canonicals.rs b/crates/wasm-encoder/src/component/canonicals.rs
index bd1c4ba0..5bec980d 100644
--- a/crates/wasm-encoder/src/component/canonicals.rs
+++ b/crates/wasm-encoder/src/component/canonicals.rs
@@ -27,6 +27,8 @@ pub enum CanonicalOption {
     /// The function to use if the async lifting of a function should receive task/stream/future progress events
     /// using a callback.
     Callback(u32),
+    /// The core function type to lower a component function into.
+    CoreType(u32),
 }
 
 impl Encode for CanonicalOption {
@@ -54,6 +56,10 @@ impl Encode for CanonicalOption {
                 sink.push(0x07);
                 idx.encode(sink);
             }
+            Self::CoreType(idx) => {
+                sink.push(0x08);
+                idx.encode(sink);
+            }
         }
     }
 }
@@ -212,7 +218,7 @@ impl CanonicalFunctionSection {
 
     /// Defines a function to acknowledge cancellation of the current task.
     pub fn task_cancel(&mut self) -> &mut Self {
-        self.bytes.push(0x25);
+        self.bytes.push(0x05);
         self.num_added += 1;
         self
     }
@@ -255,7 +261,7 @@ impl CanonicalFunctionSection {
 
     /// Defines a function to cancel an in-progress task.
     pub fn subtask_cancel(&mut self, async_: bool) -> &mut Self {
-        self.bytes.push(0x24);
+        self.bytes.push(0x06);
         self.bytes.push(if async_ { 1 } else { 0 });
         self.num_added += 1;
         self
diff --git a/crates/wasm-encoder/src/component/types.rs b/crates/wasm-encoder/src/component/types.rs
index e32c75c4..6d6a8f7b 100644
--- a/crates/wasm-encoder/src/component/types.rs
+++ b/crates/wasm-encoder/src/component/types.rs
@@ -593,6 +593,13 @@ impl ComponentDefinedTypeEncoder<'_> {
         ty.into().encode(self.0);
     }
 
+    /// Define a fixed size list type.
+    pub fn fixed_size_list(self, ty: impl Into<ComponentValType>, elements: u32) {
+        self.0.push(0x67);
+        ty.into().encode(self.0);
+        elements.encode(self.0);
+    }
+
     /// Define a tuple type.
     pub fn tuple<I, T>(self, types: I)
     where
diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs
index 3d50dfca..4b0ff50f 100644
--- a/crates/wasm-encoder/src/reencode/component.rs
+++ b/crates/wasm-encoder/src/reencode/component.rs
@@ -767,6 +767,9 @@ pub mod component_utils {
             wasmparser::ComponentDefinedType::List(t) => {
                 defined.list(reencoder.component_val_type(t));
             }
+            wasmparser::ComponentDefinedType::FixedSizeList(t, elements) => {
+                defined.fixed_size_list(reencoder.component_val_type(t), elements);
+            }
             wasmparser::ComponentDefinedType::Tuple(t) => {
                 defined.tuple(t.iter().map(|t| reencoder.component_val_type(*t)));
             }
@@ -1364,6 +1367,9 @@ pub mod component_utils {
             wasmparser::CanonicalOption::Callback(u) => {
                 crate::component::CanonicalOption::Callback(reencoder.function_index(u))
             }
+            wasmparser::CanonicalOption::CoreType(u) => {
+                crate::component::CanonicalOption::CoreType(reencoder.type_index(u))
+            }
         }
     }
 
diff --git a/crates/wasm-smith/src/config.rs b/crates/wasm-smith/src/config.rs
index a23ed457..f28b22c1 100644
--- a/crates/wasm-smith/src/config.rs
+++ b/crates/wasm-smith/src/config.rs
@@ -55,7 +55,7 @@ macro_rules! define_config {
             /// module. The implementation (e.g. function bodies, global
             /// initializers) of each export in the generated module will be
             /// random and unrelated to the implementation in the provided
-            /// module. Only globals and functions are supported.
+            /// module.
             ///
             ///
             /// Defaults to `None` which means arbitrary exports will be
@@ -73,12 +73,13 @@ macro_rules! define_config {
             ///
             /// # Module Limits
             ///
-            /// All types, functions, globals, and exports that are needed to
-            /// provide the required exports will be generated, even if it
-            /// causes the resulting module to exceed the limits defined in
-            /// [`Self::max_type_size`], [`Self::max_types`],
-            /// [`Self::max_funcs`], [`Self::max_globals`], or
-            /// [`Self::max_exports`].
+            /// All types, functions, globals, memories, tables, tags, and exports
+            /// that are needed to provide the required exports will be generated,
+            /// even if it causes the resulting module to exceed the limits defined
+            /// in [`Self::max_type_size`], [`Self::max_types`],
+            /// [`Self::max_funcs`], [`Self::max_globals`],
+            /// [`Self::max_memories`], [`Self::max_tables`],
+            /// [`Self::max_tags`], or [`Self::max_exports`].
             ///
             /// # Example
             ///
@@ -90,6 +91,9 @@ macro_rules! define_config {
             ///     (module
             ///         (func (export "foo") (param i32) (result i64) unreachable)
             ///         (global (export "bar") f32 f32.const 0)
+            ///         (memory (export "baz") 1 10)
+            ///         (table (export "qux") 5 10 (ref null extern))
+            ///         (tag (export "quux") (param f32))
             ///     )
             /// "#));
             /// ```
@@ -141,7 +145,7 @@ macro_rules! define_config {
             /// module. The implementation (e.g. function bodies, global
             /// initializers) of each export in the generated module will be
             /// random and unrelated to the implementation in the provided
-            /// module. Only globals and functions are supported.
+            /// module.
             ///
             /// Defaults to `None` which means arbitrary exports will be
             /// generated.
@@ -158,12 +162,13 @@ macro_rules! define_config {
             ///
             /// # Module Limits
             ///
-            /// All types, functions, globals, and exports that are needed to
-            /// provide the required exports will be generated, even if it
-            /// causes the resulting module to exceed the limits defined in
-            /// [`Self::max_type_size`], [`Self::max_types`],
-            /// [`Self::max_funcs`], [`Self::max_globals`], or
-            /// [`Self::max_exports`].
+            /// All types, functions, globals, memories, tables, tags, and exports
+            /// that are needed to provide the required exports will be generated,
+            /// even if it causes the resulting module to exceed the limits defined
+            /// in [`Self::max_type_size`], [`Self::max_types`],
+            /// [`Self::max_funcs`], [`Self::max_globals`],
+            /// [`Self::max_memories`], [`Self::max_tables`],
+            /// [`Self::max_tags`], or [`Self::max_exports`].
             ///
             #[cfg_attr(feature = "clap", clap(long))]
             exports: Option<std::path::PathBuf>,
diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs
index 71550dea..9f9fd495 100644
--- a/crates/wasm-smith/src/core.rs
+++ b/crates/wasm-smith/src/core.rs
@@ -1310,18 +1310,26 @@ impl Module {
         // First, parse the module-by-example to collect the types and imports.
         //
         // `available_types` will map from a signature index (which is the same as the index into
-        // this vector) as it appears in the parsed code, to the type itself as well as to the
-        // index in our newly generated module. Initially the option is `None` and will become a
-        // `Some` when we encounter an import that uses this signature in the next portion of this
-        // function. See also the `make_func_type` closure below.
-        let mut available_types = Vec::new();
+        // this vector) as it appears in the parsed code, to the type itself. We copy all the types
+        // from module-by-example into the module being constructed for the sake of simplicity
+        // and for this reason, [`Self::config::max_types`] may be surpassed.
+        let mut new_recgrps = Vec::<usize>::new();
+        let mut available_types = Vec::<SubType>::new();
         let mut available_imports = Vec::<wasmparser::Import>::new();
         for payload in wasmparser::Parser::new(0).parse_all(&example_module) {
             match payload.expect("could not parse the available import payload") {
                 wasmparser::Payload::TypeSection(type_reader) => {
-                    for ty in type_reader.into_iter_err_on_gc_types() {
-                        let ty = ty.expect("could not parse type section");
-                        available_types.push((ty, None));
+                    for recgrp in type_reader {
+                        let recgrp = recgrp.expect("could not read recursive group");
+                        new_recgrps.push(recgrp.types().len());
+                        for subtype in recgrp.into_types() {
+                            let mut subtype: SubType = subtype.try_into().unwrap();
+                            if let Some(supertype_idx) = subtype.supertype {
+                                assert!(supertype_idx < (available_types.len() as u32));
+                                subtype.depth = available_types[supertype_idx as usize].depth + 1;
+                            }
+                            available_types.push(subtype);
+                        }
                     }
                 }
                 wasmparser::Payload::ImportSection(import_reader) => {
@@ -1340,73 +1348,32 @@ impl Module {
             }
         }
 
-        // In this function we need to place imported function/tag types in the types section and
-        // generate import entries (which refer to said types) at the same time.
-        let max_types = self.config.max_types;
-        let multi_value_enabled = self.config.multi_value_enabled;
+        // We then generate import entries which refer to the imported types. Since this function
+        // is called at the very beginning of the module generation process and all types from the
+        // module-by-example are copied into the current module, no further adjustments are needed
+        // for type indices.
         let mut new_imports = Vec::with_capacity(available_imports.len());
-        let first_type_index = self.types.len();
-        let mut new_types = Vec::<SubType>::new();
-
-        // Returns the index to the translated type in the to-be type section, and the reference to
-        // the type itself.
-        let mut make_func_type = |module: &Self, parsed_sig_idx: u32| {
-            let serialized_sig_idx = match available_types.get_mut(parsed_sig_idx as usize) {
-                None => panic!("signature index refers to a type out of bounds"),
-                Some((_, Some(idx))) => *idx as usize,
-                Some((func_type, index_store)) => {
-                    let multi_value_required = func_type.results().len() > 1;
-                    let new_index = first_type_index + new_types.len();
-                    if new_index >= max_types || (multi_value_required && !multi_value_enabled) {
-                        return None;
-                    }
-                    let func_type = Rc::new(FuncType {
-                        params: func_type
-                            .params()
-                            .iter()
-                            .map(|t| (*t).try_into().unwrap())
-                            .collect(),
-                        results: func_type
-                            .results()
-                            .iter()
-                            .map(|t| (*t).try_into().unwrap())
-                            .collect(),
-                    });
-                    index_store.replace(new_index as u32);
-                    let shared = module.arbitrary_shared(u).ok()?;
-                    new_types.push(SubType {
-                        is_final: true,
-                        supertype: None,
-                        composite_type: CompositeType::new_func(Rc::clone(&func_type), shared),
-                        depth: 1,
-                    });
-                    new_index
-                }
-            };
-            match &new_types[serialized_sig_idx - first_type_index]
-                .composite_type
-                .inner
-            {
-                CompositeInnerType::Func(f) => Some((serialized_sig_idx as u32, Rc::clone(f))),
-                _ => unimplemented!(),
-            }
-        };
-
         for import in available_imports {
             let type_size_budget = self.config.max_type_size - self.type_size;
             let entity_type = match &import.ty {
                 wasmparser::TypeRef::Func(sig_idx) => {
                     if self.funcs.len() >= self.config.max_funcs {
                         continue;
-                    } else if let Some((sig_idx, func_type)) = make_func_type(&self, *sig_idx) {
-                        let entity = EntityType::Func(sig_idx, Rc::clone(&func_type));
-                        if type_size_budget < entity.size() {
-                            continue;
-                        }
-                        self.funcs.push((sig_idx, func_type));
-                        entity
                     } else {
-                        continue;
+                        match available_types.get(*sig_idx as usize) {
+                            None => panic!("signature index refers to a type out of bounds"),
+                            Some(ty) => match &ty.composite_type.inner {
+                                CompositeInnerType::Func(func_type) => {
+                                    let entity = EntityType::Func(*sig_idx, Rc::clone(func_type));
+                                    if type_size_budget < entity.size() {
+                                        continue;
+                                    }
+                                    self.funcs.push((*sig_idx, Rc::clone(func_type)));
+                                    entity
+                                }
+                                _ => panic!("a function type is required for function import"),
+                            },
+                        }
                     }
                 }
 
@@ -1414,20 +1381,27 @@ impl Module {
                     let can_add_tag = self.tags.len() < self.config.max_tags;
                     if !self.config.exceptions_enabled || !can_add_tag {
                         continue;
-                    } else if let Some((sig_idx, func_type)) = make_func_type(&self, *func_type_idx)
-                    {
-                        let tag_type = TagType {
-                            func_type_idx: sig_idx,
-                            func_type,
-                        };
-                        let entity = EntityType::Tag(tag_type.clone());
-                        if type_size_budget < entity.size() {
-                            continue;
-                        }
-                        self.tags.push(tag_type);
-                        entity
                     } else {
-                        continue;
+                        match available_types.get(*func_type_idx as usize) {
+                            None => {
+                                panic!("function type index for tag refers to a type out of bounds")
+                            }
+                            Some(ty) => match &ty.composite_type.inner {
+                                CompositeInnerType::Func(func_type) => {
+                                    let tag_type = TagType {
+                                        func_type_idx: *func_type_idx,
+                                        func_type: Rc::clone(func_type),
+                                    };
+                                    let entity = EntityType::Tag(tag_type.clone());
+                                    if type_size_budget < entity.size() {
+                                        continue;
+                                    }
+                                    self.tags.push(tag_type);
+                                    entity
+                                }
+                                _ => panic!("a function type is required for tag import"),
+                            },
+                        }
                     }
                 }
 
@@ -1456,7 +1430,7 @@ impl Module {
                 }
 
                 wasmparser::TypeRef::Global(global_ty) => {
-                    let global_ty = (*global_ty).try_into().unwrap();
+                    let global_ty = GlobalType::try_from(*global_ty).unwrap();
                     let entity = EntityType::Global(global_ty);
                     let type_size = entity.size();
                     if type_size_budget < type_size || !self.can_add_local_or_import_global() {
@@ -1476,8 +1450,13 @@ impl Module {
         }
 
         // Finally, add the entities we just generated.
-        for ty in new_types {
-            self.rec_groups.push(self.types.len()..self.types.len() + 1);
+        let mut recgrp_start_idx = self.types.len();
+        for size in new_recgrps {
+            self.rec_groups
+                .push(recgrp_start_idx..recgrp_start_idx + size);
+            recgrp_start_idx += size;
+        }
+        for ty in available_types {
             self.add_type(ty);
         }
         self.imports.extend(new_imports);
@@ -1656,9 +1635,7 @@ impl Module {
                     return Ok(false);
                 }
                 let ty = arbitrary_table_type(u, self.config(), Some(self))?;
-                let init = self.arbitrary_table_init(u, ty.element_type)?;
-                self.defined_tables.push(init);
-                self.tables.push(ty);
+                self.add_arbitrary_table_of_type(ty, u)?;
                 Ok(true)
             },
         )
@@ -1683,7 +1660,9 @@ impl Module {
         if ty.nullable && u.arbitrary()? {
             return Ok(None);
         }
-        let expr = self.arbitrary_const_expr(ValType::Ref(ty), u)?;
+        // Only imported globals are allowed in the constant initialization
+        // expressions for tables.
+        let expr = self.arbitrary_const_expr(ValType::Ref(ty), u, false)?;
         Ok(Some(expr))
     }
 
@@ -1696,8 +1675,8 @@ impl Module {
                 if !self.can_add_local_or_import_memory() {
                     return Ok(false);
                 }
-                self.num_defined_memories += 1;
-                self.memories.push(arbitrary_memtype(u, self.config())?);
+                let ty = arbitrary_memtype(u, self.config())?;
+                self.add_arbitrary_memory_of_type(ty)?;
                 Ok(true)
             },
         )
@@ -1709,22 +1688,44 @@ impl Module {
         ty: GlobalType,
         u: &mut Unstructured,
     ) -> Result<u32> {
-        let expr = self.arbitrary_const_expr(ty.val_type, u)?;
+        let expr = self.arbitrary_const_expr(ty.val_type, u, true)?;
         let global_idx = self.globals.len() as u32;
         self.globals.push(ty);
         self.defined_globals.push((global_idx, expr));
         Ok(global_idx)
     }
 
+    /// Add a new memory of the given type and return its memory index.
+    fn add_arbitrary_memory_of_type(&mut self, ty: MemoryType) -> Result<u32> {
+        let memory_idx = self.memories.len() as u32;
+        self.num_defined_memories += 1;
+        self.memories.push(ty);
+        Ok(memory_idx)
+    }
+
+    /// Add a new table of the given type and return its table index.
+    fn add_arbitrary_table_of_type(&mut self, ty: TableType, u: &mut Unstructured) -> Result<u32> {
+        let expr = self.arbitrary_table_init(u, ty.element_type)?;
+        let table_idx = self.tables.len() as u32;
+        self.tables.push(ty);
+        self.defined_tables.push(expr);
+        Ok(table_idx)
+    }
+
     /// Generates an arbitrary constant expression of the type `ty`.
-    fn arbitrary_const_expr(&mut self, ty: ValType, u: &mut Unstructured) -> Result<ConstExpr> {
+    fn arbitrary_const_expr(
+        &mut self,
+        ty: ValType,
+        u: &mut Unstructured,
+        allow_defined_globals: bool,
+    ) -> Result<ConstExpr> {
         let mut choices = mem::take(&mut self.const_expr_choices);
         choices.clear();
 
         // MVP wasm can `global.get` any immutable imported global in a
         // constant expression, and the GC proposal enables this for all
         // globals, so make all matching globals a candidate.
-        for i in self.globals_for_const_expr(ty) {
+        for i in self.globals_for_const_expr(ty, allow_defined_globals) {
             choices.push(Box::new(move |_, _| Ok(ConstExpr::global_get(i))));
         }
 
@@ -1920,6 +1921,52 @@ impl Module {
 
         // For each export, add necessary prerequisites to the module.
         let exports_types = exports_types.as_ref();
+        let check_and_get_func_type =
+            |id: wasmparser::types::CoreTypeId| -> (Rc<FuncType>, SubType) {
+                let subtype = exports_types.get(id).unwrap_or_else(|| {
+                    panic!("Unable to get subtype for {id:?} in `exports` Wasm")
+                });
+                match &subtype.composite_type.inner {
+                    wasmparser::CompositeInnerType::Func(func_type) => {
+                        assert!(
+                            subtype.is_final,
+                            "Subtype {subtype:?} from `exports` Wasm is not final"
+                        );
+                        assert!(
+                            subtype.supertype_idx.is_none(),
+                            "Subtype {subtype:?} from `exports` Wasm has non-empty supertype"
+                        );
+                        let func_type = Rc::new(FuncType {
+                            params: func_type
+                                .params()
+                                .iter()
+                                .copied()
+                                .map(|t| t.try_into().unwrap())
+                                .collect(),
+                            results: func_type
+                                .results()
+                                .iter()
+                                .copied()
+                                .map(|t| t.try_into().unwrap())
+                                .collect(),
+                        });
+                        let subtype = SubType {
+                            is_final: true,
+                            supertype: None,
+                            depth: 1,
+                            composite_type: CompositeType::new_func(
+                                Rc::clone(&func_type),
+                                subtype.composite_type.shared,
+                            ),
+                        };
+                        (func_type, subtype)
+                    }
+                    _ => panic!(
+                        "Unable to handle type {:?} from `exports` Wasm",
+                        subtype.composite_type
+                    ),
+                }
+            };
         for export in required_exports {
             let new_index = match exports_types
                 .entity_type_from_export(&export)
@@ -1928,65 +1975,38 @@ impl Module {
                 }) {
                 // For functions, add the type and a function with that type.
                 wasmparser::types::EntityType::Func(id) => {
-                    let subtype = exports_types.get(id).unwrap_or_else(|| {
-                        panic!("Unable to get subtype for function {id:?} in `exports` Wasm")
-                    });
-                    match &subtype.composite_type.inner {
-                        wasmparser::CompositeInnerType::Func(func_type) => {
-                            assert!(
-                                subtype.is_final,
-                                "Subtype {subtype:?} from `exports` Wasm is not final"
-                            );
-                            assert!(
-                                subtype.supertype_idx.is_none(),
-                                "Subtype {subtype:?} from `exports` Wasm has non-empty supertype"
-                            );
-                            let new_type = Rc::new(FuncType {
-                                params: func_type
-                                    .params()
-                                    .iter()
-                                    .copied()
-                                    .map(|t| t.try_into().unwrap())
-                                    .collect(),
-                                results: func_type
-                                    .results()
-                                    .iter()
-                                    .copied()
-                                    .map(|t| t.try_into().unwrap())
-                                    .collect(),
-                            });
-                            self.rec_groups.push(self.types.len()..self.types.len() + 1);
-                            let type_index = self.add_type(SubType {
-                                is_final: true,
-                                supertype: None,
-                                depth: 1,
-                                composite_type: CompositeType::new_func(
-                                    Rc::clone(&new_type),
-                                    subtype.composite_type.shared,
-                                ),
-                            });
-                            let func_index = self.funcs.len() as u32;
-                            self.funcs.push((type_index, new_type));
-                            self.num_defined_funcs += 1;
-                            func_index
-                        }
-                        _ => panic!(
-                            "Unable to handle type {:?} from `exports` Wasm",
-                            subtype.composite_type
-                        ),
-                    }
+                    let (func_type, subtype) = check_and_get_func_type(id);
+                    self.rec_groups.push(self.types.len()..self.types.len() + 1);
+                    let type_index = self.add_type(subtype);
+                    let func_index = self.funcs.len() as u32;
+                    self.funcs.push((type_index, func_type));
+                    self.num_defined_funcs += 1;
+                    func_index
                 }
                 // For globals, add a new global.
                 wasmparser::types::EntityType::Global(global_type) => {
                     self.add_arbitrary_global_of_type(global_type.try_into().unwrap(), u)?
                 }
-                wasmparser::types::EntityType::Table(_)
-                | wasmparser::types::EntityType::Memory(_)
-                | wasmparser::types::EntityType::Tag(_) => {
-                    panic!(
-                        "Config `exports` has an export of type {:?} which cannot yet be handled.",
-                        export.kind
-                    )
+                // For memories, add a new memory.
+                wasmparser::types::EntityType::Memory(memory_type) => {
+                    self.add_arbitrary_memory_of_type(memory_type.into())?
+                }
+                // For tables, add a new table.
+                wasmparser::types::EntityType::Table(table_type) => {
+                    self.add_arbitrary_table_of_type(table_type.try_into().unwrap(), u)?
+                }
+                // For tags, add the type.
+                wasmparser::types::EntityType::Tag(id) => {
+                    let (func_type, subtype) = check_and_get_func_type(id);
+                    self.rec_groups.push(self.types.len()..self.types.len() + 1);
+                    let type_index = self.add_type(subtype);
+                    let tag_index = self.tags.len() as u32;
+                    self.tags.push(TagType {
+                        func_type_idx: type_index,
+                        func_type: func_type,
+                    });
+                    self.num_defined_tags += 1;
+                    tag_index
                 }
             };
             self.exports
@@ -2102,10 +2122,10 @@ impl Module {
         let mut global_i32 = vec![];
         let mut global_i64 = vec![];
         if !self.config.disallow_traps {
-            for i in self.globals_for_const_expr(ValType::I32) {
+            for i in self.globals_for_const_expr(ValType::I32, true) {
                 global_i32.push(i);
             }
-            for i in self.globals_for_const_expr(ValType::I64) {
+            for i in self.globals_for_const_expr(ValType::I64, true) {
                 global_i64.push(i);
             }
         }
@@ -2264,7 +2284,7 @@ impl Module {
                 } else {
                     let mut init = vec![];
                     arbitrary_loop(u, self.config.min_elements, max, |u| {
-                        init.push(self.arbitrary_const_expr(ValType::Ref(ty), u)?);
+                        init.push(self.arbitrary_const_expr(ValType::Ref(ty), u, true)?);
                         Ok(true)
                     })?;
                     Elements::Expressions(init)
@@ -2349,10 +2369,10 @@ impl Module {
             ))
         }));
         if !self.config.disallow_traps {
-            for i in self.globals_for_const_expr(ValType::I32) {
+            for i in self.globals_for_const_expr(ValType::I32, true) {
                 choices32.push(Box::new(move |_, _, _| Ok(Offset::Global(i))));
             }
-            for i in self.globals_for_const_expr(ValType::I64) {
+            for i in self.globals_for_const_expr(ValType::I64, true) {
                 choices64.push(Box::new(move |_, _, _| Ok(Offset::Global(i))));
             }
         }
@@ -2442,11 +2462,15 @@ impl Module {
 
     /// Returns an iterator of all globals which can be used in constant
     /// expressions for a value of type `ty` specified.
-    fn globals_for_const_expr(&self, ty: ValType) -> impl Iterator<Item = u32> + '_ {
+    fn globals_for_const_expr(
+        &self,
+        ty: ValType,
+        allow_defined_globals: bool,
+    ) -> impl Iterator<Item = u32> + '_ {
         // Before the GC proposal only imported globals could be referenced, but
         // the GC proposal relaxed this feature to allow any global.
         let num_imported_globals = self.globals.len() - self.defined_globals.len();
-        let max_global = if self.config.gc_enabled {
+        let max_global = if self.config.gc_enabled && allow_defined_globals {
             self.globals.len()
         } else {
             num_imported_globals
@@ -3111,3 +3135,73 @@ impl FromStr for InstructionKind {
         }
     }
 }
+
+// Conversions from `wasmparser` to `wasm-smith`. Currently, only type conversions
+// have been implemented.
+#[cfg(feature = "wasmparser")]
+impl TryFrom<wasmparser::FuncType> for FuncType {
+    type Error = ();
+
+    fn try_from(value: wasmparser::FuncType) -> Result<Self, Self::Error> {
+        Ok(FuncType {
+            params: value
+                .params()
+                .iter()
+                .copied()
+                .map(|ty| ty.try_into().map_err(|_| ()))
+                .collect::<Result<Vec<_>, _>>()?,
+            results: value
+                .results()
+                .iter()
+                .copied()
+                .map(|ty| ty.try_into().map_err(|_| ()))
+                .collect::<Result<Vec<_>, _>>()?,
+        })
+    }
+}
+
+#[cfg(feature = "wasmparser")]
+impl TryFrom<wasmparser::CompositeType> for CompositeType {
+    type Error = ();
+
+    fn try_from(value: wasmparser::CompositeType) -> Result<Self, Self::Error> {
+        let inner_type = match value.inner {
+            wasmparser::CompositeInnerType::Func(func_type) => {
+                CompositeInnerType::Func(Rc::new(func_type.try_into()?))
+            }
+            wasmparser::CompositeInnerType::Array(array_type) => {
+                CompositeInnerType::Array(array_type.try_into().map_err(|_| ())?)
+            }
+            wasmparser::CompositeInnerType::Struct(struct_type) => {
+                CompositeInnerType::Struct(struct_type.try_into().map_err(|_| ())?)
+            }
+            wasmparser::CompositeInnerType::Cont(_) => {
+                panic!("continuation type is not supported by wasm-smith currently.")
+            }
+        };
+
+        Ok(CompositeType {
+            inner: inner_type,
+            shared: value.shared,
+        })
+    }
+}
+
+#[cfg(feature = "wasmparser")]
+impl TryFrom<wasmparser::SubType> for SubType {
+    type Error = ();
+
+    fn try_from(value: wasmparser::SubType) -> Result<Self, Self::Error> {
+        Ok(SubType {
+            is_final: value.is_final,
+            supertype: value
+                .supertype_idx
+                .map(|idx| idx.as_module_index().ok_or(()))
+                .transpose()?,
+            composite_type: value.composite_type.try_into()?,
+            // We cannot determine the depth of current subtype here, set it to 1
+            // temporarily and fix it later.
+            depth: 1,
+        })
+    }
+}
diff --git a/crates/wasm-wave/src/value/ty.rs b/crates/wasm-wave/src/value/ty.rs
index 8898892b..85205197 100644
--- a/crates/wasm-wave/src/value/ty.rs
+++ b/crates/wasm-wave/src/value/ty.rs
@@ -10,6 +10,7 @@ pub struct Type(pub(super) TypeEnum);
 pub(super) enum TypeEnum {
     Simple(SimpleType),
     List(Arc<ListType>),
+    FixedSizeList(Arc<ListType>, u32),
     Record(Arc<RecordType>),
     Tuple(Arc<TupleType>),
     Variant(Arc<VariantType>),
@@ -55,6 +56,15 @@ impl Type {
         Self(TypeEnum::List(Arc::new(ListType { element })))
     }
 
+    /// Returns a list type with the given element type.
+    pub fn fixed_size_list(element_type: impl Into<Self>, elements: u32) -> Self {
+        let element = element_type.into();
+        Self(TypeEnum::FixedSizeList(
+            Arc::new(ListType { element }),
+            elements,
+        ))
+    }
+
     /// Returns a record type with the given field types. Returns None if
     /// `fields` is empty.
     pub fn record<T: Into<Box<str>>>(
@@ -189,6 +199,7 @@ impl WasmType for Type {
         match self.0 {
             TypeEnum::Simple(simple) => simple.0,
             TypeEnum::List(_) => WasmTypeKind::List,
+            TypeEnum::FixedSizeList(_, _) => WasmTypeKind::FixedSizeList,
             TypeEnum::Record(_) => WasmTypeKind::Record,
             TypeEnum::Tuple(_) => WasmTypeKind::Tuple,
             TypeEnum::Variant(_) => WasmTypeKind::Variant,
diff --git a/crates/wasm-wave/src/value/wit.rs b/crates/wasm-wave/src/value/wit.rs
index 423090a6..d0822f97 100644
--- a/crates/wasm-wave/src/value/wit.rs
+++ b/crates/wasm-wave/src/value/wit.rs
@@ -70,6 +70,9 @@ impl<'a> TypeResolver<'a> {
             TypeDefKind::Option(some_type) => self.resolve_option(some_type),
             TypeDefKind::Result(result) => self.resolve_result(result),
             TypeDefKind::List(element_type) => self.resolve_list(element_type),
+            TypeDefKind::FixedSizeList(element_type, elements) => {
+                self.resolve_fixed_size_list(element_type, *elements)
+            }
             TypeDefKind::Type(Type::Bool) => Ok(value::Type::BOOL),
             TypeDefKind::Type(Type::U8) => Ok(value::Type::U8),
             TypeDefKind::Type(Type::U16) => Ok(value::Type::U16),
@@ -145,6 +148,11 @@ impl<'a> TypeResolver<'a> {
         let element_type = self.resolve_type(*element_type)?;
         Ok(value::Type::list(element_type))
     }
+
+    fn resolve_fixed_size_list(&self, element_type: &Type, elements: u32) -> ValueResult {
+        let element_type = self.resolve_type(*element_type)?;
+        Ok(value::Type::fixed_size_list(element_type, elements))
+    }
 }
 
 #[cfg(test)]
diff --git a/crates/wasm-wave/src/wasm/ty.rs b/crates/wasm-wave/src/wasm/ty.rs
index e131759c..499647a0 100644
--- a/crates/wasm-wave/src/wasm/ty.rs
+++ b/crates/wasm-wave/src/wasm/ty.rs
@@ -20,6 +20,7 @@ pub enum WasmTypeKind {
     Char,
     String,
     List,
+    FixedSizeList,
     Record,
     Tuple,
     Variant,
@@ -48,6 +49,7 @@ impl std::fmt::Display for WasmTypeKind {
             WasmTypeKind::Char => "char",
             WasmTypeKind::String => "string",
             WasmTypeKind::List => "list",
+            WasmTypeKind::FixedSizeList => "list<_,N>",
             WasmTypeKind::Record => "record",
             WasmTypeKind::Tuple => "tuple",
             WasmTypeKind::Variant => "variant",
diff --git a/crates/wasm-wave/src/writer.rs b/crates/wasm-wave/src/writer.rs
index eca4f37a..4233f359 100644
--- a/crates/wasm-wave/src/writer.rs
+++ b/crates/wasm-wave/src/writer.rs
@@ -75,6 +75,16 @@ impl<W: Write> Writer<W> {
                 }
                 self.write_str("]")
             }
+            WasmTypeKind::FixedSizeList => {
+                self.write_str("[")?;
+                for (idx, val) in val.unwrap_list().enumerate() {
+                    if idx != 0 {
+                        self.write_str(", ")?;
+                    }
+                    self.write_value(&*val)?;
+                }
+                self.write_str("]")
+            }
             WasmTypeKind::Record => {
                 self.write_str("{")?;
                 let mut first = true;
diff --git a/crates/wasmparser/src/features.rs b/crates/wasmparser/src/features.rs
index fbe88a38..9e5ae374 100644
--- a/crates/wasmparser/src/features.rs
+++ b/crates/wasmparser/src/features.rs
@@ -139,7 +139,7 @@ define_wasm_features! {
     /// has any associated constants. When `features` are disabled all values
     /// for proposals are fixed at compile time to their defaults.
     #[derive(Hash, Debug, Copy, Clone, Eq, PartialEq)]
-    pub struct WasmFeatures: u32 {
+    pub struct WasmFeatures: u64 {
         /// The WebAssembly `mutable-global` proposal.
         pub mutable_global: MUTABLE_GLOBAL(1) = true;
         /// The WebAssembly `saturating-float-to-int` proposal.
@@ -254,6 +254,16 @@ define_wasm_features! {
         /// Corresponds to the 📝 character in
         /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
         pub cm_error_context: CM_ERROR_CONTEXT(1 << 30) = false;
+        /// Support for fixed size lists
+        ///
+        /// Corresponds to the 🔧 character in
+        /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
+        pub cm_fixed_size_list: CM_FIXED_SIZE_LIST(1 << 31) = false;
+        /// Support for Wasm GC in the component model proposal.
+        ///
+        /// Corresponds to the 🛸 character in
+        /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
+        pub cm_gc: CM_GC(1 << 32) = false;
     }
 }
 
diff --git a/crates/wasmparser/src/readers/component/canonicals.rs b/crates/wasmparser/src/readers/component/canonicals.rs
index f7160544..6e578938 100644
--- a/crates/wasmparser/src/readers/component/canonicals.rs
+++ b/crates/wasmparser/src/readers/component/canonicals.rs
@@ -28,6 +28,8 @@ pub enum CanonicalOption {
     /// The function to use if the async lifting of a function should receive task/stream/future progress events
     /// using a callback.
     Callback(u32),
+    /// The core function type to lower this component function to.
+    CoreType(u32),
 }
 
 /// Represents a canonical function in a WebAssembly component.
@@ -367,10 +369,10 @@ impl<'a> FromReader<'a> for CanonicalFunction {
             },
             0x22 => CanonicalFunction::WaitableSetDrop,
             0x23 => CanonicalFunction::WaitableJoin,
-            0x24 => CanonicalFunction::SubtaskCancel {
+            0x06 => CanonicalFunction::SubtaskCancel {
                 async_: reader.read()?,
             },
-            0x25 => CanonicalFunction::TaskCancel,
+            0x05 => CanonicalFunction::TaskCancel,
             0x40 => CanonicalFunction::ThreadSpawnRef {
                 func_ty_index: reader.read()?,
             },
@@ -401,6 +403,7 @@ impl<'a> FromReader<'a> for CanonicalOption {
             0x05 => CanonicalOption::PostReturn(reader.read_var_u32()?),
             0x06 => CanonicalOption::Async,
             0x07 => CanonicalOption::Callback(reader.read_var_u32()?),
+            0x08 => CanonicalOption::CoreType(reader.read_var_u32()?),
             x => return reader.invalid_leading_byte(x, "canonical option"),
         })
     }
diff --git a/crates/wasmparser/src/readers/component/types.rs b/crates/wasmparser/src/readers/component/types.rs
index 0bdc1df9..888a977c 100644
--- a/crates/wasmparser/src/readers/component/types.rs
+++ b/crates/wasmparser/src/readers/component/types.rs
@@ -441,6 +441,8 @@ pub enum ComponentDefinedType<'a> {
     Variant(Box<[VariantCase<'a>]>),
     /// The type is a list of the given value type.
     List(ComponentValType),
+    /// The type is a fixed size list of the given value type.
+    FixedSizeList(ComponentValType, u32),
     /// The type is a tuple of the given value types.
     Tuple(Box<[ComponentValType]>),
     /// The type is flags with the given names.
@@ -503,8 +505,9 @@ impl<'a> ComponentDefinedType<'a> {
             },
             0x69 => ComponentDefinedType::Own(reader.read()?),
             0x68 => ComponentDefinedType::Borrow(reader.read()?),
-            0x65 => ComponentDefinedType::Future(reader.read()?),
+            0x67 => ComponentDefinedType::FixedSizeList(reader.read()?, reader.read_var_u32()?),
             0x66 => ComponentDefinedType::Stream(reader.read()?),
+            0x65 => ComponentDefinedType::Future(reader.read()?),
             x => return reader.invalid_leading_byte(x, "component defined type"),
         })
     }
diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs
index 4a7b0e34..0e587351 100644
--- a/crates/wasmparser/src/validator.rs
+++ b/crates/wasmparser/src/validator.rs
@@ -391,6 +391,14 @@ impl Validator {
     /// [`CoreTypeId`][crate::types::CoreTypeId]) for the same types that are
     /// defined multiple times across different modules and components.
     ///
+    /// # Panics
+    ///
+    /// This function will panic if the validator was mid-way through
+    /// validating a binary. Validation must complete entirely or not have
+    /// started at all for this method to be called.
+    ///
+    /// # Examples
+    ///
     /// ```
     /// fn foo() -> anyhow::Result<()> {
     /// use wasmparser::Validator;
@@ -458,7 +466,7 @@ impl Validator {
         } = self;
 
         assert!(
-            matches!(state, State::End),
+            matches!(state, State::End) || matches!(state, State::Unparsed(None)),
             "cannot reset a validator that did not successfully complete validation"
         );
         assert!(module.is_none());
@@ -1602,4 +1610,9 @@ mod tests {
 
         Ok(())
     }
+
+    #[test]
+    fn reset_fresh_validator() {
+        Validator::new().reset();
+    }
 }
diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs
index 514198af..174c4b7a 100644
--- a/crates/wasmparser/src/validator/component.rs
+++ b/crates/wasmparser/src/validator/component.rs
@@ -7,8 +7,8 @@ use super::{
         ComponentCoreModuleTypeId, ComponentCoreTypeId, ComponentDefinedType,
         ComponentDefinedTypeId, ComponentEntityType, ComponentFuncType, ComponentFuncTypeId,
         ComponentInstanceType, ComponentInstanceTypeId, ComponentType, ComponentTypeId,
-        ComponentValType, Context, CoreInstanceTypeKind, InstanceType, LoweringInfo, ModuleType,
-        RecordType, Remap, Remapping, ResourceId, SubtypeCx, TupleType, VariantCase, VariantType,
+        ComponentValType, Context, CoreInstanceTypeKind, InstanceType, ModuleType, RecordType,
+        Remap, Remapping, ResourceId, SubtypeCx, TupleType, VariantCase, VariantType,
     },
     core::{InternRecGroup, Module},
     types::{CoreTypeId, EntityType, TypeAlloc, TypeInfo, TypeList},
@@ -207,6 +207,187 @@ impl ExternKind {
     }
 }
 
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub(crate) enum Concurrency {
+    /// Synchronous.
+    #[default]
+    Sync,
+
+    /// Asynchronous.
+    Async {
+        /// When present, this is the function index of the async callback. When
+        /// omitted, we are either using stack-switching based asynchrony or are
+        /// in an operation that does not support the `callback` option (like
+        /// lowering).
+        callback: Option<u32>,
+    },
+}
+
+impl Concurrency {
+    pub(crate) fn is_sync(&self) -> bool {
+        matches!(self, Self::Sync)
+    }
+
+    pub(crate) fn is_async(&self) -> bool {
+        !self.is_sync()
+    }
+}
+
+pub(crate) struct CanonicalOptions {
+    pub(crate) memory: Option<u32>,
+    pub(crate) realloc: Option<u32>,
+    pub(crate) post_return: Option<u32>,
+    pub(crate) concurrency: Concurrency,
+    pub(crate) core_type: Option<CoreTypeId>,
+}
+
+impl CanonicalOptions {
+    pub(crate) fn require_sync(&self, offset: usize, where_: &str) -> Result<&Self> {
+        if !self.concurrency.is_sync() {
+            bail!(offset, "cannot specify `async` option on `{where_}`")
+        }
+        Ok(self)
+    }
+
+    pub(crate) fn require_memory(&self, offset: usize) -> Result<&Self> {
+        if self.memory.is_none() {
+            bail!(offset, "canonical option `memory` is required");
+        }
+        Ok(self)
+    }
+
+    pub(crate) fn require_realloc(&self, offset: usize) -> Result<&Self> {
+        // Memory is always required when `realloc` is required.
+        self.require_memory(offset)?;
+
+        if self.realloc.is_none() {
+            bail!(offset, "canonical option `realloc` is required")
+        }
+
+        Ok(self)
+    }
+
+    pub(crate) fn require_memory_if(
+        &self,
+        offset: usize,
+        when: impl Fn() -> bool,
+    ) -> Result<&Self> {
+        if self.memory.is_none() && when() {
+            self.require_memory(offset)?;
+        }
+        Ok(self)
+    }
+
+    pub(crate) fn require_realloc_if(
+        &self,
+        offset: usize,
+        when: impl Fn() -> bool,
+    ) -> Result<&Self> {
+        if self.realloc.is_none() && when() {
+            self.require_realloc(offset)?;
+        }
+        Ok(self)
+    }
+
+    pub(crate) fn check_lower(&self, offset: usize) -> Result<&Self> {
+        if self.post_return.is_some() {
+            bail!(
+                offset,
+                "canonical option `post-return` cannot be specified for lowerings"
+            );
+        }
+
+        if let Concurrency::Async { callback: Some(_) } = self.concurrency {
+            bail!(
+                offset,
+                "canonical option `callback` cannot be specified for lowerings"
+            );
+        }
+
+        Ok(self)
+    }
+
+    pub(crate) fn check_lift(
+        &self,
+        types: &TypeList,
+        state: &ComponentState,
+        core_ty: &FuncType,
+        offset: usize,
+    ) -> Result<&Self> {
+        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() {
+                bail!(offset, "canonical option `post-return` uses a core function with an incorrect signature");
+            }
+        }
+
+        match self.concurrency {
+            Concurrency::Sync => {}
+
+            Concurrency::Async { callback: None } if !state.features.cm_async_stackful() => {
+                bail!(offset, "requires the async stackful feature")
+            }
+            Concurrency::Async { callback: None } => {}
+
+            Concurrency::Async {
+                callback: Some(idx),
+            } => {
+                let func_ty = types[state.core_function_at(idx, offset)?].unwrap_func();
+                if func_ty.params() != [ValType::I32; 3] && func_ty.params() != [ValType::I32] {
+                    return Err(BinaryReaderError::new(
+                        "canonical option `callback` uses a core function with an incorrect signature",
+                        offset,
+                    ));
+                }
+            }
+        }
+
+        if self.core_type.is_some() {
+            bail!(
+                offset,
+                "canonical option `core-type` is not allowed in `canon lift`"
+            )
+        }
+
+        Ok(self)
+    }
+
+    pub(crate) fn check_core_type(
+        &self,
+        types: &mut TypeAlloc,
+        actual: FuncType,
+        offset: usize,
+    ) -> Result<CoreTypeId> {
+        if let Some(declared_id) = self.core_type {
+            let declared = types[declared_id].unwrap_func();
+
+            if actual.params() != declared.params() {
+                bail!(
+                    offset,
+                    "declared core type has `{:?}` parameter types, but actual lowering has \
+                     `{:?}` parameter types",
+                    declared.params(),
+                    actual.params(),
+                );
+            }
+
+            if actual.results() != declared.results() {
+                bail!(
+                    offset,
+                    "declared core type has `{:?}` result types, but actual lowering has \
+                     `{:?}` result types",
+                    declared.results(),
+                    actual.results(),
+                );
+            }
+
+            Ok(declared_id)
+        } else {
+            Ok(types.intern_func_type(actual, offset))
+        }
+    }
+}
+
 impl ComponentState {
     pub fn new(kind: ComponentKind, features: WasmFeatures) -> Self {
         Self {
@@ -724,9 +905,9 @@ impl ComponentState {
                         .map(|t| types.type_named_valtype(t, set))
                         .unwrap_or(true)
             }
-            ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
-                types.type_named_valtype(ty, set)
-            }
+            ComponentDefinedType::List(ty)
+            | ComponentDefinedType::FixedSizeList(ty, _)
+            | ComponentDefinedType::Option(ty) => types.type_named_valtype(ty, set),
 
             // The resource referred to by own/borrow must be named.
             ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => {
@@ -1035,41 +1216,59 @@ impl ComponentState {
         core_func_index: u32,
         type_index: u32,
         options: &[CanonicalOption],
-        types: &TypeList,
+        types: &mut TypeAlloc,
         offset: usize,
     ) -> Result<()> {
         let ty = self.function_type_at(type_index, types, offset)?;
-        let core_ty = types[self.core_function_at(core_func_index, offset)?].unwrap_func();
+        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 info = ty.lower(types, Abi::for_lift(options));
-        self.check_options(Some(core_ty), &info, options, types, offset, true)?;
+        let options = self.check_options(types, options, offset)?;
+        options.check_lift(types, self, core_ty, offset)?;
+        let func_ty = ty.lower(types, &options, Abi::Lift, offset)?;
+        debug_assert!(options.core_type.is_none());
+        let lowered_core_ty_id = types.intern_func_type(func_ty, offset);
+
+        if core_ty_id == lowered_core_ty_id {
+            self.funcs
+                .push(self.types[type_index as usize].unwrap_func());
+            return Ok(());
+        }
 
-        if core_ty.params() != info.params.as_slice() {
+        let ty = types[core_ty_id].unwrap_func();
+        let lowered_ty = types[lowered_core_ty_id].unwrap_func();
+
+        if lowered_ty.params() != ty.params() {
             bail!(
                 offset,
-                "lowered parameter types `{:?}` do not match parameter types \
-                 `{:?}` of core function {core_func_index}",
-                info.params.as_slice(),
-                core_ty.params(),
+                "lowered parameter types `{:?}` do not match parameter types `{:?}` of \
+                     core function {core_func_index}",
+                lowered_ty.params(),
+                ty.params()
             );
         }
 
-        if core_ty.results() != info.results.as_slice() {
+        if lowered_ty.results() != ty.results() {
             bail!(
                 offset,
-                "lowered result types `{:?}` do not match result types \
-                 `{:?}` of core function {core_func_index}",
-                info.results.as_slice(),
-                core_ty.results()
+                "lowered result types `{:?}` do not match result types `{:?}` of \
+                     core function {core_func_index}",
+                lowered_ty.results(),
+                ty.results()
             );
         }
 
-        self.funcs
-            .push(self.types[type_index as usize].unwrap_func());
-
-        Ok(())
+        // Otherwise, must be different rec groups or subtyping (which isn't
+        // supported yet) or something.
+        bail!(
+            offset,
+            "lowered function type `{:?}` does not match type `{:?}` of \
+                 core function {core_func_index}",
+            types[lowered_core_ty_id],
+            types[core_ty_id],
+        );
     }
 
     fn lower_function(
@@ -1083,20 +1282,12 @@ impl ComponentState {
 
         // Lowering a function is for an import, so use a function type that matches
         // the expected canonical ABI import signature.
-        let info = ty.lower(
-            types,
-            if options.contains(&CanonicalOption::Async) {
-                Abi::LowerAsync
-            } else {
-                Abi::LowerSync
-            },
-        );
-
-        self.check_options(None, &info, options, types, offset, true)?;
-
-        let id = types.intern_func_type(info.into_func_type(), offset);
-        self.core_funcs.push(id);
+        let options = self.check_options(types, options, offset)?;
+        options.check_lower(offset)?;
+        let func_ty = ty.lower(types, &options, Abi::Lower, offset)?;
+        let ty_id = options.check_core_type(types, func_ty, offset)?;
 
+        self.core_funcs.push(ty_id);
         Ok(())
     }
 
@@ -1166,21 +1357,7 @@ impl ComponentState {
             )
         }
 
-        for option in options {
-            let invalid = match option {
-                CanonicalOption::UTF8
-                | CanonicalOption::UTF16
-                | CanonicalOption::CompactUTF16
-                | CanonicalOption::Memory(_) => continue,
-                CanonicalOption::Realloc(_) => "realloc",
-                CanonicalOption::PostReturn(_) => "post-return",
-                CanonicalOption::Async => "async",
-                CanonicalOption::Callback(_) => "callback",
-            };
-            bail!(offset, "cannot specify `{invalid}` option on `task.return`")
-        }
-
-        let info = ComponentFuncType {
+        let func_ty = ComponentFuncType {
             info: TypeInfo::new(),
             params: result
                 .iter()
@@ -1199,15 +1376,26 @@ impl ComponentState {
                 })
                 .collect::<Result<_>>()?,
             result: None,
-        }
-        .lower(types, Abi::LowerSync);
+        };
 
-        self.check_options(None, &info, options, types, offset, true)?;
+        let options = self.check_options(types, options, offset)?;
+        if options.realloc.is_some() {
+            bail!(offset, "cannot specify `realloc` option on `task.return`")
+        }
+        if options.post_return.is_some() {
+            bail!(
+                offset,
+                "cannot specify `post-return` option on `task.return`"
+            )
+        }
+        options.check_lower(offset)?;
+        options.require_sync(offset, "task.return")?;
 
-        assert!(info.results.iter().next().is_none());
+        let func_ty = func_ty.lower(types, &options, Abi::Lower, offset)?;
+        assert!(func_ty.results().is_empty());
+        let ty_id = options.check_core_type(types, func_ty, offset)?;
 
-        self.core_funcs
-            .push(types.intern_func_type(FuncType::new(info.params.iter(), []), offset));
+        self.core_funcs.push(ty_id);
         Ok(())
     }
 
@@ -1268,7 +1456,7 @@ impl ComponentState {
         }
 
         self.core_funcs
-            .push(types.intern_func_type(FuncType::new([], []), offset));
+            .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset));
         Ok(())
     }
 
@@ -1337,19 +1525,22 @@ impl ComponentState {
         }
 
         let ty = self.defined_type_at(ty, offset)?;
-        let ComponentDefinedType::Stream(payload_type) = &types[ty] else {
+        let ComponentDefinedType::Stream(elem_ty) = &types[ty] else {
             bail!(offset, "`stream.read` requires a stream type")
         };
 
-        let mut info = LoweringInfo::default();
-        info.requires_memory = true;
-        info.requires_realloc = payload_type
-            .map(|ty| ty.contains_ptr(types))
-            .unwrap_or_default();
-        self.check_options(None, &info, options, types, offset, true)?;
+        let ty_id = self
+            .check_options(types, options, offset)?
+            .require_memory(offset)?
+            .require_realloc_if(offset, || elem_ty.is_some_and(|ty| ty.contains_ptr(types)))?
+            .check_lower(offset)?
+            .check_core_type(
+                types,
+                FuncType::new([ValType::I32; 3], [ValType::I32]),
+                offset,
+            )?;
 
-        self.core_funcs
-            .push(types.intern_func_type(FuncType::new([ValType::I32; 3], [ValType::I32]), offset));
+        self.core_funcs.push(ty_id);
         Ok(())
     }
 
@@ -1372,13 +1563,17 @@ impl ComponentState {
             bail!(offset, "`stream.write` requires a stream type")
         };
 
-        let mut info = LoweringInfo::default();
-        info.requires_memory = true;
-        info.requires_realloc = false;
-        self.check_options(None, &info, options, types, offset, true)?;
+        let ty_id = self
+            .check_options(types, options, offset)?
+            .require_memory(offset)?
+            .check_lower(offset)?
+            .check_core_type(
+                types,
+                FuncType::new([ValType::I32; 3], [ValType::I32]),
+                offset,
+            )?;
 
-        self.core_funcs
-            .push(types.intern_func_type(FuncType::new([ValType::I32; 3], [ValType::I32]), offset));
+        self.core_funcs.push(ty_id);
         Ok(())
     }
 
@@ -1521,19 +1716,22 @@ impl ComponentState {
         }
 
         let ty = self.defined_type_at(ty, offset)?;
-        let ComponentDefinedType::Future(payload_type) = &types[ty] else {
+        let ComponentDefinedType::Future(elem_ty) = &types[ty] else {
             bail!(offset, "`future.read` requires a future type")
         };
 
-        let mut info = LoweringInfo::default();
-        info.requires_memory = true;
-        info.requires_realloc = payload_type
-            .map(|ty| ty.contains_ptr(types))
-            .unwrap_or_default();
-        self.check_options(None, &info, options, types, offset, true)?;
+        let ty_id = self
+            .check_options(types, options, offset)?
+            .require_memory(offset)?
+            .require_realloc_if(offset, || elem_ty.is_some_and(|ty| ty.contains_ptr(types)))?
+            .check_lower(offset)?
+            .check_core_type(
+                types,
+                FuncType::new([ValType::I32; 2], [ValType::I32]),
+                offset,
+            )?;
 
-        self.core_funcs
-            .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset));
+        self.core_funcs.push(ty_id);
         Ok(())
     }
 
@@ -1556,13 +1754,17 @@ impl ComponentState {
             bail!(offset, "`future.write` requires a future type")
         };
 
-        let mut info = LoweringInfo::default();
-        info.requires_memory = true;
-        info.requires_realloc = false;
-        self.check_options(None, &info, &options, types, offset, true)?;
+        let ty_id = self
+            .check_options(types, &options, offset)?
+            .require_memory(offset)?
+            .check_lower(offset)?
+            .check_core_type(
+                types,
+                FuncType::new([ValType::I32; 2], [ValType::I32]),
+                offset,
+            )?;
 
-        self.core_funcs
-            .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset));
+        self.core_funcs.push(ty_id);
         Ok(())
     }
 
@@ -1685,13 +1887,18 @@ impl ComponentState {
             )
         }
 
-        let mut info = LoweringInfo::default();
-        info.requires_memory = true;
-        info.requires_realloc = false;
-        self.check_options(None, &info, &options, types, offset, false)?;
+        let ty_id = self
+            .check_options(types, &options, offset)?
+            .require_memory(offset)?
+            .require_sync(offset, "error-context.new")?
+            .check_lower(offset)?
+            .check_core_type(
+                types,
+                FuncType::new([ValType::I32; 2], [ValType::I32]),
+                offset,
+            )?;
 
-        self.core_funcs
-            .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset));
+        self.core_funcs.push(ty_id);
         Ok(())
     }
 
@@ -1708,13 +1915,15 @@ impl ComponentState {
             )
         }
 
-        let mut info = LoweringInfo::default();
-        info.requires_memory = true;
-        info.requires_realloc = true;
-        self.check_options(None, &info, &options, types, offset, false)?;
+        let ty_id = self
+            .check_options(types, &options, offset)?
+            .require_memory(offset)?
+            .require_realloc(offset)?
+            .require_sync(offset, "error-context.debug-message")?
+            .check_lower(offset)?
+            .check_core_type(types, FuncType::new([ValType::I32; 2], []), offset)?;
 
-        self.core_funcs
-            .push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset));
+        self.core_funcs.push(ty_id);
         Ok(())
     }
 
@@ -2111,13 +2320,10 @@ impl ComponentState {
 
     fn check_options(
         &self,
-        core_ty: Option<&FuncType>,
-        info: &LoweringInfo,
-        options: &[CanonicalOption],
         types: &TypeList,
+        options: &[CanonicalOption],
         offset: usize,
-        allow_async: bool,
-    ) -> Result<()> {
+    ) -> Result<CanonicalOptions> {
         fn display(option: CanonicalOption) -> &'static str {
             match option {
                 CanonicalOption::UTF8 => "utf8",
@@ -2128,6 +2334,7 @@ impl ComponentState {
                 CanonicalOption::PostReturn(_) => "post-return",
                 CanonicalOption::Async => "async",
                 CanonicalOption::Callback(_) => "callback",
+                CanonicalOption::CoreType(_) => "core type",
             }
         }
 
@@ -2135,8 +2342,9 @@ impl ComponentState {
         let mut memory = None;
         let mut realloc = None;
         let mut post_return = None;
-        let mut async_ = false;
+        let mut is_async = false;
         let mut callback = None;
+        let mut core_type = None;
 
         for option in options {
             match option {
@@ -2170,10 +2378,11 @@ impl ComponentState {
                 CanonicalOption::Realloc(idx) => {
                     realloc = match realloc {
                         None => {
-                            let ty = types[self.core_function_at(*idx, offset)?].unwrap_func();
-                            if ty.params()
+                            let ty_id = self.core_function_at(*idx, offset)?;
+                            let func_ty = types[ty_id].unwrap_func();
+                            if func_ty.params()
                                 != [ValType::I32, ValType::I32, ValType::I32, ValType::I32]
-                                || ty.results() != [ValType::I32]
+                                || func_ty.results() != [ValType::I32]
                             {
                                 return Err(BinaryReaderError::new(
                                     "canonical option `realloc` uses a core function with an incorrect signature",
@@ -2192,24 +2401,7 @@ impl ComponentState {
                 }
                 CanonicalOption::PostReturn(idx) => {
                     post_return = match post_return {
-                        None => {
-                            let core_ty = core_ty.ok_or_else(|| {
-                                BinaryReaderError::new(
-                                    "canonical option `post-return` cannot be specified for lowerings",
-                                    offset,
-                                )
-                            })?;
-
-                            let ty = types[self.core_function_at(*idx, offset)?].unwrap_func();
-
-                            if ty.params() != core_ty.results() || !ty.results().is_empty() {
-                                return Err(BinaryReaderError::new(
-                                    "canonical option `post-return` uses a core function with an incorrect signature",
-                                    offset,
-                                ));
-                            }
-                            Some(*idx)
-                        }
+                        None => Some(*idx),
                         Some(_) => {
                             return Err(BinaryReaderError::new(
                                 "canonical option `post-return` is specified more than once",
@@ -2219,7 +2411,7 @@ impl ComponentState {
                     }
                 }
                 CanonicalOption::Async => {
-                    if async_ {
+                    if is_async {
                         return Err(BinaryReaderError::new(
                             "canonical option `async` is specified more than once",
                             offset,
@@ -2232,96 +2424,78 @@ impl ComponentState {
                             );
                         }
 
-                        async_ = true;
+                        is_async = true;
                     }
                 }
                 CanonicalOption::Callback(idx) => {
                     callback = match callback {
+                        None => Some(*idx),
+                        Some(_) => {
+                            return Err(BinaryReaderError::new(
+                                "canonical option `callback` is specified more than once",
+                                offset,
+                            ))
+                        }
+                    }
+                }
+                CanonicalOption::CoreType(idx) => {
+                    core_type = match core_type {
                         None => {
-                            if core_ty.is_none() {
-                                return Err(BinaryReaderError::new(
-                                    "canonical option `callback` cannot be specified for lowerings",
+                            if !self.features.cm_gc() {
+                                bail!(
                                     offset,
-                                ));
+                                    "canonical option `core type` requires the component model gc feature"
+                                )
                             }
-
-                            let ty = types[self.core_function_at(*idx, offset)?].unwrap_func();
-
-                            if ty.params() != [ValType::I32; 3] && ty.params() != [ValType::I32] {
-                                return Err(BinaryReaderError::new(
-                                    "canonical option `callback` uses a core function with an incorrect signature",
+                            let ty = match self.core_type_at(*idx, offset)? {
+                                ComponentCoreTypeId::Sub(ty) => ty,
+                                ComponentCoreTypeId::Module(_) => return Err(BinaryReaderError::new(
+                                    "canonical option `core type` must reference a core function \
+                                     type",
                                     offset,
-                                ));
+                                )),
+                            };
+                            match &types[ty].composite_type.inner {
+                                CompositeInnerType::Func(_) => {}
+                                CompositeInnerType::Array(_) |
+                                CompositeInnerType::Struct(_) |
+                                CompositeInnerType::Cont(_) => return Err(BinaryReaderError::new(
+                                    "canonical option `core type` must reference a core function \
+                                     type",
+                                    offset,
+                                )),
                             }
-                            Some(*idx)
+                            Some(ty)
                         }
                         Some(_) => {
                             return Err(BinaryReaderError::new(
-                                "canonical option `callback` is specified more than once",
+                                "canonical option `core type` is specified more than once",
                                 offset,
                             ))
                         }
-                    }
+                    };
                 }
             }
         }
 
-        // Validate various combinations of options with respect to async.
-        // Modeled as a `match` here to double-check that everything is
-        // exhaustive at compile-time.
-        match (
-            async_,
-            allow_async,
-            callback.is_some(),
-            core_ty,
-            post_return.is_some(),
-        ) {
-            (true, false, ..) => bail!(offset, "async option not allowed here"),
-            (false, _, true, _, _) => {
-                bail!(offset, "cannot specify callback without lifting async")
+        let concurrency = match (is_async, callback, post_return.is_some()) {
+            (false, Some(_), _) => {
+                bail!(offset, "cannot specify callback without async")
             }
-            (true, true, _, _, true) => {
-                bail!(
-                    offset,
-                    "cannot specify post-return function when lifting async"
-                )
+            (true, _, true) => {
+                bail!(offset, "cannot specify post-return function in async")
             }
+            (false, None, _) => Concurrency::Sync,
+            (true, callback, false) => Concurrency::Async { callback },
+        };
 
-            // Async + allowed + this is a lift (core_ty present) + stackful ABI
-            (true, true, false, Some(_), false) => {
-                if !self.features.cm_async_stackful() {
-                    bail!(
-                        offset,
-                        "`async` without `callback` requires the async stackful feature"
-                    )
-                }
-            }
-
-            // Async + allowed + this is a lower (no core_ty)
-            (true, true, false, None, false) => {}
-
-            // Not async, no callback, this is ok
-            (false, _, false, _, _) => {}
-
-            // Async + allowed + callback ABI
-            (true, true, true, _, false) => {}
-        }
-
-        if info.requires_memory && memory.is_none() {
-            return Err(BinaryReaderError::new(
-                "canonical option `memory` is required",
-                offset,
-            ));
-        }
-
-        if info.requires_realloc && realloc.is_none() {
-            return Err(BinaryReaderError::new(
-                "canonical option `realloc` is required",
-                offset,
-            ));
-        }
-
-        Ok(())
+        Ok(CanonicalOptions {
+            memory,
+            realloc,
+            post_return,
+            concurrency,
+            core_type,
+        })
     }
 
     fn check_type_ref(
@@ -3475,6 +3649,21 @@ impl ComponentState {
             crate::ComponentDefinedType::List(ty) => Ok(ComponentDefinedType::List(
                 self.create_component_val_type(ty, offset)?,
             )),
+            crate::ComponentDefinedType::FixedSizeList(ty, elements) => {
+                if !self.features.cm_fixed_size_list() {
+                    bail!(
+                        offset,
+                        "Fixed size lists require the component model fixed size list feature"
+                    )
+                }
+                if elements < 1 {
+                    bail!(offset, "Fixed size lists must have more than zero elements")
+                }
+                Ok(ComponentDefinedType::FixedSizeList(
+                    self.create_component_val_type(ty, offset)?,
+                    elements,
+                ))
+            }
             crate::ComponentDefinedType::Tuple(tys) => {
                 self.create_tuple_type(tys.as_ref(), types, offset)
             }
diff --git a/crates/wasmparser/src/validator/component_types.rs b/crates/wasmparser/src/validator/component_types.rs
index 271b850f..1d7f7699 100644
--- a/crates/wasmparser/src/validator/component_types.rs
+++ b/crates/wasmparser/src/validator/component_types.rs
@@ -1,6 +1,7 @@
 //! Types relating to type information provided by validation.
 
 use super::component::ExternKind;
+use super::{CanonicalOptions, Concurrency};
 use crate::prelude::*;
 use crate::validator::names::KebabString;
 use crate::validator::types::{
@@ -8,8 +9,7 @@ use crate::validator::types::{
     Types, TypesKind, TypesRef, TypesRefKind,
 };
 use crate::{
-    BinaryReaderError, CanonicalOption, FuncType, MemoryType, PrimitiveValType, Result, TableType,
-    ValType,
+    BinaryReaderError, FuncType, MemoryType, PrimitiveValType, Result, TableType, ValType,
 };
 use core::fmt;
 use core::ops::Index;
@@ -96,16 +96,15 @@ impl fmt::Debug for LoweredTypes {
     }
 }
 
-/// Represents information about a component function type lowering.
+/// Represents a component function type's in-progress lowering into a core
+/// type.
 #[derive(Debug)]
-pub(crate) struct LoweringInfo {
-    pub(crate) params: LoweredTypes,
-    pub(crate) results: LoweredTypes,
-    pub(crate) requires_memory: bool,
-    pub(crate) requires_realloc: bool,
+struct LoweredSignature {
+    params: LoweredTypes,
+    results: LoweredTypes,
 }
 
-impl LoweringInfo {
+impl LoweredSignature {
     pub(crate) fn into_func_type(self) -> FuncType {
         FuncType::new(
             self.params.as_slice().iter().copied(),
@@ -114,13 +113,11 @@ impl LoweringInfo {
     }
 }
 
-impl Default for LoweringInfo {
+impl Default for LoweredSignature {
     fn default() -> Self {
         Self {
             params: LoweredTypes::new(MAX_FLAT_FUNC_PARAMS),
             results: LoweredTypes::new(MAX_FLAT_FUNC_RESULTS),
-            requires_memory: false,
-            requires_realloc: false,
         }
     }
 }
@@ -537,7 +534,7 @@ pub enum ComponentValType {
 
 impl TypeData for ComponentValType {
     type Id = ComponentValueTypeId;
-
+    const IS_CORE_SUB_TYPE: bool = false;
     fn type_info(&self, types: &TypeList) -> TypeInfo {
         match self {
             ComponentValType::Primitive(_) => TypeInfo::new(),
@@ -644,7 +641,7 @@ pub struct ModuleType {
 
 impl TypeData for ModuleType {
     type Id = ComponentCoreModuleTypeId;
-
+    const IS_CORE_SUB_TYPE: bool = false;
     fn type_info(&self, _types: &TypeList) -> TypeInfo {
         self.info
     }
@@ -680,7 +677,7 @@ pub struct InstanceType {
 
 impl TypeData for InstanceType {
     type Id = ComponentCoreInstanceTypeId;
-
+    const IS_CORE_SUB_TYPE: bool = false;
     fn type_info(&self, _types: &TypeList) -> TypeInfo {
         self.info
     }
@@ -733,7 +730,13 @@ pub enum ComponentEntityType {
 
 impl ComponentEntityType {
     /// Determines if component entity type `a` is a subtype of `b`.
-    pub fn is_subtype_of(a: &Self, at: TypesRef, b: &Self, bt: TypesRef) -> bool {
+    ///
+    /// # Panics
+    ///
+    /// Panics if the two given `TypesRef`s are not associated with the same
+    /// `Validator`.
+    pub fn is_subtype_of(a: &Self, at: TypesRef<'_>, b: &Self, bt: TypesRef<'_>) -> bool {
+        assert_eq!(at.id(), bt.id());
         SubtypeCx::new(at.list, bt.list)
             .component_entity_type(a, b, 0)
             .is_ok()
@@ -820,7 +823,7 @@ pub struct ComponentType {
 
 impl TypeData for ComponentType {
     type Id = ComponentTypeId;
-
+    const IS_CORE_SUB_TYPE: bool = false;
     fn type_info(&self, _types: &TypeList) -> TypeInfo {
         self.info
     }
@@ -878,7 +881,7 @@ pub struct ComponentInstanceType {
 
 impl TypeData for ComponentInstanceType {
     type Id = ComponentInstanceTypeId;
-
+    const IS_CORE_SUB_TYPE: bool = false;
     fn type_info(&self, _types: &TypeList) -> TypeInfo {
         self.info
     }
@@ -897,73 +900,41 @@ pub struct ComponentFuncType {
 
 impl TypeData for ComponentFuncType {
     type Id = ComponentFuncTypeId;
-
+    const IS_CORE_SUB_TYPE: bool = false;
     fn type_info(&self, _types: &TypeList) -> TypeInfo {
         self.info
     }
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub(crate) enum Abi {
-    /// Use to generate the core wasm signature of a component model function
-    /// that is `canon lower`'d with the synchronous ABI option set.
-    LowerSync,
-    /// Use to generate the core wasm signature of a component model function
-    /// that is `canon lower`'d with the asynchronous ABI option set.
-    LowerAsync,
-    /// Use to generate the core wasm signature that when `canon lift`'d with
-    /// the synchronous ABI option set will generate a component model function
-    /// type.
-    LiftSync,
-    /// Use to generate the core wasm signature that when `canon lift`'d with
-    /// the asynchronous + callback ABI options set will generate a component
-    /// model function type.
-    LiftAsync,
-    /// Use to generate the core wasm signature that when `canon lift`'d with
-    /// the asynchronou ABI option set will generate a component
-    /// model function type.
-    LiftAsyncStackful,
-}
-
-impl Abi {
-    pub(crate) fn for_lift(options: &[CanonicalOption]) -> Abi {
-        if options.contains(&CanonicalOption::Async) {
-            if options
-                .iter()
-                .any(|v| matches!(v, CanonicalOption::Callback(_)))
-            {
-                Abi::LiftAsync
-            } else {
-                Abi::LiftAsyncStackful
-            }
-        } else {
-            Abi::LiftSync
-        }
-    }
+    Lift,
+    Lower,
 }
 
 impl ComponentFuncType {
     /// Lowers the component function type to core parameter and result types for the
     /// canonical ABI.
-    pub(crate) fn lower(&self, types: &TypeList, abi: Abi) -> LoweringInfo {
-        let mut info = LoweringInfo::default();
+    pub(crate) fn lower(
+        &self,
+        types: &TypeList,
+        options: &CanonicalOptions,
+        abi: Abi,
+        offset: usize,
+    ) -> Result<FuncType> {
+        let mut sig = LoweredSignature::default();
 
-        let is_lower = match abi {
-            Abi::LowerAsync => {
-                for _ in 0..2 {
-                    info.params.push(ValType::I32);
-                }
-                info.results.push(ValType::I32);
-                info.requires_memory = true;
-                info.requires_realloc = self
-                    .result
-                    .map(|ty| ty.contains_ptr(types))
-                    .unwrap_or(false);
-                return info;
-            }
-            Abi::LowerSync => true,
-            Abi::LiftSync | Abi::LiftAsync | Abi::LiftAsyncStackful => false,
-        };
+        if abi == Abi::Lower && options.concurrency.is_async() {
+            for _ in 0..2 {
+                sig.params.push(ValType::I32);
+            }
+            sig.results.push(ValType::I32);
+            options.require_memory(offset)?;
+            if self.result.is_some_and(|ty| ty.contains_ptr(types)) {
+                options.require_realloc(offset)?;
+            }
+            return Ok(sig.into_func_type());
+        }
 
         for (_, ty) in self.params.iter() {
             // Check to see if `ty` has a pointer somewhere in it, needed for
@@ -972,68 +943,70 @@ impl ComponentFuncType {
             // lifted functions must specify `realloc` as well. Lifted functions
             // gain their memory requirement through the final clause of this
             // function.
-            if is_lower {
-                if !info.requires_memory {
-                    info.requires_memory = ty.contains_ptr(types);
+            match abi {
+                Abi::Lower => {
+                    options.require_memory_if(offset, || ty.contains_ptr(types))?;
                 }
-            } else {
-                if !info.requires_realloc {
-                    info.requires_realloc = ty.contains_ptr(types);
+                Abi::Lift => {
+                    options.require_realloc_if(offset, || ty.contains_ptr(types))?;
                 }
             }
 
-            if !ty.push_wasm_types(types, &mut info.params) {
+            if !ty.push_wasm_types(types, &mut sig.params) {
                 // Too many parameters to pass directly
                 // Function will have a single pointer parameter to pass the arguments
                 // via linear memory
-                info.params.clear();
-                assert!(info.params.push(ValType::I32));
-                info.requires_memory = true;
+                sig.params.clear();
+                assert!(sig.params.push(ValType::I32));
+                options.require_memory(offset)?;
 
                 // We need realloc as well when lifting a function
-                if !is_lower {
-                    info.requires_realloc = true;
+                if let Abi::Lift = abi {
+                    options.require_realloc(offset)?;
                 }
                 break;
             }
         }
 
-        match abi {
-            Abi::LowerAsync => unreachable!(),
-            Abi::LowerSync | Abi::LiftSync => {
+        match (abi, options.concurrency) {
+            (Abi::Lower, Concurrency::Async { .. }) => {
+                unreachable!("special-cased at the start of the function")
+            }
+            (Abi::Lower | Abi::Lift, Concurrency::Sync) => {
                 if let Some(ty) = &self.result {
                     // Results of lowered functions that contains pointers must be
                     // allocated by the callee meaning that realloc is required.
                     // Results of lifted function are allocated by the guest which
                     // means that no realloc option is necessary.
-                    if is_lower && !info.requires_realloc {
-                        info.requires_realloc = ty.contains_ptr(types);
-                    }
-
-                    if !ty.push_wasm_types(types, &mut info.results) {
-                        // Too many results to return directly, either a retptr parameter will be used (import)
-                        // or a single pointer will be returned (export)
-                        info.results.clear();
-                        if is_lower {
-                            info.params.max = MAX_LOWERED_TYPES;
-                            assert!(info.params.push(ValType::I32));
-                        } else {
-                            assert!(info.results.push(ValType::I32));
+                    options.require_realloc_if(offset, || {
+                        abi == Abi::Lower && ty.contains_ptr(types)
+                    })?;
+
+                    if !ty.push_wasm_types(types, &mut sig.results) {
+                        // Too many results to return directly, either a retptr
+                        // parameter will be used (import) or a single pointer
+                        // will be returned (export).
+                        sig.results.clear();
+                        options.require_memory(offset)?;
+                        match abi {
+                            Abi::Lower => {
+                                sig.params.max = MAX_LOWERED_TYPES;
+                                assert!(sig.params.push(ValType::I32));
+                            }
+                            Abi::Lift => {
+                                assert!(sig.results.push(ValType::I32));
+                            }
                         }
-                        info.requires_memory = true;
                     }
                 }
             }
-            Abi::LiftAsync => {
-                info.results.push(ValType::I32);
+            (Abi::Lift, Concurrency::Async { callback: Some(_) }) => {
+                sig.results.push(ValType::I32);
             }
-            Abi::LiftAsyncStackful => {}
+            (Abi::Lift, Concurrency::Async { callback: None }) => {}
         }
 
-        // Memory is always required when realloc is required
-        info.requires_memory |= info.requires_realloc;
-
-        info
+        Ok(sig.into_func_type())
     }
 }
 
@@ -1084,6 +1057,8 @@ pub enum ComponentDefinedType {
     Variant(VariantType),
     /// The type is a list.
     List(ComponentValType),
+    /// The type is a fixed size list.
+    FixedSizeList(ComponentValType, u32),
     /// The type is a tuple.
     Tuple(TupleType),
     /// The type is a set of flags.
@@ -1111,7 +1086,7 @@ pub enum ComponentDefinedType {
 
 impl TypeData for ComponentDefinedType {
     type Id = ComponentDefinedTypeId;
-
+    const IS_CORE_SUB_TYPE: bool = false;
     fn type_info(&self, types: &TypeList) -> TypeInfo {
         match self {
             Self::Primitive(_)
@@ -1124,7 +1099,7 @@ impl TypeData for ComponentDefinedType {
             Self::Record(r) => r.info,
             Self::Variant(v) => v.info,
             Self::Tuple(t) => t.info,
-            Self::List(ty) | Self::Option(ty) => ty.info(types),
+            Self::List(ty) | Self::FixedSizeList(ty, _) | Self::Option(ty) => ty.info(types),
             Self::Result { ok, err } => {
                 let default = TypeInfo::new();
                 let mut info = ok.map(|ty| ty.type_info(types)).unwrap_or(default);
@@ -1153,7 +1128,7 @@ impl ComponentDefinedType {
             | Self::Borrow(_)
             | Self::Future(_)
             | Self::Stream(_) => false,
-            Self::Option(ty) => ty.contains_ptr(types),
+            Self::Option(ty) | Self::FixedSizeList(ty, _) => ty.contains_ptr(types),
             Self::Result { ok, err } => {
                 ok.map(|ty| ty.contains_ptr(types)).unwrap_or(false)
                     || err.map(|ty| ty.contains_ptr(types)).unwrap_or(false)
@@ -1174,6 +1149,9 @@ impl ComponentDefinedType {
                 lowered_types,
             ),
             Self::List(_) => lowered_types.push(ValType::I32) && lowered_types.push(ValType::I32),
+            Self::FixedSizeList(ty, length) => {
+                (0..*length).all(|_n| ty.push_wasm_types(types, lowered_types))
+            }
             Self::Tuple(t) => t
                 .types
                 .iter()
@@ -1248,6 +1226,7 @@ impl ComponentDefinedType {
             ComponentDefinedType::Flags(_) => "flags",
             ComponentDefinedType::Option(_) => "option",
             ComponentDefinedType::List(_) => "list",
+            ComponentDefinedType::FixedSizeList(_, _) => "fixed size list",
             ComponentDefinedType::Result { .. } => "result",
             ComponentDefinedType::Own(_) => "own",
             ComponentDefinedType::Borrow(_) => "borrow",
@@ -1985,7 +1964,9 @@ impl TypeAlloc {
                     }
                 }
             }
-            ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
+            ComponentDefinedType::List(ty)
+            | ComponentDefinedType::FixedSizeList(ty, _)
+            | ComponentDefinedType::Option(ty) => {
                 self.free_variables_valtype(ty, set);
             }
             ComponentDefinedType::Result { ok, err } => {
@@ -2127,9 +2108,9 @@ impl TypeAlloc {
                         .map(|t| self.type_named_valtype(t, set))
                         .unwrap_or(true)
             }
-            ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
-                self.type_named_valtype(ty, set)
-            }
+            ComponentDefinedType::List(ty)
+            | ComponentDefinedType::FixedSizeList(ty, _)
+            | ComponentDefinedType::Option(ty) => self.type_named_valtype(ty, set),
 
             // own/borrow themselves don't have to be named, but the resource
             // they refer to must be named.
@@ -2175,6 +2156,9 @@ where
 {
     /// Pushes a new anonymous type within this object, returning an identifier
     /// which can be used to refer to it.
+    ///
+    /// For internal use only!
+    #[doc(hidden)]
     fn push_ty<T>(&mut self, ty: T) -> T::Id
     where
         T: TypeData;
@@ -2314,7 +2298,9 @@ where
                     }
                 }
             }
-            ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
+            ComponentDefinedType::List(ty)
+            | ComponentDefinedType::FixedSizeList(ty, _)
+            | ComponentDefinedType::Option(ty) => {
                 any_changed |= self.remap_valtype(ty, map);
             }
             ComponentDefinedType::Result { ok, err } => {
@@ -2519,7 +2505,13 @@ macro_rules! limits_match {
 
 impl<'a> SubtypeCx<'a> {
     /// Create a new instance with the specified type lists
+    ///
+    /// # Panics
+    ///
+    /// Panics if the two given `TypesRef`s are not associated with the same
+    /// `Validator`.
     pub fn new_with_refs(a: TypesRef<'a>, b: TypesRef<'a>) -> SubtypeCx<'a> {
+        assert_eq!(a.id(), b.id());
         Self::new(a.list, b.list)
     }
 
@@ -3203,6 +3195,14 @@ impl<'a> SubtypeCx<'a> {
             (Variant(_), b) => bail!(offset, "expected {}, found variant", b.desc()),
             (List(a), List(b)) | (Option(a), Option(b)) => self.component_val_type(a, b, offset),
             (List(_), b) => bail!(offset, "expected {}, found list", b.desc()),
+            (FixedSizeList(a, asize), FixedSizeList(b, bsize)) => {
+                if asize != bsize {
+                    bail!(offset, "expected fixed size {bsize}, found size {asize}")
+                } else {
+                    self.component_val_type(a, b, offset)
+                }
+            }
+            (FixedSizeList(_, _), b) => bail!(offset, "expected {}, found list", b.desc()),
             (Option(_), b) => bail!(offset, "expected {}, found option", b.desc()),
             (Tuple(a), Tuple(b)) => {
                 if a.types.len() != b.types.len() {
@@ -3379,6 +3379,10 @@ impl Remap for SubtypeArena<'_> {
     where
         T: TypeData,
     {
+        assert!(
+            !T::IS_CORE_SUB_TYPE,
+            "cannot push core sub types into `SubtypeArena`s, that would break type canonicalization"
+        );
         let index = T::Id::list(&self.list).len() + T::Id::list(self.types).len();
         let index = u32::try_from(index).unwrap();
         self.list.push(ty);
diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs
index 9105e009..6eb71f46 100644
--- a/crates/wasmparser/src/validator/types.rs
+++ b/crates/wasmparser/src/validator/types.rs
@@ -52,6 +52,9 @@ pub trait TypeData: core::fmt::Debug {
     /// The identifier for this type data.
     type Id: TypeIdentifier<Data = Self>;
 
+    /// Is this type a core sub type (or rec group of sub types)?
+    const IS_CORE_SUB_TYPE: bool;
+
     /// Get the info for this type.
     #[doc(hidden)]
     fn type_info(&self, types: &TypeList) -> TypeInfo;
@@ -134,7 +137,7 @@ impl TypeIdentifier for CoreTypeId {
 
 impl TypeData for SubType {
     type Id = CoreTypeId;
-
+    const IS_CORE_SUB_TYPE: bool = true;
     fn type_info(&self, _types: &TypeList) -> TypeInfo {
         // TODO(#1036): calculate actual size for func, array, struct.
         let size = 1 + match &self.composite_type.inner {
@@ -156,7 +159,7 @@ define_type_id!(
 
 impl TypeData for Range<CoreTypeId> {
     type Id = RecGroupId;
-
+    const IS_CORE_SUB_TYPE: bool = true;
     fn type_info(&self, _types: &TypeList) -> TypeInfo {
         let size = self.end.index() - self.start.index();
         TypeInfo::core(u32::try_from(size).unwrap())
@@ -570,9 +573,7 @@ impl<'a> TypesRef<'a> {
                 ExternalKind::Global => {
                     EntityType::Global(*module.globals.get(export.index as usize)?)
                 }
-                ExternalKind::Tag => EntityType::Tag(
-                    module.types[*module.functions.get(export.index as usize)? as usize],
-                ),
+                ExternalKind::Tag => EntityType::Tag(*module.tags.get(export.index as usize)?),
             }),
             #[cfg(feature = "component-model")]
             TypesRefKind::Component(_) => None,
@@ -780,7 +781,13 @@ impl<T> Index<usize> for SnapshotList<T> {
 
     #[inline]
     fn index(&self, index: usize) -> &T {
-        self.get(index).unwrap()
+        match self.get(index) {
+            Some(x) => x,
+            None => panic!(
+                "out-of-bounds indexing into `SnapshotList`: index is {index}, but length is {}",
+                self.len()
+            ),
+        }
     }
 }
 
@@ -947,7 +954,7 @@ impl TypeList {
     /// [`Self::intern_canonical_rec_group`].
     pub fn intern_sub_type(&mut self, sub_ty: SubType, offset: usize) -> CoreTypeId {
         let (_is_new, group_id) =
-            self.intern_canonical_rec_group(false, RecGroup::implicit(offset, sub_ty));
+            self.intern_canonical_rec_group(true, RecGroup::implicit(offset, sub_ty));
         self[group_id].start
     }
 
diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs
index f10591ea..eee550a7 100644
--- a/crates/wasmprinter/src/component.rs
+++ b/crates/wasmprinter/src/component.rs
@@ -169,6 +169,19 @@ impl Printer<'_, '_> {
         Ok(())
     }
 
+    pub(crate) fn print_fixed_size_list_type(
+        &mut self,
+        state: &State,
+        element_ty: &ComponentValType,
+        elements: u32,
+    ) -> Result<()> {
+        self.start_group("list ")?;
+        self.print_component_val_type(state, element_ty)?;
+        self.result.write_str(&format!(" {elements}"))?;
+        self.end_group()?;
+        Ok(())
+    }
+
     pub(crate) fn print_tuple_type(
         &mut self,
         state: &State,
@@ -265,6 +278,9 @@ impl Printer<'_, '_> {
             ComponentDefinedType::Record(fields) => self.print_record_type(state, fields)?,
             ComponentDefinedType::Variant(cases) => self.print_variant_type(state, cases)?,
             ComponentDefinedType::List(ty) => self.print_list_type(state, ty)?,
+            ComponentDefinedType::FixedSizeList(ty, elements) => {
+                self.print_fixed_size_list_type(state, ty, *elements)?
+            }
             ComponentDefinedType::Tuple(tys) => self.print_tuple_type(state, tys)?,
             ComponentDefinedType::Flags(names) => self.print_flag_or_enum_type("flags", names)?,
             ComponentDefinedType::Enum(cases) => self.print_flag_or_enum_type("enum", cases)?,
@@ -814,6 +830,11 @@ impl Printer<'_, '_> {
                     self.print_idx(&state.core.func_names, *idx)?;
                     self.end_group()?;
                 }
+                CanonicalOption::CoreType(idx) => {
+                    self.start_group("core-type ")?;
+                    self.print_idx(&state.core.type_names, *idx)?;
+                    self.end_group()?;
+                }
             }
         }
         Ok(())
diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs
index 89d4f3fb..21313521 100644
--- a/crates/wast/src/component/binary.rs
+++ b/crates/wast/src/component/binary.rs
@@ -109,6 +109,9 @@ fn encode_defined_type(encoder: ComponentDefinedTypeEncoder, ty: &ComponentDefin
         ComponentDefinedType::List(l) => {
             encoder.list(l.element.as_ref());
         }
+        ComponentDefinedType::FixedSizeList(l) => {
+            encoder.fixed_size_list(l.element.as_ref(), l.elements);
+        }
         ComponentDefinedType::Tuple(t) => {
             encoder.tuple(t.fields.iter());
         }
@@ -972,6 +975,7 @@ impl From<&CanonOpt<'_>> for wasm_encoder::CanonicalOption {
             CanonOpt::PostReturn(f) => Self::PostReturn(f.idx.into()),
             CanonOpt::Async => Self::Async,
             CanonOpt::Callback(f) => Self::Callback(f.idx.into()),
+            CanonOpt::CoreType(t) => Self::CoreType(t.idx.into()),
         }
     }
 }
diff --git a/crates/wast/src/component/expand.rs b/crates/wast/src/component/expand.rs
index 01b00d70..0887fc42 100644
--- a/crates/wast/src/component/expand.rs
+++ b/crates/wast/src/component/expand.rs
@@ -532,8 +532,12 @@ impl<'a> Expander<'a> {
                     }
                 }
             }
-            ComponentDefinedType::List(t) => {
-                self.expand_component_val_ty(&mut t.element);
+            ComponentDefinedType::List(List { element: t })
+            | ComponentDefinedType::FixedSizeList(FixedSizeList {
+                element: t,
+                elements: _,
+            }) => {
+                self.expand_component_val_ty(t);
             }
             ComponentDefinedType::Tuple(t) => {
                 for field in t.fields.iter_mut() {
diff --git a/crates/wast/src/component/func.rs b/crates/wast/src/component/func.rs
index f35a9f5a..900fd151 100644
--- a/crates/wast/src/component/func.rs
+++ b/crates/wast/src/component/func.rs
@@ -949,6 +949,14 @@ pub enum CanonOpt<'a> {
     Async,
     /// Use the specified function to deliver async events to stackless coroutines.
     Callback(CoreItemRef<'a, kw::func>),
+    /// Lower this component function into the specified core function type.
+    CoreType(CoreItemRef<'a, kw::r#type>),
+}
+
+impl Default for kw::r#type {
+    fn default() -> Self {
+        Self(Span::from_offset(0))
+    }
 }
 
 impl<'a> Parse<'a> for CanonOpt<'a> {
@@ -986,6 +994,11 @@ impl<'a> Parse<'a> for CanonOpt<'a> {
                     Ok(CanonOpt::Callback(
                         parser.parse::<IndexOrCoreRef<'_, _>>()?.0,
                     ))
+                } else if l.peek::<kw::core_type>()? {
+                    parser.parse::<kw::core_type>()?;
+                    Ok(CanonOpt::CoreType(
+                        parser.parse::<IndexOrCoreRef<'_, _>>()?.0,
+                    ))
                 } else {
                     Err(l.error())
                 }
@@ -1008,6 +1021,7 @@ impl Peek for CanonOpt<'_> {
                         || kw::realloc::peek(next)?
                         || kw::post_return::peek(next)?
                         || kw::callback::peek(next)?
+                        || kw::core_type::peek(next)?
                 }
                 None => false,
             })
diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs
index 21c0e56b..67b5cf65 100644
--- a/crates/wast/src/component/resolve.rs
+++ b/crates/wast/src/component/resolve.rs
@@ -486,6 +486,7 @@ impl<'a> Resolver<'a> {
                 CanonOpt::Realloc(r) | CanonOpt::PostReturn(r) | CanonOpt::Callback(r) => {
                     self.core_item_ref(r)?
                 }
+                CanonOpt::CoreType(t) => self.core_item_ref(t)?,
             }
         }
 
@@ -547,8 +548,12 @@ impl<'a> Resolver<'a> {
                     }
                 }
             }
-            ComponentDefinedType::List(l) => {
-                self.component_val_type(&mut l.element)?;
+            ComponentDefinedType::List(List { element: t })
+            | ComponentDefinedType::FixedSizeList(FixedSizeList {
+                element: t,
+                elements: _,
+            }) => {
+                self.component_val_type(t)?;
             }
             ComponentDefinedType::Tuple(t) => {
                 for field in t.fields.iter_mut() {
diff --git a/crates/wast/src/component/types.rs b/crates/wast/src/component/types.rs
index 648eb59d..a9ef3ba8 100644
--- a/crates/wast/src/component/types.rs
+++ b/crates/wast/src/component/types.rs
@@ -386,6 +386,7 @@ pub enum ComponentDefinedType<'a> {
     Record(Record<'a>),
     Variant(Variant<'a>),
     List(List<'a>),
+    FixedSizeList(FixedSizeList<'a>),
     Tuple(Tuple<'a>),
     Flags(Flags<'a>),
     Enum(Enum<'a>),
@@ -405,7 +406,7 @@ impl<'a> ComponentDefinedType<'a> {
         } else if l.peek::<kw::variant>()? {
             Ok(Self::Variant(parser.parse()?))
         } else if l.peek::<kw::list>()? {
-            Ok(Self::List(parser.parse()?))
+            parse_list(parser)
         } else if l.peek::<kw::tuple>()? {
             Ok(Self::Tuple(parser.parse()?))
         } else if l.peek::<kw::flags>()? {
@@ -586,12 +587,28 @@ pub struct List<'a> {
     pub element: Box<ComponentValType<'a>>,
 }
 
-impl<'a> Parse<'a> for List<'a> {
-    fn parse(parser: Parser<'a>) -> Result<Self> {
-        parser.parse::<kw::list>()?;
-        Ok(Self {
-            element: Box::new(parser.parse()?),
-        })
+/// A fixed size list type.
+#[derive(Debug)]
+pub struct FixedSizeList<'a> {
+    /// The element type of the array.
+    pub element: Box<ComponentValType<'a>>,
+    /// Number of Elements
+    pub elements: u32,
+}
+
+fn parse_list<'a>(parser: Parser<'a>) -> Result<ComponentDefinedType<'a>> {
+    parser.parse::<kw::list>()?;
+    let tp = parser.parse()?;
+    let elements = parser.parse::<Option<u32>>()?;
+    if let Some(elements) = elements {
+        Ok(ComponentDefinedType::FixedSizeList(FixedSizeList {
+            element: Box::new(tp),
+            elements,
+        }))
+    } else {
+        Ok(ComponentDefinedType::List(List {
+            element: Box::new(tp),
+        }))
     }
 }
 
diff --git a/crates/wast/src/lib.rs b/crates/wast/src/lib.rs
index cb4a1771..366c3847 100644
--- a/crates/wast/src/lib.rs
+++ b/crates/wast/src/lib.rs
@@ -544,6 +544,7 @@ pub mod kw {
     custom_keyword!(post_return = "post-return");
     custom_keyword!(with);
     custom_keyword!(core);
+    custom_keyword!(core_type = "core-type");
     custom_keyword!(true_ = "true");
     custom_keyword!(false_ = "false");
     custom_keyword!(language);
diff --git a/crates/wit-component/src/dummy.rs b/crates/wit-component/src/dummy.rs
index 003bed34..0cccff00 100644
--- a/crates/wit-component/src/dummy.rs
+++ b/crates/wit-component/src/dummy.rs
@@ -379,7 +379,7 @@ fn push_root_async_intrinsics(dst: &mut String) {
 (import "$root" "[waitable-set-poll]" (func (param i32 i32) (result i32)))
 (import "$root" "[waitable-set-drop]" (func (param i32)))
 (import "$root" "[waitable-join]" (func (param i32 i32)))
-(import "$root" "[yield]" (func))
+(import "$root" "[yield]" (func (result i32)))
 (import "$root" "[subtask-drop]" (func (param i32)))
 (import "$root" "[subtask-cancel]" (func (param i32) (result i32)))
 (import "$root" "[error-context-new-utf8]" (func (param i32 i32) (result i32)))
@@ -395,7 +395,7 @@ fn push_root_async_intrinsics(dst: &mut String) {
 ;; deferred behind 🚝 or 🚟 upstream
 ;;(import "$root" "[async-lower][waitable-set-wait]" (func (param i32 i32) (result i32)))
 ;;(import "$root" "[async-lower][waitable-set-poll]" (func (param i32 i32) (result i32)))
-;;(import "$root" "[async-lower][yield]" (func))
+;;(import "$root" "[async-lower][yield]" (func (result i32)))
 "#,
     );
 }
diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs
index ce255ede..f93d9a7a 100644
--- a/crates/wit-component/src/encoding.rs
+++ b/crates/wit-component/src/encoding.rs
@@ -320,6 +320,7 @@ impl TypeContents {
                 }
                 TypeDefKind::Enum(_) => Self::empty(),
                 TypeDefKind::List(t) => Self::for_type(resolve, t) | Self::LIST,
+                TypeDefKind::FixedSizeList(t, _elements) => Self::for_type(resolve, t),
                 TypeDefKind::Type(t) => Self::for_type(resolve, t),
                 TypeDefKind::Future(_) => Self::empty(),
                 TypeDefKind::Stream(_) => Self::empty(),
diff --git a/crates/wit-component/src/encoding/types.rs b/crates/wit-component/src/encoding/types.rs
index 59d4330f..7b354dc6 100644
--- a/crates/wit-component/src/encoding/types.rs
+++ b/crates/wit-component/src/encoding/types.rs
@@ -144,6 +144,12 @@ pub trait ValtypeEncoder<'a> {
                         encoder.list(ty);
                         ComponentValType::Type(index)
                     }
+                    TypeDefKind::FixedSizeList(ty, elements) => {
+                        let ty = self.encode_valtype(resolve, ty)?;
+                        let (index, encoder) = self.defined_type();
+                        encoder.fixed_size_list(ty, *elements);
+                        ComponentValType::Type(index)
+                    }
                     TypeDefKind::Type(ty) => self.encode_valtype(resolve, ty)?,
                     TypeDefKind::Future(ty) => self.encode_future(resolve, ty)?,
                     TypeDefKind::Stream(ty) => self.encode_stream(resolve, ty)?,
diff --git a/crates/wit-component/src/encoding/wit.rs b/crates/wit-component/src/encoding/wit.rs
index 5e2b0b02..5c433724 100644
--- a/crates/wit-component/src/encoding/wit.rs
+++ b/crates/wit-component/src/encoding/wit.rs
@@ -71,30 +71,8 @@ pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentTyp
     let world = &resolve.worlds[world_id];
     log::trace!("encoding world {}", world.name);
 
-    // This sort is similar in purpose to the sort below in
-    // `encode_instance`, but different in its sort. The purpose here is
-    // to ensure that when a document is either printed as WIT or
-    // encoded as wasm that decoding from those artifacts produces the
-    // same WIT package. Namely both encoding processes should encode
-    // things in the same order.
-    //
-    // When printing worlds in WIT freestanding function imports are
-    // printed first, then types. Resource functions are attached to
-    // types which means that they all come last. Sort all
-    // resource-related functions here to the back of the `imports` list
-    // while keeping everything else in front, using a stable sort to
-    // preserve preexisting ordering.
-    let mut imports = world.imports.iter().collect::<Vec<_>>();
-    imports.sort_by_key(|(_name, import)| match import {
-        WorldItem::Function(f) => match f.kind {
-            FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => 0,
-            _ => 1,
-        },
-        _ => 0,
-    });
-
     // Encode the imports
-    for (name, import) in imports {
+    for (name, import) in world.imports.iter() {
         let name = resolve.name_world_key(name);
         log::trace!("encoding import {name}");
         let ty = match import {
diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs
index 3292fd05..3452ef66 100644
--- a/crates/wit-component/src/printing.rs
+++ b/crates/wit-component/src/printing.rs
@@ -383,12 +383,32 @@ impl<O: Output> WitPrinter<O> {
         Ok(())
     }
 
+    /// Prints the world `id` within `resolve`.
+    ///
+    /// This is a little tricky to preserve round-tripping that WIT wants. This
+    /// function inherently can't preserve ordering of imports because resource
+    /// functions aren't guaranteed to be all adjacent to the resource itself
+    /// they're attached to. That means that at the very least, when printing
+    /// resource functions, items may be printed out-of-order.
+    ///
+    /// To help solve this the printing here is kept in sync with WIT encoding
+    /// of worlds which is to print items in the order of:
+    ///
+    /// * Any imported interface. Ordering between interfaces is preserved.
+    /// * Any types, including resource functions on those types. Ordering
+    ///   between types is preserved.
+    /// * Any functions, which may refer to those types. Ordering between
+    ///   functions is preserved.
+    ///
+    /// This keeps things printed in a roughly topological fashion and makes
+    /// round-tripping a bit more reliable.
     fn print_world(&mut self, resolve: &Resolve, id: WorldId) -> Result<()> {
         let prev_items = mem::replace(&mut self.any_items, false);
         let world = &resolve.worlds[id];
         let pkgid = world.package.unwrap();
         let mut types = Vec::new();
         let mut resource_funcs = HashMap::new();
+        let mut function_imports_to_print = Vec::new();
         for (name, import) in world.imports.iter() {
             match import {
                 WorldItem::Type(t) => match name {
@@ -401,6 +421,8 @@ impl<O: Output> WitPrinter<O> {
                             resource_funcs.entry(id).or_insert(Vec::new()).push(f);
                             continue;
                         }
+                        function_imports_to_print.push((name, import));
+                        continue;
                     }
                     self.print_world_item(resolve, name, import, pkgid, "import")?;
                     // Don't put a blank line between imports, but count
@@ -416,6 +438,11 @@ impl<O: Output> WitPrinter<O> {
             types.into_iter(),
             &resource_funcs,
         )?;
+
+        for (name, import) in function_imports_to_print {
+            self.print_world_item(resolve, name, import, pkgid, "import")?;
+            self.any_items = true;
+        }
         if !world.exports.is_empty() {
             self.new_item();
         }
@@ -573,6 +600,13 @@ impl<O: Output> WitPrinter<O> {
                         self.print_type_name(resolve, ty)?;
                         self.output.generic_args_end();
                     }
+                    TypeDefKind::FixedSizeList(ty, size) => {
+                        self.output.ty("list", TypeKind::BuiltIn);
+                        self.output.generic_args_start();
+                        self.print_type_name(resolve, ty)?;
+                        self.output.push_str(&format!(", {}", *size));
+                        self.output.generic_args_end();
+                    }
                     TypeDefKind::Type(ty) => self.print_type_name(resolve, ty)?,
                     TypeDefKind::Future(ty) => {
                         if let Some(ty) = ty {
@@ -747,6 +781,9 @@ impl<O: Output> WitPrinter<O> {
                     TypeDefKind::List(inner) => {
                         self.declare_list(resolve, ty.name.as_deref(), inner)?
                     }
+                    TypeDefKind::FixedSizeList(inner, size) => {
+                        self.declare_fixed_size_list(resolve, ty.name.as_deref(), inner, *size)?
+                    }
                     TypeDefKind::Type(inner) => match ty.name.as_deref() {
                         Some(name) => {
                             self.output.keyword("type");
@@ -960,6 +997,30 @@ impl<O: Output> WitPrinter<O> {
         Ok(())
     }
 
+    fn declare_fixed_size_list(
+        &mut self,
+        resolve: &Resolve,
+        name: Option<&str>,
+        ty: &Type,
+        elements: u32,
+    ) -> Result<()> {
+        if let Some(name) = name {
+            self.output.keyword("type");
+            self.output.str(" ");
+            self.print_name_type(name, TypeKind::List);
+            self.output.str(" = ");
+            self.output.ty("list", TypeKind::BuiltIn);
+            self.output.str("<");
+            self.print_type_name(resolve, ty)?;
+            self.output.str(&format!(", {elements}"));
+            self.output.str(">");
+            self.output.semicolon();
+            return Ok(());
+        }
+
+        Ok(())
+    }
+
     fn declare_stream(
         &mut self,
         resolve: &Resolve,
diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs
index 211a774a..8e2d81cf 100644
--- a/crates/wit-component/src/validation.rs
+++ b/crates/wit-component/src/validation.rs
@@ -607,7 +607,7 @@ impl ImportMap {
             }
 
             if Some(name) == names.yield_() {
-                let expected = FuncType::new([], []);
+                let expected = FuncType::new([], [ValType::I32]);
                 validate_func_sig(name, &expected, ty)?;
                 return Ok(Import::Yield { async_ });
             }
diff --git a/crates/wit-encoder/src/from_parser.rs b/crates/wit-encoder/src/from_parser.rs
index e8381d9b..c07da9c6 100644
--- a/crates/wit-encoder/src/from_parser.rs
+++ b/crates/wit-encoder/src/from_parser.rs
@@ -243,6 +243,10 @@ impl<'a> Converter<'a> {
                         let output = Type::list(self.convert_type(ty));
                         TypeDefKind::Type(output)
                     }
+                    wit_parser::TypeDefKind::FixedSizeList(ty, size) => {
+                        let output = Type::fixed_size_list(self.convert_type(ty), *size);
+                        TypeDefKind::Type(output)
+                    }
                     wit_parser::TypeDefKind::Handle(handle) => {
                         let output = self.handle_to_type(handle);
                         TypeDefKind::Type(output)
@@ -304,6 +308,9 @@ impl<'a> Converter<'a> {
                         wit_parser::TypeDefKind::List(type_) => {
                             Type::list(self.convert_type(type_))
                         }
+                        wit_parser::TypeDefKind::FixedSizeList(type_, size) => {
+                            Type::fixed_size_list(self.convert_type(type_), *size)
+                        }
                         wit_parser::TypeDefKind::Handle(handle) => self.handle_to_type(handle),
                         wit_parser::TypeDefKind::Future(type_) => {
                             Type::future(self.convert_option_type(type_))
diff --git a/crates/wit-encoder/src/ty.rs b/crates/wit-encoder/src/ty.rs
index 75b7e7c1..18513b53 100644
--- a/crates/wit-encoder/src/ty.rs
+++ b/crates/wit-encoder/src/ty.rs
@@ -26,6 +26,7 @@ pub enum Type {
     Option(Box<Type>),
     Result(Box<Result_>),
     List(Box<Type>),
+    FixedSizeList(Box<Type>, u32),
     Tuple(Tuple),
     Future(Option<Box<Type>>),
     Stream(Option<Box<Type>>),
@@ -58,6 +59,9 @@ impl Type {
     pub fn list(type_: Type) -> Self {
         Type::List(Box::new(type_))
     }
+    pub fn fixed_size_list(type_: Type, size: u32) -> Self {
+        Type::FixedSizeList(Box::new(type_), size)
+    }
     pub fn tuple(types: impl IntoIterator<Item = Type>) -> Self {
         Type::Tuple(Tuple {
             types: types.into_iter().collect(),
@@ -116,6 +120,9 @@ impl Display for Type {
             Type::List(type_) => {
                 write!(f, "list<{type_}>")
             }
+            Type::FixedSizeList(type_, size) => {
+                write!(f, "list<{type_}, {size}>")
+            }
             Type::Tuple(tuple) => tuple.fmt(f),
             Type::Future(None) => {
                 write!(f, "future")
diff --git a/crates/wit-parser/src/abi.rs b/crates/wit-parser/src/abi.rs
index 675feeb4..9dc09203 100644
--- a/crates/wit-parser/src/abi.rs
+++ b/crates/wit-parser/src/abi.rs
@@ -132,7 +132,44 @@ pub enum AbiVariant {
     GuestExportAsyncStackful,
 }
 
+pub struct FlatTypes<'a> {
+    types: &'a mut [WasmType],
+    cur: usize,
+    overflow: bool,
+}
+
+impl<'a> FlatTypes<'a> {
+    pub fn new(types: &'a mut [WasmType]) -> FlatTypes<'a> {
+        FlatTypes {
+            types,
+            cur: 0,
+            overflow: false,
+        }
+    }
+
+    pub fn push(&mut self, ty: WasmType) -> bool {
+        match self.types.get_mut(self.cur) {
+            Some(next) => {
+                *next = ty;
+                self.cur += 1;
+                true
+            }
+            None => {
+                self.overflow = true;
+                false
+            }
+        }
+    }
+
+    pub fn to_vec(&self) -> Vec<WasmType> {
+        self.types[..self.cur].to_vec()
+    }
+}
+
 impl Resolve {
+    const MAX_FLAT_PARAMS: usize = 16;
+    const MAX_FLAT_RESULTS: usize = 1;
+
     /// Get the WebAssembly type signature for this interface function
     ///
     /// The first entry returned is the list of parameters and the second entry
@@ -147,19 +184,17 @@ impl Resolve {
             };
         }
 
-        const MAX_FLAT_PARAMS: usize = 16;
-        const MAX_FLAT_RESULTS: usize = 1;
-
-        let mut params = Vec::new();
-        let mut indirect_params = false;
-        for (_, param) in func.params.iter() {
-            self.push_flat(param, &mut params);
-        }
-
-        if params.len() > MAX_FLAT_PARAMS {
-            params.truncate(0);
-            params.push(WasmType::Pointer);
-            indirect_params = true;
+        // Note that one extra parameter is allocated in case a return pointer
+        // is needed down below for imports.
+        let mut storage = [WasmType::I32; Self::MAX_FLAT_PARAMS + 1];
+        let mut params = FlatTypes::new(&mut storage);
+        let ok = self.push_flat_list(func.params.iter().map(|(_, param)| param), &mut params);
+        assert_eq!(ok, !params.overflow);
+
+        let indirect_params = !ok || params.cur > Self::MAX_FLAT_PARAMS;
+        if indirect_params {
+            params.types[0] = WasmType::Pointer;
+            params.cur = 1;
         } else {
             if matches!(
                 (&func.kind, variant),
@@ -176,15 +211,15 @@ impl Resolve {
                 // resource Handles and then use either I32 or Pointer in abi::push_flat().
                 // But this contextual information isn't available, yet.
                 // See https://github.com/bytecodealliance/wasm-tools/pull/1438 for more details.
-                assert!(matches!(params[0], WasmType::I32));
-                params[0] = WasmType::Pointer;
+                assert!(matches!(params.types[0], WasmType::I32));
+                params.types[0] = WasmType::Pointer;
             }
         }
 
         match variant {
             AbiVariant::GuestExportAsync => {
                 return WasmSignature {
-                    params,
+                    params: params.to_vec(),
                     indirect_params,
                     results: vec![WasmType::Pointer],
                     retptr: false,
@@ -192,7 +227,7 @@ impl Resolve {
             }
             AbiVariant::GuestExportAsyncStackful => {
                 return WasmSignature {
-                    params,
+                    params: params.to_vec(),
                     indirect_params,
                     results: Vec::new(),
                     retptr: false,
@@ -201,42 +236,50 @@ impl Resolve {
             _ => {}
         }
 
-        let mut results = Vec::new();
+        let mut storage = [WasmType::I32; Self::MAX_FLAT_RESULTS];
+        let mut results = FlatTypes::new(&mut storage);
         if let Some(ty) = &func.result {
-            self.push_flat(ty, &mut results)
+            self.push_flat(ty, &mut results);
         }
 
-        let mut retptr = false;
+        let retptr = results.overflow;
 
         // Rust/C don't support multi-value well right now, so if a function
         // would have multiple results then instead truncate it. Imports take a
         // return pointer to write into and exports return a pointer they wrote
         // into.
-        if results.len() > MAX_FLAT_RESULTS {
-            retptr = true;
-            results.truncate(0);
+        if retptr {
+            results.cur = 0;
             match variant {
                 AbiVariant::GuestImport => {
-                    params.push(WasmType::Pointer);
+                    assert!(params.push(WasmType::Pointer));
                 }
                 AbiVariant::GuestExport => {
-                    results.push(WasmType::Pointer);
+                    assert!(results.push(WasmType::Pointer));
                 }
                 _ => unreachable!(),
             }
         }
 
         WasmSignature {
-            params,
+            params: params.to_vec(),
             indirect_params,
-            results,
+            results: results.to_vec(),
             retptr,
         }
     }
 
+    fn push_flat_list<'a>(
+        &self,
+        mut list: impl Iterator<Item = &'a Type>,
+        result: &mut FlatTypes<'_>,
+    ) -> bool {
+        list.all(|ty| self.push_flat(ty, result))
+    }
+
     /// Appends the flat wasm types representing `ty` onto the `result`
     /// list provided.
-    pub fn push_flat(&self, ty: &Type, result: &mut Vec<WasmType>) {
+    pub fn push_flat(&self, ty: &Type, result: &mut FlatTypes<'_>) -> bool {
         match ty {
             Type::Bool
             | Type::S8
@@ -251,67 +294,53 @@ impl Resolve {
             Type::U64 | Type::S64 => result.push(WasmType::I64),
             Type::F32 => result.push(WasmType::F32),
             Type::F64 => result.push(WasmType::F64),
-            Type::String => {
-                result.push(WasmType::Pointer);
-                result.push(WasmType::Length);
-            }
+            Type::String => result.push(WasmType::Pointer) && result.push(WasmType::Length),
 
             Type::Id(id) => match &self.types[*id].kind {
                 TypeDefKind::Type(t) => self.push_flat(t, result),
 
                 TypeDefKind::Handle(Handle::Own(_) | Handle::Borrow(_)) => {
-                    result.push(WasmType::I32);
+                    result.push(WasmType::I32)
                 }
 
                 TypeDefKind::Resource => todo!(),
 
                 TypeDefKind::Record(r) => {
-                    for field in r.fields.iter() {
-                        self.push_flat(&field.ty, result);
-                    }
+                    self.push_flat_list(r.fields.iter().map(|f| &f.ty), result)
                 }
 
-                TypeDefKind::Tuple(t) => {
-                    for ty in t.types.iter() {
-                        self.push_flat(ty, result);
-                    }
-                }
+                TypeDefKind::Tuple(t) => self.push_flat_list(t.types.iter(), result),
 
                 TypeDefKind::Flags(r) => {
-                    for _ in 0..r.repr().count() {
-                        result.push(WasmType::I32);
-                    }
+                    self.push_flat_list((0..r.repr().count()).map(|_| &Type::U32), result)
                 }
 
                 TypeDefKind::List(_) => {
-                    result.push(WasmType::Pointer);
-                    result.push(WasmType::Length);
+                    result.push(WasmType::Pointer) && result.push(WasmType::Length)
+                }
+
+                TypeDefKind::FixedSizeList(ty, size) => {
+                    self.push_flat_list((0..*size).map(|_| ty), result)
                 }
 
                 TypeDefKind::Variant(v) => {
-                    result.push(v.tag().into());
-                    self.push_flat_variants(v.cases.iter().map(|c| c.ty.as_ref()), result);
+                    result.push(v.tag().into())
+                        && self.push_flat_variants(v.cases.iter().map(|c| c.ty.as_ref()), result)
                 }
 
                 TypeDefKind::Enum(e) => result.push(e.tag().into()),
 
                 TypeDefKind::Option(t) => {
-                    result.push(WasmType::I32);
-                    self.push_flat_variants([None, Some(t)], result);
+                    result.push(WasmType::I32) && self.push_flat_variants([None, Some(t)], result)
                 }
 
                 TypeDefKind::Result(r) => {
-                    result.push(WasmType::I32);
-                    self.push_flat_variants([r.ok.as_ref(), r.err.as_ref()], result);
-                }
-
-                TypeDefKind::Future(_) => {
-                    result.push(WasmType::I32);
+                    result.push(WasmType::I32)
+                        && self.push_flat_variants([r.ok.as_ref(), r.err.as_ref()], result)
                 }
 
-                TypeDefKind::Stream(_) => {
-                    result.push(WasmType::I32);
-                }
+                TypeDefKind::Future(_) => result.push(WasmType::I32),
+                TypeDefKind::Stream(_) => result.push(WasmType::I32),
 
                 TypeDefKind::Unknown => unreachable!(),
             },
@@ -321,10 +350,11 @@ impl Resolve {
     fn push_flat_variants<'a>(
         &self,
         tys: impl IntoIterator<Item = Option<&'a Type>>,
-        result: &mut Vec<WasmType>,
-    ) {
-        let mut temp = Vec::new();
-        let start = result.len();
+        result: &mut FlatTypes<'_>,
+    ) -> bool {
+        let mut temp = result.types[result.cur..].to_vec();
+        let mut temp = FlatTypes::new(&mut temp);
+        let start = result.cur;
 
         // Push each case's type onto a temporary vector, and then
         // merge that vector into our final list starting at
@@ -334,15 +364,27 @@ impl Resolve {
         // `i32` might be the `f32` bitcasted.
         for ty in tys {
             if let Some(ty) = ty {
-                self.push_flat(ty, &mut temp);
+                if !self.push_flat(ty, &mut temp) {
+                    result.overflow = true;
+                    return false;
+                }
 
-                for (i, ty) in temp.drain(..).enumerate() {
-                    match result.get_mut(start + i) {
-                        Some(prev) => *prev = join(*prev, ty),
-                        None => result.push(ty),
+                for (i, ty) in temp.types[..temp.cur].iter().enumerate() {
+                    let i = i + start;
+                    if i < result.cur {
+                        result.types[i] = join(result.types[i], *ty);
+                    } else if result.cur == result.types.len() {
+                        result.overflow = true;
+                        return false;
+                    } else {
+                        result.types[i] = *ty;
+                        result.cur += 1;
                     }
                 }
+                temp.cur = 0;
             }
         }
+
+        true
     }
 }
diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs
index 34556c3b..41186ab3 100644
--- a/crates/wit-parser/src/ast.rs
+++ b/crates/wit-parser/src/ast.rs
@@ -754,6 +754,7 @@ enum Type<'a> {
     String(Span),
     Name(Id<'a>),
     List(List<'a>),
+    FixedSizeList(FixedSizeList<'a>),
     Handle(Handle<'a>),
     Resource(Resource<'a>),
     Record(Record<'a>),
@@ -905,6 +906,12 @@ struct List<'a> {
     ty: Box<Type<'a>>,
 }
 
+struct FixedSizeList<'a> {
+    span: Span,
+    ty: Box<Type<'a>>,
+    size: u32,
+}
+
 struct Future<'a> {
     span: Span,
     ty: Option<Box<Type<'a>>>,
@@ -1363,14 +1370,34 @@ impl<'a> Type<'a> {
             Some((span, Token::String_)) => Ok(Type::String(span)),
 
             // list<T>
+            // list<T, N>
             Some((span, Token::List)) => {
                 tokens.expect(Token::LessThan)?;
                 let ty = Type::parse(tokens)?;
+                let size = if tokens.eat(Token::Comma)? {
+                    let number = tokens.next()?;
+                    if let Some((span, Token::Integer)) = number {
+                        let size: u32 = tokens.get_span(span).parse()?;
+                        Some(size)
+                    } else {
+                        return Err(err_expected(tokens, "fixed size", number).into());
+                    }
+                } else {
+                    None
+                };
                 tokens.expect(Token::GreaterThan)?;
-                Ok(Type::List(List {
-                    span,
-                    ty: Box::new(ty),
-                }))
+                if let Some(size) = size {
+                    Ok(Type::FixedSizeList(FixedSizeList {
+                        span,
+                        ty: Box::new(ty),
+                        size,
+                    }))
+                } else {
+                    Ok(Type::List(List {
+                        span,
+                        ty: Box::new(ty),
+                    }))
+                }
             }
 
             // option<T>
@@ -1483,6 +1510,7 @@ impl<'a> Type<'a> {
             | Type::ErrorContext(span) => *span,
             Type::Name(id) => id.span,
             Type::List(l) => l.span,
+            Type::FixedSizeList(l) => l.span,
             Type::Handle(h) => h.span(),
             Type::Resource(r) => r.span,
             Type::Record(r) => r.span,
diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs
index e1b7016b..fc718c9d 100644
--- a/crates/wit-parser/src/ast/resolve.rs
+++ b/crates/wit-parser/src/ast/resolve.rs
@@ -92,6 +92,7 @@ enum Key {
     Tuple(Vec<Type>),
     Enum(Vec<String>),
     List(Type),
+    FixedSizeList(Type, u32),
     Option(Type),
     Result(Option<Type>, Option<Type>),
     Future(Option<Type>),
@@ -1174,6 +1175,10 @@ impl<'a> Resolver<'a> {
                 let ty = self.resolve_type(&list.ty, stability)?;
                 TypeDefKind::List(ty)
             }
+            ast::Type::FixedSizeList(list) => {
+                let ty = self.resolve_type(&list.ty, stability)?;
+                TypeDefKind::FixedSizeList(ty, list.size)
+            }
             ast::Type::Handle(handle) => TypeDefKind::Handle(match handle {
                 ast::Handle::Own { resource } => Handle::Own(self.validate_resource(resource)?),
                 ast::Handle::Borrow { resource } => {
@@ -1334,7 +1339,10 @@ impl<'a> Resolver<'a> {
                 if !matches!(&ty.stability, Stability::Unknown) {
                     Some(&ty.stability)
                 } else {
-                    find_in_kind(types, &ty.kind)
+                    // Note that this type isn't searched recursively since the
+                    // creation of `id` should already have searched its
+                    // recursive edges, so there's no need to search again.
+                    None
                 }
             } else {
                 None
@@ -1351,7 +1359,9 @@ impl<'a> Resolver<'a> {
                     find_in_type(types, Type::Id(*id))
                 }
                 TypeDefKind::Tuple(t) => t.types.iter().find_map(|ty| find_in_type(types, *ty)),
-                TypeDefKind::List(ty) | TypeDefKind::Option(ty) => find_in_type(types, *ty),
+                TypeDefKind::List(ty)
+                | TypeDefKind::FixedSizeList(ty, _)
+                | TypeDefKind::Option(ty) => find_in_type(types, *ty),
                 TypeDefKind::Future(ty) | TypeDefKind::Stream(ty) => {
                     ty.as_ref().and_then(|ty| find_in_type(types, *ty))
                 }
@@ -1443,6 +1453,7 @@ impl<'a> Resolver<'a> {
                 Key::Enum(r.cases.iter().map(|f| f.name.clone()).collect::<Vec<_>>())
             }
             TypeDefKind::List(ty) => Key::List(*ty),
+            TypeDefKind::FixedSizeList(ty, size) => Key::FixedSizeList(*ty, *size),
             TypeDefKind::Option(t) => Key::Option(*t),
             TypeDefKind::Result(r) => Key::Result(r.ok, r.err),
             TypeDefKind::Future(ty) => Key::Future(*ty),
@@ -1678,9 +1689,9 @@ fn collect_deps<'a>(ty: &ast::Type<'a>, deps: &mut Vec<ast::Id<'a>>) {
                 }
             }
         }
-        ast::Type::Option(ast::Option_ { ty, .. }) | ast::Type::List(ast::List { ty, .. }) => {
-            collect_deps(ty, deps)
-        }
+        ast::Type::Option(ast::Option_ { ty, .. })
+        | ast::Type::List(ast::List { ty, .. })
+        | ast::Type::FixedSizeList(ast::FixedSizeList { ty, .. }) => collect_deps(ty, deps),
         ast::Type::Result(r) => {
             if let Some(ty) = &r.ok {
                 collect_deps(ty, deps);
diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs
index 26c7e93e..7dce0d1c 100644
--- a/crates/wit-parser/src/decoding.rs
+++ b/crates/wit-parser/src/decoding.rs
@@ -1254,6 +1254,7 @@ impl WitPackageDecoder<'_> {
         match &kind {
             TypeDefKind::Type(_)
             | TypeDefKind::List(_)
+            | TypeDefKind::FixedSizeList(..)
             | TypeDefKind::Tuple(_)
             | TypeDefKind::Option(_)
             | TypeDefKind::Result(_)
@@ -1294,6 +1295,11 @@ impl WitPackageDecoder<'_> {
                 Ok(TypeDefKind::List(t))
             }
 
+            ComponentDefinedType::FixedSizeList(t, size) => {
+                let t = self.convert_valtype(t)?;
+                Ok(TypeDefKind::FixedSizeList(t, *size))
+            }
+
             ComponentDefinedType::Tuple(t) => {
                 let types = t
                     .types
@@ -1586,6 +1592,15 @@ impl Registrar<'_> {
                 self.valtype(t, ty)
             }
 
+            ComponentDefinedType::FixedSizeList(t, elements) => {
+                let ty = match &self.resolve.types[id].kind {
+                    TypeDefKind::FixedSizeList(r, elements2) if elements2 == elements => r,
+                    TypeDefKind::Type(Type::Id(_)) => return Ok(()),
+                    _ => bail!("expected a fixed size {elements} list"),
+                };
+                self.valtype(t, ty)
+            }
+
             ComponentDefinedType::Tuple(t) => {
                 let ty = match &self.resolve.types[id].kind {
                     TypeDefKind::Tuple(r) => r,
diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs
index d8551db1..5f8aa6c9 100644
--- a/crates/wit-parser/src/lib.rs
+++ b/crates/wit-parser/src/lib.rs
@@ -569,6 +569,7 @@ pub enum TypeDefKind {
     Option(Type),
     Result(Result_),
     List(Type),
+    FixedSizeList(Type, u32),
     Future(Option<Type>),
     Stream(Option<Type>),
     Type(Type),
@@ -597,6 +598,7 @@ impl TypeDefKind {
             TypeDefKind::Option(_) => "option",
             TypeDefKind::Result(_) => "result",
             TypeDefKind::List(_) => "list",
+            TypeDefKind::FixedSizeList(..) => "fixed size list",
             TypeDefKind::Future(_) => "future",
             TypeDefKind::Stream(_) => "stream",
             TypeDefKind::Type(_) => "type",
@@ -1153,7 +1155,10 @@ fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec<TypeI
                 }
             }
         }
-        TypeDefKind::Option(ty) | TypeDefKind::List(ty) | TypeDefKind::Type(ty) => {
+        TypeDefKind::Option(ty)
+        | TypeDefKind::List(ty)
+        | TypeDefKind::FixedSizeList(ty, ..)
+        | TypeDefKind::Type(ty) => {
             find_futures_and_streams(resolve, *ty, results);
         }
         TypeDefKind::Result(r) => {
diff --git a/crates/wit-parser/src/live.rs b/crates/wit-parser/src/live.rs
index 27a769b0..48637f54 100644
--- a/crates/wit-parser/src/live.rs
+++ b/crates/wit-parser/src/live.rs
@@ -132,6 +132,7 @@ pub trait TypeIdVisitor {
         match &ty.kind {
             TypeDefKind::Type(t)
             | TypeDefKind::List(t)
+            | TypeDefKind::FixedSizeList(t, ..)
             | TypeDefKind::Option(t)
             | TypeDefKind::Future(Some(t))
             | TypeDefKind::Stream(Some(t)) => self.visit_type(resolve, t),
diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs
index 8950dcd1..a4ff8a15 100644
--- a/crates/wit-parser/src/resolve.rs
+++ b/crates/wit-parser/src/resolve.rs
@@ -573,7 +573,7 @@ package {name} is defined in two different locations:\n\
                 | TypeDefKind::Result(_)
                 | TypeDefKind::Future(_)
                 | TypeDefKind::Stream(_) => false,
-                TypeDefKind::Type(t) => self.all_bits_valid(t),
+                TypeDefKind::Type(t) | TypeDefKind::FixedSizeList(t, ..) => self.all_bits_valid(t),
 
                 TypeDefKind::Handle(h) => match h {
                     crate::Handle::Own(_) => true,
@@ -1931,7 +1931,52 @@ package {name} is defined in two different locations:\n\
         // topological order between them.
         let mut new_imports = IndexMap::new();
         let world = &self.worlds[world_id];
-        for (name, item) in world.imports.iter() {
+
+        // Sort the imports by "class" to ensure that this matches the order
+        // that items are printed and that items are in topological order.
+        //
+        // When printing worlds in WIT:
+        //
+        // * interfaces come first
+        // * types are next
+        //   * type imports are first
+        //   * type definitions are next
+        //   * resource definitions have methods printed inline
+        // * freestanding functions are last
+        //
+        // This reflects the topological order between items where types
+        // can refer to imports and functions can refer to these types. Ordering
+        // within a single class (e.g. imports depending on each other, types
+        // referring to each other) is already preserved by other passes in this
+        // file and general AST resolution. That means that a stable sort here
+        // can be used to ensure that each class is in the right location
+        // relative to the others.
+        //
+        // Overall this ensures that round-trips of WIT through wasm should
+        // always produce the same result.
+        let sort_key = |resolve: &Resolve, item: &WorldItem| match item {
+            WorldItem::Interface { .. } => 0,
+            WorldItem::Type(ty) => {
+                let ty = &resolve.types[*ty];
+                match ty.kind {
+                    TypeDefKind::Type(Type::Id(t)) if resolve.types[t].owner != ty.owner => 1,
+                    _ => 2,
+                }
+            }
+            WorldItem::Function(f) => {
+                if f.kind.resource().is_none() {
+                    3
+                } else {
+                    4
+                }
+            }
+        };
+
+        // Sort world items when we start to elaborate the world to start with a
+        // topological view of items.
+        let mut world_imports = world.imports.iter().collect::<Vec<_>>();
+        world_imports.sort_by_key(|(_name, import)| sort_key(self, import));
+        for (name, item) in world_imports {
             match item {
                 // Interfaces get their dependencies added first followed by the
                 // interface itself.
@@ -1987,6 +2032,11 @@ package {name} is defined in two different locations:\n\
 
         self.elaborate_world_exports(&export_interfaces, &mut new_imports, &mut new_exports)?;
 
+        // In addition to sorting at the start of elaboration also sort here at
+        // the end of elaboration to handle types being interspersed with
+        // interfaces as they're found.
+        new_imports.sort_by_cached_key(|_name, import| sort_key(self, import));
+
         // And with all that done the world is updated in-place with
         // imports/exports.
         log::trace!("imports = {:?}", new_imports);
@@ -3135,7 +3185,7 @@ impl Remap {
                     }
                 }
             }
-            Option(t) | List(t) | Future(Some(t)) | Stream(Some(t)) => {
+            Option(t) | List(t, ..) | FixedSizeList(t, ..) | Future(Some(t)) | Stream(Some(t)) => {
                 self.update_ty(resolve, t, span)?
             }
             Result(r) => {
@@ -3522,6 +3572,7 @@ impl Remap {
             TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.type_has_borrow(resolve, t)),
             TypeDefKind::Enum(_) => false,
             TypeDefKind::List(ty)
+            | TypeDefKind::FixedSizeList(ty, ..)
             | TypeDefKind::Future(Some(ty))
             | TypeDefKind::Stream(Some(ty))
             | TypeDefKind::Option(ty) => self.type_has_borrow(resolve, ty),
diff --git a/crates/wit-parser/src/resolve/clone.rs b/crates/wit-parser/src/resolve/clone.rs
index 5554826f..32bc5bc2 100644
--- a/crates/wit-parser/src/resolve/clone.rs
+++ b/crates/wit-parser/src/resolve/clone.rs
@@ -109,7 +109,9 @@ impl<'a> Cloner<'a> {
             TypeDefKind::Handle(Handle::Own(ty) | Handle::Borrow(ty)) => {
                 self.type_id(ty);
             }
-            TypeDefKind::Option(ty) | TypeDefKind::List(ty) => {
+            TypeDefKind::Option(ty)
+            | TypeDefKind::List(ty)
+            | TypeDefKind::FixedSizeList(ty, ..) => {
                 self.ty(ty);
             }
             TypeDefKind::Tuple(list) => {
diff --git a/crates/wit-parser/src/sizealign.rs b/crates/wit-parser/src/sizealign.rs
index 5e0f0e0d..a9b96dc0 100644
--- a/crates/wit-parser/src/sizealign.rs
+++ b/crates/wit-parser/src/sizealign.rs
@@ -260,6 +260,17 @@ impl SizeAlign {
     fn calculate(&self, ty: &TypeDef) -> ElementInfo {
         match &ty.kind {
             TypeDefKind::Type(t) => ElementInfo::new(self.size(t), self.align(t)),
+            TypeDefKind::FixedSizeList(t, size) => {
+                let field_align = self.align(t);
+                let field_size = self.size(t);
+                ElementInfo::new(
+                    ArchitectureSize::new(
+                        field_size.bytes.checked_mul(*size as usize).unwrap(),
+                        field_size.pointers.checked_mul(*size as usize).unwrap(),
+                    ),
+                    field_align,
+                )
+            }
             TypeDefKind::List(_) => {
                 ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer)
             }
diff --git a/crates/wit-smith/src/generate.rs b/crates/wit-smith/src/generate.rs
index d02edc41..a6fbca80 100644
--- a/crates/wit-smith/src/generate.rs
+++ b/crates/wit-smith/src/generate.rs
@@ -1,30 +1,27 @@
 use crate::config::Config;
 use arbitrary::{Arbitrary, Result, Unstructured};
-use indexmap::IndexMap;
+use indexmap::{IndexMap, IndexSet};
 use semver::Version;
 use std::collections::hash_map::{Entry, HashMap};
 use std::collections::HashSet;
 use std::fmt::Write;
+use std::mem;
 use std::rc::Rc;
 use std::str;
 use wit_parser::*;
 
 pub struct Generator {
     config: Config,
-    packages: PackageList,
+    packages: Packages,
     next_interface_id: u32,
 }
 
-type TypeList = Vec<Type>;
-type InterfaceList = IndexMap<String, FileInterface>;
-type PackageList = Vec<(PackageName, InterfaceList)>;
-
 struct InterfaceGenerator<'a> {
     generator: &'a Generator,
     file: &'a mut File,
     config: &'a Config,
     unique_names: HashSet<String>,
-    types_in_interface: TypeList,
+    types_in_interface: Vec<Type>,
 }
 
 #[derive(Clone)]
@@ -34,9 +31,17 @@ struct Type {
     is_resource: bool,
 }
 
+#[derive(Default)]
+struct Packages {
+    list: Vec<Package>,
+    packages_with_interfaces: Vec<usize>,
+    packages_with_worlds: Vec<usize>,
+}
+
 pub struct Package {
     pub name: PackageName,
     pub sources: SourceMap,
+    file: File,
 }
 
 #[derive(Clone)]
@@ -56,23 +61,26 @@ impl Generator {
     }
 
     pub fn generate(&mut self, u: &mut Unstructured<'_>) -> Result<Vec<Package>> {
-        let mut packages = Vec::new();
         let mut names = HashSet::new();
-        while packages.len() < self.config.max_packages && packages.is_empty() {
-            let (pkg, interfaces) = self.gen_package(u, &mut names)?;
-            if interfaces.len() > 0 {
-                self.packages.push((pkg.name.clone(), interfaces));
+        while self.packages.list.len() < self.config.max_packages && self.packages.list.is_empty() {
+            let pkg = self.gen_package(u, &mut names)?;
+            let i = self.packages.list.len();
+            if pkg.file.interfaces.len() > 0 {
+                self.packages.packages_with_interfaces.push(i);
+            }
+            if pkg.file.worlds.len() > 0 {
+                self.packages.packages_with_worlds.push(i);
             }
-            packages.push(pkg);
+            self.packages.list.push(pkg);
         }
-        Ok(packages)
+        Ok(mem::take(&mut self.packages.list))
     }
 
     fn gen_package(
         &mut self,
         u: &mut Unstructured<'_>,
         names: &mut HashSet<String>,
-    ) -> Result<(Package, InterfaceList)> {
+    ) -> Result<Package> {
         let namespace = gen_unique_name(u, names)?;
         let name = gen_unique_name(u, names)?;
         let version = if u.arbitrary()? {
@@ -86,6 +94,7 @@ impl Generator {
                 name,
                 version,
             },
+            file: File::default(),
             sources: SourceMap::new(),
         };
 
@@ -101,7 +110,6 @@ impl Generator {
         let mut empty = true;
         let mut files = vec![File::default()];
         let mut package_names = HashSet::new();
-        let mut package = File::default();
         log::debug!("===================== new package ====================");
         while items < self.config.max_pkg_items {
             items += 1;
@@ -114,15 +122,7 @@ impl Generator {
             let file = match files.get_mut(i) {
                 Some(file) => file,
                 None => {
-                    files.push(File {
-                        items: Vec::new(),
-                        namespace: package
-                            .interfaces
-                            .iter()
-                            .map(|(k, _)| (k.clone(), Definition::Package))
-                            .collect(),
-                        interfaces: package.interfaces.clone(),
-                    });
+                    files.push(ret.file.clone());
                     files.last_mut().unwrap()
                 }
             };
@@ -137,15 +137,37 @@ impl Generator {
 
             match generate {
                 Generate::World => {
-                    let name = file.gen_unique_package_name(u, &mut package_names)?;
+                    let name =
+                        file.gen_unique_package_name(u, &mut package_names, DefinitionKind::World)?;
                     log::debug!("new world `{name}` in {i}");
                     let world = self.gen_world(u, &name, file)?;
                     file.items.push(world);
 
+                    // Insert the world at the package and file level, asserting
+                    // uniqueness.
+                    assert!(ret.file.worlds.insert(name.clone()));
+                    assert!(file.worlds.insert(name.clone()));
+                    let prev = ret.file.namespace.insert(
+                        name.clone(),
+                        (DefinitionLevel::Package, DefinitionKind::World),
+                    );
+                    assert!(prev.is_none());
+
+                    // Insert the definition into all other files as well.
+                    for file in files.iter_mut() {
+                        if file.insert_definition(&name, DefinitionKind::World) {
+                            assert!(file.worlds.insert(name.clone()));
+                        }
+                    }
+
                     empty = false;
                 }
                 Generate::Interface => {
-                    let name = file.gen_unique_package_name(u, &mut package_names)?;
+                    let name = file.gen_unique_package_name(
+                        u,
+                        &mut package_names,
+                        DefinitionKind::Interface,
+                    )?;
                     log::debug!("new interface `{name}` in {i}");
                     let id = self.next_interface_id;
                     self.next_interface_id += 1;
@@ -162,10 +184,16 @@ impl Generator {
 
                     // This interface is defined at the package level, and it
                     // must be unique.
-                    let prev = package
+                    let prev = ret
+                        .file
                         .interfaces
                         .insert(interface.name.clone(), interface.clone());
                     assert!(prev.is_none());
+                    let prev = ret.file.namespace.insert(
+                        interface.name.clone(),
+                        (DefinitionLevel::Package, DefinitionKind::Interface),
+                    );
+                    assert!(prev.is_none());
 
                     // This is also defined at the file level, and it must be
                     // unique here too.
@@ -176,7 +204,12 @@ impl Generator {
 
                     // Insert the definition into all other files as well.
                     for file in files.iter_mut() {
-                        file.insert_definition(interface.clone());
+                        if file.insert_definition(&interface.name, DefinitionKind::Interface) {
+                            let prev = file
+                                .interfaces
+                                .insert(interface.name.clone(), interface.clone());
+                            assert!(prev.is_none());
+                        }
                     }
 
                     empty = false;
@@ -184,24 +217,38 @@ impl Generator {
                 Generate::Use => {
                     let mut piece = String::new();
                     piece.push_str("use ");
-                    let (name, id, types) = match self.gen_path(u, &mut package, &mut piece)? {
-                        Some(i) => i,
-                        None => continue,
-                    };
+                    let (name, id, types) =
+                        match self.gen_interface_path(u, &mut ret.file, &mut piece)? {
+                            Some(i) => i,
+                            None => continue,
+                        };
                     let name = name.to_string();
                     let types = types.clone();
+                    // If this interface's name already exist within this `file`
+                    // then this must be renamed with `as`. If the name exists
+                    // only at the package level then it's ok to replace it with
+                    // something else.
+                    //
+                    // If the name doesn't exist then use the fuzz input to
+                    // determine whether a rename should happen.
                     let name =
-                        if file.namespace.get(&name) == Some(&Definition::File) || u.arbitrary()? {
-                            let name = file.gen_unique_file_name(u)?;
+                        if matches!(file.namespace.get(&name), Some((DefinitionLevel::File, _)))
+                            || u.arbitrary()?
+                        {
+                            let name = file.gen_unique_file_name(u, DefinitionKind::Interface)?;
                             piece.push_str(" as %");
                             piece.push_str(&name);
                             name
                         } else {
-                            file.namespace.insert(name.clone(), Definition::File);
+                            file.namespace.insert(
+                                name.clone(),
+                                (DefinitionLevel::File, DefinitionKind::Interface),
+                            );
                             name
                         };
                     piece.push_str(";");
                     log::debug!("new use `{name}` in {i}");
+                    file.worlds.swap_remove(&name);
                     file.interfaces
                         .insert(name.clone(), FileInterface { name, id, types });
                     file.items.push(piece)
@@ -240,7 +287,7 @@ impl Generator {
             log::trace!("{s}");
             ret.sources.push(format!("wit{i}.wit").as_ref(), &s);
         }
-        Ok((ret, package.interfaces))
+        Ok(ret)
     }
 
     fn gen_world(
@@ -257,18 +304,18 @@ impl Generator {
         u: &mut Unstructured<'_>,
         name: Option<&str>,
         file: &mut File,
-    ) -> Result<(String, TypeList)> {
+    ) -> Result<(String, Vec<Type>)> {
         let mut generator = InterfaceGenerator::new(self, file);
         let ret = generator.gen_interface(u, name)?;
         Ok((ret, generator.types_in_interface))
     }
 
-    fn gen_path<'a>(
+    fn gen_interface_path<'a>(
         &'a self,
         u: &mut Unstructured<'_>,
         file: &'a mut File,
         dst: &mut String,
-    ) -> Result<Option<(&'a str, u32, &'a Rc<TypeList>)>> {
+    ) -> Result<Option<(&'a str, u32, &'a Rc<Vec<Type>>)>> {
         enum Choice {
             Interfaces,
             Packages,
@@ -277,9 +324,10 @@ impl Generator {
         if !file.interfaces.is_empty() {
             choices.push(Choice::Interfaces);
         }
-        if !self.packages.is_empty() {
+        if !self.packages.packages_with_interfaces.is_empty() {
             choices.push(Choice::Packages);
         }
+
         if choices.is_empty() {
             return Ok(None);
         }
@@ -290,30 +338,90 @@ impl Generator {
                 // Once a name is used from a file's local namespace then it
                 // can't be overridden in that namespace so switch it to a file
                 // definition from whatever it previously was.
-                file.namespace.insert(name.clone(), Definition::File);
+                file.namespace.insert(
+                    name.clone(),
+                    (DefinitionLevel::File, DefinitionKind::Interface),
+                );
+                file.worlds.swap_remove(name);
                 dst.push_str("%");
                 dst.push_str(&i.name);
                 Some((&i.name, i.id, &i.types))
             }
             Choice::Packages => {
-                let (pkg, ifaces) = u.choose(&self.packages)?;
+                let pkg = u.choose(&self.packages.packages_with_interfaces)?;
+                let pkg = &self.packages.list[*pkg];
                 dst.push_str("%");
-                dst.push_str(&pkg.namespace);
+                dst.push_str(&pkg.name.namespace);
                 dst.push_str(":");
                 dst.push_str("%");
-                dst.push_str(&pkg.name);
+                dst.push_str(&pkg.name.name);
                 dst.push_str("/");
-                let i = u.int_in_range(0..=ifaces.len() - 1)?;
-                let i = &ifaces[i];
+                let i = u.int_in_range(0..=pkg.file.interfaces.len() - 1)?;
+                let i = &pkg.file.interfaces[i];
                 dst.push_str("%");
                 dst.push_str(&i.name);
-                if let Some(version) = &pkg.version {
+                if let Some(version) = &pkg.name.version {
                     dst.push_str(&format!("@{version}"));
                 }
                 Some((&i.name, i.id, &i.types))
             }
         })
     }
+
+    fn gen_world_path<'a>(
+        &'a self,
+        u: &mut Unstructured<'_>,
+        file: &'a mut File,
+        dst: &mut String,
+    ) -> Result<Option<&'a str>> {
+        enum Choice {
+            Worlds,
+            Packages,
+        }
+        let mut choices = Vec::new();
+        if !file.worlds.is_empty() {
+            choices.push(Choice::Worlds);
+        }
+        if !self.packages.packages_with_worlds.is_empty() {
+            choices.push(Choice::Packages);
+        }
+
+        if choices.is_empty() {
+            return Ok(None);
+        }
+        Ok(match u.choose(&choices)? {
+            Choice::Worlds => {
+                let i = u.int_in_range(0..=file.worlds.len() - 1)?;
+                let name = &file.worlds[i];
+                dst.push_str("%");
+                dst.push_str(&name);
+                // Same as `gen_interface_path`, once a name is used as a world
+                // it's forced to always be a world so update its definition to
+                // be a file-level world.
+                file.namespace
+                    .insert(name.clone(), (DefinitionLevel::File, DefinitionKind::World));
+                Some(name)
+            }
+            Choice::Packages => {
+                let pkg = u.choose(&self.packages.packages_with_worlds)?;
+                let pkg = &self.packages.list[*pkg];
+                dst.push_str("%");
+                dst.push_str(&pkg.name.namespace);
+                dst.push_str(":");
+                dst.push_str("%");
+                dst.push_str(&pkg.name.name);
+                dst.push_str("/");
+                let i = u.int_in_range(0..=pkg.file.worlds.len() - 1)?;
+                let w = &pkg.file.worlds[i];
+                dst.push_str("%");
+                dst.push_str(&w);
+                if let Some(version) = &pkg.name.version {
+                    dst.push_str(&format!("@{version}"));
+                }
+                Some(w)
+            }
+        })
+    }
 }
 
 impl<'a> InterfaceGenerator<'a> {
@@ -406,6 +514,7 @@ impl<'a> InterfaceGenerator<'a> {
             AnonInterface(Direction),
             Type,
             Use,
+            Include,
         }
 
         let mut parts = Vec::new();
@@ -423,6 +532,7 @@ impl<'a> InterfaceGenerator<'a> {
                 ItemKind::Interface(dir) => (Some(dir), false),
                 ItemKind::Type => (None, true),
                 ItemKind::Use => (None, false),
+                ItemKind::Include => (None, false),
             };
 
             let mut part = String::new();
@@ -454,7 +564,7 @@ impl<'a> InterfaceGenerator<'a> {
                     self.gen_func_sig(u, &mut part, false)?;
                 }
                 ItemKind::Interface(dir) => {
-                    let id = match self.generator.gen_path(u, self.file, &mut part)? {
+                    let id = match self.generator.gen_interface_path(u, self.file, &mut part)? {
                         Some((_name, id, _types)) => id,
                         // If an interface couldn't be chosen or wasn't
                         // chosen then skip this import. A unique name was
@@ -506,6 +616,20 @@ impl<'a> InterfaceGenerator<'a> {
                         continue;
                     }
                 }
+
+                ItemKind::Include => {
+                    part.push_str("include ");
+                    if self
+                        .generator
+                        .gen_world_path(u, self.file, &mut part)?
+                        .is_none()
+                    {
+                        // If an interface couldn't be chosen or wasn't
+                        // chosen then skip this include.
+                        continue;
+                    }
+                    part.push_str(";");
+                }
             }
             parts.push(part);
         }
@@ -578,10 +702,11 @@ impl<'a> InterfaceGenerator<'a> {
 
     fn gen_use(&mut self, u: &mut Unstructured<'_>, part: &mut String) -> Result<bool> {
         let mut path = String::new();
-        let (_name, _id, types) = match self.generator.gen_path(u, self.file, &mut path)? {
-            Some(types) => types,
-            None => return Ok(false),
-        };
+        let (_name, _id, types) =
+            match self.generator.gen_interface_path(u, self.file, &mut path)? {
+                Some(types) => types,
+                None => return Ok(false),
+            };
         part.push_str("use ");
         part.push_str(&path);
         part.push_str(".{");
@@ -722,6 +847,7 @@ impl<'a> InterfaceGenerator<'a> {
             Option,
             Result,
             List,
+            FixedSizeList,
             Stream,
             Future,
             ErrorContext,
@@ -803,6 +929,16 @@ impl<'a> InterfaceGenerator<'a> {
                     self.gen_type(u, fuel, dst)?;
                     dst.push_str(">");
                 }
+                Kind::FixedSizeList => {
+                    *fuel = match fuel.checked_sub(1) {
+                        Some(fuel) => fuel,
+                        None => continue,
+                    };
+                    let elements = u.int_in_range(1..=self.config.max_type_parts as u32)?;
+                    dst.push_str("list<");
+                    self.gen_type(u, fuel, dst)?;
+                    dst.push_str(&format!(", {elements}>"));
+                }
                 Kind::Result => {
                     *fuel = match fuel.checked_sub(2) {
                         Some(fuel) => fuel,
@@ -963,31 +1099,39 @@ fn shuffle<T>(u: &mut Unstructured<'_>, mut slice: &mut [T]) -> Result<()> {
     Ok(())
 }
 
-#[derive(Default)]
+#[derive(Clone, Default)]
 struct File {
     items: Vec<String>,
-    namespace: HashMap<String, Definition>,
+    namespace: HashMap<String, (DefinitionLevel, DefinitionKind)>,
     interfaces: IndexMap<String, FileInterface>,
+    worlds: IndexSet<String>,
 }
 
 #[derive(Clone)]
 struct FileInterface {
     name: String,
     id: u32,
-    types: Rc<TypeList>,
+    types: Rc<Vec<Type>>,
 }
 
-#[derive(PartialEq)]
-enum Definition {
+#[derive(Debug, Copy, Clone, PartialEq)]
+enum DefinitionLevel {
     Package,
     File,
 }
 
+#[derive(Debug, Copy, Clone, PartialEq)]
+enum DefinitionKind {
+    World,
+    Interface,
+}
+
 impl File {
     fn gen_unique_package_name(
         &mut self,
         u: &mut Unstructured<'_>,
         names: &mut HashSet<String>,
+        kind: DefinitionKind,
     ) -> Result<String> {
         let mut name = gen_name(u)?;
         loop {
@@ -998,32 +1142,36 @@ impl File {
             }
 
             // Then make sure it's file-unique too
-            if self.claim_file_name(&mut name) {
+            if self.claim_file_name(&mut name, kind) {
                 break;
             }
         }
         Ok(name)
     }
 
-    fn gen_unique_file_name(&mut self, u: &mut Unstructured<'_>) -> Result<String> {
+    fn gen_unique_file_name(
+        &mut self,
+        u: &mut Unstructured<'_>,
+        kind: DefinitionKind,
+    ) -> Result<String> {
         let mut name = gen_name(u)?;
-        while !self.claim_file_name(&mut name) {
+        while !self.claim_file_name(&mut name, kind) {
             // try again on the next iteration
         }
         Ok(name)
     }
 
-    fn claim_file_name(&mut self, name: &mut String) -> bool {
+    fn claim_file_name(&mut self, name: &mut String, kind: DefinitionKind) -> bool {
         match self.namespace.entry(name.clone()) {
-            Entry::Occupied(mut e) => match e.get() {
+            Entry::Occupied(mut e) => match e.get().0 {
                 // If this name is already claimed elsewhere in the package
                 // then that's ok as we're going to shadow it, so switch it
                 // to a file definition.
-                Definition::Package => *e.get_mut() = Definition::File,
+                DefinitionLevel::Package => *e.get_mut() = (DefinitionLevel::File, kind),
 
                 // If it's already defined in the file try to add more stuff
                 // to the name to make the next try not collide.
-                Definition::File => {
+                DefinitionLevel::File => {
                     name.push_str("y");
                     write!(name, "{}", self.namespace.len()).unwrap();
                     return false;
@@ -1032,22 +1180,26 @@ impl File {
 
             // Not defined? Claim it.
             Entry::Vacant(v) => {
-                v.insert(Definition::File);
+                v.insert((DefinitionLevel::File, kind));
             }
         }
         true
     }
 
-    fn insert_definition(&mut self, def: FileInterface) {
-        match self.namespace.get(&def.name) {
-            Some(Definition::File) => return,
-            Some(Definition::Package) => unreachable!(),
+    fn insert_definition(&mut self, name: &str, kind: DefinitionKind) -> bool {
+        match self.namespace.get(name) {
+            // This name is already defined, so it can't be inserted.
+            Some((DefinitionLevel::File, _)) => return false,
+            Some(other) => {
+                panic!("found duplicate definition when should be package-unique: {other:?}")
+            }
             None => {}
         }
-        let prev = self.namespace.insert(def.name.clone(), Definition::Package);
-        assert!(prev.is_none());
-        let prev = self.interfaces.insert(def.name.clone(), def);
+        let prev = self
+            .namespace
+            .insert(name.to_string(), (DefinitionLevel::Package, kind));
         assert!(prev.is_none());
+        true
     }
 }
 
diff --git a/crates/wit-smith/src/lib.rs b/crates/wit-smith/src/lib.rs
index 73e6df68..b29ab4b2 100644
--- a/crates/wit-smith/src/lib.rs
+++ b/crates/wit-smith/src/lib.rs
@@ -28,6 +28,9 @@ pub fn smith(config: &Config, u: &mut Unstructured<'_>) -> Result<Vec<u8>> {
                 if e.is::<InvalidTransitiveDependency>() {
                     return Err(arbitrary::Error::IncorrectFormat);
                 }
+                if e.to_string().contains("shadows previously") {
+                    return Err(arbitrary::Error::IncorrectFormat);
+                }
                 panic!("bad wit parse: {e:?}")
             }
         };
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index 658c2015..f5152552 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -14,7 +14,6 @@ workspace = true
 anyhow = { workspace = true }
 arbitrary = { workspace = true }
 env_logger = { workspace = true }
-libfuzzer-sys = { workspace = true }
 log = { workspace = true }
 tempfile = "3.0"
 wasm-encoder = { workspace = true }
@@ -37,8 +36,13 @@ version = '0.214.0'
 package = 'wit-component'
 version = '0.214.0'
 
+[target.'cfg(fuzzing)'.dependencies]
+libfuzzer-sys = { workspace = true }
+
+[dev-dependencies]
+rand = { workspace = true }
+
 [lib]
-test = false
 doctest = false
 
 [[bin]]
diff --git a/fuzz/fuzz_targets/run.rs b/fuzz/fuzz_targets/run.rs
index 45275329..b9e08a8b 100644
--- a/fuzz/fuzz_targets/run.rs
+++ b/fuzz/fuzz_targets/run.rs
@@ -1,9 +1,19 @@
-#![no_main]
+#![cfg_attr(fuzzing, no_main)]
 
 use arbitrary::Unstructured;
-use libfuzzer_sys::fuzz_target;
 use std::sync::OnceLock;
 
+#[cfg(fuzzing)]
+use libfuzzer_sys::fuzz_target;
+#[cfg(not(fuzzing))]
+macro_rules! fuzz_target {
+    ($e:expr) => {
+        fn main() {
+            let _ = $e(&[]);
+        }
+    };
+}
+
 // Helper macro which takes a static list of fuzzers as input which are then
 // delegated to internally based on the fuzz target selected.
 //
diff --git a/fuzz/src/incremental_parse.rs b/fuzz/src/incremental_parse.rs
index 42fc2461..5817cf0f 100644
--- a/fuzz/src/incremental_parse.rs
+++ b/fuzz/src/incremental_parse.rs
@@ -217,3 +217,8 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
     }
     Ok(())
 }
+
+#[test]
+fn smoke() {
+    super::test::test_n_times(100, run);
+}
diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs
index ff0af0a2..6f34f68b 100644
--- a/fuzz/src/lib.rs
+++ b/fuzz/src/lib.rs
@@ -1,4 +1,4 @@
-use libfuzzer_sys::arbitrary::{Result, Unstructured};
+use arbitrary::{Result, Unstructured};
 use std::fmt::Debug;
 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
 use wasm_smith::{Component, Config, Module};
@@ -105,3 +105,39 @@ pub fn log_wasm(wasm: &[u8], config: impl Debug) {
         drop(std::fs::remove_file(&wat_file));
     }
 }
+#[cfg(test)]
+mod test {
+    use arbitrary::{Result, Unstructured};
+    use rand::{rngs::SmallRng, RngCore, SeedableRng};
+
+    fn gen_until_pass(mut f: impl FnMut(&mut Unstructured<'_>) -> Result<bool>) -> bool {
+        let mut rng = SmallRng::seed_from_u64(0);
+        let mut buf = vec![0; 2048];
+        let n = 3000;
+        for _ in 0..n {
+            rng.fill_bytes(&mut buf);
+            let mut u = Unstructured::new(&buf);
+
+            if f(&mut u).unwrap() {
+                return true;
+            }
+        }
+        false
+    }
+
+    /// Runs `f` with random data until it returns `Ok(())` `iters` times.
+    pub fn test_n_times(
+        iters: u32,
+        mut f: impl FnMut(&mut Unstructured<'_>) -> arbitrary::Result<()>,
+    ) {
+        let mut to_test = 0..iters;
+        let ok = gen_until_pass(|b| {
+            if f(b).is_ok() {
+                Ok(to_test.next().is_none())
+            } else {
+                Ok(false)
+            }
+        });
+        assert!(ok);
+    }
+}
diff --git a/fuzz/src/mutate.rs b/fuzz/src/mutate.rs
index 88d37902..c0c295b4 100644
--- a/fuzz/src/mutate.rs
+++ b/fuzz/src/mutate.rs
@@ -329,3 +329,8 @@ mod eval {
         }
     }
 }
+
+#[test]
+fn smoke() {
+    super::test::test_n_times(100, run);
+}
diff --git a/fuzz/src/no_traps.rs b/fuzz/src/no_traps.rs
index b3d7d81a..9f6f5df4 100644
--- a/fuzz/src/no_traps.rs
+++ b/fuzz/src/no_traps.rs
@@ -122,3 +122,8 @@ fn validate_module(wasm_bytes: &Vec<u8>) {
         panic!("Invalid module: {e}");
     }
 }
+
+#[test]
+fn smoke() {
+    super::test::test_n_times(100, run);
+}
diff --git a/fuzz/src/print.rs b/fuzz/src/print.rs
index 1e549f32..6723d4c6 100644
--- a/fuzz/src/print.rs
+++ b/fuzz/src/print.rs
@@ -13,3 +13,8 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
     let _ = cfg.print(&data, &mut wasmprinter::PrintFmtWrite(&mut dst));
     Ok(())
 }
+
+#[test]
+fn smoke() {
+    super::test::test_n_times(100, run);
+}
diff --git a/fuzz/src/reencode.rs b/fuzz/src/reencode.rs
index 7e27dbb3..692ef23f 100644
--- a/fuzz/src/reencode.rs
+++ b/fuzz/src/reencode.rs
@@ -19,3 +19,8 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
 
     Ok(())
 }
+
+#[test]
+fn smoke() {
+    super::test::test_n_times(100, run);
+}
diff --git a/fuzz/src/roundtrip_wit.rs b/fuzz/src/roundtrip_wit.rs
index 6f5e3c41..b1eb5ba8 100644
--- a/fuzz/src/roundtrip_wit.rs
+++ b/fuzz/src/roundtrip_wit.rs
@@ -188,3 +188,8 @@ impl Reencode for RemoveImports<'_, '_> {
         }
     }
 }
+
+#[test]
+fn smoke() {
+    super::test::test_n_times(100, run);
+}
diff --git a/fuzz/src/validate.rs b/fuzz/src/validate.rs
index ff14c187..2b87023b 100644
--- a/fuzz/src/validate.rs
+++ b/fuzz/src/validate.rs
@@ -109,3 +109,8 @@ fn validate_all(u: &mut Unstructured<'_>, mut validator: Validator, wasm: &[u8])
 
     Ok(())
 }
+
+#[test]
+fn smoke() {
+    super::test::test_n_times(100, run);
+}
diff --git a/fuzz/src/validate_valid_module.rs b/fuzz/src/validate_valid_module.rs
index ff23e336..dc471673 100644
--- a/fuzz/src/validate_valid_module.rs
+++ b/fuzz/src/validate_valid_module.rs
@@ -66,3 +66,8 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
     }
     Ok(())
 }
+
+#[test]
+fn smoke() {
+    super::test::test_n_times(100, run);
+}
diff --git a/fuzz/src/wit64.rs b/fuzz/src/wit64.rs
index 6060b754..cbaa7012 100644
--- a/fuzz/src/wit64.rs
+++ b/fuzz/src/wit64.rs
@@ -118,3 +118,8 @@ fn write_file(path: &str, contents: impl AsRef<[u8]>) {
         std::fs::write(path, wasmprinter::print_bytes(&contents).unwrap()).unwrap();
     }
 }
+
+#[test]
+fn smoke() {
+    super::test::test_n_times(100, run);
+}
diff --git a/src/bin/wasm-tools/component.rs b/src/bin/wasm-tools/component.rs
index 2d2e4375..4c9e8310 100644
--- a/src/bin/wasm-tools/component.rs
+++ b/src/bin/wasm-tools/component.rs
@@ -10,7 +10,7 @@ use wasm_encoder::reencode::{Error, Reencode, ReencodeComponent, RoundtripReenco
 use wasm_encoder::ModuleType;
 use wasm_tools::Output;
 use wasmparser::types::{CoreTypeId, EntityType, Types};
-use wasmparser::{Payload, ValidPayload};
+use wasmparser::{Payload, ValidPayload, WasmFeatures};
 use wat::Detect;
 use wit_component::{
     embed_component_metadata, metadata, ComponentEncoder, DecodedWasm, Linker, StringEncoding,
@@ -831,7 +831,7 @@ impl WitOpts {
         let decoded_package = decoded.package();
         let bytes = wit_component::encode(decoded.resolve(), decoded_package)?;
         if !self.skip_validation {
-            wasmparser::Validator::new().validate_all(&bytes)?;
+            wasmparser::Validator::new_with_features(WasmFeatures::all()).validate_all(&bytes)?;
         }
         self.output.output_wasm(&self.general, &bytes, self.wat)?;
         Ok(())


Test patch:
diff --git a/crates/wasm-smith/tests/available_imports.rs b/crates/wasm-smith/tests/available_imports.rs
index 124c9b1f..566ff377 100644
--- a/crates/wasm-smith/tests/available_imports.rs
+++ b/crates/wasm-smith/tests/available_imports.rs
@@ -1,6 +1,6 @@
 #![cfg(feature = "wasmparser")]
 
-use arbitrary::{Arbitrary, Unstructured};
+use arbitrary::Unstructured;
 use rand::{rngs::SmallRng, RngCore, SeedableRng};
 use std::collections::HashMap;
 use wasm_smith::{Config, Module};
@@ -38,8 +38,16 @@ fn smoke_test_imports_config() {
                 if let wasmparser::Payload::TypeSection(rdr) = payload {
                     // Gather the signature types to later check function types
                     // against.
-                    for ty in rdr.into_iter_err_on_gc_types() {
-                        sig_types.push(ty.unwrap());
+                    for recgrp in rdr {
+                        let recgrp = recgrp.unwrap();
+                        for subtype in recgrp.into_types() {
+                            match subtype.composite_type.inner {
+                                wasmparser::CompositeInnerType::Func(func_type) => {
+                                    sig_types.push(Some(func_type))
+                                }
+                                _ => sig_types.push(None),
+                            }
+                        }
                     }
                 } else if let wasmparser::Payload::ImportSection(rdr) = payload {
                     // Read out imports, checking that they all are within the
@@ -64,16 +72,22 @@ fn smoke_test_imports_config() {
                                 *seen = true
                             }
                             (Some((seen, I::Func(p, r))), TypeRef::Func(sig_idx))
-                                if sig_types[*sig_idx as usize].params() == *p
-                                    && sig_types[*sig_idx as usize].results() == *r =>
+                                if sig_types[*sig_idx as usize].clone().unwrap().params() == *p
+                                    && sig_types[*sig_idx as usize].clone().unwrap().results()
+                                        == *r =>
                             {
                                 *seen = true
                             }
                             (
                                 Some((seen, I::Tag(p))),
                                 TypeRef::Tag(wasmparser::TagType { func_type_idx, .. }),
-                            ) if sig_types[*func_type_idx as usize].params() == *p
-                                && sig_types[*func_type_idx as usize].results().is_empty() =>
+                            ) if sig_types[*func_type_idx as usize].clone().unwrap().params()
+                                == *p
+                                && sig_types[*func_type_idx as usize]
+                                    .clone()
+                                    .unwrap()
+                                    .results()
+                                    .is_empty() =>
                             {
                                 *seen = true
                             }
@@ -108,8 +122,8 @@ fn smoke_test_imports_config() {
 
 #[derive(Debug)]
 enum AvailableImportKind {
-    Func(&'static [ValType], &'static [ValType]),
-    Tag(&'static [ValType]),
+    Func(Vec<ValType>, Vec<ValType>),
+    Tag(Vec<ValType>),
     Global(ValType),
     Table(ValType),
     Memory,
@@ -121,28 +135,43 @@ fn import_config(
     Config,
     Vec<(&'static str, &'static str, AvailableImportKind)>,
 ) {
-    let mut config = Config::arbitrary(u).expect("arbitrary swarm");
-    config.gc_enabled = false;
+    let mut config = Config::default();
     config.exceptions_enabled = u.arbitrary().expect("exceptions enabled for swarm");
     let available = {
-        use {AvailableImportKind::*, ValType::*};
+        use {
+            wasmparser::{PackedIndex, RefType},
+            AvailableImportKind::*,
+            ValType::*,
+        };
         vec![
-            ("env", "pi", Func(&[I32], &[])),
-            ("env", "pi2", Func(&[I32], &[])),
-            ("env", "pipi2", Func(&[I32, I32], &[])),
-            ("env", "po", Func(&[], &[I32])),
-            ("env", "pipo", Func(&[I32], &[I32])),
-            ("env", "popo", Func(&[], &[I32, I32])),
+            ("env", "pi", Func(vec![I32], vec![])),
+            ("env", "pi2", Func(vec![I32], vec![])),
+            ("env", "pipi2", Func(vec![I32, I32], vec![])),
+            ("env", "po", Func(vec![], vec![I32])),
+            ("env", "pipo", Func(vec![I32], vec![I32])),
+            ("env", "popo", Func(vec![], vec![I32, I32])),
             ("env", "mem", Memory),
             ("env", "tbl", Table(ValType::FUNCREF)),
             ("vars", "g", Global(I64)),
-            ("tags", "tag1", Tag(&[I32])),
+            ("tags", "tag1", Tag(vec![I32])),
+            (
+                "tags",
+                "tag2",
+                Tag(vec![Ref(RefType::concrete(
+                    true,
+                    PackedIndex::from_module_index(0).unwrap(),
+                ))]),
+            ),
         ]
     };
     config.available_imports = Some(
         wat::parse_str(
             r#"
             (module
+                (rec
+                    (type $s0 (array (ref $s1)))
+                    (type $s1 (array (ref $s0)))
+                )
                 (import "env" "pi" (func (param i32)))
                 (import "env" "pi2" (func (param i32)))
                 (import "env" "pipi2" (func (param i32 i32)))
@@ -153,6 +182,7 @@ fn import_config(
                 (import "env" "tbl" (table 1 16 funcref))
                 (import "vars" "g" (global i64))
                 (import "tags" "tag1" (tag (param i32)))
+                (import "tags" "tag2" (tag (param (ref null $s0))))
             )
             "#,
         )
diff --git a/crates/wasm-smith/tests/exports.rs b/crates/wasm-smith/tests/exports.rs
index 639b9ead..e98b22a8 100644
--- a/crates/wasm-smith/tests/exports.rs
+++ b/crates/wasm-smith/tests/exports.rs
@@ -1,10 +1,11 @@
 #![cfg(feature = "wasmparser")]
 
-use arbitrary::{Arbitrary, Unstructured};
+use arbitrary::Unstructured;
 use rand::{rngs::SmallRng, RngCore, SeedableRng};
 use wasm_smith::{Config, Module};
 use wasmparser::{
-    types::EntityType, CompositeType, FuncType, GlobalType, Parser, Validator, WasmFeatures,
+    types::EntityType, CompositeType, FuncType, GlobalType, MemoryType, Parser, TableType,
+    Validator, WasmFeatures,
 };
 
 mod common;
@@ -14,6 +15,9 @@ use common::validate;
 enum ExportType {
     Func(FuncType),
     Global(GlobalType),
+    Memory(MemoryType),
+    Table(TableType),
+    Tag(FuncType),
 }
 
 #[test]
@@ -65,14 +69,26 @@ fn smoke_test_export_with_imports() {
         (module
             (import "" "foo" (func (param i32)))
             (import "" "bar" (global (mut f32)))
+            (import "" "mem" (memory 5))
+            (import "" "baz" (table 1 10 funcref))
+            (import "" "qux" (tag (param i32 f64)))
             (func (param i64) unreachable)
             (global i32 (i32.const 0))
+            (memory 10)
+            (table 10 (ref null any))
+            (tag (param f32 v128))
             (export "a" (func 0))
             (export "b" (global 0))
-            (export "c" (func 1))
-            (export "d" (global 1))
+            (export "c" (memory 0))
+            (export "d" (table 0))
+            (export "e" (tag 0))
+            (export "f" (func 1))
+            (export "g" (global 1))
+            (export "h" (memory 1))
+            (export "i" (table 1))
+            (export "j" (tag 1))
         )
-            "#;
+        "#;
     smoke_test_exports(test, 21)
 }
 
@@ -92,12 +108,25 @@ fn smoke_test_with_mutable_global_exports() {
     smoke_test_exports(test, 22)
 }
 
-fn get_func_and_global_exports(features: WasmFeatures, module: &[u8]) -> Vec<(String, ExportType)> {
+#[test]
+fn smoke_test_all_exports() {
+    let test = r#"
+        (module
+            (func (export "foo") (param i32) (result i64) unreachable)
+            (global (export "bar") f32 f32.const 0)
+            (memory (export "baz") 1 10)
+            (table (export "qux") 5 10 (ref null extern))
+            (tag (export "quux") (param f32))
+        )
+        "#;
+    smoke_test_exports(test, 23);
+}
+
+fn get_exports(features: WasmFeatures, module: &[u8]) -> Vec<(String, ExportType)> {
     let mut validator = Validator::new_with_features(features);
     let types = validate(&mut validator, module);
     let types = types.as_ref();
     let mut exports = vec![];
-
     for payload in Parser::new(0).parse_all(module) {
         let payload = payload.unwrap();
         if let wasmparser::Payload::ExportSection(rdr) = payload {
@@ -119,10 +148,27 @@ fn get_func_and_global_exports(features: WasmFeatures, module: &[u8]) -> Vec<(St
                             .push((export.name.to_string(), ExportType::Func(func_type.clone())));
                     }
                     EntityType::Global(global_type) => {
-                        exports.push((export.name.to_string(), ExportType::Global(global_type)))
+                        exports.push((export.name.to_string(), ExportType::Global(global_type)));
+                    }
+                    EntityType::Memory(memory_type) => {
+                        exports.push((export.name.to_string(), ExportType::Memory(memory_type)));
+                    }
+                    EntityType::Table(table_type) => {
+                        exports.push((export.name.to_string(), ExportType::Table(table_type)));
                     }
-                    other => {
-                        panic!("Unexpected entity type {other:?}")
+                    EntityType::Tag(core_id) => {
+                        let sub_type = types.get(core_id).expect("Failed to lookup core id");
+                        assert!(sub_type.is_final);
+                        assert!(sub_type.supertype_idx.is_none());
+                        let CompositeType {
+                            inner: wasmparser::CompositeInnerType::Func(func_type),
+                            ..
+                        } = &sub_type.composite_type
+                        else {
+                            panic!("Expected Func CompositeType, but found {sub_type:?}");
+                        };
+                        assert!(func_type.results().is_empty());
+                        exports.push((export.name.to_string(), ExportType::Tag(func_type.clone())));
                     }
                 }
             }
@@ -135,20 +181,22 @@ fn smoke_test_exports(exports_test_case: &str, seed: u64) {
     let mut rng = SmallRng::seed_from_u64(seed);
     let mut buf = vec![0; 512];
     let wasm = wat::parse_str(exports_test_case).unwrap();
-    let expected_exports = get_func_and_global_exports(WasmFeatures::default(), &wasm);
+    let expected_exports = get_exports(WasmFeatures::default(), &wasm);
 
     for _ in 0..1024 {
         rng.fill_bytes(&mut buf);
         let mut u = Unstructured::new(&buf);
 
-        let mut config = Config::arbitrary(&mut u).expect("arbitrary config");
+        // Enable all standardized proposals.
+        let mut config = Config::default();
+        config.max_memories = u.int_in_range(2..=5).unwrap();
         config.exports = Some(wasm.clone());
 
         let features = config.features();
         let module = Module::new(config, &mut u).unwrap();
         let wasm_bytes = module.to_bytes();
 
-        let generated_exports = get_func_and_global_exports(features, &wasm_bytes);
+        let generated_exports = get_exports(features, &wasm_bytes);
         assert_eq!(expected_exports, generated_exports);
     }
 }
diff --git a/crates/wit-component/tests/components/async-builtins/component.wat b/crates/wit-component/tests/components/async-builtins/component.wat
index ff4caf87..e84346ac 100644
--- a/crates/wit-component/tests/components/async-builtins/component.wat
+++ b/crates/wit-component/tests/components/async-builtins/component.wat
@@ -16,7 +16,7 @@
     (import "$root" "[waitable-set-poll]" (func (;6;) (type 4)))
     (import "$root" "[waitable-set-drop]" (func (;7;) (type 0)))
     (import "$root" "[waitable-join]" (func (;8;) (type 2)))
-    (import "$root" "[yield]" (func (;9;) (type 1)))
+    (import "$root" "[yield]" (func (;9;) (type 3)))
     (import "$root" "[subtask-drop]" (func (;10;) (type 0)))
     (import "$root" "[subtask-cancel]" (func (;11;) (type 5)))
     (import "$root" "[error-context-new-utf8]" (func (;12;) (type 4)))
diff --git a/crates/wit-component/tests/components/async-builtins/module.wat b/crates/wit-component/tests/components/async-builtins/module.wat
index ba3fadc7..adb7a582 100644
--- a/crates/wit-component/tests/components/async-builtins/module.wat
+++ b/crates/wit-component/tests/components/async-builtins/module.wat
@@ -8,7 +8,7 @@
   (import "$root" "[waitable-set-poll]" (func (param i32 i32) (result i32)))
   (import "$root" "[waitable-set-drop]" (func (param i32)))
   (import "$root" "[waitable-join]" (func (param i32 i32)))
-  (import "$root" "[yield]" (func))
+  (import "$root" "[yield]" (func (result i32)))
   (import "$root" "[subtask-drop]" (func (param i32)))
   (import "$root" "[subtask-cancel]" (func (param i32) (result i32)))
   (import "$root" "[error-context-new-utf8]" (func (param i32 i32) (result i32)))
diff --git a/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print b/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print
index 3ef196c3..7f31dbf9 100644
--- a/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print
+++ b/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print
@@ -60,11 +60,10 @@ interface other-comment-forms {
 
 /// world docs
 world coverage-world {
-  /// world func import docs
-  import imp: func();
-
   /// world typedef docs
   type t = u32;
+  /// world func import docs
+  import imp: func();
 
   /// world func export docs
   export exp: func();
diff --git a/crates/wit-component/tests/interfaces/resources.wat b/crates/wit-component/tests/interfaces/resources.wat
index 3d07b806..c4a8a9d3 100644
--- a/crates/wit-component/tests/interfaces/resources.wat
+++ b/crates/wit-component/tests/interfaces/resources.wat
@@ -100,6 +100,20 @@
       (type (;0;)
         (component
           (type (;0;)
+            (instance
+              (export (;0;) "a" (type (sub resource)))
+              (type (;1;) (own 0))
+              (type (;2;) (func (result 1)))
+              (export (;0;) "[constructor]a" (func (type 2)))
+              (type (;3;) (func))
+              (export (;1;) "[static]a.b" (func (type 3)))
+              (type (;4;) (borrow 0))
+              (type (;5;) (func (param "self" 4)))
+              (export (;2;) "[method]a.c" (func (type 5)))
+            )
+          )
+          (import "anon" (instance (;0;) (type 0)))
+          (type (;1;)
             (instance
               (export (;0;) "bar" (type (sub resource)))
               (export (;1;) "t" (type (eq 0)))
@@ -117,14 +131,14 @@
               (export (;3;) "a" (func (type 9)))
             )
           )
-          (import "foo:bar/foo" (instance (;0;) (type 0)))
-          (alias export 0 "bar" (type (;1;)))
-          (alias export 0 "t" (type (;2;)))
-          (type (;3;)
+          (import "foo:bar/foo" (instance (;1;) (type 1)))
+          (alias export 1 "bar" (type (;2;)))
+          (alias export 1 "t" (type (;3;)))
+          (type (;4;)
             (instance
-              (alias outer 1 1 (type (;0;)))
+              (alias outer 1 2 (type (;0;)))
               (export (;1;) "bar" (type (eq 0)))
-              (alias outer 1 2 (type (;2;)))
+              (alias outer 1 3 (type (;2;)))
               (export (;3;) "t" (type (eq 2)))
               (type (;4;) (own 1))
               (type (;5;) (func (param "x" 4) (result 4)))
@@ -134,29 +148,15 @@
               (export (;1;) "b" (func (type 7)))
             )
           )
-          (import "foo:bar/baz" (instance (;1;) (type 3)))
-          (alias export 1 "bar" (type (;4;)))
-          (import "bar" (type (;5;) (eq 4)))
-          (import "a" (type (;6;) (sub resource)))
-          (type (;7;)
-            (instance
-              (export (;0;) "a" (type (sub resource)))
-              (type (;1;) (own 0))
-              (type (;2;) (func (result 1)))
-              (export (;0;) "[constructor]a" (func (type 2)))
-              (type (;3;) (func))
-              (export (;1;) "[static]a.b" (func (type 3)))
-              (type (;4;) (borrow 0))
-              (type (;5;) (func (param "self" 4)))
-              (export (;2;) "[method]a.c" (func (type 5)))
-            )
-          )
-          (import "anon" (instance (;2;) (type 7)))
-          (type (;8;) (own 6))
+          (import "foo:bar/baz" (instance (;2;) (type 4)))
+          (alias export 2 "bar" (type (;5;)))
+          (import "bar" (type (;6;) (eq 5)))
+          (import "a" (type (;7;) (sub resource)))
+          (type (;8;) (own 7))
           (type (;9;) (func (result 8)))
           (import "[constructor]a" (func (;0;) (type 9)))
           (export (;1;) "x" (func (type 9)))
-          (type (;10;) (own 5))
+          (type (;10;) (own 6))
           (type (;11;) (func (result 10)))
           (export (;2;) "y" (func (type 11)))
         )
diff --git a/crates/wit-component/tests/interfaces/resources.wit.print b/crates/wit-component/tests/interfaces/resources.wit.print
index 6f386b6d..c52d6744 100644
--- a/crates/wit-component/tests/interfaces/resources.wit.print
+++ b/crates/wit-component/tests/interfaces/resources.wit.print
@@ -50,8 +50,6 @@ interface implicit-own-handles2 {
 }
 
 world some-world {
-  import foo;
-  import baz;
   import anon: interface {
     resource a {
       constructor();
@@ -59,6 +57,8 @@ world some-world {
       c: func();
     }
   }
+  import foo;
+  import baz;
   use baz.{bar};
 
   resource a {
diff --git a/crates/wit-component/tests/interfaces/world-top-level.wat b/crates/wit-component/tests/interfaces/world-top-level.wat
index 347066ec..4afcebfc 100644
--- a/crates/wit-component/tests/interfaces/world-top-level.wat
+++ b/crates/wit-component/tests/interfaces/world-top-level.wat
@@ -3,15 +3,15 @@
     (component
       (type (;0;)
         (component
-          (type (;0;) (func))
-          (import "foo" (func (;0;) (type 0)))
-          (type (;1;) (func (param "arg" u32)))
-          (import "bar" (func (;1;) (type 1)))
-          (type (;2;)
+          (type (;0;)
             (instance)
           )
-          (import "some-interface" (instance (;0;) (type 2)))
-          (export (;2;) "foo2" (func (type 0)))
+          (import "some-interface" (instance (;0;) (type 0)))
+          (type (;1;) (func))
+          (import "foo" (func (;0;) (type 1)))
+          (type (;2;) (func (param "arg" u32)))
+          (import "bar" (func (;1;) (type 2)))
+          (export (;2;) "foo2" (func (type 1)))
           (type (;3;) (func (result u32)))
           (export (;3;) "bar2" (func (type 3)))
           (type (;4;)
diff --git a/crates/wit-component/tests/interfaces/world-top-level.wit.print b/crates/wit-component/tests/interfaces/world-top-level.wit.print
index 5308f654..b28325ef 100644
--- a/crates/wit-component/tests/interfaces/world-top-level.wit.print
+++ b/crates/wit-component/tests/interfaces/world-top-level.wit.print
@@ -1,10 +1,10 @@
 package foo:foo;
 
 world foo {
-  import foo: func();
-  import bar: func(arg: u32);
   import some-interface: interface {
   }
+  import foo: func();
+  import bar: func(arg: u32);
 
   export foo2: func();
   export bar2: func() -> u32;
diff --git a/crates/wit-component/tests/interfaces/worlds-with-types.wit.print b/crates/wit-component/tests/interfaces/worlds-with-types.wit.print
index 147e1219..1c4d0e3d 100644
--- a/crates/wit-component/tests/interfaces/worlds-with-types.wit.print
+++ b/crates/wit-component/tests/interfaces/worlds-with-types.wit.print
@@ -5,20 +5,19 @@ interface import-me {
 }
 
 world simple {
-  import a: func(a: foo) -> bar;
-
   record foo {
     f: u8,
   }
 
   type bar = foo;
+  import a: func(a: foo) -> bar;
 
   export b: func(a: foo) -> bar;
 }
 world with-imports {
   import import-me;
-  import a: func(a: foo);
   use import-me.{foo};
+  import a: func(a: foo);
 
   export b: func(a: foo);
 }
diff --git a/crates/wit-parser/tests/ui/gated-include-use-with-stable.wit.json b/crates/wit-parser/tests/ui/gated-include-use-with-stable.wit.json
index 471d6b4c..b7f857b1 100644
--- a/crates/wit-parser/tests/ui/gated-include-use-with-stable.wit.json
+++ b/crates/wit-parser/tests/ui/gated-include-use-with-stable.wit.json
@@ -106,9 +106,6 @@
             }
           }
         },
-        "stable-resource": {
-          "type": 3
-        },
         "interface-1": {
           "interface": {
             "id": 1,
@@ -119,9 +116,6 @@
             }
           }
         },
-        "unversioned-resource": {
-          "type": 4
-        },
         "interface-0": {
           "interface": {
             "id": 0,
@@ -132,6 +126,12 @@
             }
           }
         },
+        "stable-resource": {
+          "type": 3
+        },
+        "unversioned-resource": {
+          "type": 4
+        },
         "unstable-resource": {
           "type": 5
         }
@@ -157,9 +157,6 @@
             }
           }
         },
-        "stable-resource": {
-          "type": 3
-        },
         "interface-1": {
           "interface": {
             "id": 1,
@@ -170,9 +167,6 @@
             }
           }
         },
-        "unversioned-resource": {
-          "type": 4
-        },
         "interface-0": {
           "interface": {
             "id": 0,
@@ -183,6 +177,12 @@
             }
           }
         },
+        "stable-resource": {
+          "type": 3
+        },
+        "unversioned-resource": {
+          "type": 4
+        },
         "unstable-resource": {
           "type": 5
         }
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list2.wit b/crates/wit-parser/tests/ui/parse-fail/bad-list2.wit
new file mode 100644
index 00000000..4313c549
--- /dev/null
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-list2.wit
@@ -0,0 +1,7 @@
+// parse-fail
+
+interface foo {
+  type z = list<u32,
+
+  type y = u32
+}
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list2.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-list2.wit.result
new file mode 100644
index 00000000..94b02e2a
--- /dev/null
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-list2.wit.result
@@ -0,0 +1,5 @@
+expected fixed size, found keyword `type`
+     --> tests/ui/parse-fail/bad-list2.wit:6:3
+      |
+    6 |   type y = u32
+      |   ^---
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit b/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit
new file mode 100644
index 00000000..1f0238ea
--- /dev/null
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit
@@ -0,0 +1,7 @@
+// parse-fail
+
+interface foo {
+  type a = list<u32,>
+
+  type y = u32
+}
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit.result
new file mode 100644
index 00000000..4f48238f
--- /dev/null
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit.result
@@ -0,0 +1,5 @@
+expected fixed size, found '>'
+     --> tests/ui/parse-fail/bad-list3.wit:4:21
+      |
+    4 |   type a = list<u32,>
+      |                     ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit b/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit
new file mode 100644
index 00000000..14fa344d
--- /dev/null
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit
@@ -0,0 +1,7 @@
+// parse-fail
+
+interface foo {
+  type b = list<u32, u32>
+
+  type y = u32
+}
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit.result
new file mode 100644
index 00000000..c2aae207
--- /dev/null
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit.result
@@ -0,0 +1,5 @@
+expected fixed size, found keyword `u32`
+     --> tests/ui/parse-fail/bad-list4.wit:4:22
+      |
+    4 |   type b = list<u32, u32>
+      |                      ^--
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/since-and-unstable.wit.json b/crates/wit-parser/tests/ui/since-and-unstable.wit.json
index ad483746..851694ac 100644
--- a/crates/wit-parser/tests/ui/since-and-unstable.wit.json
+++ b/crates/wit-parser/tests/ui/since-and-unstable.wit.json
@@ -25,6 +25,26 @@
     {
       "name": "in-a-world",
       "imports": {
+        "y": {
+          "interface": {
+            "id": 6,
+            "stability": {
+              "stable": {
+                "since": "1.0.0"
+              }
+            }
+          }
+        },
+        "interface-4": {
+          "interface": {
+            "id": 4,
+            "stability": {
+              "stable": {
+                "since": "1.0.0"
+              }
+            }
+          }
+        },
         "t1": {
           "type": 11
         },
@@ -58,26 +78,6 @@
             }
           }
         },
-        "y": {
-          "interface": {
-            "id": 6,
-            "stability": {
-              "stable": {
-                "since": "1.0.0"
-              }
-            }
-          }
-        },
-        "interface-4": {
-          "interface": {
-            "id": 4,
-            "stability": {
-              "stable": {
-                "since": "1.0.0"
-              }
-            }
-          }
-        },
         "[constructor]t7": {
           "function": {
             "name": "[constructor]t7",
diff --git a/crates/wit-parser/tests/ui/types.wit b/crates/wit-parser/tests/ui/types.wit
index d6793979..5a644f30 100644
--- a/crates/wit-parser/tests/ui/types.wit
+++ b/crates/wit-parser/tests/ui/types.wit
@@ -50,6 +50,8 @@ interface types {
   type t48 = stream<u32>;
   type t49 = future<u32>;
   type t50 = future;
+  type t51a = list<u32, 4>;
+  type t51b = list<list<u32, 4>, 2>;
 
   /// type order doesn't matter
   type foo = bar;
diff --git a/crates/wit-parser/tests/ui/types.wit.json b/crates/wit-parser/tests/ui/types.wit.json
index 1b5e70e5..a8dfbc81 100644
--- a/crates/wit-parser/tests/ui/types.wit.json
+++ b/crates/wit-parser/tests/ui/types.wit.json
@@ -53,8 +53,10 @@
         "t48": 49,
         "t49": 50,
         "t50": 51,
-        "bar": 52,
-        "foo": 53
+        "t51a": 52,
+        "t51b": 54,
+        "bar": 55,
+        "foo": 56
       },
       "functions": {},
       "package": 0
@@ -698,6 +700,40 @@
         "interface": 0
       }
     },
+    {
+      "name": "t51a",
+      "kind": {
+        "fixed-size-list": [
+          "u32",
+          4
+        ]
+      },
+      "owner": {
+        "interface": 0
+      }
+    },
+    {
+      "name": null,
+      "kind": {
+        "fixed-size-list": [
+          "u32",
+          4
+        ]
+      },
+      "owner": null
+    },
+    {
+      "name": "t51b",
+      "kind": {
+        "fixed-size-list": [
+          53,
+          2
+        ]
+      },
+      "owner": {
+        "interface": 0
+      }
+    },
     {
       "name": "bar",
       "kind": {
@@ -710,7 +746,7 @@
     {
       "name": "foo",
       "kind": {
-        "type": 52
+        "type": 55
       },
       "owner": {
         "interface": 0
diff --git a/tests/cli/component-model-async/lift.wast b/tests/cli/component-model-async/lift.wast
index a37b1c8b..dbc08bed 100644
--- a/tests/cli/component-model-async/lift.wast
+++ b/tests/cli/component-model-async/lift.wast
@@ -25,7 +25,7 @@
       (canon lift (core func $i "foo") async (post-return (func $i "post-return-foo")))
     )
   )
-  "cannot specify post-return function when lifting async"
+  "cannot specify post-return function in async"
 )
 
 ;; async lift; with callback
@@ -71,7 +71,7 @@
       (canon lift (core func $i "foo") async (callback (func $i "callback")) (post-return (func $i "post-return-foo")))
     )
   )
-  "cannot specify post-return function when lifting async"
+  "cannot specify post-return function in async"
 )
 
 ;; async lift; with incorrectly-typed core function
@@ -118,5 +118,5 @@
       (canon lift (core func $i "foo") (callback (func $i "callback")))
     )
   )
-  "cannot specify callback without lifting async"
+  "cannot specify callback without async"
 )
diff --git a/tests/cli/component-model-async/task-builtins.wast b/tests/cli/component-model-async/task-builtins.wast
index f1bca5d9..9d364d5b 100644
--- a/tests/cli/component-model-async/task-builtins.wast
+++ b/tests/cli/component-model-async/task-builtins.wast
@@ -39,7 +39,7 @@
     (core func $f (canon backpressure.set))
     (core func $task-return (canon task.return (result u32) (callback $f)))
   )
-  "cannot specify `callback` option on `task.return`")
+  "cannot specify callback without async")
 
 (assert_invalid
   (component
@@ -182,7 +182,7 @@
 ;; yield
 (component
   (core module $m
-    (import "" "yield" (func $yield))
+    (import "" "yield" (func $yield (result i32)))
   )
   (core func $yield (canon yield async))
   (core instance $i (instantiate $m (with "" (instance (export "yield" (func $yield))))))
diff --git a/tests/cli/component-model-gc/core-type.wat b/tests/cli/component-model-gc/core-type.wat
new file mode 100644
index 00000000..b5e84874
--- /dev/null
+++ b/tests/cli/component-model-gc/core-type.wat
@@ -0,0 +1,41 @@
+;; 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))
+    )
+  )
+  "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"
+)
diff --git a/tests/cli/component-model/fixed-size-list.wast b/tests/cli/component-model/fixed-size-list.wast
new file mode 100644
index 00000000..63b8943f
--- /dev/null
+++ b/tests/cli/component-model/fixed-size-list.wast
@@ -0,0 +1,79 @@
+;; RUN: wast % --assert default --snapshot tests/snapshots -f cm-fixed-size-list
+
+(component
+  (core module $m
+    (memory (export "memory") 1)
+    (func (export "ret-list") (result i32) unreachable)
+  )
+  (core instance $i (instantiate $m))
+
+  (func (export "ret-list") (result (list u32 4))
+    (canon lift (core func $i "ret-list") (memory $i "memory"))
+  )
+)
+
+(component
+  (core module $m
+    (func (export "param-list") (param i32 i32 i32 i32) unreachable)
+  )
+  (core instance $i (instantiate $m))
+
+  (func (export "param-list") (param "l" (list u32 4))
+    (canon lift (core func $i "param-list"))
+  )
+)
+
+(assert_invalid
+  (component
+    (core module $m
+      (memory (export "memory") 1)
+      (func (export "ret-list") (result i32) unreachable)
+    )
+    (core instance $i (instantiate $m))
+
+    (func (export "ret-list") (result (list u32 0))
+      (canon lift (core func $i "ret-list") (memory $i "memory"))
+    )
+  )
+  "Fixed size lists must have more than zero elements (at offset 0x54)"
+)
+
+(assert_malformed
+  (component quote
+    "(core module $m"
+      "(memory (export \"memory\") 1)"
+      "(func (export \"ret-list\") (result i32) unreachable)"
+    ")"
+    "(core instance $i (instantiate $m))"
+
+    "(func (export \"ret-list\") (result (list u32 10000000000))"
+      "(canon lift (core func $i \"ret-list\") (memory $i \"memory\"))"
+    ")"
+  )
+  "invalid u32 number: constant out of range"
+)
+
+(assert_invalid
+  (component
+    (import "y" (component $c
+      (type $t (list s32 10))
+      (import "x" (type (eq $t)))
+    ))
+
+    (type $x (list u32 9))
+    (instance (instantiate $c (with "x" (type $x))))
+  )
+  "type mismatch for import `x`")
+
+
+(assert_invalid
+  (component
+    (import "y" (component $c
+      (type $t (list s32 10))
+      (import "x" (type (eq $t)))
+    ))
+
+    (type $x (list u32))
+    (instance (instantiate $c (with "x" (type $x))))
+  )
+  "type mismatch for import `x`")
diff --git a/tests/cli/importize.wit.simple-rename.stdout b/tests/cli/importize.wit.simple-rename.stdout
index 64612e20..1514dbe8 100644
--- a/tests/cli/importize.wit.simple-rename.stdout
+++ b/tests/cli/importize.wit.simple-rename.stdout
@@ -78,11 +78,11 @@ world fail1 {
 }
 world trim-imports {
   import a;
-  import foo: func();
   import bar: interface {
   }
 
   type t = u32;
+  import foo: func();
 
   export b;
 }
diff --git a/tests/cli/importize.wit.simple-toplevel.stdout b/tests/cli/importize.wit.simple-toplevel.stdout
index 10aff4d9..47994748 100644
--- a/tests/cli/importize.wit.simple-toplevel.stdout
+++ b/tests/cli/importize.wit.simple-toplevel.stdout
@@ -75,11 +75,11 @@ world fail1 {
 }
 world trim-imports {
   import a;
-  import foo: func();
   import bar: interface {
   }
 
   type t = u32;
+  import foo: func();
 
   export b;
 }
@@ -90,8 +90,8 @@ world tricky-import {
   export f: func() -> t;
 }
 world simple-toplevel-importized {
-  import foo: func();
   import something: interface {
     foo: func();
   }
+  import foo: func();
 }
diff --git a/tests/cli/importize.wit.simple.stdout b/tests/cli/importize.wit.simple.stdout
index 1bdb5c80..7e13e853 100644
--- a/tests/cli/importize.wit.simple.stdout
+++ b/tests/cli/importize.wit.simple.stdout
@@ -78,11 +78,11 @@ world fail1 {
 }
 world trim-imports {
   import a;
-  import foo: func();
   import bar: interface {
   }
 
   type t = u32;
+  import foo: func();
 
   export b;
 }
diff --git a/tests/cli/importize.wit.toplevel-deps.stdout b/tests/cli/importize.wit.toplevel-deps.stdout
index e187bd06..f9247fc1 100644
--- a/tests/cli/importize.wit.toplevel-deps.stdout
+++ b/tests/cli/importize.wit.toplevel-deps.stdout
@@ -69,11 +69,11 @@ world fail1 {
 }
 world trim-imports {
   import a;
-  import foo: func();
   import bar: interface {
   }
 
   type t = u32;
+  import foo: func();
 
   export b;
 }
@@ -84,7 +84,6 @@ world tricky-import {
   export f: func() -> t;
 }
 world toplevel-deps-importized {
-  import bar: func() -> s;
   import something-else-dep;
   import something-else: interface {
     use something-else-dep.{t};
@@ -93,4 +92,5 @@ world toplevel-deps-importized {
   }
 
   type s = u32;
+  import bar: func() -> s;
 }
diff --git a/tests/cli/importize.wit.tricky-import.stdout b/tests/cli/importize.wit.tricky-import.stdout
index 08d2d563..c89d0acf 100644
--- a/tests/cli/importize.wit.tricky-import.stdout
+++ b/tests/cli/importize.wit.tricky-import.stdout
@@ -81,16 +81,16 @@ world fail1 {
 }
 world trim-imports {
   import a;
-  import foo: func();
   import bar: interface {
   }
 
   type t = u32;
+  import foo: func();
 
   export b;
 }
 world tricky-import-importized {
   import with-dep;
-  import f: func() -> t;
   use with-dep.{t};
+  import f: func() -> t;
 }
diff --git a/tests/cli/importize.wit.with-deps.stdout b/tests/cli/importize.wit.with-deps.stdout
index 9b567ad3..97bef88d 100644
--- a/tests/cli/importize.wit.with-deps.stdout
+++ b/tests/cli/importize.wit.with-deps.stdout
@@ -75,11 +75,11 @@ world fail1 {
 }
 world trim-imports {
   import a;
-  import foo: func();
   import bar: interface {
   }
 
   type t = u32;
+  import foo: func();
 
   export b;
 }
diff --git a/tests/cli/missing-features/component-model/fixed-size-list.wast b/tests/cli/missing-features/component-model/fixed-size-list.wast
new file mode 100644
index 00000000..b6a92ada
--- /dev/null
+++ b/tests/cli/missing-features/component-model/fixed-size-list.wast
@@ -0,0 +1,30 @@
+;; RUN: wast % --assert default --snapshot tests/snapshots
+
+(assert_invalid
+  (component
+    (core module $m
+      (memory (export "memory") 1)
+      (func (export "ret-list") (result i32) unreachable)
+    )
+    (core instance $i (instantiate $m))
+
+    (func (export "ret-list") (result (list u32 4))
+      (canon lift (core func $i "ret-list") (memory $i "memory"))
+    )
+  )
+  "Fixed size lists require the component model fixed size list feature (at offset 0x54)"
+)
+
+(assert_invalid
+  (component
+    (core module $m
+      (func (export "param-list") (param i32 i32 i32 i32) unreachable)
+    )
+    (core instance $i (instantiate $m))
+
+    (func (export "param-list") (param "l" (list u32 4))
+      (canon lift (core func $i "param-list"))
+    )
+  )
+  "Fixed size lists require the component model fixed size list feature"
+)
diff --git a/tests/cli/validate-unknown-features.wat.stderr b/tests/cli/validate-unknown-features.wat.stderr
index 1c6e4539..b14cec9e 100644
--- a/tests/cli/validate-unknown-features.wat.stderr
+++ b/tests/cli/validate-unknown-features.wat.stderr
@@ -1,4 +1,4 @@
 error: invalid value 'unknown' for '--features <FEATURES>': unknown feature `unknown`
-Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, legacy-exceptions, gc-types, stack-switching, wide-arithmetic, cm-values, cm-nested-names, cm-async, cm-async-stackful, cm-async-builtins, cm-error-context, mvp, wasm1, wasm2, wasm3, all
+Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, legacy-exceptions, gc-types, stack-switching, wide-arithmetic, cm-values, cm-nested-names, cm-async, cm-async-stackful, cm-async-builtins, cm-error-context, cm-fixed-size-list, cm-gc, mvp, wasm1, wasm2, wasm3, all
 
 For more information, try '--help'.
diff --git a/tests/cli/wit-deep-list.wit b/tests/cli/wit-deep-list.wit
new file mode 100644
index 00000000..a3c23d44
--- /dev/null
+++ b/tests/cli/wit-deep-list.wit
@@ -0,0 +1,18 @@
+// RUN[parse]: component wit % -t | validate -f cm-fixed-size-list
+// RUN[abi]: component embed --dummy % | component new | validate -f cm-fixed-size-list
+
+package a:b;
+
+world lists {
+  type t = list<t2, 16>;
+  type t2 = list<t3, 16>;
+  type t3 = list<t4, 16>;
+  type t4 = list<t5, 16>;
+  type t5 = list<t6, 16>;
+  type t6 = list<t7, 16>;
+  type t7 = list<t8, 16>;
+  type t8 = list<u8, 16>;
+
+  import x: func(t: t);
+}
+
diff --git a/tests/cli/wit-deep-record.wit b/tests/cli/wit-deep-record.wit
new file mode 100644
index 00000000..dcdb6d35
--- /dev/null
+++ b/tests/cli/wit-deep-record.wit
@@ -0,0 +1,42 @@
+// FAIL[parse]: component wit % -t
+// FAIL[abi]: component embed --dummy % | component new
+
+package a:b;
+
+world records {
+  record t1 {
+    f1: t2, f2: t2, f3: t2, f4: t2, f5: t2, f6: t2, f7: t2, f8: t2,
+    f9: t2, f10: t2, f11: t2, f12: t2, f13: t2, f14: t2, f15: t2, f16: t2,
+  }
+  record t2 {
+    f1: t3, f2: t3, f3: t3, f4: t3, f5: t3, f6: t3, f7: t3, f8: t3,
+    f9: t3, f10: t3, f11: t3, f12: t3, f13: t3, f14: t3, f15: t3, f16: t3,
+  }
+  record t3 {
+    f1: t4, f2: t4, f3: t4, f4: t4, f5: t4, f6: t4, f7: t4, f8: t4,
+    f9: t4, f10: t4, f11: t4, f12: t4, f13: t4, f14: t4, f15: t4, f16: t4,
+  }
+  record t4 {
+    f1: t5, f2: t5, f3: t5, f4: t5, f5: t5, f6: t5, f7: t5, f8: t5,
+    f9: t5, f10: t5, f11: t5, f12: t5, f13: t5, f14: t5, f15: t5, f16: t5,
+  }
+  record t5 {
+    f1: t6, f2: t6, f3: t6, f4: t6, f5: t6, f6: t6, f7: t6, f8: t6,
+    f9: t6, f10: t6, f11: t6, f12: t6, f13: t6, f14: t6, f15: t6, f16: t6,
+  }
+  record t6 {
+    f1: t7, f2: t7, f3: t7, f4: t7, f5: t7, f6: t7, f7: t7, f8: t7,
+    f9: t7, f10: t7, f11: t7, f12: t7, f13: t7, f14: t7, f15: t7, f16: t7,
+  }
+  record t7 {
+    f1: t8, f2: t8, f3: t8, f4: t8, f5: t8, f6: t8, f7: t8, f8: t8,
+    f9: t8, f10: t8, f11: t8, f12: t8, f13: t8, f14: t8, f15: t8, f16: t8,
+  }
+  record t8 {
+    f1: u8, f2: u8, f3: u8, f4: u8, f5: u8, f6: u8, f7: u8, f8: u8,
+    f9: u8, f10: u8, f11: u8, f12: u8, f13: u8, f14: u8, f15: u8, f16: u8,
+  }
+
+  import x: func(t: t1);
+}
+
diff --git a/tests/cli/wit-deep-record.wit.abi.stderr b/tests/cli/wit-deep-record.wit.abi.stderr
new file mode 100644
index 00000000..22fec7a9
--- /dev/null
+++ b/tests/cli/wit-deep-record.wit.abi.stderr
@@ -0,0 +1,4 @@
+error: decoding custom section component-type
+
+Caused by:
+    0: effective type size exceeds the limit of 1000000 (at offset 0x27)
diff --git a/tests/cli/wit-deep-record.wit.parse.stderr b/tests/cli/wit-deep-record.wit.parse.stderr
new file mode 100644
index 00000000..10ffc9d0
--- /dev/null
+++ b/tests/cli/wit-deep-record.wit.parse.stderr
@@ -0,0 +1,1 @@
+error: effective type size exceeds the limit of 1000000 (at offset 0xc)
diff --git a/tests/cli/wit-deep-tuple.wit b/tests/cli/wit-deep-tuple.wit
new file mode 100644
index 00000000..decff8e8
--- /dev/null
+++ b/tests/cli/wit-deep-tuple.wit
@@ -0,0 +1,17 @@
+// FAIL[parse]: component wit % -t
+// FAIL[abi]: component embed --dummy % | component new
+
+package a:b;
+
+world tuples {
+  type t = tuple<t2,t2,t2,t2,t2,t2,t2,t2,t2,t2,t2,t2,t2,t2,t2,t2,>;
+  type t2 = tuple<t3,t3,t3,t3,t3,t3,t3,t3,t3,t3,t3,t3,t3,t3,t3,t3,>;
+  type t3 = tuple<t4,t4,t4,t4,t4,t4,t4,t4,t4,t4,t4,t4,t4,t4,t4,t4,>;
+  type t4 = tuple<t5,t5,t5,t5,t5,t5,t5,t5,t5,t5,t5,t5,t5,t5,t5,t5,>;
+  type t5 = tuple<t6,t6,t6,t6,t6,t6,t6,t6,t6,t6,t6,t6,t6,t6,t6,t6,>;
+  type t6 = tuple<t7,t7,t7,t7,t7,t7,t7,t7,t7,t7,t7,t7,t7,t7,t7,t7,>;
+  type t7 = tuple<t8,t8,t8,t8,t8,t8,t8,t8,t8,t8,t8,t8,t8,t8,t8,t8,>;
+  type t8 = tuple<u8,u8,u8,u8,u8,u8,u8,u8,u8,u8,u8,u8,u8,u8,u8,u8,>;
+
+  import x: func(t: t);
+}
diff --git a/tests/cli/wit-deep-tuple.wit.abi.stderr b/tests/cli/wit-deep-tuple.wit.abi.stderr
new file mode 100644
index 00000000..22fec7a9
--- /dev/null
+++ b/tests/cli/wit-deep-tuple.wit.abi.stderr
@@ -0,0 +1,4 @@
+error: decoding custom section component-type
+
+Caused by:
+    0: effective type size exceeds the limit of 1000000 (at offset 0x27)
diff --git a/tests/cli/wit-deep-tuple.wit.parse.stderr b/tests/cli/wit-deep-tuple.wit.parse.stderr
new file mode 100644
index 00000000..10ffc9d0
--- /dev/null
+++ b/tests/cli/wit-deep-tuple.wit.parse.stderr
@@ -0,0 +1,1 @@
+error: effective type size exceeds the limit of 1000000 (at offset 0xc)
diff --git a/tests/cli/wit-deep-variant.wit b/tests/cli/wit-deep-variant.wit
new file mode 100644
index 00000000..a68f5428
--- /dev/null
+++ b/tests/cli/wit-deep-variant.wit
@@ -0,0 +1,47 @@
+// FAIL[parse]: component wit % -t
+
+// TODO: this should use "FAIL" and should actually be a test, but this
+// currently takes too long to run and the long runtime is in and of itself a
+// bug.
+// fail[abi]: component embed --dummy % | component new
+
+package a:b;
+
+world variants {
+  variant t1 {
+    f1(t2), f2(t2), f3(t2), f4(t2), f5(t2), f6(t2), f7(t2), f8(t2),
+    f9(t2), f10(t2), f11(t2), f12(t2), f13(t2), f14(t2), f15(t2), f16(t2),
+  }
+  variant t2 {
+    f1(t3), f2(t3), f3(t3), f4(t3), f5(t3), f6(t3), f7(t3), f8(t3),
+    f9(t3), f10(t3), f11(t3), f12(t3), f13(t3), f14(t3), f15(t3), f16(t3),
+  }
+  variant t3 {
+    f1(t4), f2(t4), f3(t4), f4(t4), f5(t4), f6(t4), f7(t4), f8(t4),
+    f9(t4), f10(t4), f11(t4), f12(t4), f13(t4), f14(t4), f15(t4), f16(t4),
+  }
+  variant t4 {
+    f1(t5), f2(t5), f3(t5), f4(t5), f5(t5), f6(t5), f7(t5), f8(t5),
+    f9(t5), f10(t5), f11(t5), f12(t5), f13(t5), f14(t5), f15(t5), f16(t5),
+  }
+  variant t5 {
+    f1(t6), f2(t6), f3(t6), f4(t6), f5(t6), f6(t6), f7(t6), f8(t6),
+    f9(t6), f10(t6), f11(t6), f12(t6), f13(t6), f14(t6), f15(t6), f16(t6),
+  }
+  variant t6 {
+    f1(t7), f2(t7), f3(t7), f4(t7), f5(t7), f6(t7), f7(t7), f8(t7),
+    f9(t7), f10(t7), f11(t7), f12(t7), f13(t7), f14(t7), f15(t7), f16(t7),
+  }
+  variant t7 {
+    f1(t8), f2(t8), f3(t8), f4(t8), f5(t8), f6(t8), f7(t8), f8(t8),
+    f9(t8), f10(t8), f11(t8), f12(t8), f13(t8), f14(t8), f15(t8), f16(t8),
+  }
+  variant t8 {
+    f1(u8), f2(u8), f3(u8), f4(u8), f5(u8), f6(u8), f7(u8), f8(u8),
+    f9(u8), f10(u8), f11(u8), f12(u8), f13(u8), f14(u8), f15(u8), f16(u8),
+  }
+
+  import x: func(t: t1);
+}
+
+
diff --git a/tests/cli/wit-deep-variant.wit.parse.stderr b/tests/cli/wit-deep-variant.wit.parse.stderr
new file mode 100644
index 00000000..10ffc9d0
--- /dev/null
+++ b/tests/cli/wit-deep-variant.wit.parse.stderr
@@ -0,0 +1,1 @@
+error: effective type size exceeds the limit of 1000000 (at offset 0xc)
diff --git a/tests/cli/wit-stability-in-binary-format.wit.stdout b/tests/cli/wit-stability-in-binary-format.wit.stdout
index 45db6e8b..54a0a939 100644
--- a/tests/cli/wit-stability-in-binary-format.wit.stdout
+++ b/tests/cli/wit-stability-in-binary-format.wit.stdout
@@ -25,13 +25,13 @@ world w {
   @since(version = 1.0.0)
   import foo;
   @since(version = 1.0.0)
-  import f: func();
-  @since(version = 1.0.0)
   import a: interface {
   }
 
   @since(version = 1.0.0)
   type t = u32;
+  @since(version = 1.0.0)
+  import f: func();
 
   @since(version = 1.0.0)
   export f: func();
diff --git a/tests/cli/wit-with-features.wit.stdout b/tests/cli/wit-with-features.wit.stdout
index b402b6aa..f188b45e 100644
--- a/tests/cli/wit-with-features.wit.stdout
+++ b/tests/cli/wit-with-features.wit.stdout
@@ -39,14 +39,14 @@ interface another-interface {
 world the-world {
   @unstable(feature = foo)
   import foo;
-  @unstable(feature = foo)
-  import x: func();
 
   @unstable(feature = foo)
   resource thing {
     @unstable(feature = foo)
     constructor();
   }
+  @unstable(feature = foo)
+  import x: func();
 
   @unstable(feature = foo)
   export y: func();
diff --git a/tests/cli/world-merging-add-imports.wit.stdout b/tests/cli/world-merging-add-imports.wit.stdout
index 6f0064d4..80d325e6 100644
--- a/tests/cli/world-merging-add-imports.wit.stdout
+++ b/tests/cli/world-merging-add-imports.wit.stdout
@@ -1,11 +1,11 @@
 package root:root;
 
 world root {
-  import a:b/b;
   import a: interface {
   }
-  import c: func() -> t;
+  import a:b/b;
   use a:b/b.{t};
+  import c: func() -> t;
 }
 package a:b {
   interface b {
@@ -14,10 +14,10 @@ package a:b {
   world into {
   }
   world %from {
-    import b;
     import a: interface {
     }
-    import c: func() -> t;
+    import b;
     use b.{t};
+    import c: func() -> t;
   }
 }
diff --git a/tests/cli/world-merging-same-imports.wit.stdout b/tests/cli/world-merging-same-imports.wit.stdout
index 90090844..e5c366b4 100644
--- a/tests/cli/world-merging-same-imports.wit.stdout
+++ b/tests/cli/world-merging-same-imports.wit.stdout
@@ -1,28 +1,28 @@
 package root:root;
 
 world root {
-  import a:b/b;
   import a: interface {
   }
-  import c: func();
+  import a:b/b;
   use a:b/b.{t};
+  import c: func();
 }
 package a:b {
   interface b {
     type t = u32;
   }
   world into {
-    import b;
     import a: interface {
     }
-    import c: func();
+    import b;
     use b.{t};
+    import c: func();
   }
   world %from {
-    import b;
     import a: interface {
     }
-    import c: func();
+    import b;
     use b.{t};
+    import c: func();
   }
 }
diff --git a/tests/cli/world-merging.wit.stdout b/tests/cli/world-merging.wit.stdout
index abcfaefc..d18e6f53 100644
--- a/tests/cli/world-merging.wit.stdout
+++ b/tests/cli/world-merging.wit.stdout
@@ -2,8 +2,8 @@ package root:root;
 
 world root {
   import a:b/x;
-  import a: func() -> t;
   use a:b/x.{t};
+  import a: func() -> t;
 
   export y: func();
   export a:b/x;
@@ -16,8 +16,8 @@ package a:b {
   }
   world foo {
     import x;
-    import a: func() -> t;
     use x.{t};
+    import a: func() -> t;
 
     export y: func();
     export x;
diff --git a/tests/snapshots/cli/component-model-async/lift.wast.json b/tests/snapshots/cli/component-model-async/lift.wast.json
index d65d106b..8a2e0682 100644
--- a/tests/snapshots/cli/component-model-async/lift.wast.json
+++ b/tests/snapshots/cli/component-model-async/lift.wast.json
@@ -12,7 +12,7 @@
       "line": 17,
       "filename": "lift.1.wasm",
       "module_type": "binary",
-      "text": "cannot specify post-return function when lifting async"
+      "text": "cannot specify post-return function in async"
     },
     {
       "type": "module",
@@ -32,7 +32,7 @@
       "line": 62,
       "filename": "lift.4.wasm",
       "module_type": "binary",
-      "text": "cannot specify post-return function when lifting async"
+      "text": "cannot specify post-return function in async"
     },
     {
       "type": "assert_invalid",
@@ -53,7 +53,7 @@
       "line": 110,
       "filename": "lift.7.wasm",
       "module_type": "binary",
-      "text": "cannot specify callback without lifting async"
+      "text": "cannot specify callback without async"
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/snapshots/cli/component-model-async/task-builtins.wast.json b/tests/snapshots/cli/component-model-async/task-builtins.wast.json
index 7f9ba4c3..9d38e0ce 100644
--- a/tests/snapshots/cli/component-model-async/task-builtins.wast.json
+++ b/tests/snapshots/cli/component-model-async/task-builtins.wast.json
@@ -32,7 +32,7 @@
       "line": 38,
       "filename": "task-builtins.4.wasm",
       "module_type": "binary",
-      "text": "cannot specify `callback` option on `task.return`"
+      "text": "cannot specify callback without async"
     },
     {
       "type": "assert_invalid",
diff --git a/tests/snapshots/cli/component-model-async/task-builtins.wast/19.print b/tests/snapshots/cli/component-model-async/task-builtins.wast/19.print
index 7034e8c4..d99f410b 100644
--- a/tests/snapshots/cli/component-model-async/task-builtins.wast/19.print
+++ b/tests/snapshots/cli/component-model-async/task-builtins.wast/19.print
@@ -1,6 +1,6 @@
 (component
   (core module $m (;0;)
-    (type (;0;) (func))
+    (type (;0;) (func (result i32)))
     (import "" "yield" (func $yield (;0;) (type 0)))
   )
   (core func $yield (;0;) (canon yield async))
diff --git a/tests/snapshots/cli/component-model-gc/core-type.wat.json b/tests/snapshots/cli/component-model-gc/core-type.wat.json
new file mode 100644
index 00000000..b47108bb
--- /dev/null
+++ b/tests/snapshots/cli/component-model-gc/core-type.wat.json
@@ -0,0 +1,32 @@
+{
+  "source_filename": "tests/cli/component-model-gc/core-type.wat",
+  "commands": [
+    {
+      "type": "module",
+      "line": 3,
+      "filename": "core-type.0.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 10,
+      "filename": "core-type.1.wasm",
+      "module_type": "binary",
+      "text": "canonical option `core-type` is not allowed in `canon lift`"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 26,
+      "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"
+    }
+  ]
+}
\ 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
new file mode 100644
index 00000000..b38ce886
--- /dev/null
+++ b/tests/snapshots/cli/component-model-gc/core-type.wat/0.print
@@ -0,0 +1,6 @@
+(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/fixed-size-list.wast.json b/tests/snapshots/cli/component-model/fixed-size-list.wast.json
new file mode 100644
index 00000000..2d7c8c44
--- /dev/null
+++ b/tests/snapshots/cli/component-model/fixed-size-list.wast.json
@@ -0,0 +1,45 @@
+{
+  "source_filename": "tests/cli/component-model/fixed-size-list.wast",
+  "commands": [
+    {
+      "type": "module",
+      "line": 3,
+      "filename": "fixed-size-list.0.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 15,
+      "filename": "fixed-size-list.1.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 27,
+      "filename": "fixed-size-list.2.wasm",
+      "module_type": "binary",
+      "text": "Fixed size lists must have more than zero elements (at offset 0x54)"
+    },
+    {
+      "type": "assert_malformed",
+      "line": 42,
+      "filename": "fixed-size-list.3.wat",
+      "module_type": "text",
+      "text": "invalid u32 number: constant out of range"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 57,
+      "filename": "fixed-size-list.4.wasm",
+      "module_type": "binary",
+      "text": "type mismatch for import `x`"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 70,
+      "filename": "fixed-size-list.5.wasm",
+      "module_type": "binary",
+      "text": "type mismatch for import `x`"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/cli/component-model/fixed-size-list.wast/0.print b/tests/snapshots/cli/component-model/fixed-size-list.wast/0.print
new file mode 100644
index 00000000..79848d67
--- /dev/null
+++ b/tests/snapshots/cli/component-model/fixed-size-list.wast/0.print
@@ -0,0 +1,18 @@
+(component
+  (core module $m (;0;)
+    (type (;0;) (func (result i32)))
+    (memory (;0;) 1)
+    (export "memory" (memory 0))
+    (export "ret-list" (func 0))
+    (func (;0;) (type 0) (result i32)
+      unreachable
+    )
+  )
+  (core instance $i (;0;) (instantiate $m))
+  (type (;0;) (list u32 4))
+  (type (;1;) (func (result 0)))
+  (alias core export $i "ret-list" (core func (;0;)))
+  (alias core export $i "memory" (core memory (;0;)))
+  (func (;0;) (type 1) (canon lift (core func 0) (memory 0)))
+  (export (;1;) "ret-list" (func 0))
+)
diff --git a/tests/snapshots/cli/component-model/fixed-size-list.wast/1.print b/tests/snapshots/cli/component-model/fixed-size-list.wast/1.print
new file mode 100644
index 00000000..ee75456d
--- /dev/null
+++ b/tests/snapshots/cli/component-model/fixed-size-list.wast/1.print
@@ -0,0 +1,15 @@
+(component
+  (core module $m (;0;)
+    (type (;0;) (func (param i32 i32 i32 i32)))
+    (export "param-list" (func 0))
+    (func (;0;) (type 0) (param i32 i32 i32 i32)
+      unreachable
+    )
+  )
+  (core instance $i (;0;) (instantiate $m))
+  (type (;0;) (list u32 4))
+  (type (;1;) (func (param "l" 0)))
+  (alias core export $i "param-list" (core func (;0;)))
+  (func (;0;) (type 1) (canon lift (core func 0)))
+  (export (;1;) "param-list" (func 0))
+)
diff --git a/tests/snapshots/cli/missing-features/component-model/fixed-size-list.wast.json b/tests/snapshots/cli/missing-features/component-model/fixed-size-list.wast.json
new file mode 100644
index 00000000..d9123336
--- /dev/null
+++ b/tests/snapshots/cli/missing-features/component-model/fixed-size-list.wast.json
@@ -0,0 +1,19 @@
+{
+  "source_filename": "tests/cli/missing-features/component-model/fixed-size-list.wast",
+  "commands": [
+    {
+      "type": "assert_invalid",
+      "line": 4,
+      "filename": "fixed-size-list.0.wasm",
+      "module_type": "binary",
+      "text": "Fixed size lists require the component model fixed size list feature (at offset 0x54)"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 19,
+      "filename": "fixed-size-list.1.wasm",
+      "module_type": "binary",
+      "text": "Fixed size lists require the component model fixed size list feature"
+    }
+  ]
+}
\ No newline at end of file