From 35137d2542c5ec8c5279aeae2cc39cd9aedec5ad Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 22 Dec 2020 23:05:23 +0100 Subject: [PATCH] add (Typed) Body --- editor/src/ast.rs | 12 +- editor/src/def.rs | 350 +++++++++++++++++++++++++++++++++++++++++- editor/src/expr.rs | 38 +++-- editor/src/pattern.rs | 12 +- editor/src/pool.rs | 8 +- editor/src/types.rs | 6 + 6 files changed, 405 insertions(+), 21 deletions(-) diff --git a/editor/src/ast.rs b/editor/src/ast.rs index 78452a9a6b..bd410315dc 100644 --- a/editor/src/ast.rs +++ b/editor/src/ast.rs @@ -204,9 +204,9 @@ pub enum Expr2 { #[derive(Debug)] pub struct ValueDef { - pattern: PatternId, // 4B - expr_type: Option<(TypeId, Rigids)>, // ? - expr_var: Variable, // 4B + pub pattern: PatternId, // 4B + pub expr_type: Option<(TypeId, Rigids)>, // ? + pub expr_var: Variable, // 4B } impl ShallowClone for ValueDef { @@ -229,11 +229,13 @@ pub enum FunctionDef { arguments: PoolVec<(Pattern2, Type2)>, // 8B rigids: NodeId, // 4B return_type: TypeId, // 4B + body: ExprId, // 4B }, NoAnnotation { name: Symbol, // 8B arguments: PoolVec<(Pattern2, Variable)>, // 8B return_var: Variable, // 4B + body: ExprId, // 4B }, } @@ -245,21 +247,25 @@ impl ShallowClone for FunctionDef { arguments, rigids, return_type, + body, } => Self::WithAnnotation { name: *name, arguments: arguments.shallow_clone(), rigids: *rigids, return_type: *return_type, + body: *body, }, Self::NoAnnotation { name, arguments, return_var, + body, } => Self::NoAnnotation { name: *name, arguments: arguments.shallow_clone(), return_var: *return_var, + body: *body, }, } } diff --git a/editor/src/def.rs b/editor/src/def.rs index a7fb4ab93b..91b660ae95 100644 --- a/editor/src/def.rs +++ b/editor/src/def.rs @@ -12,15 +12,15 @@ // }; // use crate::pattern::{bindings_from_patterns, canonicalize_pattern, Pattern}; // use crate::procedure::References; -use crate::ast::{FunctionDef, Rigids, ValueDef}; -use crate::expr::Env; +use crate::ast::{Expr2, FunctionDef, Rigids, ValueDef}; +use crate::expr::Output; +use crate::expr::{to_expr2, to_expr_id, Env}; use crate::pattern::{ symbols_and_variables_from_pattern, symbols_from_pattern, to_pattern_id, Pattern2, PatternId, }; use crate::pool::{NodeId, Pool, PoolStr, PoolVec, ShallowClone}; use crate::scope::Scope; -use crate::types::{to_annotation2, Alias, Annotation2, References, Signature, Type2, TypeId}; -use roc_can::expr::Output; +use crate::types::{to_annotation2, Alias, Annotation2, Signature, Type2, TypeId}; use roc_collections::all::{default_hasher, ImMap, ImSet, MutMap, MutSet, SendMap}; use roc_module::ident::Lowercase; use roc_module::symbol::Symbol; @@ -425,6 +425,346 @@ fn canonicalize_pending_def<'a>( // invalid aliases (shadowed, incorrect patterns ) todo!() } - _ => todo!(), + + TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr) => { + match to_annotation2(env, scope, &loc_ann.value, loc_ann.region) { + Annotation2::Erroneous(_) => todo!(), + Annotation2::Annotation { + named_rigids, + unnamed_rigids, + symbols, + signature, + } => { + // Record all the annotation's references in output.references.lookups + + for symbol in symbols { + output.references.lookups.insert(symbol); + output.references.referenced_aliases.insert(symbol); + } + + let rigids = { + let named = PoolVec::with_capacity(named_rigids.len() as u32, env.pool); + let unnamed = PoolVec::with_capacity(unnamed_rigids.len() as u32, env.pool); + + for (node_id, (name, variable)) in named.iter_node_ids().zip(named_rigids) { + let poolstr = PoolStr::new(name, env.pool); + env.pool[node_id] = (poolstr, variable); + } + + for (node_id, rigid) in unnamed.iter_node_ids().zip(unnamed_rigids) { + env.pool[node_id] = rigid; + } + + Rigids { named, unnamed } + }; + + // bookkeeping for tail-call detection. If we're assigning to an + // identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called. + let outer_identifier = env.tailcallable_symbol; + + if let Pattern2::Identifier(ref defined_symbol) = env.pool[loc_can_pattern] { + env.tailcallable_symbol = Some(*defined_symbol); + }; + + // regiser the name of this closure, to make sure the closure won't capture it's own name + if let (Pattern2::Identifier(ref defined_symbol), &ast::Expr::Closure(_, _)) = + (&env.pool[loc_can_pattern], &loc_expr.value) + { + env.closure_name_symbol = Some(*defined_symbol); + }; + + let (loc_can_expr, can_output) = + to_expr2(env, scope, &loc_expr.value, loc_expr.region); + + output.references.union_mut(can_output.references.clone()); + + // reset the tailcallable_symbol + env.tailcallable_symbol = outer_identifier; + + // First, make sure we are actually assigning an identifier instead of (for example) a tag. + // + // If we're assigning (UserId userId) = ... then this is certainly not a closure declaration, + // which also implies it's not a self tail call! + // + // Only defs of the form (foo = ...) can be closure declarations or self tail calls. + match loc_can_expr { + Expr2::Closure { + args: closure_args, + body, + extra, + name: closure_symbol, + .. + } => { + let symbol = match env.pool[loc_can_pattern] { + Pattern2::Identifier(ref s) => *s, + _ => todo!( + r"this is an error; functions must be bound with an identifier pattern!" + ), + }; + + // Since everywhere in the code it'll be referred to by its defined name, + // remove its generated name from the closure map. + let references = + env.closures.remove(&closure_symbol).unwrap_or_else(|| { + panic!( r"Tried to remove symbol {:?} from procedures, but it was not found: {:?}", closure_symbol, env.closures) + }); + + // TODO should we re-insert this function into env.closures? + env.closures.insert(symbol, references); + + // Recursion doesn't count as referencing. (If it did, all recursive functions + // would result in circular def errors!) + refs_by_symbol.entry(symbol).and_modify(|(_, refs)| { + refs.lookups.remove(&symbol); + }); + + // Functions' references don't count in defs. + // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its + // parent commit for the bug this fixed! + let refs = References::new(); + + let arguments: PoolVec<(Pattern2, Type2)> = + PoolVec::with_capacity(closure_args.len() as u32, env.pool); + + let return_type: TypeId; + + let annotation = match signature { + Signature::Value { .. } => { + todo!("type annotation says 0 arguments, but it's a function") + } + Signature::Function { + arguments: type_arguments, + closure_type_id, + return_type_id, + } + | Signature::FunctionWithAliases { + arguments: type_arguments, + closure_type_id, + return_type_id, + .. + } => { + if arguments.len() != type_arguments.len() { + panic!("argument number mismatch"); + } + + let it: Vec<_> = closure_args + .iter(env.pool) + .map(|(x, y)| (*x, *y)) + .zip( + type_arguments + .iter(env.pool) + .map(|t| t.shallow_clone()), + ) + .collect(); + + for (node_id, ((_, pattern_id), typ)) in + arguments.iter_node_ids().zip(it.into_iter()) + { + let pattern = &env.pool[pattern_id]; + + env.pool[node_id] = (pattern.shallow_clone(), typ); + } + + return_type = return_type_id; + } + }; + + let function_def = FunctionDef::WithAnnotation { + name: symbol, + arguments, + rigids: env.pool.add(rigids), + return_type, + body, + }; + + let def = Def::Function(function_def); + + can_defs_by_symbol.insert(symbol, def); + + output + } + + _ => { + let annotation = match signature { + Signature::Value { annotation } => annotation, + Signature::Function { + arguments, + closure_type_id, + return_type_id, + } => Type2::Function(arguments, closure_type_id, return_type_id), + Signature::FunctionWithAliases { annotation, .. } => annotation, + }; + let annotation = env.add(annotation, loc_ann.region); + + let value_def = ValueDef { + pattern: loc_can_pattern, + expr_type: Some((annotation, rigids)), + expr_var: env.var_store.fresh(), + }; + + let def = Def::Value(value_def); + + for symbol in + symbols_from_pattern(env.pool, env.pool.get(loc_can_pattern)) + { + can_defs_by_symbol.insert(symbol, def.shallow_clone()); + } + + for (_, (symbol, region)) in scope.idents() { + // if !vars_by_symbol.contains_key(&symbol) { + // continue; + // } + let refs = can_output.references.clone(); + refs_by_symbol.insert(*symbol, (*region, refs)); + } + + output + } + } + } + } + } + + Body(loc_pattern, loc_can_pattern, loc_expr) => { + // bookkeeping for tail-call detection. If we're assigning to an + // identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called. + let outer_identifier = env.tailcallable_symbol; + + if let Pattern2::Identifier(ref defined_symbol) = env.pool[loc_can_pattern] { + env.tailcallable_symbol = Some(*defined_symbol); + }; + + // regiser the name of this closure, to make sure the closure won't capture it's own name + if let (Pattern2::Identifier(ref defined_symbol), &ast::Expr::Closure(_, _)) = + (&env.pool[loc_can_pattern], &loc_expr.value) + { + env.closure_name_symbol = Some(*defined_symbol); + }; + + let (loc_can_expr, can_output) = to_expr2(env, scope, &loc_expr.value, loc_expr.region); + + output.references.union_mut(can_output.references.clone()); + + // reset the tailcallable_symbol + env.tailcallable_symbol = outer_identifier; + + // First, make sure we are actually assigning an identifier instead of (for example) a tag. + // + // If we're assigning (UserId userId) = ... then this is certainly not a closure declaration, + // which also implies it's not a self tail call! + // + // Only defs of the form (foo = ...) can be closure declarations or self tail calls. + match loc_can_expr { + Expr2::Closure { + args: closure_args, + body, + extra, + name: closure_symbol, + .. + } => { + let symbol = match env.pool[loc_can_pattern] { + Pattern2::Identifier(ref s) => *s, + _ => todo!( + r"this is an error; functions must be bound with an identifier pattern!" + ), + }; + + // Since everywhere in the code it'll be referred to by its defined name, + // remove its generated name from the closure map. + let references = + env.closures.remove(&closure_symbol).unwrap_or_else(|| { + panic!( r"Tried to remove symbol {:?} from procedures, but it was not found: {:?}", closure_symbol, env.closures) + }); + + // TODO should we re-insert this function into env.closures? + env.closures.insert(symbol, references); + + // Recursion doesn't count as referencing. (If it did, all recursive functions + // would result in circular def errors!) + refs_by_symbol.entry(symbol).and_modify(|(_, refs)| { + refs.lookups.remove(&symbol); + }); + + // Functions' references don't count in defs. + // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its + // parent commit for the bug this fixed! + let refs = References::new(); + + let arguments: PoolVec<(Pattern2, Variable)> = + PoolVec::with_capacity(closure_args.len() as u32, env.pool); + + let it: Vec<_> = closure_args.iter(env.pool).map(|(x, y)| (*x, *y)).collect(); + + for (node_id, (_, pattern_id)) in arguments.iter_node_ids().zip(it.into_iter()) + { + let pattern = &env.pool[pattern_id]; + + env.pool[node_id] = (pattern.shallow_clone(), env.var_store.fresh()); + } + + let function_def = FunctionDef::NoAnnotation { + name: symbol, + arguments, + return_var: env.var_store.fresh(), + body, + }; + + let def = Def::Function(function_def); + + can_defs_by_symbol.insert(symbol, def); + + output + } + + _ => { + let value_def = ValueDef { + pattern: loc_can_pattern, + expr_type: None, + expr_var: env.var_store.fresh(), + }; + + let def = Def::Value(value_def); + + for symbol in symbols_from_pattern(env.pool, env.pool.get(loc_can_pattern)) { + can_defs_by_symbol.insert(symbol, def.shallow_clone()); + } + + for (_, (symbol, region)) in scope.idents() { + // if !vars_by_symbol.contains_key(&symbol) { + // continue; + // } + let refs = can_output.references.clone(); + refs_by_symbol.insert(*symbol, (*region, refs)); + } + + output + } + } + } + } +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct References { + pub bound_symbols: MutSet, + pub lookups: MutSet, + pub referenced_aliases: MutSet, + pub calls: MutSet, +} + +impl References { + pub fn new() -> References { + Self::default() + } + + pub fn union_mut(&mut self, other: References) { + self.lookups.extend(other.lookups); + self.calls.extend(other.calls); + self.bound_symbols.extend(other.bound_symbols); + self.referenced_aliases.extend(other.referenced_aliases); + } + + pub fn has_lookup(&self, symbol: Symbol) -> bool { + self.lookups.contains(&symbol) } } diff --git a/editor/src/expr.rs b/editor/src/expr.rs index 10aee2dc6e..cc9f471388 100644 --- a/editor/src/expr.rs +++ b/editor/src/expr.rs @@ -2,16 +2,15 @@ #![allow(dead_code)] #![allow(unused_imports)] use crate::ast::{Expr2, ExprId, FloatVal, IntStyle, IntVal}; +use crate::def::References; use crate::pattern::to_pattern2; use crate::pool::{NodeId, Pool, PoolStr, PoolVec}; use crate::scope::Scope; use crate::types::{Type2, TypeId}; use bumpalo::Bump; use inlinable_string::InlinableString; -use roc_can::expr::Output; use roc_can::expr::Recursive; use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int}; -use roc_can::procedure::References; use roc_collections::all::{MutMap, MutSet}; use roc_module::low_level::LowLevel; use roc_module::operator::CalledVia; @@ -21,6 +20,27 @@ use roc_problem::can::{Problem, RuntimeError}; use roc_region::all::{Located, Region}; use roc_types::subs::{VarStore, Variable}; +#[derive(Clone, Default, Debug, PartialEq)] +pub struct Output { + pub references: crate::def::References, + pub tail_call: Option, + pub aliases: MutMap>, + pub non_closures: MutSet, +} + +impl Output { + pub fn union(&mut self, other: Self) { + self.references.union_mut(other.references); + + if let (None, Some(later)) = (self.tail_call, other.tail_call) { + self.tail_call = Some(later); + } + + self.aliases.extend(other.aliases); + self.non_closures.extend(other.non_closures); + } +} + pub struct Env<'a> { pub home: ModuleId, pub var_store: VarStore, @@ -181,7 +201,7 @@ pub fn to_expr2<'a>( scope: &mut Scope, parse_expr: &'a roc_parse::ast::Expr<'a>, region: Region, -) -> (crate::ast::Expr2, Output) { +) -> (crate::ast::Expr2, self::Output) { use roc_parse::ast::Expr::*; match parse_expr { Float(string) => { @@ -326,7 +346,7 @@ pub fn to_expr2<'a>( if let Expr2::Var(symbol) = &can_update { match canonicalize_fields(env, scope, fields) { Ok((can_fields, mut output)) => { - output.references = output.references.union(update_out.references); + output.references.union_mut(update_out.references); let answer = Expr2::Update { record_var: env.var_store.fresh(), @@ -447,8 +467,8 @@ pub fn to_expr2<'a>( let (else_expr, else_output) = to_expr2(env, scope, &else_branch.value, else_branch.region); - output.references = output.references.union(then_output.references); - output.references = output.references.union(else_output.references); + output.references.union_mut(then_output.references); + output.references.union_mut(else_output.references); let expr = Expr2::If { cond_var: env.var_store.fresh(), @@ -474,7 +494,7 @@ pub fn to_expr2<'a>( let (can_when_branch, branch_references) = canonicalize_when_branch(env, scope, *branch, &mut output); - output.references = output.references.union(branch_references); + output.references.union_mut(branch_references); env.pool[node_id] = can_when_branch; } @@ -636,7 +656,7 @@ pub fn to_expr2<'a>( env.pool[node_id] = (env.var_store.fresh(), arg_expr_id); - output.references = output.references.union(arg_out.references); + output.references.union_mut(arg_out.references); } // Default: We're not tail-calling a symbol (by name), we're tail-calling a function value. @@ -965,7 +985,7 @@ fn canonicalize_fields<'a>( todo!() } - output.references = output.references.union(field_out.references); + output.references.union_mut(field_out.references); } Err(CanonicalizeFieldProblem::InvalidOptionalValue { field_name: _, diff --git a/editor/src/pattern.rs b/editor/src/pattern.rs index a48c53f042..06fb5fe6d1 100644 --- a/editor/src/pattern.rs +++ b/editor/src/pattern.rs @@ -2,10 +2,10 @@ #![allow(dead_code)] #![allow(unused_imports)] use crate::ast::{ExprId, FloatVal, IntVal}; -use crate::expr::{to_expr_id, Env}; -use crate::pool::{NodeId, Pool, PoolStr, PoolVec}; +use crate::expr::{to_expr_id, Env, Output}; +use crate::pool::{NodeId, Pool, PoolStr, PoolVec, ShallowClone}; use crate::scope::Scope; -use roc_can::expr::{unescape_char, Output}; +use roc_can::expr::unescape_char; use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int}; use roc_module::symbol::Symbol; use roc_parse::ast::{StrLiteral, StrSegment}; @@ -55,6 +55,12 @@ pub enum Pattern2 { MalformedPattern(MalformedPatternProblem, Region), } +impl ShallowClone for Pattern2 { + fn shallow_clone(&self) -> Self { + todo!() + } +} + #[derive(Debug)] pub struct RecordDestruct { pub var: Variable, // 4B diff --git a/editor/src/pool.rs b/editor/src/pool.rs index 9fa24e568e..854a99f135 100644 --- a/editor/src/pool.rs +++ b/editor/src/pool.rs @@ -42,7 +42,7 @@ pub const NODE_BYTES: usize = 32; // On the plus side, we could be okay with higher memory usage early on, // and then later use the Mesh strategy to reduce long-running memory usage. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Eq)] pub struct NodeId { index: u32, _phantom: PhantomData, @@ -57,6 +57,12 @@ impl Clone for NodeId { } } +impl PartialEq for NodeId { + fn eq(&self, other: &Self) -> bool { + self.index == other.index + } +} + impl Copy for NodeId {} pub struct Pool { diff --git a/editor/src/types.rs b/editor/src/types.rs index 83f4664512..cb7c425445 100644 --- a/editor/src/types.rs +++ b/editor/src/types.rs @@ -41,6 +41,12 @@ pub enum Type2 { Erroneous(roc_types::types::Problem), } +impl ShallowClone for Type2 { + fn shallow_clone(&self) -> Self { + todo!() + } +} + impl Type2 { fn substitute(_pool: &mut Pool, _subs: &MutMap, _type_id: TypeId) { todo!()