Unify variables directly when possible

instead of going through a solved type
This commit is contained in:
Folkert 2021-08-22 16:29:57 +02:00
parent 1348ec433b
commit ecba687243
3 changed files with 141 additions and 49 deletions

View file

@ -694,10 +694,8 @@ impl<'a> Procs<'a> {
layout: ProcLayout<'a>,
layout_cache: &mut LayoutCache<'a>,
) {
let tuple = (name, layout);
// If we've already specialized this one, no further work is needed.
if self.specialized.contains_key(&tuple) {
if self.specialized.contains_key(&(name, layout)) {
return;
}
@ -707,15 +705,12 @@ impl<'a> Procs<'a> {
return;
}
// We're done with that tuple, so move layout back out to avoid cloning it.
let (name, layout) = tuple;
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
// This should only be called when pending_specializations is Some.
// Otherwise, it's being called in the wrong pass!
match &mut self.pending_specializations {
Some(pending_specializations) => {
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
// register the pending specialization, so this gets code genned later
if self.module_thunks.contains(&name) {
debug_assert!(layout.arguments.is_empty());
@ -736,7 +731,26 @@ impl<'a> Procs<'a> {
// (We had a bug around this before this system existed!)
self.specialized.insert((symbol, layout), InProgress);
match specialize(env, self, symbol, layout_cache, pending, partial_proc) {
// See https://github.com/rtfeldman/roc/issues/1600
//
// The annotation variable is the generic/lifted/top-level annotation.
// It is connected to the variables of the function's body
//
// fn_var is the variable representing the type that we actually need for the
// function right here.
//
// For some reason, it matters that we unify with the original variable. Extracting
// that variable into a SolvedType and then introducing it again severs some
// connection that turns out to be important
match specialize_variable(
env,
self,
symbol,
layout_cache,
fn_var,
Default::default(),
partial_proc,
) {
Ok((proc, _ignore_layout)) => {
// the `layout` is a function pointer, while `_ignore_layout` can be a
// closure. We only specialize functions, storing this value with a closure
@ -2448,13 +2462,57 @@ fn specialize_solved_type<'a>(
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
partial_proc: PartialProc<'a>,
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
specialize_variable_help(
env,
procs,
proc_name,
layout_cache,
|env| introduce_solved_type_to_subs(env, &solved_type),
host_exposed_aliases,
partial_proc,
)
}
fn specialize_variable<'a>(
env: &mut Env<'a, '_>,
procs: &mut Procs<'a>,
proc_name: Symbol,
layout_cache: &mut LayoutCache<'a>,
fn_var: Variable,
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
partial_proc: PartialProc<'a>,
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
specialize_variable_help(
env,
procs,
proc_name,
layout_cache,
|_| fn_var,
host_exposed_aliases,
partial_proc,
)
}
fn specialize_variable_help<'a, F>(
env: &mut Env<'a, '_>,
procs: &mut Procs<'a>,
proc_name: Symbol,
layout_cache: &mut LayoutCache<'a>,
fn_var_thunk: F,
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
partial_proc: PartialProc<'a>,
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>>
where
F: FnOnce(&mut Env<'a, '_>) -> Variable,
{
// add the specializations that other modules require of us
use roc_solve::solve::instantiate_rigids;
let snapshot = env.subs.snapshot();
let cache_snapshot = layout_cache.snapshot();
let fn_var = introduce_solved_type_to_subs(env, &solved_type);
// important: evaluate after the snapshot has been created!
let fn_var = fn_var_thunk(env);
// for debugging only
let raw = layout_cache
@ -2723,32 +2781,36 @@ pub fn with_hole<'a>(
hole,
),
Num(var, num) => match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
IntOrFloat::SignedIntType(precision) => Stmt::Let(
assigned,
Expr::Literal(Literal::Int(num.into())),
Layout::Builtin(int_precision_to_builtin(precision)),
hole,
),
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
assigned,
Expr::Literal(Literal::Int(num.into())),
Layout::Builtin(int_precision_to_builtin(precision)),
hole,
),
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
assigned,
Expr::Literal(Literal::Float(num as f64)),
Layout::Builtin(float_precision_to_builtin(precision)),
hole,
),
IntOrFloat::DecimalFloatType => Stmt::Let(
assigned,
Expr::Literal(Literal::Float(num as f64)),
Layout::Builtin(Builtin::Decimal),
hole,
),
},
Num(var, num) => {
// first figure out what kind of number this is
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
IntOrFloat::SignedIntType(precision) => Stmt::Let(
assigned,
Expr::Literal(Literal::Int(num.into())),
Layout::Builtin(int_precision_to_builtin(precision)),
hole,
),
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
assigned,
Expr::Literal(Literal::Int(num.into())),
Layout::Builtin(int_precision_to_builtin(precision)),
hole,
),
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
assigned,
Expr::Literal(Literal::Float(num as f64)),
Layout::Builtin(float_precision_to_builtin(precision)),
hole,
),
IntOrFloat::DecimalFloatType => Stmt::Let(
assigned,
Expr::Literal(Literal::Float(num as f64)),
Layout::Builtin(Builtin::Decimal),
hole,
),
}
}
LetNonRec(def, cont, _) => {
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
if let Closure {
@ -7865,6 +7927,12 @@ fn union_lambda_set_to_switch<'a>(
assigned: Symbol,
hole: &'a Stmt<'a>,
) -> Stmt<'a> {
// NOTE this can happen if there is a type error somewhere. Since the lambda set is empty,
// there is really nothing we can do here, so we just proceed with the hole itself and
// hope that the type error is communicated in a clear way elsewhere.
if lambda_set.is_empty() {
return hole.clone();
}
debug_assert!(!lambda_set.is_empty());
let join_point_id = JoinPointId(env.unique_symbol());