mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Merge branch 'refactor-closure-can' into scope-smarter-storage
This commit is contained in:
commit
34d77cffdb
5 changed files with 347 additions and 423 deletions
|
@ -5,7 +5,7 @@ use crate::env::Env;
|
|||
use crate::expr::ClosureData;
|
||||
use crate::expr::Expr::{self, *};
|
||||
use crate::expr::{canonicalize_expr, Output, Recursive};
|
||||
use crate::pattern::{bindings_from_patterns, canonicalize_def_header_pattern, Pattern};
|
||||
use crate::pattern::{canonicalize_def_header_pattern, BindingsFromPattern, Pattern};
|
||||
use crate::procedure::References;
|
||||
use crate::reference_matrix::ReferenceMatrix;
|
||||
use crate::scope::create_alias;
|
||||
|
@ -478,7 +478,7 @@ pub(crate) fn canonicalize_defs<'a>(
|
|||
let mut symbol_to_index: Vec<(IdentId, u32)> = Vec::with_capacity(pending_value_defs.len());
|
||||
|
||||
for (def_index, pending_def) in pending_value_defs.iter().enumerate() {
|
||||
for (s, r) in bindings_from_patterns(std::iter::once(pending_def.loc_pattern())) {
|
||||
for (s, r) in BindingsFromPattern::new(pending_def.loc_pattern()) {
|
||||
// store the top-level defs, used to ensure that closures won't capture them
|
||||
if let PatternType::TopLevelDef = pattern_type {
|
||||
env.top_level_symbols.insert(s);
|
||||
|
@ -1024,12 +1024,12 @@ fn canonicalize_pending_value_def<'a>(
|
|||
) -> DefOutput {
|
||||
use PendingValueDef::*;
|
||||
|
||||
// Make types for the body expr, even if we won't end up having a body.
|
||||
let expr_var = var_store.fresh();
|
||||
let mut vars_by_symbol = SendMap::default();
|
||||
|
||||
match pending_def {
|
||||
AnnotationOnly(_, loc_can_pattern, loc_ann) => {
|
||||
// Make types for the body expr, even if we won't end up having a body.
|
||||
let expr_var = var_store.fresh();
|
||||
let mut vars_by_symbol = SendMap::default();
|
||||
|
||||
// annotation sans body cannot introduce new rigids that are visible in other annotations
|
||||
// but the rigids can show up in type error messages, so still register them
|
||||
let type_annotation = canonicalize_annotation(
|
||||
|
@ -1150,232 +1150,121 @@ fn canonicalize_pending_value_def<'a>(
|
|||
.introduced_variables
|
||||
.union(&type_annotation.introduced_variables);
|
||||
|
||||
// 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 Pattern::Identifier(ref defined_symbol) = &loc_can_pattern.value {
|
||||
env.tailcallable_symbol = Some(*defined_symbol);
|
||||
};
|
||||
|
||||
// register the name of this closure, to make sure the closure won't capture it's own name
|
||||
if let (Pattern::Identifier(ref defined_symbol), &ast::Expr::Closure(_, _)) =
|
||||
(&loc_can_pattern.value, &loc_expr.value)
|
||||
{
|
||||
env.closure_name_symbol = Some(*defined_symbol);
|
||||
};
|
||||
|
||||
pattern_to_vars_by_symbol(&mut vars_by_symbol, &loc_can_pattern.value, expr_var);
|
||||
|
||||
let (mut loc_can_expr, can_output) =
|
||||
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
|
||||
|
||||
output.references.union_mut(&can_output.references);
|
||||
|
||||
// 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_pattern.value, &loc_can_expr.value) {
|
||||
(
|
||||
Pattern::Identifier(symbol)
|
||||
| Pattern::AbilityMemberSpecialization { ident: symbol, .. },
|
||||
Closure(ClosureData {
|
||||
function_type,
|
||||
closure_type,
|
||||
closure_ext_var,
|
||||
return_type,
|
||||
name: closure_name,
|
||||
arguments,
|
||||
loc_body: body,
|
||||
captured_symbols,
|
||||
..
|
||||
}),
|
||||
) => {
|
||||
// Since everywhere in the code it'll be referred to by its defined name,
|
||||
// remove its generated name from the closure map. (We'll re-insert it later.)
|
||||
let closure_references = env.closures.remove(closure_name).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Tried to remove symbol {:?} from procedures, but it was not found: {:?}",
|
||||
closure_name, env.closures
|
||||
)
|
||||
});
|
||||
|
||||
// The closure is self tail recursive iff it tail calls itself (by defined name).
|
||||
let is_recursive = match can_output.tail_call {
|
||||
Some(tail_symbol) if tail_symbol == *symbol => Recursive::TailRecursive,
|
||||
_ => Recursive::NotRecursive,
|
||||
};
|
||||
|
||||
loc_can_expr.value = Closure(ClosureData {
|
||||
function_type: *function_type,
|
||||
closure_type: *closure_type,
|
||||
closure_ext_var: *closure_ext_var,
|
||||
return_type: *return_type,
|
||||
name: *symbol,
|
||||
captured_symbols: captured_symbols.clone(),
|
||||
recursive: is_recursive,
|
||||
arguments: arguments.clone(),
|
||||
loc_body: body.clone(),
|
||||
});
|
||||
|
||||
let def = single_can_def(
|
||||
loc_can_pattern,
|
||||
loc_can_expr,
|
||||
expr_var,
|
||||
Some(Loc::at(loc_ann.region, type_annotation)),
|
||||
vars_by_symbol.clone(),
|
||||
);
|
||||
|
||||
output.union(can_output);
|
||||
|
||||
DefOutput {
|
||||
output,
|
||||
references: DefReferences::Function(closure_references),
|
||||
def,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let refs = can_output.references.clone();
|
||||
|
||||
let def = single_can_def(
|
||||
loc_can_pattern,
|
||||
loc_can_expr,
|
||||
expr_var,
|
||||
Some(Loc::at(loc_ann.region, type_annotation)),
|
||||
vars_by_symbol.clone(),
|
||||
);
|
||||
|
||||
output.union(can_output);
|
||||
|
||||
DefOutput {
|
||||
output,
|
||||
references: DefReferences::Value(refs),
|
||||
def,
|
||||
}
|
||||
}
|
||||
}
|
||||
canonicalize_pending_body(
|
||||
env,
|
||||
output,
|
||||
scope,
|
||||
var_store,
|
||||
loc_can_pattern,
|
||||
loc_expr,
|
||||
Some(Loc::at(loc_ann.region, type_annotation)),
|
||||
)
|
||||
}
|
||||
// If we have a pattern, then the def has a body (that is, it's not a
|
||||
// standalone annotation), so we need to canonicalize the pattern and expr.
|
||||
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;
|
||||
Body(_loc_pattern, loc_can_pattern, loc_expr) => {
|
||||
//
|
||||
canonicalize_pending_body(
|
||||
env,
|
||||
output,
|
||||
scope,
|
||||
var_store,
|
||||
loc_can_pattern,
|
||||
loc_expr,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (&ast::Pattern::Identifier(_name), &Pattern::Identifier(ref defined_symbol)) =
|
||||
(&loc_pattern.value, &loc_can_pattern.value)
|
||||
{
|
||||
// TODO trim down these arguments!
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn canonicalize_pending_body<'a>(
|
||||
env: &mut Env<'a>,
|
||||
mut output: Output,
|
||||
scope: &mut Scope,
|
||||
var_store: &mut VarStore,
|
||||
|
||||
loc_can_pattern: Loc<Pattern>,
|
||||
loc_expr: &'a Loc<ast::Expr>,
|
||||
|
||||
opt_loc_annotation: Option<Loc<crate::annotation::Annotation>>,
|
||||
) -> DefOutput {
|
||||
// We treat closure definitions `foo = \a, b -> ...` differntly from other body expressions,
|
||||
// because they need more bookkeeping (for tail calls, closure captures, etc.)
|
||||
//
|
||||
// Only defs of the form `foo = ...` can be closure declarations or self tail calls.
|
||||
let (loc_can_expr, def_references) = {
|
||||
match (&loc_can_pattern.value, &loc_expr.value) {
|
||||
(
|
||||
Pattern::Identifier(defined_symbol)
|
||||
| Pattern::AbilityMemberSpecialization {
|
||||
ident: defined_symbol,
|
||||
..
|
||||
},
|
||||
ast::Expr::Closure(arguments, body),
|
||||
) => {
|
||||
// bookkeeping for tail-call detection.
|
||||
let outer_tailcallable = env.tailcallable_symbol;
|
||||
env.tailcallable_symbol = Some(*defined_symbol);
|
||||
|
||||
// TODO isn't types_by_symbol enough? Do we need vars_by_symbol too?
|
||||
vars_by_symbol.insert(*defined_symbol, expr_var);
|
||||
};
|
||||
let (mut closure_data, can_output) = crate::expr::canonicalize_closure(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
arguments,
|
||||
body,
|
||||
Some(*defined_symbol),
|
||||
);
|
||||
|
||||
// register the name of this closure, to make sure the closure won't capture it's own name
|
||||
if let (Pattern::Identifier(ref defined_symbol), &ast::Expr::Closure(_, _)) =
|
||||
(&loc_can_pattern.value, &loc_expr.value)
|
||||
{
|
||||
env.closure_name_symbol = Some(*defined_symbol);
|
||||
};
|
||||
// reset the tailcallable_symbol
|
||||
env.tailcallable_symbol = outer_tailcallable;
|
||||
|
||||
let (mut loc_can_expr, can_output) =
|
||||
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
|
||||
// The closure is self tail recursive iff it tail calls itself (by defined name).
|
||||
let is_recursive = match can_output.tail_call {
|
||||
Some(tail_symbol) if tail_symbol == *defined_symbol => Recursive::TailRecursive,
|
||||
_ => Recursive::NotRecursive,
|
||||
};
|
||||
|
||||
// reset the tailcallable_symbol
|
||||
env.tailcallable_symbol = outer_identifier;
|
||||
closure_data.recursive = is_recursive;
|
||||
closure_data.name = *defined_symbol;
|
||||
|
||||
// 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_pattern.value, &loc_can_expr.value) {
|
||||
(
|
||||
Pattern::Identifier(symbol),
|
||||
Closure(ClosureData {
|
||||
function_type,
|
||||
closure_type,
|
||||
closure_ext_var,
|
||||
return_type,
|
||||
name: closure_name,
|
||||
arguments,
|
||||
loc_body: body,
|
||||
captured_symbols,
|
||||
..
|
||||
}),
|
||||
) => {
|
||||
// Since everywhere in the code it'll be referred to by its defined name,
|
||||
// remove its generated name from the closure map. (We'll re-insert it later.)
|
||||
let closure_references = env.closures.remove(closure_name).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Tried to remove symbol {:?} from procedures, but it was not found: {:?}",
|
||||
closure_name, env.closures
|
||||
)
|
||||
});
|
||||
let loc_can_expr = Loc::at(loc_expr.region, Expr::Closure(closure_data));
|
||||
|
||||
// The closure is self tail recursive iff it tail calls itself (by defined name).
|
||||
let is_recursive = match can_output.tail_call {
|
||||
Some(tail_symbol) if tail_symbol == *symbol => Recursive::TailRecursive,
|
||||
_ => Recursive::NotRecursive,
|
||||
};
|
||||
let def_references = DefReferences::Function(can_output.references.clone());
|
||||
output.union(can_output);
|
||||
|
||||
loc_can_expr.value = Closure(ClosureData {
|
||||
function_type: *function_type,
|
||||
closure_type: *closure_type,
|
||||
closure_ext_var: *closure_ext_var,
|
||||
return_type: *return_type,
|
||||
name: *symbol,
|
||||
captured_symbols: captured_symbols.clone(),
|
||||
recursive: is_recursive,
|
||||
arguments: arguments.clone(),
|
||||
loc_body: body.clone(),
|
||||
});
|
||||
(loc_can_expr, def_references)
|
||||
}
|
||||
|
||||
let def = single_can_def(
|
||||
loc_can_pattern,
|
||||
loc_can_expr,
|
||||
expr_var,
|
||||
None,
|
||||
vars_by_symbol.clone(),
|
||||
);
|
||||
_ => {
|
||||
let (loc_can_expr, can_output) =
|
||||
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
|
||||
|
||||
output.union(can_output);
|
||||
let def_references = DefReferences::Value(can_output.references.clone());
|
||||
output.union(can_output);
|
||||
|
||||
DefOutput {
|
||||
output,
|
||||
references: DefReferences::Function(closure_references),
|
||||
def,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let refs = can_output.references.clone();
|
||||
|
||||
let def = single_can_def(
|
||||
loc_can_pattern,
|
||||
loc_can_expr,
|
||||
expr_var,
|
||||
None,
|
||||
vars_by_symbol.clone(),
|
||||
);
|
||||
|
||||
output.union(can_output);
|
||||
|
||||
DefOutput {
|
||||
output,
|
||||
references: DefReferences::Value(refs),
|
||||
def,
|
||||
}
|
||||
}
|
||||
(loc_can_expr, def_references)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let expr_var = var_store.fresh();
|
||||
let mut vars_by_symbol = SendMap::default();
|
||||
|
||||
pattern_to_vars_by_symbol(&mut vars_by_symbol, &loc_can_pattern.value, expr_var);
|
||||
|
||||
let def = single_can_def(
|
||||
loc_can_pattern,
|
||||
loc_can_expr,
|
||||
expr_var,
|
||||
opt_loc_annotation,
|
||||
vars_by_symbol,
|
||||
);
|
||||
|
||||
DefOutput {
|
||||
output,
|
||||
references: def_references,
|
||||
def,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue