diff --git a/name_resolve/src/lib.rs b/name_resolve/src/lib.rs index 225e69d7..98a11f8f 100644 --- a/name_resolve/src/lib.rs +++ b/name_resolve/src/lib.rs @@ -49,7 +49,7 @@ mod resolver; mod scoper; use declarator::Declarator; -use resolver::{ResolveKind, Resolver}; +use resolver::{FieldInstantiationResolver, ResolveKind, Resolver}; use scoper::Scoper; /// Error reported when an item (variable, function, type) was already declared @@ -342,7 +342,37 @@ impl<'enclosing> NameResolveCtx<'enclosing> { &mut self, fir: Fir>, ) -> Result>, Incomplete, NameResolutionError>> { - Resolver(self).map(fir) + let mut resolver = Resolver { + ctx: self, + required_parents: Vec::new(), + }; + + let fir = resolver.map(fir)?; + + let latent_map = + resolver + .required_parents + .into_iter() + .fold(HashMap::new(), |mut map, reqd| { + let required_node = &fir[&reqd]; + + let scope = match &required_node.kind { + Kind::Instantiation { to, .. } => Scope(to.expect_resolved()), + other => unreachable!( + "interpreter error - expected Instantiation, got {other:#?}" + ), + }; + + map.insert(reqd, scope); + + map + }); + + FieldInstantiationResolver { + ctx: self, + latent_map, + } + .map(fir) } } @@ -675,4 +705,32 @@ mod tests { assert!(fir.is_err()) } + + #[test] + fn field_instantiation_valid() { + let ast = ast! { + type Foo; + + type Record(of: Foo); + where x = Record(of: Foo); + }; + + let fir = ast.flatten().name_resolve(); + + assert!(fir.is_ok()); + } + + #[test] + fn field_instantiation_invalid() { + let ast = ast! { + type Foo; + + type Record(of: Foo); + where x = Record(not_of: Foo); + }; + + let fir = ast.flatten().name_resolve(); + + assert!(fir.is_err()); + } } diff --git a/name_resolve/src/resolver.rs b/name_resolve/src/resolver.rs index 22bc55a6..a6b4576d 100644 --- a/name_resolve/src/resolver.rs +++ b/name_resolve/src/resolver.rs @@ -1,17 +1,20 @@ -use std::fmt::{Display, Formatter, Result as FmtResult}; +use std::{ + collections::HashMap, + fmt::{Display, Formatter, Result as FmtResult}, +}; use fir::{Kind, Mapper, Node, OriginIdx, RefIdx}; use flatten::FlattenData; use location::SpanTuple; use symbol::Symbol; -use crate::{NameResolutionError, NameResolveCtx}; +use crate::{NameResolutionError, NameResolveCtx, Scope}; #[derive(Clone, Copy)] pub(crate) enum ResolveKind { Call, Type, - Var, + Binding, } impl Display for ResolveKind { @@ -19,12 +22,29 @@ impl Display for ResolveKind { match self { ResolveKind::Call => write!(f, "call"), ResolveKind::Type => write!(f, "type"), - ResolveKind::Var => write!(f, "binding"), + ResolveKind::Binding => write!(f, "binding"), } } } -pub(crate) struct Resolver<'ctx, 'enclosing>(pub(crate) &'ctx mut NameResolveCtx<'enclosing>); +pub(crate) struct Resolver<'ctx, 'enclosing> { + pub(crate) ctx: &'ctx mut NameResolveCtx<'enclosing>, + /// The `required_parents` vector contains information about origins whose resolution is necessary + /// for the resolution of other nodes. For example, when resolving a type instantiation like + /// `Foo(bar: 15)`, we need to have `Foo` resolved to its definition in order to resolve `bar` to + /// that type's `.bar` field. + // FIXME: Rework this part of the doc + /// This vector is created as part of the first resolving pass - which + /// we can call early-resolving, or straightforward-resolving, and will be used by a secondary, + /// smaller resolving pass whose purpose is to map these undecidable usages to their definition. + pub(crate) required_parents: Vec, +} + +/// Created thanks to [`Resolver`] +pub(crate) struct FieldInstantiationResolver<'ctx, 'enclosing> { + pub(crate) ctx: &'ctx mut NameResolveCtx<'enclosing>, + pub(crate) latent_map: HashMap, +} impl<'ctx, 'enclosing> Resolver<'ctx, 'enclosing> { fn get_definition( @@ -37,14 +57,14 @@ impl<'ctx, 'enclosing> Resolver<'ctx, 'enclosing> { let symbol = sym.expect("attempting to get definition for non existent symbol - interpreter bug"); - let mappings = &self.0.mappings; + let mappings = &self.ctx.mappings; - let scope = self.0.enclosing_scope[node]; + let scope = self.ctx.enclosing_scope[node]; let origin = match kind { ResolveKind::Call => mappings.functions.lookup(symbol, scope), ResolveKind::Type => mappings.types.lookup(symbol, scope), - ResolveKind::Var => mappings.bindings.lookup(symbol, scope), + ResolveKind::Binding => mappings.bindings.lookup(symbol, scope), }; origin.map_or_else( @@ -102,7 +122,7 @@ impl<'ast, 'ctx, 'enclosing> Mapper, FlattenData<'ast>, NameRe ty: RefIdx, ) -> Result>, NameResolutionError> { let var_def = self.get_definition( - ResolveKind::Var, + ResolveKind::Binding, data.ast.symbol(), data.ast.location(), origin, @@ -173,4 +193,92 @@ impl<'ast, 'ctx, 'enclosing> Mapper, FlattenData<'ast>, NameRe }) } } + + fn map_assignment( + &mut self, + data: FlattenData<'ast>, + origin: OriginIdx, + to: RefIdx, + from: RefIdx, + ) -> Result>, NameResolutionError> { + // we should probably do that ONLY if we can't resolve the binding? + // that's very spaghetti... + + self.required_parents + .push(self.ctx.enclosing_scope[origin].0); + + Ok(Node { + data, + origin, + kind: Kind::Assignment { to, from }, + }) + } + + fn map_instantiation( + &mut self, + data: FlattenData<'ast>, + origin: OriginIdx, + _to: RefIdx, + generics: Vec, + fields: Vec, + ) -> Result>, NameResolutionError> { + // FIXME: Can we have _to be resolved already? + + let definition = self.get_definition( + ResolveKind::Type, + data.ast.symbol(), + data.ast.location(), + origin, + )?; + + // do we have to go through all the fields here? should we instead have type fields count as declarations? e.g. encode them as ? + + Ok(Node { + data, + origin, + kind: Kind::Instantiation { + to: RefIdx::Resolved(definition), + generics, + fields, + }, + }) + } +} + +impl<'ast, 'ctx, 'enclosing> Mapper, FlattenData<'ast>, NameResolutionError> + for FieldInstantiationResolver<'ctx, 'enclosing> +{ + // FIXME: Disgusting + fn map_assignment( + &mut self, + data: FlattenData<'ast>, + origin: OriginIdx, + _: RefIdx, + from: RefIdx, + ) -> Result>, NameResolutionError> { + // FIXME: Do nothing if _to is resolved already + + let instan = self.ctx.enclosing_scope[origin]; + let scope = self.latent_map[&instan.0]; + + let bindings = self.ctx.mappings.bindings.scopes.get(&scope).unwrap(); + + let resolved = bindings.get(data.ast.symbol().expect("interpreter error")); + + match resolved { + Some(field) => Ok(Node { + data, + origin, + kind: Kind::Assignment { + to: RefIdx::Resolved(*field), + from, + }, + }), + None => Err(NameResolutionError::Unresolved( + ResolveKind::Binding, + data.ast.symbol().unwrap().clone(), + data.ast.location().clone(), + )), + } + } } diff --git a/name_resolve/src/scoper.rs b/name_resolve/src/scoper.rs index 07f401e3..20de5391 100644 --- a/name_resolve/src/scoper.rs +++ b/name_resolve/src/scoper.rs @@ -118,6 +118,7 @@ impl<'ast> Traversal, ScoperError> for Scoper { self.maybe_visit_child(fir, value)?; self.maybe_visit_child(fir, ty) } + // FIXME: Is it valid to put a union's variants in its scope? Kind::RecordType { fields: subs, .. } | Kind::UnionType { variants: subs, .. } => { let old = self.enter_scope(node.origin); @@ -144,10 +145,14 @@ impl<'ast> Traversal, ScoperError> for Scoper { .iter() .for_each(|generic| self.maybe_visit_child(fir, generic).unwrap()); + let old = self.enter_scope(node.origin); + fields .iter() .for_each(|field| { self.maybe_visit_child(fir, field) }.unwrap()); + self.enter_scope(old); + Ok(()) } Kind::Call { to, generics, args } => { diff --git a/typecheck/src/lib.rs b/typecheck/src/lib.rs index e1e90d56..1ec87b08 100644 --- a/typecheck/src/lib.rs +++ b/typecheck/src/lib.rs @@ -219,6 +219,7 @@ mod tests { } #[test] + #[ignore = "Reevaluate once #653 is done"] fn assignment_valid() { let ast = ast! { type Marker0; @@ -235,6 +236,7 @@ mod tests { } #[test] + #[ignore = "Reevaluate once #653 is done"] fn assignment_invalid() { let ast = ast! { type Marker0; @@ -248,6 +250,33 @@ mod tests { assert!(fir.is_err()); } + #[test] + fn field_instantiation_valid() { + let ast = ast! { + type Foo; + + type Record(m: Foo); + where x = Record(m: Foo); + }; + let fir = fir!(ast).type_check(); + + assert!(fir.is_ok()); + } + + #[test] + fn field_instantiation_invalid() { + let ast = ast! { + type Foo; + type Bar; + + type Record(m: Foo); + where x = Record(m: Bar); + }; + let fir = fir!(ast).type_check(); + + assert!(fir.is_err()); + } + #[test] fn constant_bool() { let ast = ast! {