Fix acceptance 021: allow registering type aliases in any order.

This is the most intuitive thing I could come up with: since the
  problem is mainly due to the order in which we try declaring the
  aliases, then it suffices to simply try as much as we can, and retry
  on failure until there's no more failure.

  Note that it's important to detect cycles if we do such thing (which
  we can by noticing that a given iteration didn't make any progress).

  It works pretty well in the end and even allow us to define a new kind
  of type error should there be a cyclic definition.
This commit is contained in:
KtorZ 2022-12-20 23:21:56 +01:00
parent a3591cc7dc
commit 28c907d9de
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
3 changed files with 82 additions and 5 deletions

View File

@ -823,6 +823,73 @@ impl<'a> Environment<'a> {
/// Iterate over a module, registering any new types created by the module into the typer /// Iterate over a module, registering any new types created by the module into the typer
pub fn register_types( pub fn register_types(
&mut self,
definitions: Vec<&'a UntypedDefinition>,
module: &String,
hydrators: &mut HashMap<String, Hydrator>,
names: &mut HashMap<&'a str, &'a Span>,
) -> Result<(), Error> {
let known_types_before = names.keys().copied().collect::<Vec<_>>();
let mut error = None;
let mut remaining_definitions = vec![];
// in case we failed at registering a type-definition, we backtrack and
// try again until either of:
//
// (a) we do not make any more progress;
// (b) there's no more errors.
//
// This is because some definition, especially when combining type-aliases may depend on
// types that we haven't yet seen (because declared later in the module). In which case, it
// would suffice to register type in a different order. Thus instead of failing on the
// first error, we try to register as many types as we can, recursively until we've
// exhausted all the definitions or, until we no longer make any progress (which may signal
// a cycle).
for def in definitions {
if let Err(e) = self.register_type(def, module, hydrators, names) {
error = Some(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 cycle = remaining_definitions
.into_iter()
.filter_map(|def| match def {
Definition::TypeAlias(TypeAlias {
alias, location, ..
}) => Some((alias.to_owned(), location.to_owned())),
_ => None,
})
.collect::<Vec<(String, Span)>>();
match cycle.first() {
None => Err(e),
Some((alias, location)) => {
let mut types =
cycle.iter().map(|def| def.0.clone()).collect::<Vec<_>>();
types.push(alias.clone());
Err(Error::CyclicTypeDefinitions {
location: *location,
types,
})
}
}
} else {
self.register_types(remaining_definitions, module, hydrators, names)
}
}
}
}
pub fn register_type(
&mut self, &mut self,
def: &'a UntypedDefinition, def: &'a UntypedDefinition,
module: &String, module: &String,
@ -885,11 +952,11 @@ impl<'a> Environment<'a> {
}) => { }) => {
assert_unique_type_name(names, name, location)?; assert_unique_type_name(names, name, location)?;
// Register the paramerterised types // Register the parameterised types
let mut hydrator = Hydrator::new(); let mut hydrator = Hydrator::new();
let parameters = self.make_type_vars(args, location, &mut hydrator)?; let parameters = self.make_type_vars(args, location, &mut hydrator)?;
// Disallow creation of new types outside the paramerterised types // Disallow creation of new types outside the parameterised types
hydrator.disallow_new_type_variables(); hydrator.disallow_new_type_variables();
// Create the type that the alias resolves to // Create the type that the alias resolves to

View File

@ -271,6 +271,13 @@ pub enum Error {
name: String, name: String,
}, },
#[error("Cyclic type definition detected: {}\n", types.join(" -> "))]
CyclicTypeDefinitions {
#[label]
location: Span,
types: Vec<String>,
},
#[error("Recursive type detected\n")] #[error("Recursive type detected\n")]
RecursiveType { RecursiveType {
#[label] #[label]

View File

@ -48,9 +48,12 @@ impl UntypedModule {
// Register types so they can be used in constructors and functions // Register types so they can be used in constructors and functions
// earlier in the module. // earlier in the module.
for def in self.definitions() { environment.register_types(
environment.register_types(def, &name, &mut hydrators, &mut type_names)?; self.definitions.iter().collect(),
} &name,
&mut hydrators,
&mut type_names,
)?;
// Register values so they can be used in functions earlier in the module. // Register values so they can be used in functions earlier in the module.
for def in self.definitions() { for def in self.definitions() {