feat: start expr inference
This commit is contained in:
		
							parent
							
								
									81c87ab4da
								
							
						
					
					
						commit
						cabc653167
					
				|  | @ -77,6 +77,7 @@ version = "0.0.20" | |||
| dependencies = [ | ||||
|  "chumsky", | ||||
|  "internment", | ||||
|  "itertools", | ||||
|  "miette", | ||||
|  "pretty_assertions", | ||||
|  "thiserror", | ||||
|  | @ -294,6 +295,12 @@ version = "0.1.13" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "either" | ||||
| version = "1.8.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "fastrand" | ||||
| version = "1.8.0" | ||||
|  | @ -443,6 +450,15 @@ version = "1.1.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "itertools" | ||||
| version = "0.10.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" | ||||
| dependencies = [ | ||||
|  "either", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "itoa" | ||||
| version = "1.0.3" | ||||
|  |  | |||
|  | @ -4,7 +4,12 @@ use std::{ | |||
|     path::{Path, PathBuf}, | ||||
| }; | ||||
| 
 | ||||
| use aiken_lang::{ast::ModuleKind, builtins, tipo, IdGenerator}; | ||||
| use aiken_lang::{ | ||||
|     ast::ModuleKind, | ||||
|     builtins, | ||||
|     tipo::{self, TypeInfo}, | ||||
|     IdGenerator, | ||||
| }; | ||||
| 
 | ||||
| use crate::{ | ||||
|     config::Config, | ||||
|  | @ -39,7 +44,7 @@ pub struct Project { | |||
|     config: Config, | ||||
|     defined_modules: HashMap<String, PathBuf>, | ||||
|     id_gen: IdGenerator, | ||||
|     module_types: HashMap<String, tipo::Module>, | ||||
|     module_types: HashMap<String, TypeInfo>, | ||||
|     root: PathBuf, | ||||
|     sources: Vec<Source>, | ||||
|     pub warnings: Vec<Warning>, | ||||
|  | @ -164,19 +169,19 @@ impl Project { | |||
|             { | ||||
|                 let mut type_warnings = Vec::new(); | ||||
| 
 | ||||
|                 let ast = tipo::infer::module( | ||||
|                     &self.id_gen, | ||||
|                     ast, | ||||
|                     kind, | ||||
|                     &self.config.name, | ||||
|                     &self.module_types, | ||||
|                     &mut type_warnings, | ||||
|                 ) | ||||
|                 .map_err(|error| Error::Type { | ||||
|                     path: path.clone(), | ||||
|                     src: code.clone(), | ||||
|                     error, | ||||
|                 })?; | ||||
|                 let ast = ast | ||||
|                     .infer( | ||||
|                         &self.id_gen, | ||||
|                         kind, | ||||
|                         &self.config.name, | ||||
|                         &self.module_types, | ||||
|                         &mut type_warnings, | ||||
|                     ) | ||||
|                     .map_err(|error| Error::Type { | ||||
|                         path: path.clone(), | ||||
|                         src: code.clone(), | ||||
|                         error, | ||||
|                     })?; | ||||
| 
 | ||||
|                 // Register any warnings emitted as type warnings
 | ||||
|                 let type_warnings = type_warnings | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ authors = ["Lucas Rosa <x@rvcas.dev>", "Kasey White <kwhitemsg@gmail.com>"] | |||
| [dependencies] | ||||
| chumsky = "0.8.0" | ||||
| internment = "0.7.0" | ||||
| itertools = "0.10.5" | ||||
| miette = "5.2.0" | ||||
| thiserror = "1.0.37" | ||||
| vec1 = "1.8.0" | ||||
|  |  | |||
|  | @ -1,15 +1,19 @@ | |||
| use std::{collections::HashMap, fmt, ops::Range, sync::Arc}; | ||||
| use std::{fmt, ops::Range, sync::Arc}; | ||||
| 
 | ||||
| use internment::Intern; | ||||
| 
 | ||||
| use crate::{ | ||||
|     builtins, | ||||
|     expr::{TypedExpr, UntypedExpr}, | ||||
|     tipo::{self, PatternConstructor, Type, ValueConstructor}, | ||||
|     tipo::{fields::FieldMap, PatternConstructor, Type, TypeInfo, ValueConstructor}, | ||||
| }; | ||||
| 
 | ||||
| pub const ASSERT_VARIABLE: &str = "_try"; | ||||
| pub const CAPTURE_VARIABLE: &str = "_capture"; | ||||
| pub const PIPE_VARIABLE: &str = "_pipe"; | ||||
| pub const TRY_VARIABLE: &str = "_try"; | ||||
| 
 | ||||
| pub type TypedModule = Module<tipo::Module, TypedDefinition>; | ||||
| pub type TypedModule = Module<TypeInfo, TypedDefinition>; | ||||
| pub type UntypedModule = Module<(), UntypedDefinition>; | ||||
| 
 | ||||
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||||
|  | @ -133,11 +137,6 @@ pub enum Constant<T, RecordTag> { | |||
|         value: String, | ||||
|     }, | ||||
| 
 | ||||
|     Pair { | ||||
|         location: Span, | ||||
|         elements: Vec<Self>, | ||||
|     }, | ||||
| 
 | ||||
|     List { | ||||
|         location: Span, | ||||
|         elements: Vec<Self>, | ||||
|  | @ -154,9 +153,9 @@ pub enum Constant<T, RecordTag> { | |||
|         field_map: Option<FieldMap>, | ||||
|     }, | ||||
| 
 | ||||
|     ByteString { | ||||
|     ByteArray { | ||||
|         location: Span, | ||||
|         // segments: Vec<BitStringSegment<Self, T>>,
 | ||||
|         bytes: Vec<u8>, | ||||
|     }, | ||||
| 
 | ||||
|     Var { | ||||
|  | @ -168,6 +167,39 @@ pub enum Constant<T, RecordTag> { | |||
|     }, | ||||
| } | ||||
| 
 | ||||
| impl TypedConstant { | ||||
|     pub fn tipo(&self) -> Arc<Type> { | ||||
|         match self { | ||||
|             Constant::Int { .. } => builtins::int(), | ||||
|             Constant::String { .. } => builtins::string(), | ||||
|             Constant::ByteArray { .. } => builtins::byte_array(), | ||||
|             Constant::List { tipo, .. } | ||||
|             | Constant::Record { tipo, .. } | ||||
|             | Constant::Var { tipo, .. } => tipo.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<A, B> Constant<A, B> { | ||||
|     pub fn location(&self) -> Span { | ||||
|         match self { | ||||
|             Constant::Int { location, .. } | ||||
|             | Constant::List { location, .. } | ||||
|             | Constant::String { location, .. } | ||||
|             | Constant::Record { location, .. } | ||||
|             | Constant::ByteArray { location, .. } | ||||
|             | Constant::Var { location, .. } => *location, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_simple(&self) -> bool { | ||||
|         matches!( | ||||
|             self, | ||||
|             Self::Int { .. } | Self::ByteArray { .. } | Self::String { .. } | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub struct CallArg<A> { | ||||
|     pub label: Option<String>, | ||||
|  | @ -175,44 +207,6 @@ pub struct CallArg<A> { | |||
|     pub value: A, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub struct FieldMap { | ||||
|     pub arity: usize, | ||||
|     pub fields: HashMap<String, usize>, | ||||
| } | ||||
| 
 | ||||
| impl FieldMap { | ||||
|     pub fn new(arity: usize) -> Self { | ||||
|         Self { | ||||
|             arity, | ||||
|             fields: HashMap::new(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn insert( | ||||
|         &mut self, | ||||
|         label: String, | ||||
|         index: usize, | ||||
|         location: &Span, | ||||
|     ) -> Result<(), tipo::error::Error> { | ||||
|         match self.fields.insert(label.clone(), index) { | ||||
|             Some(_) => Err(tipo::error::Error::DuplicateField { | ||||
|                 label, | ||||
|                 location: *location, | ||||
|             }), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn into_option(self) -> Option<Self> { | ||||
|         if self.fields.is_empty() { | ||||
|             None | ||||
|         } else { | ||||
|             Some(self) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub struct RecordConstructor<T> { | ||||
|     pub location: Span, | ||||
|  | @ -232,6 +226,7 @@ pub struct RecordConstructorArg<T> { | |||
|     pub doc: Option<String>, | ||||
| } | ||||
| 
 | ||||
| pub type TypedArg = Arg<Arc<Type>>; | ||||
| pub type UntypedArg = Arg<()>; | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
|  | @ -242,6 +237,21 @@ pub struct Arg<T> { | |||
|     pub tipo: T, | ||||
| } | ||||
| 
 | ||||
| impl<A> Arg<A> { | ||||
|     pub fn set_type<B>(self, tipo: B) -> Arg<B> { | ||||
|         Arg { | ||||
|             tipo, | ||||
|             arg_name: self.arg_name, | ||||
|             location: self.location, | ||||
|             annotation: self.annotation, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_variable_name(&self) -> Option<&str> { | ||||
|         self.arg_name.get_variable_name() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub enum ArgName { | ||||
|     Discard { | ||||
|  | @ -264,6 +274,15 @@ pub enum ArgName { | |||
|     }, | ||||
| } | ||||
| 
 | ||||
| impl ArgName { | ||||
|     pub fn get_variable_name(&self) -> Option<&str> { | ||||
|         match self { | ||||
|             ArgName::Discard { .. } | ArgName::LabeledDiscard { .. } => None, | ||||
|             ArgName::NamedLabeled { name, .. } | ArgName::Named { name, .. } => Some(name), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub struct UnqualifiedImport { | ||||
|     pub location: Span, | ||||
|  | @ -272,6 +291,16 @@ pub struct UnqualifiedImport { | |||
|     pub layer: Layer, | ||||
| } | ||||
| 
 | ||||
| impl UnqualifiedImport { | ||||
|     pub fn variable_name(&self) -> &str { | ||||
|         self.as_name.as_deref().unwrap_or(&self.name) | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_value(&self) -> bool { | ||||
|         self.layer.is_value() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // TypeAst
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub enum Annotation { | ||||
|  | @ -383,6 +412,13 @@ impl Default for Layer { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Layer { | ||||
|     /// Returns `true` if the layer is [`Value`].
 | ||||
|     pub fn is_value(&self) -> bool { | ||||
|         matches!(self, Self::Value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| pub enum BinOp { | ||||
|     // Boolean logic
 | ||||
|  |  | |||
|  | @ -2,7 +2,10 @@ use std::{cell::RefCell, collections::HashMap, sync::Arc}; | |||
| 
 | ||||
| use crate::{ | ||||
|     ast::{ModuleKind, Span}, | ||||
|     tipo::{self, Type, TypeConstructor, TypeVar, ValueConstructor, ValueConstructorVariant}, | ||||
|     tipo::{ | ||||
|         self, fields::FieldMap, Type, TypeConstructor, TypeVar, ValueConstructor, | ||||
|         ValueConstructorVariant, | ||||
|     }, | ||||
|     IdGenerator, | ||||
| }; | ||||
| 
 | ||||
|  | @ -16,9 +19,9 @@ const STRING: &str = "String"; | |||
| 
 | ||||
| /// Build a prelude that can be injected
 | ||||
| /// into a compiler pipeline
 | ||||
| pub fn prelude(id_gen: &IdGenerator) -> tipo::Module { | ||||
|     let mut prelude = tipo::Module { | ||||
|         name: vec!["aiken".to_string()], | ||||
| pub fn prelude(id_gen: &IdGenerator) -> tipo::TypeInfo { | ||||
|     let mut prelude = tipo::TypeInfo { | ||||
|         name: "aiken".to_string(), | ||||
|         package: "".to_string(), | ||||
|         kind: ModuleKind::Lib, | ||||
|         types: HashMap::new(), | ||||
|  | @ -64,7 +67,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module { | |||
|             ValueConstructorVariant::Record { | ||||
|                 module: "".into(), | ||||
|                 name: "True".to_string(), | ||||
|                 field_map: None, | ||||
|                 field_map: None::<FieldMap>, | ||||
|                 arity: 0, | ||||
|                 location: Span::empty(), | ||||
|                 constructors_count: 2, | ||||
|  | @ -79,7 +82,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module { | |||
|             ValueConstructorVariant::Record { | ||||
|                 module: "".into(), | ||||
|                 name: "False".to_string(), | ||||
|                 field_map: None, | ||||
|                 field_map: None::<FieldMap>, | ||||
|                 arity: 0, | ||||
|                 location: Span::empty(), | ||||
|                 constructors_count: 2, | ||||
|  | @ -132,7 +135,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module { | |||
|                 module: "".into(), | ||||
|                 name: NIL.to_string(), | ||||
|                 arity: 0, | ||||
|                 field_map: None, | ||||
|                 field_map: None::<FieldMap>, | ||||
|                 location: Span::empty(), | ||||
|                 constructors_count: 1, | ||||
|             }, | ||||
|  | @ -179,7 +182,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module { | |||
|             ValueConstructorVariant::Record { | ||||
|                 module: "".into(), | ||||
|                 name: "Ok".to_string(), | ||||
|                 field_map: None, | ||||
|                 field_map: None::<FieldMap>, | ||||
|                 arity: 1, | ||||
|                 location: Span::empty(), | ||||
|                 constructors_count: 2, | ||||
|  | @ -196,7 +199,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module { | |||
|             ValueConstructorVariant::Record { | ||||
|                 module: "".into(), | ||||
|                 name: "Error".to_string(), | ||||
|                 field_map: None, | ||||
|                 field_map: None::<FieldMap>, | ||||
|                 arity: 1, | ||||
|                 location: Span::empty(), | ||||
|                 constructors_count: 2, | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ use crate::{ | |||
|         Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, IfBranch, Pattern, | ||||
|         RecordUpdateSpread, Span, TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg, | ||||
|     }, | ||||
|     builtins::{bool, nil}, | ||||
|     tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor}, | ||||
| }; | ||||
| 
 | ||||
|  | @ -18,16 +19,16 @@ pub enum TypedExpr { | |||
|         value: String, | ||||
|     }, | ||||
| 
 | ||||
|     Float { | ||||
|     String { | ||||
|         location: Span, | ||||
|         tipo: Arc<Type>, | ||||
|         value: String, | ||||
|     }, | ||||
| 
 | ||||
|     String { | ||||
|     ByteArray { | ||||
|         location: Span, | ||||
|         tipo: Arc<Type>, | ||||
|         value: String, | ||||
|         bytes: Vec<u8>, | ||||
|     }, | ||||
| 
 | ||||
|     Sequence { | ||||
|  | @ -161,6 +162,101 @@ pub enum TypedExpr { | |||
|     }, | ||||
| } | ||||
| 
 | ||||
| impl TypedExpr { | ||||
|     pub fn tipo(&self) -> Arc<Type> { | ||||
|         match self { | ||||
|             Self::Negate { .. } => bool(), | ||||
|             Self::Var { constructor, .. } => constructor.tipo.clone(), | ||||
|             Self::Try { then, .. } => then.tipo(), | ||||
|             Self::Fn { tipo, .. } | ||||
|             | Self::Int { tipo, .. } | ||||
|             | Self::Todo { tipo, .. } | ||||
|             | Self::When { tipo, .. } | ||||
|             | Self::List { tipo, .. } | ||||
|             | Self::Call { tipo, .. } | ||||
|             | Self::If { tipo, .. } | ||||
|             | Self::BinOp { tipo, .. } | ||||
|             | Self::Tuple { tipo, .. } | ||||
|             | Self::String { tipo, .. } | ||||
|             | Self::ByteArray { tipo, .. } | ||||
|             | Self::TupleIndex { tipo, .. } | ||||
|             | Self::Assignment { tipo, .. } | ||||
|             | Self::ModuleSelect { tipo, .. } | ||||
|             | Self::RecordAccess { tipo, .. } | ||||
|             | Self::RecordUpdate { tipo, .. } => tipo.clone(), | ||||
|             Self::Pipeline { expressions, .. } | Self::Sequence { expressions, .. } => { | ||||
|                 expressions.last().map(TypedExpr::tipo).unwrap_or_else(nil) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn type_defining_location(&self) -> Span { | ||||
|         match self { | ||||
|             Self::Fn { location, .. } | ||||
|             | Self::Int { location, .. } | ||||
|             | Self::Try { location, .. } | ||||
|             | Self::Var { location, .. } | ||||
|             | Self::Todo { location, .. } | ||||
|             | Self::When { location, .. } | ||||
|             | Self::Call { location, .. } | ||||
|             | Self::List { location, .. } | ||||
|             | Self::BinOp { location, .. } | ||||
|             | Self::Tuple { location, .. } | ||||
|             | Self::String { location, .. } | ||||
|             | Self::Negate { location, .. } | ||||
|             | Self::Pipeline { location, .. } | ||||
|             | Self::ByteArray { location, .. } | ||||
|             | Self::Assignment { location, .. } | ||||
|             | Self::TupleIndex { location, .. } | ||||
|             | Self::ModuleSelect { location, .. } | ||||
|             | Self::RecordAccess { location, .. } | ||||
|             | Self::RecordUpdate { location, .. } => *location, | ||||
| 
 | ||||
|             Self::If { | ||||
|                 location, | ||||
|                 branches, | ||||
|                 final_else, | ||||
|                 tipo, | ||||
|             } => branches.first().body.type_defining_location(), | ||||
| 
 | ||||
|             Self::Sequence { | ||||
|                 expressions, | ||||
|                 location, | ||||
|                 .. | ||||
|             } => expressions | ||||
|                 .last() | ||||
|                 .map(TypedExpr::location) | ||||
|                 .unwrap_or(*location), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn location(&self) -> Span { | ||||
|         match self { | ||||
|             Self::Fn { location, .. } | ||||
|             | Self::Try { location, .. } | ||||
|             | Self::Int { location, .. } | ||||
|             | Self::Var { location, .. } | ||||
|             | Self::Todo { location, .. } | ||||
|             | Self::When { location, .. } | ||||
|             | Self::Call { location, .. } | ||||
|             | Self::If { location, .. } | ||||
|             | Self::List { location, .. } | ||||
|             | Self::BinOp { location, .. } | ||||
|             | Self::Tuple { location, .. } | ||||
|             | Self::String { location, .. } | ||||
|             | Self::Negate { location, .. } | ||||
|             | Self::Sequence { location, .. } | ||||
|             | Self::Pipeline { location, .. } | ||||
|             | Self::ByteArray { location, .. } | ||||
|             | Self::Assignment { location, .. } | ||||
|             | Self::TupleIndex { location, .. } | ||||
|             | Self::ModuleSelect { location, .. } | ||||
|             | Self::RecordAccess { location, .. } | ||||
|             | Self::RecordUpdate { location, .. } => *location, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub enum UntypedExpr { | ||||
|     Int { | ||||
|  | @ -168,11 +264,6 @@ pub enum UntypedExpr { | |||
|         value: String, | ||||
|     }, | ||||
| 
 | ||||
|     Float { | ||||
|         location: Span, | ||||
|         value: String, | ||||
|     }, | ||||
| 
 | ||||
|     String { | ||||
|         location: Span, | ||||
|         value: String, | ||||
|  | @ -203,9 +294,9 @@ pub enum UntypedExpr { | |||
|     }, | ||||
| 
 | ||||
|     Call { | ||||
|         location: Span, | ||||
|         fun: Box<Self>, | ||||
|         arguments: Vec<CallArg<Self>>, | ||||
|         fun: Box<Self>, | ||||
|         location: Span, | ||||
|     }, | ||||
| 
 | ||||
|     BinOp { | ||||
|  | @ -215,6 +306,11 @@ pub enum UntypedExpr { | |||
|         right: Box<Self>, | ||||
|     }, | ||||
| 
 | ||||
|     ByteArray { | ||||
|         location: Span, | ||||
|         bytes: Vec<u8>, | ||||
|     }, | ||||
| 
 | ||||
|     PipeLine { | ||||
|         expressions: Vec1<Self>, | ||||
|     }, | ||||
|  | @ -344,7 +440,7 @@ impl UntypedExpr { | |||
|             | Self::When { location, .. } | ||||
|             | Self::Call { location, .. } | ||||
|             | Self::List { location, .. } | ||||
|             | Self::Float { location, .. } | ||||
|             | Self::ByteArray { location, .. } | ||||
|             | Self::BinOp { location, .. } | ||||
|             | Self::Tuple { location, .. } | ||||
|             | Self::String { location, .. } | ||||
|  |  | |||
|  | @ -1,11 +1,18 @@ | |||
| use std::{cell::RefCell, collections::HashMap, sync::Arc}; | ||||
| use std::{cell::RefCell, collections::HashMap, ops::Deref, sync::Arc}; | ||||
| 
 | ||||
| use crate::ast::{Constant, FieldMap, ModuleKind, Span, TypedConstant}; | ||||
| use crate::{ | ||||
|     ast::{Constant, ModuleKind, Span, TypedConstant}, | ||||
|     tipo::fields::FieldMap, | ||||
| }; | ||||
| 
 | ||||
| use self::environment::Environment; | ||||
| 
 | ||||
| mod environment; | ||||
| pub mod error; | ||||
| mod expr; | ||||
| pub mod fields; | ||||
| mod hydrator; | ||||
| pub mod infer; | ||||
| mod infer; | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub enum Type { | ||||
|  | @ -41,6 +48,161 @@ pub enum Type { | |||
|     // Tuple { elems: Vec<Arc<Type>> },
 | ||||
| } | ||||
| 
 | ||||
| impl Type { | ||||
|     pub fn is_result_constructor(&self) -> bool { | ||||
|         match self { | ||||
|             Type::Fn { ret, .. } => ret.is_result(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_result(&self) -> bool { | ||||
|         matches!(self, Self::App { name, module, .. } if "Result" == name && module.is_empty()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_unbound(&self) -> bool { | ||||
|         matches!(self, Self::Var { tipo } if tipo.borrow().is_unbound()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn return_type(&self) -> Option<Arc<Self>> { | ||||
|         match self { | ||||
|             Self::Fn { ret, .. } => Some(ret.clone()), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn function_types(&self) -> Option<(Vec<Arc<Self>>, Arc<Self>)> { | ||||
|         match self { | ||||
|             Self::Fn { args, ret, .. } => Some((args.clone(), ret.clone())), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_nil(&self) -> bool { | ||||
|         match self { | ||||
|             Self::App { module, name, .. } if "Nil" == name && module.is_empty() => true, | ||||
|             Self::Var { tipo } => tipo.borrow().is_nil(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_bool(&self) -> bool { | ||||
|         match self { | ||||
|             Self::App { module, name, .. } if "Bool" == name && module.is_empty() => true, | ||||
|             Self::Var { tipo } => tipo.borrow().is_bool(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_int(&self) -> bool { | ||||
|         match self { | ||||
|             Self::App { module, name, .. } if "Int" == name && module.is_empty() => true, | ||||
|             Self::Var { tipo } => tipo.borrow().is_int(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_bytearray(&self) -> bool { | ||||
|         match self { | ||||
|             Self::App { module, name, .. } if "ByteArray" == name && module.is_empty() => true, | ||||
|             Self::Var { tipo } => tipo.borrow().is_bytearray(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_string(&self) -> bool { | ||||
|         match self { | ||||
|             Self::App { module, name, .. } if "String" == name && module.is_empty() => true, | ||||
|             Self::Var { tipo } => tipo.borrow().is_string(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Get the args for the type if the type is a specific `Type::App`.
 | ||||
|     /// Returns None if the type is not a `Type::App` or is an incorrect `Type:App`
 | ||||
|     ///
 | ||||
|     /// This function is currently only used for finding the `List` type.
 | ||||
|     pub fn get_app_args( | ||||
|         &self, | ||||
|         public: bool, | ||||
|         module: &String, | ||||
|         name: &str, | ||||
|         arity: usize, | ||||
|         environment: &mut Environment<'_>, | ||||
|     ) -> Option<Vec<Arc<Self>>> { | ||||
|         match self { | ||||
|             Self::App { | ||||
|                 module: m, | ||||
|                 name: n, | ||||
|                 args, | ||||
|                 .. | ||||
|             } => { | ||||
|                 if module == m && name == n && args.len() == arity { | ||||
|                     Some(args.clone()) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             Self::Var { tipo } => { | ||||
|                 let args: Vec<_> = match tipo.borrow().deref() { | ||||
|                     TypeVar::Link { tipo } => { | ||||
|                         return tipo.get_app_args(public, module, name, arity, environment); | ||||
|                     } | ||||
| 
 | ||||
|                     TypeVar::Unbound { .. } => { | ||||
|                         (0..arity).map(|_| environment.new_unbound_var()).collect() | ||||
|                     } | ||||
| 
 | ||||
|                     TypeVar::Generic { .. } => return None, | ||||
|                 }; | ||||
| 
 | ||||
|                 // We are an unbound type variable! So convert us to a type link
 | ||||
|                 // to the desired type.
 | ||||
|                 *tipo.borrow_mut() = TypeVar::Link { | ||||
|                     tipo: Arc::new(Self::App { | ||||
|                         name: name.to_string(), | ||||
|                         module: module.to_owned(), | ||||
|                         args: args.clone(), | ||||
|                         public, | ||||
|                     }), | ||||
|                 }; | ||||
|                 Some(args) | ||||
|             } | ||||
| 
 | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn find_private_type(&self) -> Option<Self> { | ||||
|         match self { | ||||
|             Self::App { public: false, .. } => Some(self.clone()), | ||||
| 
 | ||||
|             Self::App { args, .. } => args.iter().find_map(|t| t.find_private_type()), | ||||
| 
 | ||||
|             // Self::Tuple { elems, .. } => elems.iter().find_map(|t| t.find_private_type()),
 | ||||
|             Self::Fn { ret, args, .. } => ret | ||||
|                 .find_private_type() | ||||
|                 .or_else(|| args.iter().find_map(|t| t.find_private_type())), | ||||
| 
 | ||||
|             Self::Var { tipo, .. } => match tipo.borrow().deref() { | ||||
|                 TypeVar::Unbound { .. } => None, | ||||
| 
 | ||||
|                 TypeVar::Generic { .. } => None, | ||||
| 
 | ||||
|                 TypeVar::Link { tipo, .. } => tipo.find_private_type(), | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn fn_arity(&self) -> Option<usize> { | ||||
|         match self { | ||||
|             Self::Fn { args, .. } => Some(args.len()), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
| pub enum TypeVar { | ||||
|     /// Unbound is an unbound variable. It is one specific type but we don't
 | ||||
|  | @ -72,6 +234,41 @@ impl TypeVar { | |||
|     pub fn is_unbound(&self) -> bool { | ||||
|         matches!(self, Self::Unbound { .. }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_nil(&self) -> bool { | ||||
|         match self { | ||||
|             Self::Link { tipo } => tipo.is_nil(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_bool(&self) -> bool { | ||||
|         match self { | ||||
|             Self::Link { tipo } => tipo.is_bool(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_int(&self) -> bool { | ||||
|         match self { | ||||
|             Self::Link { tipo } => tipo.is_int(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_bytearray(&self) -> bool { | ||||
|         match self { | ||||
|             Self::Link { tipo } => tipo.is_bytearray(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_string(&self) -> bool { | ||||
|         match self { | ||||
|             Self::Link { tipo } => tipo.is_string(), | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
|  | @ -89,6 +286,14 @@ impl ValueConstructor { | |||
|             tipo, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn field_map(&self) -> Option<&FieldMap> { | ||||
|         match &self.variant { | ||||
|             ValueConstructorVariant::ModuleFn { field_map, .. } | ||||
|             | ValueConstructorVariant::Record { field_map, .. } => field_map.as_ref(), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq)] | ||||
|  | @ -123,9 +328,20 @@ pub enum ValueConstructorVariant { | |||
|     }, | ||||
| } | ||||
| 
 | ||||
| impl ValueConstructorVariant { | ||||
|     pub fn location(&self) -> Span { | ||||
|         match self { | ||||
|             ValueConstructorVariant::LocalVariable { location } | ||||
|             | ValueConstructorVariant::ModuleConstant { location, .. } | ||||
|             | ValueConstructorVariant::ModuleFn { location, .. } | ||||
|             | ValueConstructorVariant::Record { location, .. } => *location, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Module { | ||||
|     pub name: Vec<String>, | ||||
| pub struct TypeInfo { | ||||
|     pub name: String, | ||||
|     pub kind: ModuleKind, | ||||
|     pub package: String, | ||||
|     pub types: HashMap<String, TypeConstructor>, | ||||
|  | @ -171,7 +387,7 @@ pub enum ModuleValueConstructor { | |||
|     Record { | ||||
|         name: String, | ||||
|         arity: usize, | ||||
|         type_: Arc<Type>, | ||||
|         tipo: Arc<Type>, | ||||
|         field_map: Option<FieldMap>, | ||||
|         location: Span, | ||||
|     }, | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,4 +1,4 @@ | |||
| use std::sync::Arc; | ||||
| use std::{collections::HashMap, sync::Arc}; | ||||
| 
 | ||||
| use miette::Diagnostic; | ||||
| 
 | ||||
|  | @ -6,8 +6,19 @@ use crate::ast::{BinOp, Span, TodoKind}; | |||
| 
 | ||||
| use super::Type; | ||||
| 
 | ||||
| // use aiken/pub
 | ||||
| 
 | ||||
| // pub fn do_thing() { pub.other() }
 | ||||
| 
 | ||||
| #[derive(Debug, thiserror::Error, Diagnostic)] | ||||
| pub enum Error { | ||||
|     #[error("duplicate argument {label}")] | ||||
|     DuplicateArgument { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         label: String, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("duplicate const {name}")] | ||||
|     DuplicateConstName { | ||||
|         #[label] | ||||
|  | @ -49,6 +60,15 @@ pub enum Error { | |||
|         name: String, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("incorrect arity expected {expected} but given {given}")] | ||||
|     IncorrectArity { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         expected: usize, | ||||
|         given: usize, | ||||
|         labels: Vec<String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("{name} has incorrect type arity expected {expected} but given {given}")] | ||||
|     IncorrectTypeArity { | ||||
|         location: Span, | ||||
|  | @ -57,18 +77,59 @@ pub enum Error { | |||
|         given: usize, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("not a function")] | ||||
|     NotFn { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         tipo: Arc<Type>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("{name} contains keyword {keyword}")] | ||||
|     KeywordInModuleName { name: String, keyword: String }, | ||||
| 
 | ||||
|     #[error("clause guard {name} is not local")] | ||||
|     NonLocalClauseGuardVariable { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         name: String, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("positional argument after labeled")] | ||||
|     PositionalArgumentAfterLabeled { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("private type leaked")] | ||||
|     PrivateTypeLeak { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         leaked: Type, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("{name} is a reserved module name")] | ||||
|     ReservedModuleName { name: String }, | ||||
| 
 | ||||
|     #[error("unexpected labeled argument {label}")] | ||||
|     UnexpectedLabeledArg { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         label: String, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("unexpected type hole")] | ||||
|     UnexpectedTypeHole { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("unknown labels")] | ||||
|     UnknownLabels { | ||||
|         unknown: Vec<(String, Span)>, | ||||
|         valid: Vec<String>, | ||||
|         supplied: Vec<String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("unknown module {name}")] | ||||
|     UnknownModule { | ||||
|         location: Span, | ||||
|  | @ -85,54 +146,76 @@ pub enum Error { | |||
|         type_constructors: Vec<String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("")] | ||||
|     #[error("unknown module value {name}")] | ||||
|     UnknownModuleValue { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         name: String, | ||||
|         module_name: String, | ||||
|         value_constructors: Vec<String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("unknown type {name} in module {module_name}")] | ||||
|     UnknownModuleType { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         name: String, | ||||
|         module_name: String, | ||||
|         type_constructors: Vec<String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("unknown type {name}")] | ||||
|     UnknownType { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         name: String, | ||||
|         types: Vec<String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("")] | ||||
|     UnknownTypeConstructorType { | ||||
|     #[error("unknown variable {name}")] | ||||
|     UnknownVariable { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         name: String, | ||||
|         type_constructors: Vec<String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("")] | ||||
|     UnknownTypeConstructorModule { | ||||
|         location: Span, | ||||
|         name: String, | ||||
|         imported_modules: Vec<String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("")] | ||||
|     UnknownTypeConstructorModuleType { | ||||
|         location: Span, | ||||
|         name: String, | ||||
|         module_name: Vec<String>, | ||||
|         type_constructors: Vec<String>, | ||||
|         variables: Vec<String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("")] | ||||
|     CouldNotUnify { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         expected: Arc<Type>, | ||||
|         given: Arc<Type>, | ||||
|         situation: Option<UnifyErrorSituation>, | ||||
|         rigid_type_names: HashMap<u64, String>, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("")] | ||||
|     ExtraVarInAlternativePattern { location: Span, name: String }, | ||||
|     ExtraVarInAlternativePattern { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         name: String, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("")] | ||||
|     MissingVarInAlternativePattern { location: Span, name: String }, | ||||
|     MissingVarInAlternativePattern { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         name: String, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("")] | ||||
|     DuplicateVarInPattern { location: Span, name: String }, | ||||
|     DuplicateVarInPattern { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|         name: String, | ||||
|     }, | ||||
| 
 | ||||
|     #[error("")] | ||||
|     RecursiveType { location: Span }, | ||||
|     RecursiveType { | ||||
|         #[label] | ||||
|         location: Span, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| impl Error { | ||||
|  | @ -143,15 +226,53 @@ impl Error { | |||
|                 expected, | ||||
|                 given, | ||||
|                 situation: note, | ||||
|                 rigid_type_names, | ||||
|             } => Error::CouldNotUnify { | ||||
|                 location, | ||||
|                 expected: given, | ||||
|                 given: expected, | ||||
|                 situation: note, | ||||
|                 rigid_type_names, | ||||
|             }, | ||||
|             other => other, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn with_unify_error_rigid_names(mut self, new_names: &HashMap<u64, String>) -> Self { | ||||
|         match self { | ||||
|             Error::CouldNotUnify { | ||||
|                 rigid_type_names: ref mut annotated_names, | ||||
|                 .. | ||||
|             } => { | ||||
|                 *annotated_names = new_names.clone(); | ||||
|                 self | ||||
|             } | ||||
|             _ => self, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn with_unify_error_situation(self, situation: UnifyErrorSituation) -> Self { | ||||
|         match self { | ||||
|             Self::CouldNotUnify { | ||||
|                 expected, | ||||
|                 given, | ||||
|                 location, | ||||
|                 rigid_type_names, | ||||
|                 .. | ||||
|             } => Self::CouldNotUnify { | ||||
|                 expected, | ||||
|                 given, | ||||
|                 situation: Some(situation), | ||||
|                 location, | ||||
|                 rigid_type_names, | ||||
|             }, | ||||
|             other => other, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn return_annotation_mismatch(self) -> Self { | ||||
|         self.with_unify_error_situation(UnifyErrorSituation::ReturnAnnotationMismatch) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, PartialEq, Clone)] | ||||
|  |  | |||
|  | @ -0,0 +1,597 @@ | |||
| use std::{collections::HashMap, sync::Arc}; | ||||
| 
 | ||||
| use crate::{ | ||||
|     ast::{Annotation, ArgName, CallArg, Constant, Span, TypedArg, TypedConstant, UntypedConstant}, | ||||
|     builtins::list, | ||||
|     expr::{TypedExpr, UntypedExpr}, | ||||
|     tipo::fields::FieldMap, | ||||
| }; | ||||
| 
 | ||||
| use super::{ | ||||
|     environment::{assert_no_labeled_arguments, EntityKind, Environment}, | ||||
|     error::Error, | ||||
|     hydrator::Hydrator, | ||||
|     ModuleValueConstructor, 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 get_field_map( | ||||
|         &mut self, | ||||
|         constructor: &TypedExpr, | ||||
|         location: Span, | ||||
|     ) -> Result<Option<&FieldMap>, 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<T>(&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 | ||||
|     } | ||||
| 
 | ||||
|     pub fn infer_fn_with_known_types( | ||||
|         &mut self, | ||||
|         args: Vec<TypedArg>, | ||||
|         body: UntypedExpr, | ||||
|         return_type: Option<Arc<Type>>, | ||||
|     ) -> Result<(Vec<TypedArg>, 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)) | ||||
|     } | ||||
| 
 | ||||
|     /// Crawl the AST, annotating each node with the inferred type or
 | ||||
|     /// returning an error.
 | ||||
|     ///
 | ||||
|     pub fn infer(&mut self, expr: UntypedExpr) -> Result<TypedExpr, Error> { | ||||
|         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::Assignment { | ||||
|                 location, | ||||
|                 pattern, | ||||
|                 value, | ||||
|                 kind, | ||||
|                 annotation, | ||||
|                 .. | ||||
|             } => self.infer_assignment(pattern, *value, kind, &annotation, location), | ||||
| 
 | ||||
|             UntypedExpr::Try { | ||||
|                 location, | ||||
|                 pattern, | ||||
|                 value, | ||||
|                 then, | ||||
|                 annotation, | ||||
|                 .. | ||||
|             } => self.infer_try(pattern, *value, *then, &annotation, location), | ||||
| 
 | ||||
|             UntypedExpr::Case { | ||||
|                 location, | ||||
|                 subjects, | ||||
|                 clauses, | ||||
|                 .. | ||||
|             } => self.infer_case(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::BitString { location, segments } => { | ||||
|                 self.infer_bit_string(segments, 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), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn type_from_annotation(&mut self, annotation: &Annotation) -> Result<Arc<Type>, Error> { | ||||
|         self.hydrator | ||||
|             .type_from_annotation(annotation, self.environment) | ||||
|     } | ||||
| 
 | ||||
|     // 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<Annotation>, | ||||
|         value: UntypedConstant, | ||||
|     ) -> Result<TypedConstant, Error> { | ||||
|         let inferred = match value { | ||||
|             Constant::Int { | ||||
|                 location, value, .. | ||||
|             } => Ok(Constant::Int { location, value }), | ||||
| 
 | ||||
|             Constant::String { | ||||
|                 location, value, .. | ||||
|             } => Ok(Constant::String { location, value }), | ||||
| 
 | ||||
|             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; | ||||
| 
 | ||||
|                         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<UntypedConstant>, | ||||
|         location: Span, | ||||
|     ) -> Result<TypedConstant, Error> { | ||||
|         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_value_constructor( | ||||
|         &mut self, | ||||
|         module: &Option<String>, | ||||
|         name: &str, | ||||
|         location: &Span, | ||||
|     ) -> Result<ValueConstructor, Error> { | ||||
|         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 instantiate(&mut self, t: Arc<Type>, ids: &mut HashMap<u64, Arc<Type>>) -> Arc<Type> { | ||||
|         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<Type> { | ||||
|         self.environment.new_unbound_var() | ||||
|     } | ||||
| 
 | ||||
|     fn unify(&mut self, t1: Arc<Type>, t2: Arc<Type>, location: Span) -> Result<(), Error> { | ||||
|         self.environment.unify(t1, t2, location) | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,147 @@ | |||
| use std::collections::{HashMap, HashSet}; | ||||
| 
 | ||||
| use itertools::Itertools; | ||||
| 
 | ||||
| use super::error::Error; | ||||
| use crate::ast::{CallArg, Span}; | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| pub struct FieldMap { | ||||
|     pub arity: usize, | ||||
|     pub fields: HashMap<String, usize>, | ||||
| } | ||||
| 
 | ||||
| impl FieldMap { | ||||
|     pub fn new(arity: usize) -> Self { | ||||
|         Self { | ||||
|             arity, | ||||
|             fields: HashMap::new(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn insert(&mut self, label: String, index: usize, location: &Span) -> Result<(), Error> { | ||||
|         match self.fields.insert(label.clone(), index) { | ||||
|             Some(_) => Err(Error::DuplicateField { | ||||
|                 label, | ||||
|                 location: *location, | ||||
|             }), | ||||
|             None => Ok(()), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn into_option(self) -> Option<Self> { | ||||
|         if self.fields.is_empty() { | ||||
|             None | ||||
|         } else { | ||||
|             Some(self) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Reorder an argument list so that labelled fields supplied out-of-order are
 | ||||
|     /// in the correct order.
 | ||||
|     pub fn reorder<A>(&self, args: &mut [CallArg<A>], location: Span) -> Result<(), Error> { | ||||
|         let mut labeled_arguments_given = false; | ||||
|         let mut seen_labels = std::collections::HashSet::new(); | ||||
|         let mut unknown_labels = Vec::new(); | ||||
| 
 | ||||
|         if self.arity as usize != args.len() { | ||||
|             return Err(Error::IncorrectArity { | ||||
|                 labels: self.incorrect_arity_labels(args), | ||||
|                 location, | ||||
|                 expected: self.arity as usize, | ||||
|                 given: args.len(), | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         for arg in args.iter() { | ||||
|             match &arg.label { | ||||
|                 Some(_) => { | ||||
|                     labeled_arguments_given = true; | ||||
|                 } | ||||
| 
 | ||||
|                 None => { | ||||
|                     if labeled_arguments_given { | ||||
|                         return Err(Error::PositionalArgumentAfterLabeled { | ||||
|                             location: arg.location, | ||||
|                         }); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let mut i = 0; | ||||
|         while i < args.len() { | ||||
|             let label = &args.get(i).expect("Field indexing to get label").label; | ||||
| 
 | ||||
|             let (label, &location) = match label { | ||||
|                 // A labelled argument, we may need to reposition it in the array vector
 | ||||
|                 Some(l) => ( | ||||
|                     l, | ||||
|                     &args | ||||
|                         .get(i) | ||||
|                         .expect("Indexing in labelled field reordering") | ||||
|                         .location, | ||||
|                 ), | ||||
| 
 | ||||
|                 // Not a labelled argument
 | ||||
|                 None => { | ||||
|                     i += 1; | ||||
|                     continue; | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             let position = match self.fields.get(label) { | ||||
|                 None => { | ||||
|                     unknown_labels.push((label.clone(), location)); | ||||
| 
 | ||||
|                     i += 1; | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 Some(&p) => p, | ||||
|             }; | ||||
| 
 | ||||
|             // If the argument is already in the right place
 | ||||
|             if position as usize == i { | ||||
|                 seen_labels.insert(label.clone()); | ||||
| 
 | ||||
|                 i += 1; | ||||
|             } else { | ||||
|                 if seen_labels.contains(label) { | ||||
|                     return Err(Error::DuplicateArgument { | ||||
|                         location, | ||||
|                         label: label.to_string(), | ||||
|                     }); | ||||
|                 } | ||||
| 
 | ||||
|                 seen_labels.insert(label.clone()); | ||||
| 
 | ||||
|                 args.swap(position as usize, i); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if unknown_labels.is_empty() { | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             let valid = self.fields.keys().map(|t| t.to_string()).collect(); | ||||
| 
 | ||||
|             Err(Error::UnknownLabels { | ||||
|                 valid, | ||||
|                 unknown: unknown_labels, | ||||
|                 supplied: seen_labels.into_iter().collect(), | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn incorrect_arity_labels<A>(&self, args: &[CallArg<A>]) -> Vec<String> { | ||||
|         let given: HashSet<_> = args.iter().filter_map(|arg| arg.label.as_ref()).collect(); | ||||
| 
 | ||||
|         self.fields | ||||
|             .keys() | ||||
|             .cloned() | ||||
|             .filter(|f| !given.contains(f)) | ||||
|             .sorted() | ||||
|             .collect() | ||||
|     } | ||||
| } | ||||
|  | @ -1,54 +1,423 @@ | |||
| use std::collections::HashMap; | ||||
| 
 | ||||
| use crate::{ | ||||
|     ast::{ModuleKind, TypedModule, UntypedModule}, | ||||
|     ast::{ | ||||
|         Definition, Layer, ModuleKind, RecordConstructor, RecordConstructorArg, TypedDefinition, | ||||
|         TypedModule, UntypedDefinition, UntypedModule, | ||||
|     }, | ||||
|     builtins::function, | ||||
|     token::Token, | ||||
|     IdGenerator, | ||||
| }; | ||||
| 
 | ||||
| use super::{ | ||||
|     environment::Environment, | ||||
|     environment::{generalise, EntityKind, Environment}, | ||||
|     error::{Error, Warning}, | ||||
|     Module, | ||||
|     expr::ExprTyper, | ||||
|     hydrator::Hydrator, | ||||
|     TypeInfo, ValueConstructor, ValueConstructorVariant, | ||||
| }; | ||||
| 
 | ||||
| pub fn module( | ||||
|     id_gen: &IdGenerator, | ||||
|     mut module: UntypedModule, | ||||
|     kind: ModuleKind, | ||||
|     package: &str, | ||||
|     modules: &HashMap<String, Module>, | ||||
|     warnings: &mut Vec<Warning>, | ||||
| ) -> Result<TypedModule, Error> { | ||||
|     let name = module.name.clone(); | ||||
|     let docs = std::mem::take(&mut module.docs); | ||||
|     let mut environment = Environment::new(id_gen.clone(), &name, modules, warnings); | ||||
| impl UntypedModule { | ||||
|     pub fn infer( | ||||
|         mut self, | ||||
|         id_gen: &IdGenerator, | ||||
|         kind: ModuleKind, | ||||
|         package: &str, | ||||
|         modules: &HashMap<String, TypeInfo>, | ||||
|         warnings: &mut Vec<Warning>, | ||||
|     ) -> Result<TypedModule, Error> { | ||||
|         let name = self.name.clone(); | ||||
|         let docs = std::mem::take(&mut self.docs); | ||||
|         let mut environment = Environment::new(id_gen.clone(), &name, modules, warnings); | ||||
| 
 | ||||
|     validate_module_name(&name)?; | ||||
|         validate_module_name(&name)?; | ||||
| 
 | ||||
|     let mut type_names = HashMap::with_capacity(module.definitions.len()); | ||||
|     let mut value_names = HashMap::with_capacity(module.definitions.len()); | ||||
|     let mut hydrators = HashMap::with_capacity(module.definitions.len()); | ||||
|         let mut type_names = HashMap::with_capacity(self.definitions.len()); | ||||
|         let mut value_names = HashMap::with_capacity(self.definitions.len()); | ||||
|         let mut hydrators = HashMap::with_capacity(self.definitions.len()); | ||||
| 
 | ||||
|     // Register any modules, types, and values being imported
 | ||||
|     // We process imports first so that anything imported can be referenced
 | ||||
|     // anywhere in the module.
 | ||||
|     for def in module.definitions() { | ||||
|         environment.register_import(def)?; | ||||
|         // Register any modules, types, and values being imported
 | ||||
|         // We process imports first so that anything imported can be referenced
 | ||||
|         // anywhere in the module.
 | ||||
|         for def in self.definitions() { | ||||
|             environment.register_import(def)?; | ||||
|         } | ||||
| 
 | ||||
|         // Register types so they can be used in constructors and functions
 | ||||
|         // earlier in the module.
 | ||||
|         for def in self.definitions() { | ||||
|             environment.register_types(def, &name, &mut hydrators, &mut type_names)?; | ||||
|         } | ||||
| 
 | ||||
|         // Register values so they can be used in functions earlier in the module.
 | ||||
|         for def in self.definitions() { | ||||
|             environment.register_values(def, &name, &mut hydrators, &mut value_names)?; | ||||
|         } | ||||
| 
 | ||||
|         // Infer the types of each definition in the module
 | ||||
|         // We first infer all the constants so they can be used in functions defined
 | ||||
|         // anywhere in the module.
 | ||||
|         let mut definitions = Vec::with_capacity(self.definitions.len()); | ||||
|         let mut consts = vec![]; | ||||
|         let mut not_consts = vec![]; | ||||
|         for def in self.into_definitions() { | ||||
|             match def { | ||||
|                 Definition::ModuleConstant { .. } => consts.push(def), | ||||
| 
 | ||||
|                 Definition::Fn { .. } | ||||
|                 | Definition::TypeAlias { .. } | ||||
|                 | Definition::DataType { .. } | ||||
|                 | Definition::Use { .. } => not_consts.push(def), | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         for def in consts.into_iter().chain(not_consts) { | ||||
|             let definition = infer_definition(def, &name, &mut hydrators, &mut environment)?; | ||||
| 
 | ||||
|             definitions.push(definition); | ||||
|         } | ||||
| 
 | ||||
|         // Generalise functions now that the entire module has been inferred
 | ||||
|         let definitions = definitions | ||||
|             .into_iter() | ||||
|             .map(|def| environment.generalise_definition(def, &name)) | ||||
|             .collect(); | ||||
| 
 | ||||
|         // Generate warnings for unused items
 | ||||
|         environment.convert_unused_to_warnings(); | ||||
| 
 | ||||
|         // Remove private and imported types and values to create the public interface
 | ||||
|         environment | ||||
|             .module_types | ||||
|             .retain(|_, info| info.public && info.module == name); | ||||
| 
 | ||||
|         environment.module_values.retain(|_, info| info.public); | ||||
| 
 | ||||
|         environment | ||||
|             .accessors | ||||
|             .retain(|_, accessors| accessors.public); | ||||
| 
 | ||||
|         // Ensure no exported values have private types in their type signature
 | ||||
|         for value in environment.module_values.values() { | ||||
|             if let Some(leaked) = value.tipo.find_private_type() { | ||||
|                 return Err(Error::PrivateTypeLeak { | ||||
|                     location: value.variant.location(), | ||||
|                     leaked, | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let Environment { | ||||
|             module_types: types, | ||||
|             module_types_constructors: types_constructors, | ||||
|             module_values: values, | ||||
|             accessors, | ||||
|             .. | ||||
|         } = environment; | ||||
| 
 | ||||
|         Ok(TypedModule { | ||||
|             docs, | ||||
|             name: name.clone(), | ||||
|             definitions, | ||||
|             kind, | ||||
|             type_info: TypeInfo { | ||||
|                 name, | ||||
|                 types, | ||||
|                 types_constructors, | ||||
|                 values, | ||||
|                 accessors, | ||||
|                 kind, | ||||
|                 package: package.to_string(), | ||||
|             }, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     // Register types so they can be used in constructors and functions
 | ||||
|     // earlier in the module.
 | ||||
|     for def in module.definitions() { | ||||
|         environment.register_types(def, &name, &mut hydrators, &mut type_names)?; | ||||
| fn infer_definition( | ||||
|     def: UntypedDefinition, | ||||
|     module_name: &String, | ||||
|     hydrators: &mut HashMap<String, Hydrator>, | ||||
|     environment: &mut Environment<'_>, | ||||
| ) -> Result<TypedDefinition, Error> { | ||||
|     match def { | ||||
|         Definition::Fn { | ||||
|             doc, | ||||
|             location, | ||||
|             name, | ||||
|             public, | ||||
|             arguments: args, | ||||
|             body, | ||||
|             return_annotation, | ||||
|             .. | ||||
|         } => { | ||||
|             let preregistered_fn = environment | ||||
|                 .get_variable(&name) | ||||
|                 .expect("Could not find preregistered type for function"); | ||||
| 
 | ||||
|             let field_map = preregistered_fn.field_map().cloned(); | ||||
| 
 | ||||
|             let preregistered_type = preregistered_fn.tipo.clone(); | ||||
| 
 | ||||
|             let (args_types, return_type) = preregistered_type | ||||
|                 .function_types() | ||||
|                 .expect("Preregistered type for fn was not a fn"); | ||||
| 
 | ||||
|             // Infer the type using the preregistered args + return types as a starting point
 | ||||
|             let (tipo, args, body, safe_to_generalise) = | ||||
|                 environment.in_new_scope(|environment| { | ||||
|                     let args = args | ||||
|                         .into_iter() | ||||
|                         .zip(&args_types) | ||||
|                         .map(|(arg_name, tipo)| arg_name.set_type(tipo.clone())) | ||||
|                         .collect(); | ||||
| 
 | ||||
|                     let mut expr_typer = ExprTyper::new(environment); | ||||
| 
 | ||||
|                     expr_typer.hydrator = hydrators | ||||
|                         .remove(&name) | ||||
|                         .expect("Could not find hydrator for fn"); | ||||
| 
 | ||||
|                     let (args, body) = | ||||
|                         expr_typer.infer_fn_with_known_types(args, body, Some(return_type))?; | ||||
| 
 | ||||
|                     let args_types = args.iter().map(|a| a.tipo.clone()).collect(); | ||||
| 
 | ||||
|                     let tipo = function(args_types, body.tipo()); | ||||
| 
 | ||||
|                     let safe_to_generalise = !expr_typer.ungeneralised_function_used; | ||||
| 
 | ||||
|                     Ok((tipo, args, body, safe_to_generalise)) | ||||
|                 })?; | ||||
| 
 | ||||
|             // Assert that the inferred type matches the type of any recursive call
 | ||||
|             environment.unify(preregistered_type, tipo.clone(), location)?; | ||||
| 
 | ||||
|             // Generalise the function if safe to do so
 | ||||
|             let tipo = if safe_to_generalise { | ||||
|                 environment.ungeneralised_functions.remove(&name); | ||||
| 
 | ||||
|                 let tipo = generalise(tipo, 0); | ||||
| 
 | ||||
|                 let module_fn = ValueConstructorVariant::ModuleFn { | ||||
|                     name: name.clone(), | ||||
|                     field_map, | ||||
|                     module: module_name.to_owned(), | ||||
|                     arity: args.len(), | ||||
|                     location, | ||||
|                 }; | ||||
| 
 | ||||
|                 environment.insert_variable(name.clone(), module_fn, tipo.clone()); | ||||
| 
 | ||||
|                 tipo | ||||
|             } else { | ||||
|                 tipo | ||||
|             }; | ||||
| 
 | ||||
|             Ok(Definition::Fn { | ||||
|                 doc, | ||||
|                 location, | ||||
|                 name, | ||||
|                 public, | ||||
|                 arguments: args, | ||||
|                 return_annotation, | ||||
|                 return_type: tipo | ||||
|                     .return_type() | ||||
|                     .expect("Could not find return type for fn"), | ||||
|                 body, | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         Definition::TypeAlias { | ||||
|             doc, | ||||
|             location, | ||||
|             public, | ||||
|             alias, | ||||
|             parameters, | ||||
|             annotation, | ||||
|             .. | ||||
|         } => { | ||||
|             let tipo = environment | ||||
|                 .get_type_constructor(&None, &alias, location) | ||||
|                 .expect("Could not find existing type for type alias") | ||||
|                 .tipo | ||||
|                 .clone(); | ||||
| 
 | ||||
|             Ok(Definition::TypeAlias { | ||||
|                 doc, | ||||
|                 location, | ||||
|                 public, | ||||
|                 alias, | ||||
|                 parameters, | ||||
|                 annotation, | ||||
|                 tipo, | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         Definition::DataType { | ||||
|             doc, | ||||
|             location, | ||||
|             public, | ||||
|             opaque, | ||||
|             name, | ||||
|             parameters, | ||||
|             constructors, | ||||
|             .. | ||||
|         } => { | ||||
|             let constructors = constructors | ||||
|                 .into_iter() | ||||
|                 .map( | ||||
|                     |RecordConstructor { | ||||
|                          location, | ||||
|                          name, | ||||
|                          arguments: args, | ||||
|                          documentation, | ||||
|                          sugar, | ||||
|                      }| { | ||||
|                         let preregistered_fn = environment | ||||
|                             .get_variable(&name) | ||||
|                             .expect("Could not find preregistered type for function"); | ||||
| 
 | ||||
|                         let preregistered_type = preregistered_fn.tipo.clone(); | ||||
| 
 | ||||
|                         let args = if let Some((args_types, _return_type)) = | ||||
|                             preregistered_type.function_types() | ||||
|                         { | ||||
|                             args.into_iter() | ||||
|                                 .zip(&args_types) | ||||
|                                 .map( | ||||
|                                     |( | ||||
|                                         RecordConstructorArg { | ||||
|                                             label, | ||||
|                                             annotation, | ||||
|                                             location, | ||||
|                                             .. | ||||
|                                         }, | ||||
|                                         t, | ||||
|                                     )| { | ||||
|                                         RecordConstructorArg { | ||||
|                                             label, | ||||
|                                             annotation, | ||||
|                                             location, | ||||
|                                             tipo: t.clone(), | ||||
|                                             doc: None, | ||||
|                                         } | ||||
|                                     }, | ||||
|                                 ) | ||||
|                                 .collect() | ||||
|                         } else { | ||||
|                             vec![] | ||||
|                         }; | ||||
| 
 | ||||
|                         RecordConstructor { | ||||
|                             location, | ||||
|                             name, | ||||
|                             arguments: args, | ||||
|                             documentation, | ||||
|                             sugar, | ||||
|                         } | ||||
|                     }, | ||||
|                 ) | ||||
|                 .collect(); | ||||
| 
 | ||||
|             let typed_parameters = environment | ||||
|                 .get_type_constructor(&None, &name, location) | ||||
|                 .expect("Could not find preregistered type constructor ") | ||||
|                 .parameters | ||||
|                 .clone(); | ||||
| 
 | ||||
|             Ok(Definition::DataType { | ||||
|                 doc, | ||||
|                 location, | ||||
|                 public, | ||||
|                 opaque, | ||||
|                 name, | ||||
|                 parameters, | ||||
|                 constructors, | ||||
|                 typed_parameters, | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         Definition::Use { | ||||
|             location, | ||||
|             module, | ||||
|             as_name, | ||||
|             mut unqualified, | ||||
|             .. | ||||
|         } => { | ||||
|             let name = module.join("/"); | ||||
| 
 | ||||
|             // Find imported module
 | ||||
|             let module_info = | ||||
|                 environment | ||||
|                     .importable_modules | ||||
|                     .get(&name) | ||||
|                     .ok_or_else(|| Error::UnknownModule { | ||||
|                         location, | ||||
|                         name, | ||||
|                         imported_modules: environment.imported_modules.keys().cloned().collect(), | ||||
|                     })?; | ||||
| 
 | ||||
|             // TODO: remove this most likely
 | ||||
|             // Record any imports that are types only as this information is
 | ||||
|             // needed to prevent types being imported in generated JavaScript
 | ||||
|             for import in unqualified.iter_mut() { | ||||
|                 if environment.imported_types.contains(import.variable_name()) { | ||||
|                     import.layer = Layer::Type; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             Ok(Definition::Use { | ||||
|                 location, | ||||
|                 module, | ||||
|                 as_name, | ||||
|                 unqualified, | ||||
|                 package: module_info.package.clone(), | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         Definition::ModuleConstant { | ||||
|             doc, | ||||
|             location, | ||||
|             name, | ||||
|             annotation, | ||||
|             public, | ||||
|             value, | ||||
|             .. | ||||
|         } => { | ||||
|             let typed_expr = ExprTyper::new(environment).infer_const(&annotation, *value)?; | ||||
| 
 | ||||
|             let tipo = typed_expr.tipo(); | ||||
| 
 | ||||
|             let variant = ValueConstructor { | ||||
|                 public, | ||||
|                 variant: ValueConstructorVariant::ModuleConstant { | ||||
|                     location, | ||||
|                     literal: typed_expr.clone(), | ||||
|                     module: module_name.to_owned(), | ||||
|                 }, | ||||
|                 tipo: tipo.clone(), | ||||
|             }; | ||||
| 
 | ||||
|             environment.insert_variable(name.clone(), variant.variant.clone(), tipo.clone()); | ||||
| 
 | ||||
|             environment.insert_module_value(&name, variant); | ||||
| 
 | ||||
|             if !public { | ||||
|                 environment.init_usage(name.clone(), EntityKind::PrivateConstant, location); | ||||
|             } | ||||
| 
 | ||||
|             Ok(Definition::ModuleConstant { | ||||
|                 doc, | ||||
|                 location, | ||||
|                 name, | ||||
|                 annotation, | ||||
|                 public, | ||||
|                 value: Box::new(typed_expr), | ||||
|                 tipo, | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Register values so they can be used in functions earlier in the module.
 | ||||
|     for def in module.definitions() { | ||||
|         environment.register_values(def, &name, &mut hydrators, &mut value_names)?; | ||||
|     } | ||||
| 
 | ||||
|     todo!() | ||||
| } | ||||
| 
 | ||||
| fn validate_module_name(name: &str) -> Result<(), Error> { | ||||
|  | @ -70,7 +439,7 @@ fn validate_module_name(name: &str) -> Result<(), Error> { | |||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn str_to_keyword(word: &str) -> Option<Token> { | ||||
| fn str_to_keyword(word: &str) -> Option<Token> { | ||||
|     // Alphabetical keywords:
 | ||||
|     match word { | ||||
|         "as" => Some(Token::As), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 rvcas
						rvcas