mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41: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,
|
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!
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue