schala/schala-lang/src/typechecking.rs

523 lines
14 KiB
Rust
Raw Normal View History

2018-02-21 02:31:28 -08:00
use std::rc::Rc;
use std::convert::TryFrom;
use std::fmt;
2018-02-21 02:31:28 -08:00
2019-02-23 00:59:58 -08:00
use ena::unify::{UnifyKey, InPlaceUnificationTable, UnificationTable, EqUnifyValue};
2019-02-23 00:34:44 -08:00
use crate::builtin::Builtin;
2019-01-07 13:00:37 -08:00
use crate::ast::*;
use crate::util::ScopeStack;
use crate::util::deref_optional_box;
2018-11-06 13:44:52 -08:00
2019-02-23 00:34:44 -08:00
#[derive(Debug, Clone, PartialEq)]
pub struct TypeData {
ty: Option<Type>
}
impl TypeData {
2019-10-23 16:07:10 -07:00
#[allow(dead_code)]
pub fn new() -> TypeData {
TypeData { ty: None }
}
}
//TODO need to hook this into the actual typechecking system somehow
#[derive(Debug, Clone)]
pub struct TypeId {
local_name: Rc<String>
}
impl TypeId {
//TODO this is definitely incomplete
pub fn lookup_name(name: &str) -> TypeId {
TypeId {
local_name: Rc::new(name.to_string())
}
}
pub fn local_name(&self) -> &str {
self.local_name.as_ref()
}
}
impl fmt::Display for TypeId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TypeId:{}", self.local_name)
}
}
2018-11-06 13:44:52 -08:00
2018-11-08 20:30:17 -08:00
pub struct TypeContext<'a> {
2019-02-15 21:11:50 -08:00
variable_map: ScopeStack<'a, Rc<String>, Type>,
2019-02-23 00:34:44 -08:00
unification_table: InPlaceUnificationTable<TypeVar>,
2018-11-06 13:44:52 -08:00
}
2018-11-11 18:04:44 -08:00
/// `InferResult` is the monad in which type inference takes place.
type InferResult<T> = Result<T, TypeError>;
2018-11-06 16:47:34 -08:00
#[derive(Debug, Clone)]
2019-02-20 01:33:45 -08:00
pub struct TypeError { pub msg: String }
2018-11-08 02:26:02 -08:00
impl TypeError {
fn new<A, T>(msg: T) -> InferResult<A> where T: Into<String> {
Err(TypeError { msg: msg.into() })
2018-11-08 02:26:02 -08:00
}
}
2018-11-06 16:47:34 -08:00
2019-09-10 03:40:41 -07:00
#[allow(dead_code)] // avoids warning from Compound
2019-02-20 01:33:45 -08:00
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
Const(TypeConst),
2019-02-21 01:17:34 -08:00
Var(TypeVar),
Arrow {
params: Vec<Type>,
ret: Box<Type>
},
2019-02-17 04:30:49 -08:00
Compound {
2019-02-21 01:17:34 -08:00
ty_name: String,
2019-02-17 04:30:49 -08:00
args:Vec<Type>
}
}
2019-02-23 00:34:44 -08:00
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TypeVar(usize);
impl UnifyKey for TypeVar {
2019-02-23 00:59:58 -08:00
type Value = Option<TypeConst>;
2019-02-23 00:34:44 -08:00
fn index(&self) -> u32 { self.0 as u32 }
fn from_index(u: u32) -> TypeVar { TypeVar(u as usize) }
fn tag() -> &'static str { "TypeVar" }
}
2019-02-21 01:17:34 -08:00
2019-02-23 00:59:58 -08:00
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypeConst {
Unit,
Nat,
Int,
Float,
StringT,
Bool,
2019-02-10 07:06:30 -08:00
Ordering,
2019-05-20 16:10:50 -07:00
//UserDefined
}
impl TypeConst {
2021-10-19 22:29:41 -07:00
/*
#[allow(dead_code)]
pub fn to_string(&self) -> String {
use self::TypeConst::*;
match self {
Unit => "()".to_string(),
Nat => "Nat".to_string(),
Int => "Int".to_string(),
Float => "Float".to_string(),
StringT => "String".to_string(),
Bool => "Bool".to_string(),
Ordering => "Ordering".to_string(),
}
}
2021-10-19 22:29:41 -07:00
*/
}
2019-02-23 00:59:58 -08:00
impl EqUnifyValue for TypeConst { }
2019-02-17 04:25:38 -08:00
macro_rules! ty {
($type_name:ident) => { Type::Const(TypeConst::$type_name) };
($t1:ident -> $t2:ident) => { Type::Arrow { params: vec![ty!($t1)], ret: box ty!($t2) } };
($t1:ident -> $t2:ident -> $t3:ident) => { Type::Arrow { params: vec![ty!($t1), ty!($t2)], ret: box ty!($t3) } };
($type_list:ident, $ret_type:ident) => {
Type::Arrow {
params: $type_list,
ret: box $ret_type,
}
}
2019-02-17 04:25:38 -08:00
}
2019-02-10 07:05:01 -08:00
//TODO find a better way to capture the to/from string logic
impl Type {
2021-10-19 22:29:41 -07:00
/*
#[allow(dead_code)]
2019-02-20 01:33:45 -08:00
pub fn to_string(&self) -> String {
2019-02-10 05:31:58 -08:00
use self::Type::*;
match self {
Const(c) => c.to_string(),
Var(v) => format!("t_{}", v.0),
Arrow { params, box ref ret } => {
if params.is_empty() {
format!("-> {}", ret.to_string())
} else {
let mut buf = String::new();
for p in params.iter() {
2019-02-24 16:24:45 -08:00
write!(buf, "{} -> ", p.to_string()).unwrap();
}
2019-02-24 16:24:45 -08:00
write!(buf, "{}", ret.to_string()).unwrap();
buf
}
},
Compound { .. } => "<some compound type>".to_string()
2019-02-10 05:31:58 -08:00
}
}
2021-10-19 22:29:41 -07:00
*/
2019-02-10 07:05:01 -08:00
fn from_string(string: &str) -> Option<Type> {
Some(match string {
2019-02-17 04:25:38 -08:00
"()" | "Unit" => ty!(Unit),
"Nat" => ty!(Nat),
"Int" => ty!(Int),
"Float" => ty!(Float),
"String" => ty!(StringT),
"Bool" => ty!(Bool),
"Ordering" => ty!(Ordering),
2019-02-10 07:05:01 -08:00
_ => return None
})
}
2019-02-10 05:31:58 -08:00
}
2019-02-09 00:25:12 -08:00
/*
2018-11-11 18:04:44 -08:00
/// `Type` is parameterized by whether the type variables can be just universal, or universal or
/// existential.
2018-11-07 13:44:28 -08:00
#[derive(Debug, Clone)]
2018-11-09 02:50:29 -08:00
enum Type<A> {
Var(A),
2018-11-07 15:39:40 -08:00
Const(TConst),
2018-11-09 02:50:29 -08:00
Arrow(Box<Type<A>>, Box<Type<A>>),
}
#[derive(Debug, Clone)]
2018-11-09 02:50:29 -08:00
enum TVar {
Univ(UVar),
2018-11-10 14:11:29 -08:00
Exist(ExistentialVar)
2018-11-08 02:26:02 -08:00
}
#[derive(Debug, Clone)]
struct UVar(Rc<String>);
#[derive(Debug, Clone)]
2018-11-09 02:50:29 -08:00
struct ExistentialVar(u32);
impl Type<UVar> {
fn to_tvar(&self) -> Type<TVar> {
match self {
Type::Var(UVar(name)) => Type::Var(TVar::Univ(UVar(name.clone()))),
Type::Const(ref c) => Type::Const(c.clone()),
Type::Arrow(a, b) => Type::Arrow(
Box::new(a.to_tvar()),
Box::new(b.to_tvar())
)
}
}
}
impl Type<TVar> {
fn skolemize(&self) -> Type<UVar> {
match self {
Type::Var(TVar::Univ(uvar)) => Type::Var(uvar.clone()),
Type::Var(TVar::Exist(_)) => Type::Var(UVar(Rc::new(format!("sk")))),
Type::Const(ref c) => Type::Const(c.clone()),
Type::Arrow(a, b) => Type::Arrow(
Box::new(a.skolemize()),
Box::new(b.skolemize())
)
}
}
}
2018-11-08 02:26:02 -08:00
impl TypeIdentifier {
fn to_monotype(&self) -> Type<UVar> {
2018-11-08 20:30:17 -08:00
match self {
TypeIdentifier::Tuple(_) => Type::Const(TConst::Nat),
2018-11-08 20:30:17 -08:00
TypeIdentifier::Singleton(TypeSingletonName { name, .. }) => {
match &name[..] {
2018-11-09 02:05:59 -08:00
"Nat" => Type::Const(TConst::Nat),
"Int" => Type::Const(TConst::Int),
"Float" => Type::Const(TConst::Float),
"Bool" => Type::Const(TConst::Bool),
"String" => Type::Const(TConst::StringT),
_ => Type::Const(TConst::Nat),
2018-11-08 20:30:17 -08:00
}
}
}
2018-11-08 02:26:02 -08:00
}
2018-11-07 03:39:31 -08:00
}
2018-11-06 16:47:34 -08:00
2018-11-07 15:39:40 -08:00
#[derive(Debug, Clone)]
enum TConst {
User(Rc<String>),
Unit,
Nat,
2018-11-07 16:39:32 -08:00
Int,
Float,
StringT,
2018-11-08 02:12:01 -08:00
Bool,
2018-11-07 15:39:40 -08:00
}
impl TConst {
fn user(name: &str) -> TConst {
TConst::User(Rc::new(name.to_string()))
}
}
2019-02-09 00:25:12 -08:00
*/
2018-11-07 15:39:40 -08:00
2018-11-08 20:30:17 -08:00
impl<'a> TypeContext<'a> {
pub fn new() -> TypeContext<'a> {
TypeContext {
variable_map: ScopeStack::new(None),
2019-02-23 00:34:44 -08:00
unification_table: UnificationTable::new(),
2018-11-08 20:30:17 -08:00
}
2018-11-06 13:44:52 -08:00
}
2019-05-20 16:10:50 -07:00
/*
fn new_env(&'a self, new_var: Rc<String>, ty: Type) -> TypeContext<'a> {
let mut new_context = TypeContext {
variable_map: self.variable_map.new_scope(None),
unification_table: UnificationTable::new(), //???? not sure if i want this
};
new_context.variable_map.insert(new_var, ty);
new_context
}
2019-05-20 16:10:50 -07:00
*/
2019-02-10 06:53:11 -08:00
fn get_type_from_name(&self, name: &TypeIdentifier) -> InferResult<Type> {
2019-02-10 07:05:01 -08:00
use self::TypeIdentifier::*;
Ok(match name {
2019-05-20 16:10:50 -07:00
Singleton(TypeSingletonName { name,.. }) => {
match Type::from_string(name) {
2019-02-10 07:05:01 -08:00
Some(ty) => ty,
2019-02-23 02:45:11 -08:00
None => return TypeError::new(format!("Unknown type name: {}", name))
2019-02-10 07:05:01 -08:00
}
},
Tuple(_) => return TypeError::new("tuples aren't ready yet"),
})
2019-02-10 06:53:11 -08:00
}
/// `typecheck` is the entry into the type-inference system, accepting an AST as an argument
/// Following the example of GHC, the compiler deliberately does typechecking before de-sugaring
/// the AST to ReducedAST
2019-02-20 01:33:45 -08:00
pub fn typecheck(&mut self, ast: &AST) -> Result<Type, TypeError> {
let mut returned_type = Type::Const(TypeConst::Unit);
2021-10-26 14:05:27 -07:00
for statement in ast.statements.statements.iter() {
returned_type = self.statement(statement)?;
2019-02-10 04:42:30 -08:00
}
2019-02-20 01:33:45 -08:00
Ok(returned_type)
2019-02-10 04:42:30 -08:00
}
2019-02-17 04:08:49 -08:00
fn statement(&mut self, statement: &Statement) -> InferResult<Type> {
2019-09-17 02:25:11 -07:00
match &statement.kind {
StatementKind::Expression(e) => self.expr(e),
StatementKind::Declaration(decl) => self.decl(decl),
2019-09-21 02:30:28 -07:00
StatementKind::Import(_) => Ok(ty!(Unit)),
2019-10-22 03:15:14 -07:00
StatementKind::Module(_) => Ok(ty!(Unit)),
2019-02-10 04:42:30 -08:00
}
}
2019-02-19 21:41:07 -08:00
fn decl(&mut self, decl: &Declaration) -> InferResult<Type> {
use self::Declaration::*;
2021-10-19 22:29:41 -07:00
if let Binding { name, expr, .. } = decl {
let ty = self.expr(expr)?;
2019-02-19 21:41:07 -08:00
self.variable_map.insert(name.clone(), ty);
}
2019-02-17 04:25:38 -08:00
Ok(ty!(Unit))
2019-02-10 04:42:30 -08:00
}
2019-06-12 03:28:46 -07:00
fn invoc(&mut self, invoc: &InvocationArgument) -> InferResult<Type> {
use InvocationArgument::*;
match invoc {
Positional(expr) => self.expr(expr),
2019-06-12 03:28:46 -07:00
_ => Ok(ty!(Nat)) //TODO this is wrong
}
}
2019-02-17 04:08:49 -08:00
fn expr(&mut self, expr: &Expression) -> InferResult<Type> {
match expr {
2019-09-18 14:15:05 -07:00
Expression { kind, type_anno: Some(anno), .. } => {
let t1 = self.expr_type(kind)?;
2019-02-10 06:53:11 -08:00
let t2 = self.get_type_from_name(anno)?;
self.unify(t2, t1)
},
2019-09-18 14:15:05 -07:00
Expression { kind, type_anno: None, .. } => self.expr_type(kind)
}
}
fn expr_type(&mut self, expr: &ExpressionKind) -> InferResult<Type> {
use self::ExpressionKind::*;
2019-02-10 05:31:58 -08:00
Ok(match expr {
2019-02-17 04:25:38 -08:00
NatLiteral(_) => ty!(Nat),
BoolLiteral(_) => ty!(Bool),
FloatLiteral(_) => ty!(Float),
StringLiteral(_) => ty!(StringT),
PrefixExp(op, expr) => self.prefix(op, expr)?,
BinExp(op, lhs, rhs) => self.binexp(op, lhs, rhs)?,
IfExpression { discriminator, body } => self.if_expr(deref_optional_box(discriminator), &**body)?,
2019-09-20 01:57:48 -07:00
Value(val) => self.handle_value(val)?,
Call { box ref f, arguments } => self.call(f, arguments)?,
2019-02-20 01:33:45 -08:00
Lambda { params, type_anno, body } => self.lambda(params, type_anno, body)?,
2019-02-17 04:25:38 -08:00
_ => ty!(Unit),
2019-02-10 05:31:58 -08:00
})
2018-11-06 13:44:52 -08:00
}
2019-02-10 06:48:25 -08:00
2019-02-17 04:08:49 -08:00
fn prefix(&mut self, op: &PrefixOp, expr: &Expression) -> InferResult<Type> {
let builtin: Option<Builtin> = TryFrom::try_from(op).ok();
let tf = match builtin.map(|b| b.get_type()) {
2019-08-13 04:17:17 -07:00
Some(ty) => ty,
None => return TypeError::new("no type found")
2019-02-10 12:21:12 -08:00
};
let tx = self.expr(expr)?;
self.handle_apply(tf, vec![tx])
2019-02-10 12:21:12 -08:00
}
2019-02-20 01:33:45 -08:00
fn binexp(&mut self, op: &BinOp, lhs: &Expression, rhs: &Expression) -> InferResult<Type> {
let builtin: Option<Builtin> = TryFrom::try_from(op).ok();
let tf = match builtin.map(|b| b.get_type()) {
2019-08-14 09:26:08 -07:00
Some(ty) => ty,
None => return TypeError::new("no type found"),
2019-02-20 03:27:46 -08:00
};
let t_lhs = self.expr(lhs)?;
let t_rhs = self.expr(rhs)?; //TODO is this order a problem? not sure
2019-02-10 12:21:12 -08:00
self.handle_apply(tf, vec![t_lhs, t_rhs])
2019-02-20 01:33:45 -08:00
}
2019-10-10 10:34:54 -07:00
fn if_expr(&mut self, discriminator: Option<&Expression>, body: &IfExpressionBody) -> InferResult<Type> {
use self::IfExpressionBody::*;
2019-02-19 23:00:41 -08:00
match (discriminator, body) {
2019-10-10 10:34:54 -07:00
(Some(expr), SimpleConditional{ then_case, else_case }) => self.handle_simple_if(expr, then_case, else_case),
_ => TypeError::new("Complex conditionals not supported".to_string())
2019-02-19 23:00:41 -08:00
}
}
2019-02-10 12:21:12 -08:00
2021-10-19 22:29:41 -07:00
#[allow(clippy::ptr_arg)]
2019-02-19 23:00:41 -08:00
fn handle_simple_if(&mut self, expr: &Expression, then_clause: &Block, else_clause: &Option<Block>) -> InferResult<Type> {
let t1 = self.expr(expr)?;
let t2 = self.block(then_clause)?;
let t3 = match else_clause {
Some(block) => self.block(block)?,
None => ty!(Unit)
};
let _ = self.unify(ty!(Bool), t1)?;
self.unify(t2, t3)
}
2021-10-19 22:29:41 -07:00
#[allow(clippy::ptr_arg)]
2019-05-20 22:04:14 -07:00
fn lambda(&mut self, params: &Vec<FormalParam>, type_anno: &Option<TypeIdentifier>, _body: &Block) -> InferResult<Type> {
2019-02-23 02:45:11 -08:00
let argument_types: InferResult<Vec<Type>> = params.iter().map(|param: &FormalParam| {
2019-06-16 14:56:52 -07:00
if let FormalParam { anno: Some(type_identifier), .. } = param {
2019-02-23 02:45:11 -08:00
self.get_type_from_name(type_identifier)
} else {
Ok(Type::Var(self.fresh_type_variable()))
}
}).collect();
let argument_types = argument_types?;
let ret_type = match type_anno.as_ref() {
Some(anno) => self.get_type_from_name(anno)?,
None => Type::Var(self.fresh_type_variable())
};
2019-02-23 00:34:44 -08:00
Ok(ty!(argument_types, ret_type))
2019-02-20 01:33:45 -08:00
}
2021-10-19 22:29:41 -07:00
fn call(&mut self, f: &Expression, args: &[ InvocationArgument ]) -> InferResult<Type> {
let tf = self.expr(f)?;
let arg_types: InferResult<Vec<Type>> = args.iter().map(|ex| self.invoc(ex)).collect();
2019-02-23 03:55:46 -08:00
let arg_types = arg_types?;
self.handle_apply(tf, arg_types)
2019-02-23 02:50:11 -08:00
}
fn handle_apply(&mut self, tf: Type, args: Vec<Type>) -> InferResult<Type> {
Ok(match tf {
Type::Arrow { ref params, ret: box ref t_ret } if params.len() == args.len() => {
for (t_param, t_arg) in params.iter().zip(args.iter()) {
let _ = self.unify(t_param.clone(), t_arg.clone())?; //TODO I think this needs to reference a sub-scope
}
t_ret.clone()
},
Type::Arrow { .. } => return TypeError::new("Wrong length"),
_ => return TypeError::new("Not a function".to_string())
})
}
2021-10-19 22:29:41 -07:00
#[allow(clippy::ptr_arg)]
2019-02-19 23:00:41 -08:00
fn block(&mut self, block: &Block) -> InferResult<Type> {
let mut output = ty!(Unit);
2021-10-26 14:05:27 -07:00
for statement in block.statements.iter() {
2019-02-19 23:00:41 -08:00
output = self.statement(statement)?;
}
Ok(output)
2019-02-10 12:21:12 -08:00
}
2019-02-10 06:48:25 -08:00
2019-08-31 23:39:01 -07:00
fn handle_value(&mut self, val: &QualifiedName) -> InferResult<Type> {
2019-09-19 01:34:21 -07:00
let QualifiedName { components: vec, .. } = val;
2019-08-31 23:39:01 -07:00
let var = &vec[0];
match self.variable_map.lookup(var) {
2019-02-12 21:14:13 -08:00
Some(ty) => Ok(ty.clone()),
2019-08-31 23:39:01 -07:00
None => TypeError::new(format!("Couldn't find variable: {}", &var)),
2019-02-12 21:14:13 -08:00
}
}
2019-02-10 06:53:11 -08:00
fn unify(&mut self, t1: Type, t2: Type) -> InferResult<Type> {
2019-02-21 18:39:41 -08:00
use self::Type::*;
2019-02-23 00:34:44 -08:00
match (t1, t2) {
(Const(ref c1), Const(ref c2)) if c1 == c2 => Ok(Const(c1.clone())), //choice of c1 is arbitrary I *think*
2019-02-23 02:45:11 -08:00
(a @ Var(_), b @ Const(_)) => self.unify(b, a),
2019-02-23 00:34:44 -08:00
(Const(ref c1), Var(ref v2)) => {
self.unification_table.unify_var_value(*v2, Some(c1.clone()))
2019-02-23 01:18:15 -08:00
.or_else(|_| TypeError::new(format!("Couldn't unify {:?} and {:?}", Const(c1.clone()), Var(*v2))))?;
2019-02-23 00:34:44 -08:00
Ok(Const(c1.clone()))
},
(Var(v1), Var(v2)) => {
2019-02-23 02:45:11 -08:00
//TODO add occurs check
self.unification_table.unify_var_var(v1, v2)
2019-02-24 16:24:45 -08:00
.or_else(|e| {
println!("Unify error: {:?}", e);
TypeError::new(format!("Two type variables {:?} and {:?} couldn't unify", v1, v2))
})?;
Ok(Var(v1)) //arbitrary decision I think
2019-02-23 00:34:44 -08:00
},
(a, b) => TypeError::new(format!("{:?} and {:?} do not unify", a, b)),
}
2019-02-10 06:48:25 -08:00
}
2019-02-23 01:27:32 -08:00
fn fresh_type_variable(&mut self) -> TypeVar {
self.unification_table.new_key(None)
2019-02-23 01:27:32 -08:00
}
2018-11-06 13:44:52 -08:00
}
2019-02-20 01:33:45 -08:00
#[cfg(test)]
mod typechecking_tests {
use super::*;
macro_rules! assert_type_in_fresh_context {
($string:expr, $type:expr) => {
let mut tc = TypeContext::new();
let ast = &crate::util::quick_ast($string);
2019-02-20 01:33:45 -08:00
let ty = tc.typecheck(ast).unwrap();
assert_eq!(ty, $type)
}
}
#[test]
fn basic_test() {
assert_type_in_fresh_context!("1", ty!(Nat));
assert_type_in_fresh_context!(r#""drugs""#, ty!(StringT));
assert_type_in_fresh_context!("true", ty!(Bool));
}
2019-02-20 03:27:46 -08:00
#[test]
fn operators() {
2019-08-13 04:17:17 -07:00
//TODO fix these with new operator regime
/*
assert_type_in_fresh_context!("-1", ty!(Int));
2019-02-20 03:27:46 -08:00
assert_type_in_fresh_context!("1 + 2", ty!(Nat));
assert_type_in_fresh_context!("-2", ty!(Int));
assert_type_in_fresh_context!("!true", ty!(Bool));
2019-08-13 04:17:17 -07:00
*/
2019-02-20 03:27:46 -08:00
}
2019-02-20 01:33:45 -08:00
}