about summary refs log tree commit diff
path: root/src/librustc_mir
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2018-09-22 00:51:48 +0200
committerFelix S. Klock II <pnkfelix@pnkfx.org>2018-10-05 10:39:39 +0200
commit4fff54381bde70cada702981912447f948e76e27 (patch)
treefcf3358f8dac978861d7c970c6ed7a78acff19b2 /src/librustc_mir
parentb8bea5a0a6aef3966008787a25949344f8cf6942 (diff)
downloadrust-4fff54381bde70cada702981912447f948e76e27.tar.gz
rust-4fff54381bde70cada702981912447f948e76e27.zip
Add flag to `mir::LocalDecl` to track whether its a temp from some subexpr a block tail expression.
Slightly refactored the `LocalDecl` construction API in the process.
Diffstat (limited to 'src/librustc_mir')
-rw-r--r--src/librustc_mir/build/block.rs39
-rw-r--r--src/librustc_mir/build/expr/as_temp.rs32
-rw-r--r--src/librustc_mir/build/expr/into.rs22
-rw-r--r--src/librustc_mir/build/expr/stmt.rs25
-rw-r--r--src/librustc_mir/build/matches/mod.rs2
-rw-r--r--src/librustc_mir/build/mod.rs67
-rw-r--r--src/librustc_mir/shim.rs1
-rw-r--r--src/librustc_mir/transform/generator.rs3
8 files changed, 171 insertions, 20 deletions
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index c4cb7958fd3..b754d63f718 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use build::{BlockAnd, BlockAndExtension, Builder};
+use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
 use build::ForGuard::OutsideGuard;
 use build::matches::ArmHasGuard;
 use hair::*;
@@ -93,6 +93,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt);
             match kind {
                 StmtKind::Expr { scope, expr } => {
+                    this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
                     unpack!(block = this.in_opt_scope(
                         opt_destruction_scope.map(|de|(de, source_info)), block, |this| {
                             let si = (scope, source_info);
@@ -109,6 +110,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     initializer,
                     lint_level
                 } => {
+                    let ignores_expr_result = if let PatternKind::Wild = *pattern.kind {
+                        true
+                    } else {
+                        false
+                    };
+                    this.block_context.push(BlockFrame::Statement { ignores_expr_result });
+
                     // Enter the remainder scope, i.e. the bindings' destruction scope.
                     this.push_scope((remainder_scope, source_info));
                     let_scope_stack.push(remainder_scope);
@@ -155,19 +163,40 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     }
                 }
             }
+
+            let popped = this.block_context.pop();
+            assert!(popped.map_or(false, |bf|bf.is_statement()));
         }
+
         // Then, the block may have an optional trailing expression which is a “return” value
-        // of the block.
+        // of the block, which is stored into `destination`.
+        let tcx = this.hir.tcx();
+        let destination_ty = destination.ty(&this.local_decls, tcx).to_ty(tcx);
         if let Some(expr) = expr {
+            let tail_result_is_ignored = destination_ty.is_unit() ||
+                match this.block_context.last() {
+                    // no context: conservatively assume result is read
+                    None => false,
+
+                    // sub-expression: block result feeds into some computation
+                    Some(BlockFrame::SubExpr) => false,
+
+                    // otherwise: use accumualated is_ignored state.
+                    Some(BlockFrame::TailExpr { tail_result_is_ignored: ignored }) |
+                    Some(BlockFrame::Statement { ignores_expr_result: ignored }) => *ignored,
+                };
+            this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored });
+
             unpack!(block = this.into(destination, block, expr));
