Repository: bytecodealliance/wasm-tools
Pull request #1257 (2023-10-19 20:05:18)

wasmparser: Use type-specific identifiers pervasively
This commit changes `wasmparser` from the uni-typed `TypeId` identifier to a myriad of type-specific identifiers, such as `ComponentInstanceTypeId` and `ComponentDefinedTypeId`. There are also identifiers for any kind of type within some group, such as `ComponentAnyTypeId` for any kind of component type and `AnyTypeId` for any kind of type, component or core.

The `wasmparser::Types` arena can be indexed by any of these leaf type identifiers, which all implement the `TypeIdentifier` trait. The resulting indexed data is the particular type, e.g. a `ComponentFuncType`, rather than the uni-typed `wasmparser::Type`.

There was a lot of ocean boiling involved here. Apologies in advance for the pain of this upgrade! That said, it should provide type safety benefits moving forward and sets the stage for further identifier refactors for core types that are necessary for Wasm GC.

If you are upgrading and were using `TypeId` before, and don't want to spend time gaining type safety on your type lookups, then just use `AnyTypeId` instead of the old `TypeId`. They are morally identical. Alternatively, if you know a particular `TypeId` was always a component function, for example, and you always did `types[id].unwrap_component_func()`, then you can switch to using `ComponentFuncTypeId`.

fitzgen: FWIW: 3.5 million fuzz iterations and no problems discovered thus far.

Patch:
diff --git a/crates/wasm-compose/src/composer.rs b/crates/wasm-compose/src/composer.rs
index b54d5787..c0e3d537 100644
--- a/crates/wasm-compose/src/composer.rs
+++ b/crates/wasm-compose/src/composer.rs
@@ -12,7 +12,7 @@ use anyhow::{anyhow, bail, Context, Result};
 use indexmap::IndexMap;
 use std::{collections::VecDeque, ffi::OsStr, path::Path};
 use wasmparser::{
-    types::{ComponentEntityType, TypeId, TypesRef},
+    types::{ComponentEntityType, ComponentInstanceTypeId, TypesRef},
     ComponentExternalKind, ComponentTypeRef,
 };
 
@@ -198,7 +198,7 @@ impl<'a> CompositionGraphBuilder<'a> {
         instance: usize,
         dependent: usize,
         arg_name: &str,
-        ty: TypeId,
+        ty: ComponentInstanceTypeId,
         types: TypesRef,
     ) -> Result<Option<ExportIndex>> {
         let (instance_name, instance_id) = self.instances.get_index(instance).unwrap();
@@ -241,7 +241,7 @@ impl<'a> CompositionGraphBuilder<'a> {
         instance: usize,
         dependent_path: &Path,
         arg_name: &str,
-        ty: TypeId,
+        ty: ComponentInstanceTypeId,
         types: TypesRef,
     ) -> Result<ExportIndex> {
         let (_, instance_id) = self.instances.get_index(instance).unwrap();
@@ -271,13 +271,21 @@ impl<'a> CompositionGraphBuilder<'a> {
     }
 
     /// Resolves an import instance reference.
-    fn resolve_import_ref(&self, r: InstanceImportRef) -> (&Component, &str, TypeId) {
+    fn resolve_import_ref(
+        &self,
+        r: InstanceImportRef,
+    ) -> (&Component, &str, ComponentInstanceTypeId) {
         let component = self.graph.get_component(r.component).unwrap();
         let (name, ty) = component.import(r.import).unwrap();
         match ty {
-            ComponentTypeRef::Instance(index) => {
-                (component, name, component.types.component_type_at(index))
-            }
+            ComponentTypeRef::Instance(index) => (
+                component,
+                name,
+                component
+                    .types
+                    .component_any_type_at(index)
+                    .unwrap_instance(),
+            ),
             _ => unreachable!("should not have an instance import ref to a non-instance import"),
         }
     }
diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs
index 79c39b95..5303193c 100644
--- a/crates/wasm-compose/src/encoding.rs
+++ b/crates/wasm-compose/src/encoding.rs
@@ -14,7 +14,11 @@ use std::{
 use wasm_encoder::*;
 use wasmparser::{
     names::KebabString,
-    types::{self, ComponentEntityType, Type, TypeId},
+    types::{
+        self, AnyTypeId, ComponentAnyTypeId, ComponentCoreModuleTypeId, ComponentCoreTypeId,
+        ComponentDefinedTypeId, ComponentEntityType, ComponentFuncTypeId, ComponentInstanceTypeId,
+        ComponentTypeId,
+    },
     ComponentExternalKind,
 };
 
@@ -113,7 +117,7 @@ pub(crate) struct TypeState<'a> {
 /// Note that this has two components: the first is the component that a type
 /// comes from and the second is the wasmparser-unique id for within that
 /// component.
-type TypeKey<'a> = (PtrKey<'a, crate::graph::Component<'a>>, TypeId);
+type TypeKey<'a> = (PtrKey<'a, crate::graph::Component<'a>>, AnyTypeId);
 
 /// A scope that types can be defined into.
 ///
@@ -334,16 +338,15 @@ impl<'a> TypeEncoder<'a> {
     fn entity_type(
         &self,
         encodable: &mut ModuleType,
-        types: &mut HashMap<TypeId, u32>,
+        types: &mut HashMap<AnyTypeId, u32>,
         ty: wasmparser::types::EntityType,
     ) -> EntityType {
         match ty {
             wasmparser::types::EntityType::Func(id) => {
-                let ty = &self.0.types[id];
-                let idx = match types.entry(id) {
+                let ty = &self.0.types[id].unwrap_func();
+                let idx = match types.entry(ComponentCoreTypeId::Sub(id).into()) {
                     Entry::Occupied(e) => *e.get(),
                     Entry::Vacant(e) => {
-                        let ty = ty.unwrap_func();
                         let index = encodable.type_count();
                         encodable.ty().function(
                             ty.params().iter().copied().map(Self::val_type),
@@ -359,7 +362,7 @@ impl<'a> TypeEncoder<'a> {
             wasmparser::types::EntityType::Global(ty) => EntityType::Global(Self::global_type(ty)),
             wasmparser::types::EntityType::Tag(id) => {
                 let ty = &self.0.types[id];
-                let idx = match types.entry(id) {
+                let idx = match types.entry(ComponentCoreTypeId::Sub(id).into()) {
                     Entry::Occupied(e) => *e.get(),
                     Entry::Vacant(e) => {
                         let ty = ty.unwrap_func();
@@ -386,22 +389,22 @@ impl<'a> TypeEncoder<'a> {
     ) -> ComponentTypeRef {
         match ty {
             wasmparser::types::ComponentEntityType::Module(id) => {
-                ComponentTypeRef::Module(self.ty(state, id))
+                ComponentTypeRef::Module(self.ty(state, id.into()))
             }
             wasmparser::types::ComponentEntityType::Func(id) => {
-                ComponentTypeRef::Func(self.ty(state, id))
+                ComponentTypeRef::Func(self.ty(state, id.into()))
             }
             wasmparser::types::ComponentEntityType::Value(ty) => {
                 ComponentTypeRef::Value(self.component_val_type(state, ty))
             }
             wasmparser::types::ComponentEntityType::Type { referenced, .. } => {
-                ComponentTypeRef::Type(TypeBounds::Eq(self.ty(state, referenced)))
+                ComponentTypeRef::Type(TypeBounds::Eq(self.ty(state, referenced.into())))
             }
             wasmparser::types::ComponentEntityType::Instance(id) => {
-                ComponentTypeRef::Instance(self.ty(state, id))
+                ComponentTypeRef::Instance(self.ty(state, id.into()))
             }
             wasmparser::types::ComponentEntityType::Component(id) => {
-                ComponentTypeRef::Component(self.ty(state, id))
+                ComponentTypeRef::Component(self.ty(state, id.into()))
             }
         }
     }
@@ -478,9 +481,8 @@ impl<'a> TypeEncoder<'a> {
         }
     }
 
-    fn module_type(&self, state: &mut TypeState<'a>, id: TypeId) -> u32 {
+    fn module_type(&self, state: &mut TypeState<'a>, id: ComponentCoreModuleTypeId) -> u32 {
         let ty = &self.0.types[id];
-        let ty = ty.unwrap_module();
 
         let module = self.module(
             ty.imports
@@ -494,18 +496,20 @@ impl<'a> TypeEncoder<'a> {
         index
     }
 
-    fn component_instance_type(&self, state: &mut TypeState<'a>, id: TypeId) -> u32 {
+    fn component_instance_type(
+        &self,
+        state: &mut TypeState<'a>,
+        id: ComponentInstanceTypeId,
+    ) -> u32 {
         let ty = &self.0.types[id];
-        let ty = ty.unwrap_component_instance();
         let instance = self.instance(state, ty.exports.iter().map(|(n, t)| (n.as_str(), *t)));
         let index = state.cur.encodable.type_count();
         state.cur.encodable.ty().instance(&instance);
         index
     }
 
-    fn component_type(&self, state: &mut TypeState<'a>, id: TypeId) -> u32 {
+    fn component_type(&self, state: &mut TypeState<'a>, id: ComponentTypeId) -> u32 {
         let ty = &self.0.types[id];
-        let ty = ty.unwrap_component();
 
         let component = self.component(
             state,
@@ -518,17 +522,16 @@ impl<'a> TypeEncoder<'a> {
         index
     }
 
-    fn component_func_type(&self, state: &mut TypeState<'a>, id: TypeId) -> u32 {
+    fn component_func_type(&self, state: &mut TypeState<'a>, id: ComponentFuncTypeId) -> u32 {
         let ty = &self.0.types[id];
-        let func_ty = ty.unwrap_component_func();
 
-        let params = func_ty
+        let params = ty
             .params
             .iter()
             .map(|(name, ty)| (name.as_str(), self.component_val_type(state, *ty)))
             .collect::<Vec<_>>();
 
-        let results = func_ty
+        let results = ty
             .results
             .iter()
             .map(|(name, ty)| (name.as_deref(), self.component_val_type(state, *ty)))
@@ -561,7 +564,7 @@ impl<'a> TypeEncoder<'a> {
     /// * Each type is translated only once
     /// * If `id` comes from a different instance it's aliased
     /// * Dispatching to the correct translation internally.
-    fn ty(&self, state: &mut TypeState<'a>, id: TypeId) -> u32 {
+    fn ty(&self, state: &mut TypeState<'a>, id: AnyTypeId) -> u32 {
         // Consult our scope's `type_defs` map, and if it's not present then
         // generate the type and fill it in.
         let key = (PtrKey(self.0), id);
@@ -576,19 +579,21 @@ impl<'a> TypeEncoder<'a> {
 
     // Inner version of `ty` above which is a separate method to make it easier
     // to use `return` and not thwart the caching above.
-    fn _ty(&self, state: &mut TypeState<'a>, id: TypeId) -> u32 {
+    fn _ty(&self, state: &mut TypeState<'a>, id: AnyTypeId) -> u32 {
         // If `id` is not an alias to anything else then it's a defined type in
         // this scope meaning that it needs to be translated and represented
         // here.
-        if self.0.types.peel_alias(id).is_none() {
-            return match &self.0.types[id] {
-                Type::Sub(_) | Type::Instance(_) => unreachable!(),
-                Type::Module(_) => self.module_type(state, id),
-                Type::Component(_) => self.component_type(state, id),
-                Type::ComponentInstance(_) => self.component_instance_type(state, id),
-                Type::ComponentFunc(_) => self.component_func_type(state, id),
-                Type::Defined(_) => self.defined_type(state, id),
-                Type::Resource(_) => unimplemented!(),
+        if id.peel_alias(&self.0.types).is_none() {
+            return match id {
+                AnyTypeId::Core(ComponentCoreTypeId::Sub(_)) => unreachable!(),
+                AnyTypeId::Core(ComponentCoreTypeId::Module(id)) => self.module_type(state, id),
+                AnyTypeId::Component(id) => match id {
+                    ComponentAnyTypeId::Resource(_) => unimplemented!(),
+                    ComponentAnyTypeId::Defined(id) => self.defined_type(state, id),
+                    ComponentAnyTypeId::Func(id) => self.component_func_type(state, id),
+                    ComponentAnyTypeId::Instance(id) => self.component_instance_type(state, id),
+                    ComponentAnyTypeId::Component(id) => self.component_type(state, id),
+                },
             };
         }
 
@@ -656,7 +661,7 @@ impl<'a> TypeEncoder<'a> {
             // in this type translation. It should be the case that we know
             // how to find all types at this point, so reaching the end means
             // there's a missing case one way or another.
-            cur = self.0.types.peel_alias(cur).unwrap_or_else(|| {
+            cur = cur.peel_alias(&self.0.types).unwrap_or_else(|| {
                 panic!("failed to find alias of {id:?}");
             });
         }
@@ -672,16 +677,15 @@ impl<'a> TypeEncoder<'a> {
                 ComponentValType::Primitive(Self::primitive(ty))
             }
             wasmparser::types::ComponentValType::Type(id) => {
-                ComponentValType::Type(self.ty(state, id))
+                ComponentValType::Type(self.ty(state, ComponentAnyTypeId::from(id).into()))
             }
         }
     }
 
-    fn defined_type(&self, state: &mut TypeState<'a>, id: TypeId) -> u32 {
+    fn defined_type(&self, state: &mut TypeState<'a>, id: ComponentDefinedTypeId) -> u32 {
         let ty = &self.0.types[id];
-        let defined_ty = ty.unwrap_defined();
 
-        match defined_ty {
+        match ty {
             wasmparser::types::ComponentDefinedType::Primitive(ty) => {
                 let index = state.cur.encodable.type_count();
                 state
@@ -706,17 +710,11 @@ impl<'a> TypeEncoder<'a> {
             wasmparser::types::ComponentDefinedType::Result { ok, err } => {
                 self.result(state, *ok, *err)
             }
-            wasmparser::types::ComponentDefinedType::Own(id) => {
-                let i = self.ty(state, *id);
-                let index = state.cur.encodable.type_count();
-                state.cur.encodable.ty().defined_type().own(i);
-                index
+            wasmparser::types::ComponentDefinedType::Own(_r) => {
+                unimplemented!()
             }
-            wasmparser::types::ComponentDefinedType::Borrow(id) => {
-                let i = self.ty(state, *id);
-                let index = state.cur.encodable.type_count();
-                state.cur.encodable.ty().defined_type().borrow(i);
-                index
+            wasmparser::types::ComponentDefinedType::Borrow(_r) => {
+                unimplemented!()
             }
         }
     }
@@ -825,7 +823,7 @@ impl<'a> TypeEncoder<'a> {
         let export = self.component_entity_type(state, export);
         if let Some(id) = id {
             // Update the index in the type map to point to this export
-            let key = (PtrKey(self.0), id);
+            let key = (PtrKey(self.0), id.into());
             let prev = state
                 .cur
                 .type_defs
@@ -872,10 +870,7 @@ impl ArgumentImport<'_> {
         // If the existing import is an instance, convert this argument import to
         // a merged instance import.
         if let ArgumentImportKind::Item(component, ComponentEntityType::Instance(id)) = &self.kind {
-            let exports = component.types[*id]
-                .unwrap_component_instance()
-                .exports
-                .iter();
+            let exports = component.types[*id].exports.iter();
 
             let mut map = IndexMap::with_capacity(exports.len());
             for (name, ty) in exports {
@@ -895,11 +890,7 @@ impl ArgumentImport<'_> {
                 ArgumentImportKind::Instance(exports),
                 ArgumentImportKind::Item(new_component, ComponentEntityType::Instance(id)),
             ) => {
-                for (name, new_type) in new_component.types[id]
-                    .unwrap_component_instance()
-                    .exports
-                    .iter()
-                {
+                for (name, new_type) in new_component.types[id].exports.iter() {
                     let dst = exports.entry(name.as_str()).or_default();
                     for (existing_component, existing_type) in dst.iter_mut() {
                         if Self::types_compatible(
@@ -1165,7 +1156,7 @@ impl<'a> ImportMap<'a> {
 /// and that'll draw a dependency from `self.cur` to that name.
 struct DependencyRegistrar<'a, 'b> {
     types: &'a types::Types,
-    defining_instances: &'b mut HashMap<TypeId, &'a str>,
+    defining_instances: &'b mut HashMap<ComponentAnyTypeId, &'a str>,
     cur: &'a str,
     deps: &'b mut HashMap<&'a str, Vec<&'a str>>,
 }
@@ -1189,7 +1180,7 @@ impl DependencyRegistrar<'_, '_> {
         }
     }
 
-    fn ty(&mut self, ty: TypeId) {
+    fn ty(&mut self, ty: ComponentAnyTypeId) {
         match self.defining_instances.entry(ty) {
             // If it's known where `ty` is defined then there's nothing else to
             // do here beyond drawing a new dependency edge. Note though that
@@ -1214,38 +1205,37 @@ impl DependencyRegistrar<'_, '_> {
             return self.ty(ty);
         }
 
-        match &self.types[ty] {
-            Type::Instance(_) | Type::Sub(_) | Type::Module(_) => {}
-            Type::Component(_) => self.component(ty),
-            Type::ComponentInstance(_) => self.instance(ty),
-            Type::ComponentFunc(_) => self.func(ty),
-            Type::Defined(_) => self.defined(ty),
-            Type::Resource(_) => {}
+        match ty {
+            ComponentAnyTypeId::Resource(_) => unimplemented!(),
+            ComponentAnyTypeId::Defined(id) => self.defined(id),
+            ComponentAnyTypeId::Func(id) => self.func(id),
+            ComponentAnyTypeId::Instance(id) => self.instance(id),
+            ComponentAnyTypeId::Component(id) => self.component(id),
         }
     }
 
     fn val_type(&mut self, ty: types::ComponentValType) {
         match ty {
-            types::ComponentValType::Type(t) => self.ty(t),
+            types::ComponentValType::Type(t) => self.ty(t.into()),
             types::ComponentValType::Primitive(_) => {}
         }
     }
 
-    fn component(&mut self, ty: TypeId) {
-        let ty = &self.types[ty].unwrap_component();
+    fn component(&mut self, ty: ComponentTypeId) {
+        let ty = &self.types[ty];
         for (_, ty) in ty.imports.iter().chain(&ty.exports) {
             self.entity(*ty);
         }
     }
 
-    fn instance(&mut self, ty: TypeId) {
-        for (_, ty) in self.types[ty].unwrap_component_instance().exports.iter() {
+    fn instance(&mut self, ty: ComponentInstanceTypeId) {
+        for (_, ty) in self.types[ty].exports.iter() {
             self.entity(*ty);
         }
     }
 
-    fn func(&mut self, ty: TypeId) {
-        let ty = &self.types[ty].unwrap_component_func();
+    fn func(&mut self, ty: ComponentFuncTypeId) {
+        let ty = &self.types[ty];
         for ty in ty
             .params
             .iter()
@@ -1256,16 +1246,16 @@ impl DependencyRegistrar<'_, '_> {
         }
     }
 
-    fn defined(&mut self, ty: TypeId) {
-        match &self.types[ty].unwrap_defined() {
+    fn defined(&mut self, ty: ComponentDefinedTypeId) {
+        match &self.types[ty] {
             types::ComponentDefinedType::Primitive(_)
             | types::ComponentDefinedType::Enum(_)
             | types::ComponentDefinedType::Flags(_) => {}
             types::ComponentDefinedType::List(t) | types::ComponentDefinedType::Option(t) => {
                 self.val_type(*t)
             }
-            types::ComponentDefinedType::Own(t) | types::ComponentDefinedType::Borrow(t) => {
-                self.ty(*t)
+            types::ComponentDefinedType::Own(_r) | types::ComponentDefinedType::Borrow(_r) => {
+                unimplemented!()
             }
             types::ComponentDefinedType::Record(r) => {
                 for (_, ty) in r.fields.iter() {
@@ -1461,7 +1451,7 @@ impl<'a> CompositionGraphEncoder<'a> {
                 if let ComponentEntityType::Type { created, .. } = ty {
                     state
                         .cur
-                        .add_type_export((PtrKey(component), *created), name);
+                        .add_type_export((PtrKey(component), (*created).into()), name);
                 }
             }
         }
diff --git a/crates/wasm-compose/src/graph.rs b/crates/wasm-compose/src/graph.rs
index 1f025c1e..cc9e3f12 100644
--- a/crates/wasm-compose/src/graph.rs
+++ b/crates/wasm-compose/src/graph.rs
@@ -10,7 +10,7 @@ use std::{
     sync::atomic::{AtomicUsize, Ordering},
 };
 use wasmparser::{
-    types::{ComponentEntityType, TypeId, Types, TypesRef},
+    types::{ComponentEntityType, ComponentInstanceTypeId, Types, TypesRef},
     Chunk, ComponentExternalKind, ComponentTypeRef, Encoding, Parser, Payload, ValidPayload,
     Validator, WasmFeatures,
 };
@@ -277,7 +277,7 @@ impl<'a> Component<'a> {
     /// Finds a compatible instance export on the component for the given instance type.
     pub(crate) fn find_compatible_export(
         &self,
-        ty: TypeId,
+        ty: ComponentInstanceTypeId,
         types: TypesRef,
     ) -> Option<ExportIndex> {
         self.exports
@@ -298,8 +298,12 @@ impl<'a> Component<'a> {
 
     /// Checks to see if an instance of this component would be a
     /// subtype of the given instance type.
-    pub(crate) fn is_instance_subtype_of(&self, ty: TypeId, types: TypesRef) -> bool {
-        let exports = types[ty].unwrap_component_instance().exports.iter();
+    pub(crate) fn is_instance_subtype_of(
+        &self,
+        ty: ComponentInstanceTypeId,
+        types: TypesRef,
+    ) -> bool {
+        let exports = types[ty].exports.iter();
 
         for (k, b) in exports {
             match self.exports.get_full(k.as_str()) {
diff --git a/crates/wasmparser/src/readers/component/types.rs b/crates/wasmparser/src/readers/component/types.rs
index b33b3cd5..9a38ae7d 100644
--- a/crates/wasmparser/src/readers/component/types.rs
+++ b/crates/wasmparser/src/readers/component/types.rs
@@ -1,7 +1,7 @@
 use crate::limits::*;
 use crate::{
     BinaryReader, ComponentAlias, ComponentExternName, ComponentImport, ComponentTypeRef,
-    FromReader, FuncType, Import, Result, SectionLimited, SubType, TypeRef, ValType,
+    FromReader, Import, Result, SectionLimited, SubType, TypeRef, ValType,
 };
 use std::fmt;
 
@@ -15,21 +15,28 @@ pub enum OuterAliasKind {
 /// Represents a core type in a WebAssembly component.
 #[derive(Debug, Clone)]
 pub enum CoreType<'a> {
-    /// The type is for a core function.
-    Func(FuncType),
+    /// The type is for a core subtype.
+    Sub(SubType),
     /// The type is for a core module.
     Module(Box<[ModuleTypeDeclaration<'a>]>),
 }
 
 impl<'a> FromReader<'a> for CoreType<'a> {
     fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
-        Ok(match reader.read_u8()? {
-            0x60 => CoreType::Func(reader.read()?),
-            0x50 => CoreType::Module(
-                reader
-                    .read_iter(MAX_WASM_MODULE_TYPE_DECLS, "module type declaration")?
-                    .collect::<Result<_>>()?,
+        Ok(match reader.peek()? {
+            0x60 => CoreType::Sub(reader.read()?),
+            0x5e | 0x5f => bail!(
+                reader.current_position(),
+                "no support for GC types in the component model yet"
             ),
+            0x50 => {
+                reader.read_u8()?;
+                CoreType::Module(
+                    reader
+                        .read_iter(MAX_WASM_MODULE_TYPE_DECLS, "module type declaration")?
+                        .collect::<Result<_>>()?,
+                )
+            }
             x => return reader.invalid_leading_byte(x, "core type"),
         })
     }
diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs
index 87c00029..53e6aa61 100644
--- a/crates/wasmparser/src/readers/core/types.rs
+++ b/crates/wasmparser/src/readers/core/types.rs
@@ -760,6 +760,16 @@ pub enum StructuralType {
     Struct(StructType),
 }
 
+impl StructuralType {
+    /// Unwrap a `FuncType` or panic.
+    pub fn unwrap_func(&self) -> &FuncType {
+        match self {
+            Self::Func(f) => f,
+            _ => panic!("not a func"),
+        }
+    }
+}
+
 /// Represents a subtype of possible other types in a WebAssembly module.
 #[derive(Debug, Clone)]
 pub struct SubType {
@@ -771,6 +781,13 @@ pub struct SubType {
     pub structural_type: StructuralType,
 }
 
+impl SubType {
+    /// Unwrap a `FuncType` or panic.
+    pub fn unwrap_func(&self) -> &FuncType {
+        self.structural_type.unwrap_func()
+    }
+}
+
 /// Represents a recursive type group in a WebAssembly module.
 #[derive(Debug, Clone)]
 pub enum RecGroup {
diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs
index 11182473..c64233f8 100644
--- a/crates/wasmparser/src/validator.rs
+++ b/crates/wasmparser/src/validator.rs
@@ -621,7 +621,7 @@ impl Validator {
             Order::Type,
             section,
             "type",
-            |state, _, types, count, offset| {
+            |state, _, _types, count, offset| {
                 check_max(
                     state.module.types.len(),
                     count,
@@ -629,7 +629,6 @@ impl Validator {
                     "types",
                     offset,
                 )?;
-                types.reserve(count as usize);
                 state.module.assert_mut().types.reserve(count as usize);
                 Ok(())
             },
@@ -1035,10 +1034,9 @@ impl Validator {
         self.process_component_section(
             section,
             "core type",
-            |components, types, count, offset| {
+            |components, _types, count, offset| {
                 let current = components.last_mut().unwrap();
                 check_max(current.type_count(), count, MAX_WASM_TYPES, "types", offset)?;
-                types.reserve(count as usize);
                 current.core_types.reserve(count as usize);
                 Ok(())
             },
@@ -1131,10 +1129,9 @@ impl Validator {
         self.process_component_section(
             section,
             "type",
-            |components, types, count, offset| {
+            |components, _types, count, offset| {
                 let current = components.last_mut().unwrap();
                 check_max(current.type_count(), count, MAX_WASM_TYPES, "types", offset)?;
-                types.reserve(count as usize);
                 current.types.reserve(count as usize);
                 Ok(())
             },
@@ -1215,7 +1212,7 @@ impl Validator {
             &f.arguments,
             f.results,
             &self.features,
-            &self.types,
+            &mut self.types,
             range.start,
         )
     }
@@ -1468,19 +1465,27 @@ mod tests {
         assert_eq!(types.memory_count(), 1);
         assert_eq!(types.table_count(), 1);
         assert_eq!(types.global_count(), 1);
-        assert_eq!(types.function_count(), 1);
+        assert_eq!(types.core_function_count(), 1);
         assert_eq!(types.tag_count(), 1);
         assert_eq!(types.element_count(), 1);
         assert_eq!(types.module_count(), 0);
         assert_eq!(types.component_count(), 0);
-        assert_eq!(types.instance_count(), 0);
+        assert_eq!(types.core_instance_count(), 0);
         assert_eq!(types.value_count(), 0);
 
-        let ty = types[types.core_type_at(0)].unwrap_func();
+        let id = match types.core_type_at(0) {
+            crate::types::ComponentCoreTypeId::Sub(s) => s,
+            crate::types::ComponentCoreTypeId::Module(_) => panic!(),
+        };
+        let ty = types[id].unwrap_func();
         assert_eq!(ty.params(), [ValType::I32, ValType::I64]);
         assert_eq!(ty.results(), [ValType::I32]);
 
-        let ty = types[types.core_type_at(1)].unwrap_func();
+        let id = match types.core_type_at(1) {
+            crate::types::ComponentCoreTypeId::Sub(s) => s,
+            crate::types::ComponentCoreTypeId::Module(_) => panic!(),
+        };
+        let ty = types[id].unwrap_func();
         assert_eq!(ty.params(), [ValType::I64, ValType::I32]);
         assert_eq!(ty.results(), []);
 
@@ -1511,7 +1516,7 @@ mod tests {
             }
         );
 
-        let id = types.function_at(0);
+        let id = types.core_function_at(0);
         let ty = types[id].unwrap_func();
         assert_eq!(ty.params(), [ValType::I32, ValType::I64]);
         assert_eq!(ty.results(), [ValType::I32]);
@@ -1545,9 +1550,9 @@ mod tests {
 
         let types = validator.validate_all(&bytes)?;
 
-        let t_id = types.component_type_at(0);
-        let a1_id = types.component_type_at(1);
-        let a2_id = types.component_type_at(2);
+        let t_id = types.component_defined_type_at(0);
+        let a1_id = types.component_defined_type_at(1);
+        let a2_id = types.component_defined_type_at(2);
 
         // The ids should all be the same
         assert!(t_id == a1_id);
@@ -1580,9 +1585,9 @@ mod tests {
 
         let types = validator.validate_all(&bytes)?;
 
-        let t_id = types.component_type_at(0);
-        let a1_id = types.component_type_at(1);
-        let a2_id = types.component_type_at(2);
+        let t_id = types.component_defined_type_at(0);
+        let a1_id = types.component_defined_type_at(1);
+        let a2_id = types.component_defined_type_at(2);
 
         // The ids should all be the same
         assert!(t_id != a1_id);
diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs
index f2363b82..8550bf28 100644
--- a/crates/wasmparser/src/validator/component.rs
+++ b/crates/wasmparser/src/validator/component.rs
@@ -4,17 +4,19 @@ use super::{
     check_max,
     core::Module,
     types::{
-        ComponentFuncType, ComponentInstanceType, ComponentType, ComponentValType, EntityType,
-        InstanceType, ModuleType, RecordType, Remapping, ResourceId, Type, TypeAlloc, TypeId,
-        TypeList, VariantCase,
+        AliasableResourceId, ComponentCoreInstanceTypeId, ComponentDefinedTypeId,
+        ComponentFuncType, ComponentFuncTypeId, ComponentInstanceType, ComponentInstanceTypeId,
+        ComponentType, ComponentTypeId, ComponentValType, CoreTypeId, EntityType, InstanceType,
+        ModuleType, RecordType, Remapping, ResourceId, TypeAlloc, TypeList, VariantCase,
     },
 };
 use crate::validator::names::{KebabName, KebabNameKind, KebabStr, KebabString};
 use crate::{
     limits::*,
     types::{
-        ComponentDefinedType, ComponentEntityType, Context, InstanceTypeKind, LoweringInfo, Remap,
-        SubtypeCx, TupleType, TypeInfo, VariantType,
+        ComponentAnyTypeId, ComponentCoreModuleTypeId, ComponentCoreTypeId, ComponentDefinedType,
+        ComponentEntityType, Context, CoreInstanceTypeKind, LoweringInfo, Remap, SubtypeCx,
+        TupleType, TypeInfo, VariantType,
     },
     BinaryReaderError, CanonicalOption, ComponentExternName, ComponentExternalKind,
     ComponentOuterAliasKind, ComponentTypeRef, ExternalKind, FuncType, GlobalType,
@@ -44,21 +46,21 @@ pub(crate) struct ComponentState {
     kind: ComponentKind,
 
     // Core index spaces
-    pub core_types: Vec<TypeId>,
-    pub core_modules: Vec<TypeId>,
-    pub core_instances: Vec<TypeId>,
-    pub core_funcs: Vec<TypeId>,
+    pub core_types: Vec<ComponentCoreTypeId>,
+    pub core_funcs: Vec<CoreTypeId>,
+    pub core_tags: Vec<CoreTypeId>,
+    pub core_modules: Vec<ComponentCoreModuleTypeId>,
+    pub core_instances: Vec<ComponentCoreInstanceTypeId>,
     pub core_memories: Vec<MemoryType>,
     pub core_tables: Vec<TableType>,
     pub core_globals: Vec<GlobalType>,
-    pub core_tags: Vec<TypeId>,
 
     // Component index spaces
-    pub types: Vec<TypeId>,
-    pub funcs: Vec<TypeId>,
+    pub types: Vec<ComponentAnyTypeId>,
+    pub funcs: Vec<ComponentFuncTypeId>,
     pub values: Vec<(ComponentValType, bool)>,
-    pub instances: Vec<TypeId>,
-    pub components: Vec<TypeId>,
+    pub instances: Vec<ComponentInstanceTypeId>,
+    pub components: Vec<ComponentTypeId>,
 
     pub imports: IndexMap<String, ComponentEntityType>,
     pub exports: IndexMap<String, ComponentEntityType>,
@@ -149,10 +151,10 @@ pub(crate) struct ComponentState {
     ///
     /// This set is consulted whenever an exported item is added since all
     /// referenced types must be members of this set.
-    exported_types: HashSet<TypeId>,
+    exported_types: HashSet<ComponentAnyTypeId>,
 
     /// Same as `exported_types`, but for imports.
-    imported_types: HashSet<TypeId>,
+    imported_types: HashSet<ComponentAnyTypeId>,
 
     /// The set of top-level resource exports and their names.
     ///
@@ -182,7 +184,7 @@ pub enum ComponentKind {
 struct KebabNameContext {
     /// A map from a resource type id to an index in the `all_resource_names`
     /// set for the name of that resource.
-    resource_name_map: HashMap<TypeId, usize>,
+    resource_name_map: HashMap<AliasableResourceId, usize>,
 
     /// All known resource names in this context, used to validate static method
     /// names to by ensuring that static methods' resource names are somewhere
@@ -257,30 +259,29 @@ impl ComponentState {
         offset: usize,
         check_limit: bool,
     ) -> Result<()> {
-        let ty = match ty {
-            crate::CoreType::Func(ty) => Type::Sub(SubType {
-                is_final: false,
-                supertype_idx: None,
-                structural_type: StructuralType::Func(ty),
-            }),
-            crate::CoreType::Module(decls) => Type::Module(Box::new(Self::create_module_type(
-                components,
-                decls.into_vec(),
-                features,
-                types,
-                offset,
-            )?)),
+        let id = match ty {
+            crate::CoreType::Sub(sub) => {
+                let id = types.push_ty(sub);
+                ComponentCoreTypeId::Sub(id)
+            }
+            crate::CoreType::Module(decls) => {
+                let mod_ty = Self::create_module_type(
+                    components,
+                    decls.into_vec(),
+                    features,
+                    types,
+                    offset,
+                )?;
+                let id = types.push_ty(mod_ty);
+                ComponentCoreTypeId::Module(id)
+            }
         };
 
         let current = components.last_mut().unwrap();
-
         if check_limit {
             check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?;
         }
-
-        let id = types.push_ty(ty);
         current.core_types.push(id);
-
         Ok(())
     }
 
@@ -295,14 +296,14 @@ impl ComponentState {
         // We have to clone the module's imports and exports here
         // because we cannot take the data out of the `MaybeOwned`
         // as it might be shared with a function validator.
-        let ty = Type::Module(Box::new(ModuleType {
+        let mod_ty = ModuleType {
             info: TypeInfo::core(module.type_size),
             imports,
             exports: module.exports.clone(),
-        }));
+        };
 
-        let id = types.push_ty(ty);
-        self.core_modules.push(id);
+        let mod_id = types.push_ty(mod_ty);
+        self.core_modules.push(mod_id);
 
         Ok(())
     }
@@ -315,7 +316,7 @@ impl ComponentState {
     ) -> Result<()> {
         let instance = match instance {
             crate::Instance::Instantiate { module_index, args } => {
-                self.instantiate_module(module_index, args.into_vec(), types, offset)?
+                self.instantiate_core_module(module_index, args.into_vec(), types, offset)?
             }
             crate::Instance::FromExports(exports) => {
                 self.instantiate_core_exports(exports.into_vec(), types, offset)?
@@ -336,27 +337,42 @@ impl ComponentState {
         check_limit: bool,
     ) -> Result<()> {
         assert!(!components.is_empty());
-        let ty = match ty {
-            crate::ComponentType::Defined(ty) => Type::Defined(
-                components
-                    .last_mut()
-                    .unwrap()
-                    .create_defined_type(ty, types, offset)?,
-            ),
-            crate::ComponentType::Func(ty) => Type::ComponentFunc(
-                components
-                    .last_mut()
-                    .unwrap()
-                    .create_function_type(ty, types, offset)?,
-            ),
-            crate::ComponentType::Component(decls) => Type::Component(Box::new(
-                Self::create_component_type(components, decls.into_vec(), features, types, offset)?,
-            )),
-            crate::ComponentType::Instance(decls) => Type::ComponentInstance(Box::new(
-                Self::create_instance_type(components, decls.into_vec(), features, types, offset)?,
-            )),
+
+        fn current(components: &mut Vec<ComponentState>) -> &mut ComponentState {
+            components.last_mut().unwrap()
+        }
+
+        let id = match ty {
+            crate::ComponentType::Defined(ty) => {
+                let ty = current(components).create_defined_type(ty, types, offset)?;
+                types.push(ty).into()
+            }
+            crate::ComponentType::Func(ty) => {
+                let ty = current(components).create_function_type(ty, types, offset)?;
+                types.push(ty).into()
+            }
+            crate::ComponentType::Component(decls) => {
+                let ty = Self::create_component_type(
+                    components,
+                    decls.into_vec(),
+                    features,
+                    types,
+                    offset,
+                )?;
+                types.push(ty).into()
+            }
+            crate::ComponentType::Instance(decls) => {
+                let ty = Self::create_instance_type(
+                    components,
+                    decls.into_vec(),
+                    features,
+                    types,
+                    offset,
+                )?;
+                types.push(ty).into()
+            }
             crate::ComponentType::Resource { rep, dtor } => {
-                let component = components.last_mut().unwrap();
+                let component = current(components);
 
                 // Resource types cannot be declared in a type context, only
                 // within a component context.
@@ -376,7 +392,7 @@ impl ComponentState {
                 // function and has the correct signature.
                 if let Some(dtor) = dtor {
                     let ty = component.core_function_at(dtor, offset)?;
-                    let ty = types[ty].unwrap_func();
+                    let ty = types[ty].structural_type.unwrap_func();
                     if ty.params() != [rep] || ty.results() != [] {
                         bail!(
                             offset,
@@ -391,17 +407,15 @@ impl ComponentState {
                 // rep listed to enable getting access to various intrinsics
                 // such as `resource.rep`.
                 let id = types.alloc_resource_id();
-                component.defined_resources.insert(id, Some(rep));
-                Type::Resource(id)
+                component.defined_resources.insert(id.resource(), Some(rep));
+                id.into()
             }
         };
 
-        let current = components.last_mut().unwrap();
+        let current = current(components);
         if check_limit {
             check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?;
         }
-
-        let id = types.push_ty(ty);
         current.types.push(id);
 
         Ok(())
@@ -485,7 +499,7 @@ impl ComponentState {
                 // Note that if `created` is the same as `referenced` then this
                 // is the original introduction of the resource which is where
                 // `self.{imported,defined}_resources` are updated.
-                if let Type::Resource(id) = types[*created] {
+                if let ComponentAnyTypeId::Resource(id) = *created {
                     match kind {
                         Some(ExternKind::Import) => {
                             // A fresh new resource is being imported into a
@@ -495,7 +509,8 @@ impl ComponentState {
                             // resource is injected with a fresh new identifier
                             // into our state.
                             if created == referenced {
-                                self.imported_resources.insert(id, vec![self.imports.len()]);
+                                self.imported_resources
+                                    .insert(id.resource(), vec![self.imports.len()]);
                             }
                         }
 
@@ -509,14 +524,15 @@ impl ComponentState {
                             // Notably the representation of this new resource
                             // is unknown so it's listed as `None`.
                             if created == referenced {
-                                self.defined_resources.insert(id, None);
+                                self.defined_resources.insert(id.resource(), None);
                             }
 
                             // If this is a type export of a resource type then
                             // update the `explicit_resources` list. A new
                             // export path is about to be created for this
                             // resource and this keeps track of that.
-                            self.explicit_resources.insert(id, vec![self.exports.len()]);
+                            self.explicit_resources
+                                .insert(id.resource(), vec![self.exports.len()]);
                         }
 
                         None => {}
@@ -569,12 +585,12 @@ impl ComponentState {
             // appropriate context so later validation of method-like-names
             // works out.
             if let Some(name) = toplevel_name {
-                if let Type::Resource(_) = types[*created] {
+                if let ComponentAnyTypeId::Resource(id) = *created {
                     let cx = match kind {
                         ExternKind::Import => &mut self.toplevel_imported_resources,
                         ExternKind::Export => &mut self.toplevel_exported_resources,
                     };
-                    cx.register(name, *created);
+                    cx.register(name, id);
                 }
             }
         }
@@ -628,15 +644,13 @@ impl ComponentState {
             // itself are valid to import/export, recursive instances are
             // captured, and everything is appropriately added to the right
             // imported/exported set.
-            ComponentEntityType::Instance(i) => {
-                let ty = types[*i].unwrap_component_instance();
-                ty.exports
-                    .values()
-                    .all(|ty| self.validate_and_register_named_types(None, kind, ty, types))
-            }
+            ComponentEntityType::Instance(i) => types[*i]
+                .exports
+                .iter()
+                .all(|(_name, ty)| self.validate_and_register_named_types(None, kind, ty, types)),
 
             // All types referred to by a function must be named.
-            ComponentEntityType::Func(id) => self.all_valtypes_named(types, *id, set),
+            ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, *id, set),
 
             ComponentEntityType::Value(ty) => types.type_named_valtype(ty, set),
 
@@ -645,88 +659,119 @@ impl ComponentState {
             ComponentEntityType::Component(_) | ComponentEntityType::Module(_) => true,
         }
     }
-    fn all_valtypes_named(&self, types: &TypeAlloc, id: TypeId, set: &HashSet<TypeId>) -> bool {
-        match &types[id] {
-            Type::Defined(i) => match i {
-                // These types do not contain anything which must be
-                // named.
-                ComponentDefinedType::Primitive(_)
-                | ComponentDefinedType::Flags(_)
-                | ComponentDefinedType::Enum(_) => true,
-
-                // Referenced types of all these aggregates must all be
-                // named.
-                ComponentDefinedType::Record(r) => {
-                    r.fields.values().all(|t| types.type_named_valtype(t, set))
-                }
-                ComponentDefinedType::Tuple(r) => {
-                    r.types.iter().all(|t| types.type_named_valtype(t, set))
-                }
-                ComponentDefinedType::Variant(r) => r
-                    .cases
-                    .values()
-                    .filter_map(|t| t.ty.as_ref())
-                    .all(|t| types.type_named_valtype(t, set)),
-                ComponentDefinedType::Result { ok, err } => {
-                    ok.as_ref()
-                        .map(|t| types.type_named_valtype(t, set))
-                        .unwrap_or(true)
-                        && err
-                            .as_ref()
-                            .map(|t| types.type_named_valtype(t, set))
-                            .unwrap_or(true)
-                }
-                ComponentDefinedType::List(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) => {
-                    set.contains(id)
-                }
-            },
-
-            // Core wasm constructs are always valid with respect to
-            // exported types, since they have none.
-            Type::Module(_) | Type::Instance(_) | Type::Sub(_) => true,
-
-            // Resource types, in isolation, are always valid to import
-            // or export since they're either attached to an import or
-            // being exported.
+    fn all_valtypes_named(
+        &self,
+        types: &TypeAlloc,
+        id: ComponentAnyTypeId,
+        set: &HashSet<ComponentAnyTypeId>,
+    ) -> bool {
+        match id {
+            // Resource types, in isolation, are always valid to import or
+            // export since they're either attached to an import or being
+            // exported.
             //
-            // Note that further validation of this happens in `finish`,
-            // too.
-            Type::Resource(_) => true,
+            // Note that further validation of this happens in `finish`, too.
+            ComponentAnyTypeId::Resource(_) => true,
 
             // Component types are validated as they are constructed,
             // so all component types are valid to export if they've
             // already been constructed.
-            Type::Component(_) => true,
+            ComponentAnyTypeId::Component(_) => true,
 
-            // Function types must have all their parameters/results named.
-            Type::ComponentFunc(ty) => ty
-                .params
-                .iter()
-                .map(|(_, ty)| ty)
-                .chain(ty.results.iter().map(|(_, ty)| ty))
-                .all(|ty| types.type_named_valtype(ty, set)),
-
-            // Instances must recursively have all referenced types named.
-            Type::ComponentInstance(ty) => ty.exports.values().all(|ty| {
-                let id = match ty {
-                    ComponentEntityType::Module(id)
-                    | ComponentEntityType::Func(id)
-                    | ComponentEntityType::Value(ComponentValType::Type(id))
-                    | ComponentEntityType::Type { created: id, .. }
-                    | ComponentEntityType::Instance(id)
-                    | ComponentEntityType::Component(id) => *id,
-                    ComponentEntityType::Value(ComponentValType::Primitive(_)) => return true,
-                };
-                self.all_valtypes_named(types, id, set)
-            }),
+            ComponentAnyTypeId::Defined(id) => self.all_valtypes_named_in_defined(types, id, set),
+            ComponentAnyTypeId::Func(id) => self.all_valtypes_named_in_func(types, id, set),
+            ComponentAnyTypeId::Instance(id) => self.all_valtypes_named_in_instance(types, id, set),
         }
     }
 
+    fn all_valtypes_named_in_instance(
+        &self,
+        types: &TypeAlloc,
+        id: ComponentInstanceTypeId,
+        set: &HashSet<ComponentAnyTypeId>,
+    ) -> bool {
+        // Instances must recursively have all referenced types named.
+        let ty = &types[id];
+        ty.exports.values().all(|ty| match ty {
+            ComponentEntityType::Module(_) => true,
+            ComponentEntityType::Func(id) => self.all_valtypes_named_in_func(types, *id, set),
+            ComponentEntityType::Type { created: id, .. } => {
+                self.all_valtypes_named(types, *id, set)
+            }
+            ComponentEntityType::Value(ComponentValType::Type(id)) => {
+                self.all_valtypes_named_in_defined(types, *id, set)
+            }
+            ComponentEntityType::Instance(id) => {
+                self.all_valtypes_named_in_instance(types, *id, set)
+            }
+            ComponentEntityType::Component(_)
+            | ComponentEntityType::Value(ComponentValType::Primitive(_)) => return true,
+        })
+    }
+
+    fn all_valtypes_named_in_defined(
+        &self,
+        types: &TypeAlloc,
+        id: ComponentDefinedTypeId,
+        set: &HashSet<ComponentAnyTypeId>,
+    ) -> bool {
+        let ty = &types[id];
+        match ty {
+            // These types do not contain anything which must be
+            // named.
+            ComponentDefinedType::Primitive(_)
+            | ComponentDefinedType::Flags(_)
+            | ComponentDefinedType::Enum(_) => true,
+
+            // Referenced types of all these aggregates must all be
+            // named.
+            ComponentDefinedType::Record(r) => {
+                r.fields.values().all(|t| types.type_named_valtype(t, set))
+            }
+            ComponentDefinedType::Tuple(r) => {
+                r.types.iter().all(|t| types.type_named_valtype(t, set))
+            }
+            ComponentDefinedType::Variant(r) => r
+                .cases
+                .values()
+                .filter_map(|t| t.ty.as_ref())
+                .all(|t| types.type_named_valtype(t, set)),
+            ComponentDefinedType::Result { ok, err } => {
+                ok.as_ref()
+                    .map(|t| types.type_named_valtype(t, set))
+                    .unwrap_or(true)
+                    && err
+                        .as_ref()
+                        .map(|t| types.type_named_valtype(t, set))
+                        .unwrap_or(true)
+            }
+            ComponentDefinedType::List(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) => {
+                set.contains(&(*id).into())
+            }
+        }
+    }
+
+    fn all_valtypes_named_in_func(
+        &self,
+        types: &TypeAlloc,
+        id: ComponentFuncTypeId,
+        set: &HashSet<ComponentAnyTypeId>,
+    ) -> bool {
+        let ty = &types[id];
+        // Function types must have all their parameters/results named.
+        ty.params
+            .iter()
+            .map(|(_, ty)| ty)
+            .chain(ty.results.iter().map(|(_, ty)| ty))
+            .all(|ty| types.type_named_valtype(ty, set))
+    }
+
     /// Updates the type `id` specified, an identifier for a component instance
     /// type, to be imported into this component.
     ///
@@ -740,8 +785,8 @@ impl ComponentState {
     /// type is used twice to import two different instances then the instances
     /// do not share resource types despite sharing the same original instance
     /// type.
-    fn prepare_instance_import(&mut self, id: &mut TypeId, types: &mut TypeAlloc) {
-        let ty = types[*id].unwrap_component_instance();
+    fn prepare_instance_import(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) {
+        let ty = &types[*id];
 
         // No special treatment for imports of instances which themselves have
         // no defined resources
@@ -775,14 +820,14 @@ impl ComponentState {
         // path for where they're imported is updated as well with
         // `self.next_import_index` as the import-to-be soon.
         let mut mapping = Remapping::default();
-        let ty = types[*id].unwrap_component_instance();
+        let ty = &types[*id];
         for (old, new) in ty.defined_resources.iter().zip(&resources) {
-            let prev = mapping.resources.insert(*old, *new);
+            let prev = mapping.resources.insert(*old, new.resource());
             assert!(prev.is_none());
 
             let mut base = vec![self.imports.len()];
             base.extend(ty.explicit_resources[old].iter().copied());
-            self.imported_resources.insert(*new, base);
+            self.imported_resources.insert(new.resource(), base);
         }
 
         // Using the old-to-new resource mapping perform a substitution on
@@ -797,7 +842,7 @@ impl ComponentState {
 
         // Now that `new_ty` is complete finish its registration and then
         // update `id` on the way out.
-        *id = types.push_ty(Type::ComponentInstance(Box::new(new_ty)));
+        *id = types.push_ty(new_ty);
     }
 
     /// Prepares an instance type, pointed to `id`, for being exported as a
@@ -806,13 +851,13 @@ impl ComponentState {
     /// This will internally perform any resource "freshening" as required and
     /// then additionally update metadata within `self` about resources being
     /// exported or defined.
-    fn prepare_instance_export(&mut self, id: &mut TypeId, types: &mut TypeAlloc) {
+    fn prepare_instance_export(&mut self, id: &mut ComponentInstanceTypeId, types: &mut TypeAlloc) {
         // Exports of an instance mean that the enclosing context
         // is inheriting the resources that the instance
         // encapsulates. This means that the instance type
         // recorded for this export will itself have no
         // defined resources.
-        let ty = types[*id].unwrap_component_instance();
+        let ty = &types[*id];
 
         // Check to see if `defined_resources` is non-empty, and if so then
         // "freshen" all the resources and inherit them to our own defined
@@ -838,8 +883,8 @@ impl ComponentState {
             let mut mapping = Remapping::default();
             for old in mem::take(&mut new_ty.defined_resources) {
                 let new = types.alloc_resource_id();
-                mapping.resources.insert(old, new);
-                self.defined_resources.insert(new, None);
+                mapping.resources.insert(old, new.resource());
+                self.defined_resources.insert(new.resource(), None);
             }
             for ty in new_ty.exports.values_mut() {
                 types.remap_component_entity(ty, &mut mapping);
@@ -848,7 +893,7 @@ impl ComponentState {
                 let id = mapping.resources.get(&id).copied().unwrap_or(id);
                 new_ty.explicit_resources.insert(id, path);
             }
-            *id = types.push_ty(Type::ComponentInstance(Box::new(new_ty)));
+            *id = types.push_ty(new_ty);
         }
 
         // Any explicit resources in the instance are now additionally explicit
@@ -857,7 +902,7 @@ impl ComponentState {
         // The path to each explicit resources gets one element prepended which
         // is `self.next_export_index`, the index of the export about to be
         // generated.
-        let ty = types[*id].unwrap_component_instance();
+        let ty = &types[*id];
         for (id, path) in ty.explicit_resources.iter() {
             let mut new_path = vec![self.exports.len()];
             new_path.extend(path);
@@ -933,7 +978,8 @@ impl ComponentState {
             );
         }
 
-        self.funcs.push(self.types[type_index as usize]);
+        self.funcs
+            .push(self.types[type_index as usize].unwrap_func());
 
         Ok(())
     }
@@ -945,7 +991,7 @@ impl ComponentState {
         types: &mut TypeAlloc,
         offset: usize,
     ) -> Result<()> {
-        let ty = types[self.function_at(func_index, offset)?].unwrap_component_func();
+        let ty = &types[self.function_at(func_index, offset)?];
 
         // Lowering a function is for an import, so use a function type that matches
         // the expected canonical ABI import signature.
@@ -953,11 +999,11 @@ impl ComponentState {
 
         self.check_options(None, &info, &options, types, offset)?;
 
-        let lowered_ty = Type::Sub(SubType {
+        let lowered_ty = SubType {
             is_final: false,
             supertype_idx: None,
             structural_type: StructuralType::Func(info.into_func_type()),
-        });
+        };
 
         let id = types.push_ty(lowered_ty);
         self.core_funcs.push(id);
@@ -972,11 +1018,11 @@ impl ComponentState {
         offset: usize,
     ) -> Result<()> {
         let rep = self.check_local_resource(resource, types, offset)?;
-        let core_ty = Type::Sub(SubType {
+        let core_ty = SubType {
             is_final: false,
             supertype_idx: None,
             structural_type: StructuralType::Func(FuncType::new([rep], [ValType::I32])),
-        });
+        };
         self.core_funcs.push(types.push_ty(core_ty));
         Ok(())
     }
@@ -988,11 +1034,11 @@ impl ComponentState {
         offset: usize,
     ) -> Result<()> {
         self.resource_at(resource, types, offset)?;
-        let core_ty = Type::Sub(SubType {
+        let core_ty = SubType {
             is_final: false,
             supertype_idx: None,
             structural_type: StructuralType::Func(FuncType::new([ValType::I32], [])),
-        });
+        };
         self.core_funcs.push(types.push_ty(core_ty));
         Ok(())
     }
@@ -1004,35 +1050,41 @@ impl ComponentState {
         offset: usize,
     ) -> Result<()> {
         let rep = self.check_local_resource(resource, types, offset)?;
-        let core_ty = Type::Sub(SubType {
+        let core_ty = SubType {
             is_final: false,
             supertype_idx: None,
             structural_type: StructuralType::Func(FuncType::new([ValType::I32], [rep])),
-        });
+        };
         self.core_funcs.push(types.push_ty(core_ty));
         Ok(())
     }
 
     fn check_local_resource(&self, idx: u32, types: &TypeList, offset: usize) -> Result<ValType> {
-        let id = self.resource_at(idx, types, offset)?;
-        let resource = types[id].unwrap_resource();
-        match self.defined_resources.get(&resource).and_then(|rep| *rep) {
+        let resource = self.resource_at(idx, types, offset)?;
+        match self
+            .defined_resources
+            .get(&resource.resource())
+            .and_then(|rep| *rep)
+        {
             Some(ty) => Ok(ty),
             None => bail!(offset, "type {idx} is not a local resource"),
         }
     }
 
-    fn resource_at<'a>(&self, idx: u32, types: &'a TypeList, offset: usize) -> Result<TypeId> {
-        let id = self.type_at(idx, false, offset)?;
-        match &types[id] {
-            Type::Resource(_) => Ok(id),
-            _ => bail!(offset, "type index {} is not a resource type", idx),
+    fn resource_at<'a>(
+        &self,
+        idx: u32,
+        _types: &'a TypeList,
+        offset: usize,
+    ) -> Result<AliasableResourceId> {
+        if let ComponentAnyTypeId::Resource(id) = self.component_type_at(idx, offset)? {
+            return Ok(id);
         }
+        bail!(offset, "type index {} is not a resource type", idx)
     }
 
     pub fn add_component(&mut self, component: ComponentType, types: &mut TypeAlloc) -> Result<()> {
-        let ty = Type::Component(Box::new(component));
-        let id = types.push_ty(ty);
+        let id = types.push_ty(component);
         self.components.push(id);
         Ok(())
     }
@@ -1056,7 +1108,7 @@ impl ComponentState {
                 offset,
             )?,
             crate::ComponentInstance::FromExports(exports) => {
-                self.instantiate_exports(exports.into_vec(), features, types, offset)?
+                self.instantiate_component_exports(exports.into_vec(), features, types, offset)?
             }
         };
 
@@ -1119,7 +1171,7 @@ impl ComponentState {
         args: &[u32],
         results: u32,
         features: &WasmFeatures,
-        types: &TypeList,
+        types: &mut TypeList,
         offset: usize,
     ) -> Result<()> {
         if !features.component_model_values {
@@ -1135,7 +1187,7 @@ impl ComponentState {
             ));
         }
 
-        let ft = types[self.function_at(func_index, offset)?].unwrap_component_func();
+        let ft = &types[self.function_at(func_index, offset)?];
 
         if ft.params.len() != args.len() {
             bail!(
@@ -1306,33 +1358,33 @@ impl ComponentState {
     ) -> Result<ComponentEntityType> {
         Ok(match ty {
             ComponentTypeRef::Module(index) => {
-                let id = self.type_at(*index, true, offset)?;
-                match &types[id] {
-                    Type::Module(_) => {}
-                    _ => bail!(offset, "core type index {index} is not a module type"),
+                let id = self.core_type_at(*index, offset)?;
+                match id {
+                    ComponentCoreTypeId::Sub(_) => {
+                        bail!(offset, "core type index {index} is not a module type")
+                    }
+                    ComponentCoreTypeId::Module(id) => ComponentEntityType::Module(id),
                 }
-                ComponentEntityType::Module(id)
             }
             ComponentTypeRef::Func(index) => {
-                let id = self.type_at(*index, false, offset)?;
-                match &types[id] {
-                    Type::ComponentFunc(_) => {}
+                let id = self.component_type_at(*index, offset)?;
+                match id {
+                    ComponentAnyTypeId::Func(id) => ComponentEntityType::Func(id),
                     _ => bail!(offset, "type index {index} is not a function type"),
                 }
-                ComponentEntityType::Func(id)
             }
             ComponentTypeRef::Value(ty) => {
                 self.check_value_support(features, offset)?;
                 let ty = match ty {
                     crate::ComponentValType::Primitive(ty) => ComponentValType::Primitive(*ty),
                     crate::ComponentValType::Type(index) => {
-                        ComponentValType::Type(self.defined_type_at(*index, types, offset)?)
+                        ComponentValType::Type(self.defined_type_at(*index, offset)?)
                     }
                 };
                 ComponentEntityType::Value(ty)
             }
             ComponentTypeRef::Type(TypeBounds::Eq(index)) => {
-                let referenced = self.type_at(*index, false, offset)?;
+                let referenced = self.component_type_at(*index, offset)?;
                 let created = types.with_unique(referenced);
                 ComponentEntityType::Type {
                     referenced,
@@ -1341,27 +1393,24 @@ impl ComponentState {
             }
             ComponentTypeRef::Type(TypeBounds::SubResource) => {
                 let id = types.alloc_resource_id();
-                let id = types.push_ty(Type::Resource(id));
                 ComponentEntityType::Type {
-                    referenced: id,
-                    created: id,
+                    referenced: id.into(),
+                    created: id.into(),
                 }
             }
             ComponentTypeRef::Instance(index) => {
-                let id = self.type_at(*index, false, offset)?;
-                match &types[id] {
-                    Type::ComponentInstance(_) => {}
+                let id = self.component_type_at(*index, offset)?;
+                match id {
+                    ComponentAnyTypeId::Instance(id) => ComponentEntityType::Instance(id),
                     _ => bail!(offset, "type index {index} is not an instance type"),
                 }
-                ComponentEntityType::Instance(id)
             }
             ComponentTypeRef::Component(index) => {
-                let id = self.type_at(*index, false, offset)?;
-                match &types[id] {
-                    Type::Component(_) => {}
+                let id = self.component_type_at(*index, offset)?;
+                match id {
+                    ComponentAnyTypeId::Component(id) => ComponentEntityType::Component(id),
                     _ => bail!(offset, "type index {index} is not a component type"),
                 }
-                ComponentEntityType::Component(id)
             }
         })
     }
@@ -1385,7 +1434,7 @@ impl ComponentState {
                 ComponentEntityType::Value(*self.value_at(export.index, offset)?)
             }
             ComponentExternalKind::Type => {
-                let referenced = self.type_at(export.index, false, offset)?;
+                let referenced = self.component_type_at(export.index, offset)?;
                 let created = types.with_unique(referenced);
                 ComponentEntityType::Type {
                     referenced,
@@ -1441,17 +1490,25 @@ impl ComponentState {
                         crate::OuterAliasKind::Type => {
                             let ty = if count == 0 {
                                 // Local alias, check the local module state
-                                state.type_id_at(index, offset)?
+                                ComponentCoreTypeId::Sub(state.type_id_at(index, offset)?)
                             } else {
                                 // Otherwise, check the enclosing component state
                                 let component =
                                     Self::check_alias_count(components, count - 1, offset)?;
-                                component.type_at(index, true, offset)?
+                                component.core_type_at(index, offset)?
                             };
 
                             check_max(state.types.len(), 1, MAX_WASM_TYPES, "types", offset)?;
 
-                            state.types.push(ty);
+                            match ty {
+                                ComponentCoreTypeId::Sub(ty) => state.types.push(ty),
+                                // TODO https://github.com/WebAssembly/component-model/issues/265
+                                ComponentCoreTypeId::Module(_) => bail!(
+                                    offset,
+                                    "not implemented: aliasing core module types into a core \
+                                     module's types index space"
+                                ),
+                            }
                         }
                     }
                 }
@@ -1591,7 +1648,7 @@ impl ComponentState {
                     );
                 }
 
-                let ty = self.create_component_val_type(*ty, types, offset)?;
+                let ty = self.create_component_val_type(*ty, offset)?;
                 info.combine(ty.info(types), offset)?;
                 Ok((name.to_owned(), ty))
             })
@@ -1618,7 +1675,7 @@ impl ComponentState {
                     })
                     .transpose()?;
 
-                let ty = self.create_component_val_type(*ty, types, offset)?;
+                let ty = self.create_component_val_type(*ty, offset)?;
                 let ty_info = ty.info(types);
                 if ty_info.contains_borrow() {
                     bail!(offset, "function result cannot contain a `borrow` type");
@@ -1635,13 +1692,13 @@ impl ComponentState {
         })
     }
 
-    fn instantiate_module(
+    fn instantiate_core_module(
         &self,
         module_index: u32,
         module_args: Vec<crate::InstantiationArg>,
         types: &mut TypeAlloc,
         offset: usize,
-    ) -> Result<TypeId> {
+    ) -> Result<ComponentCoreInstanceTypeId> {
         fn insert_arg<'a>(
             name: &'a str,
             arg: &'a InstanceType,
@@ -1665,15 +1722,14 @@ impl ComponentState {
         for module_arg in module_args {
             match module_arg.kind {
                 InstantiationArgKind::Instance => {
-                    let instance_type =
-                        types[self.core_instance_at(module_arg.index, offset)?].unwrap_instance();
+                    let instance_type = &types[self.core_instance_at(module_arg.index, offset)?];
                     insert_arg(module_arg.name, instance_type, &mut args, offset)?;
                 }
             }
         }
 
         // Validate the arguments
-        let module_type = types[module_type_id].unwrap_module();
+        let module_type = &types[module_type_id];
         let cx = SubtypeCx::new(types, types);
         for ((module, name), expected) in module_type.imports.iter() {
             let instance = args.get(module.as_str()).ok_or_else(|| {
@@ -1707,12 +1763,10 @@ impl ComponentState {
             info.combine(ty.info(types), offset)?;
         }
 
-        let ty = Type::Instance(Box::new(InstanceType {
+        Ok(types.push_ty(InstanceType {
             info,
-            kind: InstanceTypeKind::Instantiated(module_type_id),
-        }));
-
-        Ok(types.push_ty(ty))
+            kind: CoreInstanceTypeKind::Instantiated(module_type_id),
+        }))
     }
 
     fn instantiate_component(
@@ -1722,7 +1776,7 @@ impl ComponentState {
         features: &WasmFeatures,
         types: &mut TypeAlloc,
         offset: usize,
-    ) -> Result<TypeId> {
+    ) -> Result<ComponentInstanceTypeId> {
         let component_type_id = self.component_at(component_index, offset)?;
         let mut args = IndexMap::new();
 
@@ -1746,7 +1800,7 @@ impl ComponentState {
                     ComponentEntityType::Value(*self.value_at(component_arg.index, offset)?)
                 }
                 ComponentExternalKind::Type => {
-                    let ty = self.type_at(component_arg.index, false, offset)?;
+                    let ty = self.component_type_at(component_arg.index, offset)?;
                     ComponentEntityType::Type {
                         referenced: ty,
                         created: ty,
@@ -1847,7 +1901,7 @@ impl ComponentState {
         // comments are hopefully enough when augmented with communication with
         // the authors.
 
-        let component_type = types[component_type_id].unwrap_component();
+        let component_type = &types[component_type_id];
         let mut exports = component_type.exports.clone();
         let mut info = TypeInfo::new();
         for (_, ty) in component_type.exports.iter() {
@@ -1890,9 +1944,9 @@ impl ComponentState {
         // placing everything in the same `mapping` and using that for a remap
         // only once.
         let fresh_defined_resources = (0..component_type.defined_resources.len())
-            .map(|_| types.alloc_resource_id())
+            .map(|_| types.alloc_resource_id().resource())
             .collect::<IndexSet<_>>();
-        let component_type = types[component_type_id].unwrap_component();
+        let component_type = &types[component_type_id];
         for ((old, _path), new) in component_type
             .defined_resources
             .iter()
@@ -1915,7 +1969,7 @@ impl ComponentState {
         for entity in exports.values_mut() {
             types.remap_component_entity(entity, &mut mapping);
         }
-        let component_type = types[component_type_id].unwrap_component();
+        let component_type = &types[component_type_id];
         let explicit_resources = component_type
             .explicit_resources
             .iter()
@@ -1976,22 +2030,21 @@ impl ComponentState {
             self.defined_resources.insert(resource, None);
         }
 
-        let ty = Type::ComponentInstance(Box::new(ComponentInstanceType {
+        Ok(types.push_ty(ComponentInstanceType {
             info,
             defined_resources: Default::default(),
             explicit_resources,
             exports,
-        }));
-        Ok(types.push_ty(ty))
+        }))
     }
 
-    fn instantiate_exports(
+    fn instantiate_component_exports(
         &mut self,
         exports: Vec<crate::ComponentExport>,
         features: &WasmFeatures,
         types: &mut TypeAlloc,
         offset: usize,
-    ) -> Result<TypeId> {
+    ) -> Result<ComponentInstanceTypeId> {
         let mut info = TypeInfo::new();
         let mut inst_exports = IndexMap::new();
         let mut explicit_resources = IndexMap::new();
@@ -2018,17 +2071,13 @@ impl ComponentState {
                     // all explicitly exported resources on the sub-instance are
                     // now also listed as exported resources on the outer
                     // instance, just with one more element in their path.
-                    explicit_resources.extend(
-                        types[ty]
-                            .unwrap_component_instance()
-                            .explicit_resources
-                            .iter()
-                            .map(|(id, path)| {
-                                let mut new_path = vec![inst_exports.len()];
-                                new_path.extend(path);
-                                (*id, new_path)
-                            }),
-                    );
+                    explicit_resources.extend(types[ty].explicit_resources.iter().map(
+                        |(id, path)| {
+                            let mut new_path = vec![inst_exports.len()];
+                            new_path.extend(path);
+                            (*id, new_path)
+                        },
+                    ));
                     ComponentEntityType::Instance(ty)
                 }
                 ComponentExternalKind::Func => {
@@ -2039,13 +2088,13 @@ impl ComponentState {
                     ComponentEntityType::Value(*self.value_at(export.index, offset)?)
                 }
                 ComponentExternalKind::Type => {
-                    let ty = self.type_at(export.index, false, offset)?;
+                    let ty = self.component_type_at(export.index, offset)?;
                     // If this is an export of a resource type be sure to
                     // record that in the explicit list with the appropriate
                     // path because if this instance ends up getting used
                     // it'll count towards the "explicit in" check.
-                    if let Type::Resource(id) = &types[ty] {
-                        explicit_resources.insert(*id, vec![inst_exports.len()]);
+                    if let ComponentAnyTypeId::Resource(id) = ty {
+                        explicit_resources.insert(id.resource(), vec![inst_exports.len()]);
                     }
                     ComponentEntityType::Type {
                         referenced: ty,
@@ -2071,7 +2120,7 @@ impl ComponentState {
             )?;
         }
 
-        let ty = Type::ComponentInstance(Box::new(ComponentInstanceType {
+        Ok(types.push_ty(ComponentInstanceType {
             info,
             explicit_resources,
             exports: inst_exports,
@@ -2105,9 +2154,7 @@ impl ComponentState {
             // time, though, so this still remains empty and
             // `self.defined_resources` remains unperturbed.
             defined_resources: Default::default(),
-        }));
-
-        Ok(types.push_ty(ty))
+        }))
     }
 
     fn instantiate_core_exports(
@@ -2115,7 +2162,7 @@ impl ComponentState {
         exports: Vec<crate::Export>,
         types: &mut TypeAlloc,
         offset: usize,
-    ) -> Result<TypeId> {
+    ) -> Result<ComponentCoreInstanceTypeId> {
         fn insert_export(
             types: &TypeList,
             name: &str,
@@ -2187,12 +2234,10 @@ impl ComponentState {
             }
         }
 
-        let ty = Type::Instance(Box::new(InstanceType {
+        Ok(types.push_ty(InstanceType {
             info,
-            kind: InstanceTypeKind::Exports(inst_exports),
-        }));
-
-        Ok(types.push_ty(ty))
+            kind: CoreInstanceTypeKind::Exports(inst_exports),
+        }))
     }
 
     fn alias_core_instance_export(
@@ -2276,7 +2321,6 @@ impl ComponentState {
             self.check_value_support(features, offset)?;
         }
         let mut ty = match types[self.instance_at(instance_index, offset)?]
-            .unwrap_component_instance()
             .exports
             .get(name)
         {
@@ -2359,7 +2403,7 @@ impl ComponentState {
         offset: usize,
     ) -> Result<()> {
         let component = Self::check_alias_count(components, count, offset)?;
-        let ty = component.type_at(index, true, offset)?;
+        let ty = component.core_type_at(index, offset)?;
 
         let current = components.last_mut().unwrap();
         check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?;
@@ -2377,7 +2421,7 @@ impl ComponentState {
         offset: usize,
     ) -> Result<()> {
         let component = Self::check_alias_count(components, count, offset)?;
-        let ty = component.type_at(index, false, offset)?;
+        let ty = component.component_type_at(index, offset)?;
 
         // If `count` "crossed a component boundary", meaning that it went from
         // one component to another, then this must additionally verify that
@@ -2405,7 +2449,7 @@ impl ComponentState {
         if let Some(component) = components.get(pos_after_component) {
             if component.kind == ComponentKind::Component {
                 let mut free = IndexSet::new();
-                types.free_variables_type_id(ty, &mut free);
+                types.free_variables_any_type_id(ty, &mut free);
                 if !free.is_empty() {
                     bail!(
                         offset,
@@ -2448,7 +2492,7 @@ impl ComponentState {
                 self.create_variant_type(cases.as_ref(), types, offset)
             }
             crate::ComponentDefinedType::List(ty) => Ok(ComponentDefinedType::List(
-                self.create_component_val_type(ty, types, offset)?,
+                self.create_component_val_type(ty, offset)?,
             )),
             crate::ComponentDefinedType::Tuple(tys) => {
                 self.create_tuple_type(tys.as_ref(), types, offset)
@@ -2460,14 +2504,14 @@ impl ComponentState {
                 self.create_enum_type(cases.as_ref(), offset)
             }
             crate::ComponentDefinedType::Option(ty) => Ok(ComponentDefinedType::Option(
-                self.create_component_val_type(ty, types, offset)?,
+                self.create_component_val_type(ty, offset)?,
             )),
             crate::ComponentDefinedType::Result { ok, err } => Ok(ComponentDefinedType::Result {
                 ok: ok
-                    .map(|ty| self.create_component_val_type(ty, types, offset))
+                    .map(|ty| self.create_component_val_type(ty, offset))
                     .transpose()?,
                 err: err
-                    .map(|ty| self.create_component_val_type(ty, types, offset))
+                    .map(|ty| self.create_component_val_type(ty, offset))
                     .transpose()?,
             }),
             crate::ComponentDefinedType::Own(idx) => Ok(ComponentDefinedType::Own(
@@ -2494,7 +2538,7 @@ impl ComponentState {
 
         for (name, ty) in fields {
             let name = to_kebab_str(name, "record field", offset)?;
-            let ty = self.create_component_val_type(*ty, types, offset)?;
+            let ty = self.create_component_val_type(*ty, offset)?;
 
             match field_map.entry(name.to_owned()) {
                 Entry::Occupied(e) => bail!(
@@ -2549,7 +2593,7 @@ impl ComponentState {
 
             let ty = case
                 .ty
-                .map(|ty| self.create_component_val_type(ty, types, offset))
+                .map(|ty| self.create_component_val_type(ty, offset))
                 .transpose()?;
 
             match case_map.entry(name.to_owned()) {
@@ -2595,7 +2639,7 @@ impl ComponentState {
         let types = tys
             .iter()
             .map(|ty| {
-                let ty = self.create_component_val_type(*ty, types, offset)?;
+                let ty = self.create_component_val_type(*ty, offset)?;
                 info.combine(ty.info(types), offset)?;
                 Ok(ty)
             })
@@ -2656,20 +2700,25 @@ impl ComponentState {
     fn create_component_val_type(
         &self,
         ty: crate::ComponentValType,
-        types: &TypeList,
         offset: usize,
     ) -> Result<ComponentValType> {
         Ok(match ty {
             crate::ComponentValType::Primitive(pt) => ComponentValType::Primitive(pt),
             crate::ComponentValType::Type(idx) => {
-                ComponentValType::Type(self.defined_type_at(idx, types, offset)?)
+                ComponentValType::Type(self.defined_type_at(idx, offset)?)
             }
         })
     }
 
-    pub fn type_at(&self, idx: u32, core: bool, offset: usize) -> Result<TypeId> {
-        let types = if core { &self.core_types } else { &self.types };
-        types
+    pub fn core_type_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreTypeId> {
+        self.core_types
+            .get(idx as usize)
+            .copied()
+            .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
+    }
+
+    pub fn component_type_at(&self, idx: u32, offset: usize) -> Result<ComponentAnyTypeId> {
+        self.types
             .get(idx as usize)
             .copied()
             .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
@@ -2681,13 +2730,14 @@ impl ComponentState {
         types: &'a TypeList,
         offset: usize,
     ) -> Result<&'a ComponentFuncType> {
-        match &types[self.type_at(idx, false, offset)?] {
-            Type::ComponentFunc(f) => Ok(f),
+        let id = self.component_type_at(idx, offset)?;
+        match id {
+            ComponentAnyTypeId::Func(id) => Ok(&types[id]),
             _ => bail!(offset, "type index {idx} is not a function type"),
         }
     }
 
-    fn function_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
+    fn function_at(&self, idx: u32, offset: usize) -> Result<ComponentFuncTypeId> {
         self.funcs.get(idx as usize).copied().ok_or_else(|| {
             format_err!(
                 offset,
@@ -2696,7 +2746,7 @@ impl ComponentState {
         })
     }
 
-    fn component_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
+    fn component_at(&self, idx: u32, offset: usize) -> Result<ComponentTypeId> {
         self.components.get(idx as usize).copied().ok_or_else(|| {
             format_err!(
                 offset,
@@ -2705,7 +2755,7 @@ impl ComponentState {
         })
     }
 
-    fn instance_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
+    fn instance_at(&self, idx: u32, offset: usize) -> Result<ComponentInstanceTypeId> {
         self.instances.get(idx as usize).copied().ok_or_else(|| {
             format_err!(
                 offset,
@@ -2725,15 +2775,14 @@ impl ComponentState {
         }
     }
 
-    fn defined_type_at(&self, idx: u32, types: &TypeList, offset: usize) -> Result<TypeId> {
-        let id = self.type_at(idx, false, offset)?;
-        match &types[id] {
-            Type::Defined(_) => Ok(id),
-            _ => bail!(offset, "type index {} is not a defined type", idx),
+    fn defined_type_at(&self, idx: u32, offset: usize) -> Result<ComponentDefinedTypeId> {
+        match self.component_type_at(idx, offset)? {
+            ComponentAnyTypeId::Defined(id) => Ok(id),
+            _ => bail!(offset, "type index {idx} is not a defined type"),
         }
     }
 
-    fn core_function_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
+    fn core_function_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> {
         match self.core_funcs.get(idx as usize) {
             Some(id) => Ok(*id),
             None => bail!(
@@ -2743,14 +2792,14 @@ impl ComponentState {
         }
     }
 
-    fn module_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
+    fn module_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreModuleTypeId> {
         match self.core_modules.get(idx as usize) {
             Some(id) => Ok(*id),
             None => bail!(offset, "unknown module {idx}: module index out of bounds"),
         }
     }
 
-    fn core_instance_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
+    fn core_instance_at(&self, idx: u32, offset: usize) -> Result<ComponentCoreInstanceTypeId> {
         match self.core_instances.get(idx as usize) {
             Some(id) => Ok(*id),
             None => bail!(
@@ -2768,7 +2817,6 @@ impl ComponentState {
         offset: usize,
     ) -> Result<&'a EntityType> {
         match types[self.core_instance_at(instance_index, offset)?]
-            .unwrap_instance()
             .internal_exports(types)
             .get(name)
         {
@@ -2914,10 +2962,13 @@ impl ComponentState {
 
 impl KebabNameContext {
     /// Registers that the resource `id` is named `name` within this context.
-    fn register(&mut self, name: &str, id: TypeId) {
+    fn register(&mut self, name: &str, id: AliasableResourceId) {
         let idx = self.all_resource_names.len();
         let prev = self.resource_name_map.insert(id, idx);
-        assert!(prev.is_none());
+        assert!(
+            prev.is_none(),
+            "for {id:?}, inserted {idx:?} but already had {prev:?}"
+        );
         self.all_resource_names.insert(name.to_string());
     }
 
@@ -2988,7 +3039,7 @@ impl KebabNameContext {
                 ComponentEntityType::Func(id) => *id,
                 _ => bail!(offset, "item is not a func"),
             };
-            Ok(types[id].unwrap_component_func())
+            Ok(&types[id])
         };
         match name.kind() {
             // Normal kebab name or id? No validation necessary.
@@ -3005,7 +3056,7 @@ impl KebabNameContext {
                 let resource = match ty {
                     ComponentValType::Primitive(_) => None,
                     ComponentValType::Type(ty) => match &types[ty] {
-                        Type::Defined(ComponentDefinedType::Own(id)) => Some(id),
+                        ComponentDefinedType::Own(id) => Some(id),
                         _ => None,
                     },
                 };
@@ -3034,7 +3085,7 @@ impl KebabNameContext {
                 let id = match pty {
                     ComponentValType::Primitive(_) => None,
                     ComponentValType::Type(ty) => match &types[*ty] {
-                        Type::Defined(ComponentDefinedType::Borrow(id)) => Some(id),
+                        ComponentDefinedType::Borrow(id) => Some(id),
                         _ => None,
                     },
                 };
@@ -3062,7 +3113,12 @@ impl KebabNameContext {
         Ok(())
     }
 
-    fn validate_resource_name(&self, id: TypeId, name: &KebabStr, offset: usize) -> Result<()> {
+    fn validate_resource_name(
+        &self,
+        id: AliasableResourceId,
+        name: &KebabStr,
+        offset: usize,
+    ) -> Result<()> {
         let expected_name_idx = match self.resource_name_map.get(&id) {
             Some(idx) => *idx,
             None => {
diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs
index c63a81c5..46fee3e7 100644
--- a/crates/wasmparser/src/validator/core.rs
+++ b/crates/wasmparser/src/validator/core.rs
@@ -18,7 +18,7 @@ use crate::{
 use super::{
     check_max, combine_type_sizes,
     operators::{ty_to_str, OperatorValidator, OperatorValidatorAllocations},
-    types::{EntityType, Type, TypeAlloc, TypeId, TypeList},
+    types::{CoreTypeId, EntityType, TypeAlloc, TypeIdentifier, TypeList},
 };
 
 // Section order for WebAssembly modules.
@@ -478,7 +478,7 @@ pub(crate) struct Module {
     // enable parallel validation of functions.
     pub snapshot: Option<Arc<TypeList>>,
     // Stores indexes into the validator's types list.
-    pub types: Vec<TypeId>,
+    pub types: Vec<CoreTypeId>,
     pub tables: Vec<TableType>,
     pub memories: Vec<MemoryType>,
     pub globals: Vec<GlobalType>,
@@ -486,7 +486,7 @@ pub(crate) struct Module {
     pub data_count: Option<u32>,
     // Stores indexes into `types`.
     pub functions: Vec<u32>,
-    pub tags: Vec<TypeId>,
+    pub tags: Vec<CoreTypeId>,
     pub function_references: HashSet<u32>,
     pub imports: IndexMap<(String, String), Vec<EntityType>>,
     pub exports: IndexMap<String, EntityType>,
@@ -525,7 +525,7 @@ impl Module {
             .types()
             .iter()
             .map(|ty| {
-                let id = types.push_ty(Type::Sub(ty.clone()));
+                let id = types.push_ty(ty.clone());
                 if features.gc {
                     // make types in a rec group resolvable by index before validation:
                     // this is needed to support recursive types in the GC proposal
@@ -536,7 +536,7 @@ impl Module {
             .collect();
 
         for (id, ty) in idx_types {
-            self.check_subtype(id.index() as u32, ty.clone(), features, types, offset)?;
+            self.check_subtype(id.index() as u32, &ty, features, types, offset)?;
             if !features.gc {
                 self.types.push(id);
             }
@@ -547,11 +547,11 @@ impl Module {
     fn check_subtype(
         &mut self,
         type_index: u32,
-        ty: SubType,
+        ty: &SubType,
         features: &WasmFeatures,
         types: &mut TypeAlloc,
         offset: usize,
-    ) -> Result<Type> {
+    ) -> Result<()> {
         if !features.gc && (!ty.is_final || ty.supertype_idx.is_some()) {
             bail!(offset, "gc proposal must be enabled to use subtypes");
         }
@@ -566,29 +566,13 @@ impl Module {
                     "unknown type {type_index}: type index out of bounds"
                 );
             }
-            match self.type_at(types, supertype_index, offset)? {
-                Type::Sub(st) => {
-                    if !&ty.matches(st, &|idx| self.subtype_at(types, idx, offset).unwrap()) {
-                        bail!(offset, "subtype must match supertype");
-                    }
-                }
-                _ => {
-                    bail!(offset, "supertype must be a non-final subtype itself");
-                }
-            };
+            let sub = self.sub_type_at(types, supertype_index, offset)?;
+            if !&ty.matches(sub, &|idx| self.sub_type_at(types, idx, offset).unwrap()) {
+                bail!(offset, "subtype must match supertype");
+            }
         }
 
-        Ok(Type::Sub(ty))
-    }
-
-    fn subtype_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> {
-        match self.type_at(types, idx, offset)? {
-            Type::Sub(ty) => Ok(ty),
-            _ => bail!(
-                offset,
-                "subtype with index {idx} not found, offset: {offset}"
-            ),
-        }
+        Ok(())
     }
 
     fn check_structural_type(
@@ -754,15 +738,16 @@ impl Module {
         Ok(())
     }
 
-    pub fn type_id_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
+    pub fn type_id_at(&self, idx: u32, offset: usize) -> Result<CoreTypeId> {
         self.types
             .get(idx as usize)
             .copied()
             .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds"))
     }
 
-    fn type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a Type> {
-        self.type_id_at(idx, offset).map(|type_id| &types[type_id])
+    fn sub_type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> {
+        let id = self.type_id_at(idx, offset)?;
+        Ok(&types[id])
     }
 
     fn func_type_at<'a>(
@@ -771,11 +756,8 @@ impl Module {
         types: &'a TypeList,
         offset: usize,
     ) -> Result<&'a FuncType> {
-        match &types[self.type_id_at(type_index, offset)?] {
-            Type::Sub(SubType {
-                structural_type: StructuralType::Func(f),
-                ..
-            }) => Ok(f),
+        match &self.sub_type_at(types, type_index, offset)?.structural_type {
+            StructuralType::Func(f) => Ok(f),
             _ => bail!(offset, "type index {type_index} is not a function type"),
         }
     }
@@ -958,7 +940,7 @@ impl Module {
     /// E.g. a non-nullable reference can be assigned to a nullable reference, but not vice versa.
     /// Or an indexed func ref is assignable to a generic func ref, but not vice versa.
     pub(crate) fn matches(&self, ty1: ValType, ty2: ValType, types: &TypeList) -> bool {
-        ty1.matches(&ty2, &|idx| self.subtype_at(types, idx, 0).unwrap())
+        ty1.matches(&ty2, &|idx| self.sub_type_at(types, idx, 0).unwrap())
     }
 
     fn check_tag_type(
diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs
index 83537bb2..eec81143 100644
--- a/crates/wasmparser/src/validator/types.rs
+++ b/crates/wasmparser/src/validator/types.rs
@@ -6,9 +6,8 @@ use super::{
 };
 use crate::validator::names::KebabString;
 use crate::{
-    ArrayType, BinaryReaderError, Export, ExternalKind, FuncType, GlobalType, Import, MemoryType,
-    PrimitiveValType, RefType, Result, StructType, StructuralType, SubType, TableType, TypeRef,
-    ValType,
+    BinaryReaderError, Export, ExternalKind, FuncType, GlobalType, Import, MemoryType,
+    PrimitiveValType, RefType, Result, StructuralType, SubType, TableType, TypeRef, ValType,
 };
 use indexmap::{IndexMap, IndexSet};
 use std::collections::HashMap;
@@ -140,34 +139,533 @@ fn push_primitive_wasm_types(ty: &PrimitiveValType, lowered_types: &mut LoweredT
     }
 }
 
-/// Represents a unique identifier for a type known to a [`crate::Validator`].
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-#[repr(C)] // use fixed field layout to ensure minimal size
-pub struct TypeId {
-    /// The index into the global list of types.
-    index: u32,
+/// A trait shared by all type identifiers.
+///
+/// Any id that can be used to get a type from a `Types`.
+//
+// Or, internally, from a `TypeList`.
+pub trait TypeIdentifier: std::fmt::Debug + Copy + Eq + Sized + 'static {
+    /// The data pointed to by this type of id.
+    type Data: TypeData<Id = Self>;
+
+    /// Create a type id from an index.
+    #[doc(hidden)]
+    fn from_index(index: u32) -> Self;
+
+    /// Get a shared reference to the list where this id's type data is stored
+    /// within.
+    #[doc(hidden)]
+    fn list(types: &TypeList) -> &SnapshotList<Self::Data>;
+
+    /// Get an exclusive reference to the list where this id's type data is
+    /// stored within.
+    #[doc(hidden)]
+    fn list_mut(types: &mut TypeList) -> &mut SnapshotList<Self::Data>;
+
+    /// The raw index of this id.
+    #[doc(hidden)]
+    fn index(&self) -> usize;
+}
+
+/// A trait shared by all types within a `Types`.
+///
+/// This is the data that can be retreived by indexing with the associated
+/// [`TypeIdentifier`].
+pub trait TypeData: std::fmt::Debug {
+    /// The identifier for this type data.
+    type Id: TypeIdentifier<Data = Self>;
+
+    /// Get the info for this type.
+    #[doc(hidden)]
+    fn type_info(&self, types: &TypeList) -> TypeInfo;
+}
+
+/// A type that can be aliased in the component model.
+pub trait Aliasable {
+    #[doc(hidden)]
+    fn alias_id(&self) -> u32;
+
+    #[doc(hidden)]
+    fn set_alias_id(&mut self, alias_id: u32);
+}
+
+/// A fresh alias id that means the entity is not an alias of anything.
+///
+/// Note that the `TypeList::alias_counter` starts at zero, so we can't use that
+/// as this sentinel. The implementation limits are such that we can't ever
+/// generate `u32::MAX` aliases, so we don't need to worryabout running into
+/// this value in practice either.
+const NO_ALIAS: u32 = u32::MAX;
+
+macro_rules! define_type_id {
+    ($name:ident, $data:ty, $list:ident, $type_str:expr) => {
+        #[doc = "Represents a unique identifier for a "]
+        #[doc = $type_str]
+        #[doc = " type known to a [`crate::Validator`]."]
+        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+        #[repr(C)] // Use fixed field layout to ensure minimal size.
+        pub struct $name {
+            /// The index into the associated list of types.
+            index: u32,
+        }
+
+        impl TypeIdentifier for $name {
+            type Data = $data;
+
+            fn from_index(index: u32) -> Self {
+                $name { index }
+            }
+
+            fn list(types: &TypeList) -> &SnapshotList<Self::Data> {
+                &types.$list
+            }
+
+            fn list_mut(types: &mut TypeList) -> &mut SnapshotList<Self::Data> {
+                &mut types.$list
+            }
+
+            fn index(&self) -> usize {
+                usize::try_from(self.index).unwrap()
+            }
+        }
+
+        impl Aliasable for $name {
+            fn alias_id(&self) -> u32 {
+                NO_ALIAS
+            }
+
+            fn set_alias_id(&mut self, _: u32) {}
+        }
 
-    /// A unique integer assigned to this type.
+        // The size of type IDs was seen to have a large-ish impact in #844, so
+        // this assert ensures that it stays relatively small.
+        const _: () = {
+            assert!(std::mem::size_of::<$name>() <= 4);
+        };
+    };
+}
+
+/// A core WebAssembly type, in the core WebAssembly types index space.
+pub enum CoreType {
+    /// A sub type.
+    Sub(SubType),
+
+    /// A module type.
     ///
-    /// The purpose of this field is to ensure that two different `TypeId`
-    /// representations can be handed out for two different aliased types within
-    /// a component that actually point to the same underlying type (as pointed
-    /// to by the `index` field).
-    unique_id: u32,
+    /// Does not actually appear in core Wasm at the moment. Only used for the
+    /// core types index space within components.
+    Module(ModuleType),
 }
 
-impl TypeId {
-    pub(crate) fn index(&self) -> usize {
+/// Represents a unique identifier for a core type type known to a
+/// [`crate::Validator`]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[repr(C)]
+pub struct CoreTypeId {
+    index: u32,
+}
+
+const _: () = {
+    assert!(std::mem::size_of::<CoreTypeId>() <= 4);
+};
+
+impl TypeIdentifier for CoreTypeId {
+    type Data = SubType;
+
+    fn from_index(index: u32) -> Self {
+        CoreTypeId { index }
+    }
+
+    fn list(types: &TypeList) -> &SnapshotList<Self::Data> {
+        &types.core_types
+    }
+
+    fn list_mut(types: &mut TypeList) -> &mut SnapshotList<Self::Data> {
+        &mut types.core_types
+    }
+
+    fn index(&self) -> usize {
         usize::try_from(self.index).unwrap()
     }
 }
 
-// The size of `TypeId` was seen to have a large-ish impact in #844, so this
-// assert ensures that it stays relatively small.
+impl TypeData for SubType {
+    type Id = CoreTypeId;
+
+    fn type_info(&self, _types: &TypeList) -> TypeInfo {
+        // TODO(#1036): calculate actual size for func, array, struct.
+        let size = 1 + match &self.structural_type {
+            StructuralType::Func(ty) => 1 + (ty.params().len() + ty.results().len()) as u32,
+            StructuralType::Array(_) => 2,
+            StructuralType::Struct(ty) => 1 + 2 * ty.fields.len() as u32,
+        };
+        TypeInfo::core(size)
+    }
+}
+
+impl CoreType {
+    /// Get the underlying `SubType` or panic.
+    pub fn unwrap_sub(&self) -> &SubType {
+        match self {
+            CoreType::Sub(s) => s,
+            CoreType::Module(_) => panic!("`unwrap_sub` on module type"),
+        }
+    }
+
+    /// Get the underlying `FuncType` within this `SubType` or panic.
+    pub fn unwrap_func(&self) -> &FuncType {
+        match &self.unwrap_sub().structural_type {
+            StructuralType::Func(f) => f,
+            StructuralType::Array(_) | StructuralType::Struct(_) => {
+                panic!("`unwrap_func` on non-func structural type")
+            }
+        }
+    }
+
+    /// Get the underlying `ModuleType` or panic.
+    pub fn unwrap_module(&self) -> &ModuleType {
+        match self {
+            CoreType::Module(m) => m,
+            CoreType::Sub(_) => panic!("`unwrap_module` on a subtype"),
+        }
+    }
+}
+
+macro_rules! define_wrapper_id {
+    (
+        $(#[$outer_attrs:meta])*
+        pub enum $name:ident {
+            $(
+                #[unwrap = $unwrap:ident]
+                $(#[$inner_attrs:meta])*
+                $variant:ident ( $inner:ty ) ,
+            )*
+        }
+    ) => {
+        $(#[$outer_attrs])*
+        pub enum $name {
+            $(
+                $(#[$inner_attrs])*
+                $variant ( $inner ) ,
+            )*
+        }
+
+        $(
+            impl From<$inner> for $name {
+                #[inline]
+                fn from(x: $inner) -> Self {
+                    Self::$variant(x)
+                }
+            }
+
+            impl TryFrom<$name> for $inner {
+                type Error = ();
+
+                #[inline]
+                fn try_from(x: $name) -> Result<Self, Self::Error> {
+                    match x {
+                        $name::$variant(x) => Ok(x),
+                        _ => Err(())
+                    }
+                }
+            }
+        )*
+
+        impl $name {
+            $(
+                #[doc = "Unwrap a `"]
+                #[doc = stringify!($inner)]
+                #[doc = "` or panic."]
+                #[inline]
+                pub fn $unwrap(self) -> $inner {
+                    <$inner>::try_from(self).unwrap()
+                }
+            )*
+        }
+    };
+}
+
+macro_rules! define_transitive_conversions {
+    (
+        $(
+            $outer:ty,
+            $middle:ty,
+            $inner:ty,
+            $unwrap:ident;
+        )*
+    ) => {
+        $(
+            impl From<$inner> for $outer {
+                #[inline]
+                fn from(x: $inner) -> Self {
+                    <$middle>::from(x).into()
+                }
+            }
+
+            impl TryFrom<$outer> for $inner {
+                type Error = ();
+
+                #[inline]
+                fn try_from(x: $outer) -> Result<Self, Self::Error> {
+                    let middle = <$middle>::try_from(x)?;
+                    <$inner>::try_from(middle)
+                }
+            }
+
+            impl $outer {
+                #[doc = "Unwrap a `"]
+                #[doc = stringify!($inner)]
+                #[doc = "` or panic."]
+                #[inline]
+                pub fn $unwrap(self) -> $inner {
+                    <$inner>::try_from(self).unwrap()
+                }
+            }
+        )*
+    };
+}
+
+define_wrapper_id! {
+    /// An identifier pointing to any kind of type, component or core.
+    #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+    pub enum AnyTypeId {
+        #[unwrap = unwrap_component_core_type]
+        /// A core type.
+        Core(ComponentCoreTypeId),
+
+        #[unwrap = unwrap_component_any_type]
+        /// A component type.
+        Component(ComponentAnyTypeId),
+    }
+}
+
+define_transitive_conversions! {
+    AnyTypeId, ComponentCoreTypeId, CoreTypeId, unwrap_core_type;
+    AnyTypeId, ComponentCoreTypeId, ComponentCoreModuleTypeId, unwrap_component_core_module_type;
+    AnyTypeId, ComponentAnyTypeId, AliasableResourceId, unwrap_aliasable_resource;
+    AnyTypeId, ComponentAnyTypeId, ComponentDefinedTypeId, unwrap_component_defined_type;
+    AnyTypeId, ComponentAnyTypeId, ComponentFuncTypeId, unwrap_component_func_type;
+    AnyTypeId, ComponentAnyTypeId, ComponentInstanceTypeId, unwrap_component_instance_type;
+    AnyTypeId, ComponentAnyTypeId, ComponentTypeId, unwrap_component_type;
+}
+
+impl AnyTypeId {
+    /// Peel off one layer of aliasing from this type and return the aliased
+    /// inner type, or `None` if this type is not aliasing anything.
+    pub fn peel_alias(&self, types: &Types) -> Option<Self> {
+        match *self {
+            Self::Core(id) => id.peel_alias(types).map(Self::Core),
+            Self::Component(id) => types.peel_alias(id).map(Self::Component),
+        }
+    }
+}
+
+define_wrapper_id! {
+    /// An identifier for a core type or a core module's type.
+    #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+    pub enum ComponentCoreTypeId {
+        #[unwrap = unwrap_sub]
+        /// A core type.
+        Sub(CoreTypeId),
+
+        #[unwrap = unwrap_module]
+        /// A core module's type.
+        Module(ComponentCoreModuleTypeId),
+    }
+}
+
+impl ComponentCoreTypeId {
+    /// Peel off one layer of aliasing from this type and return the aliased
+    /// inner type, or `None` if this type is not aliasing anything.
+    pub fn peel_alias(&self, types: &Types) -> Option<Self> {
+        match *self {
+            Self::Sub(_) => None,
+            Self::Module(id) => types.peel_alias(id).map(Self::Module),
+        }
+    }
+}
+
+/// An aliasable resource identifier.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+pub struct AliasableResourceId {
+    id: ResourceId,
+    alias_id: u32,
+}
+
+impl Aliasable for AliasableResourceId {
+    fn alias_id(&self) -> u32 {
+        self.alias_id
+    }
+
+    fn set_alias_id(&mut self, alias_id: u32) {
+        self.alias_id = alias_id;
+    }
+}
+
+impl AliasableResourceId {
+    /// Get the underlying resource.
+    pub fn resource(&self) -> ResourceId {
+        self.id
+    }
+
+    pub(crate) fn resource_mut(&mut self) -> &mut ResourceId {
+        &mut self.id
+    }
+}
+
+define_wrapper_id! {
+    /// An identifier for any kind of component type.
+    #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+    pub enum ComponentAnyTypeId {
+        #[unwrap = unwrap_resource]
+        /// The type is a resource with the specified id.
+        Resource(AliasableResourceId),
+
+        #[unwrap = unwrap_defined]
+        /// The type is a defined type with the specified id.
+        Defined(ComponentDefinedTypeId),
+
+        #[unwrap = unwrap_func]
+        /// The type is a function type with the specified id.
+        Func(ComponentFuncTypeId),
+
+        #[unwrap = unwrap_instance]
+        /// The type is an instance type with the specified id.
+        Instance(ComponentInstanceTypeId),
+
+        #[unwrap = unwrap_component]
+        /// The type is a component type with the specified id.
+        Component(ComponentTypeId),
+    }
+}
+
+impl Aliasable for ComponentAnyTypeId {
+    fn alias_id(&self) -> u32 {
+        match self {
+            ComponentAnyTypeId::Resource(x) => x.alias_id(),
+            ComponentAnyTypeId::Defined(x) => x.alias_id(),
+            ComponentAnyTypeId::Func(x) => x.alias_id(),
+            ComponentAnyTypeId::Instance(x) => x.alias_id(),
+            ComponentAnyTypeId::Component(x) => x.alias_id(),
+        }
+    }
+
+    fn set_alias_id(&mut self, alias_id: u32) {
+        match self {
+            ComponentAnyTypeId::Resource(x) => x.set_alias_id(alias_id),
+            ComponentAnyTypeId::Defined(x) => x.set_alias_id(alias_id),
+            ComponentAnyTypeId::Func(x) => x.set_alias_id(alias_id),
+            ComponentAnyTypeId::Instance(x) => x.set_alias_id(alias_id),
+            ComponentAnyTypeId::Component(x) => x.set_alias_id(alias_id),
+        }
+    }
+}
+
+impl ComponentAnyTypeId {
+    pub(crate) fn info(&self, types: &TypeList) -> TypeInfo {
+        match *self {
+            Self::Resource(_) => TypeInfo::new(),
+            Self::Defined(id) => types[id].type_info(types),
+            Self::Func(id) => types[id].type_info(types),
+            Self::Instance(id) => types[id].type_info(types),
+            Self::Component(id) => types[id].type_info(types),
+        }
+    }
+
+    pub(crate) fn desc(&self) -> &'static str {
+        match self {
+            Self::Resource(_) => "resource",
+            Self::Defined(_) => "defined type",
+            Self::Func(_) => "func",
+            Self::Instance(_) => "instance",
+            Self::Component(_) => "component",
+        }
+    }
+}
+
+define_type_id!(ComponentTypeId, ComponentType, components, "component");
+
+define_type_id!(
+    ComponentValueTypeId,
+    ComponentValType,
+    component_values,
+    "component value"
+);
+
+define_type_id!(
+    ComponentInstanceTypeId,
+    ComponentInstanceType,
+    component_instances,
+    "component instance"
+);
+
+define_type_id!(
+    ComponentFuncTypeId,
+    ComponentFuncType,
+    component_funcs,
+    "component function"
+);
+
+define_type_id!(
+    ComponentCoreInstanceTypeId,
+    InstanceType,
+    core_instances,
+    "component's core instance"
+);
+
+define_type_id!(
+    ComponentCoreModuleTypeId,
+    ModuleType,
+    core_modules,
+    "component's core module"
+);
+
+/// Represents a unique identifier for a component type type known to a
+/// [`crate::Validator`].
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[repr(C)]
+pub struct ComponentDefinedTypeId {
+    index: u32,
+    alias_id: u32,
+}
+
 const _: () = {
-    assert!(std::mem::size_of::<TypeId>() <= 8);
+    assert!(std::mem::size_of::<ComponentDefinedTypeId>() <= 8);
 };
 
+impl TypeIdentifier for ComponentDefinedTypeId {
+    type Data = ComponentDefinedType;
+
+    fn from_index(index: u32) -> Self {
+        ComponentDefinedTypeId {
+            index,
+            alias_id: NO_ALIAS,
+        }
+    }
+
+    fn list(types: &TypeList) -> &SnapshotList<Self::Data> {
+        &types.component_defined_types
+    }
+
+    fn list_mut(types: &mut TypeList) -> &mut SnapshotList<Self::Data> {
+        &mut types.component_defined_types
+    }
+
+    fn index(&self) -> usize {
+        usize::try_from(self.index).unwrap()
+    }
+}
+
+impl Aliasable for ComponentDefinedTypeId {
+    fn alias_id(&self) -> u32 {
+        self.alias_id
+    }
+
+    fn set_alias_id(&mut self, alias_id: u32) {
+        self.alias_id = alias_id;
+    }
+}
+
 /// Metadata about a type and its transitive structure.
 ///
 /// Currently contains two properties:
@@ -191,7 +689,9 @@ const _: () = {
 /// used. Currently this uses the low 24 bits for the type size and the MSB for
 /// the borrow bit.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub(crate) struct TypeInfo(u32);
+// Only public because it shows up in a public trait's `doc(hidden)` method.
+#[doc(hidden)]
+pub struct TypeInfo(u32);
 
 impl TypeInfo {
     /// Creates a new blank set of type information.
@@ -244,183 +744,45 @@ impl TypeInfo {
     }
 }
 
-/// A unified type definition for validating WebAssembly modules and components.
-#[derive(Debug)]
-pub enum Type {
-    /// The definition is for a sub type.
-    Sub(SubType),
-    /// The definition is for a core module type.
-    ///
-    /// This variant is only supported when parsing a component.
-    Module(Box<ModuleType>),
-    /// The definition is for a core module instance type.
-    ///
-    /// This variant is only supported when parsing a component.
-    Instance(Box<InstanceType>),
-    /// The definition is for a component type.
-    ///
-    /// This variant is only supported when parsing a component.
-    Component(Box<ComponentType>),
-    /// The definition is for a component instance type.
-    ///
-    /// This variant is only supported when parsing a component.
-    ComponentInstance(Box<ComponentInstanceType>),
-    /// The definition is for a component function type.
-    ///
-    /// This variant is only supported when parsing a component.
-    ComponentFunc(ComponentFuncType),
-    /// The definition is for a component defined type.
-    ///
-    /// This variant is only supported when parsing a component.
-    Defined(ComponentDefinedType),
-    /// This definition is for a resource type in the component model.
-    ///
-    /// Each resource is identified by a unique identifier specified here.
-    Resource(ResourceId),
-}
-
-impl Type {
-    /// Converts the type to a core function type.
-    pub fn unwrap_func(&self) -> &FuncType {
-        match self {
-            Type::Sub(SubType {
-                structural_type: StructuralType::Func(ft),
-                ..
-            }) => ft,
-            _ => panic!("not a function type"),
-        }
-    }
-
-    /// Converts the type to an array type.
-    pub fn unwrap_array(&self) -> &ArrayType {
-        match self {
-            Self::Sub(SubType {
-                structural_type: StructuralType::Array(ty),
-                ..
-            }) => ty,
-            _ => panic!("not an array type"),
-        }
-    }
-
-    /// Converts the type to a struct type.
-    pub fn unwrap_struct(&self) -> &StructType {
-        match self {
-            Self::Sub(SubType {
-                structural_type: StructuralType::Struct(ty),
-                ..
-            }) => ty,
-            _ => panic!("not a struct type"),
-        }
-    }
-
-    /// Converts the type to a core module type.
-    pub fn unwrap_module(&self) -> &ModuleType {
-        match self {
-            Self::Module(ty) => ty,
-            _ => panic!("not a module type"),
-        }
-    }
-
-    /// Converts the type to a core module instance type.
-    pub fn unwrap_instance(&self) -> &InstanceType {
-        match self {
-            Self::Instance(ty) => ty,
-            _ => panic!("not an instance type"),
-        }
-    }
-
-    /// Converts the type to a component type.
-    pub fn unwrap_component(&self) -> &ComponentType {
-        match self {
-            Self::Component(ty) => ty,
-            _ => panic!("not a component type"),
-        }
-    }
-
-    /// Converts the type to a component instance type.
-    pub fn unwrap_component_instance(&self) -> &ComponentInstanceType {
-        match self {
-            Self::ComponentInstance(ty) => ty,
-            _ => panic!("not a component instance type"),
-        }
-    }
-
-    /// Converts the type to a component function type.
-    pub fn unwrap_component_func(&self) -> &ComponentFuncType {
-        match self {
-            Self::ComponentFunc(ty) => ty,
-            _ => panic!("not a component function type"),
-        }
-    }
-
-    /// Converts the type to a component defined type.
-    pub fn unwrap_defined(&self) -> &ComponentDefinedType {
-        match self {
-            Self::Defined(ty) => ty,
-            _ => panic!("not a defined type"),
-        }
-    }
-
-    /// Converts this type to a resource type, returning the corresponding id.
-    pub fn unwrap_resource(&self) -> ResourceId {
-        match self {
-            Self::Resource(id) => *id,
-            _ => panic!("not a resource type"),
-        }
-    }
-
-    pub(crate) fn calculate_info(&self, types: &TypeList) -> TypeInfo {
-        // TODO(#1036): calculate actual size for func, array, struct
-        match self {
-            Self::Sub(ty) => {
-                let size = 1 + match ty.clone().structural_type {
-                    StructuralType::Func(ty) => 1 + (ty.params().len() + ty.results().len()) as u32,
-                    StructuralType::Array(_) => 2,
-                    StructuralType::Struct(ty) => 1 + 2 * ty.fields.len() as u32,
-                };
-                TypeInfo::core(size)
-            }
-            Self::Module(ty) => ty.info,
-            Self::Instance(ty) => ty.info,
-            Self::Component(ty) => ty.info,
-            Self::ComponentInstance(ty) => ty.info,
-            Self::ComponentFunc(ty) => ty.info,
-            Self::Defined(ty) => ty.info(types),
-            Self::Resource(_) => TypeInfo::new(),
-        }
-    }
-}
-
 /// A component value type.
 #[derive(Debug, Clone, Copy)]
 pub enum ComponentValType {
     /// The value type is one of the primitive types.
     Primitive(PrimitiveValType),
     /// The type is represented with the given type identifier.
-    Type(TypeId),
+    Type(ComponentDefinedTypeId),
+}
+
+impl TypeData for ComponentValType {
+    type Id = ComponentValueTypeId;
+
+    fn type_info(&self, types: &TypeList) -> TypeInfo {
+        match self {
+            ComponentValType::Primitive(_) => TypeInfo::new(),
+            ComponentValType::Type(id) => types[*id].type_info(types),
+        }
+    }
 }
 
 impl ComponentValType {
     pub(crate) fn contains_ptr(&self, types: &TypeList) -> bool {
         match self {
             ComponentValType::Primitive(ty) => ty.contains_ptr(),
-            ComponentValType::Type(ty) => types[*ty].unwrap_defined().contains_ptr(types),
+            ComponentValType::Type(ty) => types[*ty].contains_ptr(types),
         }
     }
 
     fn push_wasm_types(&self, types: &TypeList, lowered_types: &mut LoweredTypes) -> bool {
         match self {
             Self::Primitive(ty) => push_primitive_wasm_types(ty, lowered_types),
-            Self::Type(id) => types[*id]
-                .unwrap_defined()
-                .push_wasm_types(types, lowered_types),
+            Self::Type(id) => types[*id].push_wasm_types(types, lowered_types),
         }
     }
 
     pub(crate) fn info(&self, types: &TypeList) -> TypeInfo {
         match self {
             Self::Primitive(_) => TypeInfo::new(),
-            Self::Type(id) => types.info(*id),
+            Self::Type(id) => types[*id].type_info(types),
         }
     }
 }
@@ -429,7 +791,7 @@ impl ComponentValType {
 #[derive(Debug, Clone, Copy)]
 pub enum EntityType {
     /// The entity is a function.
-    Func(TypeId),
+    Func(CoreTypeId),
     /// The entity is a table.
     Table(TableType),
     /// The entity is a memory.
@@ -437,7 +799,7 @@ pub enum EntityType {
     /// The entity is a global.
     Global(GlobalType),
     /// The entity is a tag.
-    Tag(TypeId),
+    Tag(CoreTypeId),
 }
 
 impl EntityType {
@@ -453,7 +815,7 @@ impl EntityType {
 
     pub(crate) fn info(&self, types: &TypeList) -> TypeInfo {
         match self {
-            Self::Func(id) | Self::Tag(id) => types.info(*id),
+            Self::Func(id) | Self::Tag(id) => types[*id].type_info(types),
             Self::Table(_) | Self::Memory(_) | Self::Global(_) => TypeInfo::new(),
         }
     }
@@ -516,6 +878,14 @@ pub struct ModuleType {
     pub exports: IndexMap<String, EntityType>,
 }
 
+impl TypeData for ModuleType {
+    type Id = ComponentCoreModuleTypeId;
+
+    fn type_info(&self, _types: &TypeList) -> TypeInfo {
+        self.info
+    }
+}
+
 impl ModuleType {
     /// Looks up an import by its module and name.
     ///
@@ -527,9 +897,10 @@ impl ModuleType {
 
 /// Represents the kind of module instance type.
 #[derive(Debug, Clone)]
-pub enum InstanceTypeKind {
+pub enum CoreInstanceTypeKind {
     /// The instance type is the result of instantiating a module type.
-    Instantiated(TypeId),
+    Instantiated(ComponentCoreModuleTypeId),
+
     /// The instance type is the result of instantiating from exported items.
     Exports(IndexMap<String, EntityType>),
 }
@@ -540,7 +911,15 @@ pub struct InstanceType {
     /// Metadata about this instance type
     pub(crate) info: TypeInfo,
     /// The kind of module instance type.
-    pub kind: InstanceTypeKind,
+    pub kind: CoreInstanceTypeKind,
+}
+
+impl TypeData for InstanceType {
+    type Id = ComponentCoreInstanceTypeId;
+
+    fn type_info(&self, _types: &TypeList) -> TypeInfo {
+        self.info
+    }
 }
 
 impl InstanceType {
@@ -554,8 +933,8 @@ impl InstanceType {
         types: &'a TypeList,
     ) -> &'a IndexMap<String, EntityType> {
         match &self.kind {
-            InstanceTypeKind::Instantiated(id) => &types[*id].unwrap_module().exports,
-            InstanceTypeKind::Exports(exports) => exports,
+            CoreInstanceTypeKind::Instantiated(id) => &types[*id].exports,
+            CoreInstanceTypeKind::Exports(exports) => exports,
         }
     }
 }
@@ -564,28 +943,28 @@ impl InstanceType {
 #[derive(Debug, Clone, Copy)]
 pub enum ComponentEntityType {
     /// The entity is a core module.
-    Module(TypeId),
+    Module(ComponentCoreModuleTypeId),
     /// The entity is a function.
-    Func(TypeId),
+    Func(ComponentFuncTypeId),
     /// The entity is a value.
     Value(ComponentValType),
     /// The entity is a type.
     Type {
         /// This is the identifier of the type that was referenced when this
         /// entity was created.
-        referenced: TypeId,
+        referenced: ComponentAnyTypeId,
         /// This is the identifier of the type that was created when this type
         /// was imported or exported from the component.
         ///
         /// Note that the underlying type information for the `referenced`
         /// field and for this `created` field is the same, but these two types
         /// will hash to different values.
-        created: TypeId,
+        created: ComponentAnyTypeId,
     },
     /// The entity is a component instance.
-    Instance(TypeId),
+    Instance(ComponentInstanceTypeId),
     /// The entity is a component.
-    Component(TypeId),
+    Component(ComponentTypeId),
 }
 
 impl ComponentEntityType {
@@ -609,11 +988,11 @@ impl ComponentEntityType {
 
     pub(crate) fn info(&self, types: &TypeList) -> TypeInfo {
         match self {
-            Self::Module(ty)
-            | Self::Func(ty)
-            | Self::Type { referenced: ty, .. }
-            | Self::Instance(ty)
-            | Self::Component(ty) => types.info(*ty),
+            Self::Module(ty) => types[*ty].type_info(types),
+            Self::Func(ty) => types[*ty].type_info(types),
+            Self::Type { referenced: ty, .. } => ty.info(types),
+            Self::Instance(ty) => types[*ty].type_info(types),
+            Self::Component(ty) => types[*ty].type_info(types),
             Self::Value(ty) => ty.info(types),
         }
     }
@@ -675,6 +1054,14 @@ pub struct ComponentType {
     pub explicit_resources: IndexMap<ResourceId, Vec<usize>>,
 }
 
+impl TypeData for ComponentType {
+    type Id = ComponentTypeId;
+
+    fn type_info(&self, _types: &TypeList) -> TypeInfo {
+        self.info
+    }
+}
+
 /// Represents a type of a component instance.
 #[derive(Debug, Clone)]
 pub struct ComponentInstanceType {
@@ -725,6 +1112,14 @@ pub struct ComponentInstanceType {
     pub explicit_resources: IndexMap<ResourceId, Vec<usize>>,
 }
 
+impl TypeData for ComponentInstanceType {
+    type Id = ComponentInstanceTypeId;
+
+    fn type_info(&self, _types: &TypeList) -> TypeInfo {
+        self.info
+    }
+}
+
 /// Represents a type of a component function.
 #[derive(Debug, Clone)]
 pub struct ComponentFuncType {
@@ -736,6 +1131,14 @@ pub struct ComponentFuncType {
     pub results: Box<[(Option<KebabString>, ComponentValType)]>,
 }
 
+impl TypeData for ComponentFuncType {
+    type Id = ComponentFuncTypeId;
+
+    fn type_info(&self, _types: &TypeList) -> TypeInfo {
+        self.info
+    }
+}
+
 impl ComponentFuncType {
     /// Lowers the component function type to core parameter and result types for the
     /// canonical ABI.
@@ -869,9 +1272,31 @@ pub enum ComponentDefinedType {
         err: Option<ComponentValType>,
     },
     /// The type is an owned handle to the specified resource.
-    Own(TypeId),
+    Own(AliasableResourceId),
     /// The type is a borrowed handle to the specified resource.
-    Borrow(TypeId),
+    Borrow(AliasableResourceId),
+}
+
+impl TypeData for ComponentDefinedType {
+    type Id = ComponentDefinedTypeId;
+
+    fn type_info(&self, types: &TypeList) -> TypeInfo {
+        match self {
+            Self::Primitive(_) | Self::Flags(_) | Self::Enum(_) | Self::Own(_) => TypeInfo::new(),
+            Self::Borrow(_) => TypeInfo::borrow(),
+            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::Result { ok, err } => {
+                let default = TypeInfo::new();
+                let mut info = ok.map(|ty| ty.type_info(types)).unwrap_or(default);
+                info.combine(err.map(|ty| ty.type_info(types)).unwrap_or(default), 0)
+                    .unwrap();
+                info
+            }
+        }
+    }
 }
 
 impl ComponentDefinedType {
@@ -894,24 +1319,6 @@ impl ComponentDefinedType {
         }
     }
 
-    pub(crate) fn info(&self, types: &TypeList) -> TypeInfo {
-        match self {
-            Self::Primitive(_) | Self::Flags(_) | Self::Enum(_) | Self::Own(_) => TypeInfo::new(),
-            Self::Borrow(_) => TypeInfo::borrow(),
-            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::Result { ok, err } => {
-                let default = TypeInfo::new();
-                let mut info = ok.map(|ty| ty.info(types)).unwrap_or(default);
-                info.combine(err.map(|ty| ty.info(types)).unwrap_or(default), 0)
-                    .unwrap();
-                info
-            }
-        }
-    }
-
     fn push_wasm_types(&self, types: &TypeList, lowered_types: &mut LoweredTypes) -> bool {
         match self {
             Self::Primitive(ty) => push_primitive_wasm_types(ty, lowered_types),
@@ -1078,8 +1485,11 @@ impl<'a> TypesRef<'a> {
     /// Gets a type based on its type id.
     ///
     /// Returns `None` if the type id is unknown.
-    pub fn get(&self, id: TypeId) -> Option<&'a Type> {
-        self.list.get(id.index())
+    pub fn get<T>(&self, id: T) -> Option<&'a T::Data>
+    where
+        T: TypeIdentifier,
+    {
+        self.list.get(id)
     }
 
     /// Gets a core WebAssembly type id from a type index.
@@ -1090,9 +1500,9 @@ impl<'a> TypesRef<'a> {
     /// # Panics
     ///
     /// This will panic if the `index` provided is out of bounds.
-    pub fn core_type_at(&self, index: u32) -> TypeId {
+    pub fn core_type_at(&self, index: u32) -> ComponentCoreTypeId {
         match &self.kind {
-            TypesRefKind::Module(module) => module.types[index as usize],
+            TypesRefKind::Module(module) => ComponentCoreTypeId::Sub(module.types[index as usize]),
             TypesRefKind::Component(component) => component.core_types[index as usize],
         }
     }
@@ -1101,15 +1511,41 @@ impl<'a> TypesRef<'a> {
     ///
     /// # Panics
     ///
-    /// Panics if `index` is not a valid function index or if this type
-    /// information represents a core module.
-    pub fn component_type_at(&self, index: u32) -> TypeId {
+    /// Panics if `index` is not a valid type index or if this type information
+    /// represents a core module.
+    pub fn component_any_type_at(&self, index: u32) -> ComponentAnyTypeId {
         match &self.kind {
             TypesRefKind::Module(_) => panic!("not a component"),
             TypesRefKind::Component(component) => component.types[index as usize],
         }
     }
 
+    /// Gets a component type id from a type index.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `index` is not a valid component type index or if this type
+    /// information represents a core module.
+    pub fn component_type_at(&self, index: u32) -> ComponentTypeId {
+        match self.component_any_type_at(index) {
+            ComponentAnyTypeId::Component(id) => id,
+            _ => panic!("not a component type"),
+        }
+    }
+
+    /// Gets a type id from a type index.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `index` is not a valid function index or if this type
+    /// information represents a core module.
+    pub fn component_defined_type_at(&self, index: u32) -> ComponentDefinedTypeId {
+        match self.component_any_type_at(index) {
+            ComponentAnyTypeId::Defined(id) => id,
+            _ => panic!("not a defined type"),
+        }
+    }
+
     /// Returns the number of core types defined so far.
     pub fn core_type_count(&self) -> u32 {
         match &self.kind {
@@ -1196,7 +1632,7 @@ impl<'a> TypesRef<'a> {
     /// # Panics
     ///
     /// This will panic if the `index` provided is out of bounds.
-    pub fn tag_at(&self, index: u32) -> TypeId {
+    pub fn tag_at(&self, index: u32) -> CoreTypeId {
         let tags = match &self.kind {
             TypesRefKind::Module(module) => &module.tags,
             TypesRefKind::Component(component) => &component.core_tags,
@@ -1217,7 +1653,7 @@ impl<'a> TypesRef<'a> {
     /// # Panics
     ///
     /// This will panic if the `index` provided is out of bounds.
-    pub fn function_at(&self, index: u32) -> TypeId {
+    pub fn core_function_at(&self, index: u32) -> CoreTypeId {
         match &self.kind {
             TypesRefKind::Module(module) => module.types[module.functions[index as usize] as usize],
             TypesRefKind::Component(component) => component.core_funcs[index as usize],
@@ -1263,7 +1699,7 @@ impl<'a> TypesRef<'a> {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn component_function_at(&self, index: u32) -> TypeId {
+    pub fn component_function_at(&self, index: u32) -> ComponentFuncTypeId {
         match &self.kind {
             TypesRefKind::Module(_) => panic!("not a component"),
             TypesRefKind::Component(component) => component.funcs[index as usize],
@@ -1284,7 +1720,7 @@ impl<'a> TypesRef<'a> {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn module_at(&self, index: u32) -> TypeId {
+    pub fn module_at(&self, index: u32) -> ComponentCoreModuleTypeId {
         match &self.kind {
             TypesRefKind::Module(_) => panic!("not a component"),
             TypesRefKind::Component(component) => component.core_modules[index as usize],
@@ -1305,7 +1741,7 @@ impl<'a> TypesRef<'a> {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn instance_at(&self, index: u32) -> TypeId {
+    pub fn core_instance_at(&self, index: u32) -> ComponentCoreInstanceTypeId {
         match &self.kind {
             TypesRefKind::Module(_) => panic!("not a component"),
             TypesRefKind::Component(component) => component.core_instances[index as usize],
@@ -1313,7 +1749,7 @@ impl<'a> TypesRef<'a> {
     }
 
     /// Returns the number of core wasm instances defined so far.
-    pub fn instance_count(&self) -> u32 {
+    pub fn core_instance_count(&self) -> u32 {
         match &self.kind {
             TypesRefKind::Module(_module) => 0,
             TypesRefKind::Component(component) => component.core_instances.len() as u32,
@@ -1326,7 +1762,7 @@ impl<'a> TypesRef<'a> {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn component_at(&self, index: u32) -> TypeId {
+    pub fn component_at(&self, index: u32) -> ComponentTypeId {
         match &self.kind {
             TypesRefKind::Module(_) => panic!("not a component"),
             TypesRefKind::Component(component) => component.components[index as usize],
@@ -1347,7 +1783,7 @@ impl<'a> TypesRef<'a> {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn component_instance_at(&self, index: u32) -> TypeId {
+    pub fn component_instance_at(&self, index: u32) -> ComponentInstanceTypeId {
         match &self.kind {
             TypesRefKind::Module(_) => panic!("not a component"),
             TypesRefKind::Component(component) => component.instances[index as usize],
@@ -1432,15 +1868,22 @@ impl<'a> TypesRef<'a> {
     /// Attempts to lookup the type id that `ty` is an alias of.
     ///
     /// Returns `None` if `ty` wasn't listed as aliasing a prior type.
-    pub fn peel_alias(&self, ty: TypeId) -> Option<TypeId> {
+    pub fn peel_alias<T>(&self, ty: T) -> Option<T>
+    where
+        T: Aliasable,
+    {
         self.list.peel_alias(ty)
     }
 }
 
-impl Index<TypeId> for TypesRef<'_> {
-    type Output = Type;
-    fn index(&self, id: TypeId) -> &Type {
-        &self.list[id]
+impl<T> Index<T> for TypesRef<'_>
+where
+    T: TypeIdentifier,
+{
+    type Output = T::Data;
+
+    fn index(&self, index: T) -> &Self::Output {
+        &self.list[index]
     }
 }
 
@@ -1473,7 +1916,10 @@ impl Types {
     /// Gets a type based on its type id.
     ///
     /// Returns `None` if the type id is unknown.
-    pub fn get(&self, id: TypeId) -> Option<&Type> {
+    pub fn get<T>(&self, id: T) -> Option<&T::Data>
+    where
+        T: TypeIdentifier,
+    {
         self.as_ref().get(id)
     }
 
@@ -1485,18 +1931,39 @@ impl Types {
     /// # Panics
     ///
     /// Panics if `index` is not a valid function index.
-    pub fn core_type_at(&self, index: u32) -> TypeId {
+    pub fn core_type_at(&self, index: u32) -> ComponentCoreTypeId {
         self.as_ref().core_type_at(index)
     }
 
+    /// Gets a component WebAssembly type at the given type index.
+    ///
+    /// Note that this is in contrast to [`TypesRef::core_type_at`] which gets a
+    /// core type from its index.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `index` is not a valid type index.
+    pub fn component_any_type_at(&self, index: u32) -> ComponentAnyTypeId {
+        self.as_ref().component_any_type_at(index)
+    }
+
+    /// Gets a component type at the given type index.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `index` is not a valid component type index.
+    pub fn component_type_at(&self, index: u32) -> ComponentTypeId {
+        self.as_ref().component_type_at(index)
+    }
+
     /// Gets a component type from the given component type index.
     ///
     /// # Panics
     ///
-    /// Panics if `index` is not a valid function index or if this type
+    /// Panics if `index` is not a valid defined type index or if this type
     /// information represents a core module.
-    pub fn component_type_at(&self, index: u32) -> TypeId {
-        self.as_ref().component_type_at(index)
+    pub fn component_defined_type_at(&self, index: u32) -> ComponentDefinedTypeId {
+        self.as_ref().component_defined_type_at(index)
     }
 
     /// Gets the count of core types.
@@ -1557,7 +2024,7 @@ impl Types {
     /// # Panics
     ///
     /// Panics if `index` is not a valid function index.
-    pub fn tag_at(&self, index: u32) -> TypeId {
+    pub fn tag_at(&self, index: u32) -> CoreTypeId {
         self.as_ref().tag_at(index)
     }
 
@@ -1571,15 +2038,15 @@ impl Types {
     /// # Panics
     ///
     /// Panics if `index` is not a valid function index.
-    pub fn function_at(&self, index: u32) -> TypeId {
-        self.as_ref().function_at(index)
+    pub fn core_function_at(&self, index: u32) -> CoreTypeId {
+        self.as_ref().core_function_at(index)
     }
 
     /// Gets the count of core functions defined so far.
     ///
     /// Note that this includes imported functions, defined functions, and for
     /// components lowered/aliased functions.
-    pub fn function_count(&self) -> u32 {
+    pub fn core_function_count(&self) -> u32 {
         self.as_ref().function_count()
     }
 
@@ -1603,7 +2070,7 @@ impl Types {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn component_function_at(&self, index: u32) -> TypeId {
+    pub fn component_function_at(&self, index: u32) -> ComponentFuncTypeId {
         self.as_ref().component_function_at(index)
     }
 
@@ -1618,7 +2085,7 @@ impl Types {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn module_at(&self, index: u32) -> TypeId {
+    pub fn module_at(&self, index: u32) -> ComponentCoreModuleTypeId {
         self.as_ref().module_at(index)
     }
 
@@ -1636,12 +2103,12 @@ impl Types {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn instance_at(&self, index: u32) -> TypeId {
-        self.as_ref().instance_at(index)
+    pub fn core_instance_at(&self, index: u32) -> ComponentCoreInstanceTypeId {
+        self.as_ref().core_instance_at(index)
     }
 
     /// Gets the count of imported, exported, or aliased core module instances.
-    pub fn instance_count(&self) -> usize {
+    pub fn core_instance_count(&self) -> usize {
         match &self.kind {
             TypesKind::Module(_) => 0,
             TypesKind::Component(component) => component.core_instances.len(),
@@ -1654,7 +2121,7 @@ impl Types {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn component_at(&self, index: u32) -> TypeId {
+    pub fn component_at(&self, index: u32) -> ComponentTypeId {
         self.as_ref().component_at(index)
     }
 
@@ -1672,7 +2139,7 @@ impl Types {
     ///
     /// This will panic if the `index` provided is out of bounds or if this type
     /// information represents a core module.
-    pub fn component_instance_at(&self, index: u32) -> TypeId {
+    pub fn component_instance_at(&self, index: u32) -> ComponentInstanceTypeId {
         self.as_ref().component_instance_at(index)
     }
 
@@ -1725,15 +2192,21 @@ impl Types {
     /// Attempts to lookup the type id that `ty` is an alias of.
     ///
     /// Returns `None` if `ty` wasn't listed as aliasing a prior type.
-    pub fn peel_alias(&self, ty: TypeId) -> Option<TypeId> {
-        self.as_ref().peel_alias(ty)
+    pub fn peel_alias<T>(&self, ty: T) -> Option<T>
+    where
+        T: Aliasable,
+    {
+        self.list.peel_alias(ty)
     }
 }
 
-impl Index<TypeId> for Types {
-    type Output = Type;
+impl<T> Index<T> for Types
+where
+    T: TypeIdentifier,
+{
+    type Output = T::Data;
 
-    fn index(&self, id: TypeId) -> &Type {
+    fn index(&self, id: T) -> &Self::Output {
         &self.list[id]
     }
 }
@@ -1749,7 +2222,10 @@ impl Index<TypeId> for Types {
 ///
 /// Otherwise, though, this type behaves as if it were a large `Vec<T>`, but
 /// it's represented by lists of contiguous chunks.
-pub(crate) struct SnapshotList<T> {
+//
+// Only public because it shows up in a public trait's `doc(hidden)` method.
+#[doc(hidden)]
+pub struct SnapshotList<T> {
     // All previous snapshots, the "head" of the list that this type represents.
     // The first entry in this pair is the starting index for all elements
     // contained in the list, and the second element is the list itself. Note
@@ -1766,15 +2242,10 @@ pub(crate) struct SnapshotList<T> {
 
     // The current list of types for the current snapshot that are being built.
     cur: Vec<T>,
-
-    unique_mappings: HashMap<u32, u32>,
-    unique_counter: u32,
 }
 
 struct Snapshot<T> {
     prior_types: usize,
-    unique_counter: u32,
-    unique_mappings: HashMap<u32, u32>,
     items: Vec<T>,
 }
 
@@ -1809,9 +2280,10 @@ impl<T> SnapshotList<T> {
         self.cur.len() + self.snapshots_total
     }
 
-    /// Reserve space for an additional count of items.
-    pub(crate) fn reserve(&mut self, additional: usize) {
-        self.cur.reserve(additional);
+    /// Same as `Vec::truncate` but can only truncate uncommitted elements.
+    pub(crate) fn truncate(&mut self, len: usize) {
+        assert!(len >= self.snapshots_total);
+        self.cur.truncate(len - self.snapshots_total);
     }
 
     /// Commits previously pushed types into this snapshot vector, and returns a
@@ -1824,18 +2296,12 @@ impl<T> SnapshotList<T> {
         // If the current chunk has new elements, commit them in to an
         // `Arc`-wrapped vector in the snapshots list. Note the `shrink_to_fit`
         // ahead of time to hopefully keep memory usage lower than it would
-        // otherwise be. Additionally note that the `unique_counter` is bumped
-        // here to ensure that the previous value of the unique counter is
-        // never used for an actual type so it's suitable for lookup via a
-        // binary search.
+        // otherwise be.
         let len = self.cur.len();
         if len > 0 {
-            self.unique_counter += 1;
             self.cur.shrink_to_fit();
             self.snapshots.push(Arc::new(Snapshot {
                 prior_types: self.snapshots_total,
-                unique_counter: self.unique_counter - 1,
-                unique_mappings: mem::take(&mut self.unique_mappings),
                 items: mem::take(&mut self.cur),
             }));
             self.snapshots_total += len;
@@ -1843,52 +2309,9 @@ impl<T> SnapshotList<T> {
         SnapshotList {
             snapshots: self.snapshots.clone(),
             snapshots_total: self.snapshots_total,
-            unique_mappings: HashMap::new(),
-            unique_counter: self.unique_counter,
             cur: Vec::new(),
         }
     }
-
-    /// Modifies a `TypeId` to have the same contents but a fresh new unique id.
-    ///
-    /// This is used during aliasing with components to assign types a unique
-    /// identifier that isn't equivalent to anything else but still
-    /// points to the same underlying type.
-    pub fn with_unique(&mut self, mut ty: TypeId) -> TypeId {
-        self.unique_mappings
-            .insert(self.unique_counter, ty.unique_id);
-        ty.unique_id = self.unique_counter;
-        self.unique_counter += 1;
-        ty
-    }
-
-    /// Attempts to lookup the type id that `ty` is an alias of.
-    ///
-    /// Returns `None` if `ty` wasn't listed as aliasing a prior type.
-    pub fn peel_alias(&self, ty: TypeId) -> Option<TypeId> {
-        // The unique counter in each snapshot is the unique counter at the
-        // time of the snapshot so it's guaranteed to never be used, meaning
-        // that `Ok` should never show up here. With an `Err` it's where the
-        // index would be placed meaning that the index in question is the
-        // smallest value over the unique id's value, meaning that slot has the
-        // mapping we're interested in.
-        let i = match self
-            .snapshots
-            .binary_search_by_key(&ty.unique_id, |snapshot| snapshot.unique_counter)
-        {
-            Ok(_) => unreachable!(),
-            Err(i) => i,
-        };
-
-        // If the `i` index is beyond the snapshot array then lookup in the
-        // current mappings instead since it may refer to a type not snapshot
-        // yet.
-        let unique_id = match self.snapshots.get(i) {
-            Some(snapshot) => *snapshot.unique_mappings.get(&ty.unique_id)?,
-            None => *self.unique_mappings.get(&ty.unique_id)?,
-        };
-        Some(TypeId { unique_id, ..ty })
-    }
 }
 
 impl<T> Index<usize> for SnapshotList<T> {
@@ -1900,11 +2323,14 @@ impl<T> Index<usize> for SnapshotList<T> {
     }
 }
 
-impl<T> Index<TypeId> for SnapshotList<T> {
+impl<T, U> Index<U> for SnapshotList<T>
+where
+    U: TypeIdentifier<Data = T>,
+{
     type Output = T;
 
     #[inline]
-    fn index(&self, id: TypeId) -> &T {
+    fn index(&self, id: U) -> &T {
         self.get(id.index()).unwrap()
     }
 }
@@ -1915,68 +2341,213 @@ impl<T> Default for SnapshotList<T> {
             snapshots: Vec::new(),
             snapshots_total: 0,
             cur: Vec::new(),
-            unique_counter: 1,
-            unique_mappings: HashMap::new(),
         }
     }
 }
 
 /// A snapshot list of types.
+///
+/// Note that the snapshot lists below do not correspond with index spaces. Many
+/// different kinds of types are in the same index space (e.g. all of the
+/// component model's {component, instance, defined, func} types are in the same
+/// index space). However, we store each of them in their own type-specific
+/// snapshot list and give each of them their own identifier type.
 #[derive(Default)]
-pub(crate) struct TypeList {
-    types: SnapshotList<Type>,
-    infos: SnapshotList<TypeInfo>,
+// Only public because it shows up in a public trait's `doc(hidden)` method.
+#[doc(hidden)]
+pub struct TypeList {
+    // Keeps track of which `alias_id` is an alias of which other `alias_id`.
+    alias_mappings: HashMap<u32, u32>,
+    // Counter for generating new `alias_id`s.
+    alias_counter: u32,
+    // Snapshots of previously committed `TypeList`s' aliases.
+    alias_snapshots: Vec<TypeListAliasSnapshot>,
+
+    // Core Wasm types.
+    core_types: SnapshotList<SubType>,
+
+    // Component model types.
+    components: SnapshotList<ComponentType>,
+    component_defined_types: SnapshotList<ComponentDefinedType>,
+    component_values: SnapshotList<ComponentValType>,
+    component_instances: SnapshotList<ComponentInstanceType>,
+    component_funcs: SnapshotList<ComponentFuncType>,
+    core_modules: SnapshotList<ModuleType>,
+    core_instances: SnapshotList<InstanceType>,
+}
+
+#[derive(Clone, Debug)]
+struct TypeListAliasSnapshot {
+    // The `alias_counter` at the time that this snapshot was taken.
+    alias_counter: u32,
+
+    // The alias mappings in this snapshot.
+    alias_mappings: HashMap<u32, u32>,
+}
+
+struct TypeListCheckpoint {
+    core_types: usize,
+    components: usize,
+    component_defined_types: usize,
+    component_values: usize,
+    component_instances: usize,
+    component_funcs: usize,
+    core_modules: usize,
+    core_instances: usize,
 }
 
 impl TypeList {
-    pub fn info(&self, id: TypeId) -> TypeInfo {
-        self.infos[id]
-    }
-
-    pub fn get(&self, index: usize) -> Option<&Type> {
-        self.types.get(index)
+    pub fn get<T>(&self, id: T) -> Option<&T::Data>
+    where
+        T: TypeIdentifier,
+    {
+        T::list(self).get(id.index())
     }
 
-    pub fn len(&self) -> usize {
-        debug_assert_eq!(self.types.len(), self.infos.len());
-        self.types.len()
+    pub fn push<T>(&mut self, ty: T) -> T::Id
+    where
+        T: TypeData,
+    {
+        let index = u32::try_from(T::Id::list(self).len()).unwrap();
+        let id = T::Id::from_index(index);
+        T::Id::list_mut(self).push(ty);
+        id
+    }
+
+    fn checkpoint(&self) -> TypeListCheckpoint {
+        let TypeList {
+            alias_mappings: _,
+            alias_counter: _,
+            alias_snapshots: _,
+            core_types,
+            components,
+            component_defined_types,
+            component_values,
+            component_instances,
+            component_funcs,
+            core_modules,
+            core_instances,
+        } = self;
+
+        TypeListCheckpoint {
+            core_types: core_types.len(),
+            components: components.len(),
+            component_defined_types: component_defined_types.len(),
+            component_values: component_values.len(),
+            component_instances: component_instances.len(),
+            component_funcs: component_funcs.len(),
+            core_modules: core_modules.len(),
+            core_instances: core_instances.len(),
+        }
+    }
+
+    fn reset_to_checkpoint(&mut self, checkpoint: TypeListCheckpoint) {
+        let TypeList {
+            alias_mappings: _,
+            alias_counter: _,
+            alias_snapshots: _,
+            core_types,
+            components,
+            component_defined_types,
+            component_values,
+            component_instances,
+            component_funcs,
+            core_modules,
+            core_instances,
+        } = self;
+
+        core_types.truncate(checkpoint.core_types);
+        components.truncate(checkpoint.components);
+        component_defined_types.truncate(checkpoint.component_defined_types);
+        component_values.truncate(checkpoint.component_values);
+        component_instances.truncate(checkpoint.component_instances);
+        component_funcs.truncate(checkpoint.component_funcs);
+        core_modules.truncate(checkpoint.core_modules);
+        core_instances.truncate(checkpoint.core_instances);
     }
 
-    pub fn push(&mut self, ty: Type) {
-        let info = ty.calculate_info(self);
-        self.infos.push(info);
-        self.types.push(ty);
-        debug_assert_eq!(self.types.len(), self.infos.len());
-    }
+    pub fn commit(&mut self) -> TypeList {
+        // Note that the `alias_counter` is bumped here to ensure that the
+        // previous value of the unique counter is never used for an actual type
+        // so it's suitable for lookup via a binary search.
+        let alias_counter = self.alias_counter;
+        self.alias_counter += 1;
 
-    pub fn reserve(&mut self, additional: usize) {
-        self.infos.reserve(additional);
-        self.types.reserve(additional);
-    }
+        self.alias_snapshots.push(TypeListAliasSnapshot {
+            alias_counter,
+            alias_mappings: mem::take(&mut self.alias_mappings),
+        });
 
-    pub fn commit(&mut self) -> TypeList {
         TypeList {
-            types: self.types.commit(),
-            infos: self.infos.commit(),
+            alias_mappings: HashMap::new(),
+            alias_counter: self.alias_counter,
+            alias_snapshots: self.alias_snapshots.clone(),
+            core_types: self.core_types.commit(),
+            components: self.components.commit(),
+            component_defined_types: self.component_defined_types.commit(),
+            component_values: self.component_values.commit(),
+            component_instances: self.component_instances.commit(),
+            component_funcs: self.component_funcs.commit(),
+            core_modules: self.core_modules.commit(),
+            core_instances: self.core_instances.commit(),
         }
     }
 
     /// See `SnapshotList::with_unique`.
-    pub fn with_unique(&mut self, ty: TypeId) -> TypeId {
-        self.types.with_unique(ty)
+    pub fn with_unique<T>(&mut self, mut ty: T) -> T
+    where
+        T: Aliasable,
+    {
+        self.alias_mappings
+            .insert(self.alias_counter, ty.alias_id());
+        ty.set_alias_id(self.alias_counter);
+        self.alias_counter += 1;
+        ty
     }
 
-    /// See `SnapshotList::peel_alias`.
-    pub fn peel_alias(&self, ty: TypeId) -> Option<TypeId> {
-        self.types.peel_alias(ty)
+    /// Attempts to lookup the type id that `ty` is an alias of.
+    ///
+    /// Returns `None` if `ty` wasn't listed as aliasing a prior type.
+    pub fn peel_alias<T>(&self, mut ty: T) -> Option<T>
+    where
+        T: Aliasable,
+    {
+        let alias_id = ty.alias_id();
+
+        // The unique counter in each snapshot is the unique counter at the
+        // time of the snapshot so it's guaranteed to never be used, meaning
+        // that `Ok` should never show up here. With an `Err` it's where the
+        // index would be placed meaning that the index in question is the
+        // smallest value over the unique id's value, meaning that slot has the
+        // mapping we're interested in.
+        let i = match self
+            .alias_snapshots
+            .binary_search_by_key(&alias_id, |snapshot| snapshot.alias_counter)
+        {
+            Ok(_) => unreachable!(),
+            Err(i) => i,
+        };
+
+        // If the `i` index is beyond the snapshot array then lookup in the
+        // current mappings instead since it may refer to a type not snapshot
+        // yet.
+        ty.set_alias_id(match self.alias_snapshots.get(i) {
+            Some(snapshot) => *snapshot.alias_mappings.get(&alias_id)?,
+            None => *self.alias_mappings.get(&alias_id)?,
+        });
+        Some(ty)
     }
 }
 
-impl Index<TypeId> for TypeList {
-    type Output = Type;
+impl<T> Index<T> for TypeList
+where
+    T: TypeIdentifier,
+{
+    type Output = T::Data;
 
-    fn index(&self, index: TypeId) -> &Self::Output {
-        &self.types[index]
+    fn index(&self, id: T) -> &Self::Output {
+        let arena = T::list(self);
+        &arena[id.index()]
     }
 }
 
@@ -2024,25 +2595,25 @@ impl TypeAlloc {
     ///
     /// The returned identifier is unique within this `TypeAlloc` and won't be
     /// hash-equivalent to anything else.
-    pub fn push_ty(&mut self, ty: Type) -> TypeId {
-        let index = self.list.len();
-        let index = u32::try_from(index).unwrap();
-        self.list.push(ty);
-        TypeId {
-            index,
-            unique_id: 0,
-        }
+    pub fn push_ty<T>(&mut self, ty: T) -> T::Id
+    where
+        T: TypeData,
+    {
+        self.list.push(ty)
     }
 
     /// Allocates a new unique resource identifier.
     ///
     /// Note that uniqueness is only a property within this `TypeAlloc`.
-    pub fn alloc_resource_id(&mut self) -> ResourceId {
+    pub fn alloc_resource_id(&mut self) -> AliasableResourceId {
         let contextually_unique_id = self.next_resource_id;
         self.next_resource_id = self.next_resource_id.checked_add(1).unwrap();
-        ResourceId {
-            globally_unique_id: self.globally_unique_id,
-            contextually_unique_id,
+        AliasableResourceId {
+            id: ResourceId {
+                globally_unique_id: self.globally_unique_id,
+                contextually_unique_id,
+            },
+            alias_id: NO_ALIAS,
         }
     }
 
@@ -2052,93 +2623,123 @@ impl TypeAlloc {
     /// Free variables are defined as resources. Any resource, perhaps
     /// transitively, referred to but not defined by `id` is added to the `set`
     /// and returned.
-    pub fn free_variables_type_id(&self, id: TypeId, set: &mut IndexSet<ResourceId>) {
-        match &self[id] {
-            // Core wasm constructs cannot reference resources.
-            Type::Sub(_) | Type::Module(_) | Type::Instance(_) => {}
-
-            // Recurse on the imports/exports of components, but remove the
-            // imported and defined resources within the component itself.
-            //
-            // Technically this needs to add all the free variables of the
-            // exports, remove the defined resources, then add the free
-            // variables of imports, then remove the imported resources. Given
-            // prior validation of component types, however, the defined
-            // and imported resources are disjoint and imports can't refer to
-            // defined resources, so doing this all in one go should be
-            // equivalent.
-            Type::Component(i) => {
-                for ty in i.imports.values().chain(i.exports.values()) {
-                    self.free_variables_component_entity(ty, set);
-                }
-                for (id, _path) in i.imported_resources.iter().chain(&i.defined_resources) {
-                    set.remove(id);
-                }
+    pub fn free_variables_any_type_id(
+        &self,
+        id: ComponentAnyTypeId,
+        set: &mut IndexSet<ResourceId>,
+    ) {
+        match id {
+            ComponentAnyTypeId::Resource(r) => {
+                set.insert(r.resource());
             }
-
-            // Like components, add in all the free variables of referenced
-            // types but then remove those defined by this component instance
-            // itself.
-            Type::ComponentInstance(i) => {
-                for ty in i.exports.values() {
-                    self.free_variables_component_entity(ty, set);
-                }
-                for id in i.defined_resources.iter() {
-                    set.remove(id);
-                }
+            ComponentAnyTypeId::Defined(id) => {
+                self.free_variables_component_defined_type_id(id, set)
             }
-
-            Type::Resource(r) => {
-                set.insert(*r);
+            ComponentAnyTypeId::Func(id) => self.free_variables_component_func_type_id(id, set),
+            ComponentAnyTypeId::Instance(id) => {
+                self.free_variables_component_instance_type_id(id, set)
             }
+            ComponentAnyTypeId::Component(id) => self.free_variables_component_type_id(id, set),
+        }
+    }
 
-            Type::ComponentFunc(i) => {
-                for ty in i
-                    .params
-                    .iter()
-                    .map(|(_, ty)| ty)
-                    .chain(i.results.iter().map(|(_, ty)| ty))
-                {
+    pub fn free_variables_component_defined_type_id(
+        &self,
+        id: ComponentDefinedTypeId,
+        set: &mut IndexSet<ResourceId>,
+    ) {
+        match &self[id] {
+            ComponentDefinedType::Primitive(_)
+            | ComponentDefinedType::Flags(_)
+            | ComponentDefinedType::Enum(_) => {}
+            ComponentDefinedType::Record(r) => {
+                for ty in r.fields.values() {
                     self.free_variables_valtype(ty, set);
                 }
             }
-
-            Type::Defined(i) => match i {
-                ComponentDefinedType::Primitive(_)
-                | ComponentDefinedType::Flags(_)
-                | ComponentDefinedType::Enum(_) => {}
-                ComponentDefinedType::Record(r) => {
-                    for ty in r.fields.values() {
-                        self.free_variables_valtype(ty, set);
-                    }
+            ComponentDefinedType::Tuple(r) => {
+                for ty in r.types.iter() {
+                    self.free_variables_valtype(ty, set);
                 }
-                ComponentDefinedType::Tuple(r) => {
-                    for ty in r.types.iter() {
+            }
+            ComponentDefinedType::Variant(r) => {
+                for ty in r.cases.values() {
+                    if let Some(ty) = &ty.ty {
                         self.free_variables_valtype(ty, set);
                     }
                 }
-                ComponentDefinedType::Variant(r) => {
-                    for ty in r.cases.values() {
-                        if let Some(ty) = &ty.ty {
-                            self.free_variables_valtype(ty, set);
-                        }
-                    }
-                }
-                ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
-                    self.free_variables_valtype(ty, set);
-                }
-                ComponentDefinedType::Result { ok, err } => {
-                    if let Some(ok) = ok {
-                        self.free_variables_valtype(ok, set);
-                    }
-                    if let Some(err) = err {
-                        self.free_variables_valtype(err, set);
-                    }
+            }
+            ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
+                self.free_variables_valtype(ty, set);
+            }
+            ComponentDefinedType::Result { ok, err } => {
+                if let Some(ok) = ok {
+                    self.free_variables_valtype(ok, set);
                 }
-                ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => {
-                    self.free_variables_type_id(*id, set);
+                if let Some(err) = err {
+                    self.free_variables_valtype(err, set);
                 }
-            },
+            }
+            ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => {
+                set.insert(id.resource());
+            }
+        }
+    }
+
+    pub fn free_variables_component_type_id(
+        &self,
+        id: ComponentTypeId,
+        set: &mut IndexSet<ResourceId>,
+    ) {
+        let i = &self[id];
+        // Recurse on the imports/exports of components, but remove the
+        // imported and defined resources within the component itself.
+        //
+        // Technically this needs to add all the free variables of the
+        // exports, remove the defined resources, then add the free
+        // variables of imports, then remove the imported resources. Given
+        // prior validation of component types, however, the defined
+        // and imported resources are disjoint and imports can't refer to
+        // defined resources, so doing this all in one go should be
+        // equivalent.
+        for ty in i.imports.values().chain(i.exports.values()) {
+            self.free_variables_component_entity(ty, set);
+        }
+        for (id, _path) in i.imported_resources.iter().chain(&i.defined_resources) {
+            set.remove(id);
+        }
+    }
+
+    pub fn free_variables_component_instance_type_id(
+        &self,
+        id: ComponentInstanceTypeId,
+        set: &mut IndexSet<ResourceId>,
+    ) {
+        let i = &self[id];
+        // Like components, add in all the free variables of referenced
+        // types but then remove those defined by this component instance
+        // itself.
+        for ty in i.exports.values() {
+            self.free_variables_component_entity(ty, set);
+        }
+        for id in i.defined_resources.iter() {
+            set.remove(id);
+        }
+    }
+
+    pub fn free_variables_component_func_type_id(
+        &self,
+        id: ComponentFuncTypeId,
+        set: &mut IndexSet<ResourceId>,
+    ) {
+        let i = &self[id];
+        for ty in i
+            .params
+            .iter()
+            .map(|(_, ty)| ty)
+            .chain(i.results.iter().map(|(_, ty)| ty))
+        {
+            self.free_variables_valtype(ty, set);
         }
     }
 
@@ -2149,12 +2750,14 @@ impl TypeAlloc {
         set: &mut IndexSet<ResourceId>,
     ) {
         match ty {
-            ComponentEntityType::Module(id)
-            | ComponentEntityType::Func(id)
-            | ComponentEntityType::Instance(id)
-            | ComponentEntityType::Component(id) => self.free_variables_type_id(*id, set),
+            ComponentEntityType::Module(_) => {}
+            ComponentEntityType::Func(id) => self.free_variables_component_func_type_id(*id, set),
+            ComponentEntityType::Instance(id) => {
+                self.free_variables_component_instance_type_id(*id, set)
+            }
+            ComponentEntityType::Component(id) => self.free_variables_component_type_id(*id, set),
             ComponentEntityType::Type { created, .. } => {
-                self.free_variables_type_id(*created, set);
+                self.free_variables_any_type_id(*created, set);
             }
             ComponentEntityType::Value(ty) => self.free_variables_valtype(ty, set),
         }
@@ -2164,7 +2767,7 @@ impl TypeAlloc {
     fn free_variables_valtype(&self, ty: &ComponentValType, set: &mut IndexSet<ResourceId>) {
         match ty {
             ComponentValType::Primitive(_) => {}
-            ComponentValType::Type(id) => self.free_variables_type_id(*id, set),
+            ComponentValType::Type(id) => self.free_variables_component_defined_type_id(*id, set),
         }
     }
 
@@ -2172,8 +2775,12 @@ impl TypeAlloc {
     /// via the provided `set`.
     ///
     /// This requires that `id` is a `Defined` type.
-    pub(crate) fn type_named_type_id(&self, id: TypeId, set: &HashSet<TypeId>) -> bool {
-        let ty = self[id].unwrap_defined();
+    pub(crate) fn type_named_type_id(
+        &self,
+        id: ComponentDefinedTypeId,
+        set: &HashSet<ComponentAnyTypeId>,
+    ) -> bool {
+        let ty = &self[id];
         match ty {
             // Primitives are always considered named
             ComponentDefinedType::Primitive(_) => true,
@@ -2183,7 +2790,7 @@ impl TypeAlloc {
             ComponentDefinedType::Flags(_)
             | ComponentDefinedType::Enum(_)
             | ComponentDefinedType::Record(_)
-            | ComponentDefinedType::Variant(_) => set.contains(&id),
+            | ComponentDefinedType::Variant(_) => set.contains(&id.into()),
 
             // All types below here are allowed to be anonymous, but their
             // own components must be appropriately named.
@@ -2205,11 +2812,17 @@ impl TypeAlloc {
 
             // own/borrow themselves don't have to be named, but the resource
             // they refer to must be named.
-            ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => set.contains(id),
+            ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => {
+                set.contains(&(*id).into())
+            }
         }
     }
 
-    pub(crate) fn type_named_valtype(&self, ty: &ComponentValType, set: &HashSet<TypeId>) -> bool {
+    pub(crate) fn type_named_valtype(
+        &self,
+        ty: &ComponentValType,
+        set: &HashSet<ComponentAnyTypeId>,
+    ) -> bool {
         match ty {
             ComponentValType::Primitive(_) => true,
             ComponentValType::Type(id) => self.type_named_type_id(*id, set),
@@ -2222,179 +2835,200 @@ impl TypeAlloc {
 ///
 /// This currently exists to abstract over `TypeAlloc` and `SubtypeArena` which
 /// both need to perform remapping operations.
-pub(crate) trait Remap: Index<TypeId, Output = Type> {
+pub(crate) trait Remap
+where
+    Self: Index<ComponentTypeId, Output = ComponentType>,
+    Self: Index<ComponentDefinedTypeId, Output = ComponentDefinedType>,
+    Self: Index<ComponentInstanceTypeId, Output = ComponentInstanceType>,
+    Self: Index<ComponentFuncTypeId, Output = ComponentFuncType>,
+{
     /// Pushes a new anonymous type within this object, returning an identifier
     /// which can be used to refer to it.
-    fn push_ty(&mut self, ty: Type) -> TypeId;
-
-    /// Applies a resource and type renaming map to the `id` specified,
-    /// returning `true` if the `id` was modified or `false` if it didn't need
-    /// changing.
-    ///
-    /// This will recursively modify the structure of the `id` specified and
-    /// update all references to resources found. The resource identifier keys
-    /// in the `map` specified will become the corresponding value, in addition
-    /// to any existing types in `map` becoming different tyeps.
-    ///
-    /// The `id` argument will be rewritten to a new identifier if `true` is
-    /// returned.
-    fn remap_type_id(&mut self, id: &mut TypeId, map: &mut Remapping) -> bool {
-        if let Some(new) = map.types.get(id) {
-            let changed = *new != *id;
-            *id = *new;
+    fn push_ty<T>(&mut self, ty: T) -> T::Id
+    where
+        T: TypeData;
+
+    fn map_map(
+        tmp: &mut IndexMap<ResourceId, Vec<usize>>,
+        any_changed: &mut bool,
+        map: &mut Remapping,
+    ) {
+        for (id, path) in mem::take(tmp) {
+            let id = match map.resources.get(&id) {
+                Some(id) => {
+                    *any_changed = true;
+                    *id
+                }
+                None => id,
+            };
+            tmp.insert(id, path);
+        }
+    }
+
+    fn insert_if_any_changed<T>(
+        &mut self,
+        map: &mut Remapping,
+        any_changed: bool,
+        id: &mut T::Id,
+        ty: T,
+    ) -> bool
+    where
+        T: TypeData,
+        T::Id: Into<ComponentAnyTypeId>,
+    {
+        let new = if any_changed { self.push_ty(ty) } else { *id };
+        map.types.insert((*id).into(), new.into());
+        let changed = *id != new;
+        *id = new;
+        changed
+    }
+
+    fn remap_component_any_type_id(
+        &mut self,
+        id: &mut ComponentAnyTypeId,
+        map: &mut Remapping,
+    ) -> bool {
+        match id {
+            ComponentAnyTypeId::Resource(id) => self.remap_resource_id(id, map),
+            ComponentAnyTypeId::Defined(id) => self.remap_component_defined_type_id(id, map),
+            ComponentAnyTypeId::Func(id) => self.remap_component_func_type_id(id, map),
+            ComponentAnyTypeId::Instance(id) => self.remap_component_instance_type_id(id, map),
+            ComponentAnyTypeId::Component(id) => self.remap_component_type_id(id, map),
+        }
+    }
+
+    fn remap_resource_id(&mut self, id: &mut AliasableResourceId, map: &mut Remapping) -> bool {
+        if let Some(changed) = map.remap_id(id) {
             return changed;
         }
 
-        // This function attempts what ends up probably being a relatively
-        // minor optimization where a new `id` isn't manufactured unless
-        // something about the type actually changed. That means that if the
-        // type doesn't actually use any resources, for example, then no new
-        // id is allocated.
-        let mut any_changed = false;
+        match map.resources.get(&id.resource()) {
+            None => false,
+            Some(new_id) => {
+                *id.resource_mut() = *new_id;
+                true
+            }
+        }
+    }
 
-        let map_map = |tmp: &mut IndexMap<ResourceId, Vec<usize>>,
-                       any_changed: &mut bool,
-                       map: &mut Remapping| {
-            for (id, path) in mem::take(tmp) {
-                let id = match map.resources.get(&id) {
-                    Some(id) => {
-                        *any_changed = true;
-                        *id
-                    }
-                    None => id,
-                };
-                tmp.insert(id, path);
+    fn remap_component_type_id(&mut self, id: &mut ComponentTypeId, map: &mut Remapping) -> bool {
+        if let Some(changed) = map.remap_id(id) {
+            return changed;
+        }
+
+        let mut any_changed = false;
+        let mut ty = self[*id].clone();
+        for ty in ty.imports.values_mut().chain(ty.exports.values_mut()) {
+            any_changed |= self.remap_component_entity(ty, map);
+        }
+        for (id, _) in ty
+            .imported_resources
+            .iter_mut()
+            .chain(&mut ty.defined_resources)
+        {
+            if let Some(new) = map.resources.get(id) {
+                *id = *new;
+                any_changed = true;
             }
-        };
+        }
+        Self::map_map(&mut ty.explicit_resources, &mut any_changed, map);
+        self.insert_if_any_changed(map, any_changed, id, ty)
+    }
 
-        let ty = match &self[*id] {
-            // Core wasm functions/modules/instances don't have resource types
-            // in them.
-            Type::Sub(_) | Type::Module(_) | Type::Instance(_) => return false,
+    fn remap_component_defined_type_id(
+        &mut self,
+        id: &mut ComponentDefinedTypeId,
+        map: &mut Remapping,
+    ) -> bool {
+        if let Some(changed) = map.remap_id(id) {
+            return changed;
+        }
 
-            Type::Component(i) => {
-                let mut tmp = i.clone();
-                for ty in tmp.imports.values_mut().chain(tmp.exports.values_mut()) {
-                    if self.remap_component_entity(ty, map) {
-                        any_changed = true;
-                    }
+        let mut any_changed = false;
+        let mut tmp = self[*id].clone();
+        match &mut tmp {
+            ComponentDefinedType::Primitive(_)
+            | ComponentDefinedType::Flags(_)
+            | ComponentDefinedType::Enum(_) => {}
+            ComponentDefinedType::Record(r) => {
+                for ty in r.fields.values_mut() {
+                    any_changed |= self.remap_valtype(ty, map);
                 }
-                for (id, _) in tmp
-                    .imported_resources
-                    .iter_mut()
-                    .chain(&mut tmp.defined_resources)
-                {
-                    if let Some(new) = map.resources.get(id) {
-                        *id = *new;
-                        any_changed = true;
-                    }
+            }
+            ComponentDefinedType::Tuple(r) => {
+                for ty in r.types.iter_mut() {
+                    any_changed |= self.remap_valtype(ty, map);
                 }
-                map_map(&mut tmp.explicit_resources, &mut any_changed, map);
-                Type::Component(tmp)
             }
-
-            Type::ComponentInstance(i) => {
-                let mut tmp = i.clone();
-                for ty in tmp.exports.values_mut() {
-                    if self.remap_component_entity(ty, map) {
-                        any_changed = true;
+            ComponentDefinedType::Variant(r) => {
+                for ty in r.cases.values_mut() {
+                    if let Some(ty) = &mut ty.ty {
+                        any_changed |= self.remap_valtype(ty, map);
                     }
                 }
-                for id in tmp.defined_resources.iter_mut() {
-                    if let Some(new) = map.resources.get(id) {
-                        *id = *new;
-                        any_changed = true;
-                    }
+            }
+            ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
+                any_changed |= self.remap_valtype(ty, map);
+            }
+            ComponentDefinedType::Result { ok, err } => {
+                if let Some(ok) = ok {
+                    any_changed |= self.remap_valtype(ok, map);
                 }
-                map_map(&mut tmp.explicit_resources, &mut any_changed, map);
-                Type::ComponentInstance(tmp)
+                if let Some(err) = err {
+                    any_changed |= self.remap_valtype(err, map);
+                }
+            }
+            ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => {
+                any_changed |= self.remap_resource_id(id, map);
             }
+        }
+        self.insert_if_any_changed(map, any_changed, id, tmp)
+    }
 
-            Type::Resource(id) => {
-                let id = match map.resources.get(id).copied() {
-                    Some(id) => id,
-                    None => return false,
-                };
+    fn remap_component_instance_type_id(
+        &mut self,
+        id: &mut ComponentInstanceTypeId,
+        map: &mut Remapping,
+    ) -> bool {
+        if let Some(changed) = map.remap_id(id) {
+            return changed;
+        }
+
+        let mut any_changed = false;
+        let mut tmp = self[*id].clone();
+        for ty in tmp.exports.values_mut() {
+            any_changed |= self.remap_component_entity(ty, map);
+        }
+        for id in tmp.defined_resources.iter_mut() {
+            if let Some(new) = map.resources.get(id) {
+                *id = *new;
                 any_changed = true;
-                Type::Resource(id)
-            }
-
-            Type::ComponentFunc(i) => {
-                let mut tmp = i.clone();
-                for ty in tmp
-                    .params
-                    .iter_mut()
-                    .map(|(_, ty)| ty)
-                    .chain(tmp.results.iter_mut().map(|(_, ty)| ty))
-                {
-                    if self.remap_valtype(ty, map) {
-                        any_changed = true;
-                    }
-                }
-                Type::ComponentFunc(tmp)
-            }
-            Type::Defined(i) => {
-                let mut tmp = i.clone();
-                match &mut tmp {
-                    ComponentDefinedType::Primitive(_)
-                    | ComponentDefinedType::Flags(_)
-                    | ComponentDefinedType::Enum(_) => {}
-                    ComponentDefinedType::Record(r) => {
-                        for ty in r.fields.values_mut() {
-                            if self.remap_valtype(ty, map) {
-                                any_changed = true;
-                            }
-                        }
-                    }
-                    ComponentDefinedType::Tuple(r) => {
-                        for ty in r.types.iter_mut() {
-                            if self.remap_valtype(ty, map) {
-                                any_changed = true;
-                            }
-                        }
-                    }
-                    ComponentDefinedType::Variant(r) => {
-                        for ty in r.cases.values_mut() {
-                            if let Some(ty) = &mut ty.ty {
-                                if self.remap_valtype(ty, map) {
-                                    any_changed = true;
-                                }
-                            }
-                        }
-                    }
-                    ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
-                        if self.remap_valtype(ty, map) {
-                            any_changed = true;
-                        }
-                    }
-                    ComponentDefinedType::Result { ok, err } => {
-                        if let Some(ok) = ok {
-                            if self.remap_valtype(ok, map) {
-                                any_changed = true;
-                            }
-                        }
-                        if let Some(err) = err {
-                            if self.remap_valtype(err, map) {
-                                any_changed = true;
-                            }
-                        }
-                    }
-                    ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => {
-                        if self.remap_type_id(id, map) {
-                            any_changed = true;
-                        }
-                    }
-                }
-                Type::Defined(tmp)
             }
-        };
+        }
+        Self::map_map(&mut tmp.explicit_resources, &mut any_changed, map);
+        self.insert_if_any_changed(map, any_changed, id, tmp)
+    }
 
-        let new = if any_changed { self.push_ty(ty) } else { *id };
-        let prev = map.types.insert(*id, new);
-        assert!(prev.is_none());
-        let changed = *id != new;
-        *id = new;
-        changed
+    fn remap_component_func_type_id(
+        &mut self,
+        id: &mut ComponentFuncTypeId,
+        map: &mut Remapping,
+    ) -> bool {
+        if let Some(changed) = map.remap_id(id) {
+            return changed;
+        }
+
+        let mut any_changed = false;
+        let mut tmp = self[*id].clone();
+        for ty in tmp
+            .params
+            .iter_mut()
+            .map(|(_, ty)| ty)
+            .chain(tmp.results.iter_mut().map(|(_, ty)| ty))
+        {
+            any_changed |= self.remap_valtype(ty, map);
+        }
+        self.insert_if_any_changed(map, any_changed, id, tmp)
     }
 
     /// Same as `remap_type_id`, but works with `ComponentEntityType`.
@@ -2404,21 +3038,24 @@ pub(crate) trait Remap: Index<TypeId, Output = Type> {
         map: &mut Remapping,
     ) -> bool {
         match ty {
-            ComponentEntityType::Module(id)
-            | ComponentEntityType::Func(id)
-            | ComponentEntityType::Instance(id)
-            | ComponentEntityType::Component(id) => self.remap_type_id(id, map),
+            ComponentEntityType::Module(_) => {
+                // Can't reference resources.
+                false
+            }
+            ComponentEntityType::Func(id) => self.remap_component_func_type_id(id, map),
+            ComponentEntityType::Instance(id) => self.remap_component_instance_type_id(id, map),
+            ComponentEntityType::Component(id) => self.remap_component_type_id(id, map),
             ComponentEntityType::Type {
                 referenced,
                 created,
             } => {
-                let changed = self.remap_type_id(referenced, map);
+                let mut changed = self.remap_component_any_type_id(referenced, map);
                 if *referenced == *created {
                     *created = *referenced;
-                    changed
                 } else {
-                    self.remap_type_id(created, map) || changed
+                    changed |= self.remap_component_any_type_id(created, map);
                 }
+                changed
             }
             ComponentEntityType::Value(ty) => self.remap_valtype(ty, map),
         }
@@ -2428,12 +3065,12 @@ pub(crate) trait Remap: Index<TypeId, Output = Type> {
     fn remap_valtype(&mut self, ty: &mut ComponentValType, map: &mut Remapping) -> bool {
         match ty {
             ComponentValType::Primitive(_) => false,
-            ComponentValType::Type(id) => self.remap_type_id(id, map),
+            ComponentValType::Type(id) => self.remap_component_defined_type_id(id, map),
         }
     }
 }
 
-#[derive(Default)]
+#[derive(Debug, Default)]
 pub(crate) struct Remapping {
     /// A mapping from old resource ID to new resource ID.
     pub(crate) resources: HashMap<ResourceId, ResourceId>,
@@ -2441,24 +3078,47 @@ pub(crate) struct Remapping {
     /// A mapping filled in during the remapping process which records how a
     /// type was remapped, if applicable. This avoids remapping multiple
     /// references to the same type and instead only processing it once.
-    types: HashMap<TypeId, TypeId>,
+    types: HashMap<ComponentAnyTypeId, ComponentAnyTypeId>,
 }
 
 impl Remap for TypeAlloc {
-    fn push_ty(&mut self, ty: Type) -> TypeId {
-        <TypeAlloc>::push_ty(self, ty)
+    fn push_ty<T>(&mut self, ty: T) -> T::Id
+    where
+        T: TypeData,
+    {
+        <TypeList>::push(self, ty)
     }
 }
 
-impl Index<TypeId> for TypeAlloc {
-    type Output = Type;
+impl<T> Index<T> for TypeAlloc
+where
+    T: TypeIdentifier,
+{
+    type Output = T::Data;
 
     #[inline]
-    fn index(&self, id: TypeId) -> &Type {
+    fn index(&self, id: T) -> &T::Data {
         &self.list[id]
     }
 }
 
+impl Remapping {
+    fn remap_id<T>(&self, id: &mut T) -> Option<bool>
+    where
+        T: Copy + Into<ComponentAnyTypeId> + TryFrom<ComponentAnyTypeId>,
+        T::Error: std::fmt::Debug,
+    {
+        let old: ComponentAnyTypeId = (*id).into();
+        let new = self.types.get(&old)?;
+        if *new == old {
+            Some(false)
+        } else {
+            *id = T::try_from(*new).expect("should never remap across different kinds");
+            Some(true)
+        }
+    }
+}
+
 /// Helper structure used to perform subtyping computations.
 ///
 /// This type is used whenever a subtype needs to be tested in one direction or
@@ -2499,12 +3159,12 @@ impl<'a> SubtypeCx<'a> {
     ///
     /// This enables `f` to modify the internal arenas while relying on all
     /// changes being discarded after the closure finishes.
-    fn mark<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
-        let a_len = self.a.list.len();
-        let b_len = self.b.list.len();
+    fn with_checkpoint<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
+        let a = self.a.list.checkpoint();
+        let b = self.b.list.checkpoint();
         let result = f(self);
-        self.a.list.truncate(a_len);
-        self.b.list.truncate(b_len);
+        self.a.list.reset_to_checkpoint(a);
+        self.b.list.reset_to_checkpoint(b);
         result
     }
 
@@ -2520,241 +3180,295 @@ impl<'a> SubtypeCx<'a> {
         use ComponentEntityType::*;
 
         match (a, b) {
-            (Module(a), Module(b)) => {
-                // For module type subtyping, all exports in the other module
-                // type must be present in this module type's exports (i.e. it
-                // can export *more* than what this module type needs).
-                // However, for imports, the check is reversed (i.e. it is okay
-                // to import *less* than what this module type needs).
-                self.swap();
-                let a_imports = &self.b[*a].unwrap_module().imports;
-                let b_imports = &self.a[*b].unwrap_module().imports;
-                for (k, a) in a_imports {
-                    match b_imports.get(k) {
-                        Some(b) => self.entity_type(b, a, offset).with_context(|| {
-                            format!("type mismatch in import `{}::{}`", k.0, k.1)
-                        })?,
-                        None => bail!(offset, "missing expected import `{}::{}`", k.0, k.1),
-                    }
-                }
-                self.swap();
-                let a = self.a[*a].unwrap_module();
-                let b = self.b[*b].unwrap_module();
-                for (k, b) in b.exports.iter() {
-                    match a.exports.get(k) {
-                        Some(a) => self
-                            .entity_type(a, b, offset)
-                            .with_context(|| format!("type mismatch in export `{k}`"))?,
-                        None => bail!(offset, "missing expected export `{k}`"),
-                    }
-                }
-                Ok(())
-            }
+            (Module(a), Module(b)) => self.module_type(*a, *b, offset),
             (Module(_), b) => bail!(offset, "expected {}, found module", b.desc()),
 
-            (Func(a), Func(b)) => {
-                let a = self.a[*a].unwrap_component_func();
-                let b = self.b[*b].unwrap_component_func();
-
-                // Note that this intentionally diverges from the upstream
-                // specification in terms of subtyping. This is a full
-                // type-equality check which ensures that the structure of `a`
-                // exactly matches the structure of `b`. The rationale for this
-                // is:
-                //
-                // * Primarily in Wasmtime subtyping based on function types is
-                //   not implemented. This includes both subtyping a host
-                //   import and additionally handling subtyping as functions
-                //   cross component boundaries. The host import subtyping (or
-                //   component export subtyping) is not clear how to handle at
-                //   all at this time. The subtyping of functions between
-                //   components can more easily be handled by extending the
-                //   `fact` compiler, but that hasn't been done yet.
-                //
-                // * The upstream specification is currently pretty
-                //   intentionally vague precisely what subtyping is allowed.
-                //   Implementing a strict check here is intended to be a
-                //   conservative starting point for the component model which
-                //   can be extended in the future if necessary.
-                //
-                // * The interaction with subtyping on bindings generation, for
-                //   example, is a tricky problem that doesn't have a clear
-                //   answer at this time.  Effectively this is more rationale
-                //   for being conservative in the first pass of the component
-                //   model.
-                //
-                // So, in conclusion, the test here (and other places that
-                // reference this comment) is for exact type equality with no
-                // differences.
-                if a.params.len() != b.params.len() {
-                    bail!(
-                        offset,
-                        "expected {} parameters, found {}",
-                        b.params.len(),
-                        a.params.len(),
-                    );
-                }
-                if a.results.len() != b.results.len() {
-                    bail!(
-                        offset,
-                        "expected {} results, found {}",
-                        b.results.len(),
-                        a.results.len(),
-                    );
-                }
-                for ((an, a), (bn, b)) in a.params.iter().zip(b.params.iter()) {
-                    if an != bn {
-                        bail!(offset, "expected parameter named `{bn}`, found `{an}`");
-                    }
-                    self.component_val_type(a, b, offset)
-                        .with_context(|| format!("type mismatch in function parameter `{an}`"))?;
-                }
-                for ((an, a), (bn, b)) in a.results.iter().zip(b.results.iter()) {
-                    if an != bn {
-                        bail!(offset, "mismatched result names");
-                    }
-                    self.component_val_type(a, b, offset)
-                        .with_context(|| "type mismatch with result type")?;
-                }
-                Ok(())
-            }
+            (Func(a), Func(b)) => self.component_func_type(*a, *b, offset),
             (Func(_), b) => bail!(offset, "expected {}, found func", b.desc()),
 
             (Value(a), Value(b)) => self.component_val_type(a, b, offset),
             (Value(_), b) => bail!(offset, "expected {}, found value", b.desc()),
 
             (Type { referenced: a, .. }, Type { referenced: b, .. }) => {
-                use self::Type::*;
-                match (&self.a[*a], &self.b[*b]) {
-                    (Defined(a), Defined(b)) => self.component_defined_type(a, b, offset),
-                    (Defined(_), Resource(_)) => bail!(offset, "expected resource, found type"),
-                    (Resource(a), Resource(b)) => {
-                        if a == b {
-                            Ok(())
-                        } else {
-                            bail!(offset, "resource types are not the same")
-                        }
-                    }
-                    (Resource(_), Defined(_)) => bail!(offset, "expected type, found resource"),
-                    _ => unreachable!(),
-                }
+                self.component_any_type_id(*a, *b, offset)
             }
             (Type { .. }, b) => bail!(offset, "expected {}, found type", b.desc()),
 
-            (Instance(a_id), Instance(b_id)) => {
-                // For instance type subtyping, all exports in the other
-                // instance type must be present in this instance type's
-                // exports (i.e. it can export *more* than what this instance
-                // type needs).
-                let a = self.a[*a_id].unwrap_component_instance();
-                let b = self.b[*b_id].unwrap_component_instance();
-
-                let mut exports = Vec::with_capacity(b.exports.len());
-                for (k, b) in b.exports.iter() {
-                    match a.exports.get(k) {
-                        Some(a) => exports.push((*a, *b)),
-                        None => bail!(offset, "missing expected export `{k}`"),
-                    }
-                }
-                for (i, (a, b)) in exports.iter().enumerate() {
-                    let err = match self.component_entity_type(a, b, offset) {
-                        Ok(()) => continue,
-                        Err(e) => e,
-                    };
-                    // On failure attach the name of this export as context to
-                    // the error message to leave a breadcrumb trail.
-                    let (name, _) = self.b[*b_id]
-                        .unwrap_component_instance()
-                        .exports
-                        .get_index(i)
-                        .unwrap();
-                    return Err(
-                        err.with_context(|| format!("type mismatch in instance export `{name}`"))
-                    );
-                }
-                Ok(())
-            }
+            (Instance(a), Instance(b)) => self.component_instance_type(*a, *b, offset),
             (Instance(_), b) => bail!(offset, "expected {}, found instance", b.desc()),
 
-            (Component(a), Component(b)) => {
-                // Components are ... tricky. They follow the same basic
-                // structure as core wasm modules, but they also have extra
-                // logic to handle resource types. Resources are effectively
-                // abstract types so this is sort of where an ML module system
-                // in the component model becomes a reality.
-                //
-                // This also leverages the `open_instance_type` method below
-                // heavily which internally has its own quite large suite of
-                // logic. More-or-less what's happening here is:
-                //
-                // 1. Pretend that the imports of B are given as values to the
-                //    imports of A. If A didn't import anything, for example,
-                //    that's great and the subtyping definitely passes there.
-                //    This operation produces a mapping of all the resources of
-                //    A's imports to resources in B's imports.
-                //
-                // 2. This mapping is applied to all of A's exports. This means
-                //    that all exports of A referring to A's imported resources
-                //    now instead refer to B's. Note, though that A's exports
-                //    still refer to its own defined resources.
-                //
-                // 3. The same `open_instance_type` method used during the
-                //    first step is used again, but this time on the exports
-                //    in the reverse direction. This performs a similar
-                //    operation, though, by creating a mapping from B's
-                //    defined resources to A's defined resources. The map
-                //    itself is discarded as it's not needed.
-                //
-                // The order that everything passed here is intentional, but
-                // also subtle. I personally think of it as
-                // `open_instance_type` takes a list of things to satisfy a
-                // signature and produces a mapping of resources in the
-                // signature to those provided in the list of things. The
-                // order of operations then goes:
-                //
-                // * Someone thinks they have a component of type B, but they
-                //   actually have a component of type A (e.g. due to this
-                //   subtype check passing).
-                // * This person provides the imports of B and that must be
-                //   sufficient to satisfy the imports of A. This is the first
-                //   `open_instance_type` check.
-                // * Now though the resources provided by B are substituted
-                //   into A's exports since that's what was provided.
-                // * A's exports are then handed back to the original person,
-                //   and these exports must satisfy the signature required by B
-                //   since that's what they're expecting.
-                // * This is the second `open_instance_type` which, to get
-                //   resource types to line up, will map from A's defined
-                //   resources to B's defined resources.
-                //
-                // If all that passes then the resources should all line up
-                // perfectly. Any misalignment is reported as a subtyping
-                // error.
-                let b_imports = self.b[*b]
-                    .unwrap_component()
-                    .imports
-                    .iter()
-                    .map(|(name, ty)| (name.clone(), ty.clone()))
-                    .collect();
-                self.swap();
-                let mut import_mapping =
-                    self.open_instance_type(&b_imports, *a, ExternKind::Import, offset)?;
-                self.swap();
-                self.mark(|this| {
-                    let mut a_exports = this.a[*a]
-                        .unwrap_component()
-                        .exports
-                        .iter()
-                        .map(|(name, ty)| (name.clone(), ty.clone()))
-                        .collect::<IndexMap<_, _>>();
-                    for ty in a_exports.values_mut() {
-                        this.a.remap_component_entity(ty, &mut import_mapping);
-                    }
-                    this.open_instance_type(&a_exports, *b, ExternKind::Export, offset)?;
+            (Component(a), Component(b)) => self.component_type(*a, *b, offset),
+            (Component(_), b) => bail!(offset, "expected {}, found component", b.desc()),
+        }
+    }
+
+    pub fn component_type(
+        &mut self,
+        a: ComponentTypeId,
+        b: ComponentTypeId,
+        offset: usize,
+    ) -> Result<()> {
+        // Components are ... tricky. They follow the same basic
+        // structure as core wasm modules, but they also have extra
+        // logic to handle resource types. Resources are effectively
+        // abstract types so this is sort of where an ML module system
+        // in the component model becomes a reality.
+        //
+        // This also leverages the `open_instance_type` method below
+        // heavily which internally has its own quite large suite of
+        // logic. More-or-less what's happening here is:
+        //
+        // 1. Pretend that the imports of B are given as values to the
+        //    imports of A. If A didn't import anything, for example,
+        //    that's great and the subtyping definitely passes there.
+        //    This operation produces a mapping of all the resources of
+        //    A's imports to resources in B's imports.
+        //
+        // 2. This mapping is applied to all of A's exports. This means
+        //    that all exports of A referring to A's imported resources
+        //    now instead refer to B's. Note, though that A's exports
+        //    still refer to its own defined resources.
+        //
+        // 3. The same `open_instance_type` method used during the
+        //    first step is used again, but this time on the exports
+        //    in the reverse direction. This performs a similar
+        //    operation, though, by creating a mapping from B's
+        //    defined resources to A's defined resources. The map
+        //    itself is discarded as it's not needed.
+        //
+        // The order that everything passed here is intentional, but
+        // also subtle. I personally think of it as
+        // `open_instance_type` takes a list of things to satisfy a
+        // signature and produces a mapping of resources in the
+        // signature to those provided in the list of things. The
+        // order of operations then goes:
+        //
+        // * Someone thinks they have a component of type B, but they
+        //   actually have a component of type A (e.g. due to this
+        //   subtype check passing).
+        // * This person provides the imports of B and that must be
+        //   sufficient to satisfy the imports of A. This is the first
+        //   `open_instance_type` check.
+        // * Now though the resources provided by B are substituted
+        //   into A's exports since that's what was provided.
+        // * A's exports are then handed back to the original person,
+        //   and these exports must satisfy the signature required by B
+        //   since that's what they're expecting.
+        // * This is the second `open_instance_type` which, to get
+        //   resource types to line up, will map from A's defined
+        //   resources to B's defined resources.
+        //
+        // If all that passes then the resources should all line up
+        // perfectly. Any misalignment is reported as a subtyping
+        // error.
+        let b_imports = self.b[b]
+            .imports
+            .iter()
+            .map(|(name, ty)| (name.clone(), ty.clone()))
+            .collect();
+        self.swap();
+        let mut import_mapping =
+            self.open_instance_type(&b_imports, a, ExternKind::Import, offset)?;
+        self.swap();
+        self.with_checkpoint(|this| {
+            let mut a_exports = this.a[a]
+                .exports
+                .iter()
+                .map(|(name, ty)| (name.clone(), ty.clone()))
+                .collect::<IndexMap<_, _>>();
+            for ty in a_exports.values_mut() {
+                this.a.remap_component_entity(ty, &mut import_mapping);
+            }
+            this.open_instance_type(&a_exports, b, ExternKind::Export, offset)?;
+            Ok(())
+        })
+    }
+
+    pub fn component_instance_type(
+        &mut self,
+        a_id: ComponentInstanceTypeId,
+        b_id: ComponentInstanceTypeId,
+        offset: usize,
+    ) -> Result<()> {
+        // For instance type subtyping, all exports in the other
+        // instance type must be present in this instance type's
+        // exports (i.e. it can export *more* than what this instance
+        // type needs).
+        let a = &self.a[a_id];
+        let b = &self.b[b_id];
+
+        let mut exports = Vec::with_capacity(b.exports.len());
+        for (k, b) in b.exports.iter() {
+            match a.exports.get(k) {
+                Some(a) => exports.push((*a, *b)),
+                None => bail!(offset, "missing expected export `{k}`"),
+            }
+        }
+        for (i, (a, b)) in exports.iter().enumerate() {
+            let err = match self.component_entity_type(a, b, offset) {
+                Ok(()) => continue,
+                Err(e) => e,
+            };
+            // On failure attach the name of this export as context to
+            // the error message to leave a breadcrumb trail.
+            let (name, _) = self.b[b_id].exports.get_index(i).unwrap();
+            return Err(err.with_context(|| format!("type mismatch in instance export `{name}`")));
+        }
+        Ok(())
+    }
+
+    pub fn component_func_type(
+        &mut self,
+        a: ComponentFuncTypeId,
+        b: ComponentFuncTypeId,
+        offset: usize,
+    ) -> Result<()> {
+        let a = &self.a[a];
+        let b = &self.b[b];
+
+        // Note that this intentionally diverges from the upstream
+        // specification in terms of subtyping. This is a full
+        // type-equality check which ensures that the structure of `a`
+        // exactly matches the structure of `b`. The rationale for this
+        // is:
+        //
+        // * Primarily in Wasmtime subtyping based on function types is
+        //   not implemented. This includes both subtyping a host
+        //   import and additionally handling subtyping as functions
+        //   cross component boundaries. The host import subtyping (or
+        //   component export subtyping) is not clear how to handle at
+        //   all at this time. The subtyping of functions between
+        //   components can more easily be handled by extending the
+        //   `fact` compiler, but that hasn't been done yet.
+        //
+        // * The upstream specification is currently pretty
+        //   intentionally vague precisely what subtyping is allowed.
+        //   Implementing a strict check here is intended to be a
+        //   conservative starting point for the component model which
+        //   can be extended in the future if necessary.
+        //
+        // * The interaction with subtyping on bindings generation, for
+        //   example, is a tricky problem that doesn't have a clear
+        //   answer at this time.  Effectively this is more rationale
+        //   for being conservative in the first pass of the component
+        //   model.
+        //
+        // So, in conclusion, the test here (and other places that
+        // reference this comment) is for exact type equality with no
+        // differences.
+        if a.params.len() != b.params.len() {
+            bail!(
+                offset,
+                "expected {} parameters, found {}",
+                b.params.len(),
+                a.params.len(),
+            );
+        }
+        if a.results.len() != b.results.len() {
+            bail!(
+                offset,
+                "expected {} results, found {}",
+                b.results.len(),
+                a.results.len(),
+            );
+        }
+        for ((an, a), (bn, b)) in a.params.iter().zip(b.params.iter()) {
+            if an != bn {
+                bail!(offset, "expected parameter named `{bn}`, found `{an}`");
+            }
+            self.component_val_type(a, b, offset)
+                .with_context(|| format!("type mismatch in function parameter `{an}`"))?;
+        }
+        for ((an, a), (bn, b)) in a.results.iter().zip(b.results.iter()) {
+            if an != bn {
+                bail!(offset, "mismatched result names");
+            }
+            self.component_val_type(a, b, offset)
+                .with_context(|| "type mismatch with result type")?;
+        }
+        Ok(())
+    }
+
+    pub fn module_type(
+        &mut self,
+        a: ComponentCoreModuleTypeId,
+        b: ComponentCoreModuleTypeId,
+        offset: usize,
+    ) -> Result<()> {
+        // For module type subtyping, all exports in the other module
+        // type must be present in this module type's exports (i.e. it
+        // can export *more* than what this module type needs).
+        // However, for imports, the check is reversed (i.e. it is okay
+        // to import *less* than what this module type needs).
+        self.swap();
+        let a_imports = &self.b[a].imports;
+        let b_imports = &self.a[b].imports;
+        for (k, a) in a_imports {
+            match b_imports.get(k) {
+                Some(b) => self
+                    .entity_type(b, a, offset)
+                    .with_context(|| format!("type mismatch in import `{}::{}`", k.0, k.1))?,
+                None => bail!(offset, "missing expected import `{}::{}`", k.0, k.1),
+            }
+        }
+        self.swap();
+        let a = &self.a[a];
+        let b = &self.b[b];
+        for (k, b) in b.exports.iter() {
+            match a.exports.get(k) {
+                Some(a) => self
+                    .entity_type(a, b, offset)
+                    .with_context(|| format!("type mismatch in export `{k}`"))?,
+                None => bail!(offset, "missing expected export `{k}`"),
+            }
+        }
+        Ok(())
+    }
+
+    pub fn component_any_type_id(
+        &mut self,
+        a: ComponentAnyTypeId,
+        b: ComponentAnyTypeId,
+        offset: usize,
+    ) -> Result<()> {
+        match (a, b) {
+            (ComponentAnyTypeId::Resource(a), ComponentAnyTypeId::Resource(b)) => {
+                if a.resource() == b.resource() {
                     Ok(())
-                })
+                } else {
+                    bail!(offset, "resource types are not the same")
+                }
+            }
+            (ComponentAnyTypeId::Resource(_), b) => {
+                bail!(offset, "expected {}, found resource", b.desc())
+            }
+            (ComponentAnyTypeId::Defined(a), ComponentAnyTypeId::Defined(b)) => {
+                self.component_defined_type(a, b, offset)
+            }
+            (ComponentAnyTypeId::Defined(_), b) => {
+                bail!(offset, "expected {}, found defined type", b.desc())
+            }
+
+            (ComponentAnyTypeId::Func(a), ComponentAnyTypeId::Func(b)) => {
+                self.component_func_type(a, b, offset)
+            }
+            (ComponentAnyTypeId::Func(_), b) => {
+                bail!(offset, "expected {}, found func type", b.desc())
+            }
+
+            (ComponentAnyTypeId::Instance(a), ComponentAnyTypeId::Instance(b)) => {
+                self.component_instance_type(a, b, offset)
+            }
+            (ComponentAnyTypeId::Instance(_), b) => {
+                bail!(offset, "expected {}, found instance type", b.desc())
+            }
+
+            (ComponentAnyTypeId::Component(a), ComponentAnyTypeId::Component(b)) => {
+                self.component_type(a, b, offset)
+            }
+            (ComponentAnyTypeId::Component(_), b) => {
+                bail!(offset, "expected {}, found component type", b.desc())
             }
-            (Component(_), b) => bail!(offset, "expected {}, found component", b.desc()),
         }
     }
 
@@ -2784,7 +3498,7 @@ impl<'a> SubtypeCx<'a> {
     pub fn open_instance_type(
         &mut self,
         a: &IndexMap<String, ComponentEntityType>,
-        b: TypeId,
+        b: ComponentTypeId,
         kind: ExternKind,
         offset: usize,
     ) -> Result<Remapping> {
@@ -2817,7 +3531,7 @@ impl<'a> SubtypeCx<'a> {
         // this loop and are deferred below to the main subtyping check. That
         // means that `mapping` won't necessarily have a mapping for all
         // imported resources into `component_type`, but that should be ok.
-        let component_type = self.b[b].unwrap_component();
+        let component_type = &self.b[b];
         let entities = match kind {
             ExternKind::Import => &component_type.imports,
             ExternKind::Export => &component_type.exports,
@@ -2842,16 +3556,10 @@ impl<'a> SubtypeCx<'a> {
                     ComponentEntityType::Instance(id) => id,
                     _ => unreachable!(),
                 };
-                let (name, next_ty) = self.b[id]
-                    .unwrap_component_instance()
-                    .exports
-                    .get_index(i)
-                    .unwrap();
+                let (name, next_ty) = self.b[id].exports.get_index(i).unwrap();
                 ty = *next_ty;
                 arg = match arg {
-                    Some(ComponentEntityType::Instance(id)) => {
-                        self.a[*id].unwrap_component_instance().exports.get(name)
-                    }
+                    Some(ComponentEntityType::Instance(id)) => self.a[*id].exports.get(name),
                     _ => continue 'outer,
                 };
             }
@@ -2860,8 +3568,8 @@ impl<'a> SubtypeCx<'a> {
             // indeed the expected resource.
             if cfg!(debug_assertions) {
                 let id = match ty {
-                    ComponentEntityType::Type { created, .. } => match &self.a[created] {
-                        Type::Resource(r) => *r,
+                    ComponentEntityType::Type { created, .. } => match created {
+                        ComponentAnyTypeId::Resource(id) => id.resource(),
                         _ => unreachable!(),
                     },
                     _ => unreachable!(),
@@ -2873,8 +3581,8 @@ impl<'a> SubtypeCx<'a> {
             // it's skipped and this'll wind up generating an error later on in
             // subtype checking below.
             if let Some(ComponentEntityType::Type { created, .. }) = arg {
-                if let Type::Resource(r) = &self.b[*created] {
-                    mapping.resources.insert(*resource, *r);
+                if let ComponentAnyTypeId::Resource(r) = created {
+                    mapping.resources.insert(*resource, r.resource());
                 }
             }
         }
@@ -2897,9 +3605,9 @@ impl<'a> SubtypeCx<'a> {
                 None => bail!(offset, "missing {} named `{name}`", kind.desc()),
             }
         }
-        let mut type_map = HashMap::new();
+        let mut type_map = HashMap::default();
         for (i, (actual, expected)) in to_typecheck.into_iter().enumerate() {
-            let result = self.mark(|this| {
+            let result = self.with_checkpoint(|this| {
                 let mut expected = expected;
                 this.b.remap_component_entity(&mut expected, &mut mapping);
                 mapping.types.clear();
@@ -2920,7 +3628,7 @@ impl<'a> SubtypeCx<'a> {
 
             // If an error happens then attach the name of the entity to the
             // error message using the `i` iteration counter.
-            let component_type = self.b[b].unwrap_component();
+            let component_type = &self.b[b];
             let entities = match kind {
                 ExternKind::Import => &component_type.imports,
                 ExternKind::Export => &component_type.exports,
@@ -2950,7 +3658,7 @@ impl<'a> SubtypeCx<'a> {
 
         match (a, b) {
             (EntityType::Func(a), EntityType::Func(b)) => {
-                self.func_type(self.a[*a].unwrap_func(), self.b[*b].unwrap_func(), offset)
+                self.core_func_type(self.a[*a].unwrap_func(), self.b[*b].unwrap_func(), offset)
             }
             (EntityType::Func(_), b) => bail!(offset, "expected {}, found func", b.desc()),
             (EntityType::Table(a), EntityType::Table(b)) => {
@@ -3000,13 +3708,13 @@ impl<'a> SubtypeCx<'a> {
             }
             (EntityType::Global(_), b) => bail!(offset, "expected {}, found global", b.desc()),
             (EntityType::Tag(a), EntityType::Tag(b)) => {
-                self.func_type(self.a[*a].unwrap_func(), self.b[*b].unwrap_func(), offset)
+                self.core_func_type(self.a[*a].unwrap_func(), self.b[*b].unwrap_func(), offset)
             }
             (EntityType::Tag(_), b) => bail!(offset, "expected {}, found tag", b.desc()),
         }
     }
 
-    fn func_type(&self, a: &FuncType, b: &FuncType, offset: usize) -> Result<()> {
+    fn core_func_type(&self, a: &FuncType, b: &FuncType, offset: usize) -> Result<()> {
         if a == b {
             Ok(())
         } else {
@@ -3030,30 +3738,24 @@ impl<'a> SubtypeCx<'a> {
             (ComponentValType::Primitive(a), ComponentValType::Primitive(b)) => {
                 self.primitive_val_type(*a, *b, offset)
             }
-            (ComponentValType::Type(a), ComponentValType::Type(b)) => self.component_defined_type(
-                self.a[*a].unwrap_defined(),
-                self.b[*b].unwrap_defined(),
-                offset,
-            ),
-            (ComponentValType::Primitive(a), ComponentValType::Type(b)) => {
-                match self.b[*b].unwrap_defined() {
-                    ComponentDefinedType::Primitive(b) => self.primitive_val_type(*a, *b, offset),
-                    b => bail!(offset, "expected {}, found {a}", b.desc()),
-                }
-            }
-            (ComponentValType::Type(a), ComponentValType::Primitive(b)) => {
-                match self.a[*a].unwrap_defined() {
-                    ComponentDefinedType::Primitive(a) => self.primitive_val_type(*a, *b, offset),
-                    a => bail!(offset, "expected {b}, found {}", a.desc()),
-                }
+            (ComponentValType::Type(a), ComponentValType::Type(b)) => {
+                self.component_defined_type(*a, *b, offset)
             }
+            (ComponentValType::Primitive(a), ComponentValType::Type(b)) => match &self.b[*b] {
+                ComponentDefinedType::Primitive(b) => self.primitive_val_type(*a, *b, offset),
+                b => bail!(offset, "expected {}, found {a}", b.desc()),
+            },
+            (ComponentValType::Type(a), ComponentValType::Primitive(b)) => match &self.a[*a] {
+                ComponentDefinedType::Primitive(a) => self.primitive_val_type(*a, *b, offset),
+                a => bail!(offset, "expected {b}, found {}", a.desc()),
+            },
         }
     }
 
     fn component_defined_type(
         &self,
-        a: &ComponentDefinedType,
-        b: &ComponentDefinedType,
+        a: ComponentDefinedTypeId,
+        b: ComponentDefinedTypeId,
         offset: usize,
     ) -> Result<()> {
         use ComponentDefinedType::*;
@@ -3061,7 +3763,7 @@ impl<'a> SubtypeCx<'a> {
         // Note that the implementation of subtyping here diverges from the
         // upstream specification intentionally, see the documentation on
         // function subtyping for more information.
-        match (a, b) {
+        match (&self.a[a], &self.b[b]) {
             (Primitive(a), Primitive(b)) => self.primitive_val_type(*a, *b, offset),
             (Primitive(a), b) => bail!(offset, "expected {}, found {a}", b.desc()),
             (Record(a), Record(b)) => {
@@ -3164,9 +3866,7 @@ impl<'a> SubtypeCx<'a> {
             }
             (Result { .. }, b) => bail!(offset, "expected {}, found result", b.desc()),
             (Own(a), Own(b)) | (Borrow(a), Borrow(b)) => {
-                let a = self.a[*a].unwrap_resource();
-                let b = self.b[*b].unwrap_resource();
-                if a == b {
+                if a.resource() == b.resource() {
                     Ok(())
                 } else {
                     bail!(offset, "resource types are not the same")
@@ -3200,7 +3900,7 @@ impl<'a> SubtypeCx<'a> {
         &self,
         actual: ComponentEntityType,
         expected: ComponentEntityType,
-        type_map: &mut HashMap<TypeId, TypeId>,
+        type_map: &mut HashMap<ComponentAnyTypeId, ComponentAnyTypeId>,
     ) {
         match (expected, actual) {
             (
@@ -3215,9 +3915,8 @@ impl<'a> SubtypeCx<'a> {
                 assert!(prev.is_none());
             }
             (ComponentEntityType::Instance(expected), ComponentEntityType::Instance(actual)) => {
-                let actual = self.a[actual].unwrap_component_instance();
-                for (name, expected) in self.b[expected].unwrap_component_instance().exports.iter()
-                {
+                let actual = &self.a[actual];
+                for (name, expected) in self.b[expected].exports.iter() {
                     let actual = actual.exports[name];
                     self.register_type_renamings(actual, *expected, type_map);
                 }
@@ -3238,39 +3937,46 @@ impl<'a> SubtypeCx<'a> {
 /// new types pushed into this arena are purely temporary.
 pub(crate) struct SubtypeArena<'a> {
     types: &'a TypeList,
-    list: Vec<Type>,
+    list: TypeList,
 }
 
 impl<'a> SubtypeArena<'a> {
     fn new(types: &'a TypeList) -> SubtypeArena<'a> {
         SubtypeArena {
             types,
-            list: Vec::new(),
+            list: TypeList::default(),
         }
     }
 }
 
-impl Index<TypeId> for SubtypeArena<'_> {
-    type Output = Type;
+impl<T> Index<T> for SubtypeArena<'_>
+where
+    T: TypeIdentifier,
+{
+    type Output = T::Data;
 
-    fn index(&self, id: TypeId) -> &Type {
-        if id.index() < self.types.len() {
+    fn index(&self, id: T) -> &T::Data {
+        let index = id.index();
+        if index < T::list(self.types).len() {
             &self.types[id]
         } else {
-            &self.list[id.index() - self.types.len()]
+            let temp_index = index - T::list(self.types).len();
+            let temp_index = u32::try_from(temp_index).unwrap();
+            let temp_id = T::from_index(temp_index);
+            &self.list[temp_id]
         }
     }
 }
 
 impl Remap for SubtypeArena<'_> {
-    fn push_ty(&mut self, ty: Type) -> TypeId {
-        let index = self.list.len() + self.types.len();
+    fn push_ty<T>(&mut self, ty: T) -> T::Id
+    where
+        T: TypeData,
+    {
+        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);
-        TypeId {
-            index,
-            unique_id: 0,
-        }
+        T::Id::from_index(index)
     }
 }
 
diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs
index 1fa393b7..1a6b0386 100644
--- a/crates/wasmprinter/src/lib.rs
+++ b/crates/wasmprinter/src/lib.rs
@@ -640,7 +640,13 @@ impl Printer {
             states.last().unwrap().core.types.len() as u32,
         )?;
         let ty = match ty {
-            CoreType::Func(ty) => {
+            CoreType::Sub(ty) => {
+                let ty = match &ty.structural_type {
+                    StructuralType::Func(f) => f,
+                    StructuralType::Array(_) | StructuralType::Struct(_) => {
+                        unreachable!("Wasm GC types cannot appear in components yet")
+                    }
+                };
                 self.result.push(' ');
                 self.start_group("func");
                 self.print_func_type(states.last().unwrap(), &ty, None)?;
@@ -648,7 +654,7 @@ impl Printer {
                 Some(SubType {
                     is_final: true,
                     supertype_idx: None,
-                    structural_type: StructuralType::Func(ty),
+                    structural_type: StructuralType::Func(ty.clone()),
                 })
             }
             CoreType::Module(decls) => {
diff --git a/crates/wit-component/src/decoding.rs b/crates/wit-component/src/decoding.rs
index a998104a..47064a11 100644
--- a/crates/wit-component/src/decoding.rs
+++ b/crates/wit-component/src/decoding.rs
@@ -44,6 +44,7 @@ impl<'a> ComponentInfo<'a> {
 
         for payload in Parser::new(0).parse_all(bytes) {
             let payload = payload?;
+
             match validator.payload(&payload)? {
                 ValidPayload::Ok => {}
                 ValidPayload::Parser(_) => depth += 1,
@@ -99,8 +100,8 @@ impl<'a> ComponentInfo<'a> {
             };
             match export.kind {
                 ComponentExternalKind::Type => matches!(
-                    &self.types[self.types.component_type_at(export.index)],
-                    types::Type::Component(_)
+                    self.types.component_any_type_at(export.index),
+                    types::ComponentAnyTypeId::Component(_)
                 ),
                 _ => false,
             }
@@ -140,7 +141,7 @@ impl<'a> ComponentInfo<'a> {
                 _ => unreachable!(),
             };
             let id = self.types.component_type_at(export.index);
-            let ty = self.types[id].unwrap_component();
+            let ty = &self.types[id];
             if pkg.is_some() {
                 bail!("more than one top-level exported component type found");
             }
@@ -189,7 +190,7 @@ impl<'a> ComponentInfo<'a> {
 
             let index = export.index;
             let id = self.types.component_type_at(index);
-            let component = self.types[id].unwrap_component();
+            let component = &self.types[id];
 
             // The single export of this component will determine if it's a world or an interface:
             // worlds export a component, while interfaces export an instance.
@@ -202,27 +203,22 @@ impl<'a> ComponentInfo<'a> {
 
             let name = component.exports.keys().nth(0).unwrap();
 
-            let exported = match component.exports[name] {
-                types::ComponentEntityType::Component(ty) => &self.types[ty],
-                types::ComponentEntityType::Instance(ty) => &self.types[ty],
-                _ => unreachable!(),
-            };
-
-            let name = match exported {
-                types::Type::Component(ty) => {
-                    let package_name = decoder.decode_world(name.as_str(), ty, &mut fields)?;
+            let name = match component.exports[name] {
+                types::ComponentEntityType::Component(ty) => {
+                    let package_name =
+                        decoder.decode_world(name.as_str(), &self.types[ty], &mut fields)?;
                     package_name
                 }
-                types::Type::ComponentInstance(ty) => {
+                types::ComponentEntityType::Instance(ty) => {
                     let package_name = decoder.decode_interface(
                         name.as_str(),
                         &component.imports,
-                        ty,
+                        &self.types[ty],
                         &mut fields,
                     )?;
                     package_name
                 }
-                _ => panic!(),
+                _ => unreachable!(),
             };
 
             if let Some(pkg_name) = pkg_name.as_ref() {
@@ -397,7 +393,7 @@ struct WitPackageDecoder<'a> {
     resources: HashMap<TypeOwner, HashMap<String, TypeId>>,
 
     /// A map from a type id to what it's been translated to.
-    type_map: HashMap<types::TypeId, TypeId>,
+    type_map: HashMap<types::ComponentAnyTypeId, TypeId>,
 }
 
 impl WitPackageDecoder<'_> {
@@ -410,9 +406,7 @@ impl WitPackageDecoder<'_> {
         // importing from remote packages.
         for (name, ty) in ty.imports.iter() {
             let ty = match ty {
-                types::ComponentEntityType::Instance(idx) => {
-                    self.info.types[*idx].unwrap_component_instance()
-                }
+                types::ComponentEntityType::Instance(idx) => &self.info.types[*idx],
                 _ => bail!("import `{name}` is not an instance"),
             };
             self.register_import(name, ty)
@@ -449,12 +443,12 @@ impl WitPackageDecoder<'_> {
         for (name, ty) in ty.exports.iter() {
             match ty {
                 types::ComponentEntityType::Instance(idx) => {
-                    let ty = self.info.types[*idx].unwrap_component_instance();
+                    let ty = &self.info.types[*idx];
                     self.register_interface(name.as_str(), ty, &mut fields)
                         .with_context(|| format!("failed to process export `{name}`"))?;
                 }
                 types::ComponentEntityType::Component(idx) => {
-                    let ty = self.info.types[*idx].unwrap_component();
+                    let ty = &self.info.types[*idx];
                     self.register_world(name.as_str(), ty, &mut fields)
                         .with_context(|| format!("failed to process export `{name}`"))?;
                 }
@@ -491,9 +485,7 @@ impl WitPackageDecoder<'_> {
 
         for (name, ty) in imports.iter() {
             let ty = match ty {
-                types::ComponentEntityType::Instance(idx) => {
-                    self.info.types[*idx].unwrap_component_instance()
-                }
+                types::ComponentEntityType::Instance(idx) => &self.info.types[*idx],
                 _ => bail!("import `{name}` is not an instance"),
             };
             self.register_import(name, ty)
@@ -550,7 +542,7 @@ impl WitPackageDecoder<'_> {
         let owner = TypeOwner::World(world);
         let (name, item) = match ty {
             types::ComponentEntityType::Instance(i) => {
-                let ty = self.info.types[i].unwrap_component_instance();
+                let ty = &self.info.types[i];
                 let (name, id) = if name.contains('/') {
                     let id = self.register_import(name, ty)?;
                     (WorldKey::Interface(id), id)
@@ -561,7 +553,7 @@ impl WitPackageDecoder<'_> {
                 (name, WorldItem::Interface(id))
             }
             types::ComponentEntityType::Func(i) => {
-                let ty = self.info.types[i].unwrap_component_func();
+                let ty = &self.info.types[i];
                 let func = self
                     .convert_function(name, ty, owner)
                     .with_context(|| format!("failed to decode function from import `{name}`"))?;
@@ -595,7 +587,7 @@ impl WitPackageDecoder<'_> {
         let ty = types.component_entity_type_of_export(name).unwrap();
         let (name, item) = match ty {
             types::ComponentEntityType::Func(i) => {
-                let ty = types[i].unwrap_component_func();
+                let ty = &types[i];
                 let func = self
                     .convert_function(name, ty, TypeOwner::World(world))
                     .with_context(|| format!("failed to decode function from export `{name}`"))?;
@@ -603,7 +595,7 @@ impl WitPackageDecoder<'_> {
                 (WorldKey::Name(name.to_string()), WorldItem::Function(func))
             }
             types::ComponentEntityType::Instance(i) => {
-                let ty = types[i].unwrap_component_instance();
+                let ty = &types[i];
                 let (name, id) = if name.contains('/') {
                     let id = self.register_import(name, ty)?;
                     (WorldKey::Interface(id), id)
@@ -671,9 +663,11 @@ impl WitPackageDecoder<'_> {
                         // roundtripping assertions during fuzzing.
                         Some(id) => {
                             log::debug!("type already exist");
-                            match &self.info.types[referenced] {
-                                types::Type::Defined(ty) => self.register_defined(id, ty)?,
-                                types::Type::Resource(_) => {}
+                            match referenced {
+                                types::ComponentAnyTypeId::Defined(ty) => {
+                                    self.register_defined(id, &self.info.types[ty])?;
+                                }
+                                types::ComponentAnyTypeId::Resource(_) => {}
                                 _ => unreachable!(),
                             }
                             let prev = self.type_map.insert(created, id);
@@ -715,7 +709,7 @@ impl WitPackageDecoder<'_> {
                 // functions for remote dependencies and otherwise assert
                 // they're already defined for local dependencies.
                 types::ComponentEntityType::Func(ty) => {
-                    let def = self.info.types[ty].unwrap_component_func();
+                    let def = &self.info.types[ty];
                     if self.resolve.interfaces[interface]
                         .functions
                         .contains_key(name.as_str())
@@ -741,7 +735,7 @@ impl WitPackageDecoder<'_> {
         Ok(interface)
     }
 
-    fn find_alias(&self, id: types::TypeId) -> Option<TypeId> {
+    fn find_alias(&self, id: types::ComponentAnyTypeId) -> Option<TypeId> {
         // Consult `type_map` for `referenced` or anything in its
         // chain of aliases to determine what it maps to. This may
         // bottom out in `None` in the case that this type is
@@ -874,7 +868,7 @@ impl WitPackageDecoder<'_> {
                 }
 
                 types::ComponentEntityType::Func(ty) => {
-                    let ty = self.info.types[ty].unwrap_component_func();
+                    let ty = &self.info.types[ty];
                     let func = self
                         .convert_function(name.as_str(), ty, owner)
                         .with_context(|| format!("failed to convert function '{name}'"))?;
@@ -927,8 +921,8 @@ impl WitPackageDecoder<'_> {
         &mut self,
         name: &str,
         owner: TypeOwner,
-        referenced: types::TypeId,
-        created: types::TypeId,
+        referenced: types::ComponentAnyTypeId,
+        created: types::ComponentAnyTypeId,
     ) -> Result<TypeId> {
         let kind = match self.find_alias(referenced) {
             // If this `TypeId` points to a type which has
@@ -943,11 +937,11 @@ impl WitPackageDecoder<'_> {
             // been seen before, so declare the full type.
             None => {
                 log::debug!("type export for `{name}` is a new type");
-                match &self.info.types[referenced] {
-                    types::Type::Defined(ty) => self
-                        .convert_defined(ty)
+                match referenced {
+                    types::ComponentAnyTypeId::Defined(ty) => self
+                        .convert_defined(&self.info.types[ty])
                         .context("failed to convert unaliased type")?,
-                    types::Type::Resource(_) => TypeDefKind::Resource,
+                    types::ComponentAnyTypeId::Resource(_) => TypeDefKind::Resource,
                     _ => unreachable!(),
                 }
             }
@@ -999,7 +993,7 @@ impl WitPackageDecoder<'_> {
         for (name, ty) in ty.imports.iter() {
             let (name, item) = match ty {
                 types::ComponentEntityType::Instance(idx) => {
-                    let ty = self.info.types[*idx].unwrap_component_instance();
+                    let ty = &self.info.types[*idx];
                     let (name, id) = if name.contains('/') {
                         // If a name is an interface import then it is either to
                         // a package-local or foreign interface, and both
@@ -1024,7 +1018,7 @@ impl WitPackageDecoder<'_> {
                     (WorldKey::Name(name.to_string()), WorldItem::Type(ty))
                 }
                 types::ComponentEntityType::Func(idx) => {
-                    let ty = self.info.types[*idx].unwrap_component_func();
+                    let ty = &self.info.types[*idx];
                     let func = self.convert_function(name.as_str(), ty, owner)?;
                     (WorldKey::Name(name.to_string()), WorldItem::Function(func))
                 }
@@ -1036,7 +1030,7 @@ impl WitPackageDecoder<'_> {
         for (name, ty) in ty.exports.iter() {
             let (name, item) = match ty {
                 types::ComponentEntityType::Instance(idx) => {
-                    let ty = self.info.types[*idx].unwrap_component_instance();
+                    let ty = &self.info.types[*idx];
                     let (name, id) = if name.contains('/') {
                         // Note that despite this being an export this is
                         // calling `register_import`. With a URL this interface
@@ -1053,7 +1047,7 @@ impl WitPackageDecoder<'_> {
                 }
 
                 types::ComponentEntityType::Func(idx) => {
-                    let ty = self.info.types[*idx].unwrap_component_func();
+                    let ty = &self.info.types[*idx];
                     let func = self.convert_function(name.as_str(), ty, owner)?;
                     (WorldKey::Name(name.to_string()), WorldItem::Function(func))
                 }
@@ -1135,7 +1129,7 @@ impl WitPackageDecoder<'_> {
         };
 
         // Don't create duplicate types for anything previously created.
-        if let Some(ret) = self.type_map.get(&id) {
+        if let Some(ret) = self.type_map.get(&id.into()) {
             return Ok(Type::Id(*ret));
         }
 
@@ -1144,7 +1138,7 @@ impl WitPackageDecoder<'_> {
         // errors on those types, but eventually the `bail!` here  is
         // more-or-less unreachable due to expected validation to be added to
         // the component model binary format itself.
-        let def = self.info.types[id].unwrap_defined();
+        let def = &self.info.types[id];
         let kind = self.convert_defined(def)?;
         match &kind {
             TypeDefKind::Type(_)
@@ -1171,7 +1165,7 @@ impl WitPackageDecoder<'_> {
             owner: TypeOwner::None,
             kind,
         });
-        let prev = self.type_map.insert(id, ty);
+        let prev = self.type_map.insert(id.into(), ty);
         assert!(prev.is_none());
         Ok(Type::Id(ty))
     }
@@ -1278,12 +1272,12 @@ impl WitPackageDecoder<'_> {
             }
 
             types::ComponentDefinedType::Own(id) => {
-                let id = self.type_map[id];
+                let id = self.type_map[&(*id).into()];
                 Ok(TypeDefKind::Handle(Handle::Own(id)))
             }
 
             types::ComponentDefinedType::Borrow(id) => {
-                let id = self.type_map[id];
+                let id = self.type_map[&(*id).into()];
                 Ok(TypeDefKind::Handle(Handle::Borrow(id)))
             }
         }
@@ -1415,7 +1409,7 @@ impl WitPackageDecoder<'_> {
 /// wit-defined type.
 struct Registrar<'a> {
     types: &'a types::Types,
-    type_map: &'a mut HashMap<types::TypeId, TypeId>,
+    type_map: &'a mut HashMap<types::ComponentAnyTypeId, TypeId>,
     resolve: &'a Resolve,
 }
 
@@ -1545,10 +1539,10 @@ impl Registrar<'_> {
             Type::Id(id) => *id,
             _ => bail!("expected id-based type"),
         };
-        let prev = match self.type_map.insert(wasm, wit) {
+        let prev = match self.type_map.insert(wasm.into(), wit) {
             Some(prev) => prev,
             None => {
-                let wasm = self.types[wasm].unwrap_defined();
+                let wasm = &self.types[wasm];
                 return self.defined(wit, wasm);
             }
         };
diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs
index 1262add2..2548940f 100644
--- a/crates/wit-component/src/validation.rs
+++ b/crates/wit-component/src/validation.rs
@@ -237,7 +237,7 @@ pub fn validate_module<'a>(
             None if adapters.contains(name) => {
                 let map = ret.adapters_required.entry(name).or_default();
                 for (func, ty) in funcs {
-                    let ty = types[types.core_type_at(*ty)].unwrap_func();
+                    let ty = types[types.core_type_at(*ty).unwrap_sub()].unwrap_func();
                     map.insert(func, ty.clone());
                 }
             }
@@ -526,7 +526,7 @@ pub fn validate_adapter_module<'a>(
                 Some(idx) => *idx,
                 None => bail!("adapter module did not export `{name}`"),
             };
-            let id = types.function_at(idx);
+            let id = types.core_function_at(idx);
             let actual = types[id].unwrap_func();
             validate_func_sig(name, ty, actual)?;
         }
@@ -618,7 +618,7 @@ fn validate_imports_top_level(
     for (name, ty) in funcs {
         match resolve.worlds[world].imports.get(&world_key(resolve, name)) {
             Some(WorldItem::Function(func)) => {
-                let ty = types[types.core_type_at(*ty)].unwrap_func();
+                let ty = types[types.core_type_at(*ty).unwrap_sub()].unwrap_func();
                 validate_func(resolve, ty, func, AbiVariant::GuestImport)?;
             }
             Some(_) => bail!("expected world top-level import `{name}` to be a function"),
@@ -642,7 +642,7 @@ fn valid_imported_resource_func<'a>(
 ) -> Result<Option<&'a str>> {
     if let Some(resource_name) = func_name.strip_prefix(RESOURCE_DROP) {
         if is_resource(resource_name) {
-            let ty = types[types.core_type_at(ty)].unwrap_func();
+            let ty = types[types.core_type_at(ty).unwrap_sub()].unwrap_func();
             let expected = FuncType::new([ValType::I32], []);
             validate_func_sig(func_name, &expected, ty)?;
             return Ok(Some(resource_name));
@@ -665,7 +665,7 @@ fn valid_exported_resource_func<'a>(
         .or_else(|| func_name.strip_prefix(RESOURCE_NEW))
     {
         if is_resource(resource_name) {
-            let ty = types[types.core_type_at(ty)].unwrap_func();
+            let ty = types[types.core_type_at(ty).unwrap_sub()].unwrap_func();
             let expected = FuncType::new([ValType::I32], [ValType::I32]);
             validate_func_sig(func_name, &expected, ty)?;
             return Ok(Some(resource_name));
@@ -692,7 +692,7 @@ fn validate_imported_interface(
     for (func_name, ty) in imports {
         match resolve.interfaces[interface].functions.get(*func_name) {
             Some(f) => {
-                let ty = types[types.core_type_at(*ty)].unwrap_func();
+                let ty = types[types.core_type_at(*ty).unwrap_sub()].unwrap_func();
                 validate_func(resolve, ty, f, AbiVariant::GuestImport)?;
             }
             None => match valid_imported_resource_func(func_name, *ty, types, is_resource)? {
@@ -776,7 +776,7 @@ fn validate_exported_item<'a>(
                 expected_export_name
             ),
         };
-        let id = types.function_at(*func_index);
+        let id = types.core_function_at(*func_index);
         let ty = types[id].unwrap_func();
         validate_func(resolve, ty, func, AbiVariant::GuestExport)?;
 
@@ -784,7 +784,7 @@ fn validate_exported_item<'a>(
         if let Some(index) = exports.get(&post_return[..]) {
             let ok = post_returns.insert(post_return);
             assert!(ok);
-            let id = types.function_at(*index);
+            let id = types.core_function_at(*index);
             let ty = types[id].unwrap_func();
             validate_post_return(resolve, ty, func)?;
         }
@@ -813,7 +813,7 @@ fn validate_exported_item<'a>(
                 };
                 let dtor = format!("{export_name}#[dtor]{name}");
                 if let Some((_, name, func_idx)) = exports.get_full(dtor.as_str()) {
-                    let id = types.function_at(*func_idx);
+                    let id = types.core_function_at(*func_idx);
                     let ty = types[id].unwrap_func();
                     let expected = FuncType::new([ValType::I32], []);
                     validate_func_sig(name, &expected, ty)?;


Test patch:
diff --git a/tests/local/component-model/resources.wast b/tests/local/component-model/resources.wast
index e68f1b53..99398081 100644
--- a/tests/local/component-model/resources.wast
+++ b/tests/local/component-model/resources.wast
@@ -488,7 +488,7 @@
     (type $x u32)
     (instance (instantiate $c (with "x" (type $x))))
   )
-  "expected resource, found type")
+  "expected resource, found defined type")
 
 (assert_invalid
   (component
@@ -499,7 +499,7 @@
     (type $x (resource (rep i32)))
     (instance (instantiate $c (with "x" (type $x))))
   )
-  "expected type, found resource")
+  "expected defined type, found resource")
 
 (assert_invalid
   (component
diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs
index daba84f6..a14896ff 100644
--- a/tests/roundtrip.rs
+++ b/tests/roundtrip.rs
@@ -21,7 +21,7 @@
 //!
 //!     cargo test --test roundtrip local/ref.wat
 
-use anyhow::{bail, Context, Result};
+use anyhow::{anyhow, bail, Context, Result};
 use rayon::prelude::*;
 use std::io::{Read, Write};
 use std::path::{Path, PathBuf};
@@ -209,11 +209,15 @@ struct TestState {
 
 impl TestState {
     fn run_test(&self, test: &Path, contents: &[u8]) -> Result<()> {
-        let result = match test.extension().and_then(|s| s.to_str()) {
-            Some("wat") => self.test_wat(test),
-            Some("wast") => self.test_wast(test, contents),
-            _ => bail!("unknown file extension {:?}", test),
-        };
+        let result =
+            match std::panic::catch_unwind(|| match test.extension().and_then(|s| s.to_str()) {
+                Some("wat") => self.test_wat(test),
+                Some("wast") => self.test_wast(test, contents),
+                _ => bail!("unknown file extension {:?}", test),
+            }) {
+                Ok(result) => result,
+                Err(e) => Err(anyhow!("panicked: {e:?}")),
+            };
         result.with_context(|| format!("failed test: {}", test.display()))
     }
 
@@ -309,6 +313,7 @@ impl TestState {
             .enumerate()
             .filter_map(|(index, directive)| {
                 let span = directive.span();
+
                 self.test_wast_directive(test, directive, index)
                     .with_context(|| {
                         let (line, col) = span.linecol_in(contents);