diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index fa4ee7c0092..e725592ff99 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -158,7 +158,7 @@ pub struct BlockRemainder { newtype_index!(FirstStatementIndex { - DEBUG_NAME = "", + DEBUG_FORMAT = "{}", MAX = SCOPE_DATA_REMAINDER_MAX, }); diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index f5a3c1989cf..c4a33bb07cd 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -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) -> bool { if self.block == other.block { self.statement_index <= other.statement_index diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index f29405e6650..6c90a5f38d0 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -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. diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index edd4329fa41..149999e0eee 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -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(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 { + current_depth: u32, + callback: F, + } + + impl<'tcx, F> TypeVisitor<'tcx> for RegionVisitor + where F : FnMut(ty::Region<'tcx>) + { + fn visit_binder>(&mut self, t: &Binder) -> 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 diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index c1d0d849dfb..908737669c5 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -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) => diff --git a/src/librustc_data_structures/indexed_set.rs b/src/librustc_data_structures/indexed_set.rs index c790463e47a..c5ffb003399 100644 --- a/src/librustc_data_structures/indexed_set.rs +++ b/src/librustc_data_structures/indexed_set.rs @@ -53,11 +53,19 @@ pub struct IdxSet { } impl fmt::Debug for IdxSetBuf { - 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 fmt::Debug for IdxSet { - 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 IdxSetBuf { diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 1d1b367de20..0660cd96a4a 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -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)*); ); } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 1cbc1aa7234..855362cf645 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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. diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index ee2ef00be57..819f67a39e9 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -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>, - inits: FlowInProgress>, - uninits: FlowInProgress>, +pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> { + borrows: FlowInProgress>, + inits: FlowInProgress>, + uninits: FlowInProgress>, } struct FlowInProgress where BD: BitDenotation { @@ -147,12 +175,12 @@ struct FlowInProgress 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 { 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 { // 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(&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 { 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) { 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>, - inits: DataflowResults>, - uninits: DataflowResults>) +impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { + pub(super) fn new(borrows: DataflowResults>, + inits: DataflowResults>, + uninits: DataflowResults>) -> 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>), - XI: FnMut(&mut FlowInProgress>), - XU: FnMut(&mut FlowInProgress>), + XB: FnMut(&mut FlowInProgress>), + XI: FnMut(&mut FlowInProgress>), + XU: FnMut(&mut FlowInProgress>), { 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> { +impl<'b, 'gcx, 'tcx> FlowInProgress> { fn has_any_child_of(&self, mpi: MovePathIndex) -> Option { let move_data = self.base_results.operator().move_data(); diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index b2f0ff57b62..77496c7b8f2 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -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); diff --git a/src/librustc_mir/dataflow/drop_flag_effects.rs b/src/librustc_mir/dataflow/drop_flag_effects.rs index bd41bce67da..e35bd34c40b 100644 --- a/src/librustc_mir/dataflow/drop_flag_effects.rs +++ b/src/librustc_mir/dataflow/drop_flag_effects.rs @@ -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; } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 9321121fe15..17aa8c05418 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -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>, location_map: FxHashMap, region_map: FxHashMap, FxHashSet>, region_span_map: FxHashMap, + 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>, @@ -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 { 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, + 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, - _location: Location) { - // no terminators start nor end region scopes. + sets: &mut BlockSets, + 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 diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index 19a595622b9..af99706be81 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -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, 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, 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, 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) diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index 9fa5691d647..d27a4e7e9d9 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -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, - bd: BD, - p: P) - -> DataflowResults +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, + bd: BD, + p: P) + -> DataflowResults 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, denotation: D) -> Self { diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 0790d937ceb..8f473d035ee 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -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>, } -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>, Vec>)> { 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>, Vec>)> { +pub(super) fn gather_moves<'a, 'gcx, 'tcx>(mir: &Mir<'tcx>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + param_env: ty::ParamEnv<'gcx>) + -> Result, + (MoveData<'tcx>, Vec>)> { 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 } diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 9369156a223..5bfecd01aaa 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -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>)> { builder::gather_moves(mir, tcx, param_env) } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 7e4206e14c5..b72823dff2b 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -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; diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index be1b794ecdf..94da1f31a96 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -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 { 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>, - flow_uninits: DataflowResults>, + env: &'a MoveDataParamEnv<'tcx, 'tcx>, + flow_inits: DataflowResults>, + flow_uninits: DataflowResults>, drop_flags: FxHashMap, patch: MirPatch<'tcx>, } diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 7d0814b67fb..52a50333f45 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -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(); diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs new file mode 100644 index 00000000000..a7570c610d8 --- /dev/null +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -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 or the MIT license +// , 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(&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); + } +} diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 805e9c976e4..d4938dc40bf 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -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, - regions: IndexVec, - #[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, IndexVec) { - (self.lookup_map, self.regions) - } - - fn renumber_regions(&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, + points: BTreeSet, } 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]) +} diff --git a/src/librustc_mir/transform/nll/infer.rs b/src/librustc_mir/transform/nll/region_infer.rs similarity index 67% rename from src/librustc_mir/transform/nll/infer.rs rename to src/librustc_mir/transform/nll/region_infer.rs index e6e00f295ca..c23d73e784a 100644 --- a/src/librustc_mir/transform/nll/infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -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, - constraints: IndexVec, - errors: IndexVec, +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, + + /// The constraints we have accumulated and used during solving. + constraints: Vec, + + /// List of errors we have accumulated as we add constraints. + /// After solving is done, this is replaced with an empty vector. + errors: Vec, } 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) -> 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 { + 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 + ) -> Vec 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::>() }; diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs new file mode 100644 index 00000000000..589179c2066 --- /dev/null +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -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 or the MIT license +// , 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, + 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(&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); + } +} diff --git a/src/librustc_mir/transform/nll/subtype.rs b/src/librustc_mir/transform/nll/subtype.rs new file mode 100644 index 00000000000..953fc0eb733 --- /dev/null +++ b/src/librustc_mir/transform/nll/subtype.rs @@ -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 or the MIT license +// , 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>(&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(&mut self, _a: &ty::Binder, _b: &ty::Binder) + -> RelateResult<'tcx, ty::Binder> + where T: Relate<'tcx> + { + unimplemented!(); + } +} diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 5451da2148e..a4a7699abda 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -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, 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, 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, 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, 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, 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>(&'a self, sp: S, msg: &str, diff --git a/src/librustc_mir/util/graphviz.rs b/src/librustc_mir/util/graphviz.rs index cec8067530b..a38d317b823 100644 --- a/src/librustc_mir/util/graphviz.rs +++ b/src/librustc_mir/util/graphviz.rs @@ -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, - w: &mut W) - -> io::Result<()> +pub fn write_mir_graphviz<'tcx, W>(tcx: TyCtxt<'_, '_, 'tcx>, + single: Option, + 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(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=; +/// 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, + + /// Live variables on exit to each basic block. This is equal to + /// the union of the `ins` for each successor. + pub outs: IndexVec, +} + +#[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(&self, location: Location, visitor: &mut V) + where + V: Visitor<'tcx>; +} + +impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { + fn apply(&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, - pub outs: IndexVec, -} - -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, to: &mut IndexVec| { - 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> { + fn apply(&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| { - let live: Vec = mir.local_decls.indices() + let live: Vec = 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(()) } - diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 9f61feabe41..1d924175b21 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -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, - w: &mut Write) - -> io::Result<()> +pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + single: Option, + 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, " {{")?; diff --git a/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs b/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs new file mode 100644 index 00000000000..c02977f22ea --- /dev/null +++ b/src/test/compile-fail/nll/loan_ends_mid_block_pair.rs @@ -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 or the MIT license +// , 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) { +} diff --git a/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs b/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs new file mode 100644 index 00000000000..5e3a003b54e --- /dev/null +++ b/src/test/compile-fail/nll/loan_ends_mid_block_vec.rs @@ -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 or the MIT license +// , 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]) { +} diff --git a/src/test/compile-fail/nll/region-ends-after-if-condition.rs b/src/test/compile-fail/nll/region-ends-after-if-condition.rs new file mode 100644 index 00000000000..bec56982c57 --- /dev/null +++ b/src/test/compile-fail/nll/region-ends-after-if-condition.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/compile-fail/nll/return_from_loop.rs b/src/test/compile-fail/nll/return_from_loop.rs new file mode 100644 index 00000000000..6b287fd2272 --- /dev/null +++ b/src/test/compile-fail/nll/return_from_loop.rs @@ -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 or the MIT license +// , 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; + } +} diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs index 1f9ad988acc..61dc1d20659 100644 --- a/src/test/mir-opt/end_region_destruction_extents_1.rs +++ b/src/test/mir-opt/end_region_destruction_extents_1.rs @@ -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); diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs new file mode 100644 index 00000000000..873431505f5 --- /dev/null +++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll + +fn can_panic() -> Box { + 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 >::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 diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs new file mode 100644 index 00000000000..96fd29dfe2f --- /dev/null +++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs @@ -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 or the MIT license +// , 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 diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs new file mode 100644 index 00000000000..c557763c004 --- /dev/null +++ b/src/test/mir-opt/nll/liveness-interblock.rs @@ -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 or the MIT license +// , 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 + + diff --git a/src/test/mir-opt/nll/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs new file mode 100644 index 00000000000..60a4da430b9 --- /dev/null +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -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 or the MIT license +// , 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 diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs new file mode 100644 index 00000000000..7792f0a36f3 --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -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 or the MIT license +// , 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 diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs new file mode 100644 index 00000000000..4f4bb596e5f --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -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 or the MIT license +// , 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 { + value: T +} + +unsafe impl<#[may_dangle] T> Drop for Wrap { + 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 diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs new file mode 100644 index 00000000000..0ddb745b61f --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -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 or the MIT license +// , 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 { + value: T +} + +// Look ma, no `#[may_dangle]` attribute here. +impl Drop for Wrap { + 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 diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs new file mode 100644 index 00000000000..664298b9374 --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -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 or the MIT license +// , 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 diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs new file mode 100644 index 00000000000..4ae891f5b70 --- /dev/null +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -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 or the MIT license +// , 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 diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 0473c2a5405..f8628158aff 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -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 }