From 2db9d21c1dc222be9db847e0a09f3ce6cda0111e Mon Sep 17 00:00:00 2001 From: Per Lindgren <per.lindgren@ltu.se> Date: Thu, 24 Sep 2020 15:25:53 +0200 Subject: [PATCH] borrow check, wip0 --- Cargo.lock | 32 +-- src/bc_env.rs | 111 ++++++++++ src/borrow.rs | 568 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 4 files changed, 697 insertions(+), 16 deletions(-) create mode 100644 src/bc_env.rs create mode 100644 src/borrow.rs diff --git a/Cargo.lock b/Cargo.lock index cd8a887..04a4599 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "either" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "ena" @@ -235,9 +235,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ "cfg-if", "libc", @@ -252,9 +252,9 @@ checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" dependencies = [ "libc", ] @@ -329,9 +329,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.76" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" [[package]] name = "log" @@ -387,9 +387,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro2" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175c513d55719db99da20232b06cda8bab6b83ec2d04e3283edf0213c37c1a29" +checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" dependencies = [ "unicode-xid", ] @@ -458,18 +458,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" +checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" +checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" dependencies = [ "proc-macro2", "quote", @@ -515,9 +515,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] name = "syn" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350" +checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" dependencies = [ "proc-macro2", "quote", diff --git a/src/bc_env.rs b/src/bc_env.rs new file mode 100644 index 0000000..5565df2 --- /dev/null +++ b/src/bc_env.rs @@ -0,0 +1,111 @@ +// borrow check environment + +use std::iter::FromIterator; +// use std::iter::{FromIterator, Map}; + +use crate::ast::*; +// use crate::grammar::*; +use std::collections::{HashMap, VecDeque}; + +#[derive(Debug)] +pub enum Error { + NotFound(String), + Shared(String), + Unique(String), +} +#[derive(Debug)] +pub enum Bc { + Free, + Shared, + Unique, + Ref(Box<Bc>), +} + +type Scope = i32; + +pub type IdBc = HashMap<Id, Bc>; + +#[derive(Debug)] +pub struct BcEnv(VecDeque<IdBc>); + +impl BcEnv { + pub fn new() -> Self { + BcEnv(VecDeque::new()) + } + + pub fn get(&self, id: String) -> Option<(Scope, &Bc)> { + self.0 + .iter() + .enumerate() + .find_map(|(i, hm)| hm.get(id.as_str()).map(|bc| (i as i32, bc))) + } + + pub fn get_mut(&mut self, id: String) -> Option<(Scope, &mut Bc)> { + self.0 + .iter_mut() + .enumerate() + .find_map(|(i, hm)| hm.get_mut(id.as_str()).map(|bc| (i as i32, bc))) + } + + pub fn new_id(&mut self, id: String, bc: Bc) { + let hm = self.0.front_mut().unwrap(); + println!("insert id {:?}, in scope {:?}", id, hm); + + hm.insert(id, bc); + } + + pub fn update(&mut self, id: String, bc: Bc) -> Result<(), ()> { + println!("env: update"); + match self.get_mut(id.clone()) { + Some(b) => match b { + (_i, Bc::Free) => { + *b.1 = bc; + Ok(()) + } + (_i, Bc::Shared) => match bc { + Bc::Shared => Ok(()), + + Bc::Unique => Err(()), + _ => panic!("ICE update to Free"), + }, + _ => Err(()), + }, + None => Err(()), + } + } + + pub fn push_empty_scope(&mut self) { + self.0.push_front(HashMap::new()); + } + + pub fn push_param_scope(&mut self, args: IdBc) { + self.0.push_front(args) + } + + pub fn pop_scope(&mut self) { + self.0.pop_front(); + } +} + +#[test] +fn test_bc_env() { + let mut bc_env = BcEnv::new(); + println!("{:?}", bc_env); + bc_env.push_empty_scope(); + bc_env.new_id("a".to_string(), Bc::Free); + println!("{:?}", bc_env.get("a".to_string())); + bc_env.push_empty_scope(); + bc_env.new_id("b".to_string(), Bc::Free); + println!("{:?}", bc_env.get("a".to_string())); + println!("{:?}", bc_env.get("b".to_string())); + bc_env.update("a".to_string(), Bc::Shared).unwrap(); + println!("{:?}", bc_env.get("a".to_string())); + bc_env.update("a".to_string(), Bc::Shared).unwrap(); + println!("{:?}", bc_env.get("a".to_string())); + + // it should be an error to assign a shared ref to a unique + bc_env.update("a".to_string(), Bc::Unique).unwrap_err(); + bc_env.pop_scope(); + println!("{:?}", bc_env.get("b".to_string())); + println!("{:?}", bc_env.get("a".to_string())); +} diff --git a/src/borrow.rs b/src/borrow.rs new file mode 100644 index 0000000..5b4781c --- /dev/null +++ b/src/borrow.rs @@ -0,0 +1,568 @@ +use std::io::Write; + +use env_logger::Builder; +use log::{trace, LevelFilter}; +use std::convert::From; + +use crate::ast::*; +use crate::bc_env::{Bc, BcEnv, Error, IdBc}; +use crate::env::{new_fn_env, new_type_env, FnEnv, TypeEnv}; + +// type check + +fn bc_expr( + e: &Expr, + fn_env: &FnEnv, + type_env: &TypeEnv, + bc_env: &mut BcEnv, +) -> Result<Type, Error> { + use Expr::*; + println!("expr_type {}", e); + println!("var_env {:?}", bc_env); + + match e { + // Num(_) => Ok(Type::I32), + // Bool(_) => Ok(Type::Bool), + // Infix(l, op, r) => { + // let lt = expr_type(l, fn_env, type_env, var_env)?; + // let rt = expr_type(r, fn_env, type_env, var_env)?; + + // let lt = strip_mut(lt); + // let rt = strip_mut(rt); + + // use Op::*; + // match op { + // // Arithmetic and Boolen + // Add | Mul | Div | Sub | And | Or => { + // // check if op and args are compliant + // let opt = op.get_type(); + // if lt == opt && rt == opt { + // Ok(opt) + // } else { + // Err(format!( + // "Expected type {}, found, left {}: {}, {:?} right {}: {}, {:?}", + // opt, l, lt, lt, r, rt, rt + // )) + // } + // } + // // Equality + // Eq | Neq => { + // // check if args are of same type + // if lt == rt { + // Ok(Type::Bool) + // } else { + // Err(format!( + // "Comparison requires operands of same type, left {}, right {}", + // lt, rt + // )) + // } + // } + // // Comparison + // Less | Greater | LessEq | GreaterEq => { + // if lt == Type::I32 && rt == Type::I32 { + // // check if args are of i32 + // Ok(Type::Bool) + // } else { + // Err(format!( + // "Comparison requires operands of same type, left {}, right {}", + // lt, rt + // )) + // } + // } + // _ => panic!("ICE on {}", e), + // } + // } + + // Prefix(op, r) => { + // let rt = expr_type(r, fn_env, type_env, var_env)?; + // let opt = op.get_type(); + + // // check if both of same type + // if rt == opt { + // Ok(opt) + // } else { + // Err(format!("op {} rt {}", opt, rt)) + // } + // } + + // Call(s, args) => { + // trace!("call {} with {}", s, args); + + // let arg_t: Vec<Type> = args + // .clone() + // .0 + // .into_iter() + // .map(|e| expr_type(&*e, fn_env, type_env, var_env)) + // .collect::<Result<_, _>>()?; + + // trace!("arg types {:?}", arg_t); + // let f = match fn_env.get(s.as_str()) { + // Some(f) => f, + // None => Err(format!("{} not found", s))?, + // }; + + // let par_t: Vec<Type> = (f.params.0.clone()) + // .into_iter() + // .map(|p| From::from(&p)) + // .collect(); + + // trace!( + // "fn to call {} with params {} and types {:?}", + // f, + // f.params, + // par_t + // ); + + // if arg_t == par_t { + // Ok(f.result.clone()) + // } else { + // Err(format!( + // "arguments types {:?} does not match parameter types {:?}", + // arg_t, par_t + // )) + // } + // } + + // Id(id) => match var_env.get(id.to_string()) { + // Some(t) => Ok(t.clone()), + // None => Err(format!("variable not found {}", id)), + // }, + + // As(_e, _t) => unimplemented!("here we implement explicit type cast"), + + // // Convert Expr::Ref to Type::Ref + // Ref(ref_e) => { + // let t = expr_type(ref_e, fn_env, type_env, var_env)?; + // trace!("ref_e {}, t {}", ref_e, t); + // let t = match t { + // Type::Mut(t) => t.clone(), + // t => Box::new(t), + // }; + // Ok(Type::Ref(t)) + // } + + // // Convert Expr::Mut to Type::Mut + // RefMut(ref_mut_e) => { + // let t = expr_type(ref_mut_e, fn_env, type_env, var_env)?; + // match t { + // Type::Mut(_) => Ok(Type::Ref(Box::new(t))), + // _ => Err(format!("{} is not mutable in {}", t, ref_mut_e)), + // } + // } + + // DeRef(deref_e) => { + // let t = expr_type(deref_e, fn_env, type_env, var_env)?; + // trace!("deref_t {}", &t); + // let t = strip_mut(t); + // trace!("strip deref_t {}", &t); + // match t { + // Type::Ref(dt) => Ok(*dt), + // _ => Err(format!("cannot deref {} of type {}", e, t)), + // } + // } + + // Block(b) => check_stmts(b, fn_env, type_env, var_env), + + // Stmt(s) => check_stmt(s, fn_env, type_env, var_env), + _ => unimplemented!(), + } +} + +// fn lexpr_type<'a>( +// e: &Expr, +// fn_env: &FnEnv, +// type_env: &TypeEnv, +// var_env: &'a mut VarEnv, +// ) -> Result<&'a mut Type, Error> { +// use Expr::*; +// trace!("expr_type {}", e); +// trace!("var_env {:?}", var_env); + +// match e { +// Id(id) => match var_env.get_mut(id.to_string()) { +// Some(t) => Ok(t), +// None => Err(format!("variable not found {}", id)), +// }, + +// DeRef(deref_e) => { +// let t = lexpr_type(deref_e, fn_env, type_env, var_env)?; +// trace!("deref_t {}", &t); + +// // strip mut +// let t = match t { +// Type::Mut(t) => t, +// _ => t, +// }; +// trace!("strip deref_t {}", t); +// match t { +// Type::Ref(dt) => Ok(dt), +// _ => Err(format!("cannot deref {} of type {}", e, t)), +// } +// } +// _ => Err(format!("Illegal left hand expression {}", e)), +// } +// } + +// strip a "mut T" to a T +fn strip_mut(t: Type) -> Type { + match t { + Type::Mut(t) => *t, + _ => t, + } +} + +// determine if a type is declared recursively +fn is_known(t: &Type) -> bool { + use Type::*; + match t { + Type::Unknown => false, + Unit | Bool | I32 => true, + Mut(t) | Ref(t) => is_known(t), + _ => panic!("ICE is_known"), + } +} + +pub fn bc_stmt( + s: &Stmt, + fn_env: &FnEnv, + type_env: &TypeEnv, + bc_env: &mut BcEnv, +) -> Result<(), Error> { + use Stmt::*; + // trace!("stmt: {}", s); + // match s { + // Stmt::Block(b) => check_stmts(b, fn_env, type_env, var_env), + + // Expr(e) => expr_type(&*e, fn_env, type_env, var_env), + + // Let(var_id, is_mut, ot, oe) => { + // let t: Type = match (ot, oe) { + // (Some(t), Some(e)) => { + // let e_type = expr_type(&*e, fn_env, type_env, var_env)?; + // let e_type = strip_mut(e_type); + // match strip_mut(t.clone()) == e_type { + // true => t.clone(), + // false => { + // trace!("e {}", e); + // Err(format!("incompatible types, {} <> {}", t, e_type))? + // } + // } + // } + // (None, Some(e)) => { + // let e_type = strip_mut(expr_type(&*e, fn_env, type_env, var_env)?); + // match is_known(&e_type) { + // true => e_type, + // _ => Err("reference to unknown type".to_string())?, + // } + // } + // (Some(t), None) => t.clone(), + // _ => Type::Unknown, + // }; + // let t = match is_mut { + // true => Type::Mut(Box::new(t)), + // false => t, + // }; + // var_env.new_id(var_id.clone(), t); + // trace!("var_env {:?}", var_env); + // Ok(Type::Unit) + // } + + // Assign(lh, e) => { + // trace!("assign"); + + // let e_type = expr_type(&*e, fn_env, type_env, var_env)?; + // trace!("e_type = {}", &e_type); + // let e_type = strip_mut(e_type); + // trace!("e_type stripped = {}", &e_type); + + // trace!("lh expr = {:?}", lh); + + // let lh_type = lexpr_type(lh, fn_env, type_env, var_env)?; + // trace!("lh_type {}", lh_type); + + // if match lh_type { + // Type::Unknown => { + // trace!("assign to unknown"); + // *lh_type = e_type.clone(); + // true + // } + // Type::Mut(t) => match **t { + // Type::Unknown => { + // trace!("assign to `mut Unknown`"); + // *t = Box::new(e_type.clone()); + // true + // } + // _ => **t == e_type, + // }, + + // _ => Err(format!("assignment to immutable"))?, + // } { + // Ok(Type::Unit) + // } else { + // Err(format!("cannot assign {} = {}", &lh_type, e_type)) + // } + // } + + // While(e, block) => match expr_type(&*e, fn_env, type_env, var_env) { + // Ok(Type::Bool) => { + // let _ = check_stmts(&block, fn_env, type_env, var_env)?; + // Ok(Type::Unit) // a while statement is of unit type; + // } + // _ => Err("Condition not Boolean".to_string()), + // }, + + // If(e, then, o_else) => match expr_type(&*e, fn_env, type_env, var_env)? { + // Type::Bool => { + // // The condition is of Bool type + // let then_type = check_stmts(&then, fn_env, type_env, var_env)?; + // trace!("then type {}", then_type); + // match o_else { + // None => Ok(then_type), // type of the arm + // Some(else_stmts) => { + // let else_type = check_stmts(&else_stmts, fn_env, type_env, var_env)?; + // trace!("else type {}", else_type); + + // match then_type == else_type { + // true => Ok(then_type), // same type of both arms + // false => { + // trace!("error-----"); + // Err(format!( + // "'then' arm :{} does not match 'else' arm :{}", + // then_type, else_type + // )) + // } + // } + // } + // } + // } + // _ => Err("Condition not Boolean".to_string()), + // }, + // Semi => Ok(Type::Unit), + // } + Ok(()) +} + +pub fn bc_stmts( + stmts: &Stmts, + fn_env: &FnEnv, + type_env: &TypeEnv, + bc_env: &mut BcEnv, +) -> Result<(), Error> { + bc_env.push_empty_scope(); + + let t = stmts + .stmts + .iter() + .try_fold((), |_, s| bc_stmt(s, fn_env, type_env, bc_env))?; + + bc_env.pop_scope(); + // if stmts.trailing_semi { + // Ok(()) + // } else { + // Ok(t.clone()) + // } + + Ok(()) +} + +pub fn build_env(p: &Program) -> (FnEnv, TypeEnv) { + let fn_env = new_fn_env(&p.fn_decls); + let type_env = new_type_env(&p.type_decls); + + (fn_env, type_env) +} + +// pub fn dump_env(fn_env: &FnEnv, type_env: &TypeEnv) { +// trace!("fn_env {:?}", fn_env); +// trace!("type_env {:?}", type_env); +// } + +// check a whole Program +pub fn bc_prog(p: &Program) -> Result<(), Error> { + let (fn_env, type_env) = build_env(&p); + + println!("Input program \n{}", &p); + + for fd in p.fn_decls.iter() { + println!("check function\n{}", fd); + println!("ast\n{:?}", fd); + + let mut bc_env = BcEnv::new(); + + // build a scope for the arguments + let mut arg_ty = IdBc::new(); + + for Param { is_mut, id, ty } in fd.params.0.iter() { + let ty = match *is_mut { + true => Bc::Unique, + _ => Bc::Shared, + }; + arg_ty.insert(id.to_owned(), ty); + } + + // var_env.push_param_scope(arg_ty); + + // let stmt_type = check_stmts(&fd.body, &fn_env, &type_env, &mut var_env); + // trace!("result {}: {} {:?}", &fd.id, &fd.result, &stmt_type); + + // let stmt_type = strip_mut(stmt_type?); + + // if stmt_type != fd.result { + // Err(format!( + // "return type {} does not match statements type {}", + // fd.result, stmt_type + // ))?; + // } + } + Ok(()) +} + +// unit test + +// #[test] +// fn test_stmts() { +// use crate::grammar::*; +// use crate::*; +// // use Type::*; + +// // setup environment +// let p = ProgramParser::new().parse(prog1!()).unwrap(); +// let fn_env = new_fn_env(&p.fn_decls); +// let type_env = new_type_env(&p.type_decls); +// let mut var_env = VarEnv::new(); + +// trace!("{}", &p); + +// let b = fn_env.get(&"b").unwrap(); + +// let body = &b.body; +// trace!("{}", &body); + +// trace!("{:?}", check_stmts(body, &fn_env, &type_env, &mut var_env)); +// } + +// #[test] +// fn test_expr_type() { +// use crate::grammar::*; +// use crate::*; +// use Type::*; + +// // setup environment +// let i = prog1!(); +// println!("{}", i); +// let p = ProgramParser::new().parse(prog1!()).unwrap(); +// let fn_env = new_fn_env(&p.fn_decls); +// let type_env = new_type_env(&p.type_decls); +// let mut var_env = VarEnv::new(); +// var_env.push_empty_scope(); + +// // // some test variables in scope +// var_env.new_id("i".to_string(), I32); // let i : i32 ... +// var_env.new_id("j".to_string(), Unknown); // let i ... + +// println!("p {}", p); + +// // type of number +// assert_eq!( +// expr_type(&Expr::Num(1), &fn_env, &type_env, &mut var_env), +// Ok(I32) +// ); + +// // type of variables + +// // not found +// assert!(expr_type(&Expr::Id("a".to_string()), &fn_env, &type_env, &mut var_env).is_err()); + +// // let i: i32 ... +// assert_eq!( +// expr_type(&Expr::Id("i".to_string()), &fn_env, &type_env, &mut var_env), +// Ok(I32) +// ); + +// // let j ... (has no type yet) +// assert_eq!( +// expr_type(&Expr::Id("j".to_string()), &fn_env, &type_env, &mut var_env), +// Ok(Unknown) +// ); + +// // // let n: A ... +// // assert_eq!( +// // expr_type(&Expr::Id("n".to_string()), &fn_env, &type_env, &var_env), +// // Ok(Named("A".to_string())) +// // ); + +// // type of arithmetic operation (for now just i32) +// assert_eq!( +// expr_type( +// &*ExprParser::new().parse("1 + 2 - 5").unwrap(), +// &fn_env, +// &type_env, +// &mut var_env +// ), +// Ok(I32) +// ); + +// // type of arithmetic unary operation (for now just i32) +// assert_eq!( +// expr_type( +// &*ExprParser::new().parse("- 5").unwrap(), +// &fn_env, +// &type_env, +// &mut var_env +// ), +// Ok(I32) +// ); + +// // call, with check, ok +// assert_eq!( +// expr_type( +// &*ExprParser::new().parse("b(1)").unwrap(), +// &fn_env, +// &type_env, +// &mut var_env +// ), +// Ok(I32) +// ); + +// // call, with check, ok (i: i32) +// assert_eq!( +// expr_type( +// &*ExprParser::new().parse("b(i)").unwrap(), +// &fn_env, +// &type_env, +// &mut var_env +// ), +// Ok(I32) +// ); + +// // call, with check, error wrong number args +// assert!(expr_type( +// &*ExprParser::new().parse("b(1, 2)").unwrap(), +// &fn_env, +// &type_env, +// &mut var_env +// ) +// .is_err()); + +// // call, with check, error type of arg +// assert!(expr_type( +// &*ExprParser::new().parse("b(true)").unwrap(), +// &fn_env, +// &type_env, +// &mut var_env +// ) +// .is_err()); + +// // call, with check, ok (i: i32) +// assert_eq!( +// expr_type( +// &*ExprParser::new().parse("c(n)").unwrap(), +// &fn_env, +// &type_env, +// &var_env +// ), +// Ok(Unit) +// ); + +// TODO, ref/ref mut/deref +// } diff --git a/src/lib.rs b/src/lib.rs index 5ecdd86..193a9e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,8 @@ pub mod check; #[macro_use] pub mod input; +pub mod bc_env; +pub mod borrow; pub mod env; pub mod read_file; pub mod vm; -- GitLab