+            let popped = this.block_context.pop();
+
+            assert!(popped.map_or(false, |bf|bf.is_tail_expr()));
         } else {
             // If a block has no trailing expression, then it is given an implicit return type.
             // This return type is usually `()`, unless the block is diverging, in which case the
             // return type is `!`. For the unit type, we need to actually return the unit, but in
             // the case of `!`, no return value is required, as the block will never return.
-            let tcx = this.hir.tcx();
-            let ty = destination.ty(&this.local_decls, tcx).to_ty(tcx);
-            if ty.is_unit() {
+            if destination_ty.is_unit() {
                 // We only want to assign an implicit `()` as the return value of the block if the
                 // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
                 this.cfg.push_assign_unit(block, source_info, destination);
diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs
index a2dcce6adcb..e0bf02c6739 100644
--- a/src/librustc_mir/build/expr/as_temp.rs
+++ b/src/librustc_mir/build/expr/as_temp.rs
@@ -10,7 +10,7 @@
 
 //! See docs in build/expr/mod.rs
 
-use build::{BlockAnd, BlockAndExtension, Builder};
+use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
 use hair::*;
 use rustc::middle::region;
 use rustc::mir::*;
@@ -59,14 +59,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         }
 
         let expr_ty = expr.ty;
-        let temp = if mutability == Mutability::Not {
-            this.local_decls
-                .push(LocalDecl::new_immutable_temp(expr_ty, expr_span))
-        } else {
-            this.local_decls
-                .push(LocalDecl::new_temp(expr_ty, expr_span))
-        };
+        let temp = {
+            let mut local_decl = LocalDecl::new_temp(expr_ty, expr_span);
+            if mutability == Mutability::Not {
+                local_decl = local_decl.immutable();
+            }
+
+            debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
+            // Find out whether this temp is being created within the
+            // tail expression of a block whose result is ignored.
+            for bf in this.block_context.iter().rev() {
+                match bf {
+                    BlockFrame::SubExpr => continue,
+                    BlockFrame::Statement { .. } => break,
+                    &BlockFrame::TailExpr { tail_result_is_ignored } => {
+                        local_decl = local_decl.block_tail(BlockTailInfo {
+                            tail_result_is_ignored
+                        });
+                        break;
+                    }
+                }
+            }
 
+            this.local_decls.push(local_decl)
+        };
         if !expr_ty.is_never() {
             this.cfg.push(
                 block,
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index f85e37a6ca5..4f5ed34a461 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -11,7 +11,7 @@
 //! See docs in build/expr/mod.rs
 
 use build::expr::category::{Category, RvalueFunc};
-use build::{BlockAnd, BlockAndExtension, Builder};
+use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
 use hair::*;
 use rustc::mir::*;
 use rustc::ty;
@@ -39,7 +39,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let expr_span = expr.span;
         let source_info = this.source_info(expr_span);
 
-        match expr.kind {
+        let expr_is_block_or_scope = match expr.kind {
+            ExprKind::Block { .. } => true,
+            ExprKind::Scope { .. } => true,
+            _ => false,
+        };
+
+        if !expr_is_block_or_scope {
+            this.block_context.push(BlockFrame::SubExpr);
+        }
+
+        let block_and = match expr.kind {
             ExprKind::Scope {
                 region_scope,
                 lint_level,
@@ -302,6 +312,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         visibility_scope: source_info.scope,
                         internal: true,
                         is_user_variable: None,
+                        is_block_tail: None,
                     });
                     let ptr_temp = Place::Local(ptr_temp);
                     let block = unpack!(this.into(&ptr_temp, block, ptr));
@@ -414,6 +425,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     .push_assign(block, source_info, destination, rvalue);
                 block.unit()
             }
+        };
+
+        if !expr_is_block_or_scope {
+            let popped = this.block_context.pop();
+            assert!(popped.is_some());
         }
+
+        block_and
     }
 }
diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs
index 32f09599ace..8cb2b6384b8 100644
--- a/src/librustc_mir/build/expr/stmt.rs
+++ b/src/librustc_mir/build/expr/stmt.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use build::scope::BreakableScope;
-use build::{BlockAnd, BlockAndExtension, Builder};
+use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
 use hair::*;
 use rustc::mir::*;
 
@@ -40,19 +40,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // is better for borrowck interaction with overloaded
                 // operators like x[j] = x[i].
 
+                this.block_context.push(BlockFrame::SubExpr);
+
                 // 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_local_operand(block, rhs));
                     let lhs = unpack!(block = this.as_place(block, lhs));
                     unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs));
