mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Typecheck annotations with able variables outside ability members
This commit is contained in:
parent
f8156ffd53
commit
a07323fb40
4 changed files with 88 additions and 40 deletions
|
@ -187,37 +187,8 @@ fn malformed(env: &mut Env, region: Region, name: &str) {
|
|||
env.problem(roc_problem::can::Problem::RuntimeError(problem));
|
||||
}
|
||||
|
||||
/// Canonicalizes a top-level type annotation.
|
||||
pub fn canonicalize_annotation(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
annotation: &roc_parse::ast::TypeAnnotation,
|
||||
region: Region,
|
||||
var_store: &mut VarStore,
|
||||
) -> Annotation {
|
||||
let mut introduced_variables = IntroducedVariables::default();
|
||||
let mut references = VecSet::default();
|
||||
let mut aliases = SendMap::default();
|
||||
|
||||
let typ = can_annotation_help(
|
||||
env,
|
||||
annotation,
|
||||
region,
|
||||
scope,
|
||||
var_store,
|
||||
&mut introduced_variables,
|
||||
&mut aliases,
|
||||
&mut references,
|
||||
);
|
||||
|
||||
Annotation {
|
||||
typ,
|
||||
introduced_variables,
|
||||
references,
|
||||
aliases,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn canonicalize_annotation_with_possible_clauses(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
annotation: &TypeAnnotation,
|
||||
|
@ -828,8 +799,7 @@ fn can_annotation_help(
|
|||
Where(_annotation, clauses) => {
|
||||
debug_assert!(!clauses.is_empty());
|
||||
|
||||
// Has clauses are allowed only on the top level of an ability member signature (for
|
||||
// now), which we handle elsewhere.
|
||||
// Has clauses are allowed only on the top level of a signature, which we handle elsewhere.
|
||||
env.problem(roc_problem::can::Problem::IllegalHasClause {
|
||||
region: Region::across_all(clauses.iter().map(|clause| &clause.region)),
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::abilities::MemberVariables;
|
||||
use crate::annotation::canonicalize_annotation;
|
||||
use crate::annotation::canonicalize_annotation_with_possible_clauses;
|
||||
use crate::annotation::IntroducedVariables;
|
||||
use crate::env::Env;
|
||||
use crate::expr::ClosureData;
|
||||
|
@ -318,8 +317,14 @@ pub fn canonicalize_defs<'a>(
|
|||
match type_defs.remove(&type_name).unwrap() {
|
||||
TypeDef::AliasLike(name, vars, ann, kind) => {
|
||||
let symbol = name.value;
|
||||
let can_ann =
|
||||
canonicalize_annotation(env, &mut scope, &ann.value, ann.region, var_store);
|
||||
let can_ann = canonicalize_annotation(
|
||||
env,
|
||||
&mut scope,
|
||||
&ann.value,
|
||||
ann.region,
|
||||
var_store,
|
||||
&abilities_in_scope,
|
||||
);
|
||||
|
||||
// Does this alias reference any abilities? For now, we don't permit that.
|
||||
let ability_references = can_ann
|
||||
|
@ -437,7 +442,7 @@ pub fn canonicalize_defs<'a>(
|
|||
let mut can_members = Vec::with_capacity(members.len());
|
||||
|
||||
for member in members {
|
||||
let member_annot = canonicalize_annotation_with_possible_clauses(
|
||||
let member_annot = canonicalize_annotation(
|
||||
env,
|
||||
&mut scope,
|
||||
&member.typ.value,
|
||||
|
@ -605,6 +610,7 @@ pub fn canonicalize_defs<'a>(
|
|||
var_store,
|
||||
&mut refs_by_symbol,
|
||||
&mut aliases,
|
||||
&abilities_in_scope,
|
||||
);
|
||||
|
||||
// TODO we should do something with these references; they include
|
||||
|
@ -1148,6 +1154,7 @@ fn canonicalize_pending_value_def<'a>(
|
|||
var_store: &mut VarStore,
|
||||
refs_by_symbol: &mut MutMap<Symbol, (Region, References)>,
|
||||
aliases: &mut ImMap<Symbol, Alias>,
|
||||
abilities_in_scope: &[Symbol],
|
||||
) -> Output {
|
||||
use PendingValueDef::*;
|
||||
|
||||
|
@ -1159,8 +1166,14 @@ fn canonicalize_pending_value_def<'a>(
|
|||
AnnotationOnly(_, loc_can_pattern, loc_ann) => {
|
||||
// annotation sans body cannot introduce new rigids that are visible in other annotations
|
||||
// but the rigids can show up in type error messages, so still register them
|
||||
let type_annotation =
|
||||
canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store);
|
||||
let type_annotation = canonicalize_annotation(
|
||||
env,
|
||||
scope,
|
||||
&loc_ann.value,
|
||||
loc_ann.region,
|
||||
var_store,
|
||||
abilities_in_scope,
|
||||
);
|
||||
|
||||
// Record all the annotation's references in output.references.lookups
|
||||
|
||||
|
@ -1280,8 +1293,14 @@ fn canonicalize_pending_value_def<'a>(
|
|||
}
|
||||
|
||||
TypedBody(_loc_pattern, loc_can_pattern, loc_ann, loc_expr) => {
|
||||
let type_annotation =
|
||||
canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store);
|
||||
let type_annotation = canonicalize_annotation(
|
||||
env,
|
||||
scope,
|
||||
&loc_ann.value,
|
||||
loc_ann.region,
|
||||
var_store,
|
||||
&abilities_in_scope,
|
||||
);
|
||||
|
||||
// Record all the annotation's references in output.references.lookups
|
||||
for symbol in type_annotation.references.iter() {
|
||||
|
|
|
@ -5951,4 +5951,61 @@ mod solve_expr {
|
|||
"{ tag : [ A, B ] }a -> { tag : [ A, B ] }a",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ability_constrained_in_non_member_check() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ hashEq ] to "./platform"
|
||||
|
||||
Hash has
|
||||
hash : a -> U64 | a has Hash
|
||||
|
||||
hashEq : a, a -> Bool | a has Hash
|
||||
hashEq = \x, y -> hash x == hash y
|
||||
"#
|
||||
),
|
||||
"a, a -> Bool | a has Hash",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ability_constrained_in_non_member_infer() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ hashEq ] to "./platform"
|
||||
|
||||
Hash has
|
||||
hash : a -> U64 | a has Hash
|
||||
|
||||
hashEq = \x, y -> hash x == hash y
|
||||
"#
|
||||
),
|
||||
"a, a -> Bool | a has Hash",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ability_constrained_in_non_member_infer_usage() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ result ] to "./platform"
|
||||
|
||||
Hash has
|
||||
hash : a -> U64 | a has Hash
|
||||
|
||||
hashEq = \x, y -> hash x == hash y
|
||||
|
||||
Id := U64
|
||||
hash = \$Id n -> n
|
||||
|
||||
result = hashEq ($Id 100) ($Id 101)
|
||||
"#
|
||||
),
|
||||
"Bool",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -307,6 +307,8 @@ pub fn content_to_string(
|
|||
|
||||
write_content(&env, &mut ctx, content, subs, &mut buf, Parens::Unnecessary);
|
||||
|
||||
ctx.able_variables.sort();
|
||||
ctx.able_variables.dedup();
|
||||
for (i, (var, ability)) in ctx.able_variables.into_iter().enumerate() {
|
||||
buf.push_str(if i == 0 { " | " } else { ", " });
|
||||
buf.push_str(var);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue