mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Disallow abilities on toplevel of scope
This commit is contained in:
parent
9515f60479
commit
b0734c3d80
4 changed files with 63 additions and 1 deletions
|
@ -505,6 +505,7 @@ pub(crate) fn canonicalize_defs<'a>(
|
|||
output,
|
||||
scope,
|
||||
var_store,
|
||||
pattern_type,
|
||||
&mut aliases,
|
||||
&abilities_in_scope,
|
||||
);
|
||||
|
@ -981,12 +982,13 @@ fn canonicalize_pending_value_def<'a>(
|
|||
mut output: Output,
|
||||
scope: &mut Scope,
|
||||
var_store: &mut VarStore,
|
||||
pattern_type: PatternType,
|
||||
aliases: &mut VecMap<Symbol, Alias>,
|
||||
abilities_in_scope: &[Symbol],
|
||||
) -> DefOutput {
|
||||
use PendingValueDef::*;
|
||||
|
||||
match pending_def {
|
||||
let output = match pending_def {
|
||||
AnnotationOnly(_, loc_can_pattern, loc_ann) => {
|
||||
// Make types for the body expr, even if we won't end up having a body.
|
||||
let expr_var = var_store.fresh();
|
||||
|
@ -1129,7 +1131,21 @@ fn canonicalize_pending_value_def<'a>(
|
|||
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!
|
||||
|
|
|
@ -139,6 +139,7 @@ pub enum Problem {
|
|||
region: Region,
|
||||
},
|
||||
AbilityUsedAsType(Lowercase, Symbol, Region),
|
||||
NestedSpecialization(Symbol, Region),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
|
@ -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_BINDS_MULTIPLE_VARIABLES: &str = "ABILITY MEMBER BINDS MULTIPLE VARIABLES";
|
||||
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";
|
||||
|
||||
pub fn can_problem<'b>(
|
||||
|
@ -779,6 +780,19 @@ pub fn can_problem<'b>(
|
|||
title = ABILITY_USED_AS_TYPE.to_string();
|
||||
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 {
|
||||
|
|
|
@ -9764,4 +9764,35 @@ I need all branches in an `if` to have the same type!
|
|||
"", // 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.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue