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:
parent
b8bea5a0a6
commit
4fff54381b
|
@ -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 });
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
_ => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue