From 990d4f0f76992503600384586a0571a1e3915b65 Mon Sep 17 00:00:00 2001
From: Per Lindgren <per.lindgren@ltu.se>
Date: Fri, 18 Sep 2020 15:24:45 +0200
Subject: [PATCH] all tests pass

---
 examples/mut.rs     |  4 +--
 examples/mut2.rs    |  4 +--
 src/check.rs        | 61 ++++++++++++++++++++++++++++++++++++++-------
 tests/test_check.rs | 15 +++++++++++
 tests/test_vm.rs    |  4 +++
 5 files changed, 75 insertions(+), 13 deletions(-)

diff --git a/examples/mut.rs b/examples/mut.rs
index a93ad51..ec2e140 100644
--- a/examples/mut.rs
+++ b/examples/mut.rs
@@ -1,5 +1,5 @@
-fn main() -> i32 {
+fn main() {
     let mut a = 0;
     a = 2;
-    a
+    let b = a;
 }
diff --git a/examples/mut2.rs b/examples/mut2.rs
index 2166959..33fd4af 100644
--- a/examples/mut2.rs
+++ b/examples/mut2.rs
@@ -1,8 +1,8 @@
-fn main() -> i32 {
+fn main() {
     let mut a = 0;
     a = 2;
     let mut b = a; // copy semantics
     a = 3;
     b = b + 1;
-    a + b
+    let c = a + b;
 }
diff --git a/src/check.rs b/src/check.rs
index 1b16944..18d5675 100644
--- a/src/check.rs
+++ b/src/check.rs
@@ -190,6 +190,41 @@ fn expr_type(
     }
 }
 
+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)),
+    }
+}
+
 fn strip_mut(t: Type) -> Type {
     match t {
         Type::Mut(t) => *t,
@@ -228,7 +263,8 @@ pub fn check_stmts(
                 let t: Type = match (ot, oe) {
                     (Some(t), Some(e)) => {
                         let e_type = expr_type(&*e, fn_env, type_env, var_env)?;
-                        match *t == e_type {
+                        let e_type = strip_mut(e_type);
+                        match strip_mut(t.clone()) == e_type {
                             true => t.clone(),
                             false => {
                                 trace!("e {}", e);
@@ -237,9 +273,9 @@ pub fn check_stmts(
                         }
                     }
                     (None, Some(e)) => {
-                        let et = expr_type(&*e, fn_env, type_env, var_env)?;
-                        match is_known(&et) {
-                            true => et,
+                        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())?,
                         }
                     }
@@ -256,21 +292,28 @@ pub fn check_stmts(
             }
 
             Assign(lh, e) => {
+                trace!("assign");
+
                 let e_type = expr_type(&*e, fn_env, type_env, var_env)?;
-                trace!("e_type = {}", e_type);
-                trace!("lh = {:?}", lh);
+                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 = expr_type(lh, fn_env, type_env, var_env)?;
-                trace!("lh_type {}", &lh_type);
+                let lh_type = lexpr_type(lh, fn_env, type_env, var_env)?;
+                trace!("lh_type {}", lh_type);
 
-                if match &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,
diff --git a/tests/test_check.rs b/tests/test_check.rs
index d120948..6334148 100644
--- a/tests/test_check.rs
+++ b/tests/test_check.rs
@@ -21,6 +21,11 @@ fn check_assign2() {
     assert!(check(&read_file::parse("examples/assign2.rs")).is_ok());
 }
 
+#[test]
+fn check_assign3() {
+    assert!(check(&read_file::parse("examples/assign3.rs")).is_ok());
+}
+
 #[test]
 fn check_assign_err() {
     assert!(check(&read_file::parse("examples/assign_err.rs")).is_err());
@@ -154,3 +159,13 @@ fn check_ref_err3() {
 fn check_ref_err4() {
     assert!(check(&read_file::parse("examples/ref_err4.rs")).is_err());
 }
+
+#[test]
+fn check_scopes() {
+    assert!(check(&read_file::parse("examples/scopes.rs")).is_ok());
+}
+
+#[test]
+fn check_scopes_err() {
+    assert!(check(&read_file::parse("examples/scopes_err.rs")).is_err());
+}
diff --git a/tests/test_vm.rs b/tests/test_vm.rs
index 27086b6..33d7729 100644
--- a/tests/test_vm.rs
+++ b/tests/test_vm.rs
@@ -35,6 +35,10 @@ fn scopes_test() {
     eval_prog("examples/scopes.rs");
 }
 
+#[test]
+fn scopes_err_test() {
+    eval_prog("examples/scopes_err.rs");
+}
 #[test]
 fn vm_test() {
     eval_prog("examples/vm.rs");
-- 
GitLab