diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index b021dc2192..6601ca0e52 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -189,7 +189,11 @@ pub enum Expr { }, // Test - Expect(Box>, Box>), + Expect { + loc_condition: Box>, + loc_continuation: Box>, + lookups_in_cond: Vec, + }, // Compiles, but will crash if reached RuntimeError(RuntimeError), @@ -852,6 +856,12 @@ pub fn canonicalize_expr<'a>( let (loc_condition, output1) = canonicalize_expr(env, var_store, scope, condition.region, &condition.value); + let mut lookups_in_cond = Vec::new(); + + // Get all the lookups that were referenced in the condition, + // so we can print their values later. + add_lookup_symbols(&loc_condition.value, &mut lookups_in_cond); + let (loc_continuation, output2) = canonicalize_expr( env, var_store, @@ -864,7 +874,11 @@ pub fn canonicalize_expr<'a>( output.union(output2); ( - Expect(Box::new(loc_condition), Box::new(loc_continuation)), + Expect { + loc_condition: Box::new(loc_condition), + loc_continuation: Box::new(loc_continuation), + lookups_in_cond, + }, output, ) } @@ -1407,18 +1421,26 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> } } - Expect(loc_condition, loc_expr) => { + Expect { + loc_condition, + loc_continuation, + lookups_in_cond, + } => { let loc_condition = Loc { region: loc_condition.region, value: inline_calls(var_store, scope, loc_condition.value), }; - let loc_expr = Loc { - region: loc_expr.region, - value: inline_calls(var_store, scope, loc_expr.value), + let loc_continuation = Loc { + region: loc_continuation.region, + value: inline_calls(var_store, scope, loc_continuation.value), }; - Expect(Box::new(loc_condition), Box::new(loc_expr)) + Expect { + loc_condition: Box::new(loc_condition), + loc_continuation: Box::new(loc_continuation), + lookups_in_cond, + } } LetRec(defs, loc_expr, var) => { @@ -1820,3 +1842,92 @@ pub fn unescape_char(escaped: &EscapedChar) -> char { Newline => '\n', } } + +fn add_lookup_symbols(expr: &Expr, symbols: &mut Vec) { + match expr { + Expr::Var(symbol) | Expr::Update { symbol, .. } => { + symbols.push(*symbol); + } + Expr::List { loc_elems, .. } => { + for loc_elem in loc_elems { + add_lookup_symbols(&loc_elem.value, symbols); + } + } + Expr::When { + loc_cond, branches, .. + } => { + add_lookup_symbols(&loc_cond.value, symbols); + + for branch in branches { + add_lookup_symbols(&branch.value.value, symbols); + + if let Some(guard) = &branch.guard { + add_lookup_symbols(&guard.value, symbols); + } + } + } + Expr::If { + branches, + final_else, + .. + } => { + for (loc_cond, loc_body) in branches { + add_lookup_symbols(&loc_cond.value, symbols); + add_lookup_symbols(&loc_body.value, symbols); + } + + add_lookup_symbols(&final_else.value, symbols); + } + Expr::LetRec(_, _, _) => todo!(), + Expr::LetNonRec(_, _, _) => todo!(), + Expr::Call(boxed_expr, args, _called_via) => { + // add the expr being called + add_lookup_symbols(&boxed_expr.1.value, symbols); + + for (_var, loc_arg) in args { + add_lookup_symbols(&loc_arg.value, symbols); + } + } + Expr::Tag { arguments, .. } => { + for (_var, loc_expr) in arguments { + add_lookup_symbols(&loc_expr.value, symbols); + } + } + Expr::RunLowLevel { args, .. } | Expr::ForeignCall { args, .. } => { + for (_var, arg) in args { + add_lookup_symbols(arg, symbols); + } + } + Expr::OpaqueRef { argument, .. } => { + add_lookup_symbols(&argument.1.value, symbols); + } + Expr::Access { loc_expr, .. } + | Expr::Closure(ClosureData { + loc_body: loc_expr, .. + }) => { + add_lookup_symbols(&loc_expr.value, symbols); + } + Expr::Record { fields, .. } => { + for field in fields.values() { + add_lookup_symbols(&field.loc_expr.value, symbols); + } + } + Expr::Expect { + loc_continuation, .. + } => { + add_lookup_symbols(&(*loc_continuation).value, symbols); + + // Intentionally ignore the lookups in the nested `expect` condition itself, + // because they couldn't possibly influence the outcome of this `expect`! + } + Expr::Num(_, _, _, _) + | Expr::Float(_, _, _, _, _) + | Expr::Int(_, _, _, _, _) + | Expr::Str(_) + | Expr::ZeroArgumentTag { .. } + | Expr::Accessor(_) + | Expr::SingleQuote(_) + | Expr::EmptyRecord + | Expr::RuntimeError(_) => {} + } +} diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index da3b79dfa1..80cac2b8fb 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -611,9 +611,13 @@ fn fix_values_captured_in_closure_expr( fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols); } - Expect(condition, loc_expr) => { - fix_values_captured_in_closure_expr(&mut condition.value, no_capture_symbols); - fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols); + Expect { + loc_condition, + loc_continuation, + lookups_in_cond: _, + } => { + fix_values_captured_in_closure_expr(&mut loc_condition.value, no_capture_symbols); + fix_values_captured_in_closure_expr(&mut loc_continuation.value, no_capture_symbols); } Closure(ClosureData { diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index abcedc7d05..91fa4edcbe 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -413,7 +413,11 @@ pub fn constrain_expr( constraints.exists_many(vars, cons) } - Expect(loc_cond, continuation) => { + Expect { + loc_condition, + loc_continuation, + lookups_in_cond: _, + } => { let expect_bool = |region| { let bool_type = Type::Variable(Variable::BOOL); Expected::ForReason(Reason::ExpectCondition, bool_type, region) @@ -422,16 +426,16 @@ pub fn constrain_expr( let cond_con = constrain_expr( constraints, env, - loc_cond.region, - &loc_cond.value, - expect_bool(loc_cond.region), + loc_condition.region, + &loc_condition.value, + expect_bool(loc_condition.region), ); let continuation_con = constrain_expr( constraints, env, - continuation.region, - &continuation.value, + loc_continuation.region, + &loc_continuation.value, expected, ); diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 97279ccce7..90feeff092 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -3586,7 +3586,7 @@ pub fn with_hole<'a>( EmptyRecord => let_empty_struct(assigned, hole), - Expect(_, _) => unreachable!("I think this is unreachable"), + Expect { .. } => unreachable!("I think this is unreachable"), If { cond_var, @@ -5408,8 +5408,12 @@ pub fn from_can<'a>( stmt } - Expect(condition, rest) => { - let rest = from_can(env, variable, rest.value, procs, layout_cache); + Expect { + loc_condition, + loc_continuation, + lookups_in_cond, + } => { + let rest = from_can(env, variable, loc_continuation.value, procs, layout_cache); let bool_layout = Layout::Builtin(Builtin::Bool); let cond_symbol = env.unique_symbol(); @@ -5434,7 +5438,7 @@ pub fn from_can<'a>( with_hole( env, - condition.value, + loc_condition.value, variable, procs, layout_cache,