Report errors for duplicate bound abilities

This commit is contained in:
Ayaz Hafiz 2022-10-12 10:50:51 -05:00
parent 231a72f2ee
commit 603160dae3
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
4 changed files with 56 additions and 6 deletions

View file

@ -931,31 +931,37 @@ fn canonicalize_has_clause(
let var_name = Lowercase::from(var_name); let var_name = Lowercase::from(var_name);
let mut can_abilities = Vec::with_capacity(abilities.len()); let mut can_abilities = Vec::with_capacity(abilities.len());
for ability in *abilities { for &Loc {
let ability = match ability.value { region,
value: ability,
} in *abilities
{
let ability = match ability {
TypeAnnotation::Apply(module_name, ident, _type_arguments) => { TypeAnnotation::Apply(module_name, ident, _type_arguments) => {
let symbol = make_apply_symbol(env, ability.region, scope, module_name, ident)?; let symbol = make_apply_symbol(env, region, scope, module_name, ident)?;
// Ability defined locally, whose members we are constructing right now... // Ability defined locally, whose members we are constructing right now...
if !pending_abilities_in_scope.contains_key(&symbol) if !pending_abilities_in_scope.contains_key(&symbol)
// or an ability that was imported from elsewhere // or an ability that was imported from elsewhere
&& !scope.abilities_store.is_ability(symbol) && !scope.abilities_store.is_ability(symbol)
{ {
let region = ability.region;
env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region }); env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region });
return Err(Type::Erroneous(Problem::HasClauseIsNotAbility(region))); return Err(Type::Erroneous(Problem::HasClauseIsNotAbility(region)));
} }
symbol symbol
} }
_ => { _ => {
let region = ability.region;
env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region }); env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region });
return Err(Type::Erroneous(Problem::HasClauseIsNotAbility(region))); return Err(Type::Erroneous(Problem::HasClauseIsNotAbility(region)));
} }
}; };
references.insert(ability); references.insert(ability);
can_abilities.push(ability); if can_abilities.contains(&ability) {
env.problem(roc_problem::can::Problem::DuplicateHasAbility { ability, region });
} else {
can_abilities.push(ability);
}
} }
if let Some(shadowing) = introduced_variables.named_var_by_name(&var_name) { if let Some(shadowing) = introduced_variables.named_var_by_name(&var_name) {

View file

@ -117,6 +117,10 @@ pub enum Problem {
IllegalHasClause { IllegalHasClause {
region: Region, region: Region,
}, },
DuplicateHasAbility {
ability: Symbol,
region: Region,
},
AbilityMemberMissingHasClause { AbilityMemberMissingHasClause {
member: Symbol, member: Symbol,
ability: Symbol, ability: Symbol,

View file

@ -677,6 +677,24 @@ pub fn can_problem<'b>(
severity = Severity::RuntimeError; severity = Severity::RuntimeError;
} }
Problem::DuplicateHasAbility { ability, region } => {
doc = alloc.stack([
alloc.concat([
alloc.reflow("I already saw that this type variable is bound to the "),
alloc.symbol_foreign_qualified(ability),
alloc.reflow(" ability once before:"),
]),
alloc.region(lines.convert_region(region)),
alloc.concat([
alloc.reflow("Abilities only need to bound to a type variable once in a "),
alloc.keyword("has"),
alloc.reflow(" clause!"),
]),
]);
title = "DUPLICATE BOUND ABILITY".to_string();
severity = Severity::Warning;
}
Problem::AbilityMemberMissingHasClause { Problem::AbilityMemberMissingHasClause {
member, member,
ability, ability,

View file

@ -11531,4 +11531,26 @@ All branches in an `if` must have the same type!
Tip: You can define a custom implementation of `Encoding` for `F`. Tip: You can define a custom implementation of `Encoding` for `F`.
"### "###
); );
test_report!(
duplicate_ability_in_has_clause,
indoc!(
r#"
f : a -> {} | a has Hash & Hash
f
"#
),
@r###"
DUPLICATE BOUND ABILITY /code/proj/Main.roc
I already saw that this type variable is bound to the `Hash` ability
once before:
4 f : a -> {} | a has Hash & Hash
^^^^
Abilities only need to bound to a type variable once in a `has` clause!
"###
);
} }