diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index f66be6dc54d37..0090605f46149 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -252,11 +252,6 @@ impl<'tcx> Mir<'tcx> { } else if self.local_decls[local].name.is_some() { LocalKind::Var } else { - debug_assert!( - self.local_decls[local].mutability == Mutability::Mut, - "temp should be mutable" - ); - LocalKind::Temp } } @@ -784,25 +779,30 @@ impl<'tcx> LocalDecl<'tcx> { /// Create a new `LocalDecl` for a temporary. #[inline] pub fn new_temp(ty: Ty<'tcx>, span: Span) -> Self { - LocalDecl { - mutability: Mutability::Mut, - ty, - name: None, - source_info: SourceInfo { - span, - scope: OUTERMOST_SOURCE_SCOPE, - }, - visibility_scope: OUTERMOST_SOURCE_SCOPE, - internal: false, - is_user_variable: None, - } + Self::new_local(ty, Mutability::Mut, false, span) + } + + /// Create a new immutable `LocalDecl` for a temporary. + #[inline] + pub fn new_immutable_temp(ty: Ty<'tcx>, span: Span) -> Self { + Self::new_local(ty, Mutability::Not, false, span) } /// Create a new `LocalDecl` for a internal temporary. #[inline] pub fn new_internal(ty: Ty<'tcx>, span: Span) -> Self { + Self::new_local(ty, Mutability::Mut, true, span) + } + + #[inline] + fn new_local( + ty: Ty<'tcx>, + mutability: Mutability, + internal: bool, + span: Span, + ) -> Self { LocalDecl { - mutability: Mutability::Mut, + mutability, ty, name: None, source_info: SourceInfo { @@ -810,7 +810,7 @@ impl<'tcx> LocalDecl<'tcx> { scope: OUTERMOST_SOURCE_SCOPE, }, visibility_scope: OUTERMOST_SOURCE_SCOPE, - internal: true, + internal, is_user_variable: None, } } diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs index 454f89e5d06fc..8ddcfa054321e 100644 --- a/src/librustc_mir/borrow_check/borrow_set.rs +++ b/src/librustc_mir/borrow_check/borrow_set.rs @@ -10,12 +10,14 @@ use borrow_check::place_ext::PlaceExt; use dataflow::indexes::BorrowIndex; +use dataflow::move_paths::MoveData; use rustc::mir::traversal; use rustc::mir::visit::{PlaceContext, Visitor}; -use rustc::mir::{self, Location, Mir, Place}; +use rustc::mir::{self, Location, Mir, Place, Local}; use rustc::ty::{Region, TyCtxt}; use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::bitvec::BitArray; use std::fmt; use std::hash::Hash; use std::ops::Index; @@ -43,6 +45,8 @@ crate struct BorrowSet<'tcx> { /// Map from local to all the borrows on that local crate local_map: FxHashMap>, + + crate locals_state_at_exit: LocalsStateAtExit, } impl<'tcx> Index for BorrowSet<'tcx> { @@ -96,8 +100,52 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { } } +crate enum LocalsStateAtExit { + AllAreInvalidated, + SomeAreInvalidated { has_storage_dead_or_moved: BitArray } +} + +impl LocalsStateAtExit { + fn build( + locals_are_invalidated_at_exit: bool, + mir: &Mir<'tcx>, + move_data: &MoveData<'tcx> + ) -> Self { + struct HasStorageDead(BitArray); + + impl<'tcx> Visitor<'tcx> for HasStorageDead { + fn visit_local(&mut self, local: &Local, ctx: PlaceContext<'tcx>, _: Location) { + if ctx == PlaceContext::StorageDead { + self.0.insert(*local); + } + } + } + + if locals_are_invalidated_at_exit { + LocalsStateAtExit::AllAreInvalidated + } else { + let mut has_storage_dead = HasStorageDead(BitArray::new(mir.local_decls.len())); + has_storage_dead.visit_mir(mir); + let mut has_storage_dead_or_moved = has_storage_dead.0; + for move_out in &move_data.moves { + if let Some(index) = move_data.base_local(move_out.path) { + has_storage_dead_or_moved.insert(index); + + } + } + LocalsStateAtExit::SomeAreInvalidated{ has_storage_dead_or_moved } + } + } +} + impl<'tcx> BorrowSet<'tcx> { - pub fn build(tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> Self { + pub fn build( + tcx: TyCtxt<'_, '_, 'tcx>, + mir: &Mir<'tcx>, + locals_are_invalidated_at_exit: bool, + move_data: &MoveData<'tcx> + ) -> Self { + let mut visitor = GatherBorrows { tcx, mir, @@ -107,6 +155,8 @@ impl<'tcx> BorrowSet<'tcx> { region_map: FxHashMap(), local_map: FxHashMap(), pending_activations: FxHashMap(), + locals_state_at_exit: + LocalsStateAtExit::build(locals_are_invalidated_at_exit, mir, move_data), }; for (block, block_data) in traversal::preorder(mir) { @@ -119,6 +169,7 @@ impl<'tcx> BorrowSet<'tcx> { activation_map: visitor.activation_map, region_map: visitor.region_map, local_map: visitor.local_map, + locals_state_at_exit: visitor.locals_state_at_exit, } } @@ -148,6 +199,8 @@ struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> { /// the borrow. When we find a later use of this activation, we /// remove from the map (and add to the "tombstone" set below). pending_activations: FxHashMap, + + locals_state_at_exit: LocalsStateAtExit, } impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { @@ -159,7 +212,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { location: mir::Location, ) { if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue { - if borrowed_place.ignore_borrow(self.tcx, self.mir) { + if borrowed_place.ignore_borrow( + self.tcx, self.mir, &self.locals_state_at_exit) { return; } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 3536947b25ebf..d6688a008139b 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -196,7 +196,12 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( |bd, i| DebugFormatted::new(&bd.move_data().inits[i]), )); - let borrow_set = Rc::new(BorrowSet::build(tcx, mir)); + let locals_are_invalidated_at_exit = match tcx.hir.body_owner_kind(id) { + hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false, + hir::BodyOwnerKind::Fn => true, + }; + let borrow_set = Rc::new(BorrowSet::build( + tcx, mir, locals_are_invalidated_at_exit, &mdpe.move_data)); // If we are in non-lexical mode, compute the non-lexical lifetimes. let (regioncx, polonius_output, opt_closure_req) = nll::compute_regions( @@ -241,10 +246,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( param_env: param_env, location_table, movable_generator, - locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) { - hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false, - hir::BodyOwnerKind::Fn => true, - }, + locals_are_invalidated_at_exit, access_place_error_reported: FxHashSet(), reservation_error_reported: FxHashSet(), moved_error_reported: FxHashSet(), diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs index 2b4a1553e1a89..740cc64598438 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/src/librustc_mir/borrow_check/place_ext.rs @@ -10,8 +10,9 @@ use rustc::hir; use rustc::mir::ProjectionElem; -use rustc::mir::{Local, Mir, Place}; +use rustc::mir::{Local, Mir, Place, Mutability}; use rustc::ty::{self, TyCtxt}; +use borrow_check::borrow_set::LocalsStateAtExit; /// Extension methods for the `Place` type. crate trait PlaceExt<'tcx> { @@ -19,7 +20,12 @@ crate trait PlaceExt<'tcx> { /// This is true whenever there is no action that the user can do /// to the place `self` that would invalidate the borrow. This is true /// for borrows of raw pointer dereferents as well as shared references. - fn ignore_borrow(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool; + fn ignore_borrow( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + mir: &Mir<'tcx>, + locals_state_at_exit: &LocalsStateAtExit, + ) -> bool; /// If this is a place like `x.f.g`, returns the local /// `x`. Returns `None` if this is based in a static. @@ -27,10 +33,34 @@ crate trait PlaceExt<'tcx> { } impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { - fn ignore_borrow(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool { + fn ignore_borrow( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + mir: &Mir<'tcx>, + locals_state_at_exit: &LocalsStateAtExit, + ) -> bool { match self { - Place::Promoted(_) | - Place::Local(_) => false, + Place::Promoted(_) => false, + + // If a local variable is immutable, then we only need to track borrows to guard + // against two kinds of errors: + // * The variable being dropped while still borrowed (e.g., because the fn returns + // a reference to a local variable) + // * The variable being moved while still borrowed + // + // In particular, the variable cannot be mutated -- the "access checks" will fail -- + // so we don't have to worry about mutation while borrowed. + Place::Local(index) => { + match locals_state_at_exit { + LocalsStateAtExit::AllAreInvalidated => false, + LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => { + let ignore = !has_storage_dead_or_moved.contains(*index) && + mir.local_decls[*index].mutability == Mutability::Not; + debug!("ignore_borrow: local {:?} => {:?}", index, ignore); + ignore + } + } + } Place::Static(static_) => { tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable) } @@ -39,7 +69,8 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Index(_) => proj.base.ignore_borrow(tcx, mir), + | ProjectionElem::Index(_) => proj.base.ignore_borrow( + tcx, mir, locals_state_at_exit), ProjectionElem::Deref => { let ty = proj.base.ty(mir, tcx).to_ty(tcx); @@ -55,7 +86,7 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { // borrowed *that* one, leaving the original // path unborrowed. ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => true, - _ => proj.base.ignore_borrow(tcx, mir), + _ => proj.base.ignore_borrow(tcx, mir, locals_state_at_exit), } } }, diff --git a/src/librustc_mir/build/expr/as_constant.rs b/src/librustc_mir/build/expr/as_constant.rs index 1106f750d6d24..606bd2978b642 100644 --- a/src/librustc_mir/build/expr/as_constant.rs +++ b/src/librustc_mir/build/expr/as_constant.rs @@ -18,7 +18,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Compile `expr`, yielding a compile-time constant. Assumes that /// `expr` is a valid compile-time constant! pub fn as_constant(&mut self, expr: M) -> Constant<'tcx> - where M: Mirror<'tcx, Output=Expr<'tcx>> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, { let expr = self.hir.mirror(expr); self.expr_as_constant(expr) @@ -26,18 +27,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { fn expr_as_constant(&mut self, expr: Expr<'tcx>) -> Constant<'tcx> { let this = self; - let Expr { ty, temp_lifetime: _, span, kind } - = expr; + let Expr { + ty, + temp_lifetime: _, + span, + kind, + } = expr; match kind { - ExprKind::Scope { region_scope: _, lint_level: _, value } => - this.as_constant(value), - ExprKind::Literal { literal, user_ty } => - Constant { span, ty, user_ty, literal }, - _ => - span_bug!( - span, - "expression is not a valid constant {:?}", - kind), + ExprKind::Scope { + region_scope: _, + lint_level: _, + value, + } => this.as_constant(value), + ExprKind::Literal { literal, user_ty } => Constant { + span, + ty, + user_ty, + literal, + }, + _ => span_bug!(span, "expression is not a valid constant {:?}", kind), } } } diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs index 7eae414a39137..8046d898e0a88 100644 --- a/src/librustc_mir/build/expr/as_operand.rs +++ b/src/librustc_mir/build/expr/as_operand.rs @@ -10,8 +10,8 @@ //! See docs in build/expr/mod.rs -use build::{BlockAnd, BlockAndExtension, Builder}; use build::expr::category::Category; +use build::{BlockAnd, BlockAndExtension, Builder}; use hair::*; use rustc::middle::region; use rustc::mir::*; @@ -23,9 +23,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// 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(&mut self, block: BasicBlock, expr: M) - -> BlockAnd> - where M: Mirror<'tcx, Output = Expr<'tcx>> + pub fn as_local_operand(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, { let local_scope = self.local_scope(); self.as_operand(block, local_scope, expr) @@ -37,25 +37,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// this time. /// /// The operand is known to be live until the end of `scope`. - pub fn as_operand(&mut self, - block: BasicBlock, - scope: Option, - expr: M) -> BlockAnd> - where M: Mirror<'tcx, Output = Expr<'tcx>> + pub fn as_operand( + &mut self, + block: BasicBlock, + scope: Option, + expr: M, + ) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, { let expr = self.hir.mirror(expr); self.expr_as_operand(block, scope, expr) } - fn expr_as_operand(&mut self, - mut block: BasicBlock, - scope: Option, - expr: Expr<'tcx>) - -> BlockAnd> { + fn expr_as_operand( + &mut self, + mut block: BasicBlock, + scope: Option, + expr: Expr<'tcx>, + ) -> BlockAnd> { debug!("expr_as_operand(block={:?}, expr={:?})", block, expr); let this = self; - if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind { + if let ExprKind::Scope { + region_scope, + lint_level, + value, + } = expr.kind + { let source_info = this.source_info(expr.span); let region_scope = (region_scope, source_info); return this.in_scope(region_scope, lint_level, block, |this| { @@ -64,16 +73,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } let category = Category::of(&expr.kind).unwrap(); - debug!("expr_as_operand: category={:?} for={:?}", category, expr.kind); + debug!( + "expr_as_operand: category={:?} for={:?}", + category, expr.kind + ); match category { Category::Constant => { let constant = this.as_constant(expr); block.and(Operand::Constant(box constant)) } - Category::Place | - Category::Rvalue(..) => { - let operand = - unpack!(block = this.as_temp(block, scope, expr)); + Category::Place | Category::Rvalue(..) => { + let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut)); block.and(Operand::Move(Place::Local(operand))) } } diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs index a38ca7ae5bf64..5688ea9d260f5 100644 --- a/src/librustc_mir/build/expr/as_place.rs +++ b/src/librustc_mir/build/expr/as_place.rs @@ -10,42 +10,64 @@ //! See docs in build/expr/mod.rs -use build::{BlockAnd, BlockAndExtension, Builder}; -use build::ForGuard::{OutsideGuard, RefWithinGuard}; use build::expr::category::Category; +use build::ForGuard::{OutsideGuard, RefWithinGuard}; +use build::{BlockAnd, BlockAndExtension, Builder}; use hair::*; -use rustc::mir::*; use rustc::mir::interpret::EvalErrorKind::BoundsCheck; +use rustc::mir::*; use rustc_data_structures::indexed_vec::Idx; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Compile `expr`, yielding a place that we can move from etc. - pub fn as_place(&mut self, - block: BasicBlock, - expr: M) - -> BlockAnd> - where M: Mirror<'tcx, Output=Expr<'tcx>> + pub fn as_place(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, { let expr = self.hir.mirror(expr); - self.expr_as_place(block, expr) + self.expr_as_place(block, expr, Mutability::Mut) } - fn expr_as_place(&mut self, - mut block: BasicBlock, - expr: Expr<'tcx>) - -> BlockAnd> { - debug!("expr_as_place(block={:?}, expr={:?})", block, expr); + /// Compile `expr`, yielding a place that we can move from etc. + /// Mutability note: The caller of this method promises only to read from the resulting + /// place. The place itself may or may not be mutable: + /// * If this expr is a place expr like a.b, then we will return that place. + /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary. + pub fn as_read_only_place(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, + { + let expr = self.hir.mirror(expr); + self.expr_as_place(block, expr, Mutability::Not) + } + + fn expr_as_place( + &mut self, + mut block: BasicBlock, + expr: Expr<'tcx>, + mutability: Mutability, + ) -> BlockAnd> { + debug!( + "expr_as_place(block={:?}, expr={:?}, mutability={:?})", + block, expr, mutability + ); let this = self; let expr_span = expr.span; let source_info = this.source_info(expr_span); match expr.kind { - ExprKind::Scope { region_scope, lint_level, value } => { - this.in_scope((region_scope, source_info), lint_level, block, |this| { + ExprKind::Scope { + region_scope, + lint_level, + value, + } => this.in_scope((region_scope, source_info), lint_level, block, |this| { + if mutability == Mutability::Not { + this.as_read_only_place(block, value) + } else { this.as_place(block, value) - }) - } + } + }), ExprKind::Field { lhs, name } => { let place = unpack!(block = this.as_place(block, lhs)); let place = place.field(name, expr.ty); @@ -63,32 +85,43 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // region_scope=None so place 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_temp(block, None, index)); + let idx = unpack!(block = this.as_temp(block, None, index, Mutability::Mut)); // bounds check: - let (len, lt) = (this.temp(usize_ty.clone(), expr_span), - this.temp(bool_ty, expr_span)); - this.cfg.push_assign(block, source_info, // len = len(slice) - &len, Rvalue::Len(slice.clone())); - this.cfg.push_assign(block, source_info, // lt = idx < len - <, Rvalue::BinaryOp(BinOp::Lt, - Operand::Copy(Place::Local(idx)), - Operand::Copy(len.clone()))); + let (len, lt) = ( + this.temp(usize_ty.clone(), expr_span), + this.temp(bool_ty, expr_span), + ); + this.cfg.push_assign( + block, + source_info, // len = len(slice) + &len, + Rvalue::Len(slice.clone()), + ); + this.cfg.push_assign( + block, + source_info, // lt = idx < len + <, + Rvalue::BinaryOp( + BinOp::Lt, + Operand::Copy(Place::Local(idx)), + Operand::Copy(len.clone()), + ), + ); let msg = BoundsCheck { len: Operand::Move(len), - index: Operand::Copy(Place::Local(idx)) + index: Operand::Copy(Place::Local(idx)), }; - let success = this.assert(block, Operand::Move(lt), true, - msg, expr_span); + let success = this.assert(block, Operand::Move(lt), true, msg, expr_span); success.and(slice.index(idx)) } - ExprKind::SelfRef => { - block.and(Place::Local(Local::new(1))) - } + ExprKind::SelfRef => block.and(Place::Local(Local::new(1))), ExprKind::VarRef { id } => { - let place = if this.is_bound_var_in_guard(id) && - this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards() + let place = if this.is_bound_var_in_guard(id) && this + .hir + .tcx() + .all_pat_vars_are_implicit_refs_within_guards() { let index = this.var_local_id(id, RefWithinGuard); Place::Local(index).deref() @@ -98,46 +131,48 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }; block.and(place) } - ExprKind::StaticRef { id } => { - block.and(Place::Static(Box::new(Static { def_id: id, ty: expr.ty }))) - } + ExprKind::StaticRef { id } => block.and(Place::Static(Box::new(Static { + def_id: id, + ty: expr.ty, + }))), - ExprKind::Array { .. } | - ExprKind::Tuple { .. } | - ExprKind::Adt { .. } | - ExprKind::Closure { .. } | - ExprKind::Unary { .. } | - ExprKind::Binary { .. } | - ExprKind::LogicalOp { .. } | - ExprKind::Box { .. } | - ExprKind::Cast { .. } | - ExprKind::Use { .. } | - ExprKind::NeverToAny { .. } | - ExprKind::ReifyFnPointer { .. } | - ExprKind::ClosureFnPointer { .. } | - ExprKind::UnsafeFnPointer { .. } | - ExprKind::Unsize { .. } | - ExprKind::Repeat { .. } | - ExprKind::Borrow { .. } | - ExprKind::If { .. } | - ExprKind::Match { .. } | - ExprKind::Loop { .. } | - ExprKind::Block { .. } | - ExprKind::Assign { .. } | - ExprKind::AssignOp { .. } | - ExprKind::Break { .. } | - ExprKind::Continue { .. } | - ExprKind::Return { .. } | - ExprKind::Literal { .. } | - ExprKind::InlineAsm { .. } | - ExprKind::Yield { .. } | - ExprKind::Call { .. } => { + ExprKind::Array { .. } + | ExprKind::Tuple { .. } + | ExprKind::Adt { .. } + | ExprKind::Closure { .. } + | ExprKind::Unary { .. } + | ExprKind::Binary { .. } + | ExprKind::LogicalOp { .. } + | ExprKind::Box { .. } + | ExprKind::Cast { .. } + | ExprKind::Use { .. } + | ExprKind::NeverToAny { .. } + | ExprKind::ReifyFnPointer { .. } + | ExprKind::ClosureFnPointer { .. } + | ExprKind::UnsafeFnPointer { .. } + | ExprKind::Unsize { .. } + | ExprKind::Repeat { .. } + | ExprKind::Borrow { .. } + | ExprKind::If { .. } + | ExprKind::Match { .. } + | ExprKind::Loop { .. } + | ExprKind::Block { .. } + | ExprKind::Assign { .. } + | ExprKind::AssignOp { .. } + | ExprKind::Break { .. } + | ExprKind::Continue { .. } + | ExprKind::Return { .. } + | ExprKind::Literal { .. } + | ExprKind::InlineAsm { .. } + | ExprKind::Yield { .. } + | ExprKind::Call { .. } => { // these are not places, so we need to make a temporary. debug_assert!(match Category::of(&expr.kind) { Some(Category::Place) => false, _ => true, }); - let temp = unpack!(block = this.as_temp(block, expr.temp_lifetime, expr)); + let temp = + unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability)); block.and(Place::Local(temp)) } } diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index b90a58f2a7ec0..b721120f74dbd 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -13,64 +13,84 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::Idx; -use build::{BlockAnd, BlockAndExtension, Builder}; use build::expr::category::{Category, RvalueFunc}; +use build::{BlockAnd, BlockAndExtension, Builder}; use hair::*; use rustc::middle::region; -use rustc::ty::{self, Ty, UpvarSubsts}; -use rustc::mir::*; use rustc::mir::interpret::EvalErrorKind; +use rustc::mir::*; +use rustc::ty::{self, Ty, UpvarSubsts}; use syntax_pos::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// See comment on `as_local_operand` - pub fn as_local_rvalue(&mut self, block: BasicBlock, expr: M) - -> BlockAnd> - where M: Mirror<'tcx, Output = Expr<'tcx>> + pub fn as_local_rvalue(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, { let local_scope = self.local_scope(); self.as_rvalue(block, local_scope, expr) } /// Compile `expr`, yielding an rvalue. - pub fn as_rvalue(&mut self, block: BasicBlock, scope: Option, expr: M) - -> BlockAnd> - where M: Mirror<'tcx, Output = Expr<'tcx>> + pub fn as_rvalue( + &mut self, + block: BasicBlock, + scope: Option, + expr: M, + ) -> BlockAnd> + where + M: Mirror<'tcx, Output = Expr<'tcx>>, { let expr = self.hir.mirror(expr); self.expr_as_rvalue(block, scope, expr) } - fn expr_as_rvalue(&mut self, - mut block: BasicBlock, - scope: Option, - expr: Expr<'tcx>) - -> BlockAnd> { - debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr); + fn expr_as_rvalue( + &mut self, + mut block: BasicBlock, + scope: Option, + expr: Expr<'tcx>, + ) -> BlockAnd> { + debug!( + "expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", + block, scope, expr + ); let this = self; let expr_span = expr.span; let source_info = this.source_info(expr_span); match expr.kind { - ExprKind::Scope { region_scope, lint_level, value } => { + ExprKind::Scope { + region_scope, + lint_level, + value, + } => { let region_scope = (region_scope, source_info); - this.in_scope(region_scope, lint_level, block, - |this| this.as_rvalue(block, scope, value)) + this.in_scope(region_scope, lint_level, block, |this| { + this.as_rvalue(block, scope, value) + }) } ExprKind::Repeat { value, count } => { let value_operand = unpack!(block = this.as_operand(block, scope, value)); block.and(Rvalue::Repeat(value_operand, count)) } - ExprKind::Borrow { region, borrow_kind, arg } => { - let arg_place = unpack!(block = this.as_place(block, arg)); + ExprKind::Borrow { + region, + borrow_kind, + arg, + } => { + let arg_place = match borrow_kind { + BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)), + _ => unpack!(block = this.as_place(block, arg)), + }; block.and(Rvalue::Ref(region, borrow_kind, arg_place)) } ExprKind::Binary { op, lhs, 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) + this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs) } ExprKind::Unary { op, arg } => { let arg = unpack!(block = this.as_operand(block, scope, arg)); @@ -81,11 +101,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let minval = this.minval_literal(expr_span, expr.ty); let is_min = this.temp(bool_ty, expr_span); - this.cfg.push_assign(block, source_info, &is_min, - Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval)); + this.cfg.push_assign( + block, + source_info, + &is_min, + Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval), + ); - block = this.assert(block, Operand::Move(is_min), false, - EvalErrorKind::OverflowNeg, expr_span); + block = this.assert( + block, + Operand::Move(is_min), + false, + EvalErrorKind::OverflowNeg, + expr_span, + ); } block.and(Rvalue::UnaryOp(op, arg)) } @@ -94,22 +123,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // The `Box` temporary created here is not a part of the HIR, // and therefore is not considered during generator OIBIT // determination. See the comment about `box` at `yield_in_scope`. - let result = this.local_decls.push( - LocalDecl::new_internal(expr.ty, expr_span)); - this.cfg.push(block, Statement { - source_info, - kind: StatementKind::StorageLive(result) - }); + let result = this + .local_decls + .push(LocalDecl::new_internal(expr.ty, expr_span)); + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::StorageLive(result), + }, + ); if let Some(scope) = scope { // schedule a shallow free of that memory, lest we unwind: this.schedule_drop_storage_and_value( - expr_span, scope, &Place::Local(result), value.ty, + expr_span, + scope, + &Place::Local(result), + value.ty, ); } // malloc some memory of suitable type (thus far, uninitialized): let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty); - this.cfg.push_assign(block, source_info, &Place::Local(result), box_); + this.cfg + .push_assign(block, source_info, &Place::Local(result), box_); // initialize the box contents: unpack!(block = this.into(&Place::Local(result).deref(), block, value)); @@ -170,23 +207,29 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // first process the set of fields let el_ty = expr.ty.sequence_element_type(this.hir.tcx()); - let fields: Vec<_> = - fields.into_iter() - .map(|f| unpack!(block = this.as_operand(block, scope, f))) - .collect(); + let fields: Vec<_> = fields + .into_iter() + .map(|f| unpack!(block = this.as_operand(block, scope, f))) + .collect(); block.and(Rvalue::Aggregate(box AggregateKind::Array(el_ty), fields)) } - ExprKind::Tuple { fields } => { // see (*) above + ExprKind::Tuple { fields } => { + // see (*) above // first process the set of fields - let fields: Vec<_> = - fields.into_iter() - .map(|f| unpack!(block = this.as_operand(block, scope, f))) - .collect(); + let fields: Vec<_> = fields + .into_iter() + .map(|f| unpack!(block = this.as_operand(block, scope, f))) + .collect(); block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields)) } - ExprKind::Closure { closure_id, substs, upvars, movability } => { + ExprKind::Closure { + closure_id, + substs, + upvars, + movability, + } => { // see (*) above let mut operands: Vec<_> = upvars .into_iter() @@ -212,25 +255,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // that caused the capture will cause an error. match upvar.kind { ExprKind::Borrow { - borrow_kind: BorrowKind::Mut { - allow_two_phase_borrow: false - }, + borrow_kind: + BorrowKind::Mut { + allow_two_phase_borrow: false, + }, region, arg, - } => unpack!(block = this.limit_capture_mutability( - upvar.span, - upvar.ty, - scope, - block, - arg, - region, - )), + } => unpack!( + block = this.limit_capture_mutability( + upvar.span, upvar.ty, scope, block, arg, region, + ) + ), _ => unpack!(block = this.as_operand(block, scope, upvar)), } } } - }) - .collect(); + }).collect(); let result = match substs { UpvarSubsts::Generator(substs) => { let movability = movability.unwrap(); @@ -248,23 +288,36 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { })); box AggregateKind::Generator(closure_id, substs, movability) } - UpvarSubsts::Closure(substs) => { - box AggregateKind::Closure(closure_id, substs) - } + UpvarSubsts::Closure(substs) => box AggregateKind::Closure(closure_id, substs), }; block.and(Rvalue::Aggregate(result, operands)) } ExprKind::Adt { - adt_def, variant_index, substs, user_ty, fields, base - } => { // see (*) above + adt_def, + variant_index, + substs, + user_ty, + fields, + base, + } => { + // see (*) above let is_union = adt_def.is_union(); - let active_field_index = if is_union { Some(fields[0].name.index()) } else { None }; + let active_field_index = if is_union { + Some(fields[0].name.index()) + } else { + None + }; // 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, scope, 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); @@ -274,15 +327,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // MIR does not natively support FRU, so for each // base-supplied field, generate an operand that // reads it from the base. - field_names.into_iter() + field_names + .into_iter() .zip(field_types.into_iter()) .map(|(n, ty)| match fields_map.get(&n) { Some(v) => v.clone(), - None => this.consume_by_copy_or_move(base.clone().field(n, ty)) - }) - .collect() + None => this.consume_by_copy_or_move(base.clone().field(n, ty)), + }).collect() } else { - field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect() + field_names + .iter() + .filter_map(|n| fields_map.get(n).cloned()) + .collect() }; let adt = box AggregateKind::Adt( @@ -294,8 +350,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ); block.and(Rvalue::Aggregate(adt, fields)) } - ExprKind::Assign { .. } | - ExprKind::AssignOp { .. } => { + ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { block = unpack!(this.stmt_expr(block, expr)); block.and(this.unit_rvalue()) } @@ -303,31 +358,35 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let value = unpack!(block = this.as_operand(block, scope, value)); let resume = this.cfg.start_new_block(); let cleanup = this.generator_drop_cleanup(); - this.cfg.terminate(block, source_info, TerminatorKind::Yield { - value: value, - resume: resume, - drop: cleanup, - }); + this.cfg.terminate( + block, + source_info, + TerminatorKind::Yield { + value: value, + resume: resume, + drop: cleanup, + }, + ); resume.and(this.unit_rvalue()) } - ExprKind::Literal { .. } | - ExprKind::Block { .. } | - ExprKind::Match { .. } | - ExprKind::If { .. } | - ExprKind::NeverToAny { .. } | - ExprKind::Loop { .. } | - ExprKind::LogicalOp { .. } | - ExprKind::Call { .. } | - ExprKind::Field { .. } | - ExprKind::Deref { .. } | - ExprKind::Index { .. } | - ExprKind::VarRef { .. } | - ExprKind::SelfRef | - ExprKind::Break { .. } | - ExprKind::Continue { .. } | - ExprKind::Return { .. } | - ExprKind::InlineAsm { .. } | - ExprKind::StaticRef { .. } => { + ExprKind::Literal { .. } + | ExprKind::Block { .. } + | ExprKind::Match { .. } + | ExprKind::If { .. } + | ExprKind::NeverToAny { .. } + | ExprKind::Loop { .. } + | ExprKind::LogicalOp { .. } + | ExprKind::Call { .. } + | ExprKind::Field { .. } + | ExprKind::Deref { .. } + | ExprKind::Index { .. } + | ExprKind::VarRef { .. } + | ExprKind::SelfRef + | ExprKind::Break { .. } + | ExprKind::Continue { .. } + | ExprKind::Return { .. } + | ExprKind::InlineAsm { .. } + | ExprKind::StaticRef { .. } => { // these do not have corresponding `Rvalue` variants, // so make an operand and then return that debug_assert!(match Category::of(&expr.kind) { @@ -340,19 +399,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - pub fn build_binary_op(&mut self, mut block: BasicBlock, - op: BinOp, span: Span, ty: Ty<'tcx>, - lhs: Operand<'tcx>, rhs: Operand<'tcx>) -> BlockAnd> { + pub fn build_binary_op( + &mut self, + mut block: BasicBlock, + op: BinOp, + span: Span, + ty: Ty<'tcx>, + lhs: Operand<'tcx>, + rhs: Operand<'tcx>, + ) -> BlockAnd> { let source_info = self.source_info(span); let bool_ty = self.hir.bool_ty(); if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() { let result_tup = self.hir.tcx().intern_tup(&[ty, bool_ty]); let result_value = self.temp(result_tup, span); - self.cfg.push_assign(block, source_info, - &result_value, Rvalue::CheckedBinaryOp(op, - lhs, - rhs)); + self.cfg.push_assign( + block, + source_info, + &result_value, + Rvalue::CheckedBinaryOp(op, lhs, rhs), + ); let val_fld = Field::new(0); let of_fld = Field::new(1); @@ -361,8 +428,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let err = EvalErrorKind::Overflow(op); - block = self.assert(block, Operand::Move(of), false, - err, span); + block = self.assert(block, Operand::Move(of), false, err, span); block.and(Rvalue::Use(Operand::Move(val))) } else { @@ -371,21 +437,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // and 2. there are two possible failure cases, divide-by-zero and overflow. let (zero_err, overflow_err) = if op == BinOp::Div { - (EvalErrorKind::DivisionByZero, - EvalErrorKind::Overflow(op)) + (EvalErrorKind::DivisionByZero, EvalErrorKind::Overflow(op)) } else { - (EvalErrorKind::RemainderByZero, - EvalErrorKind::Overflow(op)) + (EvalErrorKind::RemainderByZero, EvalErrorKind::Overflow(op)) }; // Check for / 0 let is_zero = self.temp(bool_ty, span); let zero = self.zero_literal(span, ty); - self.cfg.push_assign(block, source_info, &is_zero, - Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero)); + self.cfg.push_assign( + block, + source_info, + &is_zero, + Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero), + ); - block = self.assert(block, Operand::Move(is_zero), false, - zero_err, span); + block = self.assert(block, Operand::Move(is_zero), false, zero_err, span); // We only need to check for the overflow in one case: // MIN / -1, and only for signed values. @@ -394,23 +461,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let min = self.minval_literal(span, ty); let is_neg_1 = self.temp(bool_ty, span); - let is_min = self.temp(bool_ty, span); - let of = self.temp(bool_ty, span); + let is_min = self.temp(bool_ty, span); + let of = self.temp(bool_ty, span); // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead - self.cfg.push_assign(block, source_info, &is_neg_1, - Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), neg_1)); - self.cfg.push_assign(block, source_info, &is_min, - Rvalue::BinaryOp(BinOp::Eq, lhs.to_copy(), min)); + self.cfg.push_assign( + block, + source_info, + &is_neg_1, + Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), neg_1), + ); + self.cfg.push_assign( + block, + source_info, + &is_min, + Rvalue::BinaryOp(BinOp::Eq, lhs.to_copy(), min), + ); let is_neg_1 = Operand::Move(is_neg_1); let is_min = Operand::Move(is_min); - self.cfg.push_assign(block, source_info, &of, - Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min)); + self.cfg.push_assign( + block, + source_info, + &of, + Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min), + ); - block = self.assert(block, Operand::Move(of), false, - overflow_err, span); + block = self.assert(block, Operand::Move(of), false, overflow_err, span); } } @@ -430,12 +508,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let this = self; let source_info = this.source_info(upvar_span); - let temp = this.local_decls.push(LocalDecl::new_temp(upvar_ty, upvar_span)); + let temp = this + .local_decls + .push(LocalDecl::new_temp(upvar_ty, upvar_span)); - this.cfg.push(block, Statement { - source_info, - kind: StatementKind::StorageLive(temp) - }); + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::StorageLive(temp), + }, + ); let arg_place = unpack!(block = this.as_place(block, arg)); @@ -446,8 +529,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { elem: ProjectionElem::Deref, }) => { debug_assert!( - if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) - = this.local_decls[local].is_user_variable { + if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) = + this.local_decls[local].is_user_variable + { true } else { false @@ -461,10 +545,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { elem: ProjectionElem::Field(upvar_index, _), }) | Place::Projection(box Projection { - base: Place::Projection(box Projection { - ref base, - elem: ProjectionElem::Field(upvar_index, _), - }), + base: + Place::Projection(box Projection { + ref base, + elem: ProjectionElem::Field(upvar_index, _), + }), elem: ProjectionElem::Deref, }) => { // Not projected from the implicit `self` in a closure. @@ -491,7 +576,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let borrow_kind = match mutability { Mutability::Not => BorrowKind::Unique, - Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, + Mutability::Mut => BorrowKind::Mut { + allow_two_phase_borrow: false, + }, }; this.cfg.push_assign( @@ -506,7 +593,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // a constant at this time, even if the type may need dropping. if let Some(temp_lifetime) = temp_lifetime { this.schedule_drop_storage_and_value( - upvar_span, temp_lifetime, &Place::Local(temp), upvar_ty, + upvar_span, + temp_lifetime, + &Place::Local(temp), + upvar_ty, ); } diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index f66fe763b759d..a2dcce6adcb6e 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -18,42 +18,63 @@ 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(&mut self, - block: BasicBlock, - temp_lifetime: Option, - expr: M) - -> BlockAnd - where M: Mirror<'tcx, Output = Expr<'tcx>> + pub fn as_temp( + &mut self, + block: BasicBlock, + temp_lifetime: Option, + expr: M, + mutability: Mutability, + ) -> BlockAnd + where + M: Mirror<'tcx, Output = Expr<'tcx>>, { let expr = self.hir.mirror(expr); - self.expr_as_temp(block, temp_lifetime, expr) + self.expr_as_temp(block, temp_lifetime, expr, mutability) } - fn expr_as_temp(&mut self, - mut block: BasicBlock, - temp_lifetime: Option, - expr: Expr<'tcx>) - -> BlockAnd { - debug!("expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?})", - block, temp_lifetime, expr); + fn expr_as_temp( + &mut self, + mut block: BasicBlock, + temp_lifetime: Option, + expr: Expr<'tcx>, + mutability: Mutability, + ) -> BlockAnd { + debug!( + "expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})", + block, temp_lifetime, expr, mutability + ); let this = self; let expr_span = expr.span; let source_info = this.source_info(expr_span); - if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind { + if let ExprKind::Scope { + region_scope, + lint_level, + value, + } = expr.kind + { return this.in_scope((region_scope, source_info), lint_level, block, |this| { - this.as_temp(block, temp_lifetime, value) + this.as_temp(block, temp_lifetime, value, mutability) }); } let expr_ty = expr.ty; - let temp = this.local_decls.push(LocalDecl::new_temp(expr_ty, expr_span)); + 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)) + }; if !expr_ty.is_never() { - this.cfg.push(block, Statement { - source_info, - kind: StatementKind::StorageLive(temp) - }); + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::StorageLive(temp), + }, + ); } unpack!(block = this.into(&Place::Local(temp), block, expr)); @@ -63,7 +84,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // a constant at this time, even if the type may need dropping. if let Some(temp_lifetime) = temp_lifetime { this.schedule_drop_storage_and_value( - expr_span, temp_lifetime, &Place::Local(temp), expr_ty, + expr_span, + temp_lifetime, + &Place::Local(temp), + expr_ty, ); } diff --git a/src/librustc_mir/build/expr/category.rs b/src/librustc_mir/build/expr/category.rs index bce8e97d481f1..601fe2d01f861 100644 --- a/src/librustc_mir/build/expr/category.rs +++ b/src/librustc_mir/build/expr/category.rs @@ -45,53 +45,51 @@ impl Category { match *ek { ExprKind::Scope { .. } => None, - ExprKind::Field { .. } | - ExprKind::Deref { .. } | - ExprKind::Index { .. } | - ExprKind::SelfRef | - ExprKind::VarRef { .. } | - ExprKind::StaticRef { .. } => - Some(Category::Place), + ExprKind::Field { .. } + | ExprKind::Deref { .. } + | ExprKind::Index { .. } + | ExprKind::SelfRef + | ExprKind::VarRef { .. } + | ExprKind::StaticRef { .. } => Some(Category::Place), - ExprKind::LogicalOp { .. } | - ExprKind::If { .. } | - ExprKind::Match { .. } | - ExprKind::NeverToAny { .. } | - ExprKind::Call { .. } => - Some(Category::Rvalue(RvalueFunc::Into)), + ExprKind::LogicalOp { .. } + | ExprKind::If { .. } + | ExprKind::Match { .. } + | ExprKind::NeverToAny { .. } + | ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)), - ExprKind::Array { .. } | - ExprKind::Tuple { .. } | - ExprKind::Adt { .. } | - ExprKind::Closure { .. } | - ExprKind::Unary { .. } | - ExprKind::Binary { .. } | - ExprKind::Box { .. } | - ExprKind::Cast { .. } | - ExprKind::Use { .. } | - ExprKind::ReifyFnPointer { .. } | - ExprKind::ClosureFnPointer { .. } | - ExprKind::UnsafeFnPointer { .. } | - ExprKind::Unsize { .. } | - ExprKind::Repeat { .. } | - ExprKind::Borrow { .. } | - ExprKind::Assign { .. } | - ExprKind::AssignOp { .. } | - ExprKind::Yield { .. } | - ExprKind::InlineAsm { .. } => - Some(Category::Rvalue(RvalueFunc::AsRvalue)), + ExprKind::Array { .. } + | ExprKind::Tuple { .. } + | ExprKind::Adt { .. } + | ExprKind::Closure { .. } + | ExprKind::Unary { .. } + | ExprKind::Binary { .. } + | ExprKind::Box { .. } + | ExprKind::Cast { .. } + | ExprKind::Use { .. } + | ExprKind::ReifyFnPointer { .. } + | ExprKind::ClosureFnPointer { .. } + | ExprKind::UnsafeFnPointer { .. } + | ExprKind::Unsize { .. } + | ExprKind::Repeat { .. } + | ExprKind::Borrow { .. } + | ExprKind::Assign { .. } + | ExprKind::AssignOp { .. } + | ExprKind::Yield { .. } + | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), - ExprKind::Literal { .. } => - Some(Category::Constant), + ExprKind::Literal { .. } => Some(Category::Constant), - ExprKind::Loop { .. } | - ExprKind::Block { .. } | - ExprKind::Break { .. } | - ExprKind::Continue { .. } | - ExprKind::Return { .. } => - // FIXME(#27840) these probably want their own - // category, like "nonterminating" - Some(Category::Rvalue(RvalueFunc::Into)), + ExprKind::Loop { .. } + | ExprKind::Block { .. } + | ExprKind::Break { .. } + | ExprKind::Continue { .. } + | ExprKind::Return { .. } => + // FIXME(#27840) these probably want their own + // category, like "nonterminating" + { + Some(Category::Rvalue(RvalueFunc::Into)) + } } } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 3cd1270d7ef11..59ebb7703ff61 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -10,25 +10,27 @@ //! See docs in build/expr/mod.rs -use build::{BlockAnd, BlockAndExtension, Builder}; use build::expr::category::{Category, RvalueFunc}; +use build::{BlockAnd, BlockAndExtension, Builder}; use hair::*; -use rustc::ty; use rustc::mir::*; +use rustc::ty; use rustc_target::spec::abi::Abi; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Compile `expr`, storing the result into `destination`, which /// is assumed to be uninitialized. - pub fn into_expr(&mut self, - destination: &Place<'tcx>, - mut block: BasicBlock, - expr: Expr<'tcx>) - -> BlockAnd<()> - { - debug!("into_expr(destination={:?}, block={:?}, expr={:?})", - destination, block, expr); + pub fn into_expr( + &mut self, + destination: &Place<'tcx>, + mut block: BasicBlock, + expr: Expr<'tcx>, + ) -> BlockAnd<()> { + debug!( + "into_expr(destination={:?}, block={:?}, expr={:?})", + destination, block, expr + ); // since we frequently have to reference `self` from within a // closure, where `self` would be shadowed, it's easier to @@ -38,10 +40,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let source_info = this.source_info(expr_span); match expr.kind { - ExprKind::Scope { region_scope, lint_level, value } => { + ExprKind::Scope { + region_scope, + lint_level, + value, + } => { let region_scope = (region_scope, source_info); - this.in_scope(region_scope, lint_level, block, - |this| this.into(destination, block, value)) + this.in_scope(region_scope, lint_level, block, |this| { + this.into(destination, block, value) + }) } ExprKind::Block { body: ast_block } => { this.ast_block(destination, block, ast_block, source_info) @@ -63,12 +70,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if is_call { block.unit() } else { - this.cfg.terminate(block, source_info, TerminatorKind::Unreachable); + this.cfg + .terminate(block, source_info, TerminatorKind::Unreachable); let end_block = this.cfg.start_new_block(); end_block.unit() } } - ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => { + ExprKind::If { + condition: cond_expr, + then: then_expr, + otherwise: else_expr, + } => { let operand = unpack!(block = this.as_local_operand(block, cond_expr)); let mut then_block = this.cfg.start_new_block(); @@ -82,15 +94,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } else { // Body of the `if` expression without an `else` clause must return `()`, thus // we implicitly generate a `else {}` if it is not specified. - this.cfg.push_assign_unit(else_block, source_info, destination); + this.cfg + .push_assign_unit(else_block, source_info, destination); else_block }; let join_block = this.cfg.start_new_block(); - this.cfg.terminate(then_block, source_info, - TerminatorKind::Goto { target: join_block }); - this.cfg.terminate(else_block, source_info, - TerminatorKind::Goto { target: join_block }); + this.cfg.terminate( + then_block, + source_info, + TerminatorKind::Goto { target: join_block }, + ); + this.cfg.terminate( + else_block, + source_info, + TerminatorKind::Goto { target: join_block }, + ); join_block.unit() } @@ -107,9 +126,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // | (true) | (false) // [true_block] [false_block] - let (true_block, false_block, mut else_block, join_block) = - (this.cfg.start_new_block(), this.cfg.start_new_block(), - this.cfg.start_new_block(), this.cfg.start_new_block()); + let (true_block, false_block, mut else_block, join_block) = ( + 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_local_operand(block, lhs)); let blocks = match op { @@ -124,31 +146,46 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.cfg.terminate(else_block, source_info, term); this.cfg.push_assign_constant( - true_block, source_info, destination, + true_block, + source_info, + destination, Constant { span: expr_span, ty: this.hir.bool_ty(), user_ty: None, literal: this.hir.true_literal(), - }); + }, + ); this.cfg.push_assign_constant( - false_block, source_info, destination, + false_block, + source_info, + destination, Constant { span: expr_span, ty: this.hir.bool_ty(), user_ty: None, literal: this.hir.false_literal(), - }); + }, + ); - this.cfg.terminate(true_block, source_info, - TerminatorKind::Goto { target: join_block }); - this.cfg.terminate(false_block, source_info, - TerminatorKind::Goto { target: join_block }); + this.cfg.terminate( + true_block, + source_info, + TerminatorKind::Goto { target: join_block }, + ); + this.cfg.terminate( + false_block, + source_info, + TerminatorKind::Goto { target: join_block }, + ); join_block.unit() } - ExprKind::Loop { condition: opt_cond_expr, body } => { + ExprKind::Loop { + condition: opt_cond_expr, + body, + } => { // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block] // ^ | // | 0 @@ -172,35 +209,45 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let exit_block = this.cfg.start_new_block(); // start the loop - this.cfg.terminate(block, source_info, - TerminatorKind::Goto { target: loop_block }); + this.cfg.terminate( + block, + source_info, + TerminatorKind::Goto { target: loop_block }, + ); this.in_breakable_scope( - Some(loop_block), exit_block, destination.clone(), + Some(loop_block), + exit_block, + destination.clone(), move |this| { // conduct the test, if necessary let body_block; if let Some(cond_expr) = opt_cond_expr { let loop_block_end; let cond = unpack!( - loop_block_end = this.as_local_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); + let term = + TerminatorKind::if_(this.hir.tcx(), cond, body_block, exit_block); this.cfg.terminate(loop_block_end, source_info, term); // if the test is false, there's no `break` to assign `destination`, so // we have to do it; this overwrites any `break`-assigned value but it's // always `()` anyway - this.cfg.push_assign_unit(exit_block, source_info, destination); + this.cfg + .push_assign_unit(exit_block, source_info, destination); } else { body_block = this.cfg.start_new_block(); let diverge_cleanup = this.diverge_cleanup(); - this.cfg.terminate(loop_block, source_info, - TerminatorKind::FalseUnwind { - real_target: body_block, - unwind: Some(diverge_cleanup) - }) + this.cfg.terminate( + loop_block, + source_info, + TerminatorKind::FalseUnwind { + real_target: body_block, + unwind: Some(diverge_cleanup), + }, + ) } // The “return” value of the loop body must always be an unit. We therefore @@ -208,9 +255,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let tmp = this.get_unit_temp(); // Execute the body, branching back to the test. let body_block_end = unpack!(this.into(&tmp, body_block, body)); - this.cfg.terminate(body_block_end, source_info, - TerminatorKind::Goto { target: loop_block }); - } + this.cfg.terminate( + body_block_end, + source_info, + TerminatorKind::Goto { target: loop_block }, + ); + }, ); exit_block.unit() } @@ -218,16 +268,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // FIXME(canndrew): This is_never should probably be an is_uninhabited let diverges = expr.ty.is_never(); let intrinsic = match ty.sty { - ty::FnDef(def_id, _) => { + ty::FnDef(def_id, _) => { let f = ty.fn_sig(this.hir.tcx()); - if f.abi() == Abi::RustIntrinsic || - f.abi() == Abi::PlatformIntrinsic { + if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { Some(this.hir.tcx().item_name(def_id).as_str()) } else { None } } - _ => None + _ => None, }; let intrinsic = intrinsic.as_ref().map(|s| &s[..]); let fun = unpack!(block = this.as_local_operand(block, fun)); @@ -257,95 +306,99 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let block = unpack!(this.into(&ptr_temp, block, ptr)); this.into(&ptr_temp.deref(), block, val) } else { - let args: Vec<_> = - args.into_iter() - .map(|arg| unpack!(block = this.as_local_operand(block, arg))) - .collect(); + let args: Vec<_> = args + .into_iter() + .map(|arg| unpack!(block = this.as_local_operand(block, arg))) + .collect(); let success = this.cfg.start_new_block(); let cleanup = this.diverge_cleanup(); - this.cfg.terminate(block, source_info, TerminatorKind::Call { - func: fun, - args, - cleanup: Some(cleanup), - destination: if diverges { - None - } else { - Some ((destination.clone(), success)) - } - }); + this.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func: fun, + args, + cleanup: Some(cleanup), + destination: if diverges { + None + } else { + Some((destination.clone(), success)) + }, + }, + ); success.unit() } } // These cases don't actually need a destination - ExprKind::Assign { .. } | - ExprKind::AssignOp { .. } | - ExprKind::Continue { .. } | - ExprKind::Break { .. } | - ExprKind::InlineAsm { .. } | - ExprKind::Return { .. } => { + ExprKind::Assign { .. } + | ExprKind::AssignOp { .. } + | ExprKind::Continue { .. } + | ExprKind::Break { .. } + | ExprKind::InlineAsm { .. } + | ExprKind::Return { .. } => { unpack!(block = this.stmt_expr(block, expr)); this.cfg.push_assign_unit(block, source_info, destination); block.unit() } // Avoid creating a temporary - ExprKind::VarRef { .. } | - ExprKind::SelfRef | - ExprKind::StaticRef { .. } => { + ExprKind::VarRef { .. } | ExprKind::SelfRef | ExprKind::StaticRef { .. } => { debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); let place = unpack!(block = this.as_place(block, expr)); let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); - this.cfg.push_assign(block, source_info, destination, rvalue); + this.cfg + .push_assign(block, source_info, destination, rvalue); block.unit() } - ExprKind::Index { .. } | - ExprKind::Deref { .. } | - ExprKind::Field { .. } => { + ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => { debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); // Create a "fake" temporary variable so that we check that the // value is Sized. Usually, this is caught in type checking, but // in the case of box expr there is no such check. if let Place::Projection(..) = destination { - this.local_decls.push(LocalDecl::new_temp(expr.ty, expr.span)); + this.local_decls + .push(LocalDecl::new_temp(expr.ty, expr.span)); } debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); let place = unpack!(block = this.as_place(block, expr)); let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); - this.cfg.push_assign(block, source_info, destination, rvalue); + this.cfg + .push_assign(block, source_info, destination, rvalue); block.unit() } // these are the cases that are more naturally handled by some other mode - ExprKind::Unary { .. } | - ExprKind::Binary { .. } | - ExprKind::Box { .. } | - ExprKind::Cast { .. } | - ExprKind::Use { .. } | - ExprKind::ReifyFnPointer { .. } | - ExprKind::ClosureFnPointer { .. } | - ExprKind::UnsafeFnPointer { .. } | - ExprKind::Unsize { .. } | - ExprKind::Repeat { .. } | - ExprKind::Borrow { .. } | - ExprKind::Array { .. } | - ExprKind::Tuple { .. } | - ExprKind::Adt { .. } | - ExprKind::Closure { .. } | - ExprKind::Literal { .. } | - ExprKind::Yield { .. } => { + ExprKind::Unary { .. } + | ExprKind::Binary { .. } + | ExprKind::Box { .. } + | ExprKind::Cast { .. } + | ExprKind::Use { .. } + | ExprKind::ReifyFnPointer { .. } + | ExprKind::ClosureFnPointer { .. } + | ExprKind::UnsafeFnPointer { .. } + | ExprKind::Unsize { .. } + | ExprKind::Repeat { .. } + | ExprKind::Borrow { .. } + | ExprKind::Array { .. } + | ExprKind::Tuple { .. } + | ExprKind::Adt { .. } + | ExprKind::Closure { .. } + | ExprKind::Literal { .. } + | ExprKind::Yield { .. } => { debug_assert!(match Category::of(&expr.kind).unwrap() { Category::Rvalue(RvalueFunc::Into) => false, _ => true, }); let rvalue = unpack!(block = this.as_local_rvalue(block, expr)); - this.cfg.push_assign(block, source_info, destination, rvalue); + this.cfg + .push_assign(block, source_info, destination, rvalue); block.unit() } } diff --git a/src/librustc_mir/build/expr/mod.rs b/src/librustc_mir/build/expr/mod.rs index a63cf41f0663e..6442ba34da4b2 100644 --- a/src/librustc_mir/build/expr/mod.rs +++ b/src/librustc_mir/build/expr/mod.rs @@ -71,9 +71,9 @@ //! over to the "by reference" mode (`as_place`). mod as_constant; +mod as_operand; mod as_place; mod as_rvalue; -mod as_operand; mod as_temp; mod category; mod into; diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs index 6f1fe8335780d..0086cff46e53c 100644 --- a/src/librustc_mir/build/expr/stmt.rs +++ b/src/librustc_mir/build/expr/stmt.rs @@ -8,13 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use build::{BlockAnd, BlockAndExtension, Builder}; use build::scope::BreakableScope; +use build::{BlockAnd, BlockAndExtension, Builder}; use hair::*; use rustc::mir::*; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { - pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<()> { let this = self; let expr_span = expr.span; @@ -22,7 +21,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Handle a number of expressions that don't need a destination at all. This // avoids needing a mountain of temporary `()` variables. match expr.kind { - ExprKind::Scope { region_scope, lint_level, value } => { + ExprKind::Scope { + region_scope, + lint_level, + value, + } => { let value = this.hir.mirror(value); this.in_scope((region_scope, source_info), lint_level, block, |this| { this.stmt_expr(block, value) @@ -42,9 +45,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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 - )); + 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)); @@ -72,18 +73,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // we don't have to drop prior contents or anything // because AssignOp is only legal for Copy types // (overloaded ops should be desugared into a call). - let result = unpack!(block = this.build_binary_op(block, op, expr_span, lhs_ty, - Operand::Copy(lhs.clone()), rhs)); + let result = unpack!( + block = this.build_binary_op( + block, + op, + expr_span, + lhs_ty, + Operand::Copy(lhs.clone()), + rhs + ) + ); this.cfg.push_assign(block, source_info, &lhs, result); block.unit() } ExprKind::Continue { label } => { - let BreakableScope { continue_block, region_scope, .. } = - *this.find_breakable_scope(expr_span, label); - let continue_block = continue_block.expect( - "Attempted to continue in non-continuable breakable block"); - this.exit_scope(expr_span, (region_scope, source_info), block, continue_block); + let BreakableScope { + continue_block, + region_scope, + .. + } = *this.find_breakable_scope(expr_span, label); + let continue_block = continue_block + .expect("Attempted to continue in non-continuable breakable block"); + this.exit_scope( + expr_span, + (region_scope, source_info), + block, + continue_block, + ); this.cfg.start_new_block().unit() } ExprKind::Break { label, value } => { @@ -106,13 +123,10 @@ 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) => unpack!(this.into(&Place::Local(RETURN_PLACE), block, value)), None => { - this.cfg.push_assign_unit(block, - source_info, - &Place::Local(RETURN_PLACE)); + this.cfg + .push_assign_unit(block, source_info, &Place::Local(RETURN_PLACE)); block } }; @@ -121,21 +135,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.exit_scope(expr_span, (region_scope, source_info), block, return_block); this.cfg.start_new_block().unit() } - ExprKind::InlineAsm { asm, outputs, inputs } => { - let outputs = outputs.into_iter().map(|output| { - unpack!(block = this.as_place(block, output)) - }).collect(); - let inputs = inputs.into_iter().map(|input| { - unpack!(block = this.as_local_operand(block, input)) - }).collect(); - this.cfg.push(block, Statement { - source_info, - kind: StatementKind::InlineAsm { - asm: box asm.clone(), - outputs, - inputs, + ExprKind::InlineAsm { + asm, + outputs, + inputs, + } => { + let outputs = outputs + .into_iter() + .map(|output| unpack!(block = this.as_place(block, output))) + .collect(); + let inputs = inputs + .into_iter() + .map(|input| unpack!(block = this.as_local_operand(block, input))) + .collect(); + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::InlineAsm { + asm: box asm.clone(), + outputs, + inputs, + }, }, - }); + ); block.unit() } _ => { @@ -147,5 +170,4 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } } - } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 8f3595b17848c..999a858f3214c 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -115,7 +115,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { nonlexical_regioncx: Rc>, def_id: DefId, body_id: Option, - borrow_set: &Rc> + borrow_set: &Rc>, ) -> Self { let scope_tree = tcx.region_scope_tree(def_id); let root_scope = body_id.map(|body_id| { @@ -233,7 +233,13 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> { // propagate_call_return method. if let mir::Rvalue::Ref(region, _, ref place) = *rhs { - if place.ignore_borrow(self.tcx, self.mir) { return; } + if place.ignore_borrow( + self.tcx, + self.mir, + &self.borrow_set.locals_state_at_exit, + ) { + return; + } let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| { panic!("could not find BorrowIndex for location {:?}", location); }); diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 7b4cbdf7131b0..9f1ba1f0530d5 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -316,4 +316,14 @@ impl<'a, 'gcx, 'tcx> MoveData<'tcx> { -> Result, MoveError<'tcx>)>)> { builder::gather_moves(mir, tcx) } + + /// For the move path `mpi`, returns the root local variable (if any) that starts the path. + /// (e.g., for a path like `a.b.c` returns `Some(a)`) + pub fn base_local(&self, mut mpi: MovePathIndex) -> Option { + loop { + let path = &self.move_paths[mpi]; + if let Place::Local(l) = path.place { return Some(l); } + if let Some(parent) = path.parent { mpi = parent; continue } else { return None } + } + } } diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index e0643d8f97810..9f5b5040b0957 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -612,8 +612,9 @@ fn write_temp_decls(mir: &Mir, w: &mut dyn Write) -> io::Result<()> { for temp in mir.temps_iter() { writeln!( w, - "{}let mut {:?}: {};", + "{}let {}{:?}: {};", INDENT, + if mir.local_decls[temp].mutability == Mutability::Mut {"mut "} else {""}, temp, mir.local_decls[temp].ty )?; diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs index 8f8c406bcb3a4..16e2fe046fb69 100644 --- a/src/test/mir-opt/end_region_destruction_extents_1.rs +++ b/src/test/mir-opt/end_region_destruction_extents_1.rs @@ -70,10 +70,10 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // let mut _2: D1<'12ds, '10s>; // let mut _3: &'12ds S1; // let mut _4: &'12ds S1; -// let mut _5: S1; +// let _5: S1; // let mut _6: &'10s S1; // let mut _7: &'10s S1; -// let mut _8: S1; +// let _8: S1; // bb0: { // StorageLive(_2); // StorageLive(_3); @@ -118,10 +118,10 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> { // let mut _2: D1<'12ds, '10s>; // let mut _3: &'12ds S1; // let mut _4: &'12ds S1; -// let mut _5: S1; +// let _5: S1; // let mut _6: &'10s S1; // let mut _7: &'10s S1; -// let mut _8: S1; +// let _8: S1; // bb0: { // StorageLive(_2); // StorageLive(_3); diff --git a/src/test/mir-opt/storage_live_dead_in_statics.rs b/src/test/mir-opt/storage_live_dead_in_statics.rs index 730ef655b13d0..e39b7df8a7ad8 100644 --- a/src/test/mir-opt/storage_live_dead_in_statics.rs +++ b/src/test/mir-opt/storage_live_dead_in_statics.rs @@ -47,11 +47,11 @@ fn main() { // START rustc.XXX.mir_map.0.mir // let mut _0: &'static Foo; // let mut _1: &'static Foo; -// let mut _2: Foo; +// let _2: Foo; // let mut _3: &'static [(u32, u32)]; // let mut _4: &'static [(u32, u32); 42]; // let mut _5: &'static [(u32, u32); 42]; -// let mut _6: [(u32, u32); 42]; +// let _6: [(u32, u32); 42]; // let mut _7: (u32, u32); // let mut _8: (u32, u32); // let mut _9: (u32, u32);