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:
commit
0198a1ea45
18 changed files with 822 additions and 478 deletions
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
<, 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
|
||||||
|
<,
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue