Merge pull request #199 from aiken-lang/acceptance-test-021-type-alias-definition

Fix acceptance 021: allow registering type aliases in any order.
This commit is contained in:
Matthias Benkort 2022-12-21 10:07:03 +01:00 committed by GitHub
commit 127581a445
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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() {