When storing variables, merge them directly with the target rather than unifying

When we unify two variables that end up merged, the rank of the
resulting content is the lower of the two variables being merged. But
during storage, we really do mean, take the target descriptor of the
type we're merging against, and don't try to lower to a
possibly-generalized rank! This fixes a couple bugs I didn't even
realize were present!
This commit is contained in:
Ayaz Hafiz 2022-07-29 14:53:14 -04:00
parent 5f115685e4
commit 4657a957f7
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
4 changed files with 106 additions and 59 deletions

View file

@ -422,12 +422,13 @@ pub fn constrain_expr(
constraints.lookup(*symbol, expected, region)
}
&AbilityMember(symbol, specialization_id, specialization_var) => {
// make lookup constraint to lookup this symbol's type in the environment
let store_expected = constraints.equal_types_var(
// Save the expectation in the `specialization_var` so we know what to specialize, then
// lookup the member in the environment.
let store_expected = constraints.store(
expected.get_type_ref().clone(),
specialization_var,
expected,
Category::Storage(file!(), line!()),
region,
file!(),
line!(),
);
let lookup_constr = constraints.lookup(
symbol,
@ -435,7 +436,7 @@ pub fn constrain_expr(
region,
);
// Make sure we attempt to resolve the specialization, if we need to.
// Make sure we attempt to resolve the specialization, if we can.
if let Some(specialization_id) = specialization_id {
env.resolutions_to_make.push(OpportunisticResolve {
specialization_variable: specialization_var,

View file

@ -928,51 +928,10 @@ fn solve(
aliases,
*source_index,
);
let target = *target;
match unify(&mut UEnv::new(subs), actual, target, Mode::EQ) {
Success {
vars,
// ERROR NOT REPORTED
must_implement_ability: _,
lambda_sets_to_specialize,
extra_metadata: _,
} => {
introduce(subs, rank, pools, &vars);
let CompactionResult {
obligations,
awaiting_specialization,
} = compact_lambda_sets_of_vars(
subs,
derived_env,
arena,
pools,
lambda_sets_to_specialize,
&SolvePhase { abilities_store },
);
// implement obligations not reported
_ = obligations;
// but awaited specializations must be recorded
awaiting_specializations.union(awaiting_specialization);
state
}
Failure(vars, _actual_type, _expected_type, _bad_impls) => {
introduce(subs, rank, pools, &vars);
// ERROR NOT REPORTED
state
}
BadType(vars, _) => {
introduce(subs, rank, pools, &vars);
// ERROR NOT REPORTED
state
}
}
let actual_desc = subs.get(actual);
subs.union(*target, actual, actual_desc);
state
}
Lookup(symbol, expectation_index, region) => {
match env.get_var_by_symbol(symbol) {

View file

@ -6715,7 +6715,7 @@ mod solve_expr {
),
@r#"
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
Id#id(3) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
Id#id(3) : a -[[] + a:id(3):1]-> ({} -[[] + a:id(3):2]-> a) | a has Id
alias : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
"#
print_only_under_alias: true
@ -7347,6 +7347,7 @@ mod solve_expr {
#^{-1}
f = \flag, a, b ->
# ^ ^
it =
# ^^
when flag is
@ -7367,17 +7368,90 @@ mod solve_expr {
jC : C -[[jC(8)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
jD : D -[[jD(9)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
E#k(10) : E -[[k(10)]]-> {}
it : k -[[] + j:j(2):2 + j:j(2):2]-> {} | j has J, k has K
J#j(2) : j -[[] + j:j(2):1]-> (k -[[] + j:j(2):2 + j:j(2):2]-> {}) | j has J, k has K
J#j(2) : j -[[] + j:j(2):1]-> (k -[[] + j:j(2):2 + j:j(2):2]-> {}) | j has J, k has K
it : k -[[] + j:j(2):2 + j:j(2):2]-> {} | j has J, k has K
f : [A, B], C, D -[[f(11)]]-> (E -[[] + j:j(2):2 + j:j(2):2]-> {}) | j has J
f A (@C {}) (@D {}) : E -[[] + j:j(2):2 + j:j(2):2]-> {} | j has J
a : j | j has J
b : j | j has J
it : k -[[] + j:j(2):2 + a:j(2):2]-> {} | a has J, j has J, k has K
J#j(2) : j -[[] + j:j(2):1]-> (k -[[] + j:j(2):2 + a:j(2):2]-> {}) | a has J, j has J, k has K
J#j(2) : j -[[] + j:j(2):1]-> (k -[[] + a:j(2):2 + j:j(2):2]-> {}) | a has J, j has J, k has K
it : k -[[] + j:j(2):2 + a:j(2):2]-> {} | a has J, j has J, k has K
f : [A, B], C, D -[[f(11)]]-> (E -[[k(10)]]-> {})
f A (@C {}) (@D {}) : E -[[k(10)]]-> {}
main : {}
"###
);
}
#[test]
fn polymorphic_lambda_set_specialization_varying_over_multiple_variables_two_results() {
infer_queries!(
indoc!(
r#"
app "test" provides [main] to "./platform"
J has j : j -> (k -> {}) | j has J, k has K
K has k : k -> {} | k has K
C := {} has [J {j: jC}]
jC = \@C _ -> k
#^^{-1}
D := {} has [J {j: jD}]
jD = \@D _ -> k
#^^{-1}
E := {} has [K {k: kE}]
kE = \@E _ -> {}
#^^{-1}
F := {} has [K {k: kF}]
kF = \@F _ -> {}
#^^{-1}
f = \flag, a, b ->
# ^ ^
it =
# ^^
when flag is
A -> j a
# ^
B -> j b
# ^
it
# ^^
main =
#^^^^{-1}
it =
# ^^
(f A (@C {}) (@D {}))
# ^
if True
then it (@E {})
# ^^
else it (@F {})
# ^^
"#
),
@r###"
jC : C -[[jC(9)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
jD : D -[[jD(10)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
kE : E -[[kE(11)]]-> {}
kF : F -[[kF(12)]]-> {}
a : j | j has J
b : j | j has J
it : k -[[] + j:j(2):2 + a:j(2):2]-> {} | a has J, j has J, k has K
J#j(2) : j -[[] + j:j(2):1]-> (k -[[] + j:j(2):2 + a:j(2):2]-> {}) | a has J, j has J, k has K
J#j(2) : j -[[] + j:j(2):1]-> (k -[[] + a:j(2):2 + j:j(2):2]-> {}) | a has J, j has J, k has K
it : k -[[] + j:j(2):2 + a:j(2):2]-> {} | a has J, j has J, k has K
main : {}
it : k -[[] + k:k(4):1]-> {} | k has K
f : [A, B], C, D -[[f(13)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
it : E -[[kE(11)]]-> {}
it : F -[[kF(12)]]-> {}
"###
);
}
#[test]
fn polymorphic_lambda_set_specialization_branching_over_single_variable() {
infer_queries!(

View file

@ -1226,7 +1226,7 @@ mod test_reporting {
// variables they can put themselves in, and to run the constraint algorithm
// against that extra variable, rather than possibly having to translate a `Type`
// again.
@r#"
@r###"
CIRCULAR TYPE /code/proj/Main.roc
I'm inferring a weird self-referential type for `f`:
@ -1265,7 +1265,20 @@ mod test_reporting {
infinitely.
List -> List a
"#
CIRCULAR TYPE /code/proj/Main.roc
I'm inferring a weird self-referential type for `main`:
3 main =
^^^^
Here is my best effort at writing down the type. You will see for
parts of the type that repeat something already printed out
infinitely.
List -> List a
"###
);
test_report!(