use crate::{ Function, FunctionKind, InterfaceId, Resolve, Type, TypeDef, TypeDefKind, TypeId, WorldId, WorldItem, }; use indexmap::IndexSet; #[derive(Default)] pub struct LiveTypes { set: IndexSet, } impl LiveTypes { pub fn iter(&self) -> impl Iterator + '_ { self.set.iter().copied() } pub fn len(&self) -> usize { self.set.len() } pub fn add_interface(&mut self, resolve: &Resolve, iface: InterfaceId) { self.visit_interface(resolve, iface); } pub fn add_world(&mut self, resolve: &Resolve, world: WorldId) { self.visit_world(resolve, world); } pub fn add_world_item(&mut self, resolve: &Resolve, item: &WorldItem) { self.visit_world_item(resolve, item); } pub fn add_func(&mut self, resolve: &Resolve, func: &Function) { self.visit_func(resolve, func); } pub fn add_type_id(&mut self, resolve: &Resolve, ty: TypeId) { self.visit_type_id(resolve, ty); } pub fn add_type(&mut self, resolve: &Resolve, ty: &Type) { self.visit_type(resolve, ty); } } impl TypeIdVisitor for LiveTypes { fn before_visit_type_id(&mut self, id: TypeId) -> bool { !self.set.contains(&id) } fn after_visit_type_id(&mut self, id: TypeId) { assert!(self.set.insert(id)); } } /// Helper trait to walk the structure of a type and visit all `TypeId`s that /// it refers to, possibly transitively. pub trait TypeIdVisitor { /// Callback invoked just before a type is visited. /// /// If this function returns `false` the type is not visited, otherwise it's /// recursed into. fn before_visit_type_id(&mut self, id: TypeId) -> bool { let _ = id; true } /// Callback invoked once a type is finished being visited. fn after_visit_type_id(&mut self, id: TypeId) { let _ = id; } fn visit_interface(&mut self, resolve: &Resolve, iface: InterfaceId) { let iface = &resolve.interfaces[iface]; for (_, id) in iface.types.iter() { self.visit_type_id(resolve, *id); } for (_, func) in iface.functions.iter() { self.visit_func(resolve, func); } } fn visit_world(&mut self, resolve: &Resolve, world: WorldId) { let world = &resolve.worlds[world]; for (_, item) in world.imports.iter().chain(world.exports.iter()) { self.visit_world_item(resolve, item); } } fn visit_world_item(&mut self, resolve: &Resolve, item: &WorldItem) { match item { WorldItem::Interface { id, .. } => self.visit_interface(resolve, *id), WorldItem::Function(f) => self.visit_func(resolve, f), WorldItem::Type(t) => self.visit_type_id(resolve, *t), } } fn visit_func(&mut self, resolve: &Resolve, func: &Function) { match func.kind { // This resource is live as it's attached to a static method but // it's not guaranteed to be present in either params or results, so // be sure to attach it here. FunctionKind::Static(id) => self.visit_type_id(resolve, id), // The resource these are attached to is in the params/results, so // no need to re-add it here. FunctionKind::Method(_) | FunctionKind::Constructor(_) => {} FunctionKind::Freestanding => {} } for (_, ty) in func.params.iter() { self.visit_type(resolve, ty); } for ty in func.results.iter_types() { self.visit_type(resolve, ty); } } fn visit_type_id(&mut self, resolve: &Resolve, ty: TypeId) { if self.before_visit_type_id(ty) { self.visit_type_def(resolve, &resolve.types[ty]); self.after_visit_type_id(ty); } } fn visit_type_def(&mut self, resolve: &Resolve, ty: &TypeDef) { match &ty.kind { TypeDefKind::Type(t) | TypeDefKind::List(t) | TypeDefKind::Option(t) | TypeDefKind::Future(Some(t)) => self.visit_type(resolve, t), TypeDefKind::Handle(handle) => match handle { crate::Handle::Own(ty) => self.visit_type_id(resolve, *ty), crate::Handle::Borrow(ty) => self.visit_type_id(resolve, *ty), }, TypeDefKind::Resource => {} TypeDefKind::Record(r) => { for field in r.fields.iter() { self.visit_type(resolve, &field.ty); } } TypeDefKind::Tuple(r) => { for ty in r.types.iter() { self.visit_type(resolve, ty); } } TypeDefKind::Variant(v) => { for case in v.cases.iter() { if let Some(ty) = &case.ty { self.visit_type(resolve, ty); } } } TypeDefKind::Result(r) => { if let Some(ty) = &r.ok { self.visit_type(resolve, ty); } if let Some(ty) = &r.err { self.visit_type(resolve, ty); } } TypeDefKind::Stream(s) => { if let Some(ty) = &s.element { self.visit_type(resolve, ty); } if let Some(ty) = &s.end { self.visit_type(resolve, ty); } } TypeDefKind::Flags(_) | TypeDefKind::Enum(_) | TypeDefKind::Future(None) => {} TypeDefKind::Unknown => unreachable!(), } } fn visit_type(&mut self, resolve: &Resolve, ty: &Type) { match ty { Type::Id(id) => self.visit_type_id(resolve, *id), _ => {} } } } #[cfg(test)] mod tests { use super::{LiveTypes, Resolve}; fn live(wit: &str, ty: &str) -> Vec { let mut resolve = Resolve::default(); resolve.push_str("test.wit", wit).unwrap(); let (_, interface) = resolve.interfaces.iter().next_back().unwrap(); let ty = interface.types[ty]; let mut live = LiveTypes::default(); live.add_type_id(&resolve, ty); live.iter() .filter_map(|ty| resolve.types[ty].name.clone()) .collect() } #[test] fn no_deps() { let types = live( " package foo:bar; interface foo { type t = u32; } ", "t", ); assert_eq!(types, ["t"]); } #[test] fn one_dep() { let types = live( " package foo:bar; interface foo { type t = u32; type u = t; } ", "u", ); assert_eq!(types, ["t", "u"]); } #[test] fn chain() { let types = live( " package foo:bar; interface foo { resource t1; record t2 { x: t1, } variant t3 { x(t2), } flags t4 { a } enum t5 { a } type t6 = tuple; } ", "t6", ); assert_eq!(types, ["t5", "t4", "t1", "t2", "t3", "t6"]); } }