mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
Merge pull request #4558 from roc-lang/specialization-sets-for-impl-opaques
Support custom abilities for opaques with immaterial lambda sets
This commit is contained in:
commit
8c0ff4c839
5 changed files with 108 additions and 4 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4144,6 +4144,7 @@ dependencies = [
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_solve_problem",
|
"roc_solve_problem",
|
||||||
|
"roc_tracing",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -8320,6 +8320,50 @@ mod solve_expr {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn impl_ability_for_opaque_with_lambda_sets() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [isEqQ] to "./platform"
|
||||||
|
|
||||||
|
Q := [ F (Str -> Str), G ] has [Eq { isEq: isEqQ }]
|
||||||
|
|
||||||
|
isEqQ = \@Q q1, @Q q2 -> when T q1 q2 is
|
||||||
|
#^^^^^{-1}
|
||||||
|
T (F _) (F _) -> Bool.true
|
||||||
|
T G G -> Bool.true
|
||||||
|
_ -> Bool.false
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@"isEqQ : Q, Q -[[isEqQ(0)]]-> Bool"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn impl_ability_for_opaque_with_lambda_sets_material() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Q := ({} -> Str) has [Eq {isEq: isEqQ}]
|
||||||
|
|
||||||
|
isEqQ = \@Q f1, @Q f2 -> (f1 {} == f2 {})
|
||||||
|
#^^^^^{-1}
|
||||||
|
|
||||||
|
main = isEqQ (@Q \{} -> "a") (@Q \{} -> "a")
|
||||||
|
# ^^^^^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
isEqQ : ({} -[[]]-> Str), ({} -[[]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
||||||
|
isEqQ : ({} -[[6(6), 7(7)]]-> Str), ({} -[[6(6), 7(7)]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
||||||
|
"###
|
||||||
|
print_only_under_alias: true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_concrete_type_with_inference_var() {
|
fn infer_concrete_type_with_inference_var() {
|
||||||
infer_queries!(indoc!(
|
infer_queries!(indoc!(
|
||||||
|
|
|
@ -1734,6 +1734,45 @@ mod eq {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn custom_eq_impl_for_fn_opaque() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Q := ({} -> Str) has [Eq {isEq: isEqQ}]
|
||||||
|
|
||||||
|
isEqQ = \@Q _, @Q _ -> Bool.true
|
||||||
|
|
||||||
|
main = isEqQ (@Q \{} -> "a") (@Q \{} -> "a")
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
bool
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "needs https://github.com/roc-lang/roc/issues/4557 first"]
|
||||||
|
fn custom_eq_impl_for_fn_opaque_material() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Q := ({} -> Str) has [Eq {isEq: isEqQ}]
|
||||||
|
|
||||||
|
isEqQ = \@Q f1, @Q f2 -> (f1 {} == f2 {})
|
||||||
|
|
||||||
|
main = isEqQ (@Q \{} -> "a") (@Q \{} -> "a")
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
bool
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn derive_structural_eq() {
|
fn derive_structural_eq() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
|
|
@ -29,3 +29,6 @@ path = "../can"
|
||||||
|
|
||||||
[dependencies.roc_solve_problem]
|
[dependencies.roc_solve_problem]
|
||||||
path = "../solve_problem"
|
path = "../solve_problem"
|
||||||
|
|
||||||
|
[dependencies.roc_tracing]
|
||||||
|
path = "../../tracing"
|
||||||
|
|
|
@ -1259,12 +1259,29 @@ fn extract_specialization_lambda_set<M: MetaCollector>(
|
||||||
debug_assert!(member_rec_var.is_none());
|
debug_assert!(member_rec_var.is_none());
|
||||||
|
|
||||||
let member_uls = env.subs.get_subs_slice(member_uls_slice);
|
let member_uls = env.subs.get_subs_slice(member_uls_slice);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
member_uls.len(),
|
member_uls.len() <= 1,
|
||||||
1,
|
"member signature lambda sets should contain at most one unspecialized lambda set"
|
||||||
"member signature lambda sets should contain only one unspecialized lambda set"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if member_uls.is_empty() {
|
||||||
|
// This can happen if the specialized type has a lambda set that is determined to be
|
||||||
|
// immaterial in the implementation of the specialization, because the specialization
|
||||||
|
// lambda set does not line up with one required by the ability member prototype.
|
||||||
|
// As an example, consider
|
||||||
|
//
|
||||||
|
// Q := [ F (Str -> Str) ] has [Eq {isEq}]
|
||||||
|
//
|
||||||
|
// isEq = \@Q _, @Q _ -> Bool.false
|
||||||
|
//
|
||||||
|
// here the lambda set of `F`'s payload is part of the specialization signature, but it is
|
||||||
|
// irrelevant to the specialization. As such, I believe it is safe to drop the
|
||||||
|
// empty specialization lambda set.
|
||||||
|
roc_tracing::info!(ambient_function=?env.subs.get_root_key_without_compacting(specialization_lset.ambient_function), "ambient function in a specialization has a zero-lambda set");
|
||||||
|
|
||||||
|
return merge(env, ctx, Content::LambdaSet(specialization_lset));
|
||||||
|
}
|
||||||
|
|
||||||
let Uls(_, member, region) = member_uls[0];
|
let Uls(_, member, region) = member_uls[0];
|
||||||
|
|
||||||
let mut outcome: Outcome<M> = merge(env, ctx, Content::LambdaSet(specialization_lset));
|
let mut outcome: Outcome<M> = merge(env, ctx, Content::LambdaSet(specialization_lset));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue