summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wast/src/resolve/names.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/wast/src/resolve/names.rs')
-rw-r--r--third_party/rust/wast/src/resolve/names.rs1058
1 files changed, 1058 insertions, 0 deletions
diff --git a/third_party/rust/wast/src/resolve/names.rs b/third_party/rust/wast/src/resolve/names.rs
new file mode 100644
index 0000000000..91d710634a
--- /dev/null
+++ b/third_party/rust/wast/src/resolve/names.rs
@@ -0,0 +1,1058 @@
+use crate::ast::*;
+use crate::resolve::Ns;
+use crate::Error;
+use std::collections::{HashMap, HashSet};
+
+pub fn resolve<'a>(
+ id: Option<Id<'a>>,
+ fields: &mut Vec<ModuleField<'a>>,
+) -> Result<Resolver<'a>, Error> {
+ let mut names = HashMap::new();
+ let mut parents = Parents {
+ prev: None,
+ cur_id: id,
+ depth: 0,
+ names: &mut names,
+ };
+ let mut resolver = Resolver::default();
+ resolver.process(&mut parents, fields)?;
+ Ok(resolver)
+}
+
+/// Context structure used to perform name resolution.
+#[derive(Default)]
+pub struct Resolver<'a> {
+ // Namespaces within each module. Note that each namespace carries with it
+ // information about the signature of the item in that namespace. The
+ // signature is later used to synthesize the type of a module and inject
+ // type annotations if necessary.
+ funcs: Namespace<'a>,
+ globals: Namespace<'a>,
+ tables: Namespace<'a>,
+ memories: Namespace<'a>,
+ types: Namespace<'a>,
+ events: Namespace<'a>,
+ modules: Namespace<'a>,
+ instances: Namespace<'a>,
+ datas: Namespace<'a>,
+ elems: Namespace<'a>,
+ fields: Namespace<'a>,
+ type_info: Vec<TypeInfo<'a>>,
+ implicit_instances: HashSet<&'a str>,
+}
+
+impl<'a> Resolver<'a> {
+ fn process(
+ &mut self,
+ parents: &mut Parents<'a, '_>,
+ fields: &mut Vec<ModuleField<'a>>,
+ ) -> Result<(), Error> {
+ // Number everything in the module, recording what names correspond to
+ // what indices.
+ for field in fields.iter_mut() {
+ self.register(field)?;
+ }
+
+ // Then we can replace all our `Index::Id` instances with `Index::Num`
+ // in the AST. Note that this also recurses into nested modules.
+ for field in fields.iter_mut() {
+ self.resolve_field(field, parents)?;
+ }
+ Ok(())
+ }
+
+ fn register(&mut self, item: &ModuleField<'a>) -> Result<(), Error> {
+ match item {
+ ModuleField::Import(i) => {
+ // Account for implicit instances created by two-level imports
+ // first. At this time they never have a name.
+ if i.field.is_some() {
+ if self.implicit_instances.insert(i.module) {
+ self.instances.register(None, "instance")?;
+ }
+ }
+ match &i.item.kind {
+ ItemKind::Func(_) => self.funcs.register(i.item.id, "func")?,
+ ItemKind::Memory(_) => self.memories.register(i.item.id, "memory")?,
+ ItemKind::Table(_) => self.tables.register(i.item.id, "table")?,
+ ItemKind::Global(_) => self.globals.register(i.item.id, "global")?,
+ ItemKind::Event(_) => self.events.register(i.item.id, "event")?,
+ ItemKind::Module(_) => self.modules.register(i.item.id, "module")?,
+ ItemKind::Instance(_) => self.instances.register(i.item.id, "instance")?,
+ }
+ }
+ ModuleField::Global(i) => self.globals.register(i.id, "global")?,
+ ModuleField::Memory(i) => self.memories.register(i.id, "memory")?,
+ ModuleField::Func(i) => self.funcs.register(i.id, "func")?,
+ ModuleField::Table(i) => self.tables.register(i.id, "table")?,
+ ModuleField::NestedModule(m) => self.modules.register(m.id, "module")?,
+ ModuleField::Instance(i) => self.instances.register(i.id, "instance")?,
+
+ ModuleField::Type(i) => {
+ match &i.def {
+ // For GC structure types we need to be sure to populate the
+ // field namespace here as well.
+ //
+ // The field namespace is global, but the resolved indices
+ // are relative to the struct they are defined in
+ TypeDef::Struct(r#struct) => {
+ for (i, field) in r#struct.fields.iter().enumerate() {
+ if let Some(id) = field.id {
+ self.fields.register_specific(id, i as u32, "field")?;
+ }
+ }
+ }
+
+ TypeDef::Instance(_)
+ | TypeDef::Array(_)
+ | TypeDef::Func(_)
+ | TypeDef::Module(_) => {}
+ }
+
+ // Record function signatures as we see them to so we can
+ // generate errors for mismatches in references such as
+ // `call_indirect`.
+ match &i.def {
+ TypeDef::Func(f) => {
+ let params = f.params.iter().map(|p| p.2).collect();
+ let results = f.results.clone();
+ self.type_info.push(TypeInfo::Func { params, results });
+ }
+ _ => self.type_info.push(TypeInfo::Other),
+ }
+
+ self.types.register(i.id, "type")?
+ }
+ ModuleField::Elem(e) => self.elems.register(e.id, "elem")?,
+ ModuleField::Data(d) => self.datas.register(d.id, "data")?,
+ ModuleField::Event(e) => self.events.register(e.id, "event")?,
+ ModuleField::Alias(a) => match a.item_kind() {
+ ExportKind::Func => self.funcs.register(a.id, "func")?,
+ ExportKind::Table => self.tables.register(a.id, "table")?,
+ ExportKind::Memory => self.memories.register(a.id, "memory")?,
+ ExportKind::Global => self.globals.register(a.id, "global")?,
+ ExportKind::Instance => self.instances.register(a.id, "instance")?,
+ ExportKind::Module => self.modules.register(a.id, "module")?,
+ ExportKind::Event => self.events.register(a.id, "event")?,
+ ExportKind::Type => {
+ self.type_info.push(TypeInfo::Other);
+ self.types.register(a.id, "type")?
+ }
+ },
+
+ // These fields don't define any items in any index space.
+ ModuleField::Export(_) | ModuleField::Start(_) | ModuleField::Custom(_) => {
+ return Ok(())
+ }
+ };
+
+ Ok(())
+ }
+
+ fn resolve_field(
+ &self,
+ field: &mut ModuleField<'a>,
+ parents: &mut Parents<'a, '_>,
+ ) -> Result<(), Error> {
+ match field {
+ ModuleField::Import(i) => {
+ self.resolve_item_sig(&mut i.item)?;
+ Ok(())
+ }
+
+ ModuleField::Type(ty) => {
+ match &mut ty.def {
+ TypeDef::Func(func) => func.resolve(self)?,
+ TypeDef::Struct(struct_) => {
+ for field in &mut struct_.fields {
+ self.resolve_storagetype(&mut field.ty)?;
+ }
+ }
+ TypeDef::Array(array) => self.resolve_storagetype(&mut array.ty)?,
+ TypeDef::Module(m) => m.resolve(self)?,
+ TypeDef::Instance(i) => i.resolve(self)?,
+ }
+ Ok(())
+ }
+
+ ModuleField::Func(f) => {
+ let (idx, inline) = self.resolve_type_use(&mut f.ty)?;
+ let n = match idx {
+ Index::Num(n, _) => *n,
+ Index::Id(_) => panic!("expected `Num`"),
+ };
+ if let FuncKind::Inline { locals, expression } = &mut f.kind {
+ // Resolve (ref T) in locals
+ for local in locals.iter_mut() {
+ self.resolve_valtype(&mut local.ty)?;
+ }
+
+ // Build a scope with a local namespace for the function
+ // body
+ let mut scope = Namespace::default();
+
+ // Parameters come first in the scope...
+ if let Some(inline) = &inline {
+ for (id, _, _) in inline.params.iter() {
+ scope.register(*id, "local")?;
+ }
+ } else if let Some(TypeInfo::Func { params, .. }) =
+ self.type_info.get(n as usize)
+ {
+ for _ in 0..params.len() {
+ scope.register(None, "local")?;
+ }
+ }
+
+ // .. followed by locals themselves
+ for local in locals {
+ scope.register(local.id, "local")?;
+ }
+
+ // Initialize the expression resolver with this scope
+ let mut resolver = ExprResolver::new(self, scope);
+
+ // and then we can resolve the expression!
+ resolver.resolve(expression)?;
+
+ // specifically save the original `sig`, if it was present,
+ // because that's what we're using for local names.
+ f.ty.inline = inline;
+ }
+ Ok(())
+ }
+
+ ModuleField::Elem(e) => {
+ match &mut e.kind {
+ ElemKind::Active { table, offset } => {
+ self.resolve_item_ref(table)?;
+ self.resolve_expr(offset)?;
+ }
+ ElemKind::Passive { .. } | ElemKind::Declared { .. } => {}
+ }
+ match &mut e.payload {
+ ElemPayload::Indices(elems) => {
+ for idx in elems {
+ self.resolve_item_ref(idx)?;
+ }
+ }
+ ElemPayload::Exprs { exprs, ty } => {
+ for funcref in exprs {
+ if let Some(idx) = funcref {
+ self.resolve_item_ref(idx)?;
+ }
+ }
+ self.resolve_heaptype(&mut ty.heap)?;
+ }
+ }
+ Ok(())
+ }
+
+ ModuleField::Data(d) => {
+ if let DataKind::Active { memory, offset } = &mut d.kind {
+ self.resolve_item_ref(memory)?;
+ self.resolve_expr(offset)?;
+ }
+ Ok(())
+ }
+
+ ModuleField::Start(i) => {
+ self.resolve_item_ref(i)?;
+ Ok(())
+ }
+
+ ModuleField::Export(e) => {
+ self.resolve_item_ref(&mut e.index)?;
+ Ok(())
+ }
+
+ ModuleField::Global(g) => {
+ self.resolve_valtype(&mut g.ty.ty)?;
+ if let GlobalKind::Inline(expr) = &mut g.kind {
+ self.resolve_expr(expr)?;
+ }
+ Ok(())
+ }
+
+ ModuleField::Event(e) => {
+ match &mut e.ty {
+ EventType::Exception(ty) => {
+ self.resolve_type_use(ty)?;
+ }
+ }
+ Ok(())
+ }
+
+ ModuleField::Instance(i) => {
+ if let InstanceKind::Inline { module, args } = &mut i.kind {
+ self.resolve_item_ref(module)?;
+ for arg in args {
+ self.resolve_item_ref(&mut arg.index)?;
+ }
+ }
+ Ok(())
+ }
+
+ ModuleField::NestedModule(m) => {
+ let fields = match &mut m.kind {
+ NestedModuleKind::Inline { fields } => fields,
+ NestedModuleKind::Import { .. } => panic!("should only be inline"),
+ };
+ Resolver::default().process(&mut parents.push(self, m.id), fields)?;
+ Ok(())
+ }
+
+ ModuleField::Table(t) => {
+ if let TableKind::Normal(t) = &mut t.kind {
+ self.resolve_heaptype(&mut t.elem.heap)?;
+ }
+ Ok(())
+ }
+
+ ModuleField::Alias(a) => {
+ match &mut a.kind {
+ AliasKind::InstanceExport { instance, .. } => {
+ self.resolve_item_ref(instance)?;
+ }
+ AliasKind::Outer {
+ module,
+ index,
+ kind,
+ } => {
+ match (index, module) {
+ // If both indices are numeric then don't try to
+ // resolve anything since we could fail to walk up
+ // the parent chain, producing a wat2wasm error that
+ // should probably be a wasm validation error.
+ (Index::Num(..), Index::Num(..)) => {}
+ (index, module) => {
+ parents
+ .resolve(module)?
+ .resolve(index, Ns::from_export(kind))?;
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+
+ ModuleField::Memory(_) | ModuleField::Custom(_) => Ok(()),
+ }
+ }
+
+ fn resolve_valtype(&self, ty: &mut ValType<'a>) -> Result<(), Error> {
+ match ty {
+ ValType::Ref(ty) => self.resolve_heaptype(&mut ty.heap)?,
+ ValType::Rtt(_d, i) => {
+ self.resolve(i, Ns::Type)?;
+ }
+ _ => {}
+ }
+ Ok(())
+ }
+
+ fn resolve_heaptype(&self, ty: &mut HeapType<'a>) -> Result<(), Error> {
+ match ty {
+ HeapType::Index(i) => {
+ self.resolve(i, Ns::Type)?;
+ }
+ _ => {}
+ }
+ Ok(())
+ }
+
+ fn resolve_storagetype(&self, ty: &mut StorageType<'a>) -> Result<(), Error> {
+ match ty {
+ StorageType::Val(ty) => self.resolve_valtype(ty)?,
+ _ => {}
+ }
+ Ok(())
+ }
+
+ fn resolve_item_sig(&self, item: &mut ItemSig<'a>) -> Result<(), Error> {
+ match &mut item.kind {
+ ItemKind::Func(t) | ItemKind::Event(EventType::Exception(t)) => {
+ self.resolve_type_use(t)?;
+ }
+ ItemKind::Global(t) => self.resolve_valtype(&mut t.ty)?,
+ ItemKind::Instance(t) => {
+ self.resolve_type_use(t)?;
+ }
+ ItemKind::Module(m) => {
+ self.resolve_type_use(m)?;
+ }
+ ItemKind::Table(t) => {
+ self.resolve_heaptype(&mut t.elem.heap)?;
+ }
+ ItemKind::Memory(_) => {}
+ }
+ Ok(())
+ }
+
+ fn resolve_type_use<'b, T>(
+ &self,
+ ty: &'b mut TypeUse<'a, T>,
+ ) -> Result<(&'b Index<'a>, Option<T>), Error>
+ where
+ T: TypeReference<'a>,
+ {
+ let idx = ty.index.as_mut().unwrap();
+ let idx = self.resolve_item_ref(idx)?;
+
+ // If the type was listed inline *and* it was specified via a type index
+ // we need to assert they're the same.
+ //
+ // Note that we resolve the type first to transform all names to
+ // indices to ensure that all the indices line up.
+ if let Some(inline) = &mut ty.inline {
+ inline.resolve(self)?;
+ inline.check_matches(idx, self)?;
+ }
+
+ Ok((idx, ty.inline.take()))
+ }
+
+ fn resolve_expr(&self, expr: &mut Expression<'a>) -> Result<(), Error> {
+ ExprResolver::new(self, Namespace::default()).resolve(expr)
+ }
+
+ pub fn resolve(&self, idx: &mut Index<'a>, ns: Ns) -> Result<u32, Error> {
+ match ns {
+ Ns::Func => self.funcs.resolve(idx, "func"),
+ Ns::Table => self.tables.resolve(idx, "table"),
+ Ns::Global => self.globals.resolve(idx, "global"),
+ Ns::Memory => self.memories.resolve(idx, "memory"),
+ Ns::Instance => self.instances.resolve(idx, "instance"),
+ Ns::Module => self.modules.resolve(idx, "module"),
+ Ns::Event => self.events.resolve(idx, "event"),
+ Ns::Type => self.types.resolve(idx, "type"),
+ }
+ }
+
+ fn resolve_item_ref<'b, K>(&self, item: &'b mut ItemRef<'a, K>) -> Result<&'b Index<'a>, Error>
+ where
+ K: Into<ExportKind> + Copy,
+ {
+ match item {
+ ItemRef::Item { idx, kind, exports } => {
+ debug_assert!(exports.len() == 0);
+ self.resolve(
+ idx,
+ match (*kind).into() {
+ ExportKind::Func => Ns::Func,
+ ExportKind::Table => Ns::Table,
+ ExportKind::Global => Ns::Global,
+ ExportKind::Memory => Ns::Memory,
+ ExportKind::Instance => Ns::Instance,
+ ExportKind::Module => Ns::Module,
+ ExportKind::Event => Ns::Event,
+ ExportKind::Type => Ns::Type,
+ },
+ )?;
+ Ok(idx)
+ }
+ // should be expanded by now
+ ItemRef::Outer { .. } => unreachable!(),
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct Namespace<'a> {
+ names: HashMap<Id<'a>, u32>,
+ count: u32,
+}
+
+impl<'a> Namespace<'a> {
+ fn register(&mut self, name: Option<Id<'a>>, desc: &str) -> Result<u32, Error> {
+ let index = self.alloc();
+ if let Some(name) = name {
+ if let Some(_prev) = self.names.insert(name, index) {
+ // FIXME: temporarily allow duplicately-named data and element
+ // segments. This is a sort of dumb hack to get the spec test
+ // suite working (ironically).
+ //
+ // So as background, the text format disallows duplicate
+ // identifiers, causing a parse error if they're found. There
+ // are two tests currently upstream, however, data.wast and
+ // elem.wast, which *look* like they have duplicately named
+ // element and data segments. These tests, however, are using
+ // pre-bulk-memory syntax where a bare identifier was the
+ // table/memory being initialized. In post-bulk-memory this
+ // identifier is the name of the segment. Since we implement
+ // post-bulk-memory features that means that we're parsing the
+ // memory/table-to-initialize as the name of the segment.
+ //
+ // This is technically incorrect behavior but no one is
+ // hopefully relying on this too much. To get the spec tests
+ // passing we ignore errors for elem/data segments. Once the
+ // spec tests get updated enough we can remove this condition
+ // and return errors for them.
+ if desc != "elem" && desc != "data" {
+ return Err(Error::new(
+ name.span(),
+ format!("duplicate {} identifier", desc),
+ ));
+ }
+ }
+ }
+ Ok(index)
+ }
+
+ fn alloc(&mut self) -> u32 {
+ let index = self.count;
+ self.count += 1;
+ return index;
+ }
+
+ fn register_specific(&mut self, name: Id<'a>, index: u32, desc: &str) -> Result<(), Error> {
+ if let Some(_prev) = self.names.insert(name, index) {
+ return Err(Error::new(
+ name.span(),
+ format!("duplicate identifier for {}", desc),
+ ));
+ }
+ Ok(())
+ }
+
+ fn resolve(&self, idx: &mut Index<'a>, desc: &str) -> Result<u32, Error> {
+ let id = match idx {
+ Index::Num(n, _) => return Ok(*n),
+ Index::Id(id) => id,
+ };
+ if let Some(&n) = self.names.get(id) {
+ *idx = Index::Num(n, id.span());
+ return Ok(n);
+ }
+ Err(resolve_error(*id, desc))
+ }
+}
+
+fn resolve_error(id: Id<'_>, ns: &str) -> Error {
+ assert!(
+ !id.is_gensym(),
+ "symbol generated by `wast` itself cannot be resolved {:?}",
+ id
+ );
+ Error::new(
+ id.span(),
+ format!("failed to find {} named `${}`", ns, id.name()),
+ )
+}
+
+#[derive(Debug, Clone)]
+struct ExprBlock<'a> {
+ // The label of the block
+ label: Option<Id<'a>>,
+ // Whether this block pushed a new scope for resolving locals
+ pushed_scope: bool,
+}
+
+struct ExprResolver<'a, 'b> {
+ resolver: &'b Resolver<'a>,
+ // Scopes tracks the local namespace and dynamically grows as we enter/exit
+ // `let` blocks
+ scopes: Vec<Namespace<'a>>,
+ blocks: Vec<ExprBlock<'a>>,
+}
+
+impl<'a, 'b> ExprResolver<'a, 'b> {
+ fn new(resolver: &'b Resolver<'a>, initial_scope: Namespace<'a>) -> ExprResolver<'a, 'b> {
+ ExprResolver {
+ resolver,
+ scopes: vec![initial_scope],
+ blocks: Vec::new(),
+ }
+ }
+
+ fn resolve(&mut self, expr: &mut Expression<'a>) -> Result<(), Error> {
+ for instr in expr.instrs.iter_mut() {
+ self.resolve_instr(instr)?;
+ }
+ Ok(())
+ }
+
+ fn resolve_block_type(&mut self, bt: &mut BlockType<'a>) -> Result<(), Error> {
+ // Ok things get interesting here. First off when parsing `bt`
+ // *optionally* has an index and a function type listed. If
+ // they're both not present it's equivalent to 0 params and 0
+ // results.
+ //
+ // In MVP wasm blocks can have 0 params and 0-1 results. Now
+ // there's also multi-value. We want to prefer MVP wasm wherever
+ // possible (for backcompat) so we want to list this block as
+ // being an "MVP" block if we can. The encoder only has
+ // `BlockType` to work with, so it'll be looking at `params` and
+ // `results` to figure out what to encode. If `params` and
+ // `results` fit within MVP, then it uses MVP encoding
+ //
+ // To put all that together, here we handle:
+ //
+ // * If the `index` was specified, resolve it and use it as the
+ // source of truth. If this turns out to be an MVP type,
+ // record it as such.
+ // * Otherwise use `params` and `results` as the source of
+ // truth. *If* this were a non-MVP compatible block `index`
+ // would be filled by by `tyexpand.rs`.
+ //
+ // tl;dr; we handle the `index` here if it's set and then fill
+ // out `params` and `results` if we can, otherwise no work
+ // happens.
+ if bt.ty.index.is_some() {
+ let (ty, _) = self.resolver.resolve_type_use(&mut bt.ty)?;
+ let n = match ty {
+ Index::Num(n, _) => *n,
+ Index::Id(_) => panic!("expected `Num`"),
+ };
+ let ty = match self.resolver.type_info.get(n as usize) {
+ Some(TypeInfo::Func { params, results }) => (params, results),
+ _ => return Ok(()),
+ };
+ if ty.0.len() == 0 && ty.1.len() <= 1 {
+ let mut inline = FunctionType::default();
+ inline.results = ty.1.clone();
+ bt.ty.inline = Some(inline);
+ bt.ty.index = None;
+ }
+ }
+
+ // If the inline annotation persists to this point then resolve
+ // all of its inline value types.
+ if let Some(inline) = &mut bt.ty.inline {
+ inline.resolve(self.resolver)?;
+ }
+ Ok(())
+ }
+
+ fn resolve_instr(&mut self, instr: &mut Instruction<'a>) -> Result<(), Error> {
+ use crate::ast::Instruction::*;
+
+ if let Some(m) = instr.memarg_mut() {
+ self.resolver.resolve_item_ref(&mut m.memory)?;
+ }
+
+ match instr {
+ MemorySize(i) | MemoryGrow(i) | MemoryFill(i) => {
+ self.resolver.resolve_item_ref(&mut i.mem)?;
+ }
+ MemoryInit(i) => {
+ self.resolver.datas.resolve(&mut i.data, "data")?;
+ self.resolver.resolve_item_ref(&mut i.mem)?;
+ }
+ MemoryCopy(i) => {
+ self.resolver.resolve_item_ref(&mut i.src)?;
+ self.resolver.resolve_item_ref(&mut i.dst)?;
+ }
+ DataDrop(i) => {
+ self.resolver.datas.resolve(i, "data")?;
+ }
+
+ TableInit(i) => {
+ self.resolver.elems.resolve(&mut i.elem, "elem")?;
+ self.resolver.resolve_item_ref(&mut i.table)?;
+ }
+ ElemDrop(i) => {
+ self.resolver.elems.resolve(i, "elem")?;
+ }
+
+ TableCopy(i) => {
+ self.resolver.resolve_item_ref(&mut i.dst)?;
+ self.resolver.resolve_item_ref(&mut i.src)?;
+ }
+
+ TableFill(i) | TableSet(i) | TableGet(i) | TableSize(i) | TableGrow(i) => {
+ self.resolver.resolve_item_ref(&mut i.dst)?;
+ }
+
+ GlobalSet(i) | GlobalGet(i) => {
+ self.resolver.resolve_item_ref(&mut i.0)?;
+ }
+
+ LocalSet(i) | LocalGet(i) | LocalTee(i) => {
+ assert!(self.scopes.len() > 0);
+ // Resolve a local by iterating over scopes from most recent
+ // to less recent. This allows locals added by `let` blocks to
+ // shadow less recent locals.
+ for (depth, scope) in self.scopes.iter().enumerate().rev() {
+ if let Err(e) = scope.resolve(i, "local") {
+ if depth == 0 {
+ // There are no more scopes left, report this as
+ // the result
+ return Err(e);
+ }
+ } else {
+ break;
+ }
+ }
+ // We must have taken the `break` and resolved the local
+ assert!(i.is_resolved());
+ }
+
+ Call(i) | RefFunc(i) | ReturnCall(i) => {
+ self.resolver.resolve_item_ref(&mut i.0)?;
+ }
+
+ CallIndirect(c) | ReturnCallIndirect(c) => {
+ self.resolver.resolve_item_ref(&mut c.table)?;
+ self.resolver.resolve_type_use(&mut c.ty)?;
+ }
+
+ FuncBind(b) => {
+ self.resolver.resolve_type_use(&mut b.ty)?;
+ }
+
+ Let(t) => {
+ // Resolve (ref T) in locals
+ for local in &mut t.locals {
+ self.resolver.resolve_valtype(&mut local.ty)?;
+ }
+
+ // Register all locals defined in this let
+ let mut scope = Namespace::default();
+ for local in &t.locals {
+ scope.register(local.id, "local")?;
+ }
+ self.scopes.push(scope);
+ self.blocks.push(ExprBlock {
+ label: t.block.label,
+ pushed_scope: true,
+ });
+
+ self.resolve_block_type(&mut t.block)?;
+ }
+
+ Block(bt) | If(bt) | Loop(bt) | Try(bt) => {
+ self.blocks.push(ExprBlock {
+ label: bt.label,
+ pushed_scope: false,
+ });
+ self.resolve_block_type(bt)?;
+ }
+
+ // On `End` instructions we pop a label from the stack, and for both
+ // `End` and `Else` instructions if they have labels listed we
+ // verify that they match the label at the beginning of the block.
+ Else(_) | End(_) => {
+ let (matching_block, label) = match &instr {
+ Else(label) => (self.blocks.last().cloned(), label),
+ End(label) => (self.blocks.pop(), label),
+ _ => unreachable!(),
+ };
+ let matching_block = match matching_block {
+ Some(l) => l,
+ None => return Ok(()),
+ };
+
+ // Reset the local scopes to before this block was entered
+ if matching_block.pushed_scope {
+ if let End(_) = instr {
+ self.scopes.pop();
+ }
+ }
+
+ let label = match label {
+ Some(l) => l,
+ None => return Ok(()),
+ };
+ if Some(*label) == matching_block.label {
+ return Ok(());
+ }
+ return Err(Error::new(
+ label.span(),
+ "mismatching labels between end and block".to_string(),
+ ));
+ }
+
+ Br(i) | BrIf(i) | BrOnNull(i) => {
+ self.resolve_label(i)?;
+ }
+
+ BrTable(i) => {
+ for label in i.labels.iter_mut() {
+ self.resolve_label(label)?;
+ }
+ self.resolve_label(&mut i.default)?;
+ }
+
+ Throw(i) => {
+ self.resolver.resolve(i, Ns::Event)?;
+ }
+ Rethrow(i) => {
+ self.resolve_label(i)?;
+ }
+ Catch(i) => {
+ self.resolver.resolve(i, Ns::Event)?;
+ }
+
+ BrOnCast(b) => {
+ self.resolve_label(&mut b.label)?;
+ self.resolver.resolve_heaptype(&mut b.val)?;
+ self.resolver.resolve_heaptype(&mut b.rtt)?;
+ }
+
+ Select(s) => {
+ if let Some(list) = &mut s.tys {
+ for ty in list {
+ self.resolver.resolve_valtype(ty)?;
+ }
+ }
+ }
+
+ StructNew(i)
+ | StructNewWithRtt(i)
+ | StructNewDefaultWithRtt(i)
+ | ArrayNewWithRtt(i)
+ | ArrayNewDefaultWithRtt(i)
+ | ArrayGet(i)
+ | ArrayGetS(i)
+ | ArrayGetU(i)
+ | ArraySet(i)
+ | ArrayLen(i) => {
+ self.resolver.resolve(i, Ns::Type)?;
+ }
+ RTTCanon(t) => {
+ self.resolver.resolve_heaptype(t)?;
+ }
+ RTTSub(s) => {
+ self.resolver.resolve_heaptype(&mut s.input_rtt)?;
+ self.resolver.resolve_heaptype(&mut s.output_rtt)?;
+ }
+ RefTest(t) | RefCast(t) => {
+ self.resolver.resolve_heaptype(&mut t.val)?;
+ self.resolver.resolve_heaptype(&mut t.rtt)?;
+ }
+
+ StructSet(s) | StructGet(s) | StructGetS(s) | StructGetU(s) => {
+ self.resolver.resolve(&mut s.r#struct, Ns::Type)?;
+ self.resolver.fields.resolve(&mut s.field, "field")?;
+ }
+ StructNarrow(s) => {
+ self.resolver.resolve_valtype(&mut s.from)?;
+ self.resolver.resolve_valtype(&mut s.to)?;
+ }
+
+ RefNull(ty) => self.resolver.resolve_heaptype(ty)?,
+
+ _ => {}
+ }
+ Ok(())
+ }
+
+ fn resolve_label(&self, label: &mut Index<'a>) -> Result<(), Error> {
+ let id = match label {
+ Index::Num(..) => return Ok(()),
+ Index::Id(id) => *id,
+ };
+ let idx = self
+ .blocks
+ .iter()
+ .rev()
+ .enumerate()
+ .filter_map(|(i, b)| b.label.map(|l| (i, l)))
+ .find(|(_, l)| *l == id);
+ match idx {
+ Some((idx, _)) => {
+ *label = Index::Num(idx as u32, id.span());
+ Ok(())
+ }
+ None => Err(resolve_error(id, "label")),
+ }
+ }
+}
+
+struct Parents<'a, 'b> {
+ prev: Option<ParentNode<'a, 'b>>,
+ cur_id: Option<Id<'a>>,
+ depth: usize,
+ names: &'b mut HashMap<Id<'a>, usize>,
+}
+
+struct ParentNode<'a, 'b> {
+ resolver: &'b Resolver<'a>,
+ id: Option<Id<'a>>,
+ prev: Option<&'b ParentNode<'a, 'b>>,
+ prev_depth: Option<usize>,
+}
+
+impl<'a, 'b> Parents<'a, 'b> {
+ fn push<'c>(&'c mut self, resolver: &'c Resolver<'a>, id: Option<Id<'a>>) -> Parents<'a, 'c>
+ where
+ 'b: 'c,
+ {
+ let prev_depth = if let Some(id) = self.cur_id {
+ self.names.insert(id, self.depth)
+ } else {
+ None
+ };
+ Parents {
+ prev: Some(ParentNode {
+ prev: self.prev.as_ref(),
+ resolver,
+ id: self.cur_id,
+ prev_depth,
+ }),
+ cur_id: id,
+ depth: self.depth + 1,
+ names: &mut *self.names,
+ }
+ }
+
+ fn resolve(&self, index: &mut Index<'a>) -> Result<&'b Resolver<'a>, Error> {
+ let mut i = match *index {
+ Index::Num(n, _) => n,
+ Index::Id(id) => match self.names.get(&id) {
+ Some(idx) => (self.depth - *idx - 1) as u32,
+ None => return Err(resolve_error(id, "parent module")),
+ },
+ };
+ *index = Index::Num(i, index.span());
+ let mut cur = match self.prev.as_ref() {
+ Some(n) => n,
+ None => {
+ return Err(Error::new(
+ index.span(),
+ "cannot use `outer` alias in root module".to_string(),
+ ))
+ }
+ };
+ while i > 0 {
+ cur = match cur.prev {
+ Some(n) => n,
+ None => {
+ return Err(Error::new(
+ index.span(),
+ "alias to `outer` module index too large".to_string(),
+ ))
+ }
+ };
+ i -= 1;
+ }
+ Ok(cur.resolver)
+ }
+}
+
+impl<'a, 'b> Drop for Parents<'a, 'b> {
+ fn drop(&mut self) {
+ let (id, prev_depth) = match &self.prev {
+ Some(n) => (n.id, n.prev_depth),
+ None => return,
+ };
+ if let Some(id) = id {
+ match prev_depth {
+ Some(i) => {
+ self.names.insert(id, i);
+ }
+ None => {
+ self.names.remove(&id);
+ }
+ }
+ }
+ }
+}
+
+enum TypeInfo<'a> {
+ Func {
+ params: Box<[ValType<'a>]>,
+ results: Box<[ValType<'a>]>,
+ },
+ Other,
+}
+
+trait TypeReference<'a> {
+ fn check_matches(&mut self, idx: &Index<'a>, cx: &Resolver<'a>) -> Result<(), Error>;
+ fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error>;
+}
+
+impl<'a> TypeReference<'a> for FunctionType<'a> {
+ fn check_matches(&mut self, idx: &Index<'a>, cx: &Resolver<'a>) -> Result<(), Error> {
+ let n = match idx {
+ Index::Num(n, _) => *n,
+ Index::Id(_) => panic!("expected `Num`"),
+ };
+ let (params, results) = match cx.type_info.get(n as usize) {
+ Some(TypeInfo::Func { params, results }) => (params, results),
+ _ => return Ok(()),
+ };
+
+ // Here we need to check that the inline type listed (ourselves) matches
+ // what was listed in the module itself (the `params` and `results`
+ // above). The listed values in `types` are not resolved yet, although
+ // we should be resolved. In any case we do name resolution
+ // opportunistically here to see if the values are equal.
+
+ let types_not_equal = |a: &ValType, b: &ValType| {
+ let mut a = a.clone();
+ let mut b = b.clone();
+ drop(cx.resolve_valtype(&mut a));
+ drop(cx.resolve_valtype(&mut b));
+ a != b
+ };
+
+ let not_equal = params.len() != self.params.len()
+ || results.len() != self.results.len()
+ || params
+ .iter()
+ .zip(self.params.iter())
+ .any(|(a, (_, _, b))| types_not_equal(a, b))
+ || results
+ .iter()
+ .zip(self.results.iter())
+ .any(|(a, b)| types_not_equal(a, b));
+ if not_equal {
+ return Err(Error::new(
+ idx.span(),
+ format!("inline function type doesn't match type reference"),
+ ));
+ }
+
+ Ok(())
+ }
+
+ fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error> {
+ // Resolve the (ref T) value types in the final function type
+ for param in self.params.iter_mut() {
+ cx.resolve_valtype(&mut param.2)?;
+ }
+ for result in self.results.iter_mut() {
+ cx.resolve_valtype(result)?;
+ }
+ Ok(())
+ }
+}
+
+impl<'a> TypeReference<'a> for InstanceType<'a> {
+ fn check_matches(&mut self, idx: &Index<'a>, cx: &Resolver<'a>) -> Result<(), Error> {
+ drop(cx);
+ Err(Error::new(
+ idx.span(),
+ format!("cannot specify instance type as a reference and inline"),
+ ))
+ }
+
+ fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error> {
+ for export in self.exports.iter_mut() {
+ cx.resolve_item_sig(&mut export.item)?;
+ }
+ Ok(())
+ }
+}
+
+impl<'a> TypeReference<'a> for ModuleType<'a> {
+ fn check_matches(&mut self, idx: &Index<'a>, cx: &Resolver<'a>) -> Result<(), Error> {
+ drop(cx);
+ Err(Error::new(
+ idx.span(),
+ format!("cannot specify module type as a reference and inline"),
+ ))
+ }
+
+ fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error> {
+ for i in self.imports.iter_mut() {
+ cx.resolve_item_sig(&mut i.item)?;
+ }
+ for e in self.exports.iter_mut() {
+ cx.resolve_item_sig(&mut e.item)?;
+ }
+ Ok(())
+ }
+}