Solve and gen ability members that bind other able variables

This commit is contained in:
Ayaz Hafiz 2022-04-21 14:11:32 -04:00 committed by ayazhafiz
parent 5fe902b8d3
commit b5efd830e5
6 changed files with 110 additions and 28 deletions

View file

@ -578,8 +578,10 @@ fn resolve_abilities<'a>(
// What variables in the annotation are bound to the parent ability, and what variables
// are bound to some other ability?
let (variables_bound_to_ability, variables_bound_to_other_abilities): (Vec<_>, Vec<_>) =
member_annot
let (variables_bound_to_ability, _variables_bound_to_other_abilities): (
Vec<_>,
Vec<_>,
) = member_annot
.introduced_variables
.able
.iter()
@ -618,18 +620,6 @@ fn resolve_abilities<'a>(
bad_has_clauses = true;
}
if !variables_bound_to_other_abilities.is_empty() {
// Disallow variables bound to other abilities, for now.
for bad_variable in variables_bound_to_other_abilities.iter() {
env.problem(Problem::AbilityMemberBindsExternalAbility {
member: member_sym,
ability: loc_ability_name.value,
region: bad_variable.first_seen,
});
}
bad_has_clauses = true;
}
if bad_has_clauses {
// Pretend the member isn't a part of the ability
continue;

View file

@ -45,11 +45,12 @@ fn walk_def<V: Visitor>(visitor: &mut V, def: &Def) {
..
} = def;
visitor.visit_pattern(
&loc_pattern.value,
loc_pattern.region,
loc_pattern.value.opt_var(),
);
let opt_var = match loc_pattern.value {
Pattern::Identifier(..) | Pattern::AbilityMemberSpecialization { .. } => Some(*expr_var),
_ => loc_pattern.value.opt_var(),
};
visitor.visit_pattern(&loc_pattern.value, loc_pattern.region, opt_var);
visitor.visit_expr(&loc_expr.value, loc_expr.region, *expr_var);
if let Some(annot) = &annotation {
visitor.visit_annotation(annot);
@ -70,6 +71,9 @@ fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
} => {
walk_when(visitor, *cond_var, *expr_var, loc_cond, branches);
}
Expr::Call(f, loc_args, _) => {
walk_call(visitor, f, loc_args);
}
e => todo!("{:?}", e),
}
}
@ -120,6 +124,18 @@ fn walk_when_branch<V: Visitor>(visitor: &mut V, branch: &WhenBranch, expr_var:
}
}
fn walk_call<V: Visitor>(
visitor: &mut V,
f: &(Variable, Loc<Expr>, Variable, Variable),
loc_args: &[(Variable, Loc<Expr>)],
) {
let (fn_var, loc_fn_expr, _lambda_set, _ret) = f;
visitor.visit_expr(&loc_fn_expr.value, loc_fn_expr.region, *fn_var);
loc_args
.iter()
.for_each(|(v, e)| visitor.visit_expr(&e.value, e.region, *v));
}
fn walk_pattern<V: Visitor>(_visitor: &mut V, _pat: &Pattern) {
todo!()
}

View file

@ -4922,6 +4922,14 @@ fn get_specialization<'a>(
Some(member) => {
let snapshot = env.subs.snapshot();
instantiate_rigids(env.subs, member.signature_var);
let this_f = env.subs.get_content_without_compacting(symbol_var);
let member_f = env
.subs
.get_content_without_compacting(member.signature_var);
use roc_types::subs::SubsFmtContent;
let this_f = SubsFmtContent(&this_f, env.subs);
let member_f = SubsFmtContent(&member_f, env.subs);
dbg!(symbol, this_f, member_f);
let (_, must_implement_ability) = unify(
env.subs,
symbol_var,

View file

@ -173,9 +173,12 @@ pub fn type_implementing_member(
let ability_implementations_for_specialization =
specialization_must_implement_constraints
.clone()
.get_unique();
.get_unique()
.into_iter()
.filter(|mia| mia.ability == ability)
.count();
ability_implementations_for_specialization.len()
ability_implementations_for_specialization
},
1,
"Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}",

View file

@ -25,7 +25,8 @@ mod solve_expr {
// HELPERS
lazy_static! {
static ref RE_TYPE_QUERY: Regex = Regex::new(r#"^\s*#\s*(?P<where>\^+)\s*$"#).unwrap();
static ref RE_TYPE_QUERY: Regex =
Regex::new(r#"(?P<where>\^+)(?:\{-(?P<sub>\d+)\})?"#).unwrap();
}
#[derive(Debug, Clone, Copy)]
@ -35,9 +36,14 @@ mod solve_expr {
let line_info = LineInfo::new(src);
let mut queries = vec![];
for (i, line) in src.lines().enumerate() {
if let Some(capture) = RE_TYPE_QUERY.captures(line) {
for capture in RE_TYPE_QUERY.captures_iter(line) {
let wher = capture.name("where").unwrap();
let subtract_col = capture
.name("sub")
.and_then(|m| str::parse(m.as_str()).ok())
.unwrap_or(0);
let (start, end) = (wher.start() as u32, wher.end() as u32);
let (start, end) = (start - subtract_col, end - subtract_col);
let last_line = i as u32 - 1;
let start_lc = LineColumn {
line: last_line,
@ -273,7 +279,8 @@ mod solve_expr {
let start = region.start().offset;
let end = region.end().offset;
let text = &src[start as usize..end as usize];
let var = find_type_at(region, &decls).expect(&format!("No type for {}!", &text));
let var = find_type_at(region, &decls)
.expect(&format!("No type for {} ({:?})!", &text, region));
name_all_type_vars(var, subs);
let content = subs.get_content_without_compacting(var);
@ -6232,6 +6239,38 @@ mod solve_expr {
"#
),
"F b -> b",
);
}
#[test]
fn ability_member_takes_different_able_variable() {
infer_queries(
indoc!(
r#"
app "test" provides [ result ] to "./platform"
Hash has hash : a -> U64 | a has Hash
IntoHash has intoHash : a, b -> b | a has IntoHash, b has Hash
Id := U64
hash = \$Id n -> n
#^^^^{-1}
User := Id
intoHash = \$User id, _ -> id
#^^^^^^^^{-1}
result = hash (intoHash ($User ($Id 123)) ($Id 1))
# ^^^^ ^^^^^^^^
"#
),
&[
"hash : Id -> U64",
"intoHash : User, Id -> Id",
"hash : Id -> U64",
"intoHash : User, Id -> Id",
],
)
}

View file

@ -216,3 +216,29 @@ fn ability_used_as_type_still_compiles() {
u64
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn ability_member_takes_different_able_variable() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [ result ] to "./platform"
Hash has hash : a -> U64 | a has Hash
IntoHash has intoHash : a, b -> b | a has IntoHash, b has Hash
Id := U64
hash = \$Id n -> n
User := Id
intoHash = \$User id, _ -> id
result = hash (intoHash ($User ($Id 123)) ($Id 1))
"#
),
123,
u64
)
}