aiken/crates/aiken-lang/src/tipo/environment.rs

2123 lines
71 KiB
Rust

use super::{
error::{Error, Warning},
exhaustive::{simplify, Matrix, PatternStack},
hydrator::Hydrator,
AccessorsMap, RecordAccessor, Type, TypeConstructor, TypeInfo, TypeVar, ValueConstructor,
ValueConstructorVariant,
};
use crate::{
ast::{
self, Annotation, CallArg, DataType, Definition, Function, ModuleConstant, ModuleKind,
RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition, TypedFunction,
TypedPattern, TypedValidator, UnqualifiedImport, UntypedArg, UntypedDefinition,
UntypedFunction, Use, Validator, PIPE_VARIABLE,
},
tipo::{fields::FieldMap, TypeAliasAnnotation},
IdGenerator,
};
use std::{
collections::{HashMap, HashSet},
ops::Deref,
rc::Rc,
};
#[derive(Debug)]
pub struct ScopeResetData {
local_values: HashMap<String, ValueConstructor>,
}
#[derive(Debug)]
pub struct Environment<'a> {
/// Accessors defined in the current module
pub accessors: HashMap<String, AccessorsMap>,
pub current_module: &'a String,
pub current_kind: &'a ModuleKind,
/// entity_usages is a stack of scopes. When an entity is created it is
/// added to the top scope. When an entity is used we crawl down the scope
/// stack for an entity with that name and mark it as used.
/// NOTE: The bool in the tuple here tracks if the entity has been used
pub entity_usages: Vec<HashMap<String, (EntityKind, Span, bool)>>,
pub id_gen: IdGenerator,
pub importable_modules: &'a HashMap<String, TypeInfo>,
/// Modules that have been imported by the current module, along with the
/// location of the import statement where they were imported.
pub imported_modules: HashMap<String, (Span, &'a TypeInfo)>,
pub imported_types: HashSet<String>,
/// Types defined in the current module (or the prelude)
pub module_types: HashMap<String, TypeConstructor>,
/// Mapping from types to constructor names in the current module (or the prelude)
pub module_types_constructors: HashMap<String, Vec<String>>,
/// Values defined in the current module (or the prelude)
pub module_values: HashMap<String, ValueConstructor>,
/// Top-level function definitions from the module
pub module_functions: HashMap<String, &'a UntypedFunction>,
/// Top-level validator definitions from the module
pub module_validators: HashMap<String, (Span, Vec<String>)>,
/// Top-level functions that have been inferred
pub inferred_functions: HashMap<String, TypedFunction>,
previous_id: u64,
/// Values defined in the current function (or the prelude)
pub scope: HashMap<String, ValueConstructor>,
/// Functions that have not yet been inferred then generalised.
/// We use this to determine whether functions that call this one
/// can safely be generalised.
pub ungeneralised_functions: HashSet<String>,
/// Names of types or values that have been imported an unqualified fashion
/// from other modules. Used to prevent multiple imports using the same name.
pub unqualified_imported_names: HashMap<String, Span>,
pub unused_modules: HashMap<String, Span>,
/// A mapping from known annotations to their resolved type.
pub annotations: HashMap<Annotation, Rc<Type>>,
/// The user-defined target environment referred to as the module 'env'.
pub target_env: Option<&'a str>,
/// Warnings
pub warnings: &'a mut Vec<Warning>,
}
impl<'a> Environment<'a> {
pub fn find_module(&self, fragments: &[String], location: Span) -> Result<&'a TypeInfo, Error> {
let mut name = fragments.join("/");
let is_env = name == ast::ENV_MODULE;
if is_env {
name = self
.target_env
.unwrap_or(ast::DEFAULT_ENV_MODULE)
.to_string()
}
self.importable_modules.get(&name).ok_or_else(|| {
if is_env {
Error::UnknownEnvironment {
name,
known_environments: self
.importable_modules
.values()
.filter_map(|m| match m.kind {
ModuleKind::Env => Some(m.name.clone()),
ModuleKind::Lib | ModuleKind::Validator | ModuleKind::Config => None,
})
.collect(),
}
} else {
Error::UnknownModule {
location,
name,
known_modules: self.importable_modules.keys().cloned().collect(),
}
}
})
}
pub fn close_scope(&mut self, data: ScopeResetData) {
let unused = self
.entity_usages
.pop()
.expect("There was no top entity scope.");
self.handle_unused(unused);
self.scope = data.local_values;
}
pub fn annotate(&mut self, return_type: Rc<Type>, annotation: &Annotation) -> Rc<Type> {
self.annotations
.insert(annotation.clone(), return_type.clone());
return_type
}
/// Converts entities with a usage count of 0 to warnings
pub fn convert_unused_to_warnings(&mut self) {
let unused = self
.entity_usages
.pop()
.expect("Expected a bottom level of entity usages.");
self.handle_unused(unused);
for (name, location) in self.unused_modules.clone().into_iter() {
self.warnings
.push(Warning::UnusedImportedModule { name, location });
}
}
pub fn match_fun_type(
&mut self,
tipo: Rc<Type>,
arity: usize,
fn_location: Span,
call_location: Span,
) -> Result<(Vec<Rc<Type>>, Rc<Type>), Error> {
if let Type::Var { tipo, alias } = tipo.deref() {
let new_value = match tipo.borrow().deref() {
TypeVar::Link { tipo } => {
let (args, ret) =
self.match_fun_type(tipo.clone(), arity, fn_location, call_location)?;
return Ok((args, Type::with_alias(ret, alias.clone())));
}
TypeVar::Unbound { .. } => {
let args: Vec<_> = (0..arity).map(|_| self.new_unbound_var()).collect();
let ret = Type::with_alias(self.new_unbound_var(), alias.clone());
Some((args, ret))
}
TypeVar::Generic { .. } => None,
};
if let Some((args, ret)) = new_value {
*tipo.borrow_mut() = TypeVar::Link {
tipo: Type::function(args.clone(), ret.clone()),
};
return Ok((args, Type::with_alias(ret, alias.clone())));
}
}
if let Type::Fn {
args,
ret,
alias: _,
} = tipo.deref()
{
return if args.len() != arity {
Err(Error::IncorrectFunctionCallArity {
expected: args.len(),
given: arity,
location: call_location,
})
} else {
Ok((args.clone(), ret.clone()))
};
}
Err(Error::NotFn {
tipo,
location: fn_location,
})
}
fn custom_type_accessors<A>(
&mut self,
constructors: &[RecordConstructor<A>],
hydrator: &mut Hydrator,
) -> Result<Option<HashMap<String, RecordAccessor>>, Error> {
let args = get_compatible_record_fields(constructors);
let mut fields = HashMap::with_capacity(args.len());
hydrator.disallow_new_type_variables();
for (index, label, ast) in args {
let tipo = hydrator.type_from_annotation(ast, self)?;
fields.insert(
label.to_string(),
RecordAccessor {
index: index as u64,
label: label.to_string(),
tipo,
},
);
}
Ok(Some(fields))
}
pub fn generalise_definition(
&mut self,
s: TypedDefinition,
module_name: &String,
) -> TypedDefinition {
match s {
Definition::Fn(Function {
doc,
location,
name,
public,
arguments: args,
body,
return_annotation,
return_type,
end_position,
on_test_failure,
}) => {
// Lookup the inferred function information
let function = self
.get_variable(&name)
.expect("Could not find preregistered type for function");
let field_map = function.field_map().cloned();
let tipo = function.tipo.clone();
// Generalise the function if not already done so
let tipo = if self.ungeneralised_functions.remove(&name) {
generalise(tipo, 0)
} else {
tipo
};
// Insert the function into the module's interface
self.insert_module_value(
&name,
ValueConstructor {
public,
tipo,
variant: ValueConstructorVariant::ModuleFn {
name: name.clone(),
field_map,
module: module_name.to_owned(),
arity: args.len(),
location,
builtin: None,
},
},
);
Definition::Fn(Function {
doc,
location,
name,
public,
arguments: args,
return_annotation,
return_type,
body,
end_position,
on_test_failure,
})
}
Definition::Validator(Validator {
doc,
end_position,
handlers,
name,
mut fallback,
location,
params,
}) => {
let handlers = handlers
.into_iter()
.map(|mut fun| {
let handler_name = TypedValidator::handler_name(&name, &fun.name);
let old_name = fun.name;
fun.name = handler_name;
let Definition::Fn(mut fun) =
self.generalise_definition(Definition::Fn(fun), module_name)
else {
unreachable!()
};
fun.name = old_name;
fun
})
.collect();
let fallback_name = TypedValidator::handler_name(&name, &fallback.name);
let old_name = fallback.name;
fallback.name = fallback_name;
let Definition::Fn(mut fallback) =
self.generalise_definition(Definition::Fn(fallback), module_name)
else {
unreachable!()
};
fallback.name = old_name;
Definition::Validator(Validator {
doc,
name,
end_position,
handlers,
fallback,
location,
params,
})
}
definition @ (Definition::TypeAlias { .. }
| Definition::DataType { .. }
| Definition::Use { .. }
| Definition::Test { .. }
| Definition::ModuleConstant { .. }) => definition,
}
}
pub fn get_type_constructor_mut(
&mut self,
name: &str,
location: Span,
) -> Result<&mut TypeConstructor, Error> {
let types = self.module_types.keys().map(|t| t.to_string()).collect();
let constructor = self
.module_types
.get_mut(name)
.ok_or_else(|| Error::UnknownType {
location,
name: name.to_string(),
types,
})?;
Ok(constructor)
}
/// Lookup a type in the current scope.
pub fn get_type_constructor(
&mut self,
module_alias: &Option<String>,
name: &str,
location: Span,
) -> Result<&TypeConstructor, Error> {
match module_alias {
None => self
.module_types
.get(name)
.ok_or_else(|| Error::UnknownType {
location,
name: name.to_string(),
types: self.module_types.keys().map(|t| t.to_string()).collect(),
}),
Some(m) => {
let (_, module) =
self.imported_modules
.get(m)
.ok_or_else(|| Error::UnknownModule {
location,
name: name.to_string(),
known_modules: self
.importable_modules
.keys()
.map(|t| t.to_string())
.collect(),
})?;
self.unused_modules.remove(m);
module
.types
.get(name)
.ok_or_else(|| Error::UnknownModuleType {
location,
name: name.to_string(),
module_name: module.name.clone(),
type_constructors: module.types.keys().map(|t| t.to_string()).collect(),
})
}
}
}
/// Lookup a value constructor in the current scope.
///
pub fn get_value_constructor(
&mut self,
module: Option<&String>,
name: &str,
location: Span,
) -> Result<&ValueConstructor, Error> {
match module {
None => self
.scope
.get(name)
.ok_or_else(|| Error::UnknownTypeConstructor {
location,
name: name.to_string(),
constructors: self.local_constructor_names(),
}),
Some(m) => {
let (_, module) =
self.imported_modules
.get(m)
.ok_or_else(|| Error::UnknownModule {
name: m.to_string(),
known_modules: self
.importable_modules
.keys()
.map(|t| t.to_string())
.collect(),
location,
})?;
self.unused_modules.remove(m);
module
.values
.get(name)
.ok_or_else(|| Error::UnknownModuleValue {
name: name.to_string(),
module_name: module.name.clone(),
value_constructors: module.values.keys().map(|t| t.to_string()).collect(),
location,
})
}
}
}
/// Lookup a variable in the current scope.
pub fn get_variable(&self, name: &str) -> Option<&ValueConstructor> {
self.scope.get(name)
}
fn handle_unused(&mut self, unused: HashMap<String, (EntityKind, Span, bool)>) {
for (name, (kind, location, _)) in unused.into_iter().filter(|(_, (_, _, used))| !used) {
let warning = match kind {
EntityKind::ImportedType
| EntityKind::ImportedTypeAndConstructor
| EntityKind::ImportedConstructor
| EntityKind::ImportedValue => {
Warning::UnusedImportedValueOrType { name, location }
}
EntityKind::PrivateConstant => {
Warning::UnusedPrivateModuleConstant { name, location }
}
EntityKind::PrivateTypeConstructor(_) => {
Warning::UnusedConstructor { name, location }
}
EntityKind::PrivateFunction => Warning::UnusedPrivateFunction { name, location },
EntityKind::PrivateType => Warning::UnusedType { name, location },
EntityKind::Variable => Warning::UnusedVariable { name, location },
};
self.warnings.push(warning);
}
}
pub fn in_new_scope<T>(&mut self, process_scope: impl FnOnce(&mut Self) -> T) -> T {
// Record initial scope state
let initial = self.open_new_scope();
// Process scope
let result = process_scope(self);
self.close_scope(initial);
// Return result of typing the scope
result
}
/// Increments an entity's usage in the current or nearest enclosing scope
pub fn increment_usage(&mut self, name: &str) {
let mut name = name.to_string();
while let Some((kind, _, used)) = self
.entity_usages
.iter_mut()
.rev()
.find_map(|scope| scope.get_mut(&name))
{
*used = true;
match kind {
// If a type constructor is used, we consider its type also used
EntityKind::PrivateTypeConstructor(type_name) if type_name != &name => {
name.clone_from(type_name);
}
_ => return,
}
}
}
/// Inserts an entity at the current scope for usage tracking.
pub fn init_usage(&mut self, name: String, kind: EntityKind, location: Span) {
use EntityKind::*;
match self
.entity_usages
.last_mut()
.expect("Attempted to access non-existent entity usages scope")
.insert(name.to_string(), (kind, location, false))
{
// Private types can be shadowed by a constructor with the same name
//
// TODO: Improve this so that we can tell if an imported overridden
// type is actually used or not by tracking whether usages apply to
// the value or type scope
Some((ImportedType | ImportedTypeAndConstructor | PrivateType, _, _)) => (),
Some((kind, location, false)) => {
// an entity was overwritten in the top most scope without being used
let mut unused = HashMap::with_capacity(1);
unused.insert(name, (kind, location, false));
self.handle_unused(unused);
}
_ => (),
}
}
pub fn insert_accessors(&mut self, type_name: &str, accessors: AccessorsMap) {
self.accessors.insert(type_name.to_string(), accessors);
}
/// Insert a value into the current module.
/// Errors if the module already has a value with that name.
pub fn insert_module_value(&mut self, name: &str, value: ValueConstructor) {
self.module_values.insert(name.to_string(), value);
}
/// Map a type in the current scope. Errors if the module
/// already has a type with that name, unless the type is
/// from the prelude.
pub fn insert_type_constructor(
&mut self,
type_name: String,
info: TypeConstructor,
) -> Result<(), Error> {
let name = type_name.clone();
let location = info.location;
match self.module_types.insert(type_name, info) {
None => Ok(()),
Some(prelude_type) if prelude_type.module.is_empty() => Ok(()),
Some(previous) => Err(Error::DuplicateTypeName {
name,
location,
previous_location: previous.location,
}),
}
}
/// Map a type to constructors in the current scope.
pub fn insert_type_to_constructors(&mut self, type_name: String, constructors: Vec<String>) {
self.module_types_constructors
.insert(type_name, constructors);
}
/// Insert a variable in the current scope.
pub fn insert_variable(
&mut self,
name: String,
variant: ValueConstructorVariant,
tipo: Rc<Type>,
) {
self.scope.insert(
name,
ValueConstructor {
public: false,
variant,
tipo,
},
);
}
/// Instantiate converts generic variables into unbound ones.
pub fn instantiate(
&mut self,
t: Rc<Type>,
ids: &mut HashMap<u64, Rc<Type>>,
hydrator: &Hydrator,
) -> Rc<Type> {
match t.deref() {
Type::App {
public,
contains_opaque: opaque,
name,
module,
args,
alias,
} => {
let args = args
.iter()
.map(|t| self.instantiate(t.clone(), ids, hydrator))
.collect();
Rc::new(Type::App {
public: *public,
contains_opaque: *opaque,
name: name.clone(),
module: module.clone(),
alias: alias.clone(),
args,
})
}
Type::Var { tipo, alias } => {
match tipo.borrow().deref() {
TypeVar::Link { tipo } => {
return Type::with_alias(
self.instantiate(tipo.clone(), ids, hydrator),
alias.clone(),
);
}
TypeVar::Unbound { .. } => {
return Rc::new(Type::Var {
tipo: tipo.clone(),
alias: alias.clone(),
});
}
TypeVar::Generic { id } => match ids.get(id) {
Some(t) => return Type::with_alias(t.clone(), alias.clone()),
None => {
if !hydrator.is_rigid(id) {
// Check this in the hydrator, i.e. is it a created type
let v = Type::with_alias(self.new_unbound_var(), alias.clone());
ids.insert(*id, v.clone());
return v;
}
}
},
}
Rc::new(Type::Var {
tipo: tipo.clone(),
alias: alias.clone(),
})
}
Type::Fn { args, ret, alias } => Type::with_alias(
Type::function(
args.iter()
.map(|t| self.instantiate(t.clone(), ids, hydrator))
.collect(),
self.instantiate(ret.clone(), ids, hydrator),
),
alias.clone(),
),
Type::Tuple { elems, alias } => Type::with_alias(
Type::tuple(
elems
.iter()
.map(|t| self.instantiate(t.clone(), ids, hydrator))
.collect(),
),
alias.clone(),
),
Type::Pair { fst, snd, alias } => Type::with_alias(
Type::pair(
self.instantiate(fst.clone(), ids, hydrator),
self.instantiate(snd.clone(), ids, hydrator),
),
alias.clone(),
),
}
}
pub fn local_value_names(&self) -> Vec<String> {
self.scope
.keys()
.filter(|&t| PIPE_VARIABLE != t)
.map(|t| t.to_string())
.collect()
}
pub fn local_constructor_names(&self) -> Vec<String> {
self.scope
.keys()
.filter(|&t| t.chars().next().unwrap_or_default().is_uppercase())
.map(|t| t.to_string())
.collect()
}
fn make_type_vars(
&mut self,
args: &[String],
location: &Span,
hydrator: &mut Hydrator,
) -> Result<Vec<Rc<Type>>, Error> {
let mut type_vars = Vec::new();
for arg in args {
let annotation = Annotation::Var {
location: *location,
name: arg.to_string(),
};
let tipo = hydrator.type_from_annotation(&annotation, self)?;
type_vars.push(tipo);
}
Ok(type_vars)
}
pub fn new(
id_gen: IdGenerator,
current_module: &'a String,
current_kind: &'a ModuleKind,
importable_modules: &'a HashMap<String, TypeInfo>,
warnings: &'a mut Vec<Warning>,
target_env: Option<&'a str>,
) -> Self {
let prelude = importable_modules
.get("aiken")
.expect("Unable to find prelude in importable modules");
Self {
previous_id: id_gen.next(),
id_gen,
ungeneralised_functions: HashSet::new(),
inferred_functions: HashMap::new(),
module_types: prelude.types.clone(),
module_types_constructors: prelude.types_constructors.clone(),
module_values: HashMap::new(),
module_functions: HashMap::new(),
module_validators: HashMap::new(),
imported_modules: HashMap::new(),
unused_modules: HashMap::new(),
unqualified_imported_names: HashMap::new(),
accessors: prelude.accessors.clone(),
scope: prelude.values.clone(),
importable_modules,
imported_types: HashSet::new(),
current_module,
current_kind,
annotations: HashMap::new(),
warnings,
entity_usages: vec![HashMap::new()],
target_env,
}
}
/// Create a new generic type that can stand in for any type.
pub fn new_generic_var(&mut self) -> Rc<Type> {
Type::generic_var(self.next_uid())
}
/// Create a new unbound type that is a specific type, we just don't
/// know which one yet.
pub fn new_unbound_var(&mut self) -> Rc<Type> {
Type::unbound_var(self.next_uid())
}
pub fn next_uid(&mut self) -> u64 {
let id = self.id_gen.next();
self.previous_id = id;
id
}
pub fn open_new_scope(&mut self) -> ScopeResetData {
let local_values = self.scope.clone();
self.entity_usages.push(HashMap::new());
ScopeResetData { local_values }
}
pub fn previous_uid(&self) -> u64 {
self.previous_id
}
pub fn register_import(&mut self, def: &UntypedDefinition) -> Result<(), Error> {
match def {
Definition::Use(Use {
module,
as_name,
unqualified,
location,
package: _,
}) => {
let module_info = self.find_module(module, *location)?;
if module_info.kind.is_validator()
&& (self.current_kind.is_lib() || self.current_kind.is_env())
{
return Err(Error::ValidatorImported {
location: *location,
name: module.join("/"),
});
}
// Determine local alias of imported module
let module_name = as_name
.as_ref()
.or_else(|| module.last())
.expect("Typer could not identify module name.")
.clone();
// Insert unqualified imports into scope
for UnqualifiedImport {
name,
location,
as_name,
} in unqualified
{
let mut type_imported = false;
let mut value_imported = false;
let mut variant = None;
let imported_name = as_name.as_ref().unwrap_or(name);
// Check if value already was imported
if let Some(previous) = self.unqualified_imported_names.get(imported_name) {
return Err(Error::DuplicateImport {
location: *location,
previous_location: *previous,
name: name.to_string(),
module: module.clone(),
});
}
// Register the name as imported so it can't be imported a
// second time in future
self.unqualified_imported_names
.insert(imported_name.clone(), *location);
// Register the unqualified import if it is a value
if let Some(value) = module_info.values.get(name) {
self.insert_variable(
imported_name.clone(),
value.variant.clone(),
value.tipo.clone(),
);
variant = Some(&value.variant);
value_imported = true;
}
// Register the unqualified import if it is a type constructor
if let Some(typ) = module_info.types.get(name) {
let typ_info = TypeConstructor {
location: *location,
..typ.clone()
};
self.insert_type_constructor(imported_name.clone(), typ_info)?;
type_imported = true;
}
if value_imported && type_imported {
self.init_usage(
imported_name.to_string(),
EntityKind::ImportedTypeAndConstructor,
*location,
);
} else if type_imported {
self.imported_types.insert(imported_name.to_string());
self.init_usage(
imported_name.to_string(),
EntityKind::ImportedType,
*location,
);
} else if value_imported {
match variant {
Some(&ValueConstructorVariant::Record { .. }) => self.init_usage(
imported_name.to_string(),
EntityKind::ImportedConstructor,
*location,
),
_ => self.init_usage(
imported_name.to_string(),
EntityKind::ImportedValue,
*location,
),
};
} else if !value_imported {
// Error if no type or value was found with that name
return Err(Error::UnknownModuleField {
location: *location,
name: name.clone(),
module_name: module.join("/"),
value_constructors: module_info
.values
.keys()
.map(|t| t.to_string())
.collect(),
type_constructors: module_info
.types
.keys()
.map(|t| t.to_string())
.collect(),
});
}
}
if unqualified.is_empty() {
// When the module has no unqualified imports, we track its usage
// so we can warn if not used by the end of the type checking
self.unused_modules.insert(module_name.clone(), *location);
}
// Check if a module was already imported with this name
if let Some((previous_location, _)) = self.imported_modules.get(&module_name) {
return Err(Error::DuplicateImport {
location: *location,
previous_location: *previous_location,
name: module_name,
module: module.clone(),
});
}
// Register the name as imported so it can't be imported a
// second time in future
self.unqualified_imported_names
.insert(module_name.clone(), *location);
// Insert imported module into scope
self.imported_modules
.insert(module_name, (*location, module_info));
Ok(())
}
_ => Ok(()),
}
}
/// Iterate over a module, registering any new types created by the module into the typer
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 errors = vec![];
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) {
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 errors.is_empty() {
return Ok(());
}
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 {
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)
}
}
pub fn register_type(
&mut self,
def: &'a UntypedDefinition,
module: &String,
hydrators: &mut HashMap<String, Hydrator>,
names: &mut HashMap<&'a str, &'a Span>,
) -> Result<(), Error> {
match def {
Definition::DataType(DataType {
name,
public,
opaque,
parameters,
location,
constructors,
doc: _,
typed_parameters: _,
}) => {
assert_unique_type_name(names, name, location)?;
// Build a type from the type Annotation
let mut hydrator = Hydrator::new();
let parameters = self.make_type_vars(parameters, location, &mut hydrator)?;
let tipo = Rc::new(Type::App {
public: *public,
contains_opaque: *opaque,
module: module.to_owned(),
name: name.clone(),
args: parameters.clone(),
alias: None,
});
hydrators.insert(name.to_string(), hydrator);
self.insert_type_constructor(
name.clone(),
TypeConstructor {
location: *location,
module: module.to_owned(),
public: *public,
parameters,
tipo,
},
)?;
let constructor_names = constructors.iter().map(|c| c.name.clone()).collect();
self.insert_type_to_constructors(name.clone(), constructor_names);
// Keep track of private types so we can tell if they are later unused
if !public {
self.init_usage(name.clone(), EntityKind::PrivateType, *location);
}
}
Definition::TypeAlias(TypeAlias {
location,
public,
parameters: args,
alias: name,
annotation: resolved_type,
doc: _,
tipo: _,
}) => {
assert_unique_type_name(names, name, location)?;
// Register the parameterised types
let mut hydrator = Hydrator::new();
let parameters = self.make_type_vars(args, location, &mut hydrator)?;
// Disallow creation of new types outside the parameterised types
hydrator.disallow_new_type_variables();
// Create the type that the alias resolves to
let tipo = hydrator
.type_from_annotation(resolved_type, self)?
.as_ref()
.to_owned()
.set_alias(Some(
TypeAliasAnnotation {
alias: name.to_string(),
parameters: args.to_vec(),
annotation: resolved_type.clone(),
}
.into(),
));
self.insert_type_constructor(
name.clone(),
TypeConstructor {
location: *location,
module: module.to_owned(),
public: *public,
parameters,
tipo,
},
)?;
// Keep track of private types so we can tell if they are later unused
if !public {
self.init_usage(name.clone(), EntityKind::PrivateType, *location);
}
}
Definition::Fn { .. }
| Definition::Validator { .. }
| Definition::Test { .. }
| Definition::Use { .. }
| Definition::ModuleConstant { .. } => {}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn register_function(
&mut self,
name: &str,
arguments: &[UntypedArg],
return_annotation: &Option<Annotation>,
module_name: &String,
hydrators: &mut HashMap<String, Hydrator>,
names: &mut HashMap<String, &'a Span>,
location: &'a Span,
) -> Result<(), Error> {
assert_unique_value_name(names, name, location)?;
self.ungeneralised_functions.insert(name.to_string());
// Create the field map so we can reorder labels for usage of this function
let mut field_map = FieldMap::new(arguments.len(), true);
for (i, arg) in arguments.iter().enumerate() {
field_map.insert(arg.arg_name(i).get_label(), i, &arg.location)?;
}
let field_map = field_map.into_option();
// Construct type from annotations
let mut hydrator = Hydrator::new();
let mut arg_types = Vec::new();
for arg in arguments {
let tipo = hydrator.type_from_option_annotation(&arg.annotation, self)?;
arg_types.push(tipo);
}
let return_type = hydrator.type_from_option_annotation(return_annotation, self)?;
let tipo = Type::function(arg_types, return_type);
// Keep track of which types we create from annotations so we can know
// which generic types not to instantiate later when performing
// inference of the function body.
hydrators.insert(name.to_string(), hydrator);
// Insert the function into the environment
self.insert_variable(
name.to_string(),
ValueConstructorVariant::ModuleFn {
name: name.to_string(),
field_map,
module: module_name.to_owned(),
arity: arguments.len(),
location: *location,
builtin: None,
},
tipo,
);
Ok(())
}
pub fn register_values(
&mut self,
def: &'a UntypedDefinition,
module_name: &String,
hydrators: &mut HashMap<String, Hydrator>,
names: &mut HashMap<String, &'a Span>,
kind: ModuleKind,
) -> Result<(), Error> {
match def {
Definition::Fn(fun) => {
self.register_function(
&fun.name,
&fun.arguments,
&fun.return_annotation,
module_name,
hydrators,
names,
&fun.location,
)?;
self.module_functions.insert(fun.name.clone(), fun);
if !fun.public {
self.init_usage(fun.name.clone(), EntityKind::PrivateFunction, fun.location);
}
}
Definition::Validator(Validator {
handlers,
fallback,
params,
name,
doc: _,
location,
end_position: _,
}) if kind.is_validator() => {
let default_annotation = |mut arg: UntypedArg, ann: Annotation| {
if arg.annotation.is_none() {
arg.annotation = Some(ann);
arg
} else {
arg
}
};
let mut handler_names = vec![];
let params_len = params.len();
for handler in handlers {
let temp_params: Vec<UntypedArg> = params
.iter()
.cloned()
.chain(handler.arguments.clone())
.enumerate()
.map(|(ix, arg)| {
let is_datum = handler.is_spend() && ix == params_len;
let is_mint_policy = handler.is_mint() && ix == params_len + 1;
let location = arg.location;
default_annotation(
arg,
if is_datum {
Annotation::option(Annotation::data(location))
} else if is_mint_policy {
Annotation::bytearray(location)
} else {
Annotation::data(location)
},
)
})
.collect();
handler_names.push(handler.name.clone());
self.register_function(
&TypedValidator::handler_name(name.as_str(), handler.name.as_str()),
&temp_params,
&handler.return_annotation,
module_name,
hydrators,
names,
&handler.location,
)?;
}
let temp_params: Vec<UntypedArg> = params
.iter()
.cloned()
.chain(fallback.arguments.clone())
.map(|arg| {
let location = arg.location;
default_annotation(arg, Annotation::data(location))
})
.collect();
self.register_function(
&TypedValidator::handler_name(name.as_str(), fallback.name.as_str()),
&temp_params,
&fallback.return_annotation,
module_name,
hydrators,
names,
&fallback.location,
)?;
handler_names.push(fallback.name.clone());
let err_duplicate_name = |previous_location: Span| {
Err(Error::DuplicateName {
name: name.to_string(),
previous_location,
location: location.map_end(|end| end + 1 + name.len()),
})
};
if let Some((previous_location, _)) = self.imported_modules.get(name) {
return err_duplicate_name(*previous_location);
}
match self
.module_validators
.insert(name.to_string(), (*location, handler_names))
{
Some((previous_location, _)) => err_duplicate_name(previous_location),
None => Ok(()),
}?
}
Definition::Validator(Validator { location, .. }) => {
self.warnings.push(Warning::ValidatorInLibraryModule {
location: *location,
})
}
Definition::Test(test) => {
let arguments = test
.arguments
.iter()
.map(|arg| arg.clone().into())
.collect::<Vec<_>>();
self.register_function(
&test.name,
&arguments,
&test.return_annotation,
module_name,
hydrators,
names,
&test.location,
)?;
}
Definition::DataType(DataType {
public,
opaque,
name,
constructors,
doc: _,
location: _,
parameters: _,
typed_parameters: _,
}) => {
let mut hydrator = hydrators
.remove(name)
.expect("Could not find hydrator for register_values custom type");
hydrator.disallow_new_type_variables();
let typ = self
.module_types
.get(name)
.expect("Type for custom type not found in register_values")
.tipo
.clone();
// If the custom type only has a single constructor then we can access the
// fields using the record.field syntax, so store any fields accessors.
if let Some(accessors) = self.custom_type_accessors(constructors, &mut hydrator)? {
let map = AccessorsMap {
public: (*public && !*opaque),
accessors,
// TODO: improve the ownership here so that we can use the
// `return_type_constructor` below rather than looking it up twice.
tipo: typ.clone(),
};
self.insert_accessors(name, map)
}
// Check and register constructors
for constructor in constructors {
assert_unique_value_name(names, &constructor.name, &constructor.location)?;
let mut field_map = FieldMap::new(constructor.arguments.len(), false);
let mut args_types = Vec::with_capacity(constructor.arguments.len());
for (
i,
RecordConstructorArg {
label,
annotation,
location,
tipo: _,
doc: _,
},
) in constructor.arguments.iter().enumerate()
{
let t = hydrator.type_from_annotation(annotation, self)?;
args_types.push(t);
if let Some(label) = label {
field_map.insert(label.clone(), i, location)?;
}
}
let field_map = field_map.into_option();
// Insert constructor function into module scope
let typ = match constructor.arguments.len() {
0 => typ.clone(),
_ => Type::function(args_types, typ.clone()),
};
let constructor_info = ValueConstructorVariant::Record {
constructors_count: constructors.len() as u16,
name: constructor.name.clone(),
arity: constructor.arguments.len(),
field_map: field_map.clone(),
location: constructor.location,
module: module_name.to_owned(),
};
if !opaque {
self.insert_module_value(
&constructor.name,
ValueConstructor {
public: *public,
tipo: typ.clone(),
variant: constructor_info.clone(),
},
);
}
if !public {
self.init_usage(
constructor.name.clone(),
EntityKind::PrivateTypeConstructor(name.clone()),
constructor.location,
);
}
self.insert_variable(constructor.name.clone(), constructor_info, typ);
}
}
Definition::ModuleConstant(ModuleConstant { name, location, .. }) => {
assert_unique_const_name(names, name, location)?;
}
Definition::Use { .. } | Definition::TypeAlias { .. } => {}
}
Ok(())
}
/// Unify two types that should be the same.
/// Any unbound type variables will be linked to the other type as they are the same.
///
/// It two types are found to not be the same an error is returned.
#[allow(clippy::only_used_in_recursion)]
pub fn unify(
&mut self,
lhs: Rc<Type>,
rhs: Rc<Type>,
location: Span,
allow_cast: bool,
) -> Result<(), Error> {
if lhs == rhs {
return Ok(());
}
// TODO: maybe we also care to check is_link?
if allow_cast
&& (lhs.is_data() || rhs.is_data())
&& !(lhs.is_unbound() || rhs.is_unbound())
&& !(lhs.is_function() || rhs.is_function())
&& !(lhs.is_generic() || rhs.is_generic())
&& !(lhs.is_string() || rhs.is_string())
&& !lhs.contains_opaque()
{
return Ok(());
}
if allow_cast && lhs.contains_opaque() {
return Err(Error::ExpectOnOpaqueType { location });
}
// Collapse right hand side type links. Left hand side will be collapsed in the next block.
if let Type::Var { tipo, alias } = rhs.deref() {
if let TypeVar::Link { tipo } = tipo.borrow().deref() {
return self.unify(
lhs,
Type::with_alias(tipo.clone(), alias.clone()),
location,
allow_cast,
);
}
}
if let Type::Var { tipo, alias } = lhs.deref() {
enum Action {
Unify(Rc<Type>),
CouldNotUnify,
Link,
}
let action = match tipo.borrow().deref() {
TypeVar::Link { tipo } => {
Action::Unify(Type::with_alias(tipo.clone(), alias.clone()))
}
TypeVar::Unbound { id } => {
unify_unbound_type(rhs.clone(), *id, location)?;
Action::Link
}
TypeVar::Generic { id } => {
if let Type::Var { tipo, alias: _ } = rhs.deref() {
if tipo.borrow().is_unbound() {
*tipo.borrow_mut() = TypeVar::Generic { id: *id };
return Ok(());
}
}
Action::CouldNotUnify
}
};
return match action {
Action::Link => {
*tipo.borrow_mut() = TypeVar::Link { tipo: rhs };
Ok(())
}
Action::Unify(t) => self.unify(t, rhs, location, allow_cast),
Action::CouldNotUnify => Err(Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs,
situation: None,
rigid_type_names: HashMap::new(),
}),
};
}
if let Type::Var { .. } = rhs.deref() {
return self
.unify(rhs, lhs, location, false)
.map_err(|e| e.flip_unify());
}
match (lhs.deref(), rhs.deref()) {
(
Type::App {
module: m1,
name: n1,
args: args1,
public: _,
contains_opaque: _,
alias: _,
},
Type::App {
module: m2,
name: n2,
args: args2,
public: _,
contains_opaque: _,
alias: _,
},
) if m1 == m2 && n1 == n2 && args1.len() == args2.len() => {
for (a, b) in args1.iter().zip(args2) {
unify_enclosed_type(
lhs.clone(),
rhs.clone(),
self.unify(a.clone(), b.clone(), location, false),
)?;
}
Ok(())
}
(
Type::Tuple {
elems: elems1,
alias: _,
},
Type::Tuple {
elems: elems2,
alias: _,
},
) if elems1.len() == elems2.len() => {
for (a, b) in elems1.iter().zip(elems2) {
unify_enclosed_type(
lhs.clone(),
rhs.clone(),
self.unify(a.clone(), b.clone(), location, false),
)?;
}
Ok(())
}
(
Type::Pair {
fst: lhs_fst,
snd: lhs_snd,
alias: _,
},
Type::Pair {
fst: rhs_fst,
snd: rhs_snd,
alias: _,
},
) => {
for (a, b) in [lhs_fst, lhs_snd].into_iter().zip([rhs_fst, rhs_snd]) {
unify_enclosed_type(
lhs.clone(),
rhs.clone(),
self.unify(a.clone(), b.clone(), location, false),
)?;
}
Ok(())
}
(
Type::Fn {
args: args1,
ret: retrn1,
alias: _,
},
Type::Fn {
args: args2,
ret: retrn2,
alias: _,
},
) if args1.len() == args2.len() => {
for (a, b) in args1.iter().zip(args2) {
self.unify(a.clone(), b.clone(), location, allow_cast)
.map_err(|_| Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs.clone(),
situation: None,
rigid_type_names: HashMap::new(),
})?;
}
self.unify(retrn1.clone(), retrn2.clone(), location, false)
.map_err(|_| Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs.clone(),
situation: None,
rigid_type_names: HashMap::new(),
})
}
_ => Err(Error::CouldNotUnify {
location,
expected: lhs.clone(),
given: rhs.clone(),
situation: None,
rigid_type_names: HashMap::new(),
}),
}
}
/// Checks that the given patterns are exhaustive for given type.
/// https://github.com/elm/compiler/blob/047d5026fe6547c842db65f7196fed3f0b4743ee/compiler/src/Nitpick/PatternMatches.hs#L397-L475
/// http://moscova.inria.fr/~maranget/papers/warn/index.html
pub fn check_exhaustiveness(
&mut self,
unchecked_patterns: &[&TypedPattern],
location: Span,
is_let: bool,
) -> Result<(), Error> {
let mut matrix = Matrix::new();
for unchecked_pattern in unchecked_patterns {
let pattern = simplify(self, unchecked_pattern)?;
let pattern_stack = PatternStack::from(pattern);
if matrix.is_useful(&pattern_stack) {
matrix.push(pattern_stack);
} else {
let original = matrix
.flatten()
.into_iter()
.enumerate()
.find(|(_, p)| p == pattern_stack.head())
.and_then(|(index, _)| unchecked_patterns.get(index))
.map(|typed_pattern| typed_pattern.location());
return Err(Error::RedundantMatchClause {
original,
redundant: unchecked_pattern.location(),
});
}
}
let missing_patterns = matrix.collect_missing_patterns(1).flatten();
if !missing_patterns.is_empty() {
let unmatched = missing_patterns
.into_iter()
.map(|pattern| pattern.pretty())
.collect();
return Err(Error::NotExhaustivePatternMatch {
location,
unmatched,
is_let,
});
}
Ok(())
}
/// Lookup constructors for type in the current scope.
///
pub fn get_constructors_for_type(
&mut self,
full_module_name: &String,
name: &str,
location: Span,
) -> Result<Vec<ValueConstructor>, Error> {
if full_module_name.is_empty() || full_module_name == self.current_module {
self.module_types_constructors
.get(name)
.ok_or_else(|| Error::UnknownType {
name: name.to_string(),
types: self.module_types.keys().map(|t| t.to_string()).collect(),
location,
})?
.iter()
.map(|constructor| {
self.scope
.get(constructor)
.cloned()
.ok_or_else(|| Error::UnknownModuleValue {
name: name.to_string(),
module_name: self.current_module.clone(),
value_constructors: self
.module_values
.keys()
.map(|t| t.to_string())
.collect(),
location,
})
})
.collect()
} else {
let module = self
.importable_modules
.get(full_module_name)
.ok_or_else(|| Error::UnknownModule {
location,
name: name.to_string(),
known_modules: self
.importable_modules
.keys()
.map(|t| t.to_string())
.collect(),
})?;
self.unused_modules.remove(full_module_name);
let constructors =
module
.types_constructors
.get(name)
.ok_or_else(|| Error::UnknownModuleType {
location,
name: name.to_string(),
module_name: module.name.clone(),
type_constructors: module.types.keys().map(|t| t.to_string()).collect(),
})?;
constructors
.iter()
.map(|constructor| {
module.values.get(constructor).cloned().ok_or_else(|| {
Error::UnknownModuleValue {
name: name.to_string(),
module_name: module.name.clone(),
value_constructors: module
.values
.keys()
.map(|t| t.to_string())
.collect(),
location,
}
})
})
.collect()
}
}
}
/// For Keeping track of entity usages and knowing which error to display.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EntityKind {
PrivateConstant,
// String here is the type constructor's type name
PrivateTypeConstructor(String),
PrivateFunction,
ImportedConstructor,
ImportedType,
ImportedTypeAndConstructor,
ImportedValue,
PrivateType,
Variable,
}
/// This function makes sure that the type variable being unified
/// doesn't occur within the type it is being unified with. This
/// prevents the algorithm from inferring recursive types, which
/// could cause naively-implemented type checking to diverge.
/// While traversing the type tree.
fn unify_unbound_type(tipo: Rc<Type>, own_id: u64, location: Span) -> Result<(), Error> {
if let Type::Var { tipo, alias } = tipo.deref() {
let new_value = match tipo.borrow().deref() {
TypeVar::Link { tipo } => {
return unify_unbound_type(
Type::with_alias(tipo.clone(), alias.clone()),
own_id,
location,
);
}
TypeVar::Unbound { id } => {
if id == &own_id {
return Err(Error::RecursiveType { location });
} else {
Some(TypeVar::Unbound { id: *id })
}
}
TypeVar::Generic { .. } => return Ok(()),
};
if let Some(t) = new_value {
*tipo.borrow_mut() = t;
}
return Ok(());
}
match tipo.deref() {
Type::App {
args,
module: _,
name: _,
public: _,
alias: _,
contains_opaque: _,
} => {
for arg in args {
unify_unbound_type(arg.clone(), own_id, location)?
}
Ok(())
}
Type::Fn {
args,
ret,
alias: _,
} => {
for arg in args {
unify_unbound_type(arg.clone(), own_id, location)?;
}
unify_unbound_type(ret.clone(), own_id, location)
}
Type::Tuple { elems, alias: _ } => {
for elem in elems {
unify_unbound_type(elem.clone(), own_id, location)?
}
Ok(())
}
Type::Pair { fst, snd, alias: _ } => {
unify_unbound_type(fst.clone(), own_id, location)?;
unify_unbound_type(snd.clone(), own_id, location)?;
Ok(())
}
Type::Var { .. } => unreachable!(),
}
}
fn unify_enclosed_type(e1: Rc<Type>, e2: Rc<Type>, result: Result<(), Error>) -> Result<(), Error> {
// If types cannot unify, show the type error with the enclosing types, e1 and e2.
match result {
Err(Error::CouldNotUnify {
situation,
location,
rigid_type_names,
..
}) => Err(Error::CouldNotUnify {
expected: e1,
given: e2,
situation,
location,
rigid_type_names,
}),
_ => result,
}
}
fn assert_unique_type_name<'a>(
names: &mut HashMap<&'a str, &'a Span>,
name: &'a str,
location: &'a Span,
) -> Result<(), Error> {
match names.insert(name, location) {
Some(previous_location) => Err(Error::DuplicateTypeName {
name: name.to_string(),
previous_location: *previous_location,
location: *location,
}),
None => Ok(()),
}
}
fn assert_unique_value_name<'a>(
names: &mut HashMap<String, &'a Span>,
name: &str,
location: &'a Span,
) -> Result<(), Error> {
match names.insert(name.to_string(), location) {
Some(previous_location) => Err(Error::DuplicateName {
name: name.to_string(),
previous_location: *previous_location,
location: *location,
}),
None => Ok(()),
}
}
fn assert_unique_const_name<'a>(
names: &mut HashMap<String, &'a Span>,
name: &str,
location: &'a Span,
) -> Result<(), Error> {
match names.insert(name.to_string(), location) {
Some(previous_location) => Err(Error::DuplicateConstName {
name: name.to_string(),
previous_location: *previous_location,
location: *location,
}),
None => Ok(()),
}
}
pub(super) fn assert_no_labeled_arguments<A>(args: &[CallArg<A>]) -> Option<(Span, String)> {
for arg in args {
if let Some(label) = &arg.label {
return Some((arg.location, label.to_string()));
}
}
None
}
pub fn collapse_links(t: Rc<Type>) -> Rc<Type> {
if let Type::Var { tipo, alias } = t.deref() {
if let TypeVar::Link { tipo } = tipo.borrow().deref() {
return Type::with_alias(tipo.clone(), alias.clone());
}
}
t
}
/// Returns the fields that have the same label and type across all variants of
/// the given type.
fn get_compatible_record_fields<A>(
constructors: &[RecordConstructor<A>],
) -> Vec<(usize, &str, &Annotation)> {
let mut compatible = vec![];
if constructors.len() > 1 {
return compatible;
}
let first = match constructors.first() {
Some(first) => first,
None => return compatible,
};
for (index, first_argument) in first.arguments.iter().enumerate() {
// Fields without labels do not have accessors
let label = match first_argument.label.as_ref() {
Some(label) => label.as_str(),
None => continue,
};
compatible.push((index, label, &first_argument.annotation))
}
compatible
}
/// Takes a level and a type and turns all type variables within the type that have
/// level higher than the input level into generalized (polymorphic) type variables.
#[allow(clippy::only_used_in_recursion)]
pub(crate) fn generalise(t: Rc<Type>, ctx_level: usize) -> Rc<Type> {
match t.deref() {
Type::Var { tipo, alias } => Type::with_alias(
match tipo.borrow().deref() {
TypeVar::Unbound { id } => Type::generic_var(*id),
TypeVar::Link { tipo } => generalise(tipo.clone(), ctx_level),
TypeVar::Generic { .. } => Rc::new(Type::Var {
tipo: tipo.clone(),
alias: None,
}),
},
alias.clone(),
),
Type::App {
public,
contains_opaque: opaque,
module,
name,
args,
alias,
} => {
let args = args
.iter()
.map(|t| generalise(t.clone(), ctx_level))
.collect();
Rc::new(Type::App {
public: *public,
contains_opaque: *opaque,
module: module.clone(),
name: name.clone(),
args,
alias: alias.clone(),
})
}
Type::Fn { args, ret, alias } => Type::with_alias(
Type::function(
args.iter()
.map(|t| generalise(t.clone(), ctx_level))
.collect(),
generalise(ret.clone(), ctx_level),
),
alias.clone(),
),
Type::Tuple { elems, alias } => Type::with_alias(
Type::tuple(
elems
.iter()
.map(|t| generalise(t.clone(), ctx_level))
.collect(),
),
alias.clone(),
),
Type::Pair { fst, snd, alias } => Type::with_alias(
Type::pair(
generalise(fst.clone(), ctx_level),
generalise(snd.clone(), ctx_level),
),
alias.clone(),
),
}
}