mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-19 20:13:47 +00:00
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:
commit
2be4cc0402
@ -158,7 +158,7 @@ pub struct BlockRemainder {
|
||||
|
||||
newtype_index!(FirstStatementIndex
|
||||
{
|
||||
DEBUG_NAME = "",
|
||||
DEBUG_FORMAT = "{}",
|
||||
MAX = SCOPE_DATA_REMAINDER_MAX,
|
||||
});
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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) =>
|
||||
|
@ -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> {
|
||||
|
@ -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)*);
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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();
|
||||
|
269
src/librustc_mir/transform/nll/constraint_generation.rs
Normal file
269
src/librustc_mir/transform/nll/constraint_generation.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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, ®ioncx);
|
||||
|
||||
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(
|
||||
®ular_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])
|
||||
}
|
||||
|
@ -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<_>>()
|
||||
};
|
132
src/librustc_mir/transform/nll/renumber.rs
Normal file
132
src/librustc_mir/transform/nll/renumber.rs
Normal 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);
|
||||
}
|
||||
}
|
99
src/librustc_mir/transform/nll/subtype.rs
Normal file
99
src/librustc_mir/transform/nll/subtype.rs
Normal 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!();
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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, " {{")?;
|
||||
|
||||
|
50
src/test/compile-fail/nll/loan_ends_mid_block_pair.rs
Normal file
50
src/test/compile-fail/nll/loan_ends_mid_block_pair.rs
Normal 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) {
|
||||
}
|
49
src/test/compile-fail/nll/loan_ends_mid_block_vec.rs
Normal file
49
src/test/compile-fail/nll/loan_ends_mid_block_vec.rs
Normal 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]) {
|
||||
}
|
46
src/test/compile-fail/nll/region-ends-after-if-condition.rs
Normal file
46
src/test/compile-fail/nll/region-ends-after-if-condition.rs
Normal 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() { }
|
49
src/test/compile-fail/nll/return_from_loop.rs
Normal file
49
src/test/compile-fail/nll/return_from_loop.rs
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
51
src/test/mir-opt/nll/liveness-call-subtlety.rs
Normal file
51
src/test/mir-opt/nll/liveness-call-subtlety.rs
Normal 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
|
41
src/test/mir-opt/nll/liveness-drop-intra-block.rs
Normal file
41
src/test/mir-opt/nll/liveness-drop-intra-block.rs
Normal 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
|
50
src/test/mir-opt/nll/liveness-interblock.rs
Normal file
50
src/test/mir-opt/nll/liveness-interblock.rs
Normal 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
|
||||
|
||||
|
39
src/test/mir-opt/nll/reborrow-basic.rs
Normal file
39
src/test/mir-opt/nll/reborrow-basic.rs
Normal 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
|
56
src/test/mir-opt/nll/region-liveness-basic.rs
Normal file
56
src/test/mir-opt/nll/region-liveness-basic.rs
Normal 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
|
48
src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
Normal file
48
src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs
Normal 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
|
50
src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs
Normal file
50
src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs
Normal 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
|
49
src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs
Normal file
49
src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs
Normal 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
|
49
src/test/mir-opt/nll/region-subtyping-basic.rs
Normal file
49
src/test/mir-opt/nll/region-subtyping-basic.rs
Normal 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
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user