mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 16:44:33 +00:00
Merge branch 'main' into auto-snake-case
This commit is contained in:
commit
8e5d875604
74 changed files with 368 additions and 3022 deletions
|
@ -329,10 +329,6 @@ fn deep_copy_var_help(
|
|||
}
|
||||
|
||||
let new_ambient_function_var = work!(ambient_function_var);
|
||||
debug_assert_ne!(
|
||||
ambient_function_var, new_ambient_function_var,
|
||||
"lambda set cloned but its ambient function wasn't?"
|
||||
);
|
||||
|
||||
subs.set_content_unchecked(
|
||||
lambda_set_var,
|
||||
|
|
|
@ -211,18 +211,7 @@ enum Work<'a> {
|
|||
constraint: &'a Constraint,
|
||||
},
|
||||
CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc<Variable>)>),
|
||||
/// The ret_con part of a let constraint that does NOT introduces rigid and/or flex variables
|
||||
LetConNoVariables {
|
||||
scope: &'a Scope,
|
||||
rank: Rank,
|
||||
let_con: &'a LetConstraint,
|
||||
|
||||
/// The variables used to store imported types in the Subs.
|
||||
/// The `Contents` are copied from the source module, but to
|
||||
/// mimic `type_to_var`, we must add these variables to `Pools`
|
||||
/// at the correct rank
|
||||
pool_variables: &'a [Variable],
|
||||
},
|
||||
CheckSuffixFx(LocalDefVarsVec<(Symbol, Loc<Variable>)>),
|
||||
/// The ret_con part of a let constraint that introduces rigid and/or flex variables
|
||||
///
|
||||
/// These introduced variables must be generalized, hence this variant
|
||||
|
@ -288,56 +277,6 @@ fn solve(
|
|||
|
||||
continue;
|
||||
}
|
||||
Work::LetConNoVariables {
|
||||
scope,
|
||||
rank,
|
||||
let_con,
|
||||
pool_variables,
|
||||
} => {
|
||||
// NOTE be extremely careful with shadowing here
|
||||
let offset = let_con.defs_and_ret_constraint.index();
|
||||
let ret_constraint = &env.constraints.constraints[offset + 1];
|
||||
|
||||
// Add a variable for each def to new_vars_by_env.
|
||||
let local_def_vars = LocalDefVarsVec::from_def_types(
|
||||
env,
|
||||
rank,
|
||||
problems,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
&mut can_types,
|
||||
aliases,
|
||||
let_con.def_types,
|
||||
);
|
||||
|
||||
env.pools.get_mut(rank).extend(pool_variables);
|
||||
|
||||
let mut new_scope = scope.clone();
|
||||
for (symbol, loc_var) in local_def_vars.iter() {
|
||||
check_ability_specialization(
|
||||
env,
|
||||
rank,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
problems,
|
||||
*symbol,
|
||||
*loc_var,
|
||||
);
|
||||
|
||||
new_scope.insert_symbol_var_if_vacant(*symbol, loc_var.value);
|
||||
}
|
||||
|
||||
stack.push(Work::Constraint {
|
||||
scope: env.arena.alloc(new_scope),
|
||||
rank,
|
||||
constraint: ret_constraint,
|
||||
});
|
||||
// Check for infinite types first
|
||||
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
|
||||
|
||||
continue;
|
||||
}
|
||||
Work::LetConIntroducesVariables {
|
||||
scope,
|
||||
rank,
|
||||
|
@ -419,7 +358,7 @@ fn solve(
|
|||
|
||||
// check that things went well
|
||||
dbg_do!(ROC_VERIFY_RIGID_LET_GENERALIZED, {
|
||||
let rigid_vars = &env.constraints.variables[let_con.rigid_vars.indices()];
|
||||
let rigid_vars = &env.constraints[let_con.rigid_vars];
|
||||
|
||||
// NOTE the `subs.redundant` check does not come from elm.
|
||||
// It's unclear whether this is a bug with our implementation
|
||||
|
@ -427,9 +366,9 @@ fn solve(
|
|||
// or that it just never came up in elm.
|
||||
let mut it = rigid_vars
|
||||
.iter()
|
||||
.filter(|&var| {
|
||||
!env.subs.redundant(*var)
|
||||
&& env.subs.get_rank(*var) != Rank::GENERALIZED
|
||||
.filter(|loc_var| {
|
||||
let var = loc_var.value;
|
||||
!env.subs.redundant(var) && env.subs.get_rank(var) != Rank::GENERALIZED
|
||||
})
|
||||
.peekable();
|
||||
|
||||
|
@ -456,14 +395,8 @@ fn solve(
|
|||
|
||||
new_scope.insert_symbol_var_if_vacant(*symbol, loc_var.value);
|
||||
|
||||
solve_suffix_fx(
|
||||
env,
|
||||
problems,
|
||||
host_exposed_symbols,
|
||||
FxSuffixKind::Let(*symbol),
|
||||
loc_var.value,
|
||||
&loc_var.region,
|
||||
);
|
||||
// At the time of introduction, promote explicitly-effectful symbols.
|
||||
promote_effectful_symbol(env, FxSuffixKind::Let(*symbol), loc_var.value);
|
||||
}
|
||||
|
||||
// Note that this vars_by_symbol is the one returned by the
|
||||
|
@ -473,20 +406,42 @@ fn solve(
|
|||
mark: final_mark,
|
||||
};
|
||||
|
||||
// Now solve the body, using the new vars_by_symbol which includes
|
||||
// the assignments' name-to-variable mappings.
|
||||
stack.push(Work::Constraint {
|
||||
scope: env.arena.alloc(new_scope),
|
||||
rank,
|
||||
constraint: ret_constraint,
|
||||
});
|
||||
// Check for infinite types first
|
||||
stack.push(Work::CheckForInfiniteTypes(local_def_vars));
|
||||
let next_work = [
|
||||
// Check for infinite types first
|
||||
Work::CheckForInfiniteTypes(local_def_vars.clone()),
|
||||
// Now solve the body, using the new vars_by_symbol which includes
|
||||
// the assignments' name-to-variable mappings.
|
||||
Work::Constraint {
|
||||
scope: env.arena.alloc(new_scope),
|
||||
rank,
|
||||
constraint: ret_constraint,
|
||||
},
|
||||
// Finally, check the suffix fx, after we have solved all types.
|
||||
Work::CheckSuffixFx(local_def_vars),
|
||||
];
|
||||
|
||||
for work in next_work.into_iter().rev() {
|
||||
stack.push(work);
|
||||
}
|
||||
|
||||
state = state_for_ret_con;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Work::CheckSuffixFx(local_def_vars) => {
|
||||
for (symbol, loc_var) in local_def_vars.iter() {
|
||||
solve_suffix_fx(
|
||||
env,
|
||||
problems,
|
||||
host_exposed_symbols,
|
||||
FxSuffixKind::Let(*symbol),
|
||||
loc_var.value,
|
||||
&loc_var.region,
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
state = match constraint {
|
||||
|
@ -1004,100 +959,64 @@ fn solve(
|
|||
|
||||
let offset = let_con.defs_and_ret_constraint.index();
|
||||
let defs_constraint = &env.constraints.constraints[offset];
|
||||
let ret_constraint = &env.constraints.constraints[offset + 1];
|
||||
|
||||
let flex_vars = &env.constraints.variables[let_con.flex_vars.indices()];
|
||||
let rigid_vars = &env.constraints.variables[let_con.rigid_vars.indices()];
|
||||
let rigid_vars = &env.constraints[let_con.rigid_vars];
|
||||
|
||||
let pool_variables = &env.constraints.variables[pool_slice.indices()];
|
||||
|
||||
if matches!(&ret_constraint, True) && let_con.rigid_vars.is_empty() {
|
||||
debug_assert!(pool_variables.is_empty());
|
||||
|
||||
env.introduce(rank, flex_vars);
|
||||
|
||||
// If the return expression is guaranteed to solve,
|
||||
// solve the assignments themselves and move on.
|
||||
stack.push(Work::Constraint {
|
||||
scope,
|
||||
rank,
|
||||
constraint: 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::LetConNoVariables {
|
||||
scope,
|
||||
rank,
|
||||
let_con,
|
||||
pool_variables,
|
||||
});
|
||||
stack.push(Work::Constraint {
|
||||
scope,
|
||||
rank,
|
||||
constraint: defs_constraint,
|
||||
});
|
||||
|
||||
state
|
||||
// If the let-binding is generalizable, work at the next rank (which will be
|
||||
// the rank at which introduced variables will become generalized, if they end up
|
||||
// staying there); otherwise, stay at the current level.
|
||||
let binding_rank = if let_con.generalizable.0 {
|
||||
rank.next()
|
||||
} else {
|
||||
// If the let-binding is generalizable, work at the next rank (which will be
|
||||
// the rank at which introduced variables will become generalized, if they end up
|
||||
// staying there); otherwise, stay at the current level.
|
||||
let binding_rank = if let_con.generalizable.0 {
|
||||
rank.next()
|
||||
} else {
|
||||
rank
|
||||
};
|
||||
rank
|
||||
};
|
||||
|
||||
// determine the next pool
|
||||
if binding_rank.into_usize() < env.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!(binding_rank.into_usize(), 1 + env.pools.len());
|
||||
env.pools.extend_to(binding_rank.into_usize());
|
||||
}
|
||||
|
||||
let pool: &mut Vec<Variable> = env.pools.get_mut(binding_rank);
|
||||
|
||||
// Introduce the variables of this binding, and extend the pool at our binding
|
||||
// rank.
|
||||
for &var in rigid_vars.iter().chain(flex_vars.iter()) {
|
||||
env.subs.set_rank(var, binding_rank);
|
||||
}
|
||||
pool.reserve(rigid_vars.len() + flex_vars.len());
|
||||
pool.extend(rigid_vars.iter());
|
||||
pool.extend(flex_vars.iter());
|
||||
|
||||
// Now, run our binding constraint, generalize, then solve the rest of the
|
||||
// program.
|
||||
//
|
||||
// Items are popped from the stack in reverse order. That means that we'll
|
||||
// first solve the defs_constraint, and then (eventually) the ret_constraint.
|
||||
//
|
||||
// NB: LetCon gets the current scope's env and rank, not the env/rank from after solving the defs_constraint.
|
||||
// That's because the defs constraints will be solved in next_rank if it is eligible for generalization.
|
||||
// The LetCon will then generalize variables that are at a higher rank than the rank of the current scope.
|
||||
stack.push(Work::LetConIntroducesVariables {
|
||||
scope,
|
||||
rank,
|
||||
let_con,
|
||||
pool_variables,
|
||||
});
|
||||
stack.push(Work::Constraint {
|
||||
scope,
|
||||
rank: binding_rank,
|
||||
constraint: defs_constraint,
|
||||
});
|
||||
|
||||
state
|
||||
// determine the next pool
|
||||
if binding_rank.into_usize() < env.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!(binding_rank.into_usize(), 1 + env.pools.len());
|
||||
env.pools.extend_to(binding_rank.into_usize());
|
||||
}
|
||||
|
||||
let pool: &mut Vec<Variable> = env.pools.get_mut(binding_rank);
|
||||
|
||||
// Introduce the variables of this binding, and extend the pool at our binding
|
||||
// rank.
|
||||
for &var in rigid_vars.iter().map(|v| &v.value).chain(flex_vars.iter()) {
|
||||
env.subs.set_rank(var, binding_rank);
|
||||
}
|
||||
pool.reserve(rigid_vars.len() + flex_vars.len());
|
||||
pool.extend(rigid_vars.iter().map(|v| &v.value));
|
||||
pool.extend(flex_vars.iter());
|
||||
|
||||
// Now, run our binding constraint, generalize, then solve the rest of the
|
||||
// program.
|
||||
//
|
||||
// Items are popped from the stack in reverse order. That means that we'll
|
||||
// first solve the defs_constraint, and then (eventually) the ret_constraint.
|
||||
//
|
||||
// NB: LetCon gets the current scope's env and rank, not the env/rank from after solving the defs_constraint.
|
||||
// That's because the defs constraints will be solved in next_rank if it is eligible for generalization.
|
||||
// The LetCon will then generalize variables that are at a higher rank than the rank of the current scope.
|
||||
stack.push(Work::LetConIntroducesVariables {
|
||||
scope,
|
||||
rank,
|
||||
let_con,
|
||||
pool_variables,
|
||||
});
|
||||
stack.push(Work::Constraint {
|
||||
scope,
|
||||
rank: binding_rank,
|
||||
constraint: defs_constraint,
|
||||
});
|
||||
|
||||
state
|
||||
}
|
||||
IsOpenType(type_index) => {
|
||||
let actual = either_type_index_to_var(
|
||||
|
@ -1773,6 +1692,20 @@ fn solve_suffix_fx(
|
|||
}
|
||||
}
|
||||
|
||||
fn promote_effectful_symbol(env: &mut InferenceEnv<'_>, kind: FxSuffixKind, variable: Variable) {
|
||||
if kind.suffix() != IdentSuffix::Bang {
|
||||
return;
|
||||
}
|
||||
if !matches!(
|
||||
env.subs.get_content_without_compacting(variable),
|
||||
Content::FlexVar(_)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
env.subs
|
||||
.set_content(variable, Content::Structure(FlatType::EffectfulFunc));
|
||||
}
|
||||
|
||||
fn chase_alias_content(subs: &Subs, mut var: Variable) -> (Variable, &Content) {
|
||||
loop {
|
||||
match subs.get_content_without_compacting(var) {
|
||||
|
@ -2147,7 +2080,7 @@ fn check_ability_specialization(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
enum LocalDefVarsVec<T> {
|
||||
Stack(arrayvec::ArrayVec<T, 32>),
|
||||
Heap(Vec<T>),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue