Report opaques as opaques, not aliases

Closes #3313
Closes #3654
This commit is contained in:
Ayaz Hafiz 2022-07-29 11:22:54 -04:00
parent fffbbd08b0
commit b87f09115c
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
7 changed files with 80 additions and 17 deletions

View file

@ -566,6 +566,7 @@ fn can_annotation_help(
region, region,
alias_needs: alias.type_variables.len() as u8, alias_needs: alias.type_variables.len() as u8,
type_got: args.len() as u8, type_got: args.len() as u8,
alias_kind: alias.kind,
}); });
return error; return error;
} }

View file

@ -369,6 +369,7 @@ fn canonicalize_alias<'a>(
typ: symbol, typ: symbol,
variable_region: loc_lowercase.region, variable_region: loc_lowercase.region,
variable_name: loc_lowercase.value.clone(), variable_name: loc_lowercase.value.clone(),
alias_kind: AliasKind::Structural,
}); });
} }
AliasKind::Opaque => { AliasKind::Opaque => {
@ -2688,6 +2689,7 @@ fn correct_mutual_recursive_type_alias<'a>(
env, env,
&mut alias.typ, &mut alias.typ,
alias_name, alias_name,
alias.kind,
alias.region, alias.region,
rest, rest,
can_still_report_error, can_still_report_error,
@ -2870,7 +2872,15 @@ fn make_tag_union_recursive_help<'a, 'b>(
} }
_ => { _ => {
// take care to report a cyclic alias only once (not once for each alias in the cycle) // take care to report a cyclic alias only once (not once for each alias in the cycle)
mark_cyclic_alias(env, typ, symbol, region, others, *can_report_cyclic_error); mark_cyclic_alias(
env,
typ,
symbol,
alias_kind,
region,
others,
*can_report_cyclic_error,
);
*can_report_cyclic_error = false; *can_report_cyclic_error = false;
Cyclic Cyclic
@ -2882,6 +2892,7 @@ fn mark_cyclic_alias<'a>(
env: &mut Env<'a>, env: &mut Env<'a>,
typ: &mut Type, typ: &mut Type,
symbol: Symbol, symbol: Symbol,
alias_kind: AliasKind,
region: Region, region: Region,
others: Vec<Symbol>, others: Vec<Symbol>,
report: bool, report: bool,
@ -2890,7 +2901,7 @@ fn mark_cyclic_alias<'a>(
*typ = Type::Erroneous(problem); *typ = Type::Erroneous(problem);
if report { if report {
let problem = Problem::CyclicAlias(symbol, region, others); let problem = Problem::CyclicAlias(symbol, region, others, alias_kind);
env.problems.push(problem); env.problems.push(problem);
} }
} }

View file

@ -45,12 +45,13 @@ pub enum Problem {
shadow: Loc<Ident>, shadow: Loc<Ident>,
kind: ShadowKind, kind: ShadowKind,
}, },
CyclicAlias(Symbol, Region, Vec<Symbol>), CyclicAlias(Symbol, Region, Vec<Symbol>, AliasKind),
BadRecursion(Vec<CycleEntry>), BadRecursion(Vec<CycleEntry>),
PhantomTypeArgument { PhantomTypeArgument {
typ: Symbol, typ: Symbol,
variable_region: Region, variable_region: Region,
variable_name: Lowercase, variable_name: Lowercase,
alias_kind: AliasKind,
}, },
UnboundTypeVariable { UnboundTypeVariable {
typ: Symbol, typ: Symbol,

View file

@ -1322,6 +1322,7 @@ impl Type {
region, region,
type_got: args.len() as u8, type_got: args.len() as u8,
alias_needs: alias.type_variables.len() as u8, alias_needs: alias.type_variables.len() as u8,
alias_kind: AliasKind::Structural,
}); });
return; return;
} }
@ -2028,6 +2029,15 @@ pub enum AliasKind {
Opaque, Opaque,
} }
impl AliasKind {
pub fn as_str(&self) -> &'static str {
match self {
AliasKind::Structural => "alias",
AliasKind::Opaque => "opaque",
}
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct AliasVar { pub struct AliasVar {
pub name: Lowercase, pub name: Lowercase,
@ -2104,6 +2114,7 @@ pub enum Problem {
region: Region, region: Region,
type_got: u8, type_got: u8,
alias_needs: u8, alias_needs: u8,
alias_kind: AliasKind,
}, },
InvalidModule, InvalidModule,
SolvedTypeError, SolvedTypeError,

View file

@ -233,8 +233,10 @@ pub fn can_problem<'b>(
title = DUPLICATE_NAME.to_string(); title = DUPLICATE_NAME.to_string();
severity = Severity::RuntimeError; severity = Severity::RuntimeError;
} }
Problem::CyclicAlias(symbol, region, others) => { Problem::CyclicAlias(symbol, region, others, alias_kind) => {
let answer = crate::error::r#type::cyclic_alias(alloc, lines, symbol, region, others); let answer = crate::error::r#type::cyclic_alias(
alloc, lines, symbol, region, others, alias_kind,
);
doc = answer.0; doc = answer.0;
title = answer.1; title = answer.1;
@ -244,6 +246,7 @@ pub fn can_problem<'b>(
typ: alias, typ: alias,
variable_region, variable_region,
variable_name, variable_name,
alias_kind,
} => { } => {
doc = alloc.stack([ doc = alloc.stack([
alloc.concat([ alloc.concat([
@ -251,10 +254,12 @@ pub fn can_problem<'b>(
alloc.type_variable(variable_name), alloc.type_variable(variable_name),
alloc.reflow(" type parameter is not used in the "), alloc.reflow(" type parameter is not used in the "),
alloc.symbol_unqualified(alias), alloc.symbol_unqualified(alias),
alloc.reflow(" alias definition:"), alloc.reflow(" "),
alloc.reflow(alias_kind.as_str()),
alloc.reflow(" definition:"),
]), ]),
alloc.region(lines.convert_region(variable_region)), alloc.region(lines.convert_region(variable_region)),
alloc.reflow("Roc does not allow unused type alias parameters!"), alloc.reflow("Roc does not allow unused type parameters!"),
// TODO add link to this guide section // TODO add link to this guide section
alloc.tip().append(alloc.reflow( alloc.tip().append(alloc.reflow(
"If you want an unused type parameter (a so-called \"phantom type\"), \ "If you want an unused type parameter (a so-called \"phantom type\"), \

View file

@ -77,6 +77,7 @@ pub fn type_problem<'b>(
region, region,
type_got, type_got,
alias_needs, alias_needs,
alias_kind,
} => { } => {
let needed_arguments = if alias_needs == 1 { let needed_arguments = if alias_needs == 1 {
alloc.reflow("1 type argument") alloc.reflow("1 type argument")
@ -92,7 +93,9 @@ pub fn type_problem<'b>(
alloc.concat([ alloc.concat([
alloc.reflow("The "), alloc.reflow("The "),
alloc.symbol_unqualified(symbol), alloc.symbol_unqualified(symbol),
alloc.reflow(" alias expects "), alloc.reflow(" "),
alloc.reflow(alias_kind.as_str()),
alloc.reflow(" expects "),
needed_arguments, needed_arguments,
alloc.reflow(", but it got "), alloc.reflow(", but it got "),
found_arguments, found_arguments,
@ -433,16 +436,21 @@ pub fn cyclic_alias<'b>(
symbol: Symbol, symbol: Symbol,
region: roc_region::all::Region, region: roc_region::all::Region,
others: Vec<Symbol>, others: Vec<Symbol>,
alias_kind: AliasKind,
) -> (RocDocBuilder<'b>, String) { ) -> (RocDocBuilder<'b>, String) {
let when_is_recursion_legal = let when_is_recursion_legal =
alloc.reflow("Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive."); alloc.reflow("Recursion in ")
.append(alloc.reflow(alias_kind.as_str()))
.append(alloc.reflow("es is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive."));
let doc = if others.is_empty() { let doc = if others.is_empty() {
alloc.stack([ alloc.stack([
alloc alloc
.reflow("The ") .reflow("The ")
.append(alloc.symbol_unqualified(symbol)) .append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(" alias is self-recursive in an invalid way:")), .append(alloc.reflow(" "))
.append(alloc.reflow(alias_kind.as_str()))
.append(alloc.reflow(" is self-recursive in an invalid way:")),
alloc.region(lines.convert_region(region)), alloc.region(lines.convert_region(region)),
when_is_recursion_legal, when_is_recursion_legal,
]) ])
@ -451,14 +459,18 @@ pub fn cyclic_alias<'b>(
alloc alloc
.reflow("The ") .reflow("The ")
.append(alloc.symbol_unqualified(symbol)) .append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(" alias is recursive in an invalid way:")), .append(alloc.reflow(" "))
.append(alloc.reflow(alias_kind.as_str()))
.append(alloc.reflow(" is recursive in an invalid way:")),
alloc.region(lines.convert_region(region)), alloc.region(lines.convert_region(region)),
alloc alloc
.reflow("The ") .reflow("The ")
.append(alloc.symbol_unqualified(symbol)) .append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow( .append(alloc.reflow(" "))
" alias depends on itself through the following chain of definitions:", .append(alloc.reflow(alias_kind.as_str()))
)), .append(
alloc.reflow(" depends on itself through the following chain of definitions:"),
),
crate::report::cycle( crate::report::cycle(
alloc, alloc,
4, 4,

View file

@ -3112,7 +3112,7 @@ mod test_reporting {
@r###" @r###"
TOO MANY TYPE ARGUMENTS /code/proj/Main.roc TOO MANY TYPE ARGUMENTS /code/proj/Main.roc
The `Num` alias expects 1 type argument, but it got 2 instead: The `Num` opaque expects 1 type argument, but it got 2 instead:
4 a : Num.Num Num.I64 Num.F64 4 a : Num.Num Num.I64 Num.F64
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
@ -3134,7 +3134,7 @@ mod test_reporting {
@r###" @r###"
TOO MANY TYPE ARGUMENTS /code/proj/Main.roc TOO MANY TYPE ARGUMENTS /code/proj/Main.roc
The `Num` alias expects 1 type argument, but it got 2 instead: The `Num` opaque expects 1 type argument, but it got 2 instead:
4 f : Str -> Num.Num Num.I64 Num.F64 4 f : Str -> Num.Num Num.I64 Num.F64
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
@ -3210,7 +3210,7 @@ mod test_reporting {
4 Foo a : [Foo] 4 Foo a : [Foo]
^ ^
Roc does not allow unused type alias parameters! Roc does not allow unused type parameters!
Tip: If you want an unused type parameter (a so-called "phantom Tip: If you want an unused type parameter (a so-called "phantom
type"), read the guide section on phantom values. type"), read the guide section on phantom values.
@ -10148,4 +10148,26 @@ All branches in an `if` must have the same type!
Tip: Looks like the y field is missing. Tip: Looks like the y field is missing.
"### "###
); );
test_report!(
cyclic_opaque,
indoc!(
r#"
Recursive := [Infinitely Recursive]
0
"#
),
@r###"
CYCLIC ALIAS /code/proj/Main.roc
The `Recursive` opaque is self-recursive in an invalid way:
4 Recursive := [Infinitely Recursive]
^^^^^^^^^
Recursion in opaquees is only allowed if recursion happens behind a
tagged union, at least one variant of which is not recursive.
"###
);
} }