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:
KtorZ
2024-08-06 11:45:42 +02:00
parent 1ae6640cd0
commit 91e0e2493a
17 changed files with 119 additions and 128 deletions

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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(