fix bug with imported values

This commit is contained in:
Folkert 2020-12-14 01:11:28 +01:00
parent 173f84400e
commit 879cbfabe4

View file

@ -2653,24 +2653,25 @@ pub fn with_hole<'a>(
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena); let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
enum Field { enum Field {
Imported(Symbol, Variable), Function(Symbol, Variable),
NakedSymbol, ValueSymbol,
Field(roc_can::expr::Field), Field(roc_can::expr::Field),
} }
for (label, variable, _) in sorted_fields.into_iter() { for (label, variable, _) in sorted_fields.into_iter() {
// TODO how should function pointers be handled here? // TODO how should function pointers be handled here?
use ReuseSymbol::*;
match fields.remove(&label) { match fields.remove(&label) {
Some(field) => match can_reuse_symbol(procs, &field.loc_expr.value) { Some(field) => match can_reuse_symbol(env, procs, &field.loc_expr.value) {
Some(reusable) if env.is_imported_symbol(reusable) => { Imported(symbol) | LocalFunction(symbol) => {
field_symbols.push(reusable); field_symbols.push(symbol);
can_fields.push(Field::Imported(reusable, variable)); can_fields.push(Field::Function(symbol, variable));
} }
Some(reusable) => { Value(reusable) => {
field_symbols.push(reusable); field_symbols.push(reusable);
can_fields.push(Field::NakedSymbol); can_fields.push(Field::ValueSymbol);
} }
None => { NotASymbol => {
field_symbols.push(env.unique_symbol()); field_symbols.push(env.unique_symbol());
can_fields.push(Field::Field(field)); can_fields.push(Field::Field(field));
} }
@ -2693,17 +2694,19 @@ pub fn with_hole<'a>(
for (opt_field, symbol) in can_fields.into_iter().rev().zip(field_symbols.iter().rev()) for (opt_field, symbol) in can_fields.into_iter().rev().zip(field_symbols.iter().rev())
{ {
match opt_field { match opt_field {
Field::NakedSymbol => {} Field::ValueSymbol => {
Field::Imported(symbol, variable) => { // this symbol is already defined; nothing to do
// make sure this symbol is specialized appropriately in its source module }
add_needed_external(procs, env, variable, symbol); Field::Function(symbol, variable) => {
stmt = reuse_function_symbol(
let layout = layout_cache env,
.from_var(env.arena, variable, env.subs) procs,
.expect("creating layout does not fail"); layout_cache,
let expr = Expr::FunctionPointer(symbol, layout.clone()); Some(variable),
symbol,
stmt = Stmt::Let(symbol, expr, layout, env.arena.alloc(stmt)); stmt,
symbol,
);
} }
Field::Field(field) => { Field::Field(field) => {
stmt = with_hole( stmt = with_hole(
@ -2714,7 +2717,7 @@ pub fn with_hole<'a>(
layout_cache, layout_cache,
*symbol, *symbol,
env.arena.alloc(stmt), env.arena.alloc(stmt),
) );
} }
} }
} }
@ -3382,8 +3385,15 @@ pub fn with_hole<'a>(
// if the function expression (loc_expr) is already a symbol, // if the function expression (loc_expr) is already a symbol,
// re-use that symbol, and don't define its value again // re-use that symbol, and don't define its value again
let mut result; let mut result;
match can_reuse_symbol(procs, &loc_expr.value) { use ReuseSymbol::*;
Some(function_symbol) => { match can_reuse_symbol(env, procs, &loc_expr.value) {
LocalFunction(_) => {
unreachable!("if this was known to be a function, we would not be here")
}
Imported(_) => {
unreachable!("an imported value is never an anonymous function")
}
Value(function_symbol) => {
if let Layout::Closure(_, closure_fields, _) = full_layout { if let Layout::Closure(_, closure_fields, _) = full_layout {
// we're invoking a closure // we're invoking a closure
let closure_record_symbol = env.unique_symbol(); let closure_record_symbol = env.unique_symbol();
@ -3476,7 +3486,7 @@ pub fn with_hole<'a>(
); );
} }
} }
None => { NotASymbol => {
let function_symbol = env.unique_symbol(); let function_symbol = env.unique_symbol();
result = Stmt::Let( result = Stmt::Let(
@ -4717,15 +4727,33 @@ fn store_record_destruct<'a>(
/// We want to re-use symbols that are not function symbols /// We want to re-use symbols that are not function symbols
/// for any other expression, we create a new symbol, and will /// for any other expression, we create a new symbol, and will
/// later make sure it gets assigned the correct value. /// later make sure it gets assigned the correct value.
fn can_reuse_symbol<'a>(procs: &Procs<'a>, expr: &roc_can::expr::Expr) -> Option<Symbol> {
enum ReuseSymbol {
Imported(Symbol),
LocalFunction(Symbol),
Value(Symbol),
NotASymbol,
}
fn can_reuse_symbol<'a>(
env: &mut Env<'a, '_>,
procs: &Procs<'a>,
expr: &roc_can::expr::Expr,
) -> ReuseSymbol {
use ReuseSymbol::*;
if let roc_can::expr::Expr::Var(symbol) = expr { if let roc_can::expr::Expr::Var(symbol) = expr {
if procs.partial_procs.contains_key(&symbol) { let symbol = *symbol;
None
if env.is_imported_symbol(symbol) {
Imported(symbol)
} else if procs.partial_procs.contains_key(&symbol) {
LocalFunction(symbol)
} else { } else {
Some(*symbol) Value(symbol)
} }
} else { } else {
None NotASymbol
} }
} }
@ -4734,9 +4762,9 @@ fn possible_reuse_symbol<'a>(
procs: &Procs<'a>, procs: &Procs<'a>,
expr: &roc_can::expr::Expr, expr: &roc_can::expr::Expr,
) -> Symbol { ) -> Symbol {
match can_reuse_symbol(procs, expr) { match can_reuse_symbol(env, procs, expr) {
Some(s) => s, ReuseSymbol::Value(s) => s,
None => env.unique_symbol(), _ => env.unique_symbol(),
} }
} }
@ -4792,8 +4820,7 @@ fn reuse_function_symbol<'a>(
) -> Stmt<'a> { ) -> Stmt<'a> {
match procs.partial_procs.get(&original) { match procs.partial_procs.get(&original) {
None => { None => {
let is_imported = let is_imported = env.is_imported_symbol(original);
!(env.home == original.module_id() || original.module_id() == ModuleId::ATTR);
match arg_var { match arg_var {
Some(arg_var) if is_imported => { Some(arg_var) if is_imported => {
@ -4814,7 +4841,7 @@ fn reuse_function_symbol<'a>(
// an imported symbol is always a function pointer: // an imported symbol is always a function pointer:
// either it's a function, or a top-level 0-argument thunk // either it's a function, or a top-level 0-argument thunk
let expr = Expr::FunctionPointer(original, layout.clone()); let expr = Expr::FunctionPointer(original, layout.clone());
return Stmt::Let(original, expr, layout, env.arena.alloc(result)); return Stmt::Let(symbol, expr, layout, env.arena.alloc(result));
} }
_ => { _ => {
// danger: a foreign symbol may not be specialized! // danger: a foreign symbol may not be specialized!
@ -4954,19 +4981,25 @@ fn assign_to_symbol<'a>(
symbol: Symbol, symbol: Symbol,
result: Stmt<'a>, result: Stmt<'a>,
) -> Stmt<'a> { ) -> Stmt<'a> {
// if this argument is already a symbol, we don't need to re-define it use ReuseSymbol::*;
if let roc_can::expr::Expr::Var(original) = loc_arg.value { match can_reuse_symbol(env, procs, &loc_arg.value) {
reuse_function_symbol( Imported(original) | LocalFunction(original) => {
env, // for functions we must make sure they are specialized correctly
procs, reuse_function_symbol(
layout_cache, env,
Some(arg_var), procs,
symbol, layout_cache,
result, Some(arg_var),
original, symbol,
) result,
} else { original,
with_hole( )
}
Value(_) => {
// symbol is already defined; nothing else to do here
result
}
NotASymbol => with_hole(
env, env,
loc_arg.value, loc_arg.value,
arg_var, arg_var,
@ -4974,7 +5007,7 @@ fn assign_to_symbol<'a>(
layout_cache, layout_cache,
symbol, symbol,
env.arena.alloc(result), env.arena.alloc(result),
) ),
} }
} }