transform TypedDef to use fewer clones

This commit is contained in:
Folkert 2022-03-05 15:50:00 +01:00
parent 5e48577d29
commit b421df2a28
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C

View file

@ -1024,21 +1024,23 @@ fn canonicalize_pending_def<'a>(
InvalidAlias { .. } => { InvalidAlias { .. } => {
// invalid aliases and opaques (shadowed, incorrect patterns) get ignored // invalid aliases and opaques (shadowed, incorrect patterns) get ignored
} }
TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr) => { TypedBody(_loc_pattern, loc_can_pattern, loc_ann, loc_expr) => {
let ann = let type_annotation =
canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store); canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store);
// Record all the annotation's references in output.references.lookups // Record all the annotation's references in output.references.lookups
for symbol in ann.references.iter() { for symbol in type_annotation.references.iter() {
output.references.lookups.insert(*symbol); output.references.lookups.insert(*symbol);
output.references.referenced_type_defs.insert(*symbol); output.references.referenced_type_defs.insert(*symbol);
} }
for (symbol, alias) in ann.aliases.clone() { for (symbol, alias) in type_annotation.aliases.clone() {
aliases.insert(symbol, alias); aliases.insert(symbol, alias);
} }
output.introduced_variables.union(&ann.introduced_variables); output
.introduced_variables
.union(&type_annotation.introduced_variables);
// bookkeeping for tail-call detection. If we're assigning to an // bookkeeping for tail-call detection. If we're assigning to an
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called. // identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
@ -1071,27 +1073,29 @@ fn canonicalize_pending_def<'a>(
// which also implies it's not a self tail call! // which also implies it's not a self tail call!
// //
// Only defs of the form (foo = ...) can be closure declarations or self tail calls. // Only defs of the form (foo = ...) can be closure declarations or self tail calls.
if let ( if let Loc {
&Pattern::Identifier(ref defined_symbol), region,
&Closure(ClosureData { value: Pattern::Identifier(symbol),
} = loc_can_pattern
{
if let &Closure(ClosureData {
function_type, function_type,
closure_type, closure_type,
closure_ext_var, closure_ext_var,
return_type, return_type,
name: ref symbol, name: ref closure_name,
ref arguments, ref arguments,
loc_body: ref body, loc_body: ref body,
ref captured_symbols, ref captured_symbols,
.. ..
}), }) = &loc_can_expr.value
) = (&loc_can_pattern.value, &loc_can_expr.value)
{ {
// Since everywhere in the code it'll be referred to by its defined name, // 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.) // remove its generated name from the closure map. (We'll re-insert it later.)
let references = env.closures.remove(symbol).unwrap_or_else(|| { let references = env.closures.remove(closure_name).unwrap_or_else(|| {
panic!( panic!(
"Tried to remove symbol {:?} from procedures, but it was not found: {:?}", "Tried to remove symbol {:?} from procedures, but it was not found: {:?}",
symbol, env.closures closure_name, env.closures
) )
}); });
@ -1099,29 +1103,27 @@ fn canonicalize_pending_def<'a>(
// closures don't have a name, and therefore pick a fresh symbol. But in this // closures don't have a name, and therefore pick a fresh symbol. But in this
// case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...` // case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...`
// and we want to reference it by that name. // and we want to reference it by that name.
env.closures.insert(*defined_symbol, references); env.closures.insert(symbol, references);
// The closure is self tail recursive iff it tail calls itself (by defined name). // The closure is self tail recursive iff it tail calls itself (by defined name).
let is_recursive = match can_output.tail_call { let is_recursive = match can_output.tail_call {
Some(ref symbol) if symbol == defined_symbol => Recursive::TailRecursive, Some(tail_symbol) if tail_symbol == symbol => Recursive::TailRecursive,
_ => Recursive::NotRecursive, _ => Recursive::NotRecursive,
}; };
// Recursion doesn't count as referencing. (If it did, all recursive functions // Recursion doesn't count as referencing. (If it did, all recursive functions
// would result in circular def errors!) // would result in circular def errors!)
refs_by_symbol refs_by_symbol.entry(symbol).and_modify(|(_, refs)| {
.entry(*defined_symbol) refs.lookups = refs.lookups.without(&symbol);
.and_modify(|(_, refs)| {
refs.lookups = refs.lookups.without(defined_symbol);
}); });
// renamed_closure_def = Some(&defined_symbol); // renamed_closure_def = Some(&symbol);
loc_can_expr.value = Closure(ClosureData { 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: *defined_symbol, name: symbol,
captured_symbols: captured_symbols.clone(), captured_symbols: captured_symbols.clone(),
recursive: is_recursive, recursive: is_recursive,
arguments: arguments.clone(), arguments: arguments.clone(),
@ -1133,34 +1135,17 @@ fn canonicalize_pending_def<'a>(
// parent commit for the bug this fixed! // parent commit for the bug this fixed!
let refs = References::new(); let refs = References::new();
refs_by_symbol.insert(*defined_symbol, (loc_can_pattern.region, refs)); refs_by_symbol.insert(symbol, (loc_can_pattern.region, refs));
let symbol = *defined_symbol;
let def = single_typed_can_def(
loc_can_pattern,
loc_can_expr,
expr_var,
Loc::at(loc_ann.region, ann),
vars_by_symbol.clone(),
);
can_defs_by_symbol.insert(symbol, def);
} else { } else {
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
// which defined names reference each other.
if let Loc {
region,
value: Pattern::Identifier(symbol),
} = loc_can_pattern
{
let refs = can_output.references; let refs = can_output.references;
refs_by_symbol.insert(symbol, (region, refs)); refs_by_symbol.insert(symbol, (region, refs));
}
let def = single_typed_can_def( let def = single_typed_can_def(
loc_can_pattern, loc_can_pattern,
loc_can_expr, loc_can_expr,
expr_var, expr_var,
Loc::at(loc_ann.region, ann), Loc::at(loc_ann.region, type_annotation),
vars_by_symbol.clone(), vars_by_symbol.clone(),
); );
can_defs_by_symbol.insert(symbol, def); can_defs_by_symbol.insert(symbol, def);
@ -1187,9 +1172,9 @@ fn canonicalize_pending_def<'a>(
}, },
pattern_vars: vars_by_symbol.clone(), pattern_vars: vars_by_symbol.clone(),
annotation: Some(Annotation { annotation: Some(Annotation {
signature: ann.typ.clone(), signature: type_annotation.typ.clone(),
introduced_variables: ann.introduced_variables.clone(), introduced_variables: type_annotation.introduced_variables.clone(),
aliases: ann.aliases.clone(), aliases: type_annotation.aliases.clone(),
region: loc_ann.region, region: loc_ann.region,
}), }),
}, },
@ -1197,7 +1182,6 @@ fn canonicalize_pending_def<'a>(
} }
} }
} }
}
// If we have a pattern, then the def has a body (that is, it's not a // 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. // standalone annotation), so we need to canonicalize the pattern and expr.
Body(loc_pattern, loc_can_pattern, loc_expr) => { Body(loc_pattern, loc_can_pattern, loc_expr) => {