Provide better errors on unknown type in cyclic definitions.
Let's consider the following case:
```
type Var =
Integer
type Vars =
List<Var>
```
This incorrectly reports an infinite cycle; due to the inability to
properly type-check `Var` which is also a dependent var of `Vars`. Yet
the real issue here being that `Integer` is an unknown type.
This commit also upgrades miette to 7.2.0, so that we can also display
a better error output when the problem is actually a cycle.
This commit is contained in:
@@ -19,7 +19,7 @@ hex = "0.4.3"
|
||||
indexmap = "1.9.2"
|
||||
indoc = "2.0.1"
|
||||
itertools = "0.10.5"
|
||||
miette = "5.9.0"
|
||||
miette.workspace = true
|
||||
num-bigint = "0.4.3"
|
||||
ordinal = "0.3.2"
|
||||
owo-colors = { version = "3.5.0", features = ["supports-colors"] }
|
||||
|
||||
@@ -1921,7 +1921,7 @@ pub struct Span {
|
||||
|
||||
impl From<Span> for miette::SourceSpan {
|
||||
fn from(span: Span) -> Self {
|
||||
Self::new(span.start.into(), (span.end - span.start).into())
|
||||
Self::new(span.start.into(), span.end - span.start)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::{
|
||||
error::{Error, Snippet, Warning},
|
||||
error::{Error, Warning},
|
||||
exhaustive::{simplify, Matrix, PatternStack},
|
||||
hydrator::Hydrator,
|
||||
AccessorsMap, RecordAccessor, Type, TypeConstructor, TypeInfo, TypeVar, ValueConstructor,
|
||||
@@ -972,7 +972,7 @@ impl<'a> Environment<'a> {
|
||||
) -> Result<(), Error> {
|
||||
let known_types_before = names.keys().copied().collect::<Vec<_>>();
|
||||
|
||||
let mut error = None;
|
||||
let mut errors = vec![];
|
||||
let mut remaining_definitions = vec![];
|
||||
|
||||
// in case we failed at registering a type-definition, we backtrack and
|
||||
@@ -989,60 +989,57 @@ impl<'a> Environment<'a> {
|
||||
// a cycle).
|
||||
for def in definitions {
|
||||
if let Err(e) = self.register_type(def, module, hydrators, names) {
|
||||
error = Some(e);
|
||||
let type_name = match def {
|
||||
Definition::TypeAlias(TypeAlias { alias, .. }) => {
|
||||
names.remove(alias.as_str());
|
||||
Some(alias)
|
||||
}
|
||||
Definition::DataType(DataType { name, .. }) => Some(name),
|
||||
_ => None,
|
||||
};
|
||||
errors.push((type_name, e));
|
||||
remaining_definitions.push(def);
|
||||
if let Definition::TypeAlias(TypeAlias { alias, .. }) = def {
|
||||
names.remove(alias.as_str());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match error {
|
||||
None => Ok(()),
|
||||
Some(e) => {
|
||||
let known_types_after = names.keys().copied().collect::<Vec<_>>();
|
||||
if known_types_before == known_types_after {
|
||||
let unknown_name = match e {
|
||||
Error::UnknownType { ref name, .. } => name,
|
||||
_ => "",
|
||||
};
|
||||
let mut is_cyclic = false;
|
||||
let unknown_types = remaining_definitions
|
||||
.into_iter()
|
||||
.filter_map(|def| match def {
|
||||
Definition::TypeAlias(TypeAlias {
|
||||
alias, location, ..
|
||||
}) => {
|
||||
is_cyclic = is_cyclic || alias == unknown_name;
|
||||
Some(Snippet {
|
||||
location: location.to_owned(),
|
||||
})
|
||||
}
|
||||
Definition::DataType(DataType { name, location, .. }) => {
|
||||
is_cyclic = is_cyclic || name == unknown_name;
|
||||
Some(Snippet {
|
||||
location: location.to_owned(),
|
||||
})
|
||||
}
|
||||
Definition::Fn { .. }
|
||||
| Definition::Validator { .. }
|
||||
| Definition::Use { .. }
|
||||
| Definition::ModuleConstant { .. }
|
||||
| Definition::Test { .. } => None,
|
||||
})
|
||||
.collect::<Vec<Snippet>>();
|
||||
if errors.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if is_cyclic {
|
||||
Err(Error::CyclicTypeDefinitions {
|
||||
errors: unknown_types,
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
let known_types_after = names.keys().copied().collect::<Vec<_>>();
|
||||
if known_types_before == known_types_after {
|
||||
let (type_definitions, mut unknowns): (Vec<_>, Vec<_>) = errors.into_iter().unzip();
|
||||
|
||||
let first_error = unknowns.first().cloned();
|
||||
|
||||
unknowns.retain(|err| {
|
||||
if let Error::UnknownType { ref name, .. } = err {
|
||||
!type_definitions.contains(&Some(name))
|
||||
} else {
|
||||
self.register_types(remaining_definitions, module, hydrators, names)
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if unknowns.is_empty() {
|
||||
let cycle = remaining_definitions
|
||||
.iter()
|
||||
.filter_map(|def| match def {
|
||||
Definition::TypeAlias(TypeAlias { location, .. })
|
||||
| Definition::DataType(DataType { location, .. }) => Some(*location),
|
||||
Definition::Fn { .. }
|
||||
| Definition::Validator { .. }
|
||||
| Definition::Use { .. }
|
||||
| Definition::ModuleConstant { .. }
|
||||
| Definition::Test { .. } => None,
|
||||
})
|
||||
.collect::<Vec<Span>>();
|
||||
|
||||
Err(Error::CyclicTypeDefinitions { cycle })
|
||||
} else {
|
||||
Err(first_error.unwrap())
|
||||
}
|
||||
} else {
|
||||
self.register_types(remaining_definitions, module, hydrators, names)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,6 @@ use owo_colors::{
|
||||
};
|
||||
use std::{collections::HashMap, fmt::Display, rc::Rc};
|
||||
|
||||
#[derive(Debug, thiserror::Error, Diagnostic, Clone)]
|
||||
#[error("Something is possibly wrong here...")]
|
||||
pub struct Snippet {
|
||||
#[label]
|
||||
pub location: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
#[error(
|
||||
"I don't know some of the labels used in this expression. I've highlighted them just below."
|
||||
@@ -100,8 +93,8 @@ pub enum Error {
|
||||
#[diagnostic(url("https://aiken-lang.org/language-tour/custom-types#type-aliases"))]
|
||||
#[diagnostic(code("cycle"))]
|
||||
CyclicTypeDefinitions {
|
||||
#[related]
|
||||
errors: Vec<Snippet>,
|
||||
#[label(collection, "part of a cycle")]
|
||||
cycle: Vec<Span>,
|
||||
},
|
||||
|
||||
#[error(
|
||||
|
||||
Reference in New Issue
Block a user