diff --git a/crates/wasm-compose/src/composer.rs b/crates/wasm-compose/src/composer.rs index b54d57873c..c7572bdcca 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> { 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 { let (_, instance_id) = self.instances.get_index(instance).unwrap(); @@ -271,13 +271,18 @@ 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 79c39b95d3..5303193c9c 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, + types: &mut HashMap, 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::>(); - 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, + defining_instances: &'b mut HashMap, 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 1f025c1e71..cc9e3f1286 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 { 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 b33b3cd526..9a38ae7d14 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 { - 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::>()?, + 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::>()?, + ) + } 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 87c0002998..53e6aa6137 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 111824732e..c64233f81b 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 f2363b82e0..8550bf280d 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, - pub core_modules: Vec, - pub core_instances: Vec, - pub core_funcs: Vec, + pub core_types: Vec, + pub core_funcs: Vec, + pub core_tags: Vec, + pub core_modules: Vec, + pub core_instances: Vec, pub core_memories: Vec, pub core_tables: Vec, pub core_globals: Vec, - pub core_tags: Vec, // Component index spaces - pub types: Vec, - pub funcs: Vec, + pub types: Vec, + pub funcs: Vec, pub values: Vec<(ComponentValType, bool)>, - pub instances: Vec, - pub components: Vec, + pub instances: Vec, + pub components: Vec, pub imports: IndexMap, pub exports: IndexMap, @@ -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, + exported_types: HashSet, /// Same as `exported_types`, but for imports. - imported_types: HashSet, + imported_types: HashSet, /// 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, + resource_name_map: HashMap, /// 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) -> &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) -> 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, + ) -> 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, + ) -> 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, + ) -> 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, + ) -> 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 { - 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 { - 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 { + 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 { 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, types: &mut TypeAlloc, offset: usize, - ) -> Result { + ) -> Result { 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 { + ) -> Result { 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::>(); - 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, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, - ) -> Result { + ) -> Result { 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, types: &mut TypeAlloc, offset: usize, - ) -> Result { + ) -> Result { 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 { 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 { - let types = if core { &self.core_types } else { &self.types }; - types + pub fn core_type_at(&self, idx: u32, offset: usize) -> Result { + 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 { + 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 { + fn function_at(&self, idx: u32, offset: usize) -> Result { 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 { + fn component_at(&self, idx: u32, offset: usize) -> Result { 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 { + fn instance_at(&self, idx: u32, offset: usize) -> Result { 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 { - 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 { + 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 { + fn core_function_at(&self, idx: u32, offset: usize) -> Result { 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 { + fn module_at(&self, idx: u32, offset: usize) -> Result { 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 { + fn core_instance_at(&self, idx: u32, offset: usize) -> Result { 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 c63a81c52e..46fee3e7d5 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>, // Stores indexes into the validator's types list. - pub types: Vec, + pub types: Vec, pub tables: Vec, pub memories: Vec, pub globals: Vec, @@ -486,7 +486,7 @@ pub(crate) struct Module { pub data_count: Option, // Stores indexes into `types`. pub functions: Vec, - pub tags: Vec, + pub tags: Vec, pub function_references: HashSet, pub imports: IndexMap<(String, String), Vec>, pub exports: IndexMap, @@ -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 { + ) -> 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 { + pub fn type_id_at(&self, idx: u32, offset: usize) -> Result { 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 83537bb2fc..4df7eb12fd 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,542 @@ 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; + + /// 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)] + #[allow(private_interfaces)] + fn list(types: &TypeList) -> &SnapshotList; + + /// Get an exclusive reference to the list where this id's type data is + /// stored within. + #[doc(hidden)] + #[allow(private_interfaces)] + fn list_mut(types: &mut TypeList) -> &mut SnapshotList; + + /// The raw index of this id. + #[doc(hidden)] + fn index(&self) -> usize; +} + +/// 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; + +/// 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; + + #[doc(hidden)] + #[allow(private_interfaces)] + fn type_info(&self, types: &TypeList) -> TypeInfo; +} + +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 } + } + + #[allow(private_interfaces)] + fn list(types: &TypeList) -> &SnapshotList { + &types.$list + } + + #[allow(private_interfaces)] + fn list_mut(types: &mut TypeList) -> &mut SnapshotList { + &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) {} + } + + // 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 unique integer assigned to this type. + /// 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), +} + +/// 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, } -impl TypeId { - pub(crate) fn index(&self) -> usize { +const _: () = { + assert!(std::mem::size_of::() <= 4); +}; + +impl TypeIdentifier for CoreTypeId { + type Data = SubType; + + fn from_index(index: u32) -> Self { + CoreTypeId { index } + } + + #[allow(private_interfaces)] + fn list(types: &TypeList) -> &SnapshotList { + &types.core_types + } + + #[allow(private_interfaces)] + fn list_mut(types: &mut TypeList) -> &mut SnapshotList { + &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; + + #[allow(private_interfaces)] + 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 { + 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 { + 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 { + 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 { + 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::() <= 8); + assert!(std::mem::size_of::() <= 8); }; +impl TypeIdentifier for ComponentDefinedTypeId { + type Data = ComponentDefinedType; + + fn from_index(index: u32) -> Self { + ComponentDefinedTypeId { + index, + alias_id: NO_ALIAS, + } + } + + #[allow(private_interfaces)] + fn list(types: &TypeList) -> &SnapshotList { + &types.component_defined_types + } + + #[allow(private_interfaces)] + fn list_mut(types: &mut TypeList) -> &mut SnapshotList { + &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: @@ -244,183 +751,46 @@ 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), - /// The definition is for a core module instance type. - /// - /// This variant is only supported when parsing a component. - Instance(Box), - /// The definition is for a component type. - /// - /// This variant is only supported when parsing a component. - Component(Box), - /// The definition is for a component instance type. - /// - /// This variant is only supported when parsing a component. - ComponentInstance(Box), - /// 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; + + #[allow(private_interfaces)] + 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 +799,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 +807,7 @@ pub enum EntityType { /// The entity is a global. Global(GlobalType), /// The entity is a tag. - Tag(TypeId), + Tag(CoreTypeId), } impl EntityType { @@ -453,7 +823,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 +886,15 @@ pub struct ModuleType { pub exports: IndexMap, } +impl TypeData for ModuleType { + type Id = ComponentCoreModuleTypeId; + + #[allow(private_interfaces)] + fn type_info(&self, _types: &TypeList) -> TypeInfo { + self.info + } +} + impl ModuleType { /// Looks up an import by its module and name. /// @@ -527,9 +906,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), } @@ -540,7 +920,16 @@ 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; + + #[allow(private_interfaces)] + fn type_info(&self, _types: &TypeList) -> TypeInfo { + self.info + } } impl InstanceType { @@ -554,8 +943,8 @@ impl InstanceType { types: &'a TypeList, ) -> &'a IndexMap { 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 +953,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 +998,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 +1064,15 @@ pub struct ComponentType { pub explicit_resources: IndexMap>, } +impl TypeData for ComponentType { + type Id = ComponentTypeId; + + #[allow(private_interfaces)] + fn type_info(&self, _types: &TypeList) -> TypeInfo { + self.info + } +} + /// Represents a type of a component instance. #[derive(Debug, Clone)] pub struct ComponentInstanceType { @@ -725,6 +1123,15 @@ pub struct ComponentInstanceType { pub explicit_resources: IndexMap>, } +impl TypeData for ComponentInstanceType { + type Id = ComponentInstanceTypeId; + + #[allow(private_interfaces)] + fn type_info(&self, _types: &TypeList) -> TypeInfo { + self.info + } +} + /// Represents a type of a component function. #[derive(Debug, Clone)] pub struct ComponentFuncType { @@ -736,6 +1143,15 @@ pub struct ComponentFuncType { pub results: Box<[(Option, ComponentValType)]>, } +impl TypeData for ComponentFuncType { + type Id = ComponentFuncTypeId; + + #[allow(private_interfaces)] + 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 +1285,32 @@ pub enum ComponentDefinedType { err: Option, }, /// 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; + + #[allow(private_interfaces)] + 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 +1333,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 +1499,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(&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 +1514,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 +1525,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 +1646,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 +1667,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 +1713,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 +1734,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 +1755,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 +1763,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 +1776,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 +1797,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 +1882,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 { + pub fn peel_alias(&self, ty: T) -> Option + where + T: Aliasable, + { self.list.peel_alias(ty) } } -impl Index for TypesRef<'_> { - type Output = Type; - fn index(&self, id: TypeId) -> &Type { - &self.list[id] +impl Index for TypesRef<'_> +where + T: TypeIdentifier, +{ + type Output = T::Data; + + fn index(&self, index: T) -> &Self::Output { + &self.list[index] } } @@ -1473,7 +1930,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(&self, id: T) -> Option<&T::Data> + where + T: TypeIdentifier, + { self.as_ref().get(id) } @@ -1485,18 +1945,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 +2038,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 +2052,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 +2084,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 +2099,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 +2117,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 +2135,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 +2153,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 +2206,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 { - self.as_ref().peel_alias(ty) + pub fn peel_alias(&self, ty: T) -> Option + where + T: Aliasable, + { + self.list.peel_alias(ty) } } -impl Index for Types { - type Output = Type; +impl Index 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] } } @@ -1766,15 +2253,10 @@ pub(crate) struct SnapshotList { // The current list of types for the current snapshot that are being built. cur: Vec, - - unique_mappings: HashMap, - unique_counter: u32, } struct Snapshot { prior_types: usize, - unique_counter: u32, - unique_mappings: HashMap, items: Vec, } @@ -1809,9 +2291,10 @@ impl SnapshotList { 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 +2307,12 @@ impl SnapshotList { // 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 +2320,9 @@ impl SnapshotList { 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 { - // 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 Index for SnapshotList { @@ -1900,11 +2334,14 @@ impl Index for SnapshotList { } } -impl Index for SnapshotList { +impl Index for SnapshotList +where + U: TypeIdentifier, +{ type Output = T; #[inline] - fn index(&self, id: TypeId) -> &T { + fn index(&self, id: U) -> &T { self.get(id.index()).unwrap() } } @@ -1915,68 +2352,211 @@ impl Default for SnapshotList { 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, - infos: SnapshotList, + // Keeps track of which `alias_id` is an alias of which other `alias_id`. + alias_mappings: HashMap, + // Counter for generating new `alias_id`s. + alias_counter: u32, + // Snapshots of previously committed `TypeList`s' aliases. + alias_snapshots: Vec, + + // Core Wasm types. + core_types: SnapshotList, + + // Component model types. + components: SnapshotList, + component_defined_types: SnapshotList, + component_values: SnapshotList, + component_instances: SnapshotList, + component_funcs: SnapshotList, + core_modules: SnapshotList, + core_instances: SnapshotList, +} + +#[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, +} + +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(&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(&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(&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 { - 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(&self, mut ty: T) -> Option + 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 for TypeList { - type Output = Type; +impl Index 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 +2604,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(&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 +2632,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) { - 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, + ) { + 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, + ) { + 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, + ) { + 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, + ) { + 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, + ) { + 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 +2759,14 @@ impl TypeAlloc { set: &mut IndexSet, ) { 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 +2776,7 @@ impl TypeAlloc { fn free_variables_valtype(&self, ty: &ComponentValType, set: &mut IndexSet) { 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 +2784,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) -> bool { - let ty = self[id].unwrap_defined(); + pub(crate) fn type_named_type_id( + &self, + id: ComponentDefinedTypeId, + set: &HashSet, + ) -> bool { + let ty = &self[id]; match ty { // Primitives are always considered named ComponentDefinedType::Primitive(_) => true, @@ -2183,7 +2799,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 +2821,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) -> bool { + pub(crate) fn type_named_valtype( + &self, + ty: &ComponentValType, + set: &HashSet, + ) -> bool { match ty { ComponentValType::Primitive(_) => true, ComponentValType::Type(id) => self.type_named_type_id(*id, set), @@ -2222,179 +2844,200 @@ impl TypeAlloc { /// /// This currently exists to abstract over `TypeAlloc` and `SubtypeArena` which /// both need to perform remapping operations. -pub(crate) trait Remap: Index { +pub(crate) trait Remap +where + Self: Index, + Self: Index, + Self: Index, + Self: Index, +{ /// 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(&mut self, ty: T) -> T::Id + where + T: TypeData; + + fn map_map( + tmp: &mut IndexMap>, + 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( + &mut self, + map: &mut Remapping, + any_changed: bool, + id: &mut T::Id, + ty: T, + ) -> bool + where + T: TypeData, + T::Id: Into, + { + 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>, - 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 +3047,24 @@ pub(crate) trait Remap: Index { 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 +3074,12 @@ pub(crate) trait Remap: Index { 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, @@ -2441,24 +3087,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, + types: HashMap, } impl Remap for TypeAlloc { - fn push_ty(&mut self, ty: Type) -> TypeId { - ::push_ty(self, ty) + fn push_ty(&mut self, ty: T) -> T::Id + where + T: TypeData, + { + ::push(self, ty) } } -impl Index for TypeAlloc { - type Output = Type; +impl Index 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(&self, id: &mut T) -> Option + where + T: Copy + Into + TryFrom, + 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 +3168,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(&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(&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 +3189,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::>(); - 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::>(); + 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 +3507,7 @@ impl<'a> SubtypeCx<'a> { pub fn open_instance_type( &mut self, a: &IndexMap, - b: TypeId, + b: ComponentTypeId, kind: ExternKind, offset: usize, ) -> Result { @@ -2817,7 +3540,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 +3565,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 +3577,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 +3590,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 +3614,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 +3637,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 +3667,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 +3717,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 +3747,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 +3772,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 +3875,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 +3909,7 @@ impl<'a> SubtypeCx<'a> { &self, actual: ComponentEntityType, expected: ComponentEntityType, - type_map: &mut HashMap, + type_map: &mut HashMap, ) { match (expected, actual) { ( @@ -3215,9 +3924,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 +3946,46 @@ impl<'a> SubtypeCx<'a> { /// new types pushed into this arena are purely temporary. pub(crate) struct SubtypeArena<'a> { types: &'a TypeList, - list: Vec, + list: TypeList, } impl<'a> SubtypeArena<'a> { fn new(types: &'a TypeList) -> SubtypeArena<'a> { SubtypeArena { types, - list: Vec::new(), + list: TypeList::default(), } } } -impl Index for SubtypeArena<'_> { - type Output = Type; +impl Index 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(&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 1fa393b7b0..1a6b0386e8 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 a998104a59..47064a116b 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>, /// A map from a type id to what it's been translated to. - type_map: HashMap, + type_map: HashMap, } 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 { + fn find_alias(&self, id: types::ComponentAnyTypeId) -> Option { // 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 { 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, + type_map: &'a mut HashMap, 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 1262add2e6..2548940fba 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> { 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)?; diff --git a/tests/local/component-model/resources.wast b/tests/local/component-model/resources.wast index e68f1b53e9..993980819b 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 daba84f67a..a14896ff1e 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);