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