use std::{collections::HashMap, sync::Arc}; use vec1::Vec1; use crate::{ ast::{ Annotation, Arg, ArgName, AssignmentKind, BinOp, CallArg, Clause, ClauseGuard, Constant, RecordUpdateSpread, Span, TodoKind, TypedArg, TypedCallArg, TypedClause, TypedClauseGuard, TypedConstant, TypedIfBranch, TypedMultiPattern, TypedRecordUpdateArg, UntypedArg, UntypedClause, UntypedClauseGuard, UntypedConstant, UntypedIfBranch, UntypedMultiPattern, UntypedPattern, UntypedRecordUpdateArg, }, builtins::{bool, byte_array, function, int, list, string, tuple}, expr::{TypedExpr, UntypedExpr}, tipo::fields::FieldMap, }; use super::{ environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment}, error::{Error, Warning}, hydrator::Hydrator, pattern::PatternTyper, pipe::PipeTyper, ModuleValueConstructor, PatternConstructor, RecordAccessor, Type, ValueConstructor, ValueConstructorVariant, }; #[derive(Debug)] pub(crate) struct ExprTyper<'a, 'b> { pub(crate) environment: &'a mut Environment<'b>, // Type hydrator for creating types from annotations pub(crate) hydrator: Hydrator, // We keep track of whether any ungeneralised functions have been used // to determine whether it is safe to generalise this expression after // it has been inferred. pub(crate) ungeneralised_function_used: bool, } impl<'a, 'b> ExprTyper<'a, 'b> { fn check_when_exhaustiveness( &mut self, subjects_count: usize, subjects: &[Arc], typed_clauses: &[Clause, String>], location: Span, ) -> Result<(), Vec> { // Because exhaustiveness checking in presence of multiple subjects is similar // to full exhaustiveness checking of tuples or other nested record patterns, // and we currently only do only limited exhaustiveness checking of custom types // at the top level of patterns, only consider case expressions with one subject. if subjects_count != 1 { return Ok(()); } let subject_type = subjects .get(0) .expect("Asserted there's one case subject but found none"); let value_typ = collapse_links(subject_type.clone()); // Currently guards in exhaustiveness checking are assumed that they can fail, // so we go through all clauses and pluck out only the patterns // for clauses that don't have guards. let mut patterns = Vec::new(); for clause in typed_clauses { if let Clause { guard: None, .. } = clause { // clause.pattern is a list of patterns for all subjects if let Some(pattern) = clause.pattern.get(0) { patterns.push(pattern.clone()); } // A clause can be built with alternative patterns as well, e.g. `Audio(_) | Text(_) ->`. // We're interested in all patterns so we build a flattened list. for alternative_pattern in &clause.alternative_patterns { // clause.alternative_pattern is a list of patterns for all subjects if let Some(pattern) = alternative_pattern.get(0) { patterns.push(pattern.clone()); } } } } self.environment .check_exhaustiveness(patterns, value_typ, location) } pub fn do_infer_call( &mut self, fun: UntypedExpr, args: Vec>, location: Span, ) -> Result<(TypedExpr, Vec, Arc), Error> { let fun = self.infer(fun)?; let (fun, args, typ) = self.do_infer_call_with_known_fun(fun, args, location)?; Ok((fun, args, typ)) } pub fn do_infer_call_with_known_fun( &mut self, fun: TypedExpr, mut args: Vec>, location: Span, ) -> Result<(TypedExpr, Vec, Arc), Error> { // Check to see if the function accepts labelled arguments match self.get_field_map(&fun, location)? { // The fun has a field map so labelled arguments may be present and need to be reordered. Some(field_map) => field_map.reorder(&mut args, location)?, // The fun has no field map and so we error if arguments have been labelled None => assert_no_labeled_arguments(&args)?, } // Extract the type of the fun, ensuring it actually is a function let (mut args_types, return_type) = self.environment .match_fun_type(fun.tipo(), args.len(), fun.location(), location)?; let mut arguments = Vec::new(); for (tipo, arg) in args_types.iter_mut().zip(args) { let CallArg { label, value, location, } = arg; let value = self.infer_call_argument(value, tipo.clone())?; arguments.push(CallArg { label, value, location, }); } Ok((fun, arguments, return_type)) } pub fn do_infer_fn( &mut self, args: Vec, expected_args: &[Arc], body: UntypedExpr, return_annotation: &Option, ) -> Result<(Vec, TypedExpr), Error> { // Construct an initial type for each argument of the function- either an unbound // type variable or a type provided by an annotation. let mut arguments = Vec::new(); for (i, arg) in args.into_iter().enumerate() { let arg = self.infer_arg(arg, expected_args.get(i).cloned())?; arguments.push(arg); } let return_type = match return_annotation { Some(ann) => Some(self.type_from_annotation(ann)?), None => None, }; self.infer_fn_with_known_types(arguments, body, return_type) } /// Emit a warning if the given expressions should not be discarded. /// e.g. because it's a literal (why was it made in the first place?) /// e.g. because it's of the `Result` type (errors should be handled) fn expression_discarded(&mut self, discarded: &TypedExpr) { if discarded.is_literal() { self.environment.warnings.push(Warning::UnusedLiteral { location: discarded.location(), }); } if discarded.tipo().is_result() && !discarded.is_assignment() { self.environment .warnings .push(Warning::ImplicitlyDiscardedResult { location: discarded.location(), }); } } fn get_field_map( &mut self, constructor: &TypedExpr, location: Span, ) -> Result, Error> { let (module, name) = match constructor { TypedExpr::ModuleSelect { module_alias, label, .. } => (Some(module_alias), label), TypedExpr::Var { name, .. } => (None, name), _ => return Ok(None), }; Ok(self .environment .get_value_constructor(module, name, location)? .field_map()) } pub fn in_new_scope(&mut self, process_scope: impl FnOnce(&mut Self) -> T) -> T { // Create new scope let environment_reset_data = self.environment.open_new_scope(); let hydrator_reset_data = self.hydrator.open_new_scope(); // Process the scope let result = process_scope(self); // Close scope, discarding any scope local state self.environment.close_scope(environment_reset_data); self.hydrator.close_scope(hydrator_reset_data); result } /// Crawl the AST, annotating each node with the inferred type or /// returning an error. pub fn infer(&mut self, expr: UntypedExpr) -> Result { match expr { UntypedExpr::Todo { location, label, kind, .. } => Ok(self.infer_todo(location, kind, label)), UntypedExpr::Var { location, name, .. } => self.infer_var(name, location), UntypedExpr::Int { location, value, .. } => Ok(self.infer_int(value, location)), UntypedExpr::Sequence { expressions, location, } => self.infer_seq(location, expressions), UntypedExpr::Tuple { location, elems, .. } => self.infer_tuple(elems, location), UntypedExpr::String { location, value, .. } => Ok(self.infer_string(value, location)), UntypedExpr::PipeLine { expressions } => self.infer_pipeline(expressions), UntypedExpr::Fn { location, is_capture, arguments: args, body, return_annotation, .. } => self.infer_fn(args, &[], *body, is_capture, return_annotation, location), UntypedExpr::If { location, branches, final_else, } => self.infer_if(branches, *final_else, location), UntypedExpr::Assignment { location, pattern, value, kind, annotation, .. } => self.infer_assignment(pattern, *value, kind, &annotation, location), UntypedExpr::Trace { location, then, .. } => self.infer_trace(*then, location), UntypedExpr::When { location, subjects, clauses, .. } => self.infer_when(subjects, clauses, location), UntypedExpr::List { location, elements, tail, .. } => self.infer_list(elements, tail, location), UntypedExpr::Call { location, fun, arguments: args, .. } => self.infer_call(*fun, args, location), UntypedExpr::BinOp { location, name, left, right, .. } => self.infer_binop(name, *left, *right, location), UntypedExpr::FieldAccess { location, label, container, .. } => self.infer_field_access(*container, label, location), // UntypedExpr::TupleIndex { // location, // index, // tuple, // .. // } => self.infer_tuple_index(*tuple, index, location), UntypedExpr::ByteArray { location, bytes } => { Ok(self.infer_byte_array(bytes, location)) } UntypedExpr::RecordUpdate { location, constructor, spread, arguments: args, } => self.infer_record_update(*constructor, spread, args, location), UntypedExpr::Negate { location, value } => self.infer_negate(location, value), } } fn infer_byte_array(&mut self, bytes: Vec, location: Span) -> TypedExpr { TypedExpr::ByteArray { location, bytes, tipo: byte_array(), } } fn infer_binop( &mut self, name: BinOp, left: UntypedExpr, right: UntypedExpr, location: Span, ) -> Result { let (input_type, output_type) = match &name { BinOp::Eq | BinOp::NotEq => { let left = self.infer(left)?; let right = self.infer(right)?; self.unify(left.tipo(), right.tipo(), right.location())?; return Ok(TypedExpr::BinOp { location, name, tipo: bool(), left: Box::new(left), right: Box::new(right), }); } BinOp::And => (bool(), bool()), BinOp::Or => (bool(), bool()), BinOp::LtInt => (int(), bool()), BinOp::LtEqInt => (int(), bool()), BinOp::GtEqInt => (int(), bool()), BinOp::GtInt => (int(), bool()), BinOp::AddInt => (int(), int()), BinOp::SubInt => (int(), int()), BinOp::MultInt => (int(), int()), BinOp::DivInt => (int(), int()), BinOp::ModInt => (int(), int()), }; let left = self.infer(left)?; self.unify( input_type.clone(), left.tipo(), left.type_defining_location(), ) .map_err(|e| e.operator_situation(name))?; let right = self.infer(right)?; self.unify(input_type, right.tipo(), right.type_defining_location()) .map_err(|e| e.operator_situation(name))?; Ok(TypedExpr::BinOp { location, name, tipo: output_type, left: Box::new(left), right: Box::new(right), }) } fn infer_record_update( &mut self, constructor: UntypedExpr, spread: RecordUpdateSpread, args: Vec, location: Span, ) -> Result { let (module, name): (Option, String) = match self.infer(constructor.clone())? { TypedExpr::ModuleSelect { module_alias, label, .. } => (Some(module_alias), label), TypedExpr::Var { name, .. } => (None, name), constructor => { return Err(Error::RecordUpdateInvalidConstructor { location: constructor.location(), }); } }; let value_constructor = self .environment .get_value_constructor(module.as_ref(), &name, location)? .clone(); // It must be a record with a field map for us to be able to update it let (field_map, constructors_count) = match &value_constructor.variant { ValueConstructorVariant::Record { field_map: Some(field_map), constructors_count, .. } => (field_map, *constructors_count), _ => { return Err(Error::RecordUpdateInvalidConstructor { location: constructor.location(), }); } }; // We can only update a record if it is the only variant of its type. // If a record has multiple variants it cannot be safely updated as it // could be one of the other variants. if constructors_count != 1 { return Err(Error::UpdateMultiConstructorType { location: constructor.location(), }); } // The type must be a function for it to be a record constructor let ret = match value_constructor.tipo.as_ref() { Type::Fn { ret, .. } => ret, _ => { return Err(Error::RecordUpdateInvalidConstructor { location: constructor.location(), }) } }; let spread = self.infer(*spread.base)?; let return_type = self.instantiate(ret.clone(), &mut HashMap::new()); // Check that the spread variable unifies with the return type of the constructor self.unify(return_type, spread.tipo(), spread.location())?; let mut arguments = Vec::new(); for UntypedRecordUpdateArg { label, value, location, } in args { let value = self.infer(value.clone())?; let spread_field = self.infer_known_record_access(spread.clone(), label.to_string(), location)?; // Check that the update argument unifies with the corresponding // field in the record contained within the spread variable. We // need to check the spread, and not the constructor, in order // to handle polymorphic types. self.unify(spread_field.tipo(), value.tipo(), value.location())?; match field_map.fields.get(&label) { None => { panic!("Failed to lookup record field after successfully inferring that field",) } Some(p) => arguments.push(TypedRecordUpdateArg { location, label: label.to_string(), value, index: *p, }), } } if arguments.is_empty() { self.environment .warnings .push(Warning::NoFieldsRecordUpdate { location }); } if arguments.len() == field_map.arity as usize { self.environment .warnings .push(Warning::AllFieldsRecordUpdate { location }); } Ok(TypedExpr::RecordUpdate { location, tipo: spread.tipo(), spread: Box::new(spread), args: arguments, }) } fn infer_negate( &mut self, location: Span, value: Box, ) -> Result { let value = self.infer(*value)?; self.unify(bool(), value.tipo(), value.location())?; Ok(TypedExpr::Negate { location, value: Box::new(value), }) } fn infer_field_access( &mut self, container: UntypedExpr, label: String, access_location: Span, ) -> Result { // Attempt to infer the container as a record access. If that fails, we may be shadowing the name // of an imported module, so attempt to infer the container as a module access. // TODO: Remove this cloning match self.infer_record_access(container.clone(), label.clone(), access_location) { Ok(record_access) => Ok(record_access), Err(err) => match container { UntypedExpr::Var { name, location, .. } => { let module_access = self.infer_module_access(&name, label, &location, access_location); // If the name is in the environment, use the original error from // inferring the record access, so that we can suggest possible // misspellings of field names if self.environment.scope.contains_key(&name) { module_access.map_err(|_| err) } else { module_access } } _ => Err(err), }, } } fn infer_module_access( &mut self, module_alias: &str, label: String, module_location: &Span, select_location: Span, ) -> Result { let (module_name, constructor) = { let (_, module) = self .environment .imported_modules .get(module_alias) .ok_or_else(|| Error::UnknownModule { name: module_alias.to_string(), location: *module_location, imported_modules: self .environment .imported_modules .keys() .map(|t| t.to_string()) .collect(), })?; let constructor = module .values .get(&label) .ok_or_else(|| Error::UnknownModuleValue { name: label.clone(), location: Span { start: module_location.end, end: select_location.end, }, module_name: module.name.clone(), value_constructors: module.values.keys().map(|t| t.to_string()).collect(), })?; // Register this imported module as having been used, to inform // warnings of unused imports later self.environment.unused_modules.remove(module_alias); (module.name.clone(), constructor.clone()) }; let tipo = self.instantiate(constructor.tipo, &mut HashMap::new()); let constructor = match &constructor.variant { variant @ ValueConstructorVariant::ModuleFn { name, module, .. } => { variant.to_module_value_constructor(Arc::clone(&tipo), module, name) } variant @ (ValueConstructorVariant::LocalVariable { .. } | ValueConstructorVariant::ModuleConstant { .. } | ValueConstructorVariant::Record { .. }) => { variant.to_module_value_constructor(Arc::clone(&tipo), &module_name, &label) } }; Ok(TypedExpr::ModuleSelect { label, tipo: Arc::clone(&tipo), location: select_location, module_name, module_alias: module_alias.to_string(), constructor, }) } fn infer_record_access( &mut self, record: UntypedExpr, label: String, location: Span, ) -> Result { // Infer the type of the (presumed) record let record = self.infer(record)?; self.infer_known_record_access(record, label, location) } fn infer_known_record_access( &mut self, record: TypedExpr, label: String, location: Span, ) -> Result { let record = Box::new(record); // If we don't yet know the type of the record then we cannot use any accessors if record.tipo().is_unbound() { return Err(Error::RecordAccessUnknownType { location: record.location(), }); } // Error constructor helper function let unknown_field = |fields| Error::UnknownRecordField { situation: None, typ: record.tipo(), location, label: label.clone(), fields, }; // Check to see if it's a Type that can have accessible fields let accessors = match collapse_links(record.tipo()).as_ref() { // A type in the current module which may have fields Type::App { module, name, .. } if module == self.environment.current_module => { self.environment.accessors.get(name) } // A type in another module which may have fields Type::App { module, name, .. } => self .environment .importable_modules .get(module) .and_then(|module| module.accessors.get(name)), _something_without_fields => return Err(unknown_field(vec![])), } .ok_or_else(|| unknown_field(vec![]))?; // Find the accessor, if the type has one with the same label let RecordAccessor { index, label, tipo } = accessors .accessors .get(&label) .ok_or_else(|| { unknown_field(accessors.accessors.keys().map(|t| t.to_string()).collect()) })? .clone(); // Unify the record type with the accessor's stored copy of the record type. // This ensure that the type parameters of the retrieved value have the correct // types for this instance of the record. let accessor_record_type = accessors.tipo.clone(); let mut type_vars = HashMap::new(); let accessor_record_type = self.instantiate(accessor_record_type, &mut type_vars); let tipo = self.instantiate(tipo, &mut type_vars); self.unify(accessor_record_type, record.tipo(), record.location())?; Ok(TypedExpr::RecordAccess { record, label, index, location, tipo, }) } fn infer_arg( &mut self, arg: UntypedArg, expected: Option>, ) -> Result { let Arg { arg_name, annotation, location, .. } = arg; let tipo = annotation .clone() .map(|t| self.type_from_annotation(&t)) .unwrap_or_else(|| Ok(self.new_unbound_var()))?; // If we know the expected type of the argument from its contextual // usage then unify the newly constructed type with the expected type. // We do this here because then there is more type information for the // function being type checked, resulting in better type errors and the // record field access syntax working. if let Some(expected) = expected { self.unify(expected, tipo.clone(), location)?; } Ok(Arg { arg_name, location, annotation, tipo, }) } fn infer_assignment( &mut self, pattern: UntypedPattern, value: UntypedExpr, kind: AssignmentKind, annotation: &Option, location: Span, ) -> Result { let value = self.in_new_scope(|value_typer| value_typer.infer(value))?; let mut value_typ = value.tipo(); // Check that any type annotation is accurate. let pattern = if let Some(ann) = annotation { let ann_typ = self .type_from_annotation(ann) .map(|t| self.instantiate(t, &mut HashMap::new()))?; self.unify( ann_typ.clone(), value_typ.clone(), value.type_defining_location(), )?; value_typ = ann_typ.clone(); // Ensure the pattern matches the type of the value PatternTyper::new(self.environment, &self.hydrator).unify( pattern, value_typ.clone(), Some(ann_typ), )? } else { // Ensure the pattern matches the type of the value PatternTyper::new(self.environment, &self.hydrator).unify( pattern, value_typ.clone(), None, )? }; // We currently only do limited exhaustiveness checking of custom types // at the top level of patterns. // Do not perform exhaustiveness checking if user explicitly used `assert`. if kind != AssignmentKind::Assert { if let Err(unmatched) = self.environment.check_exhaustiveness( vec![pattern.clone()], collapse_links(value_typ.clone()), location, ) { return Err(Error::NotExhaustivePatternMatch { location, unmatched, }); } } Ok(TypedExpr::Assignment { location, tipo: value_typ, kind, pattern, value: Box::new(value), }) } fn infer_call( &mut self, fun: UntypedExpr, args: Vec>, location: Span, ) -> Result { let (fun, args, tipo) = self .do_infer_call(fun, args, location) .map_err(|e| e.call_situation())?; Ok(TypedExpr::Call { location, tipo, args, fun: Box::new(fun), }) } fn infer_call_argument( &mut self, value: UntypedExpr, tipo: Arc, ) -> Result { let tipo = collapse_links(tipo); let value = match (&*tipo, value) { // If the argument is expected to be a function and we are passed a // function literal with the correct number of arguments then we // have special handling of this argument, passing in information // about what the expected arguments are. This extra information // when type checking the function body means that the // `record.field` access syntax can be used, and improves error // messages. ( Type::Fn { args: expected_arguments, .. }, UntypedExpr::Fn { arguments, body, return_annotation, location, is_capture: false, .. }, ) if expected_arguments.len() == arguments.len() => self.infer_fn( arguments, expected_arguments, *body, false, return_annotation, location, ), // Otherwise just perform normal type inference. (_, value) => self.infer(value), }?; self.unify(tipo, value.tipo(), value.location())?; Ok(value) } fn infer_clause( &mut self, clause: UntypedClause, subjects: &[Arc], ) -> Result { let Clause { pattern, alternative_patterns, guard, then, location, } = clause; let (guard, then, typed_pattern, typed_alternatives) = self.in_new_scope(|clause_typer| { // Check the types let (typed_pattern, typed_alternatives) = clause_typer.infer_clause_pattern( pattern, alternative_patterns, subjects, &location, )?; let guard = clause_typer.infer_optional_clause_guard(guard)?; let then = clause_typer.infer(then)?; Ok((guard, then, typed_pattern, typed_alternatives)) })?; Ok(Clause { location, pattern: typed_pattern, alternative_patterns: typed_alternatives, guard, then, }) } fn infer_clause_guard(&mut self, guard: UntypedClauseGuard) -> Result { match guard { ClauseGuard::Var { location, name, .. } => { let constructor = self.infer_value_constructor(&None, &name, &location)?; // We cannot support all values in guard expressions as the BEAM does not match &constructor.variant { ValueConstructorVariant::LocalVariable { .. } => (), ValueConstructorVariant::ModuleFn { .. } | ValueConstructorVariant::Record { .. } => { return Err(Error::NonLocalClauseGuardVariable { location, name }); } ValueConstructorVariant::ModuleConstant { literal, .. } => { return Ok(ClauseGuard::Constant(literal.clone())) } }; Ok(ClauseGuard::Var { location, name, tipo: constructor.tipo, }) } // ClauseGuard::TupleIndex { // location, // tuple, // index, // .. // } => { // let tuple = self.infer_clause_guard(*tuple)?; // match tuple.type_().as_ref() { // Type::Tuple { elems } => { // let type_ = elems // .get(index as usize) // .ok_or(Error::OutOfBoundsTupleIndex { // location, // index, // size: elems.len(), // })? // .clone(); // Ok(ClauseGuard::TupleIndex { // location, // index, // type_, // tuple: Box::new(tuple), // }) // } // typ if typ.is_unbound() => Err(Error::NotATupleUnbound { // location: tuple.location(), // }), // _ => Err(Error::NotATuple { // location: tuple.location(), // given: tuple.type_(), // }), // } // } ClauseGuard::And { location, left, right, .. } => { let left = self.infer_clause_guard(*left)?; self.unify(bool(), left.tipo(), left.location())?; let right = self.infer_clause_guard(*right)?; self.unify(bool(), right.tipo(), right.location())?; Ok(ClauseGuard::And { location, left: Box::new(left), right: Box::new(right), }) } ClauseGuard::Or { location, left, right, .. } => { let left = self.infer_clause_guard(*left)?; self.unify(bool(), left.tipo(), left.location())?; let right = self.infer_clause_guard(*right)?; self.unify(bool(), right.tipo(), right.location())?; Ok(ClauseGuard::Or { location, left: Box::new(left), right: Box::new(right), }) } ClauseGuard::Equals { location, left, right, .. } => { let left = self.infer_clause_guard(*left)?; let right = self.infer_clause_guard(*right)?; self.unify(left.tipo(), right.tipo(), location)?; Ok(ClauseGuard::Equals { location, left: Box::new(left), right: Box::new(right), }) } ClauseGuard::NotEquals { location, left, right, .. } => { let left = self.infer_clause_guard(*left)?; let right = self.infer_clause_guard(*right)?; self.unify(left.tipo(), right.tipo(), location)?; Ok(ClauseGuard::NotEquals { location, left: Box::new(left), right: Box::new(right), }) } ClauseGuard::GtInt { location, left, right, .. } => { let left = self.infer_clause_guard(*left)?; self.unify(int(), left.tipo(), left.location())?; let right = self.infer_clause_guard(*right)?; self.unify(int(), right.tipo(), right.location())?; Ok(ClauseGuard::GtInt { location, left: Box::new(left), right: Box::new(right), }) } ClauseGuard::GtEqInt { location, left, right, .. } => { let left = self.infer_clause_guard(*left)?; self.unify(int(), left.tipo(), left.location())?; let right = self.infer_clause_guard(*right)?; self.unify(int(), right.tipo(), right.location())?; Ok(ClauseGuard::GtEqInt { location, left: Box::new(left), right: Box::new(right), }) } ClauseGuard::LtInt { location, left, right, .. } => { let left = self.infer_clause_guard(*left)?; self.unify(int(), left.tipo(), left.location())?; let right = self.infer_clause_guard(*right)?; self.unify(int(), right.tipo(), right.location())?; Ok(ClauseGuard::LtInt { location, left: Box::new(left), right: Box::new(right), }) } ClauseGuard::LtEqInt { location, left, right, .. } => { let left = self.infer_clause_guard(*left)?; self.unify(int(), left.tipo(), left.location())?; let right = self.infer_clause_guard(*right)?; self.unify(int(), right.tipo(), right.location())?; Ok(ClauseGuard::LtEqInt { location, left: Box::new(left), right: Box::new(right), }) } ClauseGuard::Constant(constant) => { self.infer_const(&None, constant).map(ClauseGuard::Constant) } } } fn infer_clause_pattern( &mut self, pattern: UntypedMultiPattern, alternatives: Vec, subjects: &[Arc], location: &Span, ) -> Result<(TypedMultiPattern, Vec), Error> { let mut pattern_typer = PatternTyper::new(self.environment, &self.hydrator); let typed_pattern = pattern_typer.infer_multi_pattern(pattern, subjects, location)?; // Each case clause has one or more patterns that may match the // subject in order for the clause to be selected, so we must type // check every pattern. let mut typed_alternatives = Vec::with_capacity(alternatives.len()); for m in alternatives { typed_alternatives .push(pattern_typer.infer_alternative_multi_pattern(m, subjects, location)?); } Ok((typed_pattern, typed_alternatives)) } fn infer_const_tuple( &mut self, untyped_elements: Vec, location: Span, ) -> Result { let mut elements = Vec::with_capacity(untyped_elements.len()); for element in untyped_elements { let element = self.infer_const(&None, element)?; elements.push(element); } Ok(Constant::Tuple { elements, location }) } // TODO: extract the type annotation checking into a infer_module_const // function that uses this function internally pub fn infer_const( &mut self, annotation: &Option, value: UntypedConstant, ) -> Result { let inferred = match value { Constant::Int { location, value, .. } => Ok(Constant::Int { location, value }), Constant::String { location, value, .. } => Ok(Constant::String { location, value }), Constant::Tuple { elements, location, .. } => self.infer_const_tuple(elements, location), Constant::List { elements, location, .. } => self.infer_const_list(elements, location), Constant::ByteArray { location, bytes } => Ok(Constant::ByteArray { location, bytes }), Constant::Record { module, location, name, args, // field_map, is always None here because untyped not yet unified .. } if args.is_empty() => { // Register the module as having been used if it was imported if let Some(ref module) = &module { self.environment.unused_modules.remove(module); } // Type check the record constructor let constructor = self.infer_value_constructor(&module, &name, &location)?; let (tag, field_map) = match &constructor.variant { ValueConstructorVariant::Record { name, field_map, .. } => (name.clone(), field_map.clone()), ValueConstructorVariant::ModuleFn { .. } | ValueConstructorVariant::LocalVariable { .. } => { return Err(Error::NonLocalClauseGuardVariable { location, name }) } // TODO: remove this clone. Could use an rc instead ValueConstructorVariant::ModuleConstant { literal, .. } => { return Ok(literal.clone()) } }; Ok(Constant::Record { module, location, name, args: vec![], tipo: constructor.tipo, tag, field_map, }) } Constant::Record { module, location, name, mut args, // field_map, is always None here because untyped not yet unified .. } => { // Register the module as having been used if it was imported if let Some(ref module) = &module { self.environment.unused_modules.remove(module); } let constructor = self.infer_value_constructor(&module, &name, &location)?; let (tag, field_map) = match &constructor.variant { ValueConstructorVariant::Record { name, field_map, .. } => (name.clone(), field_map.clone()), ValueConstructorVariant::ModuleFn { .. } | ValueConstructorVariant::LocalVariable { .. } => { return Err(Error::NonLocalClauseGuardVariable { location, name }) } // TODO: remove this clone. Could be an rc instead ValueConstructorVariant::ModuleConstant { literal, .. } => { return Ok(literal.clone()) } }; // Pretty much all the other infer functions operate on UntypedExpr // or TypedExpr rather than ClauseGuard. To make things easier we // build the TypedExpr equivalent of the constructor and use that // TODO: resvisit this. It is rather awkward at present how we // have to convert to this other data structure. let fun = match &module { Some(module_name) => { let tipo = Arc::clone(&constructor.tipo); let module_name = self .environment .imported_modules .get(module_name) .expect("Failed to find previously located module import") .1 .name .clone(); let module_value_constructor = ModuleValueConstructor::Record { name: name.clone(), field_map: field_map.clone(), arity: args.len(), tipo: Arc::clone(&tipo), location: constructor.variant.location(), }; TypedExpr::ModuleSelect { label: name.clone(), module_alias: module_name.clone(), module_name, tipo, constructor: module_value_constructor, location, } } None => TypedExpr::Var { constructor, location, name: name.clone(), }, }; // This is basically the same code as do_infer_call_with_known_fun() // except the args are typed with infer_clause_guard() here. // This duplication is a bit awkward but it works! // Potentially this could be improved later match self.get_field_map(&fun, location)? { // The fun has a field map so labelled arguments may be present and need to be reordered. Some(field_map) => field_map.reorder(&mut args, location)?, // The fun has no field map and so we error if arguments have been labelled None => assert_no_labeled_arguments(&args)?, } let (mut args_types, return_type) = self.environment.match_fun_type( fun.tipo(), args.len(), fun.location(), location, )?; let mut typed_args = Vec::new(); for (tipo, arg) in args_types.iter_mut().zip(args) { let CallArg { label, value, location, } = arg; let value = self.infer_const(&None, value)?; self.unify(tipo.clone(), value.tipo(), value.location())?; typed_args.push(CallArg { label, value, location, }); } Ok(Constant::Record { module, location, name, args: typed_args, tipo: return_type, tag, field_map, }) } Constant::Var { location, module, name, .. } => { // Register the module as having been used if it was imported if let Some(ref module) = &module { self.environment.unused_modules.remove(module); } // Infer the type of this constant let constructor = self.infer_value_constructor(&module, &name, &location)?; match constructor.variant { ValueConstructorVariant::ModuleConstant { .. } | ValueConstructorVariant::ModuleFn { .. } => Ok(Constant::Var { location, module, name, tipo: Arc::clone(&constructor.tipo), constructor: Some(Box::from(constructor)), }), // constructor.variant cannot be a LocalVariable because module constants can // only be defined at module scope. It also cannot be a Record because then // this constant would have been parsed as a Constant::Record. Therefore this // code is unreachable. _ => unreachable!(), } } }?; // Check type annotation is accurate. if let Some(ann) = annotation { let const_ann = self.type_from_annotation(ann)?; self.unify(const_ann, inferred.tipo(), inferred.location())?; }; Ok(inferred) } fn infer_const_list( &mut self, untyped_elements: Vec, location: Span, ) -> Result { let tipo = self.new_unbound_var(); let mut elements = Vec::with_capacity(untyped_elements.len()); for element in untyped_elements { let element = self.infer_const(&None, element)?; self.unify(tipo.clone(), element.tipo(), element.location())?; elements.push(element); } Ok(Constant::List { elements, location, tipo: list(tipo), }) } fn infer_if( &mut self, branches: Vec1, final_else: UntypedExpr, location: Span, ) -> Result { let first = branches.first(); let condition = self.infer(first.condition.clone())?; self.unify(bool(), condition.tipo(), condition.type_defining_location())?; let body = self.infer(first.body.clone())?; let tipo = body.tipo(); let mut typed_branches = Vec1::new(TypedIfBranch { body, condition, location: first.location, }); for branch in &branches[1..] { let condition = self.infer(branch.condition.clone())?; self.unify(bool(), condition.tipo(), condition.type_defining_location())?; let body = self.infer(first.body.clone())?; self.unify(tipo.clone(), body.tipo(), body.type_defining_location())?; typed_branches.push(TypedIfBranch { body, condition, location: branch.location, }); } let typed_final_else = self.infer(final_else)?; self.unify( tipo.clone(), typed_final_else.tipo(), typed_final_else.type_defining_location(), )?; Ok(TypedExpr::If { location, branches: typed_branches, final_else: Box::new(typed_final_else), tipo, }) } fn infer_fn( &mut self, args: Vec, expected_args: &[Arc], body: UntypedExpr, is_capture: bool, return_annotation: Option, location: Span, ) -> Result { let (args, body) = self.do_infer_fn(args, expected_args, body, &return_annotation)?; let args_types = args.iter().map(|a| a.tipo.clone()).collect(); let tipo = function(args_types, body.tipo()); Ok(TypedExpr::Fn { location, tipo, is_capture, args, body: Box::new(body), return_annotation, }) } pub fn infer_fn_with_known_types( &mut self, args: Vec, body: UntypedExpr, return_type: Option>, ) -> Result<(Vec, TypedExpr), Error> { let (body_rigid_names, body_infer) = self.in_new_scope(|body_typer| { for (arg, t) in args.iter().zip(args.iter().map(|arg| arg.tipo.clone())) { match &arg.arg_name { ArgName::Named { name, .. } | ArgName::NamedLabeled { name, .. } => { body_typer.environment.insert_variable( name.to_string(), ValueConstructorVariant::LocalVariable { location: arg.location, }, t, ); body_typer.environment.init_usage( name.to_string(), EntityKind::Variable, arg.location, ); } ArgName::Discard { .. } | ArgName::LabeledDiscard { .. } => (), }; } (body_typer.hydrator.rigid_names(), body_typer.infer(body)) }); let body = body_infer.map_err(|e| e.with_unify_error_rigid_names(&body_rigid_names))?; // Check that any return type is accurate. if let Some(return_type) = return_type { self.unify(return_type, body.tipo(), body.type_defining_location()) .map_err(|e| { e.return_annotation_mismatch() .with_unify_error_rigid_names(&body_rigid_names) })?; } Ok((args, body)) } fn infer_int(&mut self, value: String, location: Span) -> TypedExpr { TypedExpr::Int { location, value, tipo: int(), } } fn infer_list( &mut self, elements: Vec, tail: Option>, location: Span, ) -> Result { let tipo = self.new_unbound_var(); let mut elems = Vec::new(); for elem in elements.into_iter() { let element = self.infer(elem)?; // Ensure they all have the same type self.unify(tipo.clone(), element.tipo(), location)?; elems.push(element) } // Type check the ..tail, if there is one let tipo = list(tipo); let tail = match tail { Some(tail) => { let tail = self.infer(*tail)?; // Ensure the tail has the same type as the preceeding elements self.unify(tipo.clone(), tail.tipo(), location)?; Some(Box::new(tail)) } None => None, }; Ok(TypedExpr::List { location, tipo, elements: elems, tail, }) } fn infer_optional_clause_guard( &mut self, guard: Option, ) -> Result, Error> { match guard { // If there is no guard we do nothing None => Ok(None), // If there is a guard we assert that it is of type Bool Some(guard) => { let guard = self.infer_clause_guard(guard)?; self.unify(bool(), guard.tipo(), guard.location())?; Ok(Some(guard)) } } } fn infer_pipeline(&mut self, expressions: Vec1) -> Result { PipeTyper::infer(self, expressions) } fn infer_seq(&mut self, location: Span, untyped: Vec) -> Result { let count = untyped.len(); let mut expressions = Vec::with_capacity(count); for (i, expression) in untyped.into_iter().enumerate() { let expression = self.infer(expression)?; // This isn't the final expression in the sequence, so call the // `expression_discarded` function to see if anything is being // discarded that we think shouldn't be. if i < count - 1 { self.expression_discarded(&expression); } expressions.push(expression); } Ok(TypedExpr::Sequence { location, expressions, }) } fn infer_string(&mut self, value: String, location: Span) -> TypedExpr { TypedExpr::String { location, value, tipo: string(), } } fn infer_tuple(&mut self, elems: Vec, location: Span) -> Result { let mut typed_elems = vec![]; for elem in elems { let typed_elem = self.infer(elem)?; typed_elems.push(typed_elem); } let tipo = tuple(typed_elems.iter().map(|e| e.tipo()).collect()); Ok(TypedExpr::Tuple { location, elems: typed_elems, tipo, }) } fn infer_todo(&mut self, location: Span, kind: TodoKind, label: Option) -> TypedExpr { let tipo = self.new_unbound_var(); self.environment.warnings.push(Warning::Todo { kind, location, tipo: tipo.clone(), }); TypedExpr::Todo { location, label, tipo, } } fn infer_trace(&mut self, then: UntypedExpr, location: Span) -> Result { // Check the type of the following code let then = self.infer(then)?; let tipo = then.tipo(); Ok(TypedExpr::Trace { location, tipo, then: Box::new(then), }) } fn infer_value_constructor( &mut self, module: &Option, name: &str, location: &Span, ) -> Result { let constructor = match module { // Look in the current scope for a binding with this name None => { let constructor = self.environment .get_variable(name) .cloned() .ok_or_else(|| Error::UnknownVariable { location: *location, name: name.to_string(), variables: self.environment.local_value_names(), })?; // Note whether we are using an ungeneralised function so that we can // tell if it is safe to generalise this function after inference has // completed. if matches!( &constructor.variant, ValueConstructorVariant::ModuleFn { .. } ) { let is_ungeneralised = self.environment.ungeneralised_functions.contains(name); self.ungeneralised_function_used = self.ungeneralised_function_used || is_ungeneralised; } // Register the value as seen for detection of unused values self.environment.increment_usage(name); constructor } // Look in an imported module for a binding with this name Some(module_name) => { let (_, module) = &self .environment .imported_modules .get(module_name) .ok_or_else(|| Error::UnknownModule { location: *location, name: module_name.to_string(), imported_modules: self .environment .imported_modules .keys() .map(|t| t.to_string()) .collect(), })?; module .values .get(name) .cloned() .ok_or_else(|| Error::UnknownModuleValue { location: *location, module_name: module_name.to_string(), name: name.to_string(), value_constructors: module.values.keys().map(|t| t.to_string()).collect(), })? } }; let ValueConstructor { public, variant, tipo, } = constructor; // Instantiate generic variables into unbound variables for this usage let tipo = self.instantiate(tipo, &mut HashMap::new()); Ok(ValueConstructor { public, variant, tipo, }) } fn infer_var(&mut self, name: String, location: Span) -> Result { let constructor = self.infer_value_constructor(&None, &name, &location)?; Ok(TypedExpr::Var { constructor, location, name, }) } fn infer_when( &mut self, subjects: Vec, clauses: Vec, location: Span, ) -> Result { let subjects_count = subjects.len(); let mut typed_subjects = Vec::with_capacity(subjects_count); let mut subject_types = Vec::with_capacity(subjects_count); let mut typed_clauses = Vec::with_capacity(clauses.len()); let return_type = self.new_unbound_var(); for subject in subjects { let subject = self.in_new_scope(|subject_typer| { let subject = subject_typer.infer(subject)?; Ok(subject) })?; subject_types.push(subject.tipo()); typed_subjects.push(subject); } for clause in clauses { let typed_clause = self.infer_clause(clause, &subject_types)?; self.unify( return_type.clone(), typed_clause.then.tipo(), typed_clause.location(), ) .map_err(|e| e.case_clause_mismatch())?; typed_clauses.push(typed_clause); } if let Err(unmatched) = self.check_when_exhaustiveness(subjects_count, &subject_types, &typed_clauses, location) { return Err(Error::NotExhaustivePatternMatch { location, unmatched, }); } Ok(TypedExpr::When { location, tipo: return_type, subjects: typed_subjects, clauses: typed_clauses, }) } fn instantiate(&mut self, t: Arc, ids: &mut HashMap>) -> Arc { self.environment.instantiate(t, ids, &self.hydrator) } pub fn new(environment: &'a mut Environment<'b>) -> Self { let mut hydrator = Hydrator::new(); hydrator.permit_holes(true); Self { hydrator, environment, ungeneralised_function_used: false, } } pub fn new_unbound_var(&mut self) -> Arc { self.environment.new_unbound_var() } pub fn type_from_annotation(&mut self, annotation: &Annotation) -> Result, Error> { self.hydrator .type_from_annotation(annotation, self.environment) } fn unify(&mut self, t1: Arc, t2: Arc, location: Span) -> Result<(), Error> { self.environment.unify(t1, t2, location) } }