1
Fork 0

Auto merge of #53909 - mikhail-m1:53643, r=nikomatsakis

Skip a shared borrow of a immutable local variables

issue #53643

r? @nikomatsakis
This commit is contained in:
bors 2018-09-08 19:57:14 +00:00
commit 0198a1ea45
18 changed files with 822 additions and 478 deletions

View file

@ -249,11 +249,6 @@ impl<'tcx> Mir<'tcx> {
} else if self.local_decls[local].name.is_some() { } else if self.local_decls[local].name.is_some() {
LocalKind::Var LocalKind::Var
} else { } else {
debug_assert!(
self.local_decls[local].mutability == Mutability::Mut,
"temp should be mutable"
);
LocalKind::Temp LocalKind::Temp
} }
} }
@ -782,25 +777,30 @@ impl<'tcx> LocalDecl<'tcx> {
/// Create a new `LocalDecl` for a temporary. /// Create a new `LocalDecl` for a temporary.
#[inline] #[inline]
pub fn new_temp(ty: Ty<'tcx>, span: Span) -> Self { pub fn new_temp(ty: Ty<'tcx>, span: Span) -> Self {
LocalDecl { Self::new_local(ty, Mutability::Mut, false, span)
mutability: Mutability::Mut, }
ty,
name: None, /// Create a new immutable `LocalDecl` for a temporary.
source_info: SourceInfo { #[inline]
span, pub fn new_immutable_temp(ty: Ty<'tcx>, span: Span) -> Self {
scope: OUTERMOST_SOURCE_SCOPE, Self::new_local(ty, Mutability::Not, false, span)
},
visibility_scope: OUTERMOST_SOURCE_SCOPE,
internal: false,
is_user_variable: None,
}
} }
/// Create a new `LocalDecl` for a internal temporary. /// Create a new `LocalDecl` for a internal temporary.
#[inline] #[inline]
pub fn new_internal(ty: Ty<'tcx>, span: Span) -> Self { 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 { LocalDecl {
mutability: Mutability::Mut, mutability,
ty, ty,
name: None, name: None,
source_info: SourceInfo { source_info: SourceInfo {
@ -808,7 +808,7 @@ impl<'tcx> LocalDecl<'tcx> {
scope: OUTERMOST_SOURCE_SCOPE, scope: OUTERMOST_SOURCE_SCOPE,
}, },
visibility_scope: OUTERMOST_SOURCE_SCOPE, visibility_scope: OUTERMOST_SOURCE_SCOPE,
internal: true, internal,
is_user_variable: None, is_user_variable: None,
} }
} }

View file

@ -10,12 +10,14 @@
use borrow_check::place_ext::PlaceExt; use borrow_check::place_ext::PlaceExt;
use dataflow::indexes::BorrowIndex; use dataflow::indexes::BorrowIndex;
use dataflow::move_paths::MoveData;
use rustc::mir::traversal; use rustc::mir::traversal;
use rustc::mir::visit::{PlaceContext, Visitor}; 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::ty::{Region, TyCtxt};
use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::bitvec::BitArray;
use std::fmt; use std::fmt;
use std::hash::Hash; use std::hash::Hash;
use std::ops::Index; use std::ops::Index;
@ -43,6 +45,8 @@ crate struct BorrowSet<'tcx> {
/// Map from local to all the borrows on that local /// Map from local to all the borrows on that local
crate local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>, crate local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
crate locals_state_at_exit: LocalsStateAtExit,
} }
impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> { impl<'tcx> Index<BorrowIndex> 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<Local> }
}
impl LocalsStateAtExit {
fn build(
locals_are_invalidated_at_exit: bool,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>
) -> Self {
struct HasStorageDead(BitArray<Local>);
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> { 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 { let mut visitor = GatherBorrows {
tcx, tcx,
mir, mir,
@ -107,6 +155,8 @@ impl<'tcx> BorrowSet<'tcx> {
region_map: FxHashMap(), region_map: FxHashMap(),
local_map: FxHashMap(), local_map: FxHashMap(),
pending_activations: 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) { for (block, block_data) in traversal::preorder(mir) {
@ -119,6 +169,7 @@ impl<'tcx> BorrowSet<'tcx> {
activation_map: visitor.activation_map, activation_map: visitor.activation_map,
region_map: visitor.region_map, region_map: visitor.region_map,
local_map: visitor.local_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 /// the borrow. When we find a later use of this activation, we
/// remove from the map (and add to the "tombstone" set below). /// remove from the map (and add to the "tombstone" set below).
pending_activations: FxHashMap<mir::Local, BorrowIndex>, pending_activations: FxHashMap<mir::Local, BorrowIndex>,
locals_state_at_exit: LocalsStateAtExit,
} }
impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { 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, location: mir::Location,
) { ) {
if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue { 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; return;
} }

View file

@ -196,7 +196,12 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|bd, i| DebugFormatted::new(&bd.move_data().inits[i]), |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. // If we are in non-lexical mode, compute the non-lexical lifetimes.
let (regioncx, polonius_output, opt_closure_req) = nll::compute_regions( 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, param_env: param_env,
location_table, location_table,
movable_generator, movable_generator,
locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) { locals_are_invalidated_at_exit,
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false,
hir::BodyOwnerKind::Fn => true,
},
access_place_error_reported: FxHashSet(), access_place_error_reported: FxHashSet(),
reservation_error_reported: FxHashSet(), reservation_error_reported: FxHashSet(),
moved_error_reported: FxHashSet(), moved_error_reported: FxHashSet(),

View file

@ -10,8 +10,9 @@
use rustc::hir; use rustc::hir;
use rustc::mir::ProjectionElem; use rustc::mir::ProjectionElem;
use rustc::mir::{Local, Mir, Place}; use rustc::mir::{Local, Mir, Place, Mutability};
use rustc::ty::{self, TyCtxt}; use rustc::ty::{self, TyCtxt};
use borrow_check::borrow_set::LocalsStateAtExit;
/// Extension methods for the `Place` type. /// Extension methods for the `Place` type.
crate trait PlaceExt<'tcx> { 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 /// 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 /// to the place `self` that would invalidate the borrow. This is true
/// for borrows of raw pointer dereferents as well as shared references. /// 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 /// If this is a place like `x.f.g`, returns the local
/// `x`. Returns `None` if this is based in a static. /// `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> { 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 { match self {
Place::Promoted(_) | Place::Promoted(_) => false,
Place::Local(_) => 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_) => { Place::Static(static_) => {
tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable) tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable)
} }
@ -39,7 +69,8 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
| ProjectionElem::Downcast(..) | ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::ConstantIndex { .. } | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Index(_) => proj.base.ignore_borrow(tcx, mir), | ProjectionElem::Index(_) => proj.base.ignore_borrow(
tcx, mir, locals_state_at_exit),
ProjectionElem::Deref => { ProjectionElem::Deref => {
let ty = proj.base.ty(mir, tcx).to_ty(tcx); 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 // borrowed *that* one, leaving the original
// path unborrowed. // path unborrowed.
ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => true, ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => true,
_ => proj.base.ignore_borrow(tcx, mir), _ => proj.base.ignore_borrow(tcx, mir, locals_state_at_exit),
} }
} }
}, },

View file

@ -18,7 +18,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// Compile `expr`, yielding a compile-time constant. Assumes that /// Compile `expr`, yielding a compile-time constant. Assumes that
/// `expr` is a valid compile-time constant! /// `expr` is a valid compile-time constant!
pub fn as_constant<M>(&mut self, expr: M) -> Constant<'tcx> pub fn as_constant<M>(&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); let expr = self.hir.mirror(expr);
self.expr_as_constant(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> { fn expr_as_constant(&mut self, expr: Expr<'tcx>) -> Constant<'tcx> {
let this = self; let this = self;
let Expr { ty, temp_lifetime: _, span, kind } let Expr {
= expr; ty,
temp_lifetime: _,
span,
kind,
} = expr;
match kind { match kind {
ExprKind::Scope { region_scope: _, lint_level: _, value } => ExprKind::Scope {
this.as_constant(value), region_scope: _,
ExprKind::Literal { literal, user_ty } => lint_level: _,
Constant { span, ty, user_ty, literal }, value,
_ => } => this.as_constant(value),
span_bug!( ExprKind::Literal { literal, user_ty } => Constant {
span, span,
"expression is not a valid constant {:?}", ty,
kind), user_ty,
literal,
},
_ => span_bug!(span, "expression is not a valid constant {:?}", kind),
} }
} }
} }

