mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
record, tag, alias errors
- duplicate fields and tags are reported - circular aliases are reported
This commit is contained in:
parent
0372b34e86
commit
f6af66f342
13 changed files with 679 additions and 55 deletions
|
@ -326,6 +326,8 @@ fn can_annotation_help(
|
||||||
TagUnion { tags, ext } => {
|
TagUnion { tags, ext } => {
|
||||||
let mut tag_types = Vec::with_capacity(tags.len());
|
let mut tag_types = Vec::with_capacity(tags.len());
|
||||||
|
|
||||||
|
let mut seen = MutSet::default();
|
||||||
|
|
||||||
for tag in tags.iter() {
|
for tag in tags.iter() {
|
||||||
can_tag(
|
can_tag(
|
||||||
env,
|
env,
|
||||||
|
@ -338,6 +340,17 @@ fn can_annotation_help(
|
||||||
&mut tag_types,
|
&mut tag_types,
|
||||||
references,
|
references,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let added = &tag_types.last().unwrap().0;
|
||||||
|
let is_new = seen.insert(added.clone());
|
||||||
|
|
||||||
|
if !is_new {
|
||||||
|
env.problem(roc_problem::can::Problem::DuplicateTag {
|
||||||
|
tag_name: added.clone(),
|
||||||
|
tag_region: tag.region,
|
||||||
|
tag_union_region: region,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ext_type = match ext {
|
let ext_type = match ext {
|
||||||
|
@ -405,7 +418,16 @@ fn can_assigned_field<'a>(
|
||||||
);
|
);
|
||||||
let label = Lowercase::from(field_name.value);
|
let label = Lowercase::from(field_name.value);
|
||||||
|
|
||||||
field_types.insert(label, field_type);
|
let removed = field_types.insert(label, field_type);
|
||||||
|
|
||||||
|
if removed.is_some() {
|
||||||
|
let field_region = Region::span_across(&field_name.region, &annotation.region);
|
||||||
|
env.problem(roc_problem::can::Problem::DuplicateRecordFieldType {
|
||||||
|
field_name: field_name.value.into(),
|
||||||
|
field_region,
|
||||||
|
record_region: region,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LabelOnly(loc_field_name) => {
|
LabelOnly(loc_field_name) => {
|
||||||
// Interpret { a, b } as { a : a, b : b }
|
// Interpret { a, b } as { a : a, b : b }
|
||||||
|
|
|
@ -32,24 +32,32 @@ impl Constraint {
|
||||||
match self {
|
match self {
|
||||||
True | SaveTheEnvironment => {}
|
True | SaveTheEnvironment => {}
|
||||||
|
|
||||||
Eq(typ, expected, _, _) => {
|
Eq(typ, expected, _, region) => {
|
||||||
expected
|
let expected_region = expected.get_annotation_region().unwrap_or(*region);
|
||||||
.get_type_mut_ref()
|
expected.get_type_mut_ref().instantiate_aliases(
|
||||||
.instantiate_aliases(aliases, var_store, introduced);
|
expected_region,
|
||||||
typ.instantiate_aliases(aliases, var_store, introduced);
|
aliases,
|
||||||
|
var_store,
|
||||||
|
introduced,
|
||||||
|
);
|
||||||
|
typ.instantiate_aliases(*region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
Lookup(_, expected, _) => {
|
Lookup(_, expected, region) => {
|
||||||
expected
|
let expected_region = expected.get_annotation_region().unwrap_or(*region);
|
||||||
.get_type_mut_ref()
|
expected.get_type_mut_ref().instantiate_aliases(
|
||||||
.instantiate_aliases(aliases, var_store, introduced);
|
expected_region,
|
||||||
|
aliases,
|
||||||
|
var_store,
|
||||||
|
introduced,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern(_, _, typ, pexpected) => {
|
Pattern(region, _, typ, pexpected) => {
|
||||||
pexpected
|
pexpected
|
||||||
.get_type_mut_ref()
|
.get_type_mut_ref()
|
||||||
.instantiate_aliases(aliases, var_store, introduced);
|
.instantiate_aliases(*region, aliases, var_store, introduced);
|
||||||
typ.instantiate_aliases(aliases, var_store, introduced);
|
typ.instantiate_aliases(*region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
And(nested) => {
|
And(nested) => {
|
||||||
|
@ -65,8 +73,8 @@ impl Constraint {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut introduced = ImSet::default();
|
let mut introduced = ImSet::default();
|
||||||
for Located { value: typ, .. } in letcon.def_types.iter_mut() {
|
for Located { region, value: typ } in letcon.def_types.iter_mut() {
|
||||||
typ.instantiate_aliases(&new_aliases, var_store, &mut introduced);
|
typ.instantiate_aliases(*region, &new_aliases, var_store, &mut introduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
letcon.defs_constraint.instantiate_aliases_help(
|
letcon.defs_constraint.instantiate_aliases_help(
|
||||||
|
|
|
@ -201,6 +201,7 @@ pub fn canonicalize_defs<'a>(
|
||||||
let mut can_vars: Vec<Located<(Lowercase, Variable)>> =
|
let mut can_vars: Vec<Located<(Lowercase, Variable)>> =
|
||||||
Vec::with_capacity(vars.len());
|
Vec::with_capacity(vars.len());
|
||||||
|
|
||||||
|
let mut is_phantom = false;
|
||||||
for loc_lowercase in vars {
|
for loc_lowercase in vars {
|
||||||
if let Some(var) = can_ann
|
if let Some(var) = can_ann
|
||||||
.introduced_variables
|
.introduced_variables
|
||||||
|
@ -212,12 +213,31 @@ pub fn canonicalize_defs<'a>(
|
||||||
region: loc_lowercase.region,
|
region: loc_lowercase.region,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
panic!("TODO handle phantom type variables, they are not allowed!\nThe {:?} variable in the definition of {:?} gives trouble", loc_lowercase, symbol);
|
is_phantom = true;
|
||||||
|
|
||||||
|
env.problems.push(Problem::PhantomTypeArgument {
|
||||||
|
alias: symbol,
|
||||||
|
variable_region: loc_lowercase.region,
|
||||||
|
variable_name: loc_lowercase.value.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if is_phantom {
|
||||||
|
// Bail out
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if can_ann.typ.contains_symbol(symbol) {
|
if can_ann.typ.contains_symbol(symbol) {
|
||||||
make_tag_union_recursive(symbol, &mut can_ann.typ, var_store);
|
make_tag_union_recursive(
|
||||||
|
env,
|
||||||
|
symbol,
|
||||||
|
name.region,
|
||||||
|
vec![],
|
||||||
|
&mut can_ann.typ,
|
||||||
|
var_store,
|
||||||
|
&mut false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let alias = roc_types::types::Alias {
|
let alias = roc_types::types::Alias {
|
||||||
|
@ -231,7 +251,7 @@ pub fn canonicalize_defs<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
correct_mutual_recursive_type_alias(&mut aliases, &var_store);
|
correct_mutual_recursive_type_alias(env, &mut aliases, &var_store);
|
||||||
|
|
||||||
// Now that we have the scope completely assembled, and shadowing resolved,
|
// Now that we have the scope completely assembled, and shadowing resolved,
|
||||||
// we're ready to canonicalize any body exprs.
|
// we're ready to canonicalize any body exprs.
|
||||||
|
@ -836,7 +856,11 @@ fn canonicalize_pending_def<'a>(
|
||||||
region: loc_lowercase.region,
|
region: loc_lowercase.region,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
panic!("TODO handle phantom type variables, they are not allowed!");
|
env.problems.push(Problem::PhantomTypeArgument {
|
||||||
|
alias: symbol,
|
||||||
|
variable_region: loc_lowercase.region,
|
||||||
|
variable_name: loc_lowercase.value.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,7 +876,9 @@ fn canonicalize_pending_def<'a>(
|
||||||
|
|
||||||
scope.add_alias(symbol, name.region, can_vars, rec_type_union);
|
scope.add_alias(symbol, name.region, can_vars, rec_type_union);
|
||||||
} else {
|
} else {
|
||||||
panic!("recursion in type alias that is not behind a Tag");
|
env.problems
|
||||||
|
.push(Problem::CyclicAlias(symbol, name.region, vec![]));
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1385,7 +1411,11 @@ fn pending_typed_body<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make aliases recursive
|
/// Make aliases recursive
|
||||||
fn correct_mutual_recursive_type_alias(aliases: &mut SendMap<Symbol, Alias>, var_store: &VarStore) {
|
fn correct_mutual_recursive_type_alias<'a>(
|
||||||
|
env: &mut Env<'a>,
|
||||||
|
aliases: &mut SendMap<Symbol, Alias>,
|
||||||
|
var_store: &VarStore,
|
||||||
|
) {
|
||||||
let mut symbols_introduced = ImSet::default();
|
let mut symbols_introduced = ImSet::default();
|
||||||
|
|
||||||
for (key, _) in aliases.iter() {
|
for (key, _) in aliases.iter() {
|
||||||
|
@ -1434,11 +1464,17 @@ fn correct_mutual_recursive_type_alias(aliases: &mut SendMap<Symbol, Alias>, var
|
||||||
&mutually_recursive_symbols,
|
&mutually_recursive_symbols,
|
||||||
all_successors_without_self,
|
all_successors_without_self,
|
||||||
) {
|
) {
|
||||||
|
// make sure we report only one error for the cycle, not an error for every
|
||||||
|
// alias in the cycle.
|
||||||
|
let mut can_still_report_error = true;
|
||||||
|
|
||||||
// TODO use itertools to be more efficient here
|
// TODO use itertools to be more efficient here
|
||||||
for rec in &cycle {
|
for rec in &cycle {
|
||||||
let mut to_instantiate = ImMap::default();
|
let mut to_instantiate = ImMap::default();
|
||||||
|
let mut others = Vec::with_capacity(cycle.len() - 1);
|
||||||
for other in &cycle {
|
for other in &cycle {
|
||||||
if rec != other {
|
if rec != other {
|
||||||
|
others.push(*other);
|
||||||
if let Some(alias) = originals.get(other) {
|
if let Some(alias) = originals.get(other) {
|
||||||
to_instantiate.insert(*other, alias.clone());
|
to_instantiate.insert(*other, alias.clone());
|
||||||
}
|
}
|
||||||
|
@ -1447,11 +1483,20 @@ fn correct_mutual_recursive_type_alias(aliases: &mut SendMap<Symbol, Alias>, var
|
||||||
|
|
||||||
if let Some(alias) = aliases.get_mut(rec) {
|
if let Some(alias) = aliases.get_mut(rec) {
|
||||||
alias.typ.instantiate_aliases(
|
alias.typ.instantiate_aliases(
|
||||||
|
alias.region,
|
||||||
&to_instantiate,
|
&to_instantiate,
|
||||||
var_store,
|
var_store,
|
||||||
&mut ImSet::default(),
|
&mut ImSet::default(),
|
||||||
);
|
);
|
||||||
make_tag_union_recursive(*rec, &mut alias.typ, var_store);
|
make_tag_union_recursive(
|
||||||
|
env,
|
||||||
|
*rec,
|
||||||
|
alias.region,
|
||||||
|
others,
|
||||||
|
&mut alias.typ,
|
||||||
|
var_store,
|
||||||
|
&mut can_still_report_error,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1459,14 +1504,40 @@ fn correct_mutual_recursive_type_alias(aliases: &mut SendMap<Symbol, Alias>, var
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_tag_union_recursive(symbol: Symbol, typ: &mut Type, var_store: &VarStore) {
|
fn make_tag_union_recursive<'a>(
|
||||||
|
env: &mut Env<'a>,
|
||||||
|
symbol: Symbol,
|
||||||
|
region: Region,
|
||||||
|
others: Vec<Symbol>,
|
||||||
|
typ: &mut Type,
|
||||||
|
var_store: &VarStore,
|
||||||
|
can_report_error: &mut bool,
|
||||||
|
) {
|
||||||
match typ {
|
match typ {
|
||||||
Type::TagUnion(tags, ext) => {
|
Type::TagUnion(tags, ext) => {
|
||||||
let rec_var = var_store.fresh();
|
let rec_var = var_store.fresh();
|
||||||
*typ = Type::RecursiveTagUnion(rec_var, tags.to_vec(), ext.clone());
|
*typ = Type::RecursiveTagUnion(rec_var, tags.to_vec(), ext.clone());
|
||||||
typ.substitute_alias(symbol, &Type::Variable(rec_var));
|
typ.substitute_alias(symbol, &Type::Variable(rec_var));
|
||||||
}
|
}
|
||||||
Type::Alias(_, _, actual) => make_tag_union_recursive(symbol, actual, var_store),
|
Type::Alias(_, _, actual) => make_tag_union_recursive(
|
||||||
_ => panic!("recursion in type alias is not behind a Tag"),
|
env,
|
||||||
|
symbol,
|
||||||
|
region,
|
||||||
|
others,
|
||||||
|
actual,
|
||||||
|
var_store,
|
||||||
|
can_report_error,
|
||||||
|
),
|
||||||
|
_ => {
|
||||||
|
let problem = roc_types::types::Problem::CyclicAlias(symbol, region, others.clone());
|
||||||
|
*typ = Type::Erroneous(problem);
|
||||||
|
|
||||||
|
if *can_report_error {
|
||||||
|
*can_report_error = false;
|
||||||
|
|
||||||
|
let problem = Problem::CyclicAlias(symbol, region, others);
|
||||||
|
env.problems.push(problem);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,15 @@ impl<T> Expected<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_annotation_region(&self) -> Option<Region> {
|
||||||
|
match self {
|
||||||
|
Expected::FromAnnotation(_, _, AnnotationSource::TypedBody { region }, _) => {
|
||||||
|
Some(*region)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn replace<U>(self, new: U) -> Expected<U> {
|
pub fn replace<U>(self, new: U) -> Expected<U> {
|
||||||
match self {
|
match self {
|
||||||
Expected::NoExpectation(_val) => Expected::NoExpectation(new),
|
Expected::NoExpectation(_val) => Expected::NoExpectation(new),
|
||||||
|
|
|
@ -193,7 +193,8 @@ pub fn canonicalize_expr<'a>(
|
||||||
let (can_update, update_out) =
|
let (can_update, update_out) =
|
||||||
canonicalize_expr(env, var_store, scope, loc_update.region, &loc_update.value);
|
canonicalize_expr(env, var_store, scope, loc_update.region, &loc_update.value);
|
||||||
if let Var(symbol) = &can_update.value {
|
if let Var(symbol) = &can_update.value {
|
||||||
let (can_fields, mut output) = canonicalize_fields(env, var_store, scope, fields);
|
let (can_fields, mut output) =
|
||||||
|
canonicalize_fields(env, var_store, scope, region, fields);
|
||||||
|
|
||||||
output.references = output.references.union(update_out.references);
|
output.references = output.references.union(update_out.references);
|
||||||
|
|
||||||
|
@ -219,7 +220,8 @@ pub fn canonicalize_expr<'a>(
|
||||||
if fields.is_empty() {
|
if fields.is_empty() {
|
||||||
(EmptyRecord, Output::default())
|
(EmptyRecord, Output::default())
|
||||||
} else {
|
} else {
|
||||||
let (can_fields, output) = canonicalize_fields(env, var_store, scope, fields);
|
let (can_fields, output) =
|
||||||
|
canonicalize_fields(env, var_store, scope, region, fields);
|
||||||
|
|
||||||
(
|
(
|
||||||
Record {
|
Record {
|
||||||
|
@ -885,6 +887,7 @@ fn canonicalize_fields<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
var_store: &VarStore,
|
var_store: &VarStore,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
region: Region,
|
||||||
fields: &'a [Located<ast::AssignedField<'a, ast::Expr<'a>>>],
|
fields: &'a [Located<ast::AssignedField<'a, ast::Expr<'a>>>],
|
||||||
) -> (SendMap<Lowercase, Field>, Output) {
|
) -> (SendMap<Lowercase, Field>, Output) {
|
||||||
let mut can_fields = SendMap::default();
|
let mut can_fields = SendMap::default();
|
||||||
|
@ -900,7 +903,15 @@ fn canonicalize_fields<'a>(
|
||||||
loc_expr: Box::new(field_expr),
|
loc_expr: Box::new(field_expr),
|
||||||
};
|
};
|
||||||
|
|
||||||
can_fields.insert(label, field);
|
let replaced = can_fields.insert(label.clone(), field);
|
||||||
|
|
||||||
|
if replaced.is_some() {
|
||||||
|
env.problems.push(Problem::DuplicateRecordFieldValue {
|
||||||
|
field_name: label,
|
||||||
|
field_region: loc_field.region,
|
||||||
|
record_region: region,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
output.references = output.references.union(field_out.references);
|
output.references = output.references.union(field_out.references);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use inlinable_string::InlinableString;
|
use inlinable_string::InlinableString;
|
||||||
use roc_collections::all::MutSet;
|
use roc_collections::all::MutSet;
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_parse::operator::BinOp;
|
use roc_parse::operator::BinOp;
|
||||||
use roc_parse::pattern::PatternType;
|
use roc_parse::pattern::PatternType;
|
||||||
|
@ -21,6 +21,27 @@ pub enum Problem {
|
||||||
original_region: Region,
|
original_region: Region,
|
||||||
shadow: Located<Ident>,
|
shadow: Located<Ident>,
|
||||||
},
|
},
|
||||||
|
CyclicAlias(Symbol, Region, Vec<Symbol>),
|
||||||
|
PhantomTypeArgument {
|
||||||
|
alias: Symbol,
|
||||||
|
variable_region: Region,
|
||||||
|
variable_name: Lowercase,
|
||||||
|
},
|
||||||
|
DuplicateRecordFieldValue {
|
||||||
|
field_name: Lowercase,
|
||||||
|
record_region: Region,
|
||||||
|
field_region: Region,
|
||||||
|
},
|
||||||
|
DuplicateRecordFieldType {
|
||||||
|
field_name: Lowercase,
|
||||||
|
record_region: Region,
|
||||||
|
field_region: Region,
|
||||||
|
},
|
||||||
|
DuplicateTag {
|
||||||
|
tag_name: TagName,
|
||||||
|
tag_union_region: Region,
|
||||||
|
tag_region: Region,
|
||||||
|
},
|
||||||
RuntimeError(RuntimeError),
|
RuntimeError(RuntimeError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -401,6 +401,91 @@ pub fn can_problem<'b>(
|
||||||
shadow,
|
shadow,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Problem::CyclicAlias(symbol, region, others) => {
|
||||||
|
if others.is_empty() {
|
||||||
|
todo!("cyclic alias")
|
||||||
|
} else {
|
||||||
|
alloc.stack(vec![
|
||||||
|
alloc
|
||||||
|
.reflow("The ")
|
||||||
|
.append(alloc.symbol_unqualified(symbol))
|
||||||
|
.append(alloc.reflow(" alias is recursive in an invalid way:")),
|
||||||
|
alloc.region(region),
|
||||||
|
alloc
|
||||||
|
.reflow("The ")
|
||||||
|
.append(alloc.symbol_unqualified(symbol))
|
||||||
|
.append(alloc.reflow(
|
||||||
|
" alias depends on itself through the following chain of definitions:",
|
||||||
|
)),
|
||||||
|
cycle(
|
||||||
|
alloc,
|
||||||
|
4,
|
||||||
|
alloc.symbol_unqualified(symbol),
|
||||||
|
others
|
||||||
|
.into_iter()
|
||||||
|
.map(|other| alloc.symbol_unqualified(other))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
),
|
||||||
|
alloc.reflow(
|
||||||
|
"Recursion in aliases is only allowed if recursion happens behind a tag.",
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Problem::PhantomTypeArgument {
|
||||||
|
alias,
|
||||||
|
variable_region,
|
||||||
|
variable_name,
|
||||||
|
} => alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("The "),
|
||||||
|
alloc.type_variable(variable_name),
|
||||||
|
alloc.reflow(" type variable is not used in the "),
|
||||||
|
alloc.symbol_unqualified(alias),
|
||||||
|
alloc.reflow(" alias definition:"),
|
||||||
|
]),
|
||||||
|
alloc.region(variable_region),
|
||||||
|
alloc.reflow("Roc does not allow phantom type parameters!"),
|
||||||
|
]),
|
||||||
|
Problem::DuplicateRecordFieldValue {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
record_region,
|
||||||
|
} => alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("This record defines the "),
|
||||||
|
alloc.record_field(field_name),
|
||||||
|
alloc.reflow(" field twice!"),
|
||||||
|
]),
|
||||||
|
alloc.region_with_subregion(record_region, field_region),
|
||||||
|
alloc.reflow("In the rest of the program, I will use the second definition."),
|
||||||
|
]),
|
||||||
|
Problem::DuplicateRecordFieldType {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
record_region,
|
||||||
|
} => alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("This annotation defines the "),
|
||||||
|
alloc.record_field(field_name),
|
||||||
|
alloc.reflow(" field twice!"),
|
||||||
|
]),
|
||||||
|
alloc.region_with_subregion(record_region, field_region),
|
||||||
|
alloc.reflow("In the rest of the program, I will use the second definition."),
|
||||||
|
]),
|
||||||
|
Problem::DuplicateTag {
|
||||||
|
tag_name,
|
||||||
|
tag_union_region,
|
||||||
|
tag_region,
|
||||||
|
} => alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("This annotation defines the "),
|
||||||
|
alloc.tag_name(tag_name),
|
||||||
|
alloc.reflow(" tag twice!"),
|
||||||
|
]),
|
||||||
|
alloc.region_with_subregion(tag_union_region, tag_region),
|
||||||
|
alloc.reflow("In the rest of the program, I will use the second definition."),
|
||||||
|
]),
|
||||||
Problem::RuntimeError(runtime_error) => pretty_runtime_error(alloc, runtime_error),
|
Problem::RuntimeError(runtime_error) => pretty_runtime_error(alloc, runtime_error),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -711,6 +796,7 @@ impl<'a> RocDocAllocator<'a> {
|
||||||
self.text(content.to_string()).annotate(Annotation::BinOp)
|
self.text(content.to_string()).annotate(Annotation::BinOp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Turns of backticks/colors in a block
|
||||||
pub fn type_block(
|
pub fn type_block(
|
||||||
&'a self,
|
&'a self,
|
||||||
content: DocBuilder<'a, Self, Annotation>,
|
content: DocBuilder<'a, Self, Annotation>,
|
||||||
|
@ -731,6 +817,11 @@ impl<'a> RocDocAllocator<'a> {
|
||||||
) -> DocBuilder<'a, Self, Annotation> {
|
) -> DocBuilder<'a, Self, Annotation> {
|
||||||
debug_assert!(region.contains(&sub_region));
|
debug_assert!(region.contains(&sub_region));
|
||||||
|
|
||||||
|
// If the outer region takes more than 1 full screen (~60 lines), only show the inner region
|
||||||
|
if region.end_line - region.start_line > 60 {
|
||||||
|
return self.region_with_subregion(sub_region, sub_region);
|
||||||
|
}
|
||||||
|
|
||||||
// if true, the final line of the snippet will be some ^^^ that point to the region where
|
// if true, the final line of the snippet will be some ^^^ that point to the region where
|
||||||
// the problem is. Otherwise, the snippet will have a > on the lines that are in the regon
|
// the problem is. Otherwise, the snippet will have a > on the lines that are in the regon
|
||||||
// where the problem is.
|
// where the problem is.
|
||||||
|
|
|
@ -29,6 +29,54 @@ pub fn type_problem<'b>(
|
||||||
CircularType(region, symbol, overall_type) => {
|
CircularType(region, symbol, overall_type) => {
|
||||||
to_circular_report(alloc, filename, region, symbol, overall_type)
|
to_circular_report(alloc, filename, region, symbol, overall_type)
|
||||||
}
|
}
|
||||||
|
BadType(type_problem) => {
|
||||||
|
use roc_types::types::Problem::*;
|
||||||
|
match type_problem {
|
||||||
|
BadTypeArguments {
|
||||||
|
symbol,
|
||||||
|
region,
|
||||||
|
type_got,
|
||||||
|
alias_needs,
|
||||||
|
} => {
|
||||||
|
let needed_arguments = if alias_needs == 1 {
|
||||||
|
alloc.reflow("1 type argument")
|
||||||
|
} else {
|
||||||
|
alloc
|
||||||
|
.text(alias_needs.to_string())
|
||||||
|
.append(alloc.reflow(" type arguments"))
|
||||||
|
};
|
||||||
|
|
||||||
|
let found_arguments = alloc.text(type_got.to_string());
|
||||||
|
|
||||||
|
let doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("The "),
|
||||||
|
alloc.symbol_unqualified(symbol),
|
||||||
|
alloc.reflow(" alias expects "),
|
||||||
|
needed_arguments,
|
||||||
|
alloc.reflow(", but it got "),
|
||||||
|
found_arguments,
|
||||||
|
alloc.reflow(" instead:"),
|
||||||
|
]),
|
||||||
|
alloc.region(region),
|
||||||
|
alloc.reflow("Are there missing parentheses?"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let title = if type_got > alias_needs {
|
||||||
|
"TOO MANY TYPE ARGUMENTS".to_string()
|
||||||
|
} else {
|
||||||
|
"TOO FEW TYPE ARGUMENTS".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
title,
|
||||||
|
doc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => panic!("unhandled bad type: {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2257,4 +2257,286 @@ mod test_reporting {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn circular_alias() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Foo : { x: Bar }
|
||||||
|
Bar : { y : Foo }
|
||||||
|
|
||||||
|
f : Foo
|
||||||
|
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
// should not report Bar as unused!
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
The `Bar` alias is recursive in an invalid way:
|
||||||
|
|
||||||
|
2 ┆ Bar : { y : Foo }
|
||||||
|
┆ ^^^^^^^^^^^
|
||||||
|
|
||||||
|
The `Bar` alias depends on itself through the following chain of
|
||||||
|
definitions:
|
||||||
|
|
||||||
|
┌─────┐
|
||||||
|
│ Bar
|
||||||
|
│ ↓
|
||||||
|
│ Foo
|
||||||
|
└─────┘
|
||||||
|
|
||||||
|
Recursion in aliases is only allowed if recursion happens behind a
|
||||||
|
tag.
|
||||||
|
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
`Bar` is not used anywhere in your code.
|
||||||
|
|
||||||
|
2 ┆ Bar : { y : Foo }
|
||||||
|
┆ ^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
If you didn't intend on using `Bar` then remove it so future readers of
|
||||||
|
your code don't wonder why it is there.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_duplicate_field_same_type() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 4, y: 3, x: 4 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
This record defines the `.x` field twice!
|
||||||
|
|
||||||
|
1 ┆ { x: 4, y: 3, x: 4 }
|
||||||
|
┆ ^^^^
|
||||||
|
|
||||||
|
In the rest of the program, I will use the second definition.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_duplicate_field_different_types() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 4, y: 3, x: "foo" }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
This record defines the `.x` field twice!
|
||||||
|
|
||||||
|
1 ┆ { x: 4, y: 3, x: "foo" }
|
||||||
|
┆ ^^^^^^^^
|
||||||
|
|
||||||
|
In the rest of the program, I will use the second definition.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_type_duplicate_field() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
a : { foo : Int, bar : Float, foo : Str }
|
||||||
|
a = { bar: 3.0, foo: "foo" }
|
||||||
|
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
This annotation defines the `.foo` field twice!
|
||||||
|
|
||||||
|
1 ┆ a : { foo : Int, bar : Float, foo : Str }
|
||||||
|
┆ ^^^^^^^^^
|
||||||
|
|
||||||
|
In the rest of the program, I will use the second definition.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tag_union_duplicate_tag() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
a : [ Foo Int, Bar Float, Foo Str ]
|
||||||
|
a = Foo "foo"
|
||||||
|
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
This annotation defines the `Foo` tag twice!
|
||||||
|
|
||||||
|
1 ┆ a : [ Foo Int, Bar Float, Foo Str ]
|
||||||
|
┆ ^^^^^^^
|
||||||
|
|
||||||
|
In the rest of the program, I will use the second definition.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_num() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
a : Num Int Float
|
||||||
|
a = 3
|
||||||
|
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- TOO MANY TYPE ARGUMENTS -----------------------------------------------------
|
||||||
|
|
||||||
|
The `Num` alias expects 1 type argument, but it got 2 instead:
|
||||||
|
|
||||||
|
1 ┆ a : Num Int Float
|
||||||
|
┆ ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Are there missing parentheses?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_num_fn() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
f : Bool -> Num Int Float
|
||||||
|
f = \_ -> 3
|
||||||
|
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- TOO MANY TYPE ARGUMENTS -----------------------------------------------------
|
||||||
|
|
||||||
|
The `Num` alias expects 1 type argument, but it got 2 instead:
|
||||||
|
|
||||||
|
1 ┆ f : Bool -> Num Int Float
|
||||||
|
┆ ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Are there missing parentheses?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn too_few_type_arguments() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Pair a b : [ Pair a b ]
|
||||||
|
|
||||||
|
x : Pair Int
|
||||||
|
x = 3
|
||||||
|
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- TOO FEW TYPE ARGUMENTS ------------------------------------------------------
|
||||||
|
|
||||||
|
The `Pair` alias expects 2 type arguments, but it got 1 instead:
|
||||||
|
|
||||||
|
3 ┆ x : Pair Int
|
||||||
|
┆ ^^^^^^^^
|
||||||
|
|
||||||
|
Are there missing parentheses?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn too_many_type_arguments() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Pair a b : [ Pair a b ]
|
||||||
|
|
||||||
|
x : Pair Int Int Int
|
||||||
|
x = 3
|
||||||
|
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- TOO MANY TYPE ARGUMENTS -----------------------------------------------------
|
||||||
|
|
||||||
|
The `Pair` alias expects 2 type arguments, but it got 3 instead:
|
||||||
|
|
||||||
|
3 ┆ x : Pair Int Int Int
|
||||||
|
┆ ^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Are there missing parentheses?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn phantom_type_variable() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Foo a : [ Foo ]
|
||||||
|
|
||||||
|
f : Foo Int
|
||||||
|
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
-- SYNTAX PROBLEM --------------------------------------------------------------
|
||||||
|
|
||||||
|
The `a` type variable is not used in the `Foo` alias definition:
|
||||||
|
|
||||||
|
1 ┆ Foo a : [ Foo ]
|
||||||
|
┆ ^
|
||||||
|
|
||||||
|
Roc does not allow phantom type parameters!
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub enum TypeError {
|
||||||
BadExpr(Region, Category, ErrorType, Expected<ErrorType>),
|
BadExpr(Region, Category, ErrorType, Expected<ErrorType>),
|
||||||
BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>),
|
BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>),
|
||||||
CircularType(Region, Symbol, ErrorType),
|
CircularType(Region, Symbol, ErrorType),
|
||||||
|
BadType(roc_types::types::Problem),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SubsByModule = MutMap<ModuleId, ExposedModuleTypes>;
|
pub type SubsByModule = MutMap<ModuleId, ExposedModuleTypes>;
|
||||||
|
@ -166,6 +167,13 @@ fn solve(
|
||||||
|
|
||||||
problems.push(problem);
|
problems.push(problem);
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
BadType(vars, problem) => {
|
||||||
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
|
problems.push(TypeError::BadType(problem));
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,6 +236,13 @@ fn solve(
|
||||||
|
|
||||||
problems.push(problem);
|
problems.push(problem);
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
BadType(vars, problem) => {
|
||||||
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
|
problems.push(TypeError::BadType(problem));
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,6 +293,13 @@ fn solve(
|
||||||
|
|
||||||
problems.push(problem);
|
problems.push(problem);
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
BadType(vars, problem) => {
|
||||||
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
|
problems.push(TypeError::BadType(problem));
|
||||||
|
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -829,7 +851,7 @@ fn circular_error(
|
||||||
loc_var: &Located<Variable>,
|
loc_var: &Located<Variable>,
|
||||||
) {
|
) {
|
||||||
let var = loc_var.value;
|
let var = loc_var.value;
|
||||||
let error_type = subs.var_to_error_type(var);
|
let (error_type, _) = subs.var_to_error_type(var);
|
||||||
let problem = TypeError::CircularType(loc_var.region, symbol, error_type);
|
let problem = TypeError::CircularType(loc_var.region, symbol, error_type);
|
||||||
|
|
||||||
subs.set_content(var, Content::Error);
|
subs.set_content(var, Content::Error);
|
||||||
|
|
|
@ -37,9 +37,10 @@ impl fmt::Debug for Mark {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct NameState {
|
struct ErrorTypeState {
|
||||||
taken: MutSet<Lowercase>,
|
taken: MutSet<Lowercase>,
|
||||||
normals: u32,
|
normals: u32,
|
||||||
|
problems: Vec<crate::types::Problem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
|
@ -363,7 +364,7 @@ impl Subs {
|
||||||
explicit_substitute(self, x, y, z, &mut seen)
|
explicit_substitute(self, x, y, z, &mut seen)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn var_to_error_type(&mut self, var: Variable) -> ErrorType {
|
pub fn var_to_error_type(&mut self, var: Variable) -> (ErrorType, Vec<crate::types::Problem>) {
|
||||||
let names = get_var_names(self, var, ImMap::default());
|
let names = get_var_names(self, var, ImMap::default());
|
||||||
let mut taken = MutSet::default();
|
let mut taken = MutSet::default();
|
||||||
|
|
||||||
|
@ -371,9 +372,13 @@ impl Subs {
|
||||||
taken.insert(name);
|
taken.insert(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut state = NameState { taken, normals: 0 };
|
let mut state = ErrorTypeState {
|
||||||
|
taken,
|
||||||
|
normals: 0,
|
||||||
|
problems: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
var_to_err_type(self, &mut state, var)
|
(var_to_err_type(self, &mut state, var), state.problems)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore(&mut self, var: Variable) {
|
pub fn restore(&mut self, var: Variable) {
|
||||||
|
@ -1114,7 +1119,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn var_to_err_type(subs: &mut Subs, state: &mut NameState, var: Variable) -> ErrorType {
|
fn var_to_err_type(subs: &mut Subs, state: &mut ErrorTypeState, var: Variable) -> ErrorType {
|
||||||
let desc = subs.get(var);
|
let desc = subs.get(var);
|
||||||
|
|
||||||
if desc.mark == Mark::OCCURS {
|
if desc.mark == Mark::OCCURS {
|
||||||
|
@ -1132,7 +1137,7 @@ fn var_to_err_type(subs: &mut Subs, state: &mut NameState, var: Variable) -> Err
|
||||||
|
|
||||||
fn content_to_err_type(
|
fn content_to_err_type(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
state: &mut NameState,
|
state: &mut ErrorTypeState,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
content: Content,
|
content: Content,
|
||||||
) -> ErrorType {
|
) -> ErrorType {
|
||||||
|
@ -1174,7 +1179,11 @@ fn content_to_err_type(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flat_type_to_err_type(subs: &mut Subs, state: &mut NameState, flat_type: FlatType) -> ErrorType {
|
fn flat_type_to_err_type(
|
||||||
|
subs: &mut Subs,
|
||||||
|
state: &mut ErrorTypeState,
|
||||||
|
flat_type: FlatType,
|
||||||
|
) -> ErrorType {
|
||||||
use self::FlatType::*;
|
use self::FlatType::*;
|
||||||
|
|
||||||
match flat_type {
|
match flat_type {
|
||||||
|
@ -1296,11 +1305,15 @@ fn flat_type_to_err_type(subs: &mut Subs, state: &mut NameState, flat_type: Flat
|
||||||
|
|
||||||
Boolean(b) => ErrorType::Boolean(b),
|
Boolean(b) => ErrorType::Boolean(b),
|
||||||
|
|
||||||
Erroneous(_) => ErrorType::Error,
|
Erroneous(problem) => {
|
||||||
|
state.problems.push(problem);
|
||||||
|
|
||||||
|
ErrorType::Error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_fresh_var_name(state: &mut NameState) -> Lowercase {
|
fn get_fresh_var_name(state: &mut ErrorTypeState) -> Lowercase {
|
||||||
let (name, new_index) = name_type_var(state.normals, &mut state.taken);
|
let (name, new_index) = name_type_var(state.normals, &mut state.taken);
|
||||||
|
|
||||||
state.normals = new_index;
|
state.normals = new_index;
|
||||||
|
|
|
@ -395,6 +395,7 @@ impl Type {
|
||||||
|
|
||||||
pub fn instantiate_aliases(
|
pub fn instantiate_aliases(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
region: Region,
|
||||||
aliases: &ImMap<Symbol, Alias>,
|
aliases: &ImMap<Symbol, Alias>,
|
||||||
var_store: &VarStore,
|
var_store: &VarStore,
|
||||||
introduced: &mut ImSet<Variable>,
|
introduced: &mut ImSet<Variable>,
|
||||||
|
@ -404,34 +405,44 @@ impl Type {
|
||||||
match self {
|
match self {
|
||||||
Function(args, ret) => {
|
Function(args, ret) => {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg.instantiate_aliases(aliases, var_store, introduced);
|
arg.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
ret.instantiate_aliases(aliases, var_store, introduced);
|
ret.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
for x in args {
|
for x in args {
|
||||||
x.instantiate_aliases(aliases, var_store, introduced);
|
x.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ext.instantiate_aliases(aliases, var_store, introduced);
|
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
Record(fields, ext) => {
|
Record(fields, ext) => {
|
||||||
for x in fields.iter_mut() {
|
for x in fields.iter_mut() {
|
||||||
x.instantiate_aliases(aliases, var_store, introduced);
|
x.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
ext.instantiate_aliases(aliases, var_store, introduced);
|
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
Alias(_, type_args, actual_type) => {
|
Alias(_, type_args, actual_type) => {
|
||||||
for arg in type_args {
|
for arg in type_args {
|
||||||
arg.1.instantiate_aliases(aliases, var_store, introduced);
|
arg.1
|
||||||
|
.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
actual_type.instantiate_aliases(aliases, var_store, introduced);
|
actual_type.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
if let Some(alias) = aliases.get(symbol) {
|
if let Some(alias) = aliases.get(symbol) {
|
||||||
debug_assert!(args.len() == alias.vars.len());
|
if args.len() != alias.vars.len() {
|
||||||
|
*self = Type::Erroneous(Problem::BadTypeArguments {
|
||||||
|
symbol: *symbol,
|
||||||
|
region,
|
||||||
|
type_got: args.len() as u8,
|
||||||
|
alias_needs: alias.vars.len() as u8,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut actual = alias.typ.clone();
|
let mut actual = alias.typ.clone();
|
||||||
|
|
||||||
let mut named_args = Vec::with_capacity(args.len());
|
let mut named_args = Vec::with_capacity(args.len());
|
||||||
|
@ -447,7 +458,7 @@ impl Type {
|
||||||
) in alias.vars.iter().zip(args.iter())
|
) in alias.vars.iter().zip(args.iter())
|
||||||
{
|
{
|
||||||
let mut filler = filler.clone();
|
let mut filler = filler.clone();
|
||||||
filler.instantiate_aliases(aliases, var_store, introduced);
|
filler.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
named_args.push((lowercase.clone(), filler.clone()));
|
named_args.push((lowercase.clone(), filler.clone()));
|
||||||
substitution.insert(*placeholder, filler);
|
substitution.insert(*placeholder, filler);
|
||||||
}
|
}
|
||||||
|
@ -463,7 +474,7 @@ impl Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
actual.substitute(&substitution);
|
actual.substitute(&substitution);
|
||||||
actual.instantiate_aliases(aliases, var_store, introduced);
|
actual.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
|
|
||||||
// instantiate recursion variable!
|
// instantiate recursion variable!
|
||||||
if let Type::RecursiveTagUnion(rec_var, mut tags, mut ext) = actual {
|
if let Type::RecursiveTagUnion(rec_var, mut tags, mut ext) = actual {
|
||||||
|
@ -487,7 +498,7 @@ impl Type {
|
||||||
} else {
|
} else {
|
||||||
// one of the special-cased Apply types.
|
// one of the special-cased Apply types.
|
||||||
for x in args {
|
for x in args {
|
||||||
x.instantiate_aliases(aliases, var_store, introduced);
|
x.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -691,11 +702,18 @@ pub struct Alias {
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
pub enum Problem {
|
pub enum Problem {
|
||||||
CanonicalizationProblem,
|
CanonicalizationProblem,
|
||||||
Mismatch(Mismatch, ErrorType, ErrorType),
|
|
||||||
CircularType(Symbol, ErrorType, Region),
|
CircularType(Symbol, ErrorType, Region),
|
||||||
|
CyclicAlias(Symbol, Region, Vec<Symbol>),
|
||||||
UnrecognizedIdent(InlinableString),
|
UnrecognizedIdent(InlinableString),
|
||||||
Shadowed(Region, Located<Ident>),
|
Shadowed(Region, Located<Ident>),
|
||||||
|
BadTypeArguments {
|
||||||
|
symbol: Symbol,
|
||||||
|
region: Region,
|
||||||
|
type_got: u8,
|
||||||
|
alias_needs: u8,
|
||||||
|
},
|
||||||
InvalidModule,
|
InvalidModule,
|
||||||
|
// Mismatch(Mismatch, ErrorType, ErrorType),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
|
|
|
@ -73,6 +73,7 @@ struct Context {
|
||||||
pub enum Unified {
|
pub enum Unified {
|
||||||
Success(Pool),
|
Success(Pool),
|
||||||
Failure(Pool, ErrorType, ErrorType),
|
Failure(Pool, ErrorType, ErrorType),
|
||||||
|
BadType(Pool, roc_types::types::Problem),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -91,11 +92,18 @@ pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) -> Unified {
|
||||||
if mismatches.is_empty() {
|
if mismatches.is_empty() {
|
||||||
Unified::Success(vars)
|
Unified::Success(vars)
|
||||||
} else {
|
} else {
|
||||||
let type1 = subs.var_to_error_type(var1);
|
let (type1, mut problems) = subs.var_to_error_type(var1);
|
||||||
let type2 = subs.var_to_error_type(var2);
|
let (type2, problems2) = subs.var_to_error_type(var2);
|
||||||
|
|
||||||
|
problems.extend(problems2);
|
||||||
|
|
||||||
subs.union(var1, var2, Content::Error.into());
|
subs.union(var1, var2, Content::Error.into());
|
||||||
Unified::Failure(vars, type1, type2)
|
|
||||||
|
if !problems.is_empty() {
|
||||||
|
Unified::BadType(vars, problems.remove(0))
|
||||||
|
} else {
|
||||||
|
Unified::Failure(vars, type1, type2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue