Auto merge of #45538 - nikomatsakis:nll-liveness, r=pnkfelix

enable non-lexical lifetimes in the MIR borrow checker

This PR, joint work with @spastorino, fills out the NLL infrastructure and integrates it with the borrow checker. **Don't get too excited:** it includes still a number of hacks (the subtyping code is particularly hacky). However, it *does* kinda' work. =)

The final commit demonstrates this by including a test that -- with both the AST borrowck and MIR borrowck -- reports an error by default. But if you pass `-Znll`, you only get an error from the AST borrowck, demonstrating that the integration succeeds:

```
struct MyStruct {
    field: String
}

fn main() {
    let mut my_struct = MyStruct { field: format!("Hello") };

    let value = &my_struct.field;
    if value.is_empty() {
        my_struct.field.push_str("Hello, world!");
        //~^ ERROR cannot borrow (Ast)
    }
}
```
This commit is contained in:
bors 2017-11-01 18:14:13 +00:00
commit 2be4cc0402
43 changed files with 2106 additions and 626 deletions

View File

@ -158,7 +158,7 @@ pub struct BlockRemainder {
newtype_index!(FirstStatementIndex
{
DEBUG_NAME = "",
DEBUG_FORMAT = "{}",
MAX = SCOPE_DATA_REMAINDER_MAX,
});

View File

@ -417,7 +417,7 @@ pub enum BorrowKind {
newtype_index!(Local
{
DEBUG_NAME = "_",
DEBUG_FORMAT = "_{}",
const RETURN_POINTER = 0,
});
@ -553,7 +553,7 @@ pub struct UpvarDecl {
///////////////////////////////////////////////////////////////////////////
// BasicBlock
newtype_index!(BasicBlock { DEBUG_NAME = "bb" });
newtype_index!(BasicBlock { DEBUG_FORMAT = "bb{}" });
///////////////////////////////////////////////////////////////////////////
// BasicBlockData and Terminator
@ -1135,7 +1135,7 @@ pub type LvalueProjection<'tcx> = Projection<'tcx, Lvalue<'tcx>, Local, Ty<'tcx>
/// and the index is a local.
pub type LvalueElem<'tcx> = ProjectionElem<'tcx, Local, Ty<'tcx>>;
newtype_index!(Field { DEBUG_NAME = "field" });
newtype_index!(Field { DEBUG_FORMAT = "field[{}]" });
impl<'tcx> Lvalue<'tcx> {
pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> {
@ -1202,7 +1202,7 @@ impl<'tcx> Debug for Lvalue<'tcx> {
newtype_index!(VisibilityScope
{
DEBUG_NAME = "scope",
DEBUG_FORMAT = "scope[{}]",
const ARGUMENT_VISIBILITY_SCOPE = 0,
});
@ -1529,7 +1529,7 @@ pub struct Constant<'tcx> {
pub literal: Literal<'tcx>,
}
newtype_index!(Promoted { DEBUG_NAME = "promoted" });
newtype_index!(Promoted { DEBUG_FORMAT = "promoted[{}]" });
#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub enum Literal<'tcx> {
@ -1637,6 +1637,14 @@ impl fmt::Debug for Location {
}
impl Location {
/// Returns the location immediately after this one within the enclosing block.
///
/// Note that if this location represents a terminator, then the
/// resulting location would be out of bounds and invalid.
pub fn successor_within_block(&self) -> Location {
Location { block: self.block, statement_index: self.statement_index + 1 }
}
pub fn dominates(&self, other: &Location, dominators: &Dominators<BasicBlock>) -> bool {
if self.block == other.block {
self.statement_index <= other.statement_index

View File

@ -39,13 +39,13 @@ pub enum MirSource {
GeneratorDrop(NodeId),
}
impl<'a, 'tcx> MirSource {
pub fn from_local_def_id(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> MirSource {
impl<'a, 'gcx, 'tcx> MirSource {
pub fn from_local_def_id(tcx: TyCtxt<'a, 'gcx, 'tcx>, def_id: DefId) -> MirSource {
let id = tcx.hir.as_local_node_id(def_id).expect("mir source requires local def-id");
Self::from_node(tcx, id)
}
pub fn from_node(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: NodeId) -> MirSource {
pub fn from_node(tcx: TyCtxt<'a, 'gcx, 'tcx>, id: NodeId) -> MirSource {
use hir::*;
// Handle constants in enum discriminants, types, and repeat expressions.

View File

@ -218,6 +218,43 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
{
value.fold_with(&mut RegionFolder::new(self, skipped_regions, &mut f))
}
pub fn for_each_free_region<T,F>(self,
value: &T,
callback: F)
where F: FnMut(ty::Region<'tcx>),
T: TypeFoldable<'tcx>,
{
value.visit_with(&mut RegionVisitor { current_depth: 0, callback });
struct RegionVisitor<F> {
current_depth: u32,
callback: F,
}
impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor<F>
where F : FnMut(ty::Region<'tcx>)
{
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<T>) -> bool {
self.current_depth += 1;
t.skip_binder().visit_with(self);
self.current_depth -= 1;
false // keep visiting
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
match *r {
ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => {
/* ignore bound regions */
}
_ => (self.callback)(r),
}
false // keep visiting
}
}
}
}
/// Folds over the substructure of a type, visiting its component

View File

@ -484,7 +484,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
// 3. Where does old loan expire.
let previous_end_span =
old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree).end_point();
Some(old_loan.kill_scope.span(self.tcx(), &self.bccx.region_scope_tree)
.end_point());
let mut err = match (new_loan.kind, old_loan.kind) {
(ty::MutBorrow, ty::MutBorrow) =>

View File

@ -53,11 +53,19 @@ pub struct IdxSet<T: Idx> {
}
impl<T: Idx> fmt::Debug for IdxSetBuf<T> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) }
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
w.debug_list()
.entries(self.iter())
.finish()
}
}
impl<T: Idx> fmt::Debug for IdxSet<T> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) }
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
w.debug_list()
.entries(self.iter())
.finish()
}
}
impl<T: Idx> IdxSetBuf<T> {

View File

@ -47,7 +47,7 @@ macro_rules! newtype_index {
newtype_index!(
@type[$name]
@max[::std::u32::MAX]
@debug_name[unsafe {::std::intrinsics::type_name::<$name>() }]);
@debug_format["{}"]);
);
// Define any constants
@ -55,14 +55,14 @@ macro_rules! newtype_index {
newtype_index!(
@type[$name]
@max[::std::u32::MAX]
@debug_name[unsafe {::std::intrinsics::type_name::<$name>() }]
@debug_format["{}"]
$($tokens)+);
);
// ---- private rules ----
// Base case, user-defined constants (if any) have already been defined
(@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]) => (
(@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]) => (
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
RustcEncodable, RustcDecodable)]
pub struct $type(pub u32);
@ -79,40 +79,43 @@ macro_rules! newtype_index {
impl ::std::fmt::Debug for $type {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(fmt, "{}{}", $debug_name, self.0)
write!(fmt, $debug_format, self.0)
}
}
);
// Rewrite final without comma to one that includes comma
(@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]
(@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]
$name:ident = $constant:expr) => (
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $name = $constant,);
newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $name = $constant,);
);
// Rewrite final const without comma to one that includes comma
(@type[$type:ident] @max[$_max:expr] @debug_name[$debug_name:expr]
(@type[$type:ident] @max[$_max:expr] @debug_format[$debug_format:expr]
const $name:ident = $constant:expr) => (
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] const $name = $constant,);
newtype_index!(@type[$type]
@max[$max]
@debug_format[$debug_format]
const $name = $constant,);
);
// Replace existing default for max
(@type[$type:ident] @max[$_max:expr] @debug_name[$debug_name:expr]
(@type[$type:ident] @max[$_max:expr] @debug_format[$debug_format:expr]
MAX = $max:expr, $($tokens:tt)*) => (
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*);
newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*);
);
// Replace existing default for debug_name
(@type[$type:ident] @max[$max:expr] @debug_name[$_debug_name:expr]
DEBUG_NAME = $debug_name:expr, $($tokens:tt)*) => (
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*);
// Replace existing default for debug_format
(@type[$type:ident] @max[$max:expr] @debug_format[$_debug_format:expr]
DEBUG_FORMAT = $debug_format:expr, $($tokens:tt)*) => (
newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*);
);
// Assign a user-defined constant (as final param)
(@type[$type:ident] @max[$max:expr] @debug_name[$debug_name:expr]
(@type[$type:ident] @max[$max:expr] @debug_format[$debug_format:expr]
const $name:ident = $constant:expr, $($tokens:tt)*) => (
pub const $name: $type = $type($constant);
newtype_index!(@type[$type] @max[$max] @debug_name[$debug_name] $($tokens)*);
newtype_index!(@type[$type] @max[$max] @debug_format[$debug_format] $($tokens)*);
);
}

View File

@ -1007,7 +1007,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants);
passes.push_pass(MIR_VALIDATED, mir::transform::simplify::SimplifyCfg::new("qualify-consts"));
passes.push_pass(MIR_VALIDATED, mir::transform::nll::NLL);
// borrowck runs between MIR_VALIDATED and MIR_OPTIMIZED.

View File