-                    block.unit()
                 } else {
                     let rhs = unpack!(block = this.as_local_rvalue(block, rhs));
                     let lhs = unpack!(block = this.as_place(block, lhs));
                     this.cfg.push_assign(block, source_info, &lhs, rhs);
-                    block.unit()
                 }
+
+                this.block_context.pop();
+                block.unit()
             }
             ExprKind::AssignOp { op, lhs, rhs } => {
                 // FIXME(#28160) there is an interesting semantics
@@ -66,6 +69,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let lhs = this.hir.mirror(lhs);
                 let lhs_ty = lhs.ty;
 
+                this.block_context.push(BlockFrame::SubExpr);
+
                 // As above, RTL.
                 let rhs = unpack!(block = this.as_local_operand(block, rhs));
                 let lhs = unpack!(block = this.as_place(block, lhs));
@@ -85,6 +90,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 );
                 this.cfg.push_assign(block, source_info, &lhs, result);
 
+                this.block_context.pop();
                 block.unit()
             }
             ExprKind::Continue { label } => {
@@ -114,7 +120,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     (break_block, region_scope, break_destination.clone())
                 };
                 if let Some(value) = value {
-                    unpack!(block = this.into(&destination, block, value))
+                    this.block_context.push(BlockFrame::SubExpr);
+                    unpack!(block = this.into(&destination, block, value));
+                    this.block_context.pop();
                 } else {
                     this.cfg.push_assign_unit(block, source_info, &destination)
                 }
@@ -123,7 +131,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
             ExprKind::Return { value } => {
                 block = match value {
-                    Some(value) => unpack!(this.into(&Place::Local(RETURN_PLACE), block, value)),
+                    Some(value) => {
+                        this.block_context.push(BlockFrame::SubExpr);
+                        let result = unpack!(this.into(&Place::Local(RETURN_PLACE), block, value));
+                        this.block_context.pop();
+                        result
+                    }
                     None => {
                         this.cfg
                             .push_assign_unit(block, source_info, &Place::Local(RETURN_PLACE));
@@ -140,6 +153,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 outputs,
                 inputs,
             } => {
+                this.block_context.push(BlockFrame::SubExpr);
                 let outputs = outputs
                     .into_iter()
                     .map(|output| unpack!(block = this.as_place(block, output)))
@@ -161,6 +175,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         },
                     },
                 );
+                this.block_context.pop();
                 block.unit()
             }
             _ => {
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index e40ed51f7d3..656c78a46ed 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -1485,6 +1485,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             source_info,
             visibility_scope,
             internal: false,
+            is_block_tail: None,
             is_user_variable: Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
                 binding_mode,
                 // hypothetically, `visit_bindings` could try to unzip
@@ -1518,6 +1519,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 visibility_scope,
                 // FIXME: should these secretly injected ref_for_guard's be marked as `internal`?
                 internal: false,
+                is_block_tail: None,
                 is_user_variable: Some(ClearCrossCrate::Set(BindingForm::RefForGuard)),
             });
             LocalsForNode::ForGuard {
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 3dbd3bbb415..9e78932bffe 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -281,6 +281,57 @@ fn liberated_closure_env_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
     tcx.liberate_late_bound_regions(closure_def_id, &closure_env_ty)
 }
 
