add (Typed) Body

This commit is contained in:
Folkert 2020-12-22 23:05:23 +01:00
parent 4d692af639
commit 35137d2542
6 changed files with 405 additions and 21 deletions

View File

@ -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<Rigids>, // 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,
},
}
}

View File

@ -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<Symbol>,
pub lookups: MutSet<Symbol>,
pub referenced_aliases: MutSet<Symbol>,
pub calls: MutSet<Symbol>,
}
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)
}
}

View File

@ -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<Symbol>,
pub aliases: MutMap<Symbol, NodeId<crate::types::Alias>>,
pub non_closures: MutSet<Symbol>,
}
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: _,

View File

@ -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

View File

@ -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<T> {
index: u32,
_phantom: PhantomData<T>,
@ -57,6 +57,12 @@ impl<T> Clone for NodeId<T> {
}
}
impl<T> PartialEq for NodeId<T> {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
impl<T> Copy for NodeId<T> {}
pub struct Pool {

View File

@ -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<Variable, TypeId>, _type_id: TypeId) {
todo!()