Explicitly disallow ability definitions in nested scopes

Abilities can only be defined on the toplevel of a module. There is a
technical reason to this, which is that during type solving we must
introduce all abilities at the very beginning, and we need to make sure
ranks are correct. But there is a practical reason as well, which is
that nested ability definitions don't seem to be very useful.

Note that specializations can be nested, and are allowed to be. Also, we
can revisit this in the future. I just don't want experiments to break
right now because someone uses an ability in a nested scope where we
don't expect.

Closes #2878
This commit is contained in:
Ayaz Hafiz 2022-04-18 18:04:46 -04:00
parent 340f6b7c88
commit f129777115
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
5 changed files with 89 additions and 31 deletions

View file

@ -45,6 +45,7 @@ const ILLEGAL_HAS_CLAUSE: &str = "ILLEGAL HAS CLAUSE";
const ABILITY_MEMBER_MISSING_HAS_CLAUSE: &str = "ABILITY MEMBER MISSING HAS CLAUSE";
const ABILITY_MEMBER_HAS_EXTRANEOUS_HAS_CLAUSE: &str = "ABILITY MEMBER HAS EXTRANEOUS HAS CLAUSE";
const ABILITY_MEMBER_BINDS_MULTIPLE_VARIABLES: &str = "ABILITY MEMBER BINDS MULTIPLE VARIABLES";
const ABILITY_NOT_ON_TOPLEVEL: &str = "ABILITY NOT ON TOP-LEVEL";
pub fn can_problem<'b>(
alloc: &'b RocDocAllocator<'b>,
@ -736,6 +737,18 @@ pub fn can_problem<'b>(
title = ABILITY_MEMBER_HAS_EXTRANEOUS_HAS_CLAUSE.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityNotOnToplevel { region } => {
doc = alloc.stack(vec![
alloc.concat(vec![alloc.reflow(
"This ability definition is not on the top-level of a module:",
)]),
alloc.region(lines.convert_region(region)),
alloc.reflow("Abilities can only be defined on the top-level of a Roc module."),
]);
title = ABILITY_NOT_ON_TOPLEVEL.to_string();
severity = Severity::RuntimeError;
}
};
Report {