@ -17,7 +17,8 @@ use rustc::ty::maps::Providers;
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue, Local};
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind};
use rustc::mir::transform::{MirSource};
use rustc::mir::transform::MirSource;
use transform::nll;
use rustc_data_structures::indexed_set::{self, IdxSetBuf};
use rustc_data_structures::indexed_vec::{Idx};
@ -46,93 +47,120 @@ pub fn provide(providers: &mut Providers) {
}
fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
let mir = tcx.mir_validated(def_id);
let input_mir = tcx.mir_validated(def_id);
let src = MirSource::from_local_def_id(tcx, def_id);
debug!("run query mir_borrowck: {}", tcx.node_path_str(src.item_id()));
let mir: &Mir<'tcx> = &mir.borrow();
if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.debugging_opts.borrowck_mir {
if {
!tcx.has_attr(def_id, "rustc_mir_borrowck") &&
!tcx.sess.opts.debugging_opts.borrowck_mir &&
!tcx.sess.opts.debugging_opts.nll
} {
return;
}
let id = src.item_id();
tcx.infer_ctxt().enter(|infcx| {
let input_mir: &Mir = &input_mir.borrow();
do_mir_borrowck(&infcx, input_mir, def_id, src);
});
debug!("mir_borrowck done");
}
fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
input_mir: &Mir<'gcx>,
def_id: DefId,
src: MirSource)
{
let tcx = infcx.tcx;
let attributes = tcx.get_attrs(def_id);
let param_env = tcx.param_env(def_id);
tcx.infer_ctxt().enter(|_infcx| {
let move_data = match MoveData::gather_moves(mir, tcx, param_env) {
Ok(move_data) => move_data,
Err((move_data, move_errors)) => {
for move_error in move_errors {
let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
MoveError::UnionMove { .. } =>
unimplemented!("dont know how to report union move errors yet."),
MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind),
};
let origin = Origin::Mir;
let mut err = match kind {
IllegalMoveOriginKind::Static =>
tcx.cannot_move_out_of(span, "static item", origin),
IllegalMoveOriginKind::BorrowedContent =>
tcx.cannot_move_out_of(span, "borrowed_content", origin),
IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } =>
tcx.cannot_move_out_of_interior_of_drop(span, ty, origin),
IllegalMoveOriginKind::InteriorOfSlice { elem_ty: ty, is_index } =>
tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
IllegalMoveOriginKind::InteriorOfArray { elem_ty: ty, is_index } =>
tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
};
err.emit();
}
move_data
let id = src.item_id();
let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx, param_env) {
Ok(move_data) => move_data,
Err((move_data, move_errors)) => {
for move_error in move_errors {
let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
MoveError::UnionMove { .. } =>
unimplemented!("dont know how to report union move errors yet."),
MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind),
};
let origin = Origin::Mir;
let mut err = match kind {
IllegalMoveOriginKind::Static =>
tcx.cannot_move_out_of(span, "static item", origin),
IllegalMoveOriginKind::BorrowedContent =>
tcx.cannot_move_out_of(span, "borrowed_content", origin),
IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } =>
tcx.cannot_move_out_of_interior_of_drop(span, ty, origin),
IllegalMoveOriginKind::InteriorOfSlice { elem_ty: ty, is_index } =>
tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
IllegalMoveOriginKind::InteriorOfArray { elem_ty: ty, is_index } =>
tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
};
err.emit();
}
};
let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
Borrows::new(tcx, mir),
|bd, i| bd.location(i));
let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
MaybeInitializedLvals::new(tcx, mir, &mdpe),
|bd, i| &bd.move_data().move_paths[i]);
let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
MaybeUninitializedLvals::new(tcx, mir, &mdpe),
|bd, i| &bd.move_data().move_paths[i]);
move_data
}
};
let mut mbcx = MirBorrowckCtxt {
tcx: tcx,
mir: mir,
node_id: id,
move_data: &mdpe.move_data,
param_env: param_env,
fake_infer_ctxt: &_infcx,
};
// Make our own copy of the MIR. This copy will be modified (in place) to
// contain non-lexical lifetimes. It will have a lifetime tied
// to the inference context.
let mut mir: Mir<'tcx> = input_mir.clone();
let mir = &mut mir;
let mut state = InProgress::new(flow_borrows,
flow_inits,
flow_uninits);
// If we are in non-lexical mode, compute the non-lexical lifetimes.
let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll {
None
} else {
Some(nll::compute_regions(infcx, src, mir))
};
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
});
let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
Borrows::new(tcx, mir, opt_regioncx.as_ref()),
|bd, i| bd.location(i));
let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
MaybeInitializedLvals::new(tcx, mir, &mdpe),
|bd, i| &bd.move_data().move_paths[i]);
let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
MaybeUninitializedLvals::new(tcx, mir, &mdpe),
|bd, i| &bd.move_data().move_paths[i]);
debug!("mir_borrowck done");
let mut mbcx = MirBorrowckCtxt {
tcx: tcx,
mir: mir,
node_id: id,
move_data: &mdpe.move_data,
param_env: param_env,
fake_infer_ctxt: &infcx,
};
let mut state = InProgress::new(flow_borrows,
flow_inits,
flow_uninits);
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
}
#[allow(dead_code)]
pub struct MirBorrowckCtxt<'c, 'b, 'a: 'b+'c, 'gcx: 'a+'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'gcx>,
mir: &'b Mir<'gcx>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'b Mir<'tcx>,
node_id: ast::NodeId,
move_data: &'b MoveData<'gcx>,
move_data: &'b MoveData<'tcx>,
param_env: ParamEnv<'tcx>,
fake_infer_ctxt: &'c InferCtxt<'c, 'gcx, 'tcx>,
}
// (forced to be `pub` due to its use as an associated type below.)
pub struct InProgress<'b, 'tcx: 'b> {
borrows: FlowInProgress<Borrows<'b, 'tcx>>,
inits: FlowInProgress<MaybeInitializedLvals<'b, 'tcx>>,
uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>>,
pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> {
borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
}
struct FlowInProgress<BD> where BD: BitDenotation {
@ -147,12 +175,12 @@ struct FlowInProgress<BD> where BD: BitDenotation {
// 2. loans made in overlapping scopes do not conflict
// 3. assignments do not affect things loaned out as immutable
// 4. moves do not affect things loaned out in any way
impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx>
for MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
{
type FlowState = InProgress<'b, 'gcx>;
type FlowState = InProgress<'b, 'gcx, 'tcx>;
fn mir(&self) -> &'b Mir<'gcx> { self.mir }
fn mir(&self) -> &'b Mir<'tcx> { self.mir }
fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
flow_state.each_flow(|b| b.reset_to_entry_of(bb),
@ -193,7 +221,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
fn visit_statement_entry(&mut self,
location: Location,
stmt: &Statement<'gcx>,
stmt: &Statement<'tcx>,
flow_state: &Self::FlowState) {
let summary = flow_state.summary();
debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location, stmt, summary);
@ -261,7 +289,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
fn visit_terminator_entry(&mut self,
location: Location,
term: &Terminator<'gcx>,
term: &Terminator<'tcx>,
flow_state: &Self::FlowState) {
let loc = location;
let summary = flow_state.summary();
@ -405,9 +433,9 @@ enum WriteKind {
impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
fn access_lvalue(&mut self,
context: Context,
lvalue_span: (&Lvalue<'gcx>, Span),
lvalue_span: (&Lvalue<'tcx>, Span),
kind: (ShallowOrDeep, ReadOrWrite),
flow_state: &InProgress<'b, 'gcx>) {
flow_state: &InProgress<'b, 'gcx, 'tcx>) {
// FIXME: also need to check permissions (e.g. reject mut
// borrow of immutable ref, moves through non-`Box`-ref)
let (sd, rw) = kind;
@ -425,8 +453,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
context, lvalue_span, borrow),
ReadKind::Borrow(bk) => {
let end_issued_loan_span =
flow_state.borrows.base_results.operator().region_span(
&borrow.region).end_point();
flow_state.borrows.base_results.operator().opt_region_end_span(
&borrow.region);
this.report_conflicting_borrow(
context, common_prefix, lvalue_span, bk,
&borrow, end_issued_loan_span)
@ -438,8 +466,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
match kind {
WriteKind::MutableBorrow(bk) => {
let end_issued_loan_span =
flow_state.borrows.base_results.operator().region_span(
&borrow.region).end_point();
flow_state.borrows.base_results.operator().opt_region_end_span(
&borrow.region);
this.report_conflicting_borrow(
context, common_prefix, lvalue_span, bk,
&borrow, end_issued_loan_span)
@ -460,10 +488,10 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
fn mutate_lvalue(&mut self,
context: Context,
lvalue_span: (&Lvalue<'gcx>, Span),
lvalue_span: (&Lvalue<'tcx>, Span),
kind: ShallowOrDeep,
mode: MutateMode,
flow_state: &InProgress<'b, 'gcx>) {
flow_state: &InProgress<'b, 'gcx, 'tcx>) {
// Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
match mode {
MutateMode::WriteAndRead => {
@ -482,9 +510,9 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
fn consume_rvalue(&mut self,
context: Context,
(rvalue, span): (&Rvalue<'gcx>, Span),
(rvalue, span): (&Rvalue<'tcx>, Span),
_location: Location,
flow_state: &InProgress<'b, 'gcx>) {
flow_state: &InProgress<'b, 'gcx, 'tcx>) {
match *rvalue {
Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => {
let access_kind = match bk {
@ -540,8 +568,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
fn consume_operand(&mut self,
context: Context,
consume_via_drop: ConsumeKind,
(operand, span): (&Operand<'gcx>, Span),
flow_state: &InProgress<'b, 'gcx>) {
(operand, span): (&Operand<'tcx>, Span),
flow_state: &InProgress<'b, 'gcx, 'tcx>) {
match *operand {
Operand::Consume(ref lvalue) => {
self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state)
@ -553,8 +581,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
fn consume_lvalue(&mut self,
context: Context,
consume_via_drop: ConsumeKind,
lvalue_span: (&Lvalue<'gcx>, Span),
flow_state: &InProgress<'b, 'gcx>) {
lvalue_span: (&Lvalue<'tcx>, Span),
flow_state: &InProgress<'b, 'gcx, 'tcx>) {
let lvalue = lvalue_span.0;
let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
let moves_by_default =
@ -584,8 +612,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
fn check_if_reassignment_to_immutable_state(&mut self,
context: Context,
(lvalue, span): (&Lvalue<'gcx>, Span),
flow_state: &InProgress<'b, 'gcx>) {
(lvalue, span): (&Lvalue<'tcx>, Span),
flow_state: &InProgress<'b, 'gcx, 'tcx>) {
let move_data = self.move_data;
// determine if this path has a non-mut owner (and thus needs checking).
@ -635,8 +663,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
fn check_if_path_is_moved(&mut self,
context: Context,
desired_action: &str,
lvalue_span: (&Lvalue<'gcx>, Span),
flow_state: &InProgress<'b, 'gcx>) {
lvalue_span: (&Lvalue<'tcx>, Span),
flow_state: &InProgress<'b, 'gcx, 'tcx>) {
// FIXME: analogous code in check_loans first maps `lvalue` to
// its base_path ... but is that what we want here?
let lvalue = self.base_path(lvalue_span.0);
@ -725,7 +753,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
/// An Err result includes a tag indicated why the search failed.
/// Currenly this can only occur if the lvalue is built off of a
/// static variable, as we do not track those in the MoveData.
fn move_path_closest_to(&mut self, lvalue: &Lvalue<'gcx>)
fn move_path_closest_to(&mut self, lvalue: &Lvalue<'tcx>)
-> Result<MovePathIndex, NoMovePathFound>
{
let mut last_prefix = lvalue;
@ -743,7 +771,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
}
fn move_path_for_lvalue(&mut self,
lvalue: &Lvalue<'gcx>)
lvalue: &Lvalue<'tcx>)
-> Option<MovePathIndex>
{
// If returns None, then there is no move path corresponding
@ -758,8 +786,8 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
fn check_if_assigned_path_is_moved(&mut self,
context: Context,
(lvalue, span): (&Lvalue<'gcx>, Span),
flow_state: &InProgress<'b, 'gcx>) {
(lvalue, span): (&Lvalue<'tcx>, Span),
flow_state: &InProgress<'b, 'gcx, 'tcx>) {
// recur down lvalue; dispatch to check_if_path_is_moved when necessary
let mut lvalue = lvalue;
loop {
@ -827,10 +855,10 @@ enum NoMovePathFound {
impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
fn each_borrow_involving_path<F>(&mut self,
_context: Context,
access_lvalue: (ShallowOrDeep, &Lvalue<'gcx>),
flow_state: &InProgress<'b, 'gcx>,
access_lvalue: (ShallowOrDeep, &Lvalue<'tcx>),
flow_state: &InProgress<'b, 'gcx, 'tcx>,
mut op: F)
where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'gcx>, &Lvalue) -> Control
where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Lvalue) -> Control
{
let (access, lvalue) = access_lvalue;
@ -928,9 +956,9 @@ mod prefixes {
}
pub(super) struct Prefixes<'c, 'tcx: 'c> {
pub(super) struct Prefixes<'c, 'gcx: 'tcx, 'tcx: 'c> {
mir: &'c Mir<'tcx>,
tcx: TyCtxt<'c, 'tcx, 'tcx>,
tcx: TyCtxt<'c, 'gcx, 'tcx>,
kind: PrefixSet,
next: Option<&'c Lvalue<'tcx>>,
}
@ -951,15 +979,15 @@ mod prefixes {
/// (inclusive) from longest to smallest, potentially
/// terminating the iteration early based on `kind`.
pub(super) fn prefixes<'d>(&self,
lvalue: &'d Lvalue<'gcx>,
lvalue: &'d Lvalue<'tcx>,
kind: PrefixSet)
-> Prefixes<'d, 'gcx> where 'b: 'd
-> Prefixes<'d, 'gcx, 'tcx> where 'b: 'd
{
Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx }
}
}
impl<'c, 'tcx> Iterator for Prefixes<'c, 'tcx> {
impl<'c, 'gcx, 'tcx> Iterator for Prefixes<'c, 'gcx, 'tcx> {
type Item = &'c Lvalue<'tcx>;
fn next(&mut self) -> Option<Self::Item> {
let mut cursor = match self.next {
@ -1101,7 +1129,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
(lvalue, span): (&Lvalue, Span),
gen_borrow_kind: BorrowKind,
issued_borrow: &BorrowData,
end_issued_loan_span: Span) {
end_issued_loan_span: Option<Span>) {
use self::prefixes::IsPrefixOf;
assert!(common_prefix.is_prefix_of(lvalue));
@ -1315,7 +1343,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
// moves out of a Box. They should be removed when/if we stop
// treating Box specially (e.g. when/if DerefMove is added...)
fn base_path<'d>(&self, lvalue: &'d Lvalue<'gcx>) -> &'d Lvalue<'gcx> {
fn base_path<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> &'d Lvalue<'tcx> {
//! Returns the base of the leftmost (deepest) dereference of an
//! Box in `lvalue`. If there is no dereference of an Box
//! in `lvalue`, then it just returns `lvalue` itself.
@ -1364,10 +1392,10 @@ impl ContextKind {
fn new(self, loc: Location) -> Context { Context { kind: self, loc: loc } }
}
impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
pub(super) fn new(borrows: DataflowResults<Borrows<'b, 'tcx>>,
inits: DataflowResults<MaybeInitializedLvals<'b, 'tcx>>,
uninits: DataflowResults<MaybeUninitializedLvals<'b, 'tcx>>)
impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
pub(super) fn new(borrows: DataflowResults<Borrows<'b, 'gcx, 'tcx>>,
inits: DataflowResults<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
uninits: DataflowResults<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>)
-> Self {
InProgress {
borrows: FlowInProgress::new(borrows),
@ -1380,9 +1408,9 @@ impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
mut xform_borrows: XB,
mut xform_inits: XI,
mut xform_uninits: XU) where
XB: FnMut(&mut FlowInProgress<Borrows<'b, 'tcx>>),
XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'tcx>>),
XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>>),
XB: FnMut(&mut FlowInProgress<Borrows<'b, 'gcx, 'tcx>>),
XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>),
XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>),
{
xform_borrows(&mut self.borrows);
xform_inits(&mut self.inits);
@ -1438,7 +1466,7 @@ impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
}
}
impl<'b, 'tcx> FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>> {
impl<'b, 'gcx, 'tcx> FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>> {
fn has_any_child_of(&self, mpi: MovePathIndex) -> Option<MovePathIndex> {
let move_data = self.base_results.operator().move_data();

View File

@ -240,10 +240,10 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
///////////////////////////////////////////////////////////////////////////
// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
pub fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
closure_expr_id: ast::NodeId,
body_id: hir::BodyId)
-> Ty<'tcx> {
pub fn closure_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
closure_expr_id: ast::NodeId,
body_id: hir::BodyId)
-> Ty<'tcx> {
let closure_expr_hir_id = tcx.hir.node_to_hir_id(closure_expr_id);
let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_hir_id);

View File

@ -58,9 +58,9 @@ pub fn move_path_children_matching<'tcx, F>(move_data: &MoveData<'tcx>,
/// is no need to maintain separate drop flags to track such state.
///
/// FIXME: we have to do something for moving slice patterns.
fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
lv: &mir::Lvalue<'tcx>) -> bool {
fn lvalue_contents_drop_state_cannot_differ<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
lv: &mir::Lvalue<'tcx>) -> bool {
let ty = lv.ty(mir, tcx).to_ty(tcx);
match ty.sty {
ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => {
@ -79,8 +79,8 @@ fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
}
}
pub(crate) fn on_lookup_result_bits<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub(crate) fn on_lookup_result_bits<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
lookup_result: LookupResult,
@ -97,16 +97,16 @@ pub(crate) fn on_lookup_result_bits<'a, 'tcx, F>(
}
}
pub(crate) fn on_all_children_bits<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub(crate) fn on_all_children_bits<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
move_path_index: MovePathIndex,
mut each_child: F)
where F: FnMut(MovePathIndex)
{
fn is_terminal_path<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
fn is_terminal_path<'a, 'gcx, 'tcx>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
path: MovePathIndex) -> bool
@ -115,8 +115,8 @@ pub(crate) fn on_all_children_bits<'a, 'tcx, F>(
tcx, mir, &move_data.move_paths[path].lvalue)
}
fn on_all_children_bits<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
fn on_all_children_bits<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
move_data: &MoveData<'tcx>,
move_path_index: MovePathIndex,
@ -138,10 +138,10 @@ pub(crate) fn on_all_children_bits<'a, 'tcx, F>(
on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child);
}
pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub(crate) fn on_all_drop_children_bits<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
ctxt: &MoveDataParamEnv<'tcx>,
ctxt: &MoveDataParamEnv<'gcx, 'tcx>,
path: MovePathIndex,
mut each_child: F)
where F: FnMut(MovePathIndex)
@ -151,7 +151,9 @@ pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>(
let ty = lvalue.ty(mir, tcx).to_ty(tcx);
debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, lvalue, ty);
if ty.needs_drop(tcx, ctxt.param_env) {
let gcx = tcx.global_tcx();
let erased_ty = gcx.lift(&tcx.erase_regions(&ty)).unwrap();
if erased_ty.needs_drop(gcx, ctxt.param_env) {
each_child(child);
} else {
debug!("on_all_drop_children_bits - skipping")
@ -159,10 +161,10 @@ pub(crate) fn on_all_drop_children_bits<'a, 'tcx, F>(
})
}
pub(crate) fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub(crate) fn drop_flag_effects_for_function_entry<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
ctxt: &MoveDataParamEnv<'tcx>,
ctxt: &MoveDataParamEnv<'gcx, 'tcx>,
mut callback: F)
where F: FnMut(MovePathIndex, DropFlagState)
{
@ -176,10 +178,10 @@ pub(crate) fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
}
}
pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub(crate) fn drop_flag_effects_for_location<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
ctxt: &MoveDataParamEnv<'tcx>,
ctxt: &MoveDataParamEnv<'gcx, 'tcx>,
loc: Location,
mut callback: F)
where F: FnMut(MovePathIndex, DropFlagState)
@ -196,7 +198,9 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>(
// don't move out of non-Copy things
let lvalue = &move_data.move_paths[path].lvalue;
let ty = lvalue.ty(mir, tcx).to_ty(tcx);
if !ty.moves_by_default(tcx, param_env, DUMMY_SP) {
let gcx = tcx.global_tcx();
let erased_ty = gcx.lift(&tcx.erase_regions(&ty)).unwrap();
if !erased_ty.moves_by_default(gcx, param_env, DUMMY_SP) {
continue;
}

View File

@ -21,6 +21,8 @@ use rustc_data_structures::indexed_vec::{IndexVec};
use dataflow::{BitDenotation, BlockSets, DataflowOperator};
pub use dataflow::indexes::BorrowIndex;
use transform::nll::region_infer::RegionInferenceContext;
use transform::nll::ToRegionIndex;
use syntax_pos::Span;
@ -29,13 +31,14 @@ use std::fmt;
// `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be
// uniquely identified in the MIR by the `Location` of the assigment
// statement in which it appears on the right hand side.
pub struct Borrows<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
region_span_map: FxHashMap<RegionKind, Span>,
nonlexical_regioncx: Option<&'a RegionInferenceContext>,
}
// temporarily allow some dead fields: `kind` and `region` will be
@ -63,8 +66,11 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
}
}
impl<'a, 'tcx> Borrows<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
nonlexical_regioncx: Option<&'a RegionInferenceContext>)
-> Self {
let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
location_map: FxHashMap(),
region_map: FxHashMap(),
@ -75,7 +81,8 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
borrows: visitor.idx_vec,
location_map: visitor.location_map,
region_map: visitor.region_map,
region_span_map: visitor.region_span_map};
region_span_map: visitor.region_span_map,
nonlexical_regioncx };
struct GatherBorrows<'tcx> {
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
@ -116,14 +123,34 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
&self.borrows[idx].location
}
pub fn region_span(&self, region: &Region) -> Span {
/// Returns the span for the "end point" given region. This will
/// return `None` if NLL is enabled, since that concept has no
/// meaning there. Otherwise, it should return some.
pub fn opt_region_end_span(&self, region: &Region) -> Option<Span> {
let opt_span = self.region_span_map.get(region);
assert!(opt_span.is_some(), "end region not found for {:?}", region);
*opt_span.unwrap()
assert!(self.nonlexical_regioncx.is_some() ||
opt_span.is_some(), "end region not found for {:?}", region);
opt_span.map(|s| s.end_point())
}
/// Add all borrows to the kill set, if those borrows are out of scope at `location`.
fn kill_loans_out_of_scope_at_location(&self,
sets: &mut BlockSets<BorrowIndex>,
location: Location) {
if let Some(regioncx) = self.nonlexical_regioncx {
for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
let borrow_region = regioncx.region_value(borrow_data.region.to_region_index());
if !borrow_region.may_contain(location) && location != borrow_data.location {
debug!("kill_loans_out_of_scope_at_location: kill{:?} \
location={:?} borrow_data={:?}", borrow_index, location, borrow_data);
sets.kill(&borrow_index);
}
}
}
}
}
impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
type Idx = BorrowIndex;
fn name() -> &'static str { "borrows" }
fn bits_per_block(&self) -> usize {
@ -146,6 +173,7 @@ impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
match stmt.kind {
mir::StatementKind::EndRegion(region_scope) => {
if let Some(borrow_indexes) = self.region_map.get(&ReScope(region_scope)) {
assert!(self.nonlexical_regioncx.is_none());
for idx in borrow_indexes { sets.kill(&idx); }
} else {
// (if there is no entry, then there are no borrows to be tracked)
@ -172,11 +200,14 @@ impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
mir::StatementKind::Nop => {}
}
self.kill_loans_out_of_scope_at_location(sets, location);
}
fn terminator_effect(&self,
_sets: &mut BlockSets<BorrowIndex>,
_location: Location) {
// no terminators start nor end region scopes.
sets: &mut BlockSets<BorrowIndex>,
location: Location) {
self.kill_loans_out_of_scope_at_location(sets, location);
}
fn propagate_call_return(&self,
@ -188,14 +219,14 @@ impl<'a, 'tcx> BitDenotation for Borrows<'a, 'tcx> {
}
}
impl<'a, 'tcx> BitwiseOperator for Borrows<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // union effects of preds when computing borrows
}
}
impl<'a, 'tcx> DataflowOperator for Borrows<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> DataflowOperator for Borrows<'a, 'gcx, 'tcx> {
#[inline]
fn bottom_value() -> bool {
false // bottom = no Rvalue::Refs are active by default

View File

@ -69,23 +69,23 @@ pub(super) mod borrows;
/// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeUninitializedLvals` yields the set of
/// l-values that would require a dynamic drop-flag at that statement.
pub struct MaybeInitializedLvals<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub struct MaybeInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
}
impl<'a, 'tcx: 'a> MaybeInitializedLvals<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'tcx>)
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> Self
{
MaybeInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
}
}
impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
}
@ -124,23 +124,23 @@ impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'tcx> {
/// Similarly, at a given `drop` statement, the set-intersection
/// between this data and `MaybeInitializedLvals` yields the set of
/// l-values that would require a dynamic drop-flag at that statement.
pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub struct MaybeUninitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
}
impl<'a, 'tcx: 'a> MaybeUninitializedLvals<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'tcx>)
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> Self
{
MaybeUninitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
}
}
impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
}
@ -185,27 +185,27 @@ impl<'a, 'tcx: 'a> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'tcx> {
/// Similarly, at a given `drop` statement, the set-difference between
/// this data and `MaybeInitializedLvals` yields the set of l-values
/// that would require a dynamic drop-flag at that statement.
pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub struct DefinitelyInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'tcx>,
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
}
impl<'a, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
mdpe: &'a MoveDataParamEnv<'tcx>)
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
-> Self
{
DefinitelyInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
}
}
impl<'a, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
}
impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
state: DropFlagState)
{
@ -216,7 +216,7 @@ impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
}
}
impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
state: DropFlagState)
{
@ -227,7 +227,7 @@ impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
}
}
impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
state: DropFlagState)
{
@ -238,7 +238,7 @@ impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
}
}
impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
type Idx = MovePathIndex;
fn name() -> &'static str { "maybe_init" }
fn bits_per_block(&self) -> usize {
@ -290,7 +290,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
}
}
impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
type Idx = MovePathIndex;
fn name() -> &'static str { "maybe_uninit" }
fn bits_per_block(&self) -> usize {
@ -345,7 +345,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
}
}
impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
type Idx = MovePathIndex;
fn name() -> &'static str { "definite_init" }
fn bits_per_block(&self) -> usize {
@ -399,21 +399,21 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
}
}
impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // "maybe" means we union effects of both preds
}
}
impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // "maybe" means we union effects of both preds
}
}
impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 & pred2 // "definitely" means we intersect effects of both preds
@ -430,21 +430,21 @@ impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
// propagating, or you start at all-ones and then use Intersect as
// your merge when propagating.
impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn bottom_value() -> bool {
false // bottom = uninitialized
}
}
impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn bottom_value() -> bool {
false // bottom = initialized (start_block_effect counters this at outset)
}
}
impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn bottom_value() -> bool {
true // bottom = initialized (start_block_effect counters this at outset)

View File

@ -91,19 +91,19 @@ pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option
return None;
}
pub struct MoveDataParamEnv<'tcx> {
pub struct MoveDataParamEnv<'gcx, 'tcx> {
pub(crate) move_data: MoveData<'tcx>,
pub(crate) param_env: ty::ParamEnv<'tcx>,
pub(crate) param_env: ty::ParamEnv<'gcx>,
}
pub(crate) fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
node_id: ast::NodeId,
attributes: &[ast::Attribute],
dead_unwinds: &IdxSet<BasicBlock>,
bd: BD,
p: P)
-> DataflowResults<BD>
pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
node_id: ast::NodeId,
attributes: &[ast::Attribute],
dead_unwinds: &IdxSet<BasicBlock>,
bd: BD,
p: P)
-> DataflowResults<BD>
where BD: BitDenotation,
P: Fn(&BD, BD::Idx) -> &fmt::Debug
{
@ -612,9 +612,9 @@ pub trait BitDenotation: DataflowOperator {
dest_lval: &mir::Lvalue);
}
impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
impl<'a, 'gcx, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
{
pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub fn new(_tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
dead_unwinds: &'a IdxSet<mir::BasicBlock>,
denotation: D) -> Self {

View File

@ -25,18 +25,18 @@ use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, Move
use super::{MoveError};
use super::IllegalMoveOriginKind::*;
struct MoveDataBuilder<'a, 'tcx: 'a> {
struct MoveDataBuilder<'a, 'gcx: 'tcx, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'gcx>,
data: MoveData<'tcx>,
errors: Vec<MoveError<'tcx>>,
}
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> {
fn new(mir: &'a Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>)
tcx: TyCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'gcx>)
-> Self {
let mut move_paths = IndexVec::new();
let mut path_map = IndexVec::new();
@ -86,7 +86,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}
}
impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
/// This creates a MovePath for a given lvalue, returning an `MovePathError`
/// if that lvalue can't be moved from.
///
@ -175,7 +175,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
}
}
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> {
fn finalize(self) -> Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<MoveError<'tcx>>)> {
debug!("{}", {
debug!("moves for {:?}:", self.mir.span);
@ -197,11 +197,11 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}
}
pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>)
-> Result<MoveData<'tcx>,
(MoveData<'tcx>, Vec<MoveError<'tcx>>)> {
pub(super) fn gather_moves<'a, 'gcx, 'tcx>(mir: &Mir<'tcx>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'gcx>)
-> Result<MoveData<'tcx>,
(MoveData<'tcx>, Vec<MoveError<'tcx>>)> {
let mut builder = MoveDataBuilder::new(mir, tcx, param_env);
for (bb, block) in mir.basic_blocks().iter_enumerated() {
@ -220,7 +220,7 @@ pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>,
builder.finalize()
}
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> {
fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) {
debug!("gather_statement({:?}, {:?})", loc, stmt);
(Gatherer { builder: self, loc }).gather_statement(stmt);
@ -232,12 +232,12 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
}
}
struct Gatherer<'b, 'a: 'b, 'tcx: 'a> {
builder: &'b mut MoveDataBuilder<'a, 'tcx>,
struct Gatherer<'b, 'a: 'b, 'gcx: 'tcx, 'tcx: 'a> {
builder: &'b mut MoveDataBuilder<'a, 'gcx, 'tcx>,
loc: Location,
}
impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
fn gather_statement(&mut self, stmt: &Statement<'tcx>) {
match stmt.kind {
StatementKind::Assign(ref lval, ref rval) => {
@ -352,8 +352,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
debug!("gather_move({:?}, {:?})", self.loc, lval);
let tcx = self.builder.tcx;
let gcx = tcx.global_tcx();
let lv_ty = lval.ty(self.builder.mir, tcx).to_ty(tcx);
if !lv_ty.moves_by_default(tcx, self.builder.param_env, DUMMY_SP) {
let erased_ty = gcx.lift(&tcx.erase_regions(&lv_ty)).unwrap();
if !erased_ty.moves_by_default(gcx, self.builder.param_env, DUMMY_SP) {
debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", self.loc, lval, lv_ty);
return
}

View File

@ -256,10 +256,10 @@ impl<'tcx> MoveError<'tcx> {
}
}
impl<'a, 'tcx> MoveData<'tcx> {
impl<'a, 'gcx, 'tcx> MoveData<'tcx> {
pub fn gather_moves(mir: &Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>)
tcx: TyCtxt<'a, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'gcx>)
-> Result<Self, (Self, Vec<MoveError<'tcx>>)> {
builder::gather_moves(mir, tcx, param_env)
}

View File

@ -18,13 +18,14 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(conservative_impl_trait)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(i128_type)]
#![feature(rustc_diagnostic_macros)]
#![feature(placement_in_syntax)]
#![feature(collection_placement)]
#![feature(nonzero)]
#![feature(underscore_lifetimes)]
#[macro_use]
extern crate bitflags;

View File

@ -83,7 +83,7 @@ fn find_dead_unwinds<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
id: ast::NodeId,
env: &MoveDataParamEnv<'tcx>)
env: &MoveDataParamEnv<'tcx, 'tcx>)
-> IdxSetBuf<BasicBlock>
{
debug!("find_dead_unwinds({:?})", mir.span);
@ -146,7 +146,7 @@ impl InitializationData {
fn apply_location<'a,'tcx>(&mut self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
env: &MoveDataParamEnv<'tcx>,
env: &MoveDataParamEnv<'tcx, 'tcx>,
loc: Location)
{
drop_flag_effects_for_location(tcx, mir, env, loc, |path, df| {
@ -280,9 +280,9 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> {
struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &'a Mir<'tcx>,
env: &'a MoveDataParamEnv<'tcx>,
flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx>>,
flow_uninits: DataflowResults<MaybeUninitializedLvals<'a, 'tcx>>,
env: &'a MoveDataParamEnv<'tcx, 'tcx>,
flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx, 'tcx>>,
flow_uninits: DataflowResults<MaybeUninitializedLvals<'a, 'tcx, 'tcx>>,
drop_flags: FxHashMap<MovePathIndex, Local>,
patch: MirPatch<'tcx>,
}

View File

@ -68,7 +68,7 @@ use rustc::mir::visit::{LvalueContext, Visitor, MutVisitor};
use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior};
use rustc::ty::subst::{Kind, Substs};
use util::dump_mir;
use util::liveness;
use util::liveness::{self, LivenessMode};
use rustc_const_math::ConstInt;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_set::IdxSetBuf;
@ -348,7 +348,10 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ignored.visit_mir(mir);
let mut set = liveness::LocalSet::new_empty(mir.local_decls.len());
let liveness = liveness::liveness_of_locals(mir);
let liveness = liveness::liveness_of_locals(mir, LivenessMode {
include_regular_use: true,
include_drops: true,
});
liveness::dump_mir(tcx, "generator_liveness", source, mir, &liveness);
let mut storage_liveness_map = HashMap::new();

View File

@ -0,0 +1,269 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::hir;
use rustc::mir::{BasicBlock, BorrowKind, Location, Lvalue, Mir, Rvalue, Statement, StatementKind};
use rustc::mir::transform::MirSource;
use rustc::mir::visit::Visitor;
use rustc::mir::Lvalue::Projection;
use rustc::mir::{LvalueProjection, ProjectionElem};
use rustc::infer::InferCtxt;
use rustc::traits::{self, ObligationCause};
use rustc::ty::{self, Ty};
use rustc::ty::fold::TypeFoldable;
use rustc::util::common::ErrorReported;
use rustc_data_structures::fx::FxHashSet;
use syntax::codemap::DUMMY_SP;
use super::subtype;
use super::LivenessResults;
use super::ToRegionIndex;
use super::region_infer::RegionInferenceContext;
pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
regioncx: &mut RegionInferenceContext,
mir: &Mir<'tcx>,
mir_source: MirSource,
liveness: &LivenessResults,
) {
ConstraintGeneration {
infcx,
regioncx,
mir,
liveness,
mir_source,
}.add_constraints();
}
struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &'cx mut RegionInferenceContext,
mir: &'cx Mir<'tcx>,
liveness: &'cx LivenessResults,
mir_source: MirSource,
}
impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
fn add_constraints(&mut self) {
self.add_liveness_constraints();
self.add_borrow_constraints();
}
/// Liveness constraints:
///
/// > If a variable V is live at point P, then all regions R in the type of V
/// > must include the point P.
fn add_liveness_constraints(&mut self) {
debug!("add_liveness_constraints()");
for bb in self.mir.basic_blocks().indices() {
debug!("add_liveness_constraints: bb={:?}", bb);
self.liveness
.regular
.simulate_block(self.mir, bb, |location, live_locals| {
for live_local in live_locals.iter() {
let live_local_ty = self.mir.local_decls[live_local].ty;
self.add_regular_live_constraint(live_local_ty, location);
}
});
self.liveness
.drop
.simulate_block(self.mir, bb, |location, live_locals| {
for live_local in live_locals.iter() {
let live_local_ty = self.mir.local_decls[live_local].ty;
self.add_drop_live_constraint(live_local_ty, location);
}
});
}
}
/// Some variable with type `live_ty` is "regular live" at
/// `location` -- i.e., it may be used later. This means that all
/// regions appearing in the type `live_ty` must be live at
/// `location`.
fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
where
T: TypeFoldable<'tcx>,
{
debug!(
"add_regular_live_constraint(live_ty={:?}, location={:?})",
live_ty,
location
);
self.infcx
.tcx
.for_each_free_region(&live_ty, |live_region| {
let vid = live_region.to_region_index();
self.regioncx.add_live_point(vid, location);
});
}
/// Some variable with type `live_ty` is "drop live" at `location`
/// -- i.e., it may be dropped later. This means that *some* of
/// the regions in its type must be live at `location`. The
/// precise set will depend on the dropck constraints, and in
/// particular this takes `#[may_dangle]` into account.
fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) {
debug!(
"add_drop_live_constraint(dropped_ty={:?}, location={:?})",
dropped_ty,
location
);
let tcx = self.infcx.tcx;
let mut types = vec![(dropped_ty, 0)];
let mut known = FxHashSet();
while let Some((ty, depth)) = types.pop() {
let span = DUMMY_SP; // FIXME
let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
Ok(result) => result,
Err(ErrorReported) => {
continue;
}
};
let ty::DtorckConstraint {
outlives,
dtorck_types,
} = result;
// All things in the `outlives` array may be touched by
// the destructor and must be live at this point.
for outlive in outlives {
if let Some(ty) = outlive.as_type() {
self.add_regular_live_constraint(ty, location);
} else if let Some(r) = outlive.as_region() {
self.add_regular_live_constraint(r, location);
} else {
bug!()
}
}
// However, there may also be some types that
// `dtorck_constraint_for_ty` could not resolve (e.g.,
// associated types and parameters). We need to normalize
// associated types here and possibly recursively process.
let def_id = tcx.hir.local_def_id(self.mir_source.item_id());
let param_env = self.infcx.tcx.param_env(def_id);
for ty in dtorck_types {
// FIXME -- I think that this may disregard some region obligations
// or something. Do we care? -nmatsakis
let cause = ObligationCause::dummy();
match traits::fully_normalize(self.infcx, cause, param_env, &ty) {
Ok(ty) => match ty.sty {
ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
self.add_regular_live_constraint(ty, location);
}
_ => if known.insert(ty) {
types.push((ty, depth + 1));
},
},
Err(errors) => {
self.infcx.report_fulfillment_errors(&errors, None);
}
}
}
}
}
fn add_borrow_constraints(&mut self) {
self.visit_mir(self.mir);
}
fn add_borrow_constraint(
&mut self,
location: Location,
destination_lv: &Lvalue<'tcx>,
borrow_region: ty::Region<'tcx>,
_borrow_kind: BorrowKind,
_borrowed_lv: &Lvalue<'tcx>,
) {
let tcx = self.infcx.tcx;
let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
let destination_region = match destination_ty.sty {
ty::TyRef(r, _) => r,
_ => bug!()
};
self.regioncx.add_outlives(borrow_region.to_region_index(),
destination_region.to_region_index(),
location.successor_within_block());
}
fn add_reborrow_constraint(
&mut self,
location: Location,
borrow_region: ty::Region<'tcx>,
borrowed_lv: &Lvalue<'tcx>,
) {
if let Projection(ref proj) = *borrowed_lv {
let LvalueProjection { ref base, ref elem } = **proj;
if let ProjectionElem::Deref = *elem {
let tcx = self.infcx.tcx;
let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
let base_sty = &base_ty.sty;
if let ty::TyRef(base_region, ty::TypeAndMut{ ty: _, mutbl }) = *base_sty {
match mutbl {
hir::Mutability::MutImmutable => { },
hir::Mutability::MutMutable => {
self.add_reborrow_constraint(location, borrow_region, base);
},
}
self.regioncx.add_outlives(base_region.to_region_index(),
borrow_region.to_region_index(),
location.successor_within_block());
}
}
}
}
}
impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
fn visit_statement(&mut self,
block: BasicBlock,
statement: &Statement<'tcx>,
location: Location) {
debug!("visit_statement(statement={:?}, location={:?})", statement, location);
// Look for a statement like:
//
// D = & L
//
// where D is the path to which we are assigning, and
// L is the path that is borrowed.
if let StatementKind::Assign(ref destination_lv, ref rv) = statement.kind {
if let Rvalue::Ref(region, bk, ref borrowed_lv) = *rv {
self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv);
self.add_reborrow_constraint(location, region, borrowed_lv);
}
let tcx = self.infcx.tcx;
let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
let rv_ty = rv.ty(self.mir, tcx);
for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) {
self.regioncx.add_outlives(a, b, location.successor_within_block());
}
}
self.super_statement(block, statement, location);
}
}

View File

@ -8,169 +8,150 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use self::infer::InferenceContext;
use rustc::ty::TypeFoldable;
use rustc::ty::subst::{Kind, Substs};
use rustc::ty::{Ty, TyCtxt, ClosureSubsts, RegionVid, RegionKind};
use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, Lookup};
use rustc::mir::transform::{MirPass, MirSource};
use rustc::infer::{self as rustc_infer, InferCtxt};
use rustc::util::nodemap::FxHashSet;
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
use syntax_pos::DUMMY_SP;
use std::collections::HashMap;
use rustc::ty::{self, RegionKind};
use rustc::mir::{Location, Mir};
use rustc::mir::transform::MirSource;
use rustc::infer::InferCtxt;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
use std::collections::BTreeSet;
use std::fmt;
use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
use util as mir_util;
use self::mir_util::PassWhere;
mod infer;
mod constraint_generation;
mod subtype;
#[allow(dead_code)]
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
lookup_map: HashMap<RegionVid, Lookup>,
regions: IndexVec<RegionIndex, Region>,
#[allow(dead_code)]
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
pub(crate) mod region_infer;
use self::region_infer::RegionInferenceContext;
mod renumber;
/// Computes the (non-lexical) regions from the input MIR.
///
/// This may result in errors being reported.
pub fn compute_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
source: MirSource,
mir: &mut Mir<'tcx>,
) -> RegionInferenceContext {
// Replace all regions with fresh inference variables.
let num_region_variables = renumber::renumber_mir(infcx, mir);
// Compute what is live where.
let liveness = &LivenessResults {
regular: liveness::liveness_of_locals(
&mir,
LivenessMode {
include_regular_use: true,
include_drops: false,
},
),
drop: liveness::liveness_of_locals(
&mir,
LivenessMode {
include_regular_use: false,
include_drops: true,
},
),
};
// Create the region inference context, generate the constraints,
// and then solve them.
let mut regioncx = RegionInferenceContext::new(num_region_variables);
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness);
let errors = regioncx.solve(infcx, &mir);
assert!(errors.is_empty(), "FIXME: report region inference failures");
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests.
dump_mir_results(infcx, liveness, source, &mir, &regioncx);
regioncx
}
impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
NLLVisitor {
infcx,
lookup_map: HashMap::new(),
regions: IndexVec::new(),
}
struct LivenessResults {
regular: LivenessResult,
drop: LivenessResult,
}
fn dump_mir_results<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
liveness: &LivenessResults,
source: MirSource,
mir: &Mir<'tcx>,
regioncx: &RegionInferenceContext,
) {
if !mir_util::dump_enabled(infcx.tcx, "nll", source) {
return;
}
pub fn into_results(self) -> (HashMap<RegionVid, Lookup>, IndexVec<RegionIndex, Region>) {
(self.lookup_map, self.regions)
}
fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> {
self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| {
self.regions.push(Region::default());
self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
.indices()
.flat_map(|bb| {
let mut results = vec![];
liveness
.regular
.simulate_block(&mir, bb, |location, local_set| {
results.push((location, local_set.clone()));
});
results
})
}
.collect();
fn store_region(&mut self, region: &RegionKind, lookup: Lookup) {
if let RegionKind::ReVar(rid) = *region {
self.lookup_map.entry(rid).or_insert(lookup);
}
}
fn store_ty_regions(&mut self, ty: &Ty<'tcx>, lookup: Lookup) {
for region in ty.regions() {
self.store_region(region, lookup);
}
}
fn store_kind_regions(&mut self, kind: &'tcx Kind, lookup: Lookup) {
if let Some(ty) = kind.as_type() {
self.store_ty_regions(&ty, lookup);
} else if let Some(region) = kind.as_region() {
self.store_region(region, lookup);
}
}
}
impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, lookup: Lookup) {
let old_ty = *ty;
*ty = self.renumber_regions(&old_ty);
self.store_ty_regions(ty, lookup);
}
fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
*substs = self.renumber_regions(&{*substs});
let lookup = Lookup::Loc(location);
for kind in *substs {
self.store_kind_regions(kind, lookup);
}
}
fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
match *rvalue {
Rvalue::Ref(ref mut r, _, _) => {
let old_r = *r;
*r = self.renumber_regions(&old_r);
let lookup = Lookup::Loc(location);
self.store_region(r, lookup);
}
Rvalue::Use(..) |
Rvalue::Repeat(..) |
Rvalue::Len(..) |
Rvalue::Cast(..) |
Rvalue::BinaryOp(..) |
Rvalue::CheckedBinaryOp(..) |
Rvalue::UnaryOp(..) |
Rvalue::Discriminant(..) |
Rvalue::NullaryOp(..) |
Rvalue::Aggregate(..) => {
// These variants don't contain regions.
}
}
self.super_rvalue(rvalue, location);
}
fn visit_closure_substs(&mut self,
substs: &mut ClosureSubsts<'tcx>,
location: Location) {
*substs = self.renumber_regions(substs);
let lookup = Lookup::Loc(location);
for kind in substs.substs {
self.store_kind_regions(kind, lookup);
}
}
fn visit_statement(&mut self,
block: BasicBlock,
statement: &mut Statement<'tcx>,
location: Location) {
if let StatementKind::EndRegion(_) = statement.kind {
statement.kind = StatementKind::Nop;
}
self.super_statement(block, statement, location);
}
}
// MIR Pass for non-lexical lifetimes
pub struct NLL;
impl MirPass for NLL {
fn run_pass<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: MirSource,
mir: &mut Mir<'tcx>) {
if !tcx.sess.opts.debugging_opts.nll {
return;
}
tcx.infer_ctxt().enter(|infcx| {
// Clone mir so we can mutate it without disturbing the rest of the compiler
let mut renumbered_mir = mir.clone();
let mut visitor = NLLVisitor::new(&infcx);
visitor.visit_mir(&mut renumbered_mir);
mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| {
if let PassWhere::BeforeCFG = pass_where {
for (index, value) in visitor.regions.iter_enumerated() {
writeln!(out, "// R{:03}: {:?}", index.0, value)?;
}
}
Ok(())
});
let (_lookup_map, regions) = visitor.into_results();
let mut inference_context = InferenceContext::new(regions);
inference_context.solve(&infcx, &renumbered_mir);
let drop_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
.indices()
.flat_map(|bb| {
let mut results = vec![];
liveness
.drop
.simulate_block(&mir, bb, |location, local_set| {
results.push((location, local_set.clone()));
});
results
})
}
.collect();
mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| {
match pass_where {
// Before the CFG, dump out the values for each region variable.
PassWhere::BeforeCFG => for region in regioncx.regions() {
writeln!(
out,
"| {:?}: {:?}",
region,
regioncx.region_value(region)
)?;
},
// Before each basic block, dump out the values
// that are live on entry to the basic block.
PassWhere::BeforeBlock(bb) => {
let s = live_variable_set(&liveness.regular.ins[bb], &liveness.drop.ins[bb]);
writeln!(out, " | Live variables on entry to {:?}: {}", bb, s)?;
}
PassWhere::InCFG(location) => {
let s = live_variable_set(
&regular_liveness_per_location[&location],
&drop_liveness_per_location[&location],
);
writeln!(out, " | Live variables at {:?}: {}", location, s)?;
}
PassWhere::AfterCFG => {}
}
Ok(())
});
}
#[derive(Clone, Default, PartialEq, Eq)]
pub struct Region {
points: FxHashSet<Location>,
points: BTreeSet<Location>,
}
impl fmt::Debug for Region {
@ -189,4 +170,52 @@ impl Region {
}
}
newtype_index!(RegionIndex);
newtype_index!(RegionIndex {
DEBUG_FORMAT = "'_#{}r",
});
/// Right now, we piggy back on the `ReVar` to store our NLL inference
/// regions. These are indexed with `RegionIndex`. This method will
/// assert that the region is a `ReVar` and convert the internal index
/// into a `RegionIndex`. This is reasonable because in our MIR we
/// replace all free regions with inference variables.
pub trait ToRegionIndex {
fn to_region_index(&self) -> RegionIndex;
}
impl ToRegionIndex for RegionKind {
fn to_region_index(&self) -> RegionIndex {
if let &ty::ReVar(vid) = self {
RegionIndex::new(vid.index as usize)
} else {
bug!("region is not an ReVar: {:?}", self)
}
}
}
fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String {
// sort and deduplicate:
let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect();
// construct a string with each local, including `(drop)` if it is
// only dropped, versus a regular use.
let mut string = String::new();
for local in all_locals {
string.push_str(&format!("{:?}", local));
if !regular.contains(&local) {
assert!(drops.contains(&local));
string.push_str(" (drop)");
}
string.push_str(", ");
}
let len = if string.is_empty() {
0
} else {
string.len() - 2
};
format!("[{}]", &string[..len])
}

