From 4ca9ea0b89669578a2e9697a5b9c98da87888090 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 4 Mar 2022 23:57:41 +0100 Subject: [PATCH 1/6] refactor After --- compiler/solve/src/solve.rs | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index c7ce79b7a6..9bedf114f7 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -211,19 +211,13 @@ pub fn run_in_place( state.env } -enum After { - CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc)>), -} - enum Work<'a> { Constraint { env: &'a Env, rank: Rank, constraint: &'a Constraint, - after: Option, }, - /// Something to be done after a constraint and all its dependencies are fully solved. - After(After), + CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc)>), } #[allow(clippy::too_many_arguments)] @@ -238,16 +232,16 @@ fn solve( subs: &mut Subs, constraint: &Constraint, ) -> State { - let mut stack = vec![Work::Constraint { + let initial = Work::Constraint { env, rank, constraint, - after: None, - }]; + }; + let mut stack = vec![initial]; while let Some(work_item) = stack.pop() { let (env, rank, constraint) = match work_item { - Work::After(After::CheckForInfiniteTypes(def_vars)) => { + Work::CheckForInfiniteTypes(def_vars) => { for (symbol, loc_var) in def_vars.iter() { check_for_infinite_type(subs, problems, *symbol, *loc_var); } @@ -258,15 +252,7 @@ fn solve( env, rank, constraint, - after, - } => { - // Push the `after` on first so that we look at it immediately after finishing all - // the children of this constraint. - if let Some(after) = after { - stack.push(Work::After(after)); - } - (env, rank, constraint) - } + } => (env, rank, constraint), }; state = match constraint { @@ -421,7 +407,6 @@ fn solve( env, rank, constraint: sub_constraint, - after: None, }) } @@ -483,7 +468,6 @@ fn solve( env, rank, constraint: &let_con.defs_constraint, - after: None, }); state } @@ -523,11 +507,11 @@ fn solve( new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); } + stack.push(Work::CheckForInfiniteTypes(local_def_vars)); stack.push(Work::Constraint { env: arena.alloc(new_env), rank, constraint: ret_con, - after: Some(After::CheckForInfiniteTypes(local_def_vars)), }); state @@ -667,11 +651,11 @@ fn solve( // Now solve the body, using the new vars_by_symbol which includes // the assignments' name-to-variable mappings. + stack.push(Work::CheckForInfiniteTypes(local_def_vars)); stack.push(Work::Constraint { env: arena.alloc(new_env), rank, constraint: ret_con, - after: Some(After::CheckForInfiniteTypes(local_def_vars)), }); state_for_ret_con From 41df04184e10f986988ed71eb8a90028bf0cf2ca Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 00:26:03 +0100 Subject: [PATCH 2/6] make the simple case tail-recursive --- compiler/solve/src/solve.rs | 89 ++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 9bedf114f7..080a693493 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -1,6 +1,6 @@ use bumpalo::Bump; use roc_can::constraint::Constraint::{self, *}; -use roc_can::constraint::PresenceConstraint; +use roc_can::constraint::{LetConstraint, PresenceConstraint}; use roc_can::expected::{Expected, PExpected}; use roc_collections::all::MutMap; use roc_module::ident::TagName; @@ -218,6 +218,11 @@ enum Work<'a> { constraint: &'a Constraint, }, CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc)>), + LetConSimple { + env: &'a Env, + rank: Rank, + let_con: &'a LetConstraint, + }, } #[allow(clippy::too_many_arguments)] @@ -241,6 +246,11 @@ fn solve( let mut stack = vec![initial]; while let Some(work_item) = stack.pop() { let (env, rank, constraint) = match work_item { + Work::Constraint { + env, + rank, + constraint, + } => (env, rank, constraint), Work::CheckForInfiniteTypes(def_vars) => { for (symbol, loc_var) in def_vars.iter() { check_for_infinite_type(subs, problems, *symbol, *loc_var); @@ -248,11 +258,35 @@ fn solve( // No constraint to be solved continue; } - Work::Constraint { - env, - rank, - constraint, - } => (env, rank, constraint), + Work::LetConSimple { env, rank, let_con } => { + // Add a variable for each def to new_vars_by_env. + let mut local_def_vars = LocalDefVarsVec::with_length(let_con.def_types.len()); + + for (symbol, loc_type) in let_con.def_types.iter() { + let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); + + local_def_vars.push(( + *symbol, + Loc { + value: var, + region: loc_type.region, + }, + )); + } + + let mut new_env = env.clone(); + for (symbol, loc_var) in local_def_vars.iter() { + new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); + } + + stack.push(Work::CheckForInfiniteTypes(local_def_vars)); + stack.push(Work::Constraint { + env: arena.alloc(new_env), + rank, + constraint: &let_con.ret_constraint, + }); + continue; + } }; state = match constraint { @@ -471,47 +505,12 @@ fn solve( }); state } - ret_con if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => { - // TODO: make into `WorkItem` with `After` - let state = solve( - arena, - env, - state, - rank, - pools, - problems, - cached_aliases, - subs, - &let_con.defs_constraint, - ); - - // Add a variable for each def to new_vars_by_env. - let mut local_def_vars = - LocalDefVarsVec::with_length(let_con.def_types.len()); - - for (symbol, loc_type) in let_con.def_types.iter() { - let var = - type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); - - local_def_vars.push(( - *symbol, - Loc { - value: var, - region: loc_type.region, - }, - )); - } - - let mut new_env = env.clone(); - for (symbol, loc_var) in local_def_vars.iter() { - new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); - } - - stack.push(Work::CheckForInfiniteTypes(local_def_vars)); + _ if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => { + stack.push(Work::LetConSimple { env, rank, let_con }); stack.push(Work::Constraint { - env: arena.alloc(new_env), + env, rank, - constraint: ret_con, + constraint: &let_con.defs_constraint, }); state From 3a1add6ce81652d0a1df1800f116d037047189d6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 00:51:08 +0100 Subject: [PATCH 3/6] add LocalDefVarsVec::from_def_types --- compiler/solve/src/solve.rs | 71 +++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 080a693493..d8538383ea 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -2,7 +2,7 @@ use bumpalo::Bump; use roc_can::constraint::Constraint::{self, *}; use roc_can::constraint::{LetConstraint, PresenceConstraint}; use roc_can::expected::{Expected, PExpected}; -use roc_collections::all::MutMap; +use roc_collections::all::{MutMap, SendMap}; use roc_module::ident::TagName; use roc_module::symbol::Symbol; use roc_region::all::{Loc, Region}; @@ -260,19 +260,13 @@ fn solve( } Work::LetConSimple { env, rank, let_con } => { // Add a variable for each def to new_vars_by_env. - let mut local_def_vars = LocalDefVarsVec::with_length(let_con.def_types.len()); - - for (symbol, loc_type) in let_con.def_types.iter() { - let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); - - local_def_vars.push(( - *symbol, - Loc { - value: var, - region: loc_type.region, - }, - )); - } + let local_def_vars = LocalDefVarsVec::from_def_types( + rank, + pools, + cached_aliases, + subs, + &let_con.def_types, + ); let mut new_env = env.clone(); for (symbol, loc_var) in local_def_vars.iter() { @@ -548,22 +542,13 @@ fn solve( // run solver in next pool // Add a variable for each def to local_def_vars. - let mut local_def_vars = - LocalDefVarsVec::with_length(let_con.def_types.len()); - - for (symbol, loc_type) in let_con.def_types.iter() { - let def_type = &loc_type.value; - - let var = type_to_var(subs, next_rank, pools, cached_aliases, def_type); - - local_def_vars.push(( - *symbol, - Loc { - value: var, - region: loc_type.region, - }, - )); - } + let local_def_vars = LocalDefVarsVec::from_def_types( + next_rank, + pools, + cached_aliases, + subs, + &let_con.def_types, + ); // Solve the assignments' constraints first. // TODO: make into `WorkItem` with `After` @@ -759,6 +744,32 @@ impl LocalDefVarsVec { } } +impl LocalDefVarsVec<(Symbol, Loc)> { + fn from_def_types( + rank: Rank, + pools: &mut Pools, + cached_aliases: &mut MutMap, + subs: &mut Subs, + def_types: &SendMap>, + ) -> Self { + let mut local_def_vars = Self::with_length(def_types.len()); + + for (symbol, loc_type) in def_types.iter() { + let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); + + local_def_vars.push(( + *symbol, + Loc { + value: var, + region: loc_type.region, + }, + )); + } + + local_def_vars + } +} + use std::cell::RefCell; std::thread_local! { /// Scratchpad arena so we don't need to allocate a new one all the time From c7d8ae6c798be447ec8ec4e5919f29c8cdb7a938 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 01:02:27 +0100 Subject: [PATCH 4/6] make complex case tail-recursive --- compiler/solve/src/solve.rs | 200 ++++++++++++++++++++---------------- 1 file changed, 110 insertions(+), 90 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index d8538383ea..3a4f20b277 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -223,6 +223,13 @@ enum Work<'a> { rank: Rank, let_con: &'a LetConstraint, }, + LetConComplex { + env: &'a Env, + rank: Rank, + next_rank: Rank, + let_con: &'a LetConstraint, + local_def_vars: LocalDefVarsVec<(Symbol, Loc)>, + }, } #[allow(clippy::too_many_arguments)] @@ -259,6 +266,8 @@ fn solve( continue; } Work::LetConSimple { env, rank, let_con } => { + // NOTE be extremely careful with shadowing here + // Add a variable for each def to new_vars_by_env. let local_def_vars = LocalDefVarsVec::from_def_types( rank, @@ -281,6 +290,96 @@ fn solve( }); continue; } + Work::LetConComplex { + env, + rank, + next_rank, + let_con, + local_def_vars, + } => { + // NOTE be extremely careful with shadowing here + let mark = state.mark; + let saved_env = state.env; + + let rigid_vars = &let_con.rigid_vars; + + let young_mark = mark; + let visit_mark = young_mark.next(); + let final_mark = visit_mark.next(); + + debug_assert_eq!( + { + let offenders = pools + .get(next_rank) + .iter() + .filter(|var| { + let current_rank = + subs.get_rank(roc_types::subs::Variable::clone(var)); + + current_rank.into_usize() > next_rank.into_usize() + }) + .collect::>(); + + let result = offenders.len(); + + if result > 0 { + dbg!(&subs, &offenders, &let_con.def_types); + } + + result + }, + 0 + ); + + // pop pool + generalize(subs, young_mark, visit_mark, next_rank, pools); + + pools.get_mut(next_rank).clear(); + + // check that things went well + debug_assert!({ + // NOTE the `subs.redundant` check is added for the uniqueness + // inference, and does not come from elm. It's unclear whether this is + // a bug with uniqueness inference (something is redundant that + // shouldn't be) or that it just never came up in elm. + let failing: Vec<_> = rigid_vars + .iter() + .filter(|&var| !subs.redundant(*var) && subs.get_rank(*var) != Rank::NONE) + .collect(); + + if !failing.is_empty() { + println!("Rigids {:?}", &rigid_vars); + println!("Failing {:?}", failing); + } + + failing.is_empty() + }); + + let mut new_env = env.clone(); + for (symbol, loc_var) in local_def_vars.iter() { + new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); + } + + // Note that this vars_by_symbol is the one returned by the + // previous call to solve() + let state_for_ret_con = State { + env: saved_env, + mark: final_mark, + }; + + // Now solve the body, using the new vars_by_symbol which includes + // the assignments' name-to-variable mappings. + stack.push(Work::CheckForInfiniteTypes(local_def_vars)); + stack.push(Work::Constraint { + env: arena.alloc(new_env), + rank, + constraint: &let_con.ret_constraint, + }); + + state = state_for_ret_con; + + continue; + } }; state = match constraint { @@ -509,7 +608,7 @@ fn solve( state } - ret_con => { + _ => { let rigid_vars = &let_con.rigid_vars; let flex_vars = &let_con.flex_vars; @@ -550,99 +649,20 @@ fn solve( &let_con.def_types, ); - // Solve the assignments' constraints first. - // TODO: make into `WorkItem` with `After` - let State { - env: saved_env, - mark, - } = solve( - arena, + stack.push(Work::LetConComplex { env, - state, - next_rank, - pools, - problems, - cached_aliases, - subs, - &let_con.defs_constraint, - ); - - let young_mark = mark; - let visit_mark = young_mark.next(); - let final_mark = visit_mark.next(); - - debug_assert_eq!( - { - let offenders = pools - .get(next_rank) - .iter() - .filter(|var| { - let current_rank = - subs.get_rank(roc_types::subs::Variable::clone(var)); - - current_rank.into_usize() > next_rank.into_usize() - }) - .collect::>(); - - let result = offenders.len(); - - if result > 0 { - dbg!(&subs, &offenders, &let_con.def_types); - } - - result - }, - 0 - ); - - // pop pool - generalize(subs, young_mark, visit_mark, next_rank, pools); - - pools.get_mut(next_rank).clear(); - - // check that things went well - debug_assert!({ - // NOTE the `subs.redundant` check is added for the uniqueness - // inference, and does not come from elm. It's unclear whether this is - // a bug with uniqueness inference (something is redundant that - // shouldn't be) or that it just never came up in elm. - let failing: Vec<_> = rigid_vars - .iter() - .filter(|&var| { - !subs.redundant(*var) && subs.get_rank(*var) != Rank::NONE - }) - .collect(); - - if !failing.is_empty() { - println!("Rigids {:?}", &rigid_vars); - println!("Failing {:?}", failing); - } - - failing.is_empty() - }); - - let mut new_env = env.clone(); - for (symbol, loc_var) in local_def_vars.iter() { - new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); - } - - // Note that this vars_by_symbol is the one returned by the - // previous call to solve() - let state_for_ret_con = State { - env: saved_env, - mark: final_mark, - }; - - // Now solve the body, using the new vars_by_symbol which includes - // the assignments' name-to-variable mappings. - stack.push(Work::CheckForInfiniteTypes(local_def_vars)); - stack.push(Work::Constraint { - env: arena.alloc(new_env), rank, - constraint: ret_con, + let_con, + local_def_vars, + next_rank, + }); + stack.push(Work::Constraint { + env, + rank: next_rank, + constraint: &let_con.defs_constraint, }); - state_for_ret_con + state } } } From a79f6c6cdd11622867ce91a065e3fcf57ebed712 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 01:29:21 +0100 Subject: [PATCH 5/6] restructure and add comments --- compiler/solve/src/solve.rs | 169 ++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 86 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 3a4f20b277..24fd26ca1a 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -226,9 +226,7 @@ enum Work<'a> { LetConComplex { env: &'a Env, rank: Rank, - next_rank: Rank, let_con: &'a LetConstraint, - local_def_vars: LocalDefVarsVec<(Symbol, Loc)>, }, } @@ -257,12 +255,17 @@ fn solve( env, rank, constraint, - } => (env, rank, constraint), + } => { + // the default case; actually solve this constraint + (env, rank, constraint) + } Work::CheckForInfiniteTypes(def_vars) => { + // after a LetCon, we must check if any of the variables that we introduced + // loop back to themselves after solving the ret_constraint for (symbol, loc_var) in def_vars.iter() { check_for_infinite_type(subs, problems, *symbol, *loc_var); } - // No constraint to be solved + continue; } Work::LetConSimple { env, rank, let_con } => { @@ -288,16 +291,13 @@ fn solve( rank, constraint: &let_con.ret_constraint, }); + continue; } - Work::LetConComplex { - env, - rank, - next_rank, - let_con, - local_def_vars, - } => { + Work::LetConComplex { env, rank, let_con } => { // NOTE be extremely careful with shadowing here + let next_rank = rank.next(); + let mark = state.mark; let saved_env = state.env; @@ -307,16 +307,22 @@ fn solve( let visit_mark = young_mark.next(); let final_mark = visit_mark.next(); + // Add a variable for each def to local_def_vars. + let local_def_vars = LocalDefVarsVec::from_def_types( + next_rank, + pools, + cached_aliases, + subs, + &let_con.def_types, + ); + debug_assert_eq!( { let offenders = pools .get(next_rank) .iter() .filter(|var| { - let current_rank = - subs.get_rank(roc_types::subs::Variable::clone(var)); - - current_rank.into_usize() > next_rank.into_usize() + subs.get_rank(**var).into_usize() > next_rank.into_usize() }) .collect::>(); @@ -385,7 +391,6 @@ fn solve( state = match constraint { True => state, SaveTheEnvironment => { - // NOTE deviation: elm only copies the env into the state on SaveTheEnvironment let mut copy = state; copy.env = env.clone(); @@ -585,85 +590,77 @@ fn solve( } } Let(let_con) => { - match &let_con.ret_constraint { - True if let_con.rigid_vars.is_empty() => { - introduce(subs, rank, pools, &let_con.flex_vars); + if matches!(&let_con.ret_constraint, True) && let_con.rigid_vars.is_empty() { + introduce(subs, rank, pools, &let_con.flex_vars); - // If the return expression is guaranteed to solve, - // solve the assignments themselves and move on. - stack.push(Work::Constraint { - env, - rank, - constraint: &let_con.defs_constraint, - }); - state + // If the return expression is guaranteed to solve, + // solve the assignments themselves and move on. + stack.push(Work::Constraint { + env, + rank, + constraint: &let_con.defs_constraint, + }); + + state + } else if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() { + // items are popped from the stack in reverse order. That means that we'll + // first solve then defs_constraint, and then (eventually) the ret_constraint. + // + // Note that the LetConSimple gets the current env and rank, + // and not the env/rank from after solving the defs_constraint + stack.push(Work::LetConSimple { env, rank, let_con }); + stack.push(Work::Constraint { + env, + rank, + constraint: &let_con.defs_constraint, + }); + + state + } else { + let rigid_vars = &let_con.rigid_vars; + let flex_vars = &let_con.flex_vars; + + // work in the next pool to localize header + let next_rank = rank.next(); + + // introduce variables + for &var in rigid_vars.iter().chain(flex_vars.iter()) { + subs.set_rank(var, next_rank); } - _ if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => { - stack.push(Work::LetConSimple { env, rank, let_con }); - stack.push(Work::Constraint { - env, - rank, - constraint: &let_con.defs_constraint, - }); - state + // determine the next pool + if next_rank.into_usize() < pools.len() { + // Nothing to do, we already accounted for the next rank, no need to + // adjust the pools + } else { + // we should be off by one at this point + debug_assert_eq!(next_rank.into_usize(), 1 + pools.len()); + pools.extend_to(next_rank.into_usize()); } - _ => { - let rigid_vars = &let_con.rigid_vars; - let flex_vars = &let_con.flex_vars; - // work in the next pool to localize header - let next_rank = rank.next(); + let pool: &mut Vec = pools.get_mut(next_rank); - // introduce variables - for &var in rigid_vars.iter().chain(flex_vars.iter()) { - subs.set_rank(var, next_rank); - } + // Replace the contents of this pool with rigid_vars and flex_vars + pool.clear(); + pool.reserve(rigid_vars.len() + flex_vars.len()); + pool.extend(rigid_vars.iter()); + pool.extend(flex_vars.iter()); - // determine the next pool - if next_rank.into_usize() < pools.len() { - // Nothing to do, we already accounted for the next rank, no need to - // adjust the pools - } else { - // we should be off by one at this point - debug_assert_eq!(next_rank.into_usize(), 1 + pools.len()); - pools.extend_to(next_rank.into_usize()); - } + // run solver in next pool - let pool: &mut Vec = pools.get_mut(next_rank); + // items are popped from the stack in reverse order. That means that we'll + // first solve then defs_constraint, and then (eventually) the ret_constraint. + // + // Note that the LetConSimple gets the current env and rank, + // and not the env/rank from after solving the defs_constraint + stack.push(Work::LetConComplex { env, rank, let_con }); + stack.push(Work::Constraint { + env, + rank: next_rank, + constraint: &let_con.defs_constraint, + }); - // Replace the contents of this pool with rigid_vars and flex_vars - pool.clear(); - pool.reserve(rigid_vars.len() + flex_vars.len()); - pool.extend(rigid_vars.iter()); - pool.extend(flex_vars.iter()); - - // run solver in next pool - - // Add a variable for each def to local_def_vars. - let local_def_vars = LocalDefVarsVec::from_def_types( - next_rank, - pools, - cached_aliases, - subs, - &let_con.def_types, - ); - - stack.push(Work::LetConComplex { - env, - rank, - let_con, - local_def_vars, - next_rank, - }); - stack.push(Work::Constraint { - env, - rank: next_rank, - constraint: &let_con.defs_constraint, - }); - - state - } + state } } Present(typ, PresenceConstraint::IsOpen) => { From 1b6624730322d6468a258084f6d9dc0de1a76bb7 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 19:30:15 +0100 Subject: [PATCH 6/6] renaming --- compiler/solve/src/solve.rs | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index b82994af92..7a4efb1e48 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -222,12 +222,17 @@ enum Work<'a> { constraint: &'a Constraint, }, CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc)>), - LetConSimple { + /// The ret_con part of a let constraint that does NOT introduces rigid and/or flex variables + LetConNoVariables { env: &'a Env, rank: Rank, let_con: &'a LetConstraint, }, - LetConComplex { + /// The ret_con part of a let constraint that introduces rigid and/or flex variables + /// + /// These introduced variables must be generalized, hence this variant + /// is more complex than `LetConNoVariables`. + LetConIntroducesVariables { env: &'a Env, rank: Rank, let_con: &'a LetConstraint, @@ -274,7 +279,7 @@ fn solve( continue; } - Work::LetConSimple { env, rank, let_con } => { + Work::LetConNoVariables { env, rank, let_con } => { // NOTE be extremely careful with shadowing here let offset = let_con.defs_and_ret_constraint.index(); let ret_constraint = &constraints.constraints[offset + 1]; @@ -298,18 +303,16 @@ fn solve( stack.push(Work::Constraint { env: arena.alloc(new_env), rank, - constraint: &ret_constraint, + constraint: ret_constraint, }); continue; } - Work::LetConComplex { env, rank, let_con } => { + Work::LetConIntroducesVariables { env, rank, let_con } => { // NOTE be extremely careful with shadowing here let offset = let_con.defs_and_ret_constraint.index(); let ret_constraint = &constraints.constraints[offset + 1]; - let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()]; - let next_rank = rank.next(); let mark = state.mark; @@ -361,6 +364,8 @@ fn solve( // inference, and does not come from elm. It's unclear whether this is // a bug with uniqueness inference (something is redundant that // shouldn't be) or that it just never came up in elm. + let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()]; + let failing: Vec<_> = rigid_vars .iter() .filter(|&var| !subs.redundant(*var) && subs.get_rank(*var) != Rank::NONE) @@ -392,7 +397,7 @@ fn solve( stack.push(Work::Constraint { env: arena.alloc(new_env), rank, - constraint: &ret_constraint, + constraint: ret_constraint, }); state = state_for_ret_con; @@ -627,14 +632,14 @@ fn solve( let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()]; if matches!(&ret_constraint, True) && let_con.rigid_vars.is_empty() { - introduce(subs, rank, pools, &flex_vars); + introduce(subs, rank, pools, flex_vars); // If the return expression is guaranteed to solve, // solve the assignments themselves and move on. stack.push(Work::Constraint { env, rank, - constraint: &defs_constraint, + constraint: defs_constraint, }); state @@ -644,11 +649,11 @@ fn solve( // // Note that the LetConSimple gets the current env and rank, // and not the env/rank from after solving the defs_constraint - stack.push(Work::LetConSimple { env, rank, let_con }); + stack.push(Work::LetConNoVariables { env, rank, let_con }); stack.push(Work::Constraint { env, rank, - constraint: &defs_constraint, + constraint: defs_constraint, }); state @@ -686,11 +691,11 @@ fn solve( // // Note that the LetConSimple gets the current env and rank, // and not the env/rank from after solving the defs_constraint - stack.push(Work::LetConComplex { env, rank, let_con }); + stack.push(Work::LetConIntroducesVariables { env, rank, let_con }); stack.push(Work::Constraint { env, rank: next_rank, - constraint: &defs_constraint, + constraint: defs_constraint, }); state