about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAriel Ben-Yehuda <ariel.byd@gmail.com>2017-02-26 16:21:08 +0200
committerAriel Ben-Yehuda <ariel.byd@gmail.com>2017-03-03 13:54:18 +0200
commit906c06a2f6add77531c08dfd4014e7c65c1743b2 (patch)
treee551f93b29cc14efb0f68cb252c89945c18d4a93
parent6755fb8ba2300a121cb14bd79327c3eb730bc55d (diff)
downloadrust-906c06a2f6add77531c08dfd4014e7c65c1743b2.tar.gz
rust-906c06a2f6add77531c08dfd4014e7c65c1743b2.zip
make operands live to the end of their containing expression
In MIR construction, operands need to live exactly until they are used,
which is during the (sub)expression that made the call to `as_operand`.

Before this PR, operands lived until the end of the temporary scope,
which was sometimes unnecessarily longer and sometimes too short.

Fixes #38669.
-rw-r--r--src/librustc_mir/build/expr/as_lvalue.rs8
-rw-r--r--src/librustc_mir/build/expr/as_operand.rs32
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs53
-rw-r--r--src/librustc_mir/build/expr/as_temp.rs21
-rw-r--r--src/librustc_mir/build/expr/into.rs16
-rw-r--r--src/librustc_mir/build/expr/stmt.rs8
-rw-r--r--src/librustc_mir/build/matches/mod.rs2
-rw-r--r--src/librustc_mir/build/scope.rs10
-rw-r--r--src/test/mir-opt/basic_assignment.rs85
-rw-r--r--src/test/mir-opt/issue-38669.rs52
-rw-r--r--src/test/mir-opt/storage_ranges.rs5
-rw-r--r--src/test/run-pass/mir_drop_order.rs21
12 files changed, 257 insertions, 56 deletions
diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs
index 5abfe084f22..8886a310429 100644
--- a/src/librustc_mir/build/expr/as_lvalue.rs
+++ b/src/librustc_mir/build/expr/as_lvalue.rs
@@ -56,8 +56,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
 
                 let slice = unpack!(block = this.as_lvalue(block, lhs));
-
-                let idx = unpack!(block = this.as_operand(block, index));
+                // extent=None so lvalue indexes live forever. They are scalars so they
+                // do not need storage annotations, and they are often copied between
+                // places.
+                let idx = unpack!(block = this.as_operand(block, None, index));
 
                 // bounds check:
                 let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty));
@@ -121,7 +123,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     Some(Category::Lvalue) => false,
                     _ => true,
                 });
-                this.as_temp(block, expr)
+                this.as_temp(block, expr.temp_lifetime, expr)
             }
         }
     }
diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs
index 09cdcc74ef6..8d79e755685 100644
--- a/src/librustc_mir/build/expr/as_operand.rs
+++ b/src/librustc_mir/build/expr/as_operand.rs
@@ -13,29 +13,52 @@
 use build::{BlockAnd, BlockAndExtension, Builder};
 use build::expr::category::Category;
 use hair::*;
+use rustc::middle::region::CodeExtent;
 use rustc::mir::*;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
+    /// Returns an operand suitable for use until the end of the current
+    /// scope expression.
+    ///
+    /// The operand returned from this function will *not be valid* after
+    /// an ExprKind::Scope is passed, so please do *not* return it from
+    /// functions to avoid bad miscompiles.
+    pub fn as_local_operand<M>(&mut self, block: BasicBlock, expr: M)
+                             -> BlockAnd<Operand<'tcx>>
+        where M: Mirror<'tcx, Output = Expr<'tcx>>
+    {
+        let topmost_scope = self.topmost_scope(); // FIXME(#6393)
+        self.as_operand(block, Some(topmost_scope), expr)
+    }
+
     /// Compile `expr` into a value that can be used as an operand.
     /// If `expr` is an lvalue like `x`, this will introduce a
     /// temporary `tmp = x`, so that we capture the value of `x` at
     /// this time.
