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.
This commit is contained in:
Felix S. Klock II 2018-09-22 00:51:48 +02:00
parent b8bea5a0a6
commit 4fff54381b
11 changed files with 207 additions and 23 deletions

View File

@ -29,6 +29,7 @@ impl_stable_hash_for!(struct mir::LocalDecl<'tcx> {
source_info,
visibility_scope,
internal,
is_block_tail,
is_user_variable
});
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, var_hir_id, by_ref, mutability });

View File

@ -638,6 +638,18 @@ mod binding_form_impl {
}
}
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct BlockTailInfo {
/// If `true`, then the value resulting from evaluating this tail
/// expression is ignored by the block's expression context.
///
/// Examples include `{ ...; tail };` and `let _ = { ...; tail };`
/// but not e.g. `let _x = { ...; tail };`
pub tail_result_is_ignored: bool,
}
impl_stable_hash_for!(struct BlockTailInfo { tail_result_is_ignored });
/// A MIR local.
///
/// This can be a binding declared by the user, a temporary inserted by the compiler, a function
@ -677,6 +689,12 @@ pub struct LocalDecl<'tcx> {
/// generator.
pub internal: bool,
/// If this local is a temporary and `is_block_tail` is `Some`,
/// then it is a temporary created for evaluation of some
/// subexpression of some block's tail expression (with no
/// intervening statement context).
pub is_block_tail: Option<BlockTailInfo>,
/// Type of this local.
pub ty: Ty<'tcx>,
@ -825,10 +843,19 @@ impl<'tcx> LocalDecl<'tcx> {
Self::new_local(ty, Mutability::Mut, false, span)
}
/// Create a new immutable `LocalDecl` for a temporary.
/// Converts `self` into same `LocalDecl` except tagged as immutable.
#[inline]
pub fn new_immutable_temp(ty: Ty<'tcx>, span: Span) -> Self {
Self::new_local(ty, Mutability::Not, false, span)
pub fn immutable(mut self) -> Self {
self.mutability = Mutability::Not;
self
}
/// Converts `self` into same `LocalDecl` except tagged as internal temporary.
#[inline]
pub fn block_tail(mut self, info: BlockTailInfo) -> Self {
assert!(self.is_block_tail.is_none());
self.is_block_tail = Some(info);
self
}
/// Create a new `LocalDecl` for a internal temporary.
@ -856,6 +883,7 @@ impl<'tcx> LocalDecl<'tcx> {
visibility_scope: OUTERMOST_SOURCE_SCOPE,
internal,
is_user_variable: None,
is_block_tail: None,
}
}
@ -874,6 +902,7 @@ impl<'tcx> LocalDecl<'tcx> {
},
visibility_scope: OUTERMOST_SOURCE_SCOPE,
internal: false,
is_block_tail: None,
name: None, // FIXME maybe we do want some name here?
is_user_variable: None,
}
@ -2668,6 +2697,7 @@ pub enum ClosureOutlivesSubject<'tcx> {
*/
CloneTypeFoldableAndLiftImpls! {
BlockTailInfo,
Mutability,
SourceInfo,
UpvarDecl,
@ -2711,6 +2741,7 @@ BraceStructTypeFoldableImpl! {
user_ty,
name,
source_info,
is_block_tail,
visibility_scope,
}
}

View File

@ -728,6 +728,7 @@ macro_rules! make_mir_visitor {
ref $($mutability)* visibility_scope,
internal: _,
is_user_variable: _,
is_block_tail: _,
} = *local_decl;
self.visit_ty(ty, TyContext::LocalDecl {

View File

@ -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);

View File

@ -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,

View File

@ -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
}
}

View File

@ -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()
}
_ => {

View File

@ -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 {

View File

@ -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,
});
}

View File

@ -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,
}
}

View File

@ -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,
};