feat: start expr inference
This commit is contained in:
parent
81c87ab4da
commit
cabc653167
|
@ -77,6 +77,7 @@ version = "0.0.20"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chumsky",
|
"chumsky",
|
||||||
"internment",
|
"internment",
|
||||||
|
"itertools",
|
||||||
"miette",
|
"miette",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -294,6 +295,12 @@ version = "0.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -443,6 +450,15 @@ version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
|
checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
|
|
@ -4,7 +4,12 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use aiken_lang::{ast::ModuleKind, builtins, tipo, IdGenerator};
|
use aiken_lang::{
|
||||||
|
ast::ModuleKind,
|
||||||
|
builtins,
|
||||||
|
tipo::{self, TypeInfo},
|
||||||
|
IdGenerator,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
|
@ -39,7 +44,7 @@ pub struct Project {
|
||||||
config: Config,
|
config: Config,
|
||||||
defined_modules: HashMap<String, PathBuf>,
|
defined_modules: HashMap<String, PathBuf>,
|
||||||
id_gen: IdGenerator,
|
id_gen: IdGenerator,
|
||||||
module_types: HashMap<String, tipo::Module>,
|
module_types: HashMap<String, TypeInfo>,
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
sources: Vec<Source>,
|
sources: Vec<Source>,
|
||||||
pub warnings: Vec<Warning>,
|
pub warnings: Vec<Warning>,
|
||||||
|
@ -164,19 +169,19 @@ impl Project {
|
||||||
{
|
{
|
||||||
let mut type_warnings = Vec::new();
|
let mut type_warnings = Vec::new();
|
||||||
|
|
||||||
let ast = tipo::infer::module(
|
let ast = ast
|
||||||
&self.id_gen,
|
.infer(
|
||||||
ast,
|
&self.id_gen,
|
||||||
kind,
|
kind,
|
||||||
&self.config.name,
|
&self.config.name,
|
||||||
&self.module_types,
|
&self.module_types,
|
||||||
&mut type_warnings,
|
&mut type_warnings,
|
||||||
)
|
)
|
||||||
.map_err(|error| Error::Type {
|
.map_err(|error| Error::Type {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
src: code.clone(),
|
src: code.clone(),
|
||||||
error,
|
error,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Register any warnings emitted as type warnings
|
// Register any warnings emitted as type warnings
|
||||||
let type_warnings = type_warnings
|
let type_warnings = type_warnings
|
||||||
|
|
|
@ -13,6 +13,7 @@ authors = ["Lucas Rosa <x@rvcas.dev>", "Kasey White <kwhitemsg@gmail.com>"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chumsky = "0.8.0"
|
chumsky = "0.8.0"
|
||||||
internment = "0.7.0"
|
internment = "0.7.0"
|
||||||
|
itertools = "0.10.5"
|
||||||
miette = "5.2.0"
|
miette = "5.2.0"
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
vec1 = "1.8.0"
|
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 internment::Intern;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
builtins,
|
||||||
expr::{TypedExpr, UntypedExpr},
|
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 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>;
|
pub type UntypedModule = Module<(), UntypedDefinition>;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
@ -133,11 +137,6 @@ pub enum Constant<T, RecordTag> {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
Pair {
|
|
||||||
location: Span,
|
|
||||||
elements: Vec<Self>,
|
|
||||||
},
|
|
||||||
|
|
||||||
List {
|
List {
|
||||||
location: Span,
|
location: Span,
|
||||||
elements: Vec<Self>,
|
elements: Vec<Self>,
|
||||||
|
@ -154,9 +153,9 @@ pub enum Constant<T, RecordTag> {
|
||||||
field_map: Option<FieldMap>,
|
field_map: Option<FieldMap>,
|
||||||
},
|
},
|
||||||
|
|
||||||
ByteString {
|
ByteArray {
|
||||||
location: Span,
|
location: Span,
|
||||||
// segments: Vec<BitStringSegment<Self, T>>,
|
bytes: Vec<u8>,
|
||||||
},
|
},
|
||||||
|
|
||||||
Var {
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct CallArg<A> {
|
pub struct CallArg<A> {
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
|
@ -175,44 +207,6 @@ pub struct CallArg<A> {
|
||||||
pub value: 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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct RecordConstructor<T> {
|
pub struct RecordConstructor<T> {
|
||||||
pub location: Span,
|
pub location: Span,
|
||||||
|
@ -232,6 +226,7 @@ pub struct RecordConstructorArg<T> {
|
||||||
pub doc: Option<String>,
|
pub doc: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type TypedArg = Arg<Arc<Type>>;
|
||||||
pub type UntypedArg = Arg<()>;
|
pub type UntypedArg = Arg<()>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -242,6 +237,21 @@ pub struct Arg<T> {
|
||||||
pub tipo: 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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ArgName {
|
pub enum ArgName {
|
||||||
Discard {
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct UnqualifiedImport {
|
pub struct UnqualifiedImport {
|
||||||
pub location: Span,
|
pub location: Span,
|
||||||
|
@ -272,6 +291,16 @@ pub struct UnqualifiedImport {
|
||||||
pub layer: Layer,
|
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
|
// TypeAst
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Annotation {
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum BinOp {
|
pub enum BinOp {
|
||||||
// Boolean logic
|
// Boolean logic
|
||||||
|
|
|
@ -2,7 +2,10 @@ use std::{cell::RefCell, collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{ModuleKind, Span},
|
ast::{ModuleKind, Span},
|
||||||
tipo::{self, Type, TypeConstructor, TypeVar, ValueConstructor, ValueConstructorVariant},
|
tipo::{
|
||||||
|
self, fields::FieldMap, Type, TypeConstructor, TypeVar, ValueConstructor,
|
||||||
|
ValueConstructorVariant,
|
||||||
|
},
|
||||||
IdGenerator,
|
IdGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,9 +19,9 @@ const STRING: &str = "String";
|
||||||
|
|
||||||
/// Build a prelude that can be injected
|
/// Build a prelude that can be injected
|
||||||
/// into a compiler pipeline
|
/// into a compiler pipeline
|
||||||
pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
pub fn prelude(id_gen: &IdGenerator) -> tipo::TypeInfo {
|
||||||
let mut prelude = tipo::Module {
|
let mut prelude = tipo::TypeInfo {
|
||||||
name: vec!["aiken".to_string()],
|
name: "aiken".to_string(),
|
||||||
package: "".to_string(),
|
package: "".to_string(),
|
||||||
kind: ModuleKind::Lib,
|
kind: ModuleKind::Lib,
|
||||||
types: HashMap::new(),
|
types: HashMap::new(),
|
||||||
|
@ -64,7 +67,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
ValueConstructorVariant::Record {
|
ValueConstructorVariant::Record {
|
||||||
module: "".into(),
|
module: "".into(),
|
||||||
name: "True".to_string(),
|
name: "True".to_string(),
|
||||||
field_map: None,
|
field_map: None::<FieldMap>,
|
||||||
arity: 0,
|
arity: 0,
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
constructors_count: 2,
|
constructors_count: 2,
|
||||||
|
@ -79,7 +82,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
ValueConstructorVariant::Record {
|
ValueConstructorVariant::Record {
|
||||||
module: "".into(),
|
module: "".into(),
|
||||||
name: "False".to_string(),
|
name: "False".to_string(),
|
||||||
field_map: None,
|
field_map: None::<FieldMap>,
|
||||||
arity: 0,
|
arity: 0,
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
constructors_count: 2,
|
constructors_count: 2,
|
||||||
|
@ -132,7 +135,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
module: "".into(),
|
module: "".into(),
|
||||||
name: NIL.to_string(),
|
name: NIL.to_string(),
|
||||||
arity: 0,
|
arity: 0,
|
||||||
field_map: None,
|
field_map: None::<FieldMap>,
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
constructors_count: 1,
|
constructors_count: 1,
|
||||||
},
|
},
|
||||||
|
@ -179,7 +182,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
ValueConstructorVariant::Record {
|
ValueConstructorVariant::Record {
|
||||||
module: "".into(),
|
module: "".into(),
|
||||||
name: "Ok".to_string(),
|
name: "Ok".to_string(),
|
||||||
field_map: None,
|
field_map: None::<FieldMap>,
|
||||||
arity: 1,
|
arity: 1,
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
constructors_count: 2,
|
constructors_count: 2,
|
||||||
|
@ -196,7 +199,7 @@ pub fn prelude(id_gen: &IdGenerator) -> tipo::Module {
|
||||||
ValueConstructorVariant::Record {
|
ValueConstructorVariant::Record {
|
||||||
module: "".into(),
|
module: "".into(),
|
||||||
name: "Error".to_string(),
|
name: "Error".to_string(),
|
||||||
field_map: None,
|
field_map: None::<FieldMap>,
|
||||||
arity: 1,
|
arity: 1,
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
constructors_count: 2,
|
constructors_count: 2,
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
||||||
Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, IfBranch, Pattern,
|
Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, IfBranch, Pattern,
|
||||||
RecordUpdateSpread, Span, TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg,
|
RecordUpdateSpread, Span, TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg,
|
||||||
},
|
},
|
||||||
|
builtins::{bool, nil},
|
||||||
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
|
tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,16 +19,16 @@ pub enum TypedExpr {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
Float {
|
String {
|
||||||
location: Span,
|
location: Span,
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
String {
|
ByteArray {
|
||||||
location: Span,
|
location: Span,
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
value: String,
|
bytes: Vec<u8>,
|
||||||
},
|
},
|
||||||
|
|
||||||
Sequence {
|
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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum UntypedExpr {
|
pub enum UntypedExpr {
|
||||||
Int {
|
Int {
|
||||||
|
@ -168,11 +264,6 @@ pub enum UntypedExpr {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
Float {
|
|
||||||
location: Span,
|
|
||||||
value: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
String {
|
String {
|
||||||
location: Span,
|
location: Span,
|
||||||
value: String,
|
value: String,
|
||||||
|
@ -203,9 +294,9 @@ pub enum UntypedExpr {
|
||||||
},
|
},
|
||||||
|
|
||||||
Call {
|
Call {
|
||||||
location: Span,
|
|
||||||
fun: Box<Self>,
|
|
||||||
arguments: Vec<CallArg<Self>>,
|
arguments: Vec<CallArg<Self>>,
|
||||||
|
fun: Box<Self>,
|
||||||
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
BinOp {
|
BinOp {
|
||||||
|
@ -215,6 +306,11 @@ pub enum UntypedExpr {
|
||||||
right: Box<Self>,
|
right: Box<Self>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ByteArray {
|
||||||
|
location: Span,
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
},
|
||||||
|
|
||||||
PipeLine {
|
PipeLine {
|
||||||
expressions: Vec1<Self>,
|
expressions: Vec1<Self>,
|
||||||
},
|
},
|
||||||
|
@ -344,7 +440,7 @@ impl UntypedExpr {
|
||||||
| Self::When { location, .. }
|
| Self::When { location, .. }
|
||||||
| Self::Call { location, .. }
|
| Self::Call { location, .. }
|
||||||
| Self::List { location, .. }
|
| Self::List { location, .. }
|
||||||
| Self::Float { location, .. }
|
| Self::ByteArray { location, .. }
|
||||||
| Self::BinOp { location, .. }
|
| Self::BinOp { location, .. }
|
||||||
| Self::Tuple { location, .. }
|
| Self::Tuple { location, .. }
|
||||||
| Self::String { 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;
|
mod environment;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
mod expr;
|
||||||
|
pub mod fields;
|
||||||
mod hydrator;
|
mod hydrator;
|
||||||
pub mod infer;
|
mod infer;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
|
@ -41,6 +48,161 @@ pub enum Type {
|
||||||
// Tuple { elems: Vec<Arc<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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum TypeVar {
|
pub enum TypeVar {
|
||||||
/// Unbound is an unbound variable. It is one specific type but we don't
|
/// 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 {
|
pub fn is_unbound(&self) -> bool {
|
||||||
matches!(self, Self::Unbound { .. })
|
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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -89,6 +286,14 @@ impl ValueConstructor {
|
||||||
tipo,
|
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)]
|
#[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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Module {
|
pub struct TypeInfo {
|
||||||
pub name: Vec<String>,
|
pub name: String,
|
||||||
pub kind: ModuleKind,
|
pub kind: ModuleKind,
|
||||||
pub package: String,
|
pub package: String,
|
||||||
pub types: HashMap<String, TypeConstructor>,
|
pub types: HashMap<String, TypeConstructor>,
|
||||||
|
@ -171,7 +387,7 @@ pub enum ModuleValueConstructor {
|
||||||
Record {
|
Record {
|
||||||
name: String,
|
name: String,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
type_: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
field_map: Option<FieldMap>,
|
field_map: Option<FieldMap>,
|
||||||
location: Span,
|
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;
|
use miette::Diagnostic;
|
||||||
|
|
||||||
|
@ -6,8 +6,19 @@ use crate::ast::{BinOp, Span, TodoKind};
|
||||||
|
|
||||||
use super::Type;
|
use super::Type;
|
||||||
|
|
||||||
|
// use aiken/pub
|
||||||
|
|
||||||
|
// pub fn do_thing() { pub.other() }
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, Diagnostic)]
|
#[derive(Debug, thiserror::Error, Diagnostic)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("duplicate argument {label}")]
|
||||||
|
DuplicateArgument {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
label: String,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("duplicate const {name}")]
|
#[error("duplicate const {name}")]
|
||||||
DuplicateConstName {
|
DuplicateConstName {
|
||||||
#[label]
|
#[label]
|
||||||
|
@ -49,6 +60,15 @@ pub enum Error {
|
||||||
name: String,
|
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}")]
|
#[error("{name} has incorrect type arity expected {expected} but given {given}")]
|
||||||
IncorrectTypeArity {
|
IncorrectTypeArity {
|
||||||
location: Span,
|
location: Span,
|
||||||
|
@ -57,18 +77,59 @@ pub enum Error {
|
||||||
given: usize,
|
given: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("not a function")]
|
||||||
|
NotFn {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("{name} contains keyword {keyword}")]
|
#[error("{name} contains keyword {keyword}")]
|
||||||
KeywordInModuleName { name: String, keyword: String },
|
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")]
|
#[error("{name} is a reserved module name")]
|
||||||
ReservedModuleName { name: String },
|
ReservedModuleName { name: String },
|
||||||
|
|
||||||
|
#[error("unexpected labeled argument {label}")]
|
||||||
|
UnexpectedLabeledArg {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
label: String,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("unexpected type hole")]
|
#[error("unexpected type hole")]
|
||||||
UnexpectedTypeHole {
|
UnexpectedTypeHole {
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("unknown labels")]
|
||||||
|
UnknownLabels {
|
||||||
|
unknown: Vec<(String, Span)>,
|
||||||
|
valid: Vec<String>,
|
||||||
|
supplied: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("unknown module {name}")]
|
#[error("unknown module {name}")]
|
||||||
UnknownModule {
|
UnknownModule {
|
||||||
location: Span,
|
location: Span,
|
||||||
|
@ -85,54 +146,76 @@ pub enum Error {
|
||||||
type_constructors: Vec<String>,
|
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 {
|
UnknownType {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
types: Vec<String>,
|
types: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("")]
|
#[error("unknown variable {name}")]
|
||||||
UnknownTypeConstructorType {
|
UnknownVariable {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
type_constructors: Vec<String>,
|
variables: 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>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("")]
|
#[error("")]
|
||||||
CouldNotUnify {
|
CouldNotUnify {
|
||||||
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
expected: Arc<Type>,
|
expected: Arc<Type>,
|
||||||
given: Arc<Type>,
|
given: Arc<Type>,
|
||||||
situation: Option<UnifyErrorSituation>,
|
situation: Option<UnifyErrorSituation>,
|
||||||
|
rigid_type_names: HashMap<u64, String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("")]
|
#[error("")]
|
||||||
ExtraVarInAlternativePattern { location: Span, name: String },
|
ExtraVarInAlternativePattern {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("")]
|
#[error("")]
|
||||||
MissingVarInAlternativePattern { location: Span, name: String },
|
MissingVarInAlternativePattern {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("")]
|
#[error("")]
|
||||||
DuplicateVarInPattern { location: Span, name: String },
|
DuplicateVarInPattern {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("")]
|
#[error("")]
|
||||||
RecursiveType { location: Span },
|
RecursiveType {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
@ -143,15 +226,53 @@ impl Error {
|
||||||
expected,
|
expected,
|
||||||
given,
|
given,
|
||||||
situation: note,
|
situation: note,
|
||||||
|
rigid_type_names,
|
||||||
} => Error::CouldNotUnify {
|
} => Error::CouldNotUnify {
|
||||||
location,
|
location,
|
||||||
expected: given,
|
expected: given,
|
||||||
given: expected,
|
given: expected,
|
||||||
situation: note,
|
situation: note,
|
||||||
|
rigid_type_names,
|
||||||
},
|
},
|
||||||
other => other,
|
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)]
|
#[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 std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{ModuleKind, TypedModule, UntypedModule},
|
ast::{
|
||||||
|
Definition, Layer, ModuleKind, RecordConstructor, RecordConstructorArg, TypedDefinition,
|
||||||
|
TypedModule, UntypedDefinition, UntypedModule,
|
||||||
|
},
|
||||||
|
builtins::function,
|
||||||
token::Token,
|
token::Token,
|
||||||
IdGenerator,
|
IdGenerator,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
environment::Environment,
|
environment::{generalise, EntityKind, Environment},
|
||||||
error::{Error, Warning},
|
error::{Error, Warning},
|
||||||
Module,
|
expr::ExprTyper,
|
||||||
|
hydrator::Hydrator,
|
||||||
|
TypeInfo, ValueConstructor, ValueConstructorVariant,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn module(
|
impl UntypedModule {
|
||||||
id_gen: &IdGenerator,
|
pub fn infer(
|
||||||
mut module: UntypedModule,
|
mut self,
|
||||||
kind: ModuleKind,
|
id_gen: &IdGenerator,
|
||||||
package: &str,
|
kind: ModuleKind,
|
||||||
modules: &HashMap<String, Module>,
|
package: &str,
|
||||||
warnings: &mut Vec<Warning>,
|
modules: &HashMap<String, TypeInfo>,
|
||||||
) -> Result<TypedModule, Error> {
|
warnings: &mut Vec<Warning>,
|
||||||
let name = module.name.clone();
|
) -> Result<TypedModule, Error> {
|
||||||
let docs = std::mem::take(&mut module.docs);
|
let name = self.name.clone();
|
||||||
let mut environment = Environment::new(id_gen.clone(), &name, modules, warnings);
|
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 type_names = HashMap::with_capacity(self.definitions.len());
|
||||||
let mut value_names = HashMap::with_capacity(module.definitions.len());
|
let mut value_names = HashMap::with_capacity(self.definitions.len());
|
||||||
let mut hydrators = HashMap::with_capacity(module.definitions.len());
|
let mut hydrators = HashMap::with_capacity(self.definitions.len());
|
||||||
|
|
||||||
// Register any modules, types, and values being imported
|
// Register any modules, types, and values being imported
|
||||||
// We process imports first so that anything imported can be referenced
|
// We process imports first so that anything imported can be referenced
|
||||||
// anywhere in the module.
|
// anywhere in the module.
|
||||||
for def in module.definitions() {
|
for def in self.definitions() {
|
||||||
environment.register_import(def)?;
|
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
|
fn infer_definition(
|
||||||
// earlier in the module.
|
def: UntypedDefinition,
|
||||||
for def in module.definitions() {
|
module_name: &String,
|
||||||
environment.register_types(def, &name, &mut hydrators, &mut type_names)?;
|
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> {
|
fn validate_module_name(name: &str) -> Result<(), Error> {
|
||||||
|
@ -70,7 +439,7 @@ fn validate_module_name(name: &str) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn str_to_keyword(word: &str) -> Option<Token> {
|
fn str_to_keyword(word: &str) -> Option<Token> {
|
||||||
// Alphabetical keywords:
|
// Alphabetical keywords:
|
||||||
match word {
|
match word {
|
||||||
"as" => Some(Token::As),
|
"as" => Some(Token::As),
|
||||||
|
|
Loading…
Reference in New Issue