View file

@ -10,8 +10,8 @@
//! See docs in build/expr/mod.rs //! See docs in build/expr/mod.rs
use build::{BlockAnd, BlockAndExtension, Builder};
use build::expr::category::Category; use build::expr::category::Category;
use build::{BlockAnd, BlockAndExtension, Builder};
use hair::*; use hair::*;
use rustc::middle::region; use rustc::middle::region;
use rustc::mir::*; 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 /// The operand returned from this function will *not be valid* after
/// an ExprKind::Scope is passed, so please do *not* return it from /// an ExprKind::Scope is passed, so please do *not* return it from
/// functions to avoid bad miscompiles. /// functions to avoid bad miscompiles.
pub fn as_local_operand<M>(&mut self, block: BasicBlock, expr: M) pub fn as_local_operand<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Operand<'tcx>>
-> BlockAnd<Operand<'tcx>> where
where M: Mirror<'tcx, Output = Expr<'tcx>> M: Mirror<'tcx, Output = Expr<'tcx>>,
{ {
let local_scope = self.local_scope(); let local_scope = self.local_scope();
self.as_operand(block, local_scope, expr) self.as_operand(block, local_scope, expr)
@ -37,25 +37,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// this time. /// this time.
/// ///
/// The operand is known to be live until the end of `scope`. /// The operand is known to be live until the end of `scope`.
pub fn as_operand<M>(&mut self, pub fn as_operand<M>(
block: BasicBlock, &mut self,
scope: Option<region::Scope>, block: BasicBlock,
expr: M) -> BlockAnd<Operand<'tcx>> scope: Option<region::Scope>,
where M: Mirror<'tcx, Output = Expr<'tcx>> expr: M,
) -> BlockAnd<Operand<'tcx>>
where
M: Mirror<'tcx, Output = Expr<'tcx>>,
{ {
let expr = self.hir.mirror(expr); let expr = self.hir.mirror(expr);
self.expr_as_operand(block, scope, expr) self.expr_as_operand(block, scope, expr)
} }
fn expr_as_operand(&mut self, fn expr_as_operand(
mut block: BasicBlock, &mut self,
scope: Option<region::Scope>, mut block: BasicBlock,
expr: Expr<'tcx>) scope: Option<region::Scope>,
-> BlockAnd<Operand<'tcx>> { expr: Expr<'tcx>,
) -> BlockAnd<Operand<'tcx>> {
debug!("expr_as_operand(block={:?}, expr={:?})", block, expr); debug!("expr_as_operand(block={:?}, expr={:?})", block, expr);
let this = self; 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 source_info = this.source_info(expr.span);
let region_scope = (region_scope, source_info); let region_scope = (region_scope, source_info);
return this.in_scope(region_scope, lint_level, block, |this| { 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(); 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 { match category {
Category::Constant => { Category::Constant => {
let constant = this.as_constant(expr); let constant = this.as_constant(expr);
block.and(Operand::Constant(box constant)) block.and(Operand::Constant(box constant))
} }
Category::Place | Category::Place | Category::Rvalue(..) => {
Category::Rvalue(..) => { let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
let operand =
unpack!(block = this.as_temp(block, scope, expr));
block.and(Operand::Move(Place::Local(operand))) block.and(Operand::Move(Place::Local(operand)))
} }
} }

View file

@ -10,42 +10,64 @@
//! See docs in build/expr/mod.rs //! See docs in build/expr/mod.rs
use build::{BlockAnd, BlockAndExtension, Builder};
use build::ForGuard::{OutsideGuard, RefWithinGuard};
use build::expr::category::Category; use build::expr::category::Category;
use build::ForGuard::{OutsideGuard, RefWithinGuard};
use build::{BlockAnd, BlockAndExtension, Builder};
use hair::*; use hair::*;
use rustc::mir::*;
use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
use rustc::mir::*;
use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::Idx;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// Compile `expr`, yielding a place that we can move from etc. /// Compile `expr`, yielding a place that we can move from etc.
pub fn as_place<M>(&mut self, pub fn as_place<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
block: BasicBlock, where
expr: M) M: Mirror<'tcx, Output = Expr<'tcx>>,
-> BlockAnd<Place<'tcx>>
where M: Mirror<'tcx, Output=Expr<'tcx>>
{ {
let expr = self.hir.mirror(expr); 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, /// Compile `expr`, yielding a place that we can move from etc.
mut block: BasicBlock, /// Mutability note: The caller of this method promises only to read from the resulting
expr: Expr<'tcx>) /// place. The place itself may or may not be mutable:
-> BlockAnd<Place<'tcx>> { /// * If this expr is a place expr like a.b, then we will return that place.
debug!("expr_as_place(block={:?}, expr={:?})", block, expr); /// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
pub fn as_read_only_place<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
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<Place<'tcx>> {
debug!(
"expr_as_place(block={:?}, expr={:?}, mutability={:?})",
block, expr, mutability
);
let this = self; let this = self;
let expr_span = expr.span; let expr_span = expr.span;
let source_info = this.source_info(expr_span); let source_info = this.source_info(expr_span);
match expr.kind { match expr.kind {
ExprKind::Scope { region_scope, lint_level, value } => { ExprKind::Scope {
this.in_scope((region_scope, source_info), lint_level, block, |this| { 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) this.as_place(block, value)
}) }
} }),
ExprKind::Field { lhs, name } => { ExprKind::Field { lhs, name } => {
let place = unpack!(block = this.as_place(block, lhs)); let place = unpack!(block = this.as_place(block, lhs));
let place = place.field(name, expr.ty); 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 // region_scope=None so place indexes live forever. They are scalars so they
// do not need storage annotations, and they are often copied between // do not need storage annotations, and they are often copied between
// places. // 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: // bounds check:
let (len, lt) = (this.temp(usize_ty.clone(), expr_span), let (len, lt) = (
this.temp(bool_ty, expr_span)); this.temp(usize_ty.clone(), expr_span),
this.cfg.push_assign(block, source_info, // len = len(slice) this.temp(bool_ty, expr_span),
&len, Rvalue::Len(slice.clone())); );
this.cfg.push_assign(block, source_info, // lt = idx < len this.cfg.push_assign(
&lt, Rvalue::BinaryOp(BinOp::Lt, block,
Operand::Copy(Place::Local(idx)), source_info, // len = len(slice)
Operand::Copy(len.clone()))); &len,
Rvalue::Len(slice.clone()),
);
this.cfg.push_assign(
block,
source_info, // lt = idx < len
&lt,
Rvalue::BinaryOp(
BinOp::Lt,
Operand::Copy(Place::Local(idx)),
Operand::Copy(len.clone()),
),
);
let msg = BoundsCheck { let msg = BoundsCheck {
len: Operand::Move(len), 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, let success = this.assert(block, Operand::Move(lt), true, msg, expr_span);
msg, expr_span);
success.and(slice.index(idx)) success.and(slice.index(idx))
} }
ExprKind::SelfRef => { ExprKind::SelfRef => block.and(Place::Local(Local::new(1))),
block.and(Place::Local(Local::new(1)))
}
ExprKind::VarRef { id } => { ExprKind::VarRef { id } => {
let place = if this.is_bound_var_in_guard(id) && let place = if this.is_bound_var_in_guard(id) && this
this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards() .hir
.tcx()
.all_pat_vars_are_implicit_refs_within_guards()
{ {
let index = this.var_local_id(id, RefWithinGuard); let index = this.var_local_id(id, RefWithinGuard);
Place::Local(index).deref() Place::Local(index).deref()
@ -98,46 +131,48 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}; };
block.and(place) block.and(place)
} }
ExprKind::StaticRef { id } => { ExprKind::StaticRef { id } => block.and(Place::Static(Box::new(Static {
block.and(Place::Static(Box::new(Static { def_id: id, ty: expr.ty }))) def_id: id,
} ty: expr.ty,
}))),
ExprKind::Array { .. } | ExprKind::Array { .. }
ExprKind::Tuple { .. } | | ExprKind::Tuple { .. }
ExprKind::Adt { .. } | | ExprKind::Adt { .. }
ExprKind::Closure { .. } | | ExprKind::Closure { .. }
ExprKind::Unary { .. } | | ExprKind::Unary { .. }
ExprKind::Binary { .. } | | ExprKind::Binary { .. }
ExprKind::LogicalOp { .. } | | ExprKind::LogicalOp { .. }
ExprKind::Box { .. } | | ExprKind::Box { .. }
ExprKind::Cast { .. } | | ExprKind::Cast { .. }
ExprKind::Use { .. } | | ExprKind::Use { .. }
ExprKind::NeverToAny { .. } | | ExprKind::NeverToAny { .. }
ExprKind::ReifyFnPointer { .. } | | ExprKind::ReifyFnPointer { .. }
ExprKind::ClosureFnPointer { .. } | | ExprKind::ClosureFnPointer { .. }
ExprKind::UnsafeFnPointer { .. } | | ExprKind::UnsafeFnPointer { .. }
ExprKind::Unsize { .. } | | ExprKind::Unsize { .. }
ExprKind::Repeat { .. } | | ExprKind::Repeat { .. }
ExprKind::Borrow { .. } | | ExprKind::Borrow { .. }
ExprKind::If { .. } | | ExprKind::If { .. }
ExprKind::Match { .. } | | ExprKind::Match { .. }
ExprKind::Loop { .. } | | ExprKind::Loop { .. }
ExprKind::Block { .. } | | ExprKind::Block { .. }
ExprKind::Assign { .. } | | ExprKind::Assign { .. }
ExprKind::AssignOp { .. } | | ExprKind::AssignOp { .. }
ExprKind::Break { .. } | | ExprKind::Break { .. }
ExprKind::Continue { .. } | | ExprKind::Continue { .. }
ExprKind::Return { .. } | | ExprKind::Return { .. }
ExprKind::Literal { .. } | | ExprKind::Literal { .. }
ExprKind::InlineAsm { .. } | | ExprKind::InlineAsm { .. }
ExprKind::Yield { .. } | | ExprKind::Yield { .. }
ExprKind::Call { .. } => { | ExprKind::Call { .. } => {
// these are not places, so we need to make a temporary. // these are not places, so we need to make a temporary.
debug_assert!(match Category::of(&expr.kind) { debug_assert!(match Category::of(&expr.kind) {
Some(Category::Place) => false, Some(Category::Place) => false,
_ => true, _ => 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)) block.and(Place::Local(temp))
} }
} }

View file

@ -13,64 +13,84 @@
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::Idx;
use build::{BlockAnd, BlockAndExtension, Builder};
use build::expr::category::{Category, RvalueFunc}; use build::expr::category::{Category, RvalueFunc};
use build::{BlockAnd, BlockAndExtension, Builder};
use hair::*; use hair::*;
use rustc::middle::region; use rustc::middle::region;
use rustc::ty::{self, Ty, UpvarSubsts};
use rustc::mir::*;
use rustc::mir::interpret::EvalErrorKind; use rustc::mir::interpret::EvalErrorKind;
use rustc::mir::*;
use rustc::ty::{self, Ty, UpvarSubsts};
use syntax_pos::Span; use syntax_pos::Span;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// See comment on `as_local_operand` /// See comment on `as_local_operand`
pub fn as_local_rvalue<M>(&mut self, block: BasicBlock, expr: M) pub fn as_local_rvalue<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Rvalue<'tcx>>
-> BlockAnd<Rvalue<'tcx>> where
where M: Mirror<'tcx, Output = Expr<'tcx>> M: Mirror<'tcx, Output = Expr<'tcx>>,
{ {
let local_scope = self.local_scope(); let local_scope = self.local_scope();
self.as_rvalue(block, local_scope, expr) self.as_rvalue(block, local_scope, expr)
} }
/// Compile `expr`, yielding an rvalue. /// Compile `expr`, yielding an rvalue.
pub fn as_rvalue<M>(&mut self, block: BasicBlock, scope: Option<region::Scope>, expr: M) pub fn as_rvalue<M>(
-> BlockAnd<Rvalue<'tcx>> &mut self,
where M: Mirror<'tcx, Output = Expr<'tcx>> block: BasicBlock,
scope: Option<region::Scope>,
expr: M,
) -> BlockAnd<Rvalue<'tcx>>
where
M: Mirror<'tcx, Output = Expr<'tcx>>,
{ {
let expr = self.hir.mirror(expr); let expr = self.hir.mirror(expr);
self.expr_as_rvalue(block, scope, expr) self.expr_as_rvalue(block, scope, expr)
} }
fn expr_as_rvalue(&mut self, fn expr_as_rvalue(
mut block: BasicBlock, &mut self,
scope: Option<region::Scope>, mut block: BasicBlock,
expr: Expr<'tcx>) scope: Option<region::Scope>,
-> BlockAnd<Rvalue<'tcx>> { expr: Expr<'tcx>,
debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr); ) -> BlockAnd<Rvalue<'tcx>> {
debug!(
"expr_as_rvalue(block={:?}, scope={:?}, expr={:?})",
block, scope, expr
);
let this = self; let this = self;
let expr_span = expr.span; let expr_span = expr.span;
let source_info = this.source_info(expr_span); let source_info = this.source_info(expr_span);
match expr.kind { match expr.kind {
ExprKind::Scope { region_scope, lint_level, value } => { ExprKind::Scope {
region_scope,
lint_level,
value,
} => {
let region_scope = (region_scope, source_info); let region_scope = (region_scope, source_info);
this.in_scope(region_scope, lint_level, block, this.in_scope(region_scope, lint_level, block, |this| {
|this| this.as_rvalue(block, scope, value)) this.as_rvalue(block, scope, value)
})
} }
ExprKind::Repeat { value, count } => { ExprKind::Repeat { value, count } => {
let value_operand = unpack!(block = this.as_operand(block, scope, value)); let value_operand = unpack!(block = this.as_operand(block, scope, value));
block.and(Rvalue::Repeat(value_operand, count)) block.and(Rvalue::Repeat(value_operand, count))
} }
ExprKind::Borrow { region, borrow_kind, arg } => { ExprKind::Borrow {
let arg_place = unpack!(block = this.as_place(block, arg)); 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)) block.and(Rvalue::Ref(region, borrow_kind, arg_place))
} }
ExprKind::Binary { op, lhs, rhs } => { ExprKind::Binary { op, lhs, rhs } => {
let lhs = unpack!(block = this.as_operand(block, scope, lhs)); let lhs = unpack!(block = this.as_operand(block, scope, lhs));
let rhs = unpack!(block = this.as_operand(block, scope, rhs)); let rhs = unpack!(block = this.as_operand(block, scope, rhs));
this.build_binary_op(block, op, expr_span, expr.ty, this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs)
lhs, rhs)
} }
ExprKind::Unary { op, arg } => { ExprKind::Unary { op, arg } => {
let arg = unpack!(block = this.as_operand(block, scope, 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 minval = this.minval_literal(expr_span, expr.ty);
let is_min = this.temp(bool_ty, expr_span); let is_min = this.temp(bool_ty, expr_span);
this.cfg.push_assign(block, source_info, &is_min, this.cfg.push_assign(
Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval)); block,
source_info,
&is_min,
Rvalue::BinaryOp(BinOp::Eq, arg.to_copy(), minval),
);
block = this.assert(block, Operand::Move(is_min), false, block = this.assert(
EvalErrorKind::OverflowNeg, expr_span); block,
Operand::Move(is_min),
false,
EvalErrorKind::OverflowNeg,
expr_span,
);
} }
block.and(Rvalue::UnaryOp(op, arg)) block.and(Rvalue::UnaryOp(op, arg))
} }
@ -94,22 +123,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// The `Box<T>` temporary created here is not a part of the HIR, // The `Box<T>` temporary created here is not a part of the HIR,
// and therefore is not considered during generator OIBIT // and therefore is not considered during generator OIBIT
// determination. See the comment about `box` at `yield_in_scope`. // determination. See the comment about `box` at `yield_in_scope`.
let result = this.local_decls.push( let result = this
LocalDecl::new_internal(expr.ty, expr_span)); .local_decls
this.cfg.push(block, Statement { .push(LocalDecl::new_internal(expr.ty, expr_span));
source_info, this.cfg.push(
kind: StatementKind::StorageLive(result) block,
}); Statement {
source_info,
kind: StatementKind::StorageLive(result),
},
);
if let Some(scope) = scope { if let Some(scope) = scope {
// schedule a shallow free of that memory, lest we unwind: // schedule a shallow free of that memory, lest we unwind:
this.schedule_drop_storage_and_value( 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): // malloc some memory of suitable type (thus far, uninitialized):
let box_ = Rvalue::NullaryOp(NullOp::Box, value.ty); 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: // initialize the box contents:
unpack!(block = this.into(&Place::Local(result).deref(), block, value)); 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 // first process the set of fields
let el_ty = expr.ty.sequence_element_type(this.hir.tcx()); let el_ty = expr.ty.sequence_element_type(this.hir.tcx());
let fields: Vec<_> = let fields: Vec<_> = fields
fields.into_iter() .into_iter()
.map(|f| unpack!(block = this.as_operand(block, scope, f))) .map(|f| unpack!(block = this.as_operand(block, scope, f)))
.collect(); .collect();
block.and(Rvalue::Aggregate(box AggregateKind::Array(el_ty), fields)) 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 // first process the set of fields
let fields: Vec<_> = let fields: Vec<_> = fields
fields.into_iter() .into_iter()
.map(|f| unpack!(block = this.as_operand(block, scope, f))) .map(|f| unpack!(block = this.as_operand(block, scope, f)))
.collect(); .collect();
block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields)) block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields))
} }
ExprKind::Closure { closure_id, substs, upvars, movability } => { ExprKind::Closure {
closure_id,
substs,
upvars,
movability,
} => {
// see (*) above // see (*) above
let mut operands: Vec<_> = upvars let mut operands: Vec<_> = upvars
.into_iter() .into_iter()
@ -212,25 +255,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// that caused the capture will cause an error. // that caused the capture will cause an error.
match upvar.kind { match upvar.kind {
ExprKind::Borrow { ExprKind::Borrow {
borrow_kind: BorrowKind::Mut { borrow_kind:
allow_two_phase_borrow: false BorrowKind::Mut {
}, allow_two_phase_borrow: false,
},
region, region,
arg, arg,
} => unpack!(block = this.limit_capture_mutability( } => unpack!(
upvar.span, block = this.limit_capture_mutability(
upvar.ty, upvar.span, upvar.ty, scope, block, arg, region,
scope, )
block, ),
arg,
region,
)),
_ => unpack!(block = this.as_operand(block, scope, upvar)), _ => unpack!(block = this.as_operand(block, scope, upvar)),
} }
} }
} }
}) }).collect();
.collect();
let result = match substs { let result = match substs {
UpvarSubsts::Generator(substs) => { UpvarSubsts::Generator(substs) => {
let movability = movability.unwrap(); let movability = movability.unwrap();
@ -248,23 +288,36 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
})); }));
box AggregateKind::Generator(closure_id, substs, movability) box AggregateKind::Generator(closure_id, substs, movability)
} }
UpvarSubsts::Closure(substs) => { UpvarSubsts::Closure(substs) => box AggregateKind::Closure(closure_id, substs),
box AggregateKind::Closure(closure_id, substs)
}
}; };
block.and(Rvalue::Aggregate(result, operands)) block.and(Rvalue::Aggregate(result, operands))
} }
ExprKind::Adt { ExprKind::Adt {
adt_def, variant_index, substs, user_ty, fields, base adt_def,
} => { // see (*) above variant_index,
substs,
user_ty,
fields,
base,
} => {
// see (*) above
let is_union = adt_def.is_union(); 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 // first process the set of fields that were provided
// (evaluating them in order given by user) // (evaluating them in order given by user)
let fields_map: FxHashMap<_, _> = fields.into_iter() let fields_map: FxHashMap<_, _> = fields
.map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr)))) .into_iter()
.collect(); .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); 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 // MIR does not natively support FRU, so for each
// base-supplied field, generate an operand that // base-supplied field, generate an operand that
// reads it from the base. // reads it from the base.
field_names.into_iter() field_names
.into_iter()
.zip(field_types.into_iter()) .zip(field_types.into_iter())
.map(|(n, ty)| match fields_map.get(&n) { .map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(), Some(v) => v.clone(),
None => this.consume_by_copy_or_move(base.clone().field(n, ty)) None => this.consume_by_copy_or_move(base.clone().field(n, ty)),
}) }).collect()
.collect()
} else { } 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( let adt = box AggregateKind::Adt(
@ -294,8 +350,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
); );
block.and(Rvalue::Aggregate(adt, fields)) block.and(Rvalue::Aggregate(adt, fields))
} }
ExprKind::Assign { .. } | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
ExprKind::AssignOp { .. } => {
block = unpack!(this.stmt_expr(block, expr)); block = unpack!(this.stmt_expr(block, expr));
block.and(this.unit_rvalue()) 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 value = unpack!(block = this.as_operand(block, scope, value));
let resume = this.cfg.start_new_block(); let resume = this.cfg.start_new_block();
let cleanup = this.generator_drop_cleanup(); let cleanup = this.generator_drop_cleanup();
this.cfg.terminate(block, source_info, TerminatorKind::Yield { this.cfg.terminate(
value: value, block,
resume: resume, source_info,
drop: cleanup, TerminatorKind::Yield {
}); value: value,
resume: resume,
drop: cleanup,
},
);
resume.and(this.unit_rvalue()) resume.and(this.unit_rvalue())
} }
ExprKind::Literal { .. } | ExprKind::Literal { .. }
ExprKind::Block { .. } | | ExprKind::Block { .. }
ExprKind::Match { .. } | | ExprKind::Match { .. }
ExprKind::If { .. } | | ExprKind::If { .. }
ExprKind::NeverToAny { .. } | | ExprKind::NeverToAny { .. }
ExprKind::Loop { .. } | | ExprKind::Loop { .. }
ExprKind::LogicalOp { .. } | | ExprKind::LogicalOp { .. }
ExprKind::Call { .. } | | ExprKind::Call { .. }
ExprKind::Field { .. } | | ExprKind::Field { .. }
ExprKind::Deref { .. } | | ExprKind::Deref { .. }
ExprKind::Index { .. } | | ExprKind::Index { .. }
ExprKind::VarRef { .. } | | ExprKind::VarRef { .. }
ExprKind::SelfRef | | ExprKind::SelfRef
ExprKind::Break { .. } | | ExprKind::Break { .. }
ExprKind::Continue { .. } | | ExprKind::Continue { .. }
ExprKind::Return { .. } | | ExprKind::Return { .. }
ExprKind::InlineAsm { .. } | | ExprKind::InlineAsm { .. }
ExprKind::StaticRef { .. } => { | ExprKind::StaticRef { .. } => {
// these do not have corresponding `Rvalue` variants, // these do not have corresponding `Rvalue` variants,
// so make an operand and then return that // so make an operand and then return that
debug_assert!(match Category::of(&expr.kind) { 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, pub fn build_binary_op(
op: BinOp, span: Span, ty: Ty<'tcx>, &mut self,
lhs: Operand<'tcx>, rhs: Operand<'tcx>) -> BlockAnd<Rvalue<'tcx>> { mut block: BasicBlock,
op: BinOp,
span: Span,
ty: Ty<'tcx>,
lhs: Operand<'tcx>,
rhs: Operand<'tcx>,
) -> BlockAnd<Rvalue<'tcx>> {
let source_info = self.source_info(span); let source_info = self.source_info(span);
let bool_ty = self.hir.bool_ty(); let bool_ty = self.hir.bool_ty();
if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() { if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() {
let result_tup = self.hir.tcx().intern_tup(&[ty, bool_ty]); let result_tup = self.hir.tcx().intern_tup(&[ty, bool_ty]);
let result_value = self.temp(result_tup, span); let result_value = self.temp(result_tup, span);
self.cfg.push_assign(block, source_info, self.cfg.push_assign(
&result_value, Rvalue::CheckedBinaryOp(op, block,
lhs, source_info,
rhs)); &result_value,
Rvalue::CheckedBinaryOp(op, lhs, rhs),
);
let val_fld = Field::new(0); let val_fld = Field::new(0);
let of_fld = Field::new(1); let of_fld = Field::new(1);
@ -361,8 +428,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let err = EvalErrorKind::Overflow(op); let err = EvalErrorKind::Overflow(op);
block = self.assert(block, Operand::Move(of), false, block = self.assert(block, Operand::Move(of), false, err, span);
err, span);
block.and(Rvalue::Use(Operand::Move(val))) block.and(Rvalue::Use(Operand::Move(val)))
} else { } 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. // and 2. there are two possible failure cases, divide-by-zero and overflow.
let (zero_err, overflow_err) = if op == BinOp::Div { let (zero_err, overflow_err) = if op == BinOp::Div {
(EvalErrorKind::DivisionByZero, (EvalErrorKind::DivisionByZero, EvalErrorKind::Overflow(op))
EvalErrorKind::Overflow(op))
} else { } else {
(EvalErrorKind::RemainderByZero, (EvalErrorKind::RemainderByZero, EvalErrorKind::Overflow(op))
EvalErrorKind::Overflow(op))
}; };
// Check for / 0 // Check for / 0
let is_zero = self.temp(bool_ty, span); let is_zero = self.temp(bool_ty, span);
let zero = self.zero_literal(span, ty); let zero = self.zero_literal(span, ty);
self.cfg.push_assign(block, source_info, &is_zero, self.cfg.push_assign(
Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero)); block,
source_info,
&is_zero,
Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), zero),
);
block = self.assert(block, Operand::Move(is_zero), false, block = self.assert(block, Operand::Move(is_zero), false, zero_err, span);
zero_err, span);
// We only need to check for the overflow in one case: // We only need to check for the overflow in one case:
// MIN / -1, and only for signed values. // 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 min = self.minval_literal(span, ty);
let is_neg_1 = self.temp(bool_ty, span); let is_neg_1 = self.temp(bool_ty, span);
let is_min = self.temp(bool_ty, span); let is_min = self.temp(bool_ty, span);
let of = self.temp(bool_ty, span); let of = self.temp(bool_ty, span);
// this does (rhs == -1) & (lhs == MIN). It could short-circuit instead // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead
self.cfg.push_assign(block, source_info, &is_neg_1, self.cfg.push_assign(
Rvalue::BinaryOp(BinOp::Eq, rhs.to_copy(), neg_1)); block,
self.cfg.push_assign(block, source_info, &is_min, source_info,
Rvalue::BinaryOp(BinOp::Eq, lhs.to_copy(), min)); &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_neg_1 = Operand::Move(is_neg_1);
let is_min = Operand::Move(is_min); let is_min = Operand::Move(is_min);
self.cfg.push_assign(block, source_info, &of, self.cfg.push_assign(
Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min)); block,
source_info,
&of,
Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min),
);
block = self.assert(block, Operand::Move(of), false, block = self.assert(block, Operand::Move(of), false, overflow_err, span);
overflow_err, span);
} }
} }
@ -430,12 +508,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let this = self; let this = self;
let source_info = this.source_info(upvar_span); 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 { this.cfg.push(
source_info, block,
kind: StatementKind::StorageLive(temp) Statement {
}); source_info,
kind: StatementKind::StorageLive(temp),
},
);
let arg_place = unpack!(block = this.as_place(block, arg)); 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, elem: ProjectionElem::Deref,
}) => { }) => {
debug_assert!( debug_assert!(
if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
= this.local_decls[local].is_user_variable { this.local_decls[local].is_user_variable
{
true true
} else { } else {
false false
@ -461,10 +545,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
elem: ProjectionElem::Field(upvar_index, _), elem: ProjectionElem::Field(upvar_index, _),
}) })
| Place::Projection(box Projection { | Place::Projection(box Projection {
base: Place::Projection(box Projection { base:
ref base, Place::Projection(box Projection {
elem: ProjectionElem::Field(upvar_index, _), ref base,
}), elem: ProjectionElem::Field(upvar_index, _),
}),
elem: ProjectionElem::Deref, elem: ProjectionElem::Deref,
}) => { }) => {
// Not projected from the implicit `self` in a closure. // 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 { let borrow_kind = match mutability {
Mutability::Not => BorrowKind::Unique, 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( 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. // a constant at this time, even if the type may need dropping.
if let Some(temp_lifetime) = temp_lifetime { if let Some(temp_lifetime) = temp_lifetime {
this.schedule_drop_storage_and_value( this.schedule_drop_storage_and_value(
upvar_span, temp_lifetime, &Place::Local(temp), upvar_ty, upvar_span,
temp_lifetime,
&Place::Local(temp),
upvar_ty,
); );
} }

View file

@ -18,42 +18,63 @@ use rustc::mir::*;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// Compile `expr` into a fresh temporary. This is used when building /// Compile `expr` into a fresh temporary. This is used when building
/// up rvalues so as to freeze the value that will be consumed. /// up rvalues so as to freeze the value that will be consumed.
pub fn as_temp<M>(&mut self, pub fn as_temp<M>(
block: BasicBlock, &mut self,
temp_lifetime: Option<region::Scope>, block: BasicBlock,
expr: M) temp_lifetime: Option<region::Scope>,
-> BlockAnd<Local> expr: M,
where M: Mirror<'tcx, Output = Expr<'tcx>> mutability: Mutability,
) -> BlockAnd<Local>
where
M: Mirror<'tcx, Output = Expr<'tcx>>,
{ {
let expr = self.hir.mirror(expr); 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, fn expr_as_temp(
mut block: BasicBlock, &mut self,
temp_lifetime: Option<region::Scope>, mut block: BasicBlock,
expr: Expr<'tcx>) temp_lifetime: Option<region::Scope>,
-> BlockAnd<Local> { expr: Expr<'tcx>,
debug!("expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?})", mutability: Mutability,
block, temp_lifetime, expr); ) -> BlockAnd<Local> {
debug!(
"expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
block, temp_lifetime, expr, mutability
);
let this = self; let this = self;
let expr_span = expr.span; let expr_span = expr.span;
let source_info = this.source_info(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| { 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 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() { if !expr_ty.is_never() {
this.cfg.push(block, Statement { this.cfg.push(
source_info, block,
kind: StatementKind::StorageLive(temp) Statement {
}); source_info,
kind: StatementKind::StorageLive(temp),
},
);
} }
unpack!(block = this.into(&Place::Local(temp), block, expr)); 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. // a constant at this time, even if the type may need dropping.
if let Some(temp_lifetime) = temp_lifetime { if let Some(temp_lifetime) = temp_lifetime {
this.schedule_drop_storage_and_value( this.schedule_drop_storage_and_value(
expr_span, temp_lifetime, &Place::Local(temp), expr_ty, expr_span,
temp_lifetime,
&Place::Local(temp),
expr_ty,
); );
} }

View file

@ -45,53 +45,51 @@ impl Category {
match *ek { match *ek {
ExprKind::Scope { .. } => None, ExprKind::Scope { .. } => None,
ExprKind::Field { .. } | ExprKind::Field { .. }
ExprKind::Deref { .. } | | ExprKind::Deref { .. }
ExprKind::Index { .. } | | ExprKind::Index { .. }
ExprKind::SelfRef | | ExprKind::SelfRef
ExprKind::VarRef { .. } | | ExprKind::VarRef { .. }
ExprKind::StaticRef { .. } => | ExprKind::StaticRef { .. } => Some(Category::Place),
Some(Category::Place),
ExprKind::LogicalOp { .. } | ExprKind::LogicalOp { .. }
ExprKind::If { .. } | | ExprKind::If { .. }
ExprKind::Match { .. } | | ExprKind::Match { .. }
ExprKind::NeverToAny { .. } | | ExprKind::NeverToAny { .. }
ExprKind::Call { .. } => | ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
Some(Category::Rvalue(RvalueFunc::Into)),
ExprKind::Array { .. } | ExprKind::Array { .. }
ExprKind::Tuple { .. } | | ExprKind::Tuple { .. }
ExprKind::Adt { .. } | | ExprKind::Adt { .. }
ExprKind::Closure { .. } | | ExprKind::Closure { .. }
ExprKind::Unary { .. } | | ExprKind::Unary { .. }
ExprKind::Binary { .. } | | ExprKind::Binary { .. }
ExprKind::Box { .. } | | ExprKind::Box { .. }
ExprKind::Cast { .. } | | ExprKind::Cast { .. }
ExprKind::Use { .. } | | ExprKind::Use { .. }
ExprKind::ReifyFnPointer { .. } | | ExprKind::ReifyFnPointer { .. }
ExprKind::ClosureFnPointer { .. } | | ExprKind::ClosureFnPointer { .. }
ExprKind::UnsafeFnPointer { .. } | | ExprKind::UnsafeFnPointer { .. }
ExprKind::Unsize { .. } | | ExprKind::Unsize { .. }
ExprKind::Repeat { .. } | | ExprKind::Repeat { .. }
ExprKind::Borrow { .. } | | ExprKind::Borrow { .. }
ExprKind::Assign { .. } | | ExprKind::Assign { .. }
ExprKind::AssignOp { .. } | | ExprKind::AssignOp { .. }
ExprKind::Yield { .. } | | ExprKind::Yield { .. }
ExprKind::InlineAsm { .. } => | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
Some(Category::Rvalue(RvalueFunc::AsRvalue)),
ExprKind::Literal { .. } => ExprKind::Literal { .. } => Some(Category::Constant),
Some(Category::Constant),
ExprKind::Loop { .. } | ExprKind::Loop { .. }
ExprKind::Block { .. } | | ExprKind::Block { .. }
ExprKind::Break { .. } | | ExprKind::Break { .. }
ExprKind::Continue { .. } | | ExprKind::Continue { .. }
ExprKind::Return { .. } => | ExprKind::Return { .. } =>
// FIXME(#27840) these probably want their own // FIXME(#27840) these probably want their own
// category, like "nonterminating" // category, like "nonterminating"
Some(Category::Rvalue(RvalueFunc::Into)), {
Some(Category::Rvalue(RvalueFunc::Into))
}
} }
} }
} }

View file

@ -10,25 +10,27 @@
//! See docs in build/expr/mod.rs //! See docs in build/expr/mod.rs
use build::{BlockAnd, BlockAndExtension, Builder};
use build::expr::category::{Category, RvalueFunc}; use build::expr::category::{Category, RvalueFunc};
use build::{BlockAnd, BlockAndExtension, Builder};
use hair::*; use hair::*;
use rustc::ty;
use rustc::mir::*; use rustc::mir::*;
use rustc::ty;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// Compile `expr`, storing the result into `destination`, which /// Compile `expr`, storing the result into `destination`, which
/// is assumed to be uninitialized. /// is assumed to be uninitialized.
pub fn into_expr(&mut self, pub fn into_expr(
destination: &Place<'tcx>, &mut self,
mut block: BasicBlock, destination: &Place<'tcx>,
expr: Expr<'tcx>) mut block: BasicBlock,
-> BlockAnd<()> expr: Expr<'tcx>,
{ ) -> BlockAnd<()> {
debug!("into_expr(destination={:?}, block={:?}, expr={:?})", debug!(
destination, block, expr); "into_expr(destination={:?}, block={:?}, expr={:?})",
destination, block, expr
);
// since we frequently have to reference `self` from within a // since we frequently have to reference `self` from within a
// closure, where `self` would be shadowed, it's easier to // 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); let source_info = this.source_info(expr_span);
match expr.kind { match expr.kind {
ExprKind::Scope { region_scope, lint_level, value } => { ExprKind::Scope {
region_scope,
lint_level,
value,
} => {
let region_scope = (region_scope, source_info); let region_scope = (region_scope, source_info);
this.in_scope(region_scope, lint_level, block, this.in_scope(region_scope, lint_level, block, |this| {
|this| this.into(destination, block, value)) this.into(destination, block, value)
})
} }
ExprKind::Block { body: ast_block } => { ExprKind::Block { body: ast_block } => {
this.ast_block(destination, block, ast_block, source_info) this.ast_block(destination, block, ast_block, source_info)
@ -63,12 +70,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if is_call { if is_call {
block.unit() block.unit()
} else { } 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(); let end_block = this.cfg.start_new_block();
end_block.unit() 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 operand = unpack!(block = this.as_local_operand(block, cond_expr));
let mut then_block = this.cfg.start_new_block(); let mut then_block = this.cfg.start_new_block();
@ -82,15 +94,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} else { } else {
// Body of the `if` expression without an `else` clause must return `()`, thus // Body of the `if` expression without an `else` clause must return `()`, thus
// we implicitly generate a `else {}` if it is not specified. // 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 else_block
}; };
let join_block = this.cfg.start_new_block(); let join_block = this.cfg.start_new_block();
this.cfg.terminate(then_block, source_info, this.cfg.terminate(
TerminatorKind::Goto { target: join_block }); then_block,
this.cfg.terminate(else_block, source_info, source_info,
TerminatorKind::Goto { target: join_block }); TerminatorKind::Goto { target: join_block },
);
this.cfg.terminate(
else_block,
source_info,
TerminatorKind::Goto { target: join_block },
);
join_block.unit() join_block.unit()
} }
@ -107,9 +126,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// | (true) | (false) // | (true) | (false)
// [true_block] [false_block] // [true_block] [false_block]
let (true_block, false_block, mut else_block, join_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(), 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 lhs = unpack!(block = this.as_local_operand(block, lhs));
let blocks = match op { 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.terminate(else_block, source_info, term);
this.cfg.push_assign_constant( this.cfg.push_assign_constant(
true_block, source_info, destination, true_block,
source_info,
destination,
Constant { Constant {
span: expr_span, span: expr_span,
ty: this.hir.bool_ty(), ty: this.hir.bool_ty(),
user_ty: None, user_ty: None,
literal: this.hir.true_literal(), literal: this.hir.true_literal(),
}); },
);
this.cfg.push_assign_constant( this.cfg.push_assign_constant(
false_block, source_info, destination, false_block,
source_info,
destination,
Constant { Constant {
span: expr_span, span: expr_span,
ty: this.hir.bool_ty(), ty: this.hir.bool_ty(),
user_ty: None, user_ty: None,
literal: this.hir.false_literal(), literal: this.hir.false_literal(),
}); },
);
this.cfg.terminate(true_block, source_info, this.cfg.terminate(
TerminatorKind::Goto { target: join_block }); true_block,
this.cfg.terminate(false_block, source_info, source_info,
TerminatorKind::Goto { target: join_block }); TerminatorKind::Goto { target: join_block },
);
this.cfg.terminate(
false_block,
source_info,
TerminatorKind::Goto { target: join_block },
);
join_block.unit() 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] // [block] --> [loop_block] -/eval. cond./-> [loop_block_end] -1-> [exit_block]
// ^ | // ^ |
// | 0 // | 0
@ -172,35 +209,45 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let exit_block = this.cfg.start_new_block(); let exit_block = this.cfg.start_new_block();
// start the loop // start the loop
this.cfg.terminate(block, source_info, this.cfg.terminate(
TerminatorKind::Goto { target: loop_block }); block,
source_info,
TerminatorKind::Goto { target: loop_block },
);
this.in_breakable_scope( this.in_breakable_scope(
Some(loop_block), exit_block, destination.clone(), Some(loop_block),
exit_block,
destination.clone(),
move |this| { move |this| {
// conduct the test, if necessary // conduct the test, if necessary
let body_block; let body_block;
if let Some(cond_expr) = opt_cond_expr { if let Some(cond_expr) = opt_cond_expr {
let loop_block_end; let loop_block_end;
let cond = unpack!( 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(); body_block = this.cfg.start_new_block();
let term = TerminatorKind::if_(this.hir.tcx(), cond, let term =
body_block, exit_block); TerminatorKind::if_(this.hir.tcx(), cond, body_block, exit_block);
this.cfg.terminate(loop_block_end, source_info, term); this.cfg.terminate(loop_block_end, source_info, term);
// if the test is false, there's no `break` to assign `destination`, so // 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 // we have to do it; this overwrites any `break`-assigned value but it's
// always `()` anyway // always `()` anyway
this.cfg.push_assign_unit(exit_block, source_info, destination); this.cfg
.push_assign_unit(exit_block, source_info, destination);
} else { } else {
body_block = this.cfg.start_new_block(); body_block = this.cfg.start_new_block();
let diverge_cleanup = this.diverge_cleanup(); let diverge_cleanup = this.diverge_cleanup();
this.cfg.terminate(loop_block, source_info, this.cfg.terminate(
TerminatorKind::FalseUnwind { loop_block,
real_target: body_block, source_info,
unwind: Some(diverge_cleanup) TerminatorKind::FalseUnwind {
}) real_target: body_block,
unwind: Some(diverge_cleanup),
},
)
} }
// The “return” value of the loop body must always be an unit. We therefore // 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(); let tmp = this.get_unit_temp();
// Execute the body, branching back to the test. // Execute the body, branching back to the test.
let body_block_end = unpack!(this.into(&tmp, body_block, body)); let body_block_end = unpack!(this.into(&tmp, body_block, body));
this.cfg.terminate(body_block_end, source_info, this.cfg.terminate(
TerminatorKind::Goto { target: loop_block }); body_block_end,
} source_info,
TerminatorKind::Goto { target: loop_block },
);
},
); );
exit_block.unit() 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 // FIXME(canndrew): This is_never should probably be an is_uninhabited
let diverges = expr.ty.is_never(); let diverges = expr.ty.is_never();
let intrinsic = match ty.sty { let intrinsic = match ty.sty {
ty::FnDef(def_id, _) => { ty::FnDef(def_id, _) => {
let f = ty.fn_sig(this.hir.tcx()); let f = ty.fn_sig(this.hir.tcx());
if f.abi() == Abi::RustIntrinsic || if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
f.abi() == Abi::PlatformIntrinsic {
Some(this.hir.tcx().item_name(def_id).as_str()) Some(this.hir.tcx().item_name(def_id).as_str())
} else { } else {
None None
} }
} }
_ => None _ => None,
}; };
let intrinsic = intrinsic.as_ref().map(|s| &s[..]); let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
let fun = unpack!(block = this.as_local_operand(block, fun)); 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)); let block = unpack!(this.into(&ptr_temp, block, ptr));
this.into(&ptr_temp.deref(), block, val) this.into(&ptr_temp.deref(), block, val)
} else { } else {
let args: Vec<_> = let args: Vec<_> = args
args.into_iter() .into_iter()
.map(|arg| unpack!(block = this.as_local_operand(block, arg))) .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
.collect(); .collect();
let success = this.cfg.start_new_block(); let success = this.cfg.start_new_block();
let cleanup = this.diverge_cleanup(); let cleanup = this.diverge_cleanup();
this.cfg.terminate(block, source_info, TerminatorKind::Call { this.cfg.terminate(
func: fun, block,
args, source_info,
cleanup: Some(cleanup), TerminatorKind::Call {
destination: if diverges { func: fun,
None args,
} else { cleanup: Some(cleanup),
Some ((destination.clone(), success)) destination: if diverges {
} None
}); } else {
Some((destination.clone(), success))
},
},
);
success.unit() success.unit()
} }
} }
// These cases don't actually need a destination // These cases don't actually need a destination
ExprKind::Assign { .. } | ExprKind::Assign { .. }
ExprKind::AssignOp { .. } | | ExprKind::AssignOp { .. }
ExprKind::Continue { .. } | | ExprKind::Continue { .. }
ExprKind::Break { .. } | | ExprKind::Break { .. }
ExprKind::InlineAsm { .. } | | ExprKind::InlineAsm { .. }
ExprKind::Return { .. } => { | ExprKind::Return { .. } => {
unpack!(block = this.stmt_expr(block, expr)); unpack!(block = this.stmt_expr(block, expr));
this.cfg.push_assign_unit(block, source_info, destination); this.cfg.push_assign_unit(block, source_info, destination);
block.unit() block.unit()
} }
// Avoid creating a temporary // Avoid creating a temporary
ExprKind::VarRef { .. } | ExprKind::VarRef { .. } | ExprKind::SelfRef | ExprKind::StaticRef { .. } => {
ExprKind::SelfRef |
ExprKind::StaticRef { .. } => {
debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
let place = unpack!(block = this.as_place(block, expr)); let place = unpack!(block = this.as_place(block, expr));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); 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() block.unit()
} }
ExprKind::Index { .. } | ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
ExprKind::Deref { .. } |
ExprKind::Field { .. } => {
debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
// Create a "fake" temporary variable so that we check that the // Create a "fake" temporary variable so that we check that the
// value is Sized. Usually, this is caught in type checking, but // value is Sized. Usually, this is caught in type checking, but
// in the case of box expr there is no such check. // in the case of box expr there is no such check.
if let Place::Projection(..) = destination { 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)); debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
let place = unpack!(block = this.as_place(block, expr)); let place = unpack!(block = this.as_place(block, expr));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); 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() block.unit()
} }
// these are the cases that are more naturally handled by some other mode // these are the cases that are more naturally handled by some other mode
ExprKind::Unary { .. } | ExprKind::Unary { .. }
ExprKind::Binary { .. } | | ExprKind::Binary { .. }
ExprKind::Box { .. } | | ExprKind::Box { .. }
ExprKind::Cast { .. } | | ExprKind::Cast { .. }
ExprKind::Use { .. } | | ExprKind::Use { .. }
ExprKind::ReifyFnPointer { .. } | | ExprKind::ReifyFnPointer { .. }
ExprKind::ClosureFnPointer { .. } | | ExprKind::ClosureFnPointer { .. }
ExprKind::UnsafeFnPointer { .. } | | ExprKind::UnsafeFnPointer { .. }
ExprKind::Unsize { .. } | | ExprKind::Unsize { .. }
ExprKind::Repeat { .. } | | ExprKind::Repeat { .. }
ExprKind::Borrow { .. } | | ExprKind::Borrow { .. }
ExprKind::Array { .. } | | ExprKind::Array { .. }
ExprKind::Tuple { .. } | | ExprKind::Tuple { .. }
ExprKind::Adt { .. } | | ExprKind::Adt { .. }
ExprKind::Closure { .. } | | ExprKind::Closure { .. }
ExprKind::Literal { .. } | | ExprKind::Literal { .. }
ExprKind::Yield { .. } => { | ExprKind::Yield { .. } => {
debug_assert!(match Category::of(&expr.kind).unwrap() { debug_assert!(match Category::of(&expr.kind).unwrap() {
Category::Rvalue(RvalueFunc::Into) => false, Category::Rvalue(RvalueFunc::Into) => false,
_ => true, _ => true,
}); });
let rvalue = unpack!(block = this.as_local_rvalue(block, expr)); 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() block.unit()
} }
} }