View File

@ -12,13 +12,22 @@ use super::{Region, RegionIndex};
use std::mem;
use rustc::infer::InferCtxt;
use rustc::mir::{Location, Mir};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::fx::FxHashSet;
pub struct InferenceContext {
definitions: IndexVec<RegionIndex, VarDefinition>,
constraints: IndexVec<ConstraintIndex, Constraint>,
errors: IndexVec<InferenceErrorIndex, InferenceError>,
pub struct RegionInferenceContext {
/// Contains the definition for every region variable. Region
/// variables are identified by their index (`RegionIndex`). The
/// definition contains information about where the region came
/// from as well as its final inferred value.
definitions: IndexVec<RegionIndex, RegionDefinition>,
/// The constraints we have accumulated and used during solving.
constraints: Vec<Constraint>,
/// List of errors we have accumulated as we add constraints.
/// After solving is done, this is replaced with an empty vector.
errors: Vec<InferenceError>,
}
pub struct InferenceError {
@ -26,24 +35,13 @@ pub struct InferenceError {
pub name: (), // FIXME(nashenas88) RegionName
}
newtype_index!(InferenceErrorIndex);
struct VarDefinition {
#[derive(Default)]
struct RegionDefinition {
name: (), // FIXME(nashenas88) RegionName
value: Region,
capped: bool,
}
impl VarDefinition {
pub fn new(value: Region) -> Self {
Self {
name: (),
value,
capped: false,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Constraint {
sub: RegionIndex,
@ -51,24 +49,43 @@ pub struct Constraint {
point: Location,
}
newtype_index!(ConstraintIndex);
impl InferenceContext {
pub fn new(values: IndexVec<RegionIndex, Region>) -> Self {
impl RegionInferenceContext {
pub fn new(num_region_variables: usize) -> Self {
Self {
definitions: values.into_iter().map(VarDefinition::new).collect(),
constraints: IndexVec::new(),
errors: IndexVec::new(),
definitions: (0..num_region_variables)
.map(|_| RegionDefinition::default())
.collect(),
constraints: Vec::new(),
errors: Vec::new(),
}
}
/// Returns an iterator over all the region indices.
pub fn regions(&self) -> impl Iterator<Item = RegionIndex> {
self.definitions.indices()
}
/// Returns the inferred value for the region `r`.
///
/// Until `solve()` executes, this value is not particularly meaningful.
pub fn region_value(&self, r: RegionIndex) -> &Region {
&self.definitions[r].value
}
/// Flags a region as being "capped" -- this means that if its
/// value is required to grow as a result of some constraint
/// (e.g., `add_live_point` or `add_outlives`), that indicates an
/// error. This is used for the regions representing named
/// lifetime parameters on a function: they get initialized to
/// their complete value, and then "capped" so that they can no
/// longer grow.
#[allow(dead_code)]
pub fn cap_var(&mut self, v: RegionIndex) {
pub(super) fn cap_var(&mut self, v: RegionIndex) {
self.definitions[v].capped = true;
}
#[allow(dead_code)]
pub fn add_live_point(&mut self, v: RegionIndex, point: Location) {
pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) {
debug!("add_live_point({:?}, {:?})", v, point);
let definition = &mut self.definitions[v];
if definition.value.add_point(point) {
@ -81,22 +98,17 @@ impl InferenceContext {
}
}
#[allow(dead_code)]
pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
pub(super) fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
self.constraints.push(Constraint { sup, sub, point });
}
#[allow(dead_code)]
pub fn region(&self, v: RegionIndex) -> &Region {
&self.definitions[v].value
}
pub fn solve<'a, 'gcx, 'tcx>(
/// Perform region inference.
pub(super) fn solve<'a, 'gcx, 'tcx>(
&mut self,
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
) -> IndexVec<InferenceErrorIndex, InferenceError>
) -> Vec<InferenceError>
where
'gcx: 'tcx + 'a,
'tcx: 'a,
@ -139,13 +151,12 @@ impl InferenceContext {
debug!("\n");
}
mem::replace(&mut self.errors, IndexVec::new())
mem::replace(&mut self.errors, Vec::new())
}
}
struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> {
#[allow(dead_code)]
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
#[allow(dead_code)] infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
}
@ -183,17 +194,22 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
let block_data = &self.mir[p.block];
let successor_points = if p.statement_index < block_data.statements.len() {
vec![Location {
statement_index: p.statement_index + 1,
..p
}]
vec![
Location {
statement_index: p.statement_index + 1,
..p
},
]
} else {
block_data.terminator()
block_data
.terminator()
.successors()
.iter()
.map(|&basic_block| Location {
statement_index: 0,
block: basic_block,
.map(|&basic_block| {
Location {
statement_index: 0,
block: basic_block,
}
})
.collect::<Vec<_>>()
};

View File

@ -0,0 +1,132 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::ty::TypeFoldable;
use rustc::ty::subst::{Kind, Substs};
use rustc::ty::{Ty, ClosureSubsts, RegionVid, RegionKind};
use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, Lookup};
use rustc::infer::{self as rustc_infer, InferCtxt};
use syntax_pos::DUMMY_SP;
use std::collections::HashMap;
/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
mir: &mut Mir<'tcx>)
-> usize
{
let mut visitor = NLLVisitor::new(infcx);
visitor.visit_mir(mir);
visitor.num_region_variables
}
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
lookup_map: HashMap<RegionVid, Lookup>,
num_region_variables: usize,
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
}
impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
NLLVisitor {
infcx,
lookup_map: HashMap::new(),
num_region_variables: 0
}
}
fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> {
self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| {
self.num_region_variables += 1;
self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
})
}
fn store_region(&mut self, region: &RegionKind, lookup: Lookup) {
if let RegionKind::ReVar(rid) = *region {
self.lookup_map.entry(rid).or_insert(lookup);
}
}
fn store_ty_regions(&mut self, ty: &Ty<'tcx>, lookup: Lookup) {
for region in ty.regions() {
self.store_region(region, lookup);
}
}
fn store_kind_regions(&mut self, kind: &'tcx Kind, lookup: Lookup) {
if let Some(ty) = kind.as_type() {
self.store_ty_regions(&ty, lookup);
} else if let Some(region) = kind.as_region() {
self.store_region(region, lookup);
}
}
}
impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, lookup: Lookup) {
let old_ty = *ty;
*ty = self.renumber_regions(&old_ty);
self.store_ty_regions(ty, lookup);
}
fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
*substs = self.renumber_regions(&{*substs});
let lookup = Lookup::Loc(location);
for kind in *substs {
self.store_kind_regions(kind, lookup);
}
}
fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
match *rvalue {
Rvalue::Ref(ref mut r, _, _) => {
let old_r = *r;
*r = self.renumber_regions(&old_r);
let lookup = Lookup::Loc(location);
self.store_region(r, lookup);
}
Rvalue::Use(..) |
Rvalue::Repeat(..) |
Rvalue::Len(..) |
Rvalue::Cast(..) |
Rvalue::BinaryOp(..) |
Rvalue::CheckedBinaryOp(..) |
Rvalue::UnaryOp(..) |
Rvalue::Discriminant(..) |
Rvalue::NullaryOp(..) |
Rvalue::Aggregate(..) => {
// These variants don't contain regions.
}
}
self.super_rvalue(rvalue, location);
}
fn visit_closure_substs(&mut self,
substs: &mut ClosureSubsts<'tcx>,
location: Location) {
*substs = self.renumber_regions(substs);
let lookup = Lookup::Loc(location);
for kind in substs.substs {
self.store_kind_regions(kind, lookup);
}
}
fn visit_statement(&mut self,
block: BasicBlock,
statement: &mut Statement<'tcx>,
location: Location) {
if let StatementKind::EndRegion(_) = statement.kind {
statement.kind = StatementKind::Nop;
}
self.super_statement(block, statement, location);
}
}

View File

@ -0,0 +1,99 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::RegionIndex;
use transform::nll::ToRegionIndex;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation};
pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
a: Ty<'tcx>,
b: Ty<'tcx>)
-> Vec<(RegionIndex, RegionIndex)>
{
let mut subtype = Subtype::new(tcx);
match subtype.relate(&a, &b) {
Ok(_) => subtype.outlives_pairs,
Err(_) => bug!("Fail to relate a = {:?} and b = {:?}", a, b)
}
}
struct Subtype<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
outlives_pairs: Vec<(RegionIndex, RegionIndex)>,
ambient_variance: ty::Variance,
}
impl<'a, 'gcx, 'tcx> Subtype<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Subtype<'a, 'gcx, 'tcx> {
Subtype {
tcx,
outlives_pairs: vec![],
ambient_variance: ty::Covariant,
}
}
}
impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Subtype<'a, 'gcx, 'tcx> {
fn tag(&self) -> &'static str { "Subtype" }
fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.tcx }
fn a_is_expected(&self) -> bool { true } // irrelevant
fn relate_with_variance<T: Relate<'tcx>>(&mut self,
variance: ty::Variance,
a: &T,
b: &T)
-> RelateResult<'tcx, T>
{
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance);
let result = self.relate(a, b);
self.ambient_variance = old_ambient_variance;
result
}
fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
relate::super_relate_tys(self, t, t2)
}
fn regions(&mut self, r_a: ty::Region<'tcx>, r_b: ty::Region<'tcx>)
-> RelateResult<'tcx, ty::Region<'tcx>> {
let a = r_a.to_region_index();
let b = r_b.to_region_index();
match self.ambient_variance {
ty::Covariant => {
self.outlives_pairs.push((b, a));
},
ty::Invariant => {
self.outlives_pairs.push((a, b));
self.outlives_pairs.push((b, a));
},
ty::Contravariant => {
self.outlives_pairs.push((a, b));
},
ty::Bivariant => {},
}
Ok(r_a)
}
fn binders<T>(&mut self, _a: &ty::Binder<T>, _b: &ty::Binder<T>)
-> RelateResult<'tcx, ty::Binder<T>>
where T: Relate<'tcx>
{
unimplemented!();
}
}