-    pub fn as_operand<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Operand<'tcx>>
+    ///
+    /// The operand is known to be live until the end of `scope`.
+    pub fn as_operand<M>(&mut self,
+                         block: BasicBlock,
+                         scope: Option<CodeExtent>,
+                         expr: M) -> BlockAnd<Operand<'tcx>>
         where M: Mirror<'tcx, Output = Expr<'tcx>>
     {
         let expr = self.hir.mirror(expr);
-        self.expr_as_operand(block, expr)
+        self.expr_as_operand(block, scope, expr)
     }
 
     fn expr_as_operand(&mut self,
                        mut block: BasicBlock,
+                       scope: Option<CodeExtent>,
                        expr: Expr<'tcx>)
                        -> BlockAnd<Operand<'tcx>> {
         debug!("expr_as_operand(block={:?}, expr={:?})", block, expr);
         let this = self;
 
         if let ExprKind::Scope { extent, value } = expr.kind {
-            return this.in_scope(extent, block, |this| this.as_operand(block, value));
+            return this.in_scope(extent, block, |this| {
+                this.as_operand(block, scope, value)
+            });
         }
 
         let category = Category::of(&expr.kind).unwrap();
@@ -47,7 +70,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
             Category::Lvalue |
             Category::Rvalue(..) => {
-                let operand = unpack!(block = this.as_temp(block, expr));
+                let operand =
+                    unpack!(block = this.as_temp(block, scope, expr));
                 block.and(Operand::Consume(operand))
             }
         }
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 7f5d9c36ece..961713bcfe6 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -21,22 +21,34 @@ use build::expr::category::{Category, RvalueFunc};
 use hair::*;
 use rustc_const_math::{ConstInt, ConstIsize};
 use rustc::middle::const_val::ConstVal;
+use rustc::middle::region::CodeExtent;
 use rustc::ty;
 use rustc::mir::*;
 use syntax::ast;
 use syntax_pos::Span;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
+    /// See comment on `as_local_operand`
+    pub fn as_local_rvalue<M>(&mut self, block: BasicBlock, expr: M)
+                             -> BlockAnd<Rvalue<'tcx>>
+        where M: Mirror<'tcx, Output = Expr<'tcx>>
+    {
+        let topmost_scope = self.topmost_scope(); // FIXME(#6393)
+        self.as_rvalue(block, Some(topmost_scope), expr)
+    }
+
     /// Compile `expr`, yielding an rvalue.