View file

@ -71,9 +71,9 @@
//! over to the "by reference" mode (`as_place`). //! over to the "by reference" mode (`as_place`).
mod as_constant; mod as_constant;
mod as_operand;
mod as_place; mod as_place;
mod as_rvalue; mod as_rvalue;
mod as_operand;
mod as_temp; mod as_temp;
mod category; mod category;
mod into; mod into;

View file

@ -8,13 +8,12 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use build::{BlockAnd, BlockAndExtension, Builder};
use build::scope::BreakableScope; use build::scope::BreakableScope;
use build::{BlockAnd, BlockAndExtension, Builder};
use hair::*; use hair::*;
use rustc::mir::*; use rustc::mir::*;
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<()> { pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<()> {
let this = self; let this = self;
let expr_span = expr.span; 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 // Handle a number of expressions that don't need a destination at all. This
// avoids needing a mountain of temporary `()` variables. // avoids needing a mountain of temporary `()` variables.
match expr.kind { match expr.kind {
ExprKind::Scope { region_scope, lint_level, value } => { ExprKind::Scope {
region_scope,
lint_level,
value,
} => {
let value = this.hir.mirror(value); let value = this.hir.mirror(value);
this.in_scope((region_scope, source_info), lint_level, block, |this| { this.in_scope((region_scope, source_info), lint_level, block, |this| {
this.stmt_expr(block, value) this.stmt_expr(block, value)
@ -42,9 +45,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if this.hir.needs_drop(lhs.ty) { if this.hir.needs_drop(lhs.ty) {
let rhs = unpack!(block = this.as_local_operand(block, rhs)); let rhs = unpack!(block = this.as_local_operand(block, rhs));
let lhs = unpack!(block = this.as_place(block, lhs)); let lhs = unpack!(block = this.as_place(block, lhs));
unpack!(block = this.build_drop_and_replace( unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs));
block, lhs_span, lhs, rhs
));
block.unit() block.unit()
} else { } else {
let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); 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 // we don't have to drop prior contents or anything
// because AssignOp is only legal for Copy types // because AssignOp is only legal for Copy types
// (overloaded ops should be desugared into a call). // (overloaded ops should be desugared into a call).
let result = unpack!(block = this.build_binary_op(block, op, expr_span, lhs_ty, let result = unpack!(
Operand::Copy(lhs.clone()), rhs)); 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); this.cfg.push_assign(block, source_info, &lhs, result);
block.unit() block.unit()
} }
ExprKind::Continue { label } => { ExprKind::Continue { label } => {
let BreakableScope { continue_block, region_scope, .. } = let BreakableScope {
*this.find_breakable_scope(expr_span, label); continue_block,
let continue_block = continue_block.expect( region_scope,
"Attempted to continue in non-continuable breakable block"); ..
this.exit_scope(expr_span, (region_scope, source_info), block, continue_block); } = *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() this.cfg.start_new_block().unit()
} }
ExprKind::Break { label, value } => { ExprKind::Break { label, value } => {
@ -106,13 +123,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} }
ExprKind::Return { value } => { ExprKind::Return { value } => {
block = match value { block = match value {
Some(value) => { Some(value) => unpack!(this.into(&Place::Local(RETURN_PLACE), block, value)),
unpack!(this.into(&Place::Local(RETURN_PLACE), block, value))
}
None => { None => {
this.cfg.push_assign_unit(block, this.cfg
source_info, .push_assign_unit(block, source_info, &Place::Local(RETURN_PLACE));
&Place::Local(RETURN_PLACE));
block 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.exit_scope(expr_span, (region_scope, source_info), block, return_block);
this.cfg.start_new_block().unit() this.cfg.start_new_block().unit()
} }
ExprKind::InlineAsm { asm, outputs, inputs } => { ExprKind::InlineAsm {
let outputs = outputs.into_iter().map(|output| { asm,
unpack!(block = this.as_place(block, output)) outputs,
}).collect(); inputs,
let inputs = inputs.into_iter().map(|input| { } => {
unpack!(block = this.as_local_operand(block, input)) let outputs = outputs
}).collect(); .into_iter()
this.cfg.push(block, Statement { .map(|output| unpack!(block = this.as_place(block, output)))
source_info, .collect();
kind: StatementKind::InlineAsm { let inputs = inputs
asm: box asm.clone(), .into_iter()
outputs, .map(|input| unpack!(block = this.as_local_operand(block, input)))
inputs, .collect();
this.cfg.push(
block,
Statement {
source_info,
kind: StatementKind::InlineAsm {
asm: box asm.clone(),
outputs,
inputs,
},
}, },
}); );
block.unit() block.unit()
} }
_ => { _ => {
@ -147,5 +170,4 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} }
} }
} }
} }

View file

@ -153,7 +153,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>, nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
def_id: DefId, def_id: DefId,
body_id: Option<hir::BodyId>, body_id: Option<hir::BodyId>,
borrow_set: &Rc<BorrowSet<'tcx>> borrow_set: &Rc<BorrowSet<'tcx>>,
) -> Self { ) -> Self {
let scope_tree = tcx.region_scope_tree(def_id); let scope_tree = tcx.region_scope_tree(def_id);
let root_scope = body_id.map(|body_id| { let root_scope = body_id.map(|body_id| {
@ -269,7 +269,13 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
// propagate_call_return method. // propagate_call_return method.
if let mir::Rvalue::Ref(region, _, ref place) = *rhs { 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(|| { let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
panic!("could not find BorrowIndex for location {:?}", location); panic!("could not find BorrowIndex for location {:?}", location);
}); });

View file

@ -334,4 +334,14 @@ impl<'a, 'gcx, 'tcx> MoveData<'tcx> {
-> Result<Self, (Self, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { -> Result<Self, (Self, Vec<(Place<'tcx>, MoveError<'tcx>)>)> {
builder::gather_moves(mir, 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<Local> {
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 }
}
}
} }

View file

@ -612,8 +612,9 @@ fn write_temp_decls(mir: &Mir, w: &mut dyn Write) -> io::Result<()> {
for temp in mir.temps_iter() { for temp in mir.temps_iter() {
writeln!( writeln!(
w, w,
"{}let mut {:?}: {};", "{}let {}{:?}: {};",
INDENT, INDENT,
if mir.local_decls[temp].mutability == Mutability::Mut {"mut "} else {""},
temp, temp,
mir.local_decls[temp].ty mir.local_decls[temp].ty
)?; )?;

View file

@ -70,10 +70,10 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> {
// let mut _2: D1<'12ds, '10s>; // let mut _2: D1<'12ds, '10s>;
// let mut _3: &'12ds S1; // let mut _3: &'12ds S1;
// let mut _4: &'12ds S1; // let mut _4: &'12ds S1;
// let mut _5: S1; // let _5: S1;
// let mut _6: &'10s S1; // let mut _6: &'10s S1;
// let mut _7: &'10s S1; // let mut _7: &'10s S1;
// let mut _8: S1; // let _8: S1;
// bb0: { // bb0: {
// StorageLive(_2); // StorageLive(_2);
// StorageLive(_3); // 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 _2: D1<'12ds, '10s>;
// let mut _3: &'12ds S1; // let mut _3: &'12ds S1;
// let mut _4: &'12ds S1; // let mut _4: &'12ds S1;
// let mut _5: S1; // let _5: S1;
// let mut _6: &'10s S1; // let mut _6: &'10s S1;
// let mut _7: &'10s S1; // let mut _7: &'10s S1;
// let mut _8: S1; // let _8: S1;
// bb0: { // bb0: {
// StorageLive(_2); // StorageLive(_2);
// StorageLive(_3); // StorageLive(_3);

View file

@ -47,11 +47,11 @@ fn main() {
// START rustc.XXX.mir_map.0.mir // START rustc.XXX.mir_map.0.mir
// let mut _0: &'static Foo; // let mut _0: &'static Foo;
// let mut _1: &'static Foo; // let mut _1: &'static Foo;
// let mut _2: Foo; // let _2: Foo;
// let mut _3: &'static [(u32, u32)]; // let mut _3: &'static [(u32, u32)];
// let mut _4: &'static [(u32, u32); 42]; // let mut _4: &'static [(u32, u32); 42];
// let mut _5: &'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 _7: (u32, u32);
// let mut _8: (u32, u32); // let mut _8: (u32, u32);
// let mut _9: (u32, u32); // let mut _9: (u32, u32);