+#[derive(Debug, PartialEq, Eq)]
+pub enum BlockFrame {
+    /// Evaluation is currently within a statement.
+    ///
+    /// Examples include:
+    ///  1. `EXPR;`
+    ///  2. `let _ = EXPR;`
+    ///  3. `let x = EXPR;`
+    Statement {
+        /// If true, then statement discards result from evaluating
+        /// the expression (such as examples 1 and 2 above).
+        ignores_expr_result: bool
+    },
+
+    /// Evaluation is currently within the tail expression of a block.
+    ///
+    /// Example: `{ STMT_1; STMT_2; EXPR }`
+    TailExpr {
+        /// If true, then the surrounding context of the block ignores
+        /// the result of evaluating the block's tail expression.
+        ///
+        /// Example: `let _ = { STMT_1; EXPR };`
+        tail_result_is_ignored: bool
+    },
+
+    /// Generic mark meaning that the block occurred as a subexpression
+    /// where the result might be used.
+    ///
+    /// Examples: `foo(EXPR)`, `match EXPR { ... }`
+    SubExpr,
+}
+
+impl BlockFrame {
+    fn is_tail_expr(&self) -> bool {
+        match *self {
+            BlockFrame::TailExpr { .. } => true,
+
+            BlockFrame::Statement { .. } |
+            BlockFrame::SubExpr => false,
+        }
+    }
+    fn is_statement(&self) -> bool {
+        match *self {
+            BlockFrame::Statement { .. } => true,
+
+            BlockFrame::TailExpr { .. } |
+            BlockFrame::SubExpr => false,
+        }
+    }
+ }
+
 struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     hir: Cx<'a, 'gcx, 'tcx>,
     cfg: CFG<'tcx>,
@@ -292,6 +343,20 @@ struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     /// see the `scope` module for more details
     scopes: Vec<scope::Scope<'tcx>>,
 
+    /// the block-context: each time we build the code within an hair::Block,
+    /// we push a frame here tracking whether we are building a statement or
+    /// if we are pushing the tail expression of the block. This is used to
+    /// embed information in generated temps about whether they were created
+    /// for a block tail expression or not.
+    ///
+    /// It would be great if we could fold this into `self.scopes`
+    /// somehow; but right now I think that is very tightly tied to
+    /// the code generation in ways that we cannot (or should not)
+    /// start just throwing new entries onto that vector in order to
+    /// distinguish the context of EXPR1 from the context of EXPR2 in
+    /// `{ STMTS; EXPR1 } + EXPR2`
+    block_context: Vec<BlockFrame>,
+
     /// The current unsafe block in scope, even if it is hidden by
     /// a PushUnsafeBlock
     unpushed_unsafe: Safety,
@@ -695,6 +760,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             fn_span: span,
             arg_count,
             scopes: vec![],
+            block_context: vec![],
             source_scopes: IndexVec::new(),
             source_scope: OUTERMOST_SOURCE_SCOPE,
             source_scope_local_data: IndexVec::new(),
@@ -781,6 +847,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 name,
                 internal: false,
                 is_user_variable: None,
+                is_block_tail: None,
             });
         }
 
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 6c3dd0ea3cc..4c9d29bb6d0 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -147,6 +147,7 @@ fn temp_decl(mutability: Mutability, ty: Ty, span: Span) -> LocalDecl {
         visibility_scope: source_info.scope,
         internal: false,
         is_user_variable: None,
+        is_block_tail: None,
     }
 }
 
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 62adbf1bdf7..c2ae6832cc0 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -308,6 +308,7 @@ fn replace_result_variable<'tcx>(
         source_info,
         visibility_scope: source_info.scope,
         internal: false,
+        is_block_tail: None,
         is_user_variable: None,
     };
     let new_ret_local = Local::new(mir.local_decls.len());
@@ -662,6 +663,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
         source_info,
         visibility_scope: source_info.scope,
         internal: false,
+        is_block_tail: None,
         is_user_variable: None,
     };
 
@@ -679,6 +681,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
         source_info,
         visibility_scope: source_info.scope,
         internal: false,
+        is_block_tail: None,
         is_user_variable: None,
     };