-    pub fn as_rvalue<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Rvalue<'tcx>>
+    pub fn as_rvalue<M>(&mut self, block: BasicBlock, scope: Option<CodeExtent>, expr: M)
+                        -> BlockAnd<Rvalue<'tcx>>
         where M: Mirror<'tcx, Output = Expr<'tcx>>
     {
         let expr = self.hir.mirror(expr);
-        self.expr_as_rvalue(block, expr)
+        self.expr_as_rvalue(block, scope, expr)
     }
 
     fn expr_as_rvalue(&mut self,
                       mut block: BasicBlock,
+                      scope: Option<CodeExtent>,
                       expr: Expr<'tcx>)
                       -> BlockAnd<Rvalue<'tcx>> {
         debug!("expr_as_rvalue(block={:?}, expr={:?})", block, expr);
@@ -47,10 +59,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
         match expr.kind {
             ExprKind::Scope { extent, value } => {
-                this.in_scope(extent, block, |this| this.as_rvalue(block, value))
+                this.in_scope(extent, block, |this| this.as_rvalue(block, scope, value))
             }
             ExprKind::Repeat { value, count } => {
-                let value_operand = unpack!(block = this.as_operand(block, value));
+                let value_operand = unpack!(block = this.as_operand(block, scope, value));
                 block.and(Rvalue::Repeat(value_operand, count))
             }
             ExprKind::Borrow { region, borrow_kind, arg } => {
@@ -58,13 +70,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 block.and(Rvalue::Ref(region, borrow_kind, arg_lvalue))
             }
             ExprKind::Binary { op, lhs, rhs } => {
-                let lhs = unpack!(block = this.as_operand(block, lhs));
-                let rhs = unpack!(block = this.as_operand(block, rhs));
+                let lhs = unpack!(block = this.as_operand(block, scope, lhs));
+                let rhs = unpack!(block = this.as_operand(block, scope, rhs));
                 this.build_binary_op(block, op, expr_span, expr.ty,
                                      lhs, rhs)
             }
             ExprKind::Unary { op, arg } => {
-                let arg = unpack!(block = this.as_operand(block, arg));
+                let arg = unpack!(block = this.as_operand(block, scope, arg));
                 // Check for -MIN on signed integers
                 if this.hir.check_overflow() && op == UnOp::Neg && expr.ty.is_signed() {
                     let bool_ty = this.hir.bool_ty();
@@ -97,27 +109,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             ExprKind::Cast { source } => {
                 let source = this.hir.mirror(source);
 
-                let source = unpack!(block = this.as_operand(block, source));
+                let source = unpack!(block = this.as_operand(block, scope, source));
                 block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
             }
             ExprKind::Use { source } => {
-                let source = unpack!(block = this.as_operand(block, source));
+                let source = unpack!(block = this.as_operand(block, scope, source));
                 block.and(Rvalue::Use(source))
             }
             ExprKind::ReifyFnPointer { source } => {
-                let source = unpack!(block = this.as_operand(block, source));
+                let source = unpack!(block = this.as_operand(block, scope, source));
                 block.and(Rvalue::Cast(CastKind::ReifyFnPointer, source, expr.ty))
             }
             ExprKind::UnsafeFnPointer { source } => {
-                let source = unpack!(block = this.as_operand(block, source));
+                let source = unpack!(block = this.as_operand(block, scope, source));
                 block.and(Rvalue::Cast(CastKind::UnsafeFnPointer, source, expr.ty))
             }
             ExprKind::ClosureFnPointer { source } => {
-                let source = unpack!(block = this.as_operand(block, source));
+                let source = unpack!(block = this.as_operand(block, scope, source));
                 block.and(Rvalue::Cast(CastKind::ClosureFnPointer, source, expr.ty))
             }
             ExprKind::Unsize { source } => {
-                let source = unpack!(block = this.as_operand(block, source));
+                let source = unpack!(block = this.as_operand(block, scope, source));
                 block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty))
             }
             ExprKind::Array { fields } => {
@@ -150,7 +162,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // first process the set of fields
                 let fields: Vec<_> =
                     fields.into_iter()
-                          .map(|f| unpack!(block = this.as_operand(block, f)))
+                          .map(|f| unpack!(block = this.as_operand(block, scope, f)))
                           .collect();
 
                 block.and(Rvalue::Aggregate(AggregateKind::Array, fields))
@@ -159,7 +171,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // first process the set of fields
                 let fields: Vec<_> =
                     fields.into_iter()
-                          .map(|f| unpack!(block = this.as_operand(block, f)))
+                          .map(|f| unpack!(block = this.as_operand(block, scope, f)))
                           .collect();
 
                 block.and(Rvalue::Aggregate(AggregateKind::Tuple, fields))
@@ -167,7 +179,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             ExprKind::Closure { closure_id, substs, upvars } => { // see (*) above
                 let upvars =
                     upvars.into_iter()
-                          .map(|upvar| unpack!(block = this.as_operand(block, upvar)))
+                          .map(|upvar| unpack!(block = this.as_operand(block, scope, upvar)))
                           .collect();
                 block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars))
             }
@@ -179,10 +191,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                 // first process the set of fields that were provided
                 // (evaluating them in order given by user)
-                let fields_map: FxHashMap<_, _> =
-                    fields.into_iter()
-                          .map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr))))
-                          .collect();
+                let fields_map: FxHashMap<_, _> = fields.into_iter()
+                    .map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr))))
+                    .collect();
 
                 let field_names = this.hir.all_fields(adt_def, variant_index);
 
@@ -235,7 +246,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     Some(Category::Rvalue(RvalueFunc::AsRvalue)) => false,
                     _ => true,
                 });
-                let operand = unpack!(block = this.as_operand(block, expr));
+                let operand = unpack!(block = this.as_operand(block, scope, expr));
                 block.and(Rvalue::Use(operand))
             }
         }
diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs
index 0ae4bcc4205..69b95702009 100644
--- a/src/librustc_mir/build/expr/as_temp.rs
+++ b/src/librustc_mir/build/expr/as_temp.rs
@@ -13,29 +13,38 @@
 use build::{BlockAnd, BlockAndExtension, Builder};
 use build::expr::category::Category;
 use hair::*;
+use rustc::middle::region::CodeExtent;
 use rustc::mir::*;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// Compile `expr` into a fresh temporary. This is used when building
     /// up rvalues so as to freeze the value that will be consumed.
