mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Unify variables directly when possible
instead of going through a solved type
This commit is contained in:
parent
1348ec433b
commit
ecba687243
3 changed files with 141 additions and 49 deletions
|
@ -694,10 +694,8 @@ impl<'a> Procs<'a> {
|
||||||
layout: ProcLayout<'a>,
|
layout: ProcLayout<'a>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
) {
|
) {
|
||||||
let tuple = (name, layout);
|
|
||||||
|
|
||||||
// If we've already specialized this one, no further work is needed.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,15 +705,12 @@ impl<'a> Procs<'a> {
|
||||||
return;
|
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.
|
// This should only be called when pending_specializations is Some.
|
||||||
// Otherwise, it's being called in the wrong pass!
|
// Otherwise, it's being called in the wrong pass!
|
||||||
match &mut self.pending_specializations {
|
match &mut self.pending_specializations {
|
||||||
Some(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
|
// register the pending specialization, so this gets code genned later
|
||||||
if self.module_thunks.contains(&name) {
|
if self.module_thunks.contains(&name) {
|
||||||
debug_assert!(layout.arguments.is_empty());
|
debug_assert!(layout.arguments.is_empty());
|
||||||
|
@ -736,7 +731,26 @@ impl<'a> Procs<'a> {
|
||||||
// (We had a bug around this before this system existed!)
|
// (We had a bug around this before this system existed!)
|
||||||
self.specialized.insert((symbol, layout), InProgress);
|
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)) => {
|
Ok((proc, _ignore_layout)) => {
|
||||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||||
// closure. We only specialize functions, storing this value with a closure
|
// 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>,
|
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc: PartialProc<'a>,
|
||||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'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
|
// add the specializations that other modules require of us
|
||||||
use roc_solve::solve::instantiate_rigids;
|
use roc_solve::solve::instantiate_rigids;
|
||||||
|
|
||||||
let snapshot = env.subs.snapshot();
|
let snapshot = env.subs.snapshot();
|
||||||
let cache_snapshot = layout_cache.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
|
// for debugging only
|
||||||
let raw = layout_cache
|
let raw = layout_cache
|
||||||
|
@ -2723,32 +2781,36 @@ pub fn with_hole<'a>(
|
||||||
hole,
|
hole,
|
||||||
),
|
),
|
||||||
|
|
||||||
Num(var, num) => match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
|
Num(var, num) => {
|
||||||
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
// first figure out what kind of number this is
|
||||||
assigned,
|
|
||||||
Expr::Literal(Literal::Int(num.into())),
|
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
|
||||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
||||||
hole,
|
assigned,
|
||||||
),
|
Expr::Literal(Literal::Int(num.into())),
|
||||||
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
|
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||||
assigned,
|
hole,
|
||||||
Expr::Literal(Literal::Int(num.into())),
|
),
|
||||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
|
||||||
hole,
|
assigned,
|
||||||
),
|
Expr::Literal(Literal::Int(num.into())),
|
||||||
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
|
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||||
assigned,
|
hole,
|
||||||
Expr::Literal(Literal::Float(num as f64)),
|
),
|
||||||
Layout::Builtin(float_precision_to_builtin(precision)),
|
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
|
||||||
hole,
|
assigned,
|
||||||
),
|
Expr::Literal(Literal::Float(num as f64)),
|
||||||
IntOrFloat::DecimalFloatType => Stmt::Let(
|
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||||
assigned,
|
hole,
|
||||||
Expr::Literal(Literal::Float(num as f64)),
|
),
|
||||||
Layout::Builtin(Builtin::Decimal),
|
IntOrFloat::DecimalFloatType => Stmt::Let(
|
||||||
hole,
|
assigned,
|
||||||
),
|
Expr::Literal(Literal::Float(num as f64)),
|
||||||
},
|
Layout::Builtin(Builtin::Decimal),
|
||||||
|
hole,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
LetNonRec(def, cont, _) => {
|
LetNonRec(def, cont, _) => {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
if let Closure {
|
if let Closure {
|
||||||
|
@ -7865,6 +7927,12 @@ fn union_lambda_set_to_switch<'a>(
|
||||||
assigned: Symbol,
|
assigned: Symbol,
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'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());
|
debug_assert!(!lambda_set.is_empty());
|
||||||
|
|
||||||
let join_point_id = JoinPointId(env.unique_symbol());
|
let join_point_id = JoinPointId(env.unique_symbol());
|
||||||
|
|
|
@ -554,10 +554,10 @@ impl<'a> LambdaSet<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(()) | Err((_, Content::FlexVar(_))) => {
|
Ok(()) | Err((_, Content::FlexVar(_))) => {
|
||||||
// TODO hack for builting functions.
|
// this can happen when there is a type error somewhere
|
||||||
Ok(LambdaSet {
|
Ok(LambdaSet {
|
||||||
set: &[],
|
set: &[],
|
||||||
representation: arena.alloc(Layout::Struct(&[])),
|
representation: arena.alloc(Layout::Union(UnionLayout::NonRecursive(&[]))),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => panic!("called LambdaSet.from_var on invalid input"),
|
_ => panic!("called LambdaSet.from_var on invalid input"),
|
||||||
|
|
|
@ -2683,26 +2683,50 @@ fn list_walk_until() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
fn int_literal_not_specialized_with_annotation() {
|
||||||
fn int_literal_not_specialized() {
|
|
||||||
// see https://github.com/rtfeldman/roc/issues/1600
|
// see https://github.com/rtfeldman/roc/issues/1600
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
|
||||||
satisfy : (U8 -> Bool) -> Str
|
|
||||||
satisfy = \_ -> "foo"
|
|
||||||
|
|
||||||
|
|
||||||
main : I64
|
|
||||||
main =
|
main =
|
||||||
p1 = (\u -> u == 97)
|
satisfy : (U8 -> Str) -> Str
|
||||||
|
satisfy = \_ -> "foo"
|
||||||
|
|
||||||
satisfyA = satisfy p1
|
myEq : a, a -> Str
|
||||||
|
myEq = \_, _ -> "bar"
|
||||||
|
|
||||||
when satisfyA is
|
p1 : Num * -> Str
|
||||||
|
p1 = (\u -> myEq u 64)
|
||||||
|
|
||||||
|
when satisfy p1 is
|
||||||
|
_ -> 32
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
32,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_literal_not_specialized_no_annotation() {
|
||||||
|
// see https://github.com/rtfeldman/roc/issues/1600
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
satisfy : (U8 -> Str) -> Str
|
||||||
|
satisfy = \_ -> "foo"
|
||||||
|
|
||||||
|
myEq : a, a -> Str
|
||||||
|
myEq = \_, _ -> "bar"
|
||||||
|
|
||||||
|
p1 = (\u -> myEq u 64)
|
||||||
|
|
||||||
|
when satisfy p1 is
|
||||||
_ -> 32
|
_ -> 32
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue