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, } #[derive(Debug)] pub struct Environment<'a> { /// Accessors defined in the current module pub accessors: HashMap, 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>, pub id_gen: IdGenerator, pub importable_modules: &'a HashMap, /// 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, pub imported_types: HashSet, /// Types defined in the current module (or the prelude) pub module_types: HashMap, /// Mapping from types to constructor names in the current module (or the prelude) pub module_types_constructors: HashMap>, /// Values defined in the current module (or the prelude) pub module_values: HashMap, /// Top-level function definitions from the module pub module_functions: HashMap, /// Top-level validator definitions from the module pub module_validators: HashMap)>, /// Top-level functions that have been inferred pub inferred_functions: HashMap, previous_id: u64, /// Values defined in the current function (or the prelude) pub scope: HashMap, /// 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, /// 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, pub unused_modules: HashMap, /// A mapping from known annotations to their resolved type. pub annotations: HashMap>, /// The user-defined target environment referred to as the module 'env'. pub target_env: Option<&'a str>, /// Warnings pub warnings: &'a mut Vec, } 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, annotation: &Annotation) -> Rc { 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, arity: usize, fn_location: Span, call_location: Span, ) -> Result<(Vec>, Rc), 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( &mut self, constructors: &[RecordConstructor], hydrator: &mut Hydrator, ) -> Result>, 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, 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) { 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(&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) { 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, ) { self.scope.insert( name, ValueConstructor { public: false, variant, tipo, }, ); } /// Instantiate converts generic variables into unbound ones. pub fn instantiate( &mut self, t: Rc, ids: &mut HashMap>, hydrator: &Hydrator, ) -> Rc { 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 { self.scope .keys() .filter(|&t| PIPE_VARIABLE != t) .map(|t| t.to_string()) .collect() } pub fn local_constructor_names(&self) -> Vec { 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>, 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, warnings: &'a mut Vec, 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::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::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, names: &mut HashMap<&'a str, &'a Span>, ) -> Result<(), Error> { let known_types_before = names.keys().copied().collect::>(); 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::>(); 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::>(); 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, 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, module_name: &String, hydrators: &mut HashMap, names: &mut HashMap, 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, names: &mut HashMap, 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 = 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 = 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::>(); 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, rhs: Rc, 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), 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, 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, 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, e2: Rc, 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, 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, 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(args: &[CallArg]) -> 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) -> Rc { 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( constructors: &[RecordConstructor], ) -> 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, ctx_level: usize) -> Rc { 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(), ), } }