-    pub fn as_temp<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Lvalue<'tcx>>
+    pub fn as_temp<M>(&mut self,
+                      block: BasicBlock,
+                      temp_lifetime: Option<CodeExtent>,
+                      expr: M)
+                      -> BlockAnd<Lvalue<'tcx>>
         where M: Mirror<'tcx, Output = Expr<'tcx>>
     {
         let expr = self.hir.mirror(expr);
-        self.expr_as_temp(block, expr)
+        self.expr_as_temp(block, temp_lifetime, expr)
     }
 
-    fn expr_as_temp(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<Lvalue<'tcx>> {
+    fn expr_as_temp(&mut self,
+                    mut block: BasicBlock,
+                    temp_lifetime: Option<CodeExtent>,
+                    expr: Expr<'tcx>)
+                    -> BlockAnd<Lvalue<'tcx>> {
         debug!("expr_as_temp(block={:?}, expr={:?})", block, expr);
         let this = self;
 
-        if let ExprKind::Scope { extent, value } = expr.kind {
-            return this.in_scope(extent, block, |this| this.as_temp(block, value));
+        if let ExprKind::Scope { .. } = expr.kind {
+            span_bug!(expr.span, "unexpected scope expression in as_temp: {:?}",
+                      expr);
         }
 
         let expr_ty = expr.ty.clone();
         let temp = this.temp(expr_ty.clone());
-        let temp_lifetime = expr.temp_lifetime;
         let expr_span = expr.span;
         let source_info = this.source_info(expr_span);
 
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index ae51951b519..dab85106873 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -52,7 +52,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     _ => false,
                 };
 
-                unpack!(block = this.as_rvalue(block, source));
+                unpack!(block = this.as_local_rvalue(block, source));
 
                 // This is an optimization. If the expression was a call then we already have an
                 // unreachable block. Don't bother to terminate it and create a new one.
@@ -65,7 +65,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 }
             }
             ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
-                let operand = unpack!(block = this.as_operand(block, cond_expr));
+                let operand = unpack!(block = this.as_local_operand(block, cond_expr));
 
                 let mut then_block = this.cfg.start_new_block();
                 let mut else_block = this.cfg.start_new_block();
@@ -107,7 +107,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     (this.cfg.start_new_block(), this.cfg.start_new_block(),
                      this.cfg.start_new_block(), this.cfg.start_new_block());
 
-                let lhs = unpack!(block = this.as_operand(block, lhs));
+                let lhs = unpack!(block = this.as_local_operand(block, lhs));
                 let blocks = match op {
                     LogicalOp::And => (else_block, false_block),
                     LogicalOp::Or => (true_block, else_block),
@@ -115,7 +115,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
                 this.cfg.terminate(block, source_info, term);
 
-                let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
+                let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
                 let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
                 this.cfg.terminate(else_block, source_info, term);
 
@@ -173,7 +173,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         if let Some(cond_expr) = opt_cond_expr {
                             let loop_block_end;
                             let cond = unpack!(
-                                loop_block_end = this.as_operand(loop_block, cond_expr));
+                                loop_block_end = this.as_local_operand(loop_block, cond_expr));
                             body_block = this.cfg.start_new_block();
                             let term = TerminatorKind::if_(this.hir.tcx(), cond,
                                                            body_block, exit_block);
@@ -206,10 +206,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     }
                     _ => false
                 };
-                let fun = unpack!(block = this.as_operand(block, fun));
+                let fun = unpack!(block = this.as_local_operand(block, fun));
                 let args: Vec<_> =
                     args.into_iter()
-                        .map(|arg| unpack!(block = this.as_operand(block, arg)))
+                        .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
                         .collect();
 
                 let success = this.cfg.start_new_block();
@@ -265,7 +265,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     _ => true,
                 });
 
-                let rvalue = unpack!(block = this.as_rvalue(block, expr));
+                let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
                 this.cfg.push_assign(block, source_info, destination, rvalue);
                 block.unit()
             }
diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs
index c577aab40db..be39dcbf6d0 100644
--- a/src/librustc_mir/build/expr/stmt.rs
+++ b/src/librustc_mir/build/expr/stmt.rs
@@ -38,14 +38,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // Generate better code for things that don't need to be
                 // dropped.
                 if this.hir.needs_drop(lhs.ty) {
-                    let rhs = unpack!(block = this.as_operand(block, rhs));
+                    let rhs = unpack!(block = this.as_local_operand(block, rhs));
                     let lhs = unpack!(block = this.as_lvalue(block, lhs));
                     unpack!(block = this.build_drop_and_replace(
                         block, lhs_span, lhs, rhs
                     ));
                     block.unit()
                 } else {
-                    let rhs = unpack!(block = this.as_rvalue(block, rhs));
+                    let rhs = unpack!(block = this.as_local_rvalue(block, rhs));
                     let lhs = unpack!(block = this.as_lvalue(block, lhs));
                     this.cfg.push_assign(block, source_info, &lhs, rhs);
                     block.unit()
@@ -64,7 +64,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let lhs_ty = lhs.ty;
 
                 // As above, RTL.
-                let rhs = unpack!(block = this.as_operand(block, rhs));
+                let rhs = unpack!(block = this.as_local_operand(block, rhs));
                 let lhs = unpack!(block = this.as_lvalue(block, lhs));
 
                 // we don't have to drop prior contents or anything
@@ -122,7 +122,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     unpack!(block = this.as_lvalue(block, output))
                 }).collect();
                 let inputs = inputs.into_iter().map(|input| {
-                    unpack!(block = this.as_operand(block, input))
+                    unpack!(block = this.as_local_operand(block, input))
                 }).collect();
                 this.cfg.push(block, Statement {
                     source_info: source_info,
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index fd39f511e6d..705eb1f5660 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -661,7 +661,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             // guard, this block is simply unreachable
             let guard = self.hir.mirror(guard);
             let source_info = self.source_info(guard.span);
-            let cond = unpack!(block = self.as_operand(block, guard));
+            let cond = unpack!(block = self.as_local_operand(block, guard));
             let otherwise = self.cfg.start_new_block();
             self.cfg.terminate(block, source_info,
                                TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise));
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index 282361fc13e..3dab1717f6b 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -253,9 +253,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             f: F)
         where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>)
     {
-        let extent = self.scopes.last().map(|scope| scope.extent).unwrap();
+        let extent = self.topmost_scope();
         let loop_scope = LoopScope {
-            extent: extent.clone(),
+            extent: extent,
             continue_block: loop_block,
             break_block: break_block,
             break_destination: break_destination,
@@ -416,6 +416,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         self.scopes[1].extent
     }
 
+    /// Returns the topmost active scope, which is known to be alive until
+    /// the next scope expression.
+    pub fn topmost_scope(&self) -> CodeExtent {
+        self.scopes.last().expect("topmost_scope: no scopes present").extent
+    }
+
     // Scheduling drops
     // ================
     /// Indicates that `lvalue` should be dropped on exit from
diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs
new file mode 100644
index 00000000000..9c924a23903
--- /dev/null
+++ b/src/test/mir-opt/basic_assignment.rs
@@ -0,0 +1,85 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// this tests move up progration, which is not yet implemented
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// check that codegen of assignment expressions is sane. Assignments
+// tend to be absent in simple code, so subtle breakage in them can
+// leave a quite hard-to-find trail of destruction.
+
+fn main() {
+    let nodrop_x = false;
+    let nodrop_y;
+
+    nodrop_y = nodrop_x;
+
+    let drop_x : Option<Box<u32>> = None;
+    let drop_y;
+
+    drop_y = drop_x;
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg.initial-after.mir
+//     bb0: {
+//         StorageLive(_1);
+//         _1 = const false;
+//         StorageLive(_2);
+//         StorageLive(_3);
+//         _3 = _1;
+//         _2 = _3;
+//         StorageDead(_3);
+//         StorageLive(_4);
+//         _4 = std::option::Option<std::boxed::Box<u32>>::None;
+//         StorageLive(_6);
+//         StorageLive(_7);
+//         _7 = _4;
+//         replace(_6 <- _7) -> [return: bb5, unwind: bb4];
+//     }
+//     bb1: {
+//         resume;
+//     }
+//     bb2: {
+//         drop(_4) -> bb1;
+//     }
+//     bb3: {
+//         drop(_6) -> bb2;
+//     }
+//     bb4: {
+//         drop(_7) -> bb3;
+//     }
+//     bb5: {
+//         drop(_7) -> [return: bb6, unwind: bb3];
+//     }
+//     bb6: {
+//         StorageDead(_7);
+//         _0 = ();
+//         drop(_6) -> [return: bb7, unwind: bb2];
+//     }
+//     bb7: {
+//         StorageDead(_6);
+//         drop(_4) -> bb8;
+//     }
+//     bb8: {
+//         StorageDead(_4);
+//         StorageDead(_2);
+//         StorageDead(_1);
+//         return;
+//     }
+// END rustc.node4.SimplifyCfg.initial-after.mir
diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs
new file mode 100644
index 00000000000..1d452907cf5
--- /dev/null
+++ b/src/test/mir-opt/issue-38669.rs
@@ -0,0 +1,52 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// check that we don't StorageDead booleans before they are used
+
+fn main() {
+    let mut should_break = false;
+    loop {
+        if should_break {
+            break;
+        }
+        should_break = true;
+    }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyCfg.initial-after.mir
+//     bb0: {
+//         StorageLive(_1);
+//         _1 = const false;
+//         goto -> bb1;
+//     }
+//
+//     bb1: {
+//         StorageLive(_4);
+//         _4 = _1;
+//         switchInt(_4) -> [0u8: bb3, otherwise: bb2];
+//     }
+//
+//     bb2: {
+//         StorageLive(_6);
+//         _0 = ();
+//         StorageDead(_4);
+//         StorageDead(_1);
+//         return;
+//     }
+//
+//     bb3: {
+//         _3 = ();
+//         StorageDead(_4);
+//         _1 = const true;
+//         _2 = ();
+//         goto -> bb1;
+//     }
+// END rustc.node4.SimplifyCfg.initial-after.mir
diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs
index c63db741e5a..3fbd1a36f2f 100644
--- a/src/test/mir-opt/storage_ranges.rs
+++ b/src/test/mir-opt/storage_ranges.rs
@@ -28,8 +28,8 @@ fn main() {
 //         StorageLive(_5);
 //         _5 = _1;
 //         _4 = std::option::Option<i32>::Some(_5,);
-//         _3 = &_4;
 //         StorageDead(_5);
+//         _3 = &_4;
 //         _2 = ();
 //         StorageDead(_3);
 //         StorageDead(_4);
@@ -38,6 +38,5 @@ fn main() {
 //         _0 = ();
 //         StorageDead(_6);
 //         StorageDead(_1);
-//         return;
-//     }
+//      }
 // END rustc.node4.TypeckMir.before.mir
diff --git a/src/test/run-pass/mir_drop_order.rs b/src/test/run-pass/mir_drop_order.rs
index 55ac5ca067b..e7da43597f1 100644
--- a/src/test/run-pass/mir_drop_order.rs
+++ b/src/test/run-pass/mir_drop_order.rs
@@ -9,23 +9,27 @@
 // except according to those terms.
 
 use std::cell::RefCell;
+use std::panic;
 
 pub struct DropLogger<'a> {
     id: usize,
-    log: &'a RefCell<Vec<usize>>
+    log: &'a panic::AssertUnwindSafe<RefCell<Vec<usize>>>
 }
 
 impl<'a> Drop for DropLogger<'a> {
     fn drop(&mut self) {
-        self.log.borrow_mut().push(self.id);
+        self.log.0.borrow_mut().push(self.id);
     }
 }
 
+struct InjectedFailure;
+
+#[allow(unreachable_code)]
 fn main() {
-    let log = RefCell::new(vec![]);
+    let log = panic::AssertUnwindSafe(RefCell::new(vec![]));
     let d = |id| DropLogger { id: id, log: &log };
     let get = || -> Vec<_> {
-        let mut m = log.borrow_mut();
+        let mut m = log.0.borrow_mut();
         let n = m.drain(..);
         n.collect()
     };
@@ -39,4 +43,13 @@ fn main() {
     // *after* the let result (tho they have the same scope
     // as far as scope-based borrowck goes).
     assert_eq!(get(), vec![0, 2, 3, 1]);
+
+    let _ = std::panic::catch_unwind(|| {
+        (d(4), &d(5), d(6), &d(7), panic!(InjectedFailure));
+    });
+
+    // here, the temporaries (5/7) live until the end of the
+    // containing statement, which is destroyed after the operands
+    // (4/6) on a panic.
+    assert_eq!(get(), vec![6, 4, 7, 5]);
 }