mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Solve and gen ability members that bind other able variables
This commit is contained in:
parent
5fe902b8d3
commit
b5efd830e5
6 changed files with 110 additions and 28 deletions
|
@ -578,8 +578,10 @@ fn resolve_abilities<'a>(
|
||||||
|
|
||||||
// What variables in the annotation are bound to the parent ability, and what variables
|
// What variables in the annotation are bound to the parent ability, and what variables
|
||||||
// are bound to some other ability?
|
// are bound to some other ability?
|
||||||
let (variables_bound_to_ability, variables_bound_to_other_abilities): (Vec<_>, Vec<_>) =
|
let (variables_bound_to_ability, _variables_bound_to_other_abilities): (
|
||||||
member_annot
|
Vec<_>,
|
||||||
|
Vec<_>,
|
||||||
|
) = member_annot
|
||||||
.introduced_variables
|
.introduced_variables
|
||||||
.able
|
.able
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -618,18 +620,6 @@ fn resolve_abilities<'a>(
|
||||||
bad_has_clauses = true;
|
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 {
|
if bad_has_clauses {
|
||||||
// Pretend the member isn't a part of the ability
|
// Pretend the member isn't a part of the ability
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -45,11 +45,12 @@ fn walk_def<V: Visitor>(visitor: &mut V, def: &Def) {
|
||||||
..
|
..
|
||||||
} = def;
|
} = def;
|
||||||
|
|
||||||
visitor.visit_pattern(
|
let opt_var = match loc_pattern.value {
|
||||||
&loc_pattern.value,
|
Pattern::Identifier(..) | Pattern::AbilityMemberSpecialization { .. } => Some(*expr_var),
|
||||||
loc_pattern.region,
|
_ => loc_pattern.value.opt_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);
|
visitor.visit_expr(&loc_expr.value, loc_expr.region, *expr_var);
|
||||||
if let Some(annot) = &annotation {
|
if let Some(annot) = &annotation {
|
||||||
visitor.visit_annotation(annot);
|
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);
|
walk_when(visitor, *cond_var, *expr_var, loc_cond, branches);
|
||||||
}
|
}
|
||||||
|
Expr::Call(f, loc_args, _) => {
|
||||||
|
walk_call(visitor, f, loc_args);
|
||||||
|
}
|
||||||
e => todo!("{:?}", e),
|
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) {
|
fn walk_pattern<V: Visitor>(_visitor: &mut V, _pat: &Pattern) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4922,6 +4922,14 @@ fn get_specialization<'a>(
|
||||||
Some(member) => {
|
Some(member) => {
|
||||||
let snapshot = env.subs.snapshot();
|
let snapshot = env.subs.snapshot();
|
||||||
instantiate_rigids(env.subs, member.signature_var);
|
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(
|
let (_, must_implement_ability) = unify(
|
||||||
env.subs,
|
env.subs,
|
||||||
symbol_var,
|
symbol_var,
|
||||||
|
|
|
@ -173,9 +173,12 @@ pub fn type_implementing_member(
|
||||||
let ability_implementations_for_specialization =
|
let ability_implementations_for_specialization =
|
||||||
specialization_must_implement_constraints
|
specialization_must_implement_constraints
|
||||||
.clone()
|
.clone()
|
||||||
.get_unique();
|
.get_unique()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|mia| mia.ability == ability)
|
||||||
|
.count();
|
||||||
|
|
||||||
ability_implementations_for_specialization.len()
|
ability_implementations_for_specialization
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
"Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}",
|
"Multiple variables bound to an ability - this is ambiguous and should have been caught in canonicalization: {:?}",
|
||||||
|
|
|
@ -25,7 +25,8 @@ mod solve_expr {
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
|
||||||
lazy_static! {
|
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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -35,9 +36,14 @@ mod solve_expr {
|
||||||
let line_info = LineInfo::new(src);
|
let line_info = LineInfo::new(src);
|
||||||
let mut queries = vec![];
|
let mut queries = vec![];
|
||||||
for (i, line) in src.lines().enumerate() {
|
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 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) = (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 last_line = i as u32 - 1;
|
||||||
let start_lc = LineColumn {
|
let start_lc = LineColumn {
|
||||||
line: last_line,
|
line: last_line,
|
||||||
|
@ -273,7 +279,8 @@ mod solve_expr {
|
||||||
let start = region.start().offset;
|
let start = region.start().offset;
|
||||||
let end = region.end().offset;
|
let end = region.end().offset;
|
||||||
let text = &src[start as usize..end as usize];
|
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);
|
name_all_type_vars(var, subs);
|
||||||
let content = subs.get_content_without_compacting(var);
|
let content = subs.get_content_without_compacting(var);
|
||||||
|
@ -6232,6 +6239,38 @@ mod solve_expr {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"F b -> b",
|
"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",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -216,3 +216,29 @@ fn ability_used_as_type_still_compiles() {
|
||||||
u64
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue