mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 07:49:17 +03:00
Lift translation of lets
This commit is contained in:
parent
4f8273ba89
commit
6b9b64274b
@ -15,7 +15,6 @@ use roc_debug_flags::{
|
||||
dbg_do, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE,
|
||||
ROC_PRINT_IR_AFTER_SPECIALIZATION,
|
||||
};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
@ -2076,6 +2075,294 @@ impl<'a> Stmt<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_can_let<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
def: Box<roc_can::def::Def>,
|
||||
cont: Box<Loc<roc_can::expr::Expr>>,
|
||||
variable: Variable,
|
||||
hole: Option<Stmt<'a>>,
|
||||
) -> Stmt<'a> {
|
||||
use roc_can::expr::Expr::*;
|
||||
|
||||
macro_rules! lower_rest {
|
||||
($variable:expr, $expr:expr) => {
|
||||
match hole {
|
||||
None => from_can(env, $variable, $expr, procs, layout_cache),
|
||||
Some(_) => unreachable!(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||
return match def.loc_expr.value {
|
||||
Closure(closure_data) => {
|
||||
register_capturing_closure(env, procs, layout_cache, *symbol, closure_data);
|
||||
|
||||
lower_rest!(variable, cont.value)
|
||||
}
|
||||
Accessor(accessor_data) => {
|
||||
let fresh_record_symbol = env.unique_symbol();
|
||||
register_noncapturing_closure(
|
||||
env,
|
||||
procs,
|
||||
*symbol,
|
||||
accessor_data.to_closure_data(fresh_record_symbol),
|
||||
);
|
||||
|
||||
lower_rest!(variable, cont.value)
|
||||
}
|
||||
Var(original) | AbilityMember(original, _) => {
|
||||
// a variable is aliased, e.g.
|
||||
//
|
||||
// foo = bar
|
||||
//
|
||||
// or
|
||||
//
|
||||
// foo = RBTRee.empty
|
||||
|
||||
// TODO: right now we need help out rustc with the closure types;
|
||||
// it isn't able to infer the right lifetime bounds. See if we
|
||||
// can remove the annotations in the future.
|
||||
let build_rest =
|
||||
|env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>| {
|
||||
from_can(env, def.expr_var, cont.value, procs, layout_cache)
|
||||
};
|
||||
|
||||
return handle_variable_aliasing(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
def.expr_var,
|
||||
*symbol,
|
||||
original,
|
||||
build_rest,
|
||||
);
|
||||
}
|
||||
LetNonRec(nested_def, nested_cont) => {
|
||||
use roc_can::expr::Expr::*;
|
||||
// We must transform
|
||||
//
|
||||
// let answer = 1337
|
||||
// in
|
||||
// let unused =
|
||||
// let nested = 17
|
||||
// in
|
||||
// nested
|
||||
// in
|
||||
// answer
|
||||
//
|
||||
// into
|
||||
//
|
||||
// let answer = 1337
|
||||
// in
|
||||
// let nested = 17
|
||||
// in
|
||||
// let unused = nested
|
||||
// in
|
||||
// answer
|
||||
|
||||
let new_def = roc_can::def::Def {
|
||||
loc_pattern: def.loc_pattern,
|
||||
loc_expr: *nested_cont,
|
||||
pattern_vars: def.pattern_vars,
|
||||
annotation: def.annotation,
|
||||
expr_var: def.expr_var,
|
||||
};
|
||||
|
||||
let new_inner = LetNonRec(Box::new(new_def), cont);
|
||||
|
||||
let new_outer = LetNonRec(nested_def, Box::new(Loc::at_zero(new_inner)));
|
||||
|
||||
lower_rest!(variable, new_outer)
|
||||
}
|
||||
LetRec(nested_defs, nested_cont) => {
|
||||
use roc_can::expr::Expr::*;
|
||||
// We must transform
|
||||
//
|
||||
// let answer = 1337
|
||||
// in
|
||||
// let unused =
|
||||
// let nested = \{} -> nested {}
|
||||
// in
|
||||
// nested
|
||||
// in
|
||||
// answer
|
||||
//
|
||||
// into
|
||||
//
|
||||
// let answer = 1337
|
||||
// in
|
||||
// let nested = \{} -> nested {}
|
||||
// in
|
||||
// let unused = nested
|
||||
// in
|
||||
// answer
|
||||
|
||||
let new_def = roc_can::def::Def {
|
||||
loc_pattern: def.loc_pattern,
|
||||
loc_expr: *nested_cont,
|
||||
pattern_vars: def.pattern_vars,
|
||||
annotation: def.annotation,
|
||||
expr_var: def.expr_var,
|
||||
};
|
||||
|
||||
let new_inner = LetNonRec(Box::new(new_def), cont);
|
||||
|
||||
let new_outer = LetRec(nested_defs, Box::new(Loc::at_zero(new_inner)));
|
||||
|
||||
lower_rest!(variable, new_outer)
|
||||
}
|
||||
_ => {
|
||||
let rest = lower_rest!(variable, cont.value);
|
||||
|
||||
// Remove all the requested symbol specializations now, since this is the
|
||||
// def site and hence we won't need them any higher up.
|
||||
let mut needed_specializations = procs.symbol_specializations.remove(*symbol);
|
||||
|
||||
match needed_specializations.len() {
|
||||
0 => {
|
||||
// We don't need any specializations, that means this symbol is never
|
||||
// referenced.
|
||||
with_hole(
|
||||
env,
|
||||
def.loc_expr.value,
|
||||
def.expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
*symbol,
|
||||
env.arena.alloc(rest),
|
||||
)
|
||||
}
|
||||
|
||||
// We do need specializations
|
||||
1 => {
|
||||
let (_specialization_mark, (var, specialized_symbol)) =
|
||||
needed_specializations.next().unwrap();
|
||||
|
||||
// Unify the expr_var with the requested specialization once.
|
||||
let _res = roc_unify::unify::unify(env.subs, var, def.expr_var, Mode::EQ);
|
||||
|
||||
resolve_abilities_in_specialized_body(
|
||||
env,
|
||||
procs,
|
||||
&def.loc_expr.value,
|
||||
def.expr_var,
|
||||
);
|
||||
|
||||
with_hole(
|
||||
env,
|
||||
def.loc_expr.value,
|
||||
def.expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
specialized_symbol,
|
||||
env.arena.alloc(rest),
|
||||
)
|
||||
}
|
||||
_n => {
|
||||
let mut stmt = rest;
|
||||
|
||||
// Need to eat the cost and create a specialized version of the body for
|
||||
// each specialization.
|
||||
for (_specialization_mark, (var, specialized_symbol)) in
|
||||
needed_specializations
|
||||
{
|
||||
use crate::copy::deep_copy_type_vars_into_expr;
|
||||
|
||||
let (new_def_expr_var, specialized_expr) = deep_copy_type_vars_into_expr(
|
||||
env.arena,
|
||||
env.subs,
|
||||
def.expr_var,
|
||||
&def.loc_expr.value,
|
||||
)
|
||||
.expect(
|
||||
"expr marked as having specializations, but it has no type variables!",
|
||||
);
|
||||
|
||||
let _res =
|
||||
roc_unify::unify::unify(env.subs, var, new_def_expr_var, Mode::EQ);
|
||||
|
||||
resolve_abilities_in_specialized_body(
|
||||
env,
|
||||
procs,
|
||||
&def.loc_expr.value,
|
||||
def.expr_var,
|
||||
);
|
||||
|
||||
stmt = with_hole(
|
||||
env,
|
||||
specialized_expr,
|
||||
new_def_expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
specialized_symbol,
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
|
||||
stmt
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// this may be a destructure pattern
|
||||
let (mono_pattern, assignments) =
|
||||
match from_can_pattern(env, procs, layout_cache, &def.loc_pattern.value) {
|
||||
Ok(v) => v,
|
||||
Err(_) => todo!(),
|
||||
};
|
||||
|
||||
// convert the continuation
|
||||
let mut stmt = lower_rest!(variable, cont.value);
|
||||
|
||||
// layer on any default record fields
|
||||
for (symbol, variable, expr) in assignments {
|
||||
let specialization_symbol = procs
|
||||
.symbol_specializations
|
||||
.remove_single(symbol)
|
||||
// Can happen when the symbol was never used under this body, and hence has no
|
||||
// requested specialization.
|
||||
.unwrap_or(symbol);
|
||||
|
||||
let hole = env.arena.alloc(stmt);
|
||||
stmt = with_hole(
|
||||
env,
|
||||
expr,
|
||||
variable,
|
||||
procs,
|
||||
layout_cache,
|
||||
specialization_symbol,
|
||||
hole,
|
||||
);
|
||||
}
|
||||
|
||||
if let roc_can::expr::Expr::Var(outer_symbol) = def.loc_expr.value {
|
||||
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||
} else {
|
||||
let outer_symbol = env.unique_symbol();
|
||||
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt);
|
||||
|
||||
resolve_abilities_in_specialized_body(env, procs, &def.loc_expr.value, def.expr_var);
|
||||
|
||||
// convert the def body, store in outer_symbol
|
||||
with_hole(
|
||||
env,
|
||||
def.loc_expr.value,
|
||||
def.expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
outer_symbol,
|
||||
env.arena.alloc(stmt),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// turn record/tag patterns into a when expression, e.g.
|
||||
///
|
||||
/// foo = \{ x } -> body
|
||||
@ -5787,286 +6074,7 @@ pub fn from_can<'a>(
|
||||
|
||||
from_can(env, variable, cont.value, procs, layout_cache)
|
||||
}
|
||||
LetNonRec(def, cont) => {
|
||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||
match def.loc_expr.value {
|
||||
roc_can::expr::Expr::Closure(closure_data) => {
|
||||
register_capturing_closure(env, procs, layout_cache, *symbol, closure_data);
|
||||
|
||||
return from_can(env, variable, cont.value, procs, layout_cache);
|
||||
}
|
||||
roc_can::expr::Expr::Accessor(accessor_data) => {
|
||||
let fresh_record_symbol = env.unique_symbol();
|
||||
register_noncapturing_closure(
|
||||
env,
|
||||
procs,
|
||||
*symbol,
|
||||
accessor_data.to_closure_data(fresh_record_symbol),
|
||||
);
|
||||
|
||||
return from_can(env, variable, cont.value, procs, layout_cache);
|
||||
}
|
||||
roc_can::expr::Expr::Var(original)
|
||||
| roc_can::expr::Expr::AbilityMember(original, _) => {
|
||||
// a variable is aliased, e.g.
|
||||
//
|
||||
// foo = bar
|
||||
//
|
||||
// or
|
||||
//
|
||||
// foo = RBTRee.empty
|
||||
|
||||
// TODO: right now we need help out rustc with the closure types;
|
||||
// it isn't able to infer the right lifetime bounds. See if we
|
||||
// can remove the annotations in the future.
|
||||
let build_rest =
|
||||
|env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>| {
|
||||
from_can(env, def.expr_var, cont.value, procs, layout_cache)
|
||||
};
|
||||
|
||||
return handle_variable_aliasing(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
def.expr_var,
|
||||
*symbol,
|
||||
original,
|
||||
build_rest,
|
||||
);
|
||||
}
|
||||
roc_can::expr::Expr::LetNonRec(nested_def, nested_cont) => {
|
||||
use roc_can::expr::Expr::*;
|
||||
// We must transform
|
||||
//
|
||||
// let answer = 1337
|
||||
// in
|
||||
// let unused =
|
||||
// let nested = 17
|
||||
// in
|
||||
// nested
|
||||
// in
|
||||
// answer
|
||||
//
|
||||
// into
|
||||
//
|
||||
// let answer = 1337
|
||||
// in
|
||||
// let nested = 17
|
||||
// in
|
||||
// let unused = nested
|
||||
// in
|
||||
// answer
|
||||
|
||||
let new_def = roc_can::def::Def {
|
||||
loc_pattern: def.loc_pattern,
|
||||
loc_expr: *nested_cont,
|
||||
pattern_vars: def.pattern_vars,
|
||||
annotation: def.annotation,
|
||||
expr_var: def.expr_var,
|
||||
};
|
||||
|
||||
let new_inner = LetNonRec(Box::new(new_def), cont);
|
||||
|
||||
let new_outer = LetNonRec(nested_def, Box::new(Loc::at_zero(new_inner)));
|
||||
|
||||
return from_can(env, variable, new_outer, procs, layout_cache);
|
||||
}
|
||||
roc_can::expr::Expr::LetRec(nested_defs, nested_cont) => {
|
||||
use roc_can::expr::Expr::*;
|
||||
// We must transform
|
||||
//
|
||||
// let answer = 1337
|
||||
// in
|
||||
// let unused =
|
||||
// let nested = \{} -> nested {}
|
||||
// in
|
||||
// nested
|
||||
// in
|
||||
// answer
|
||||
//
|
||||
// into
|
||||
//
|
||||
// let answer = 1337
|
||||
// in
|
||||
// let nested = \{} -> nested {}
|
||||
// in
|
||||
// let unused = nested
|
||||
// in
|
||||
// answer
|
||||
|
||||
let new_def = roc_can::def::Def {
|
||||
loc_pattern: def.loc_pattern,
|
||||
loc_expr: *nested_cont,
|
||||
pattern_vars: def.pattern_vars,
|
||||
annotation: def.annotation,
|
||||
expr_var: def.expr_var,
|
||||
};
|
||||
|
||||
let new_inner = LetNonRec(Box::new(new_def), cont);
|
||||
|
||||
let new_outer = LetRec(nested_defs, Box::new(Loc::at_zero(new_inner)));
|
||||
|
||||
return from_can(env, variable, new_outer, procs, layout_cache);
|
||||
}
|
||||
_ => {
|
||||
let rest = from_can(env, variable, cont.value, procs, layout_cache);
|
||||
|
||||
// Remove all the requested symbol specializations now, since this is the
|
||||
// def site and hence we won't need them any higher up.
|
||||
let mut needed_specializations =
|
||||
procs.symbol_specializations.remove(*symbol);
|
||||
|
||||
if needed_specializations.len() == 0 {
|
||||
// We don't need any specializations, that means this symbol is never
|
||||
// referenced.
|
||||
return with_hole(
|
||||
env,
|
||||
def.loc_expr.value,
|
||||
def.expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
*symbol,
|
||||
env.arena.alloc(rest),
|
||||
);
|
||||
}
|
||||
|
||||
// We do need specializations
|
||||
|
||||
let mut stmt = rest;
|
||||
|
||||
if needed_specializations.len() == 1 {
|
||||
let (_specialization_mark, (var, specialized_symbol)) =
|
||||
needed_specializations.next().unwrap();
|
||||
|
||||
// Unify the expr_var with the requested specialization once.
|
||||
let _res =
|
||||
roc_unify::unify::unify(env.subs, var, def.expr_var, Mode::EQ);
|
||||
|
||||
resolve_abilities_in_specialized_body(
|
||||
env,
|
||||
procs,
|
||||
&def.loc_expr.value,
|
||||
def.expr_var,
|
||||
);
|
||||
|
||||
return with_hole(
|
||||
env,
|
||||
def.loc_expr.value,
|
||||
def.expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
specialized_symbol,
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
} else {
|
||||
// Need to eat the cost and create a specialized version of the body for each specialization.
|
||||
for (_specialization_mark, (var, specialized_symbol)) in
|
||||
needed_specializations
|
||||
{
|
||||
use crate::copy::deep_copy_type_vars_into_expr;
|
||||
|
||||
let (new_def_expr_var, specialized_expr) =
|
||||
deep_copy_type_vars_into_expr(
|
||||
env.arena,
|
||||
env.subs,
|
||||
def.expr_var,
|
||||
&def.loc_expr.value
|
||||
).expect("expr marked as having specializations, but it has no type variables!");
|
||||
|
||||
let _res = roc_unify::unify::unify(
|
||||
env.subs,
|
||||
var,
|
||||
new_def_expr_var,
|
||||
Mode::EQ,
|
||||
);
|
||||
|
||||
resolve_abilities_in_specialized_body(
|
||||
env,
|
||||
procs,
|
||||
&def.loc_expr.value,
|
||||
def.expr_var,
|
||||
);
|
||||
|
||||
stmt = with_hole(
|
||||
env,
|
||||
specialized_expr,
|
||||
new_def_expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
specialized_symbol,
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
|
||||
return stmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this may be a destructure pattern
|
||||
let (mono_pattern, assignments) =
|
||||
match from_can_pattern(env, procs, layout_cache, &def.loc_pattern.value) {
|
||||
Ok(v) => v,
|
||||
Err(_) => todo!(),
|
||||
};
|
||||
|
||||
if let Pattern::Identifier(_symbol) = mono_pattern {
|
||||
internal_error!("Identifier patterns should be handled in a higher code pass!")
|
||||
}
|
||||
|
||||
// convert the continuation
|
||||
let mut stmt = from_can(env, variable, cont.value, procs, layout_cache);
|
||||
|
||||
// layer on any default record fields
|
||||
for (symbol, variable, expr) in assignments {
|
||||
let specialization_symbol = procs
|
||||
.symbol_specializations
|
||||
.remove_single(symbol)
|
||||
// Can happen when the symbol was never used under this body, and hence has no
|
||||
// requested specialization.
|
||||
.unwrap_or(symbol);
|
||||
|
||||
let hole = env.arena.alloc(stmt);
|
||||
stmt = with_hole(
|
||||
env,
|
||||
expr,
|
||||
variable,
|
||||
procs,
|
||||
layout_cache,
|
||||
specialization_symbol,
|
||||
hole,
|
||||
);
|
||||
}
|
||||
|
||||
if let roc_can::expr::Expr::Var(outer_symbol) = def.loc_expr.value {
|
||||
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||
} else {
|
||||
let outer_symbol = env.unique_symbol();
|
||||
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt);
|
||||
|
||||
resolve_abilities_in_specialized_body(
|
||||
env,
|
||||
procs,
|
||||
&def.loc_expr.value,
|
||||
def.expr_var,
|
||||
);
|
||||
|
||||
// convert the def body, store in outer_symbol
|
||||
with_hole(
|
||||
env,
|
||||
def.loc_expr.value,
|
||||
def.expr_var,
|
||||
procs,
|
||||
layout_cache,
|
||||
outer_symbol,
|
||||
env.arena.alloc(stmt),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LetNonRec(def, cont) => from_can_let(env, procs, layout_cache, def, cont, variable, None),
|
||||
_ => {
|
||||
let symbol = env.unique_symbol();
|
||||
let hole = env.arena.alloc(Stmt::Ret(symbol));
|
||||
|
Loading…
Reference in New Issue
Block a user