//! Traversals over the can ast. use roc_region::all::{Loc, Region}; use roc_types::subs::Variable; use crate::{ def::{Annotation, Declaration, Def}, expr::{ClosureData, Expr, WhenBranch}, pattern::Pattern, }; macro_rules! visit_list { ($visitor:ident, $walk:ident, $list:expr) => { for elem in $list { $visitor.$walk(elem) } }; } fn walk_decls(visitor: &mut V, decls: &[Declaration]) { visit_list!(visitor, visit_decl, decls) } fn walk_decl(visitor: &mut V, decl: &Declaration) { match decl { Declaration::Declare(def) => { visitor.visit_def(def); } Declaration::DeclareRec(defs) => { visit_list!(visitor, visit_def, defs) } Declaration::Builtin(def) => visitor.visit_def(def), Declaration::InvalidCycle(_cycles) => { todo!() } } } fn walk_def(visitor: &mut V, def: &Def) { let Def { loc_pattern, loc_expr, annotation, expr_var, .. } = def; let opt_var = match loc_pattern.value { Pattern::Identifier(..) | Pattern::AbilityMemberSpecialization { .. } => Some(*expr_var), _ => loc_pattern.value.opt_var(), }; visitor.visit_pattern(&loc_pattern.value, loc_pattern.region, opt_var); visitor.visit_expr(&loc_expr.value, loc_expr.region, *expr_var); if let Some(annot) = &annotation { visitor.visit_annotation(annot); } } fn walk_expr(visitor: &mut V, expr: &Expr) { match expr { Expr::Closure(closure_data) => walk_closure(visitor, closure_data), Expr::When { cond_var, expr_var, loc_cond, branches, region: _, branches_cond_var: _, exhaustive: _, } => { walk_when(visitor, *cond_var, *expr_var, loc_cond, branches); } Expr::Call(f, loc_args, _) => { walk_call(visitor, f, loc_args); } e => todo!("{:?}", e), } } fn walk_closure(visitor: &mut V, clos: &ClosureData) { let ClosureData { arguments, loc_body, return_type, .. } = clos; arguments.iter().for_each(|(var, _exhaustive_mark, arg)| { visitor.visit_pattern(&arg.value, arg.region, Some(*var)) }); visitor.visit_expr(&loc_body.value, loc_body.region, *return_type); } fn walk_when( visitor: &mut V, cond_var: Variable, expr_var: Variable, loc_cond: &Loc, branches: &[WhenBranch], ) { visitor.visit_expr(&loc_cond.value, loc_cond.region, cond_var); branches .iter() .for_each(|branch| walk_when_branch(visitor, branch, expr_var)); } fn walk_when_branch(visitor: &mut V, branch: &WhenBranch, expr_var: Variable) { let WhenBranch { patterns, value, guard, redundant: _, } = branch; patterns .iter() .for_each(|pat| visitor.visit_pattern(&pat.value, pat.region, pat.value.opt_var())); visitor.visit_expr(&value.value, value.region, expr_var); if let Some(guard) = guard { visitor.visit_expr(&guard.value, guard.region, Variable::BOOL); } } fn walk_call( visitor: &mut V, f: &(Variable, Loc, Variable, Variable), loc_args: &[(Variable, Loc)], ) { let (fn_var, loc_fn_expr, _lambda_set, _ret) = f; visitor.visit_expr(&loc_fn_expr.value, loc_fn_expr.region, *fn_var); loc_args .iter() .for_each(|(v, e)| visitor.visit_expr(&e.value, e.region, *v)); } fn walk_pattern(_visitor: &mut V, _pat: &Pattern) { todo!() } trait Visitor: Sized { fn visit_decls(&mut self, decls: &[Declaration]) { walk_decls(self, decls); } fn visit_decl(&mut self, decl: &Declaration) { walk_decl(self, decl); } fn visit_def(&mut self, def: &Def) { walk_def(self, def); } fn visit_pattern(&mut self, pat: &Pattern, _region: Region, _opt_var: Option) { walk_pattern(self, pat) } fn visit_annotation(&mut self, _pat: &Annotation) { // TODO } fn visit_expr(&mut self, expr: &Expr, _region: Region, _var: Variable) { walk_expr(self, expr); } } struct TypeAtVisitor { region: Region, typ: Option, } impl Visitor for TypeAtVisitor { fn visit_expr(&mut self, expr: &Expr, region: Region, var: Variable) { if region == self.region { debug_assert!(self.typ.is_none()); self.typ = Some(var); return; } if region.contains(&self.region) { walk_expr(self, expr); } } fn visit_pattern(&mut self, pat: &Pattern, region: Region, opt_var: Option) { if region == self.region { debug_assert!(self.typ.is_none()); self.typ = opt_var; return; } if region.contains(&self.region) { walk_pattern(self, pat) } } } /// Attempts to find the type of an expression at `region`, if it exists. pub fn find_type_at(region: Region, decls: &[Declaration]) -> Option { let mut visitor = TypeAtVisitor { region, typ: None }; visitor.visit_decls(decls); visitor.typ }