Disallow abilities on toplevel of scope

This commit is contained in:
Ayaz Hafiz 2022-05-09 09:45:02 -04:00
parent 9515f60479
commit b0734c3d80
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
4 changed files with 63 additions and 1 deletions

View file

@ -505,6 +505,7 @@ pub(crate) fn canonicalize_defs<'a>(
output, output,
scope, scope,
var_store, var_store,
pattern_type,
&mut aliases, &mut aliases,
&abilities_in_scope, &abilities_in_scope,
); );
@ -981,12 +982,13 @@ fn canonicalize_pending_value_def<'a>(
mut output: Output, mut output: Output,
scope: &mut Scope, scope: &mut Scope,
var_store: &mut VarStore, var_store: &mut VarStore,
pattern_type: PatternType,
aliases: &mut VecMap<Symbol, Alias>, aliases: &mut VecMap<Symbol, Alias>,
abilities_in_scope: &[Symbol], abilities_in_scope: &[Symbol],
) -> DefOutput { ) -> DefOutput {
use PendingValueDef::*; use PendingValueDef::*;
match pending_def { let output = match pending_def {
AnnotationOnly(_, loc_can_pattern, loc_ann) => { AnnotationOnly(_, loc_can_pattern, loc_ann) => {
// Make types for the body expr, even if we won't end up having a body. // Make types for the body expr, even if we won't end up having a body.
let expr_var = var_store.fresh(); let expr_var = var_store.fresh();
@ -1129,7 +1131,21 @@ fn canonicalize_pending_value_def<'a>(
None, None,
) )
} }
};
// Disallow ability specializations that aren't on the toplevel (note: we might loosen this
// restriction later on).
if pattern_type != PatternType::TopLevelDef {
if let Loc {
value: Pattern::AbilityMemberSpecialization { specializes, .. },
region,
} = output.def.loc_pattern
{
env.problem(Problem::NestedSpecialization(specializes, region));
}
} }
output
} }
// TODO trim down these arguments! // TODO trim down these arguments!

View file

@ -139,6 +139,7 @@ pub enum Problem {
region: Region, region: Region,
}, },
AbilityUsedAsType(Lowercase, Symbol, Region), AbilityUsedAsType(Lowercase, Symbol, Region),
NestedSpecialization(Symbol, Region),
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]

View file

@ -46,6 +46,7 @@ const ABILITY_MEMBER_MISSING_HAS_CLAUSE: &str = "ABILITY MEMBER MISSING HAS CLAU
const ABILITY_MEMBER_HAS_EXTRANEOUS_HAS_CLAUSE: &str = "ABILITY MEMBER HAS EXTRANEOUS 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_MEMBER_BINDS_MULTIPLE_VARIABLES: &str = "ABILITY MEMBER BINDS MULTIPLE VARIABLES";
const ABILITY_NOT_ON_TOPLEVEL: &str = "ABILITY NOT ON TOP-LEVEL"; const ABILITY_NOT_ON_TOPLEVEL: &str = "ABILITY NOT ON TOP-LEVEL";
const SPECIALIZATION_NOT_ON_TOPLEVEL: &str = "SPECIALIZATION NOT ON TOP-LEVEL";
const ABILITY_USED_AS_TYPE: &str = "ABILITY USED AS TYPE"; const ABILITY_USED_AS_TYPE: &str = "ABILITY USED AS TYPE";
pub fn can_problem<'b>( pub fn can_problem<'b>(
@ -779,6 +780,19 @@ pub fn can_problem<'b>(
title = ABILITY_USED_AS_TYPE.to_string(); title = ABILITY_USED_AS_TYPE.to_string();
severity = Severity::RuntimeError; severity = Severity::RuntimeError;
} }
Problem::NestedSpecialization(member, region) => {
doc = alloc.stack([
alloc.concat([
alloc.reflow("This specialization of the "),
alloc.symbol_unqualified(member),
alloc.reflow("ability member is in a nested scope:"),
]),
alloc.region(lines.convert_region(region)),
alloc.reflow("Specializations can only be defined on the top-level of a module."),
]);
title = SPECIALIZATION_NOT_ON_TOPLEVEL.to_string();
severity = Severity::Warning;
}
}; };
Report { Report {

View file

@ -9764,4 +9764,35 @@ I need all branches in an `if` to have the same type!
"", // no problem "", // no problem
) )
} }
#[test]
fn nested_specialization() {
new_report_problem_as(
"nested_specialization",
indoc!(
r#"
app "test" provides [ main ] to "./platform"
Default has default : {} -> a | a has Default
main =
A := {}
default = \{} -> @A {}
default {}
"#
),
indoc!(
r#"
SPECIALIZATION NOT ON TOP-LEVEL /code/proj/Main.roc
This specialization of the `default`ability member is in a nested scope:
7 default = \{} -> @A {}
^^^^^^^
Specializations can only be defined on the top-level of a module.
"#
),
)
}
} }