View File

@ -93,7 +93,7 @@ pub trait BorrowckErrors {
opt_via: &str,
old_loan_span: Span,
old_opt_via: &str,
old_load_end_span:Span,
old_load_end_span: Option<Span>,
o: Origin)
-> DiagnosticBuilder
{
@ -106,13 +106,17 @@ pub trait BorrowckErrors {
err.span_label(new_loan_span,
format!("mutable borrow starts here in previous \
iteration of loop{}", opt_via));
err.span_label(old_load_end_span, "mutable borrow ends here");
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(old_load_end_span, "mutable borrow ends here");
}
} else {
err.span_label(old_loan_span,
format!("first mutable borrow occurs here{}", old_opt_via));
err.span_label(new_loan_span,
format!("second mutable borrow occurs here{}", opt_via));
err.span_label(old_load_end_span, "first borrow ends here");
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(old_load_end_span, "first borrow ends here");
}
}
err
}
@ -121,7 +125,7 @@ pub trait BorrowckErrors {
new_loan_span: Span,
desc: &str,
old_loan_span: Span,
old_load_end_span: Span,
old_load_end_span: Option<Span>,
o: Origin)
-> DiagnosticBuilder
{
@ -134,9 +138,11 @@ pub trait BorrowckErrors {
err.span_label(
new_loan_span,
"second closure is constructed here");
err.span_label(
old_load_end_span,
"borrow from first closure ends here");
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(
old_load_end_span,
"borrow from first closure ends here");
}
err
}
@ -147,7 +153,7 @@ pub trait BorrowckErrors {
old_loan_span: Span,
noun_old: &str,
old_opt_via: &str,
previous_end_span: Span,
previous_end_span: Option<Span>,
o: Origin)
-> DiagnosticBuilder
{
@ -158,7 +164,9 @@ pub trait BorrowckErrors {
format!("closure construction occurs here{}", opt_via));
err.span_label(old_loan_span,
format!("borrow occurs here{}", old_opt_via));
err.span_label(previous_end_span, "borrow ends here");
if let Some(previous_end_span) = previous_end_span {
err.span_label(previous_end_span, "borrow ends here");
}
err
}
@ -169,7 +177,7 @@ pub trait BorrowckErrors {
kind_new: &str,
old_loan_span: Span,
old_opt_via: &str,
previous_end_span: Span,
previous_end_span: Option<Span>,
o: Origin)
-> DiagnosticBuilder
{
@ -181,7 +189,9 @@ pub trait BorrowckErrors {
format!("borrow occurs here{}", opt_via));
err.span_label(old_loan_span,
format!("closure construction occurs here{}", old_opt_via));
err.span_label(previous_end_span, "borrow from closure ends here");
if let Some(previous_end_span) = previous_end_span {
err.span_label(previous_end_span, "borrow from closure ends here");
}
err
}
@ -194,7 +204,7 @@ pub trait BorrowckErrors {
noun_old: &str,
kind_old: &str,
msg_old: &str,
old_load_end_span: Span,
old_load_end_span: Option<Span>,
o: Origin)
-> DiagnosticBuilder
{
@ -203,7 +213,9 @@ pub trait BorrowckErrors {
desc_new, msg_new, kind_new, noun_old, kind_old, msg_old, OGN=o);
err.span_label(span, format!("{} borrow occurs here{}", kind_new, msg_new));
err.span_label(old_span, format!("{} borrow occurs here{}", kind_old, msg_old));
err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old));
if let Some(old_load_end_span) = old_load_end_span {
err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old));
}
err
}
@ -429,7 +441,7 @@ pub trait BorrowckErrors {
}
}
impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> {
impl<'b, 'gcx, 'tcx> BorrowckErrors for TyCtxt<'b, 'gcx, 'tcx> {
fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
msg: &str,

View File

@ -21,10 +21,10 @@ use rustc_data_structures::indexed_vec::Idx;
use super::pretty::dump_mir_def_ids;
/// Write a graphviz DOT graph of a list of MIRs.
pub fn write_mir_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
single: Option<DefId>,
w: &mut W)
-> io::Result<()>
pub fn write_mir_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>,
single: Option<DefId>,
w: &mut W)
-> io::Result<()>
where W: Write
{
for def_id in dump_mir_def_ids(tcx, single) {
@ -36,10 +36,10 @@ pub fn write_mir_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
/// Write a graphviz DOT graph of the MIR.
pub fn write_mir_fn_graphviz<'a, 'tcx, W>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
nodeid: NodeId,
mir: &Mir,
w: &mut W) -> io::Result<()>
pub fn write_mir_fn_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>,
nodeid: NodeId,
mir: &Mir,
w: &mut W) -> io::Result<()>
where W: Write
{
writeln!(w, "digraph Mir_{} {{", nodeid)?;
@ -137,11 +137,11 @@ fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result
/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
/// all the variables and temporaries.
fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
nid: NodeId,
mir: &Mir,
w: &mut W)
-> io::Result<()> {
fn write_graph_label<'a, 'gcx, 'tcx, W: Write>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
nid: NodeId,
mir: &Mir,
w: &mut W)
-> io::Result<()> {
write!(w, " label=<fn {}(", dot::escape_html(&tcx.node_path_str(nid)))?;
// fn argument types.

View File

@ -35,48 +35,235 @@
use rustc::mir::*;
use rustc::mir::visit::{LvalueContext, Visitor};
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::indexed_set::IdxSetBuf;
use util::pretty::{write_basic_block, dump_enabled, write_mir_intro};
use util::pretty::{dump_enabled, write_basic_block, write_mir_intro};
use rustc::mir::transform::MirSource;
use rustc::ty::item_path;
use std::path::{PathBuf, Path};
use std::path::{Path, PathBuf};
use std::fs;
use rustc::ty::TyCtxt;
use std::io::{self, Write};
pub type LocalSet = IdxSetBuf<Local>;
/// This gives the result of the liveness analysis at the boundary of
/// basic blocks. You can use `simulate_block` to obtain the
/// intra-block results.
pub struct LivenessResult {
/// Liveness mode in use when these results were computed.
pub mode: LivenessMode,
/// Live variables on entry to each basic block.
pub ins: IndexVec<BasicBlock, LocalSet>,
/// Live variables on exit to each basic block. This is equal to
/// the union of the `ins` for each successor.
pub outs: IndexVec<BasicBlock, LocalSet>,
}
#[derive(Copy, Clone, Debug)]
pub struct LivenessMode {
/// If true, then we will consider "regular uses" of a variable to be live.
/// For example, if the user writes `foo(x)`, then this is a regular use of
/// the variable `x`.
pub include_regular_use: bool,
/// If true, then we will consider (implicit) drops of a variable
/// to be live. For example, if the user writes `{ let x =
/// vec![...]; .. }`, then the drop at the end of the block is an
/// implicit drop.
///
/// NB. Despite its name, a call like `::std::mem::drop(x)` is
/// **not** considered a drop for this purposes, but rather a
/// regular use.
pub include_drops: bool,
}
/// Compute which local variables are live within the given function
/// `mir`. The liveness mode `mode` determines what sorts of uses are
/// considered to make a variable live (e.g., do drops count?).
pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> LivenessResult {
let locals = mir.local_decls.len();
let def_use: IndexVec<_, _> = mir.basic_blocks()
.iter()
.map(|b| block(mode, b, locals))
.collect();
let mut ins: IndexVec<_, _> = mir.basic_blocks()
.indices()
.map(|_| LocalSet::new_empty(locals))
.collect();
let mut outs = ins.clone();
let mut changed = true;
let mut bits = LocalSet::new_empty(locals);
while changed {
changed = false;
for b in mir.basic_blocks().indices().rev() {
// outs[b] = {ins of successors}
bits.clear();
for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() {
bits.union(&ins[successor]);
}
outs[b].clone_from(&bits);
// bits = use (bits - def)
def_use[b].apply(&mut bits);
// update bits on entry and flag if they have changed
if ins[b] != bits {
ins[b].clone_from(&bits);
changed = true;
}
}
}
LivenessResult { mode, ins, outs }
}
impl LivenessResult {
/// Walks backwards through the statements/terminator in the given
/// basic block `block`. At each point within `block`, invokes
/// the callback `op` with the current location and the set of
/// variables that are live on entry to that location.
pub fn simulate_block<'tcx, OP>(&self, mir: &Mir<'tcx>, block: BasicBlock, mut callback: OP)
where
OP: FnMut(Location, &LocalSet),
{
let data = &mir[block];
// Get a copy of the bits on exit from the block.
let mut bits = self.outs[block].clone();
// Start with the maximal statement index -- i.e., right before
// the terminator executes.
let mut statement_index = data.statements.len();
// Compute liveness right before terminator and invoke callback.
let terminator_location = Location {
block,
statement_index,
};
let terminator_defs_uses = self.defs_uses(mir, terminator_location, &data.terminator);
terminator_defs_uses.apply(&mut bits);
callback(terminator_location, &bits);
// Compute liveness before each statement (in rev order) and invoke callback.
for statement in data.statements.iter().rev() {
statement_index -= 1;
let statement_location = Location {
block,
statement_index,
};
let statement_defs_uses = self.defs_uses(mir, statement_location, statement);
statement_defs_uses.apply(&mut bits);
callback(statement_location, &bits);
}
assert_eq!(bits, self.ins[block]);
}
fn defs_uses<'tcx, V>(&self, mir: &Mir<'tcx>, location: Location, thing: &V) -> DefsUses
where
V: MirVisitable<'tcx>,
{
let locals = mir.local_decls.len();
let mut visitor = DefsUsesVisitor {
mode: self.mode,
defs_uses: DefsUses {
defs: LocalSet::new_empty(locals),
uses: LocalSet::new_empty(locals),
},
};
// Visit the various parts of the basic block in reverse. If we go
// forward, the logic in `add_def` and `add_use` would be wrong.
thing.apply(location, &mut visitor);
visitor.defs_uses
}
}
struct DefsUsesVisitor {
mode: LivenessMode,
defs_uses: DefsUses,
}
#[derive(Eq, PartialEq, Clone)]
struct BlockInfo {
struct DefsUses {
defs: LocalSet,
uses: LocalSet,
}
struct BlockInfoVisitor {
pre_defs: LocalSet,
defs: LocalSet,
uses: LocalSet,
impl DefsUses {
fn apply(&self, bits: &mut LocalSet) -> bool {
bits.subtract(&self.defs) | bits.union(&self.uses)
}
fn add_def(&mut self, index: Local) {
// If it was used already in the block, remove that use
// now that we found a definition.
//
// Example:
//
// // Defs = {X}, Uses = {}
// X = 5
// // Defs = {}, Uses = {X}
// use(X)
self.uses.remove(&index);
self.defs.add(&index);
}
fn add_use(&mut self, index: Local) {
// Inverse of above.
//
// Example:
//
// // Defs = {}, Uses = {X}
// use(X)
// // Defs = {X}, Uses = {}
// X = 5
// // Defs = {}, Uses = {X}
// use(X)
self.defs.remove(&index);
self.uses.add(&index);
}
}
impl<'tcx> Visitor<'tcx> for BlockInfoVisitor {
fn visit_local(&mut self,
&local: &Local,
context: LvalueContext<'tcx>,
_: Location) {
impl<'tcx> Visitor<'tcx> for DefsUsesVisitor {
fn visit_local(&mut self, &local: &Local, context: LvalueContext<'tcx>, _: Location) {
match context {
///////////////////////////////////////////////////////////////////////////
// DEFS
LvalueContext::Store |
// We let Call defined the result in both the success and unwind cases.
// This may not be right.
// We let Call define the result in both the success and
// unwind cases. This is not really correct, however it
// does not seem to be observable due to the way that we
// generate MIR. See the test case
// `mir-opt/nll/liveness-call-subtlety.rs`. To do things
// properly, we would apply the def in call only to the
// input from the success path and not the unwind
// path. -nmatsakis
LvalueContext::Call |
// Storage live and storage dead aren't proper defines, but we can ignore
// values that come before them.
LvalueContext::StorageLive |
LvalueContext::StorageDead => {
self.defs.add(&local);
self.defs_uses.add_def(local);
}
///////////////////////////////////////////////////////////////////////////
// REGULAR USES
//
// These are uses that occur *outside* of a drop. For the
// purposes of NLL, these are special in that **all** the
// lifetimes appearing in the variable must be live for each regular use.
LvalueContext::Projection(..) |
// Borrows only consider their local used at the point of the borrow.
@ -87,124 +274,108 @@ impl<'tcx> Visitor<'tcx> for BlockInfoVisitor {
LvalueContext::Inspect |
LvalueContext::Consume |
LvalueContext::Validate |
LvalueContext::Validate => {
if self.mode.include_regular_use {
self.defs_uses.add_use(local);
}
}
///////////////////////////////////////////////////////////////////////////
// DROP USES
//
// These are uses that occur in a DROP (a MIR drop, not a
// call to `std::mem::drop()`). For the purposes of NLL,
// uses in drop are special because `#[may_dangle]`
// attributes can affect whether lifetimes must be live.
// We consider drops to always be uses of locals.
// Drop eloboration should be run before this analysis otherwise
// the results might be too pessimistic.
LvalueContext::Drop => {
// Ignore uses which are already defined in this block
if !self.pre_defs.contains(&local) {
self.uses.add(&local);
if self.mode.include_drops {
self.defs_uses.add_use(local);
}
}
}
}
}
fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> BlockInfo {
let mut visitor = BlockInfoVisitor {
pre_defs: LocalSet::new_empty(locals),
defs: LocalSet::new_empty(locals),
uses: LocalSet::new_empty(locals),
fn block<'tcx>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses {
let mut visitor = DefsUsesVisitor {
mode,
defs_uses: DefsUses {
defs: LocalSet::new_empty(locals),
uses: LocalSet::new_empty(locals),
},
};
let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 };
let dummy_location = Location {
block: BasicBlock::new(0),
statement_index: 0,
};
for statement in &b.statements {
visitor.visit_statement(BasicBlock::new(0), statement, dummy_location);
visitor.pre_defs.union(&visitor.defs);
}
// Visit the various parts of the basic block in reverse. If we go
// forward, the logic in `add_def` and `add_use` would be wrong.
visitor.visit_terminator(BasicBlock::new(0), b.terminator(), dummy_location);
for statement in b.statements.iter().rev() {
visitor.visit_statement(BasicBlock::new(0), statement, dummy_location);
}
BlockInfo {
defs: visitor.defs,
uses: visitor.uses,
visitor.defs_uses
}
trait MirVisitable<'tcx> {
fn apply<V>(&self, location: Location, visitor: &mut V)
where
V: Visitor<'tcx>;
}
impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> {
fn apply<V>(&self, location: Location, visitor: &mut V)
where
V: Visitor<'tcx>,
{
visitor.visit_statement(location.block, self, location)
}
}
// This gives the result of the liveness analysis at the boundary of basic blocks
pub struct LivenessResult {
pub ins: IndexVec<BasicBlock, LocalSet>,
pub outs: IndexVec<BasicBlock, LocalSet>,
}
pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
let locals = mir.local_decls.len();
let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| {
block(b, locals)
}).collect();
let copy = |from: &IndexVec<BasicBlock, LocalSet>, to: &mut IndexVec<BasicBlock, LocalSet>| {
for (i, set) in to.iter_enumerated_mut() {
set.clone_from(&from[i]);
}
};
let mut ins: IndexVec<_, _> = mir.basic_blocks()
.indices()
.map(|_| LocalSet::new_empty(locals)).collect();
let mut outs = ins.clone();
let mut ins_ = ins.clone();
let mut outs_ = outs.clone();
loop {
copy(&ins, &mut ins_);
copy(&outs, &mut outs_);
for b in mir.basic_blocks().indices().rev() {
// out = {ins of successors}
outs[b].clear();
for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() {
outs[b].union(&ins[successor]);
}
// in = use (out - def)
ins[b].clone_from(&outs[b]);
ins[b].subtract(&def_use[b].defs);
ins[b].union(&def_use[b].uses);
}
if ins_ == ins && outs_ == outs {
break;
}
}
LivenessResult {
ins,
outs,
impl<'tcx> MirVisitable<'tcx> for Option<Terminator<'tcx>> {
fn apply<V>(&self, location: Location, visitor: &mut V)
where
V: Visitor<'tcx>,
{
visitor.visit_terminator(location.block, self.as_ref().unwrap(), location)
}
}
pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_name: &str,
source: MirSource,
mir: &Mir<'tcx>,
result: &LivenessResult) {
pub fn dump_mir<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_name: &str,
source: MirSource,
mir: &Mir<'tcx>,
result: &LivenessResult,
) {
if !dump_enabled(tcx, pass_name, source) {
return;
}
let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
let node_path = item_path::with_forced_impl_filename_line(|| {
// see notes on #41697 below
tcx.item_path_str(tcx.hir.local_def_id(source.item_id()))
});
dump_matched_mir_node(tcx, pass_name, &node_path,
source, mir, result);
dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, result);
}
fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_name: &str,
node_path: &str,
source: MirSource,
mir: &Mir<'tcx>,
result: &LivenessResult) {
fn dump_matched_mir_node<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_name: &str,
node_path: &str,
source: MirSource,
mir: &Mir<'tcx>,
result: &LivenessResult,
) {
let mut file_path = PathBuf::new();
if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
let p = Path::new(file_dir);
file_path.push(p);
};
let file_name = format!("rustc.node{}{}-liveness.mir",
source.item_id(), pass_name);
let file_name = format!("rustc.node{}{}-liveness.mir", source.item_id(), pass_name);
file_path.push(&file_name);
let _ = fs::File::create(&file_path).and_then(|mut file| {
writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
@ -216,16 +387,18 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
});
}
pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource,
mir: &Mir<'tcx>,
w: &mut Write,
result: &LivenessResult)
-> io::Result<()> {
pub fn write_mir_fn<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource,
mir: &Mir<'tcx>,
w: &mut Write,
result: &LivenessResult,
) -> io::Result<()> {
write_mir_intro(tcx, src, mir, w)?;
for block in mir.basic_blocks().indices() {
let print = |w: &mut Write, prefix, result: &IndexVec<BasicBlock, LocalSet>| {
let live: Vec<String> = mir.local_decls.indices()
let live: Vec<String> = mir.local_decls
.indices()
.filter(|i| result[block].contains(i))
.map(|i| format!("{:?}", i))
.collect();
@ -242,4 +415,3 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
writeln!(w, "}}")?;
Ok(())
}

View File

@ -56,13 +56,13 @@ pub enum PassWhere {
/// - `substring1&substring2,...` -- `&`-separated list of substrings
/// that can appear in the pass-name or the `item_path_str` for the given
/// node-id. If any one of the substrings match, the data is dumped out.
pub fn dump_mir<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_num: Option<(MirSuite, MirPassIndex)>,
pass_name: &str,
disambiguator: &Display,
source: MirSource,
mir: &Mir<'tcx>,
extra_data: F)
pub fn dump_mir<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_num: Option<(MirSuite, MirPassIndex)>,
pass_name: &str,
disambiguator: &Display,
source: MirSource,
mir: &Mir<'tcx>,
extra_data: F)
where
F: FnMut(PassWhere, &mut Write) -> io::Result<()>
{
@ -77,10 +77,10 @@ where
disambiguator, source, mir, extra_data);
}
pub fn dump_enabled<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_name: &str,
source: MirSource)
-> bool {
pub fn dump_enabled<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_name: &str,
source: MirSource)
-> bool {
let filters = match tcx.sess.opts.debugging_opts.dump_mir {
None => return false,
Some(ref filters) => filters,
@ -101,14 +101,14 @@ pub fn dump_enabled<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// `item_path_str()` would otherwise trigger `type_of`, and this can
// run while we are already attempting to evaluate `type_of`.
fn dump_matched_mir_node<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_num: Option<(MirSuite, MirPassIndex)>,
pass_name: &str,
node_path: &str,
disambiguator: &Display,
source: MirSource,
mir: &Mir<'tcx>,
mut extra_data: F)
fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_num: Option<(MirSuite, MirPassIndex)>,
pass_name: &str,
node_path: &str,
disambiguator: &Display,
source: MirSource,
mir: &Mir<'tcx>,
mut extra_data: F)
where
F: FnMut(PassWhere, &mut Write) -> io::Result<()>
{
@ -161,10 +161,10 @@ where
}
/// Write out a human-readable textual representation for the given MIR.
pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
single: Option<DefId>,
w: &mut Write)
-> io::Result<()>
pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
single: Option<DefId>,
w: &mut Write)
-> io::Result<()>
{
writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
@ -192,12 +192,12 @@ pub fn write_mir_pretty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
Ok(())
}
pub fn write_mir_fn<'a, 'tcx, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource,
mir: &Mir<'tcx>,
extra_data: &mut F,
w: &mut Write)
-> io::Result<()>
pub fn write_mir_fn<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
src: MirSource,
mir: &Mir<'tcx>,
extra_data: &mut F,
w: &mut Write)
-> io::Result<()>
where
F: FnMut(PassWhere, &mut Write) -> io::Result<()>
{
@ -321,11 +321,11 @@ fn write_scope_tree(tcx: TyCtxt,
/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
/// local variables (both user-defined bindings and compiler temporaries).
pub fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource,
mir: &Mir,
w: &mut Write)
-> io::Result<()> {
pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
src: MirSource,
mir: &Mir,
w: &mut Write)
-> io::Result<()> {
write_mir_sig(tcx, src, mir, w)?;
writeln!(w, " {{")?;

View File

@ -0,0 +1,50 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-Zborrowck-mir -Znll
#![allow(warnings)]
#![feature(rustc_attrs)]
fn main() {
}
fn nll_fail() {
let mut data = ('a', 'b', 'c');
let c = &mut data.0;
capitalize(c);
data.0 = 'e';
//~^ ERROR (Ast) [E0506]
//~| ERROR (Mir) [E0506]
data.0 = 'f';
//~^ ERROR (Ast) [E0506]
//~| ERROR (Mir) [E0506]
data.0 = 'g';
//~^ ERROR (Ast) [E0506]
//~| ERROR (Mir) [E0506]
capitalize(c);
}
fn nll_ok() {
let mut data = ('a', 'b', 'c');
let c = &mut data.0;
capitalize(c);
data.0 = 'e';
//~^ ERROR (Ast) [E0506]
data.0 = 'f';
//~^ ERROR (Ast) [E0506]
data.0 = 'g';
//~^ ERROR (Ast) [E0506]
}
fn capitalize(_: &mut char) {
}

View File

@ -0,0 +1,49 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-Zborrowck-mir -Znll
#![allow(warnings)]
#![feature(rustc_attrs)]
fn main() {
}
fn nll_fail() {
let mut data = vec!['a', 'b', 'c'];
let slice = &mut data;
capitalize(slice);
data.push('d');
//~^ ERROR (Ast) [E0499]
//~| ERROR (Mir) [E0499]
data.push('e');
//~^ ERROR (Ast) [E0499]
//~| ERROR (Mir) [E0499]
data.push('f');
//~^ ERROR (Ast) [E0499]
//~| ERROR (Mir) [E0499]
capitalize(slice);
}
fn nll_ok() {
let mut data = vec!['a', 'b', 'c'];
let slice = &mut data;
capitalize(slice);
data.push('d');
//~^ ERROR (Ast) [E0499]
data.push('e');
//~^ ERROR (Ast) [E0499]
data.push('f');
//~^ ERROR (Ast) [E0499]
}
fn capitalize(_: &mut [char]) {
}

View File

@ -0,0 +1,46 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Basic test for liveness constraints: the region (`R1`) that appears
// in the type of `p` includes the points after `&v[0]` up to (but not
// including) the call to `use_x`. The `else` branch is not included.
// compile-flags:-Zborrowck-mir -Znll
#![allow(warnings)]
#![feature(rustc_attrs)]
struct MyStruct {
field: String
}
fn foo1() {
let mut my_struct = MyStruct { field: format!("Hello") };
let value = &my_struct.field;
if value.is_empty() {
my_struct.field.push_str("Hello, world!");
//~^ ERROR (Ast) [E0502]
}
}
fn foo2() {
let mut my_struct = MyStruct { field: format!("Hello") };
let value = &my_struct.field;
if value.is_empty() {
my_struct.field.push_str("Hello, world!");
//~^ ERROR (Ast) [E0502]
//~| ERROR (Mir) [E0502]
}
drop(value);
}
fn main() { }

View File

@ -0,0 +1,49 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Basic test for liveness constraints: the region (`R1`) that appears
// in the type of `p` includes the points after `&v[0]` up to (but not
// including) the call to `use_x`. The `else` branch is not included.
// compile-flags:-Zborrowck-mir -Znll
#![allow(warnings)]
#![feature(rustc_attrs)]
struct MyStruct {
field: String
}
fn main() {
}
fn nll_fail() {
let mut my_struct = MyStruct { field: format!("Hello") };
let value = &mut my_struct.field;
loop {
my_struct.field.push_str("Hello, world!");
//~^ ERROR (Ast) [E0499]
//~| ERROR (Mir) [E0499]
value.len();
return;
}
}
fn nll_ok() {
let mut my_struct = MyStruct { field: format!("Hello") };
let value = &mut my_struct.field;
loop {
my_struct.field.push_str("Hello, world!");
//~^ ERROR (Ast) [E0499]
return;
}
}

View File

@ -133,11 +133,11 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> {
// StorageLive(_3);
// StorageLive(_4);
// StorageLive(_5);
// _5 = promoted1;
// _5 = promoted[1];
// _4 = &'12ds (*_5);
// StorageLive(_7);
// StorageLive(_8);
// _8 = promoted0;
// _8 = promoted[0];
// _7 = &'10s (*_8);
// _3 = D1<'12ds, '10s>::{{constructor}}(_4, _7);
// EndRegion('10s);

View File

@ -0,0 +1,51 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-Znll
fn can_panic() -> Box<usize> {
Box::new(44)
}
fn main() {
let mut x = Box::new(22);
x = can_panic();
}
// Check that:
// - `_1` is the variable corresponding to `x`
// and
// - `_1` is live when `can_panic` is called (because it may be dropped)
//
// END RUST SOURCE
// START rustc.node12.nll.0.mir
// | Live variables on entry to bb0: []
// bb0: {
// | Live variables at bb0[0]: []
// StorageLive(_1);
// | Live variables at bb0[1]: []
// StorageLive(_2);
// | Live variables at bb0[2]: []
// _2 = const 22usize;
// | Live variables at bb0[3]: [_2]
// _1 = const <std::boxed::Box<T>>::new(_2) -> bb1;
// }
// END rustc.node12.nll.0.mir
// START rustc.node12.nll.0.mir
// | Live variables on entry to bb1: [_1 (drop)]
// bb1: {
// | Live variables at bb1[0]: [_1 (drop)]
// StorageDead(_2);
// | Live variables at bb1[1]: [_1 (drop)]
// StorageLive(_3);
// | Live variables at bb1[2]: [_1 (drop)]
// _3 = const can_panic() -> [return: bb2, unwind: bb4];
// }
// END rustc.node12.nll.0.mir

View File

@ -0,0 +1,41 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-Znll
#![allow(warnings)]
fn use_x(_: usize) -> bool { true }
fn main() {
let mut x = 22;
loop {
// Key point: `x` not live on entry to this basic block.
x = 55;
if use_x(x) { break; }
}
}
// END RUST SOURCE
// START rustc.node12.nll.0.mir
// | Live variables on entry to bb1: []
// bb1: {
// | Live variables at bb1[0]: []
// _1 = const 55usize;
// | Live variables at bb1[1]: [_1]
// StorageLive(_3);
// | Live variables at bb1[2]: [_1]
// StorageLive(_4);
// | Live variables at bb1[3]: [_1]
// _4 = _1;
// | Live variables at bb1[4]: [_4]
// _3 = const use_x(_4) -> bb2;
// }
// END rustc.node12.nll.0.mir

View File

@ -0,0 +1,50 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags:-Znll
fn cond() -> bool { false }
fn make_live(_: usize) { }
fn make_dead() { }
fn main() {
let x = 5;
if cond() {
make_live(x);
} else {
// x should be dead on entry to this block
make_dead();
}
}
// END RUST SOURCE
// START rustc.node18.nll.0.mir
// | Live variables on entry to bb2: [_1]
// bb2: {
// | Live variables at bb2[0]: [_1]
// StorageLive(_4);
// | Live variables at bb2[1]: [_1]
// _4 = _1;
// | Live variables at bb2[2]: [_4]
// _3 = const make_live(_4) -> bb4;
// }
// END rustc.node18.nll.0.mir
// START rustc.node18.nll.0.mir
// | Live variables on entry to bb3: []
// bb3: {
// | Live variables at bb3[0]: []
// _5 = const make_dead() -> bb5;
// }
// END rustc.node18.nll.0.mir

View File

@ -0,0 +1,39 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Basic test for reborrow constraints: the region (`R5`) that appears
// in the type of `r_a` must outlive the region (`R7`) that appears in
// the type of `r_b`
// compile-flags:-Znll -Zverbose
// ^^^^^^^^^ force compiler to dump more region information
#![allow(warnings)]
fn use_x(_: &mut i32) -> bool { true }
fn main() {
let mut foo: i32 = 22;
let r_a: &mut i32 = &mut foo;
let r_b: &mut i32 = &mut *r_a;
use_x(r_b);
}
// END RUST SOURCE
// START rustc.node13.nll.0.mir
// | '_#5r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]}
// ...
// | '_#7r: {bb0[11], bb0[12], bb0[13], bb0[14]}
// END rustc.node13.nll.0.mir
// START rustc.node13.nll.0.mir
// let _2: &'_#5r mut i32;
// ...
// let _4: &'_#7r mut i32;
// END rustc.node13.nll.0.mir

View File

@ -0,0 +1,56 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Basic test for liveness constraints: the region (`R1`) that appears
// in the type of `p` includes the points after `&v[0]` up to (but not
// including) the call to `use_x`. The `else` branch is not included.
// compile-flags:-Znll -Zverbose
// ^^^^^^^^^ force compiler to dump more region information
#![allow(warnings)]
fn use_x(_: usize) -> bool { true }
fn main() {
let mut v = [1, 2, 3];
let p = &v[0];
if true {
use_x(*p);
} else {
use_x(22);
}
}
// END RUST SOURCE
// START rustc.node12.nll.0.mir
// | '_#0r: {bb1[1], bb2[0], bb2[1]}
// | '_#1r: {bb1[1], bb2[0], bb2[1]}
// ...
// let _2: &'_#1r usize;
// END rustc.node12.nll.0.mir
// START rustc.node12.nll.0.mir
// bb1: {
// | Live variables at bb1[0]: [_1, _3]
// _2 = &'_#0r _1[_3];
// | Live variables at bb1[1]: [_2]
// switchInt(const true) -> [0u8: bb3, otherwise: bb2];
// }
// END rustc.node12.nll.0.mir
// START rustc.node12.nll.0.mir
// bb2: {
// | Live variables at bb2[0]: [_2]
// StorageLive(_7);
// | Live variables at bb2[1]: [_2]
// _7 = (*_2);
// | Live variables at bb2[2]: [_7]
// _6 = const use_x(_7) -> bb4;
// }
// END rustc.node12.nll.0.mir

View File

@ -0,0 +1,48 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Basic test for liveness constraints: the region (`R1`) that appears
// in the type of `p` includes the points after `&v[0]` up to (but not
// including) the call to `use_x`. The `else` branch is not included.
// compile-flags:-Znll -Zverbose
// ^^^^^^^^^ force compiler to dump more region information
#![allow(warnings)]
#![feature(dropck_eyepatch)]
#![feature(generic_param_attrs)]
fn use_x(_: usize) -> bool { true }
fn main() {
let mut v = [1, 2, 3];
let p: Wrap<& /* R4 */ usize> = Wrap { value: &v[0] };
if true {
use_x(*p.value);
} else {
use_x(22);
}
// `p` will get dropped here. However, because of the
// `#[may_dangle]` attribute, we do not need to consider R4 live.
}
struct Wrap<T> {
value: T
}
unsafe impl<#[may_dangle] T> Drop for Wrap<T> {
fn drop(&mut self) { }
}
// END RUST SOURCE
// START rustc.node12.nll.0.mir
// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]}
// END rustc.node12.nll.0.mir

View File

@ -0,0 +1,50 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Basic test for liveness constraints: the region (`R1`) that appears
// in the type of `p` includes the points after `&v[0]` up to (but not
// including) the call to `use_x`. The `else` branch is not included.
// ignore-tidy-linelength
// compile-flags:-Znll -Zverbose
// ^^^^^^^^^ force compiler to dump more region information
#![allow(warnings)]
fn use_x(_: usize) -> bool { true }
fn main() {
let mut v = [1, 2, 3];
let p: Wrap<& /* R1 */ usize> = Wrap { value: &v[0] };
if true {
use_x(*p.value);
} else {
use_x(22);
}
// `p` will get dropped here. Because the `#[may_dangle]`
// attribute is not present on `Wrap`, we must conservatively
// assume that the dtor may access the `value` field, and hence we
// must consider R1 to be live.
}
struct Wrap<T> {
value: T
}
// Look ma, no `#[may_dangle]` attribute here.
impl<T> Drop for Wrap<T> {
fn drop(&mut self) { }
}
// END RUST SOURCE
// START rustc.node12.nll.0.mir
// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]}
// END rustc.node12.nll.0.mir

View File

@ -0,0 +1,49 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test for the subregion constraints. In this case, the region R3 on
// `p` includes two disjoint regions of the control-flow graph. The
// borrows in `&v[0]` and `&v[1]` each (in theory) have to outlive R3,
// but only at a particular point, and hence they wind up including
// distinct regions.
// compile-flags:-Znll -Zverbose
// ^^^^^^^^^ force compiler to dump more region information
#![allow(warnings)]
fn use_x(_: usize) -> bool { true }
fn main() {
let mut v = [1, 2, 3];
let mut p = &v[0];
if true {
use_x(*p);
} else {
use_x(22);
}
p = &v[1];
use_x(*p);
}
// END RUST SOURCE
// START rustc.node12.nll.0.mir
// | '_#0r: {bb1[1], bb2[0], bb2[1]}
// ...
// | '_#2r: {bb7[2], bb7[3], bb7[4]}
// | '_#3r: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]}
// ...
// let mut _2: &'_#3r usize;
// ...
// _2 = &'_#0r _1[_3];
// ...
// _2 = &'_#2r (*_11);
// END rustc.node12.nll.0.mir

View File

@ -0,0 +1,49 @@
// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Basic test for liveness constraints: the region (`R1`) that appears
// in the type of `p` includes the points after `&v[0]` up to (but not
// including) the call to `use_x`. The `else` branch is not included.
// compile-flags:-Znll -Zverbose
// ^^^^^^^^^ force compiler to dump more region information
#![allow(warnings)]
fn use_x(_: usize) -> bool { true }
fn main() {
let mut v = [1, 2, 3];
let p = &v[0];
let q = p;
if true {
use_x(*q);
} else {
use_x(22);
}
}
// END RUST SOURCE
// START rustc.node12.nll.0.mir
// | '_#0r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]}
// | '_#1r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]}
// | '_#2r: {bb1[5], bb1[6], bb2[0], bb2[1]}
// END rustc.node12.nll.0.mir
// START rustc.node12.nll.0.mir
// let _2: &'_#1r usize;
// ...
// let _6: &'_#2r usize;
// ...
// _2 = &'_#0r _1[_3];
// ...
// _7 = _2;
// ...
// _6 = _7;
// END rustc.node12.nll.0.mir

View File

@ -1404,6 +1404,7 @@ actual:\n\
"-Zdump-mir-exclude-pass-number"]);
let mir_dump_dir = self.get_mir_dump_dir();
let _ = fs::remove_dir_all(&mir_dump_dir);
create_dir_all(mir_dump_dir.as_path()).unwrap();
let mut dir_opt = "-Zdump-mir-dir=".to_string();
dir_opt.push_str(mir_dump_dir.to_str().unwrap());
@ -2367,12 +2368,10 @@ actual:\n\
}
fn get_mir_dump_dir(&self) -> PathBuf {
let mut mir_dump_dir = PathBuf::from(self.config.build_base
.as_path()
.to_str()
.unwrap());
let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
debug!("input_file: {:?}", self.testpaths.file);
mir_dump_dir.push(self.testpaths.file.file_stem().unwrap().to_str().unwrap());
mir_dump_dir.push(&self.testpaths.relative_dir);
mir_dump_dir.push(self.testpaths.file.file_stem().unwrap());
mir_dump_dir
}