From 1f6583fe06ecb307004a6a7744c43a26bac85e8c Mon Sep 17 00:00:00 2001
From: Scott Olson <scott@solson.me>
Date: Thu, 7 Apr 2016 03:02:02 -0600
Subject: [PATCH] Implement drop/deallocation for Box.

---
 src/interpreter.rs | 32 ++++++++++++++++++++++++++++++--
 src/memory.rs      | 16 ++++++++++++++++
 test/errors.rs     | 15 ++++++++++++---
 3 files changed, 58 insertions(+), 5 deletions(-)

diff --git a/src/interpreter.rs b/src/interpreter.rs
index 5ea2a117b8c..324a2008e38 100644
--- a/src/interpreter.rs
+++ b/src/interpreter.rs
@@ -360,8 +360,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> {
                 }
             }
 
-            Drop { target, .. } => {
-                // TODO: Handle destructors and dynamic drop.
+            Drop { ref value, target, .. } => {
+                let ptr = try!(self.eval_lvalue(value)).to_ptr();
+                let ty = self.lvalue_ty(value);
+                try!(self.drop(ptr, ty));
                 TerminatorTarget::Block(target)
             }
 
@@ -371,6 +373,28 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> {
         Ok(target)
     }
 
+    fn drop(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult<()> {
+        if !self.type_needs_drop(ty) {
+            self.log(1, || print!("no need to drop {:?}", ty));
+            return Ok(());
+        }
+        self.log(1, || print!("need to drop {:?}", ty));
+
+        match ty.sty {
+            ty::TyBox(contents_ty) => {
+                let contents_ptr = try!(self.memory.read_ptr(ptr));
+                try!(self.drop(contents_ptr, contents_ty));
+                self.log(1, || print!("deallocating box"));
+                try!(self.memory.deallocate(contents_ptr));
+            }
+
+            // TODO(tsion): Implement drop for other relevant types (e.g. aggregates).
+            _ => {}
+        }
+
+        Ok(())
+    }
+
     fn call_intrinsic(
         &mut self,
         name: &str,
@@ -847,6 +871,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> {
         infer::normalize_associated_type(self.tcx, &substituted)
     }
 
+    fn type_needs_drop(&self, ty: ty::Ty<'tcx>) -> bool {
+        self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment())
+    }
+
     fn type_is_sized(&self, ty: ty::Ty<'tcx>) -> bool {
         ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP)
     }
diff --git a/src/memory.rs b/src/memory.rs
index b41cd7760b0..661f4567264 100644
--- a/src/memory.rs
+++ b/src/memory.rs
@@ -140,6 +140,22 @@ impl Memory {
         Ok(())
     }
 
+    // TODO(tsion): See comment on `reallocate`.
+    pub fn deallocate(&mut self, ptr: Pointer) -> EvalResult<()> {
+        if ptr.offset != 0 {
+            // TODO(tsion): Report error about non-__rust_allocate'd pointer.
+            panic!()
+        }
+
+        if self.alloc_map.remove(&ptr.alloc_id.0).is_none() {
+            // TODO(tsion): Report error about erroneous free. This is blocked on properly tracking
+            // already-dropped state since this if-statement is entered even in safe code without
+            // it.
+        }
+
+        Ok(())
+    }
+
     ////////////////////////////////////////////////////////////////////////////////
     // Allocation accessors
     ////////////////////////////////////////////////////////////////////////////////
diff --git a/test/errors.rs b/test/errors.rs
index f6f5eae9d0f..a488d7acb43 100644
--- a/test/errors.rs
+++ b/test/errors.rs
@@ -19,20 +19,29 @@ fn pointers_to_different_allocations_are_unorderable() -> bool {
 }
 
 #[miri_run]
-fn invalid_bools_are_rejected() -> u8 {
+fn invalid_bool() -> u8 {
     let b = unsafe { std::mem::transmute::<u8, bool>(2) };
     if b { 1 } else { 2 }
 }
 
 #[miri_run]
-fn undefined_byte_reads_are_rejected() -> u8 {
+fn undefined_byte_read() -> u8 {
     let v: Vec<u8> = Vec::with_capacity(10);
     let undef = unsafe { *v.get_unchecked(5) };
     undef + 1
 }
 
 #[miri_run]
-fn out_of_bounds_reads_are_rejected() -> u8 {
+fn out_of_bounds_read() -> u8 {
     let v: Vec<u8> = vec![1, 2];
     unsafe { *v.get_unchecked(5) }
 }
+
+#[miri_run]
+fn dangling_pointer_deref() -> i32 {
+    let p = {
+        let b = Box::new(42);
+        &*b as *const i32
+    };
+    unsafe { *p }
+}