about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_borrowck/borrowck/mir/gather_moves.rs52
-rw-r--r--src/librustc_borrowck/borrowck/mir/mod.rs6
-rw-r--r--src/test/run-pass/dynamic-drop.rs25
3 files changed, 60 insertions, 23 deletions
diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
index 2713a3c371d..bd38f554dc9 100644
--- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
@@ -12,7 +12,6 @@
 use rustc::ty::{self, TyCtxt, ParameterEnvironment};
 use rustc::mir::repr::*;
 use rustc::util::nodemap::FnvHashMap;
-use rustc::util::common::ErrorReported;
 use rustc_data_structures::indexed_vec::{IndexVec};
 
 use syntax::codemap::DUMMY_SP;
@@ -198,6 +197,11 @@ struct MoveDataBuilder<'a, 'tcx: 'a> {
     data: MoveData<'tcx>,
 }
 
+pub enum MovePathError {
+    IllegalMove,
+    UnionMove { path: MovePathIndex },
+}
+
 impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
     fn new(mir: &'a Mir<'tcx>,
            tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -256,7 +260,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
         move_path
     }
 
-    /// This creates a MovePath for a given lvalue, returning an `ErrorReported`
+    /// This creates a MovePath for a given lvalue, returning an `MovePathError`
     /// if that lvalue can't be moved from.
     ///
     /// NOTE: lvalues behind references *do not* get a move path, which is
@@ -264,7 +268,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
     ///
     /// Maybe we should have seperate "borrowck" and "moveck" modes.
     fn move_path_for(&mut self, lval: &Lvalue<'tcx>)
-                     -> Result<MovePathIndex, ErrorReported>
+                     -> Result<MovePathIndex, MovePathError>
     {
         debug!("lookup({:?})", lval);
         match *lval {
@@ -272,7 +276,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
             Lvalue::Arg(arg) => Ok(self.data.rev_lookup.args[arg]),
             Lvalue::Temp(temp) => Ok(self.data.rev_lookup.temps[temp]),
             // error: can't move out of a static
-            Lvalue::Static(..) => Err(ErrorReported),
+            Lvalue::Static(..) => Err(MovePathError::IllegalMove),
             Lvalue::ReturnPointer => match self.data.rev_lookup.return_ptr {
                 Some(ptr) => Ok(ptr),
                 ref mut ptr @ None => {
@@ -300,21 +304,28 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
     fn move_path_for_projection(&mut self,
                                 lval: &Lvalue<'tcx>,
                                 proj: &LvalueProjection<'tcx>)
-                                -> Result<MovePathIndex, ErrorReported>
+                                -> Result<MovePathIndex, MovePathError>
     {
         let base = try!(self.move_path_for(&proj.base));
         let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
         match lv_ty.sty {
             // error: can't move out of borrowed content
-            ty::TyRef(..) | ty::TyRawPtr(..) => return Err(ErrorReported),
+            ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove),
             // error: can't move out of struct with destructor
-            ty::TyStruct(adt, _) | ty::TyEnum(adt, _) if adt.has_dtor() =>
-                return Err(ErrorReported),
-
-            ty::TyArray(..) | ty::TySlice(..) => match proj.elem {
+            ty::TyAdt(adt, _) if adt.has_dtor() =>
+                return Err(MovePathError::IllegalMove),
+            // move out of union - always move the entire union
+            ty::TyAdt(adt, _) if adt.is_union() =>
+                return Err(MovePathError::UnionMove { path: base }),
+            // error: can't move out of a slice
+            ty::TySlice(..) =>
+                return Err(MovePathError::IllegalMove),
+            ty::TyArray(..) => match proj.elem {
                 // error: can't move out of an array
-                ProjectionElem::Index(..) => return Err(ErrorReported),
-                _ => {}
+                ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove),
+                _ => {
+                    // FIXME: still badly broken
+                }
             },
             _ => {}
         };
@@ -521,13 +532,16 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
             return
         }
 
-        let path = self.move_path_for(lval).unwrap_or_else(|_| {
-            // Moving out of a bad path. Eventually, this should be a MIR
-            // borrowck error instead of a bug.
-            span_bug!(self.mir.span,
-                      "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
-                      lval, lv_ty, loc);
-        });
+        let path = match self.move_path_for(lval) {
+            Ok(path) | Err(MovePathError::UnionMove { path }) => path,
+            Err(MovePathError::IllegalMove) => {
+                // Moving out of a bad path. Eventually, this should be a MIR
+                // borrowck error instead of a bug.
+                span_bug!(self.mir.span,
+                          "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}",
+                          lval, lv_ty, loc);
+            }
+        };
         let move_out = self.data.moves.push(MoveOut { path: path, source: loc });
 
         debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}",
diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs
index 7c2410a2c14..5b5d782bc83 100644
--- a/src/librustc_borrowck/borrowck/mir/mod.rs
+++ b/src/librustc_borrowck/borrowck/mir/mod.rs
@@ -256,12 +256,12 @@ fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
     let ty = lv.ty(mir, tcx).to_ty(tcx);
     match ty.sty {
         ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => {
-            debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} refd => false",
+            debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} refd => true",
                    lv, ty);
             true
         }
-        ty::TyAdt(def, _) if def.has_dtor() => {
-            debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => false",
+        ty::TyAdt(def, _) if def.has_dtor() || def.is_union() => {
+            debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => true",
                    lv, ty);
             true
         }
diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs
index 2b016dfb33e..a2cca206409 100644
--- a/src/test/run-pass/dynamic-drop.rs
+++ b/src/test/run-pass/dynamic-drop.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rustc_attrs)]
+#![feature(untagged_unions)]
 
 use std::cell::{Cell, RefCell};
 use std::panic;
@@ -111,6 +111,20 @@ fn assignment1(a: &Allocator, c0: bool) {
     _v = _w;
 }
 
+#[allow(unions_with_drop_fields)]
+union Boxy<T> {
+    a: T,
+    b: T,
+}
+
+fn union1(a: &Allocator) {
+    unsafe {
+        let mut u = Boxy { a: a.alloc() };
+        u.b = a.alloc();
+        drop(u.a);
+    }
+}
+
 fn run_test<F>(mut f: F)
     where F: FnMut(&Allocator)
 {
@@ -136,6 +150,13 @@ fn run_test<F>(mut f: F)
     }
 }
 
+fn run_test_nopanic<F>(mut f: F)
+    where F: FnMut(&Allocator)
+{
+    let first_alloc = Allocator::new(usize::MAX);
+    f(&first_alloc);
+}
+
 fn main() {
     run_test(|a| dynamic_init(a, false));
     run_test(|a| dynamic_init(a, true));
@@ -149,4 +170,6 @@ fn main() {
 
     run_test(|a| assignment1(a, false));
     run_test(|a| assignment1(a, true));
+
+    run_test_nopanic(|a| union1(a));
 }