From 157231183c586dd19d150a7324b07d65cf7a002f Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 6 Dec 2017 20:27:38 +0200 Subject: [PATCH] start extracting things into modules The borrow_check module is too big for its own good --- .../borrow_check/error_reporting.rs | 550 ++++++++ src/librustc_mir/borrow_check/flow.rs | 335 +++++ src/librustc_mir/borrow_check/mod.rs | 1105 +---------------- .../borrow_check/nll/constraint_generation.rs | 1 - src/librustc_mir/borrow_check/prefixes.rs | 189 +++ 5 files changed, 1132 insertions(+), 1048 deletions(-) create mode 100644 src/librustc_mir/borrow_check/error_reporting.rs create mode 100644 src/librustc_mir/borrow_check/flow.rs create mode 100644 src/librustc_mir/borrow_check/prefixes.rs diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs new file mode 100644 index 00000000000..7ae980bc861 --- /dev/null +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -0,0 +1,550 @@ +// 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 syntax_pos::Span; +use rustc::mir::{BorrowKind, Field, Local, Location, Operand}; +use rustc::mir::{Place, ProjectionElem, Rvalue, StatementKind}; +use rustc::ty; +use rustc_data_structures::indexed_vec::Idx; + +use super::{MirBorrowckCtxt, Context}; +use super::{InitializationRequiringAction, PrefixSet}; +use super::flow::FlowInProgress; +use dataflow::{BorrowData, MovingOutStatements}; +use dataflow::move_paths::MovePathIndex; +use util::borrowck_errors::{BorrowckErrors, Origin}; + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + pub(super) fn report_use_of_moved_or_uninitialized( + &mut self, + _context: Context, + desired_action: InitializationRequiringAction, + (place, span): (&Place<'tcx>, Span), + mpi: MovePathIndex, + curr_move_out: &FlowInProgress>, + ) { + let mois = self.move_data.path_map[mpi] + .iter() + .filter(|moi| curr_move_out.contains(moi)) + .collect::>(); + + if mois.is_empty() { + let item_msg = match self.describe_place(place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + self.tcx + .cannot_act_on_uninitialized_variable( + span, + desired_action.as_noun(), + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ) + .span_label(span, format!("use of possibly uninitialized {}", item_msg)) + .emit(); + } else { + let msg = ""; //FIXME: add "partially " or "collaterally " + + let mut err = self.tcx.cannot_act_on_moved_value( + span, + desired_action.as_noun(), + msg, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); + + err.span_label( + span, + format!( + "value {} here after move", + desired_action.as_verb_in_past_tense() + ), + ); + for moi in mois { + let move_msg = ""; //FIXME: add " (into closure)" + let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span; + if span == move_span { + err.span_label( + span, + format!("value moved{} here in previous iteration of loop", move_msg), + ); + } else { + err.span_label(move_span, format!("value moved{} here", move_msg)); + }; + } + //FIXME: add note for closure + err.emit(); + } + } + + pub(super) fn report_move_out_while_borrowed( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { + let value_msg = match self.describe_place(place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + let borrow_msg = match self.describe_place(&borrow.place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + self.tcx + .cannot_move_when_borrowed( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ) + .span_label( + self.retrieve_borrow_span(borrow), + format!("borrow of {} occurs here", borrow_msg), + ) + .span_label(span, format!("move out of {} occurs here", value_msg)) + .emit(); + } + + pub(super) fn report_use_while_mutably_borrowed( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { + let mut err = self.tcx.cannot_use_when_mutably_borrowed( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + self.retrieve_borrow_span(borrow), + &self.describe_place(&borrow.place).unwrap_or("_".to_owned()), + Origin::Mir, + ); + + err.emit(); + } + + /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of + /// the local assigned at `location`. + /// This is done by searching in statements succeeding `location` + /// and originating from `maybe_closure_span`. + fn find_closure_span( + &self, + maybe_closure_span: Span, + location: Location, + ) -> Option<(Span, Span)> { + use rustc::hir::ExprClosure; + use rustc::mir::AggregateKind; + + let local = if let StatementKind::Assign(Place::Local(local), _) = + self.mir[location.block].statements[location.statement_index].kind + { + local + } else { + return None; + }; + + for stmt in &self.mir[location.block].statements[location.statement_index + 1..] { + if maybe_closure_span != stmt.source_info.span { + break; + } + + if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind { + if let AggregateKind::Closure(def_id, _) = **kind { + debug!("find_closure_span: found closure {:?}", places); + + return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { + let args_span = if let ExprClosure(_, _, _, span, _) = + self.tcx.hir.expect_expr(node_id).node + { + span + } else { + return None; + }; + + self.tcx + .with_freevars(node_id, |freevars| { + for (v, place) in freevars.iter().zip(places) { + match *place { + Operand::Copy(Place::Local(l)) | + Operand::Move(Place::Local(l)) if local == l => + { + debug!( + "find_closure_span: found captured local {:?}", + l + ); + return Some(v.span); + } + _ => {} + } + } + None + }) + .map(|var_span| (args_span, var_span)) + } else { + None + }; + } + } + } + + None + } + + pub(super) fn report_conflicting_borrow( + &mut self, + context: Context, + (place, span): (&Place<'tcx>, Span), + gen_borrow_kind: BorrowKind, + issued_borrow: &BorrowData, + end_issued_loan_span: Option, + ) { + let issued_span = self.retrieve_borrow_span(issued_borrow); + + let new_closure_span = self.find_closure_span(span, context.loc); + let span = new_closure_span.map(|(args, _)| args).unwrap_or(span); + let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location); + let issued_span = old_closure_span + .map(|(args, _)| args) + .unwrap_or(issued_span); + + let desc_place = self.describe_place(place).unwrap_or("_".to_owned()); + + // FIXME: supply non-"" `opt_via` when appropriate + let mut err = match ( + gen_borrow_kind, + "immutable", + "mutable", + issued_borrow.kind, + "immutable", + "mutable", + ) { + (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) | + (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx + .cannot_reborrow_already_borrowed( + span, + &desc_place, + "", + lft, + issued_span, + "it", + rgt, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx + .cannot_mutably_borrow_multiply( + span, + &desc_place, + "", + issued_span, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx + .cannot_uniquely_borrow_by_two_closures( + span, + &desc_place, + issued_span, + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure( + span, + &desc_place, + "", + issued_span, + "it", + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => self.tcx + .cannot_reborrow_already_uniquely_borrowed( + span, + &desc_place, + "", + lft, + issued_span, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Mut, _, lft, BorrowKind::Unique, _, _) => self.tcx + .cannot_reborrow_already_uniquely_borrowed( + span, + &desc_place, + "", + lft, + issued_span, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(), + }; + + if let Some((_, var_span)) = old_closure_span { + err.span_label( + var_span, + format!( + "previous borrow occurs due to use of `{}` in closure", + desc_place + ), + ); + } + + if let Some((_, var_span)) = new_closure_span { + err.span_label( + var_span, + format!("borrow occurs due to use of `{}` in closure", desc_place), + ); + } + + err.emit(); + } + + pub(super) fn report_borrowed_value_does_not_live_long_enough( + &mut self, + _: Context, + (place, span): (&Place<'tcx>, Span), + end_span: Option, + ) { + let root_place = self.prefixes(place, PrefixSet::All).last().unwrap(); + let proper_span = match *root_place { + Place::Local(local) => self.mir.local_decls[local].source_info.span, + _ => span, + }; + let mut err = self.tcx + .path_does_not_live_long_enough(span, "borrowed value", Origin::Mir); + err.span_label(proper_span, "temporary value created here"); + err.span_label(span, "temporary value dropped here while still borrowed"); + err.note("consider using a `let` binding to increase its lifetime"); + + if let Some(end) = end_span { + err.span_label(end, "temporary value needs to live until here"); + } + + err.emit(); + } + + pub(super) fn report_illegal_mutation_of_borrowed( + &mut self, + _: Context, + (place, span): (&Place<'tcx>, Span), + loan: &BorrowData, + ) { + let mut err = self.tcx.cannot_assign_to_borrowed( + span, + self.retrieve_borrow_span(loan), + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); + + err.emit(); + } + + pub(super) fn report_illegal_reassignment( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + assigned_span: Span, + ) { + let mut err = self.tcx.cannot_reassign_immutable( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); + err.span_label(span, "cannot assign twice to immutable variable"); + if span != assigned_span { + let value_msg = match self.describe_place(place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + err.span_label(assigned_span, format!("first assignment to {}", value_msg)); + } + err.emit(); + } +} + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + // End-user visible description of `place` if one can be found. If the + // place is a temporary for instance, None will be returned. + pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option { + let mut buf = String::new(); + match self.append_place_to_string(place, &mut buf, false) { + Ok(()) => Some(buf), + Err(()) => None, + } + } + + // Appends end-user visible description of `place` to `buf`. + fn append_place_to_string( + &self, + place: &Place<'tcx>, + buf: &mut String, + mut autoderef: bool, + ) -> Result<(), ()> { + match *place { + Place::Local(local) => { + self.append_local_to_string(local, buf)?; + } + Place::Static(ref static_) => { + buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id))); + } + Place::Projection(ref proj) => { + match proj.elem { + ProjectionElem::Deref => { + if let Some(field) = self.is_upvar_field_projection(&proj.base) { + let var_index = field.index(); + let name = self.mir.upvar_decls[var_index].debug_name.to_string(); + if self.mir.upvar_decls[var_index].by_ref { + buf.push_str(&name); + } else { + buf.push_str(&format!("*{}", &name)); + } + } else { + if autoderef { + self.append_place_to_string(&proj.base, buf, autoderef)?; + } else { + buf.push_str(&"*"); + self.append_place_to_string(&proj.base, buf, autoderef)?; + } + } + } + ProjectionElem::Downcast(..) => { + self.append_place_to_string(&proj.base, buf, autoderef)?; + } + ProjectionElem::Field(field, _ty) => { + autoderef = true; + + if let Some(field) = self.is_upvar_field_projection(place) { + let var_index = field.index(); + let name = self.mir.upvar_decls[var_index].debug_name.to_string(); + buf.push_str(&name); + } else { + let field_name = self.describe_field(&proj.base, field); + self.append_place_to_string(&proj.base, buf, autoderef)?; + buf.push_str(&format!(".{}", field_name)); + } + } + ProjectionElem::Index(index) => { + autoderef = true; + + self.append_place_to_string(&proj.base, buf, autoderef)?; + buf.push_str("["); + if let Err(_) = self.append_local_to_string(index, buf) { + buf.push_str(".."); + } + buf.push_str("]"); + } + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { + autoderef = true; + // Since it isn't possible to borrow an element on a particular index and + // then use another while the borrow is held, don't output indices details + // to avoid confusing the end-user + self.append_place_to_string(&proj.base, buf, autoderef)?; + buf.push_str(&"[..]"); + } + }; + } + } + + Ok(()) + } + + // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have + // a name, then `Err` is returned + fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> { + let local = &self.mir.local_decls[local_index]; + match local.name { + Some(name) => { + buf.push_str(&format!("{}", name)); + Ok(()) + } + None => Err(()), + } + } + + // End-user visible description of the `field`nth field of `base` + fn describe_field(&self, base: &Place, field: Field) -> String { + match *base { + Place::Local(local) => { + let local = &self.mir.local_decls[local]; + self.describe_field_from_ty(&local.ty, field) + } + Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Deref => self.describe_field(&proj.base, field), + ProjectionElem::Downcast(def, variant_index) => { + format!("{}", def.variants[variant_index].fields[field.index()].name) + } + ProjectionElem::Field(_, field_type) => { + self.describe_field_from_ty(&field_type, field) + } + ProjectionElem::Index(..) | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } => { + format!("{}", self.describe_field(&proj.base, field)) + } + }, + } + } + + // End-user visible description of the `field_index`nth field of `ty` + fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String { + if ty.is_box() { + // If the type is a box, the field is described from the boxed type + self.describe_field_from_ty(&ty.boxed_ty(), field) + } else { + match ty.sty { + ty::TyAdt(def, _) => if def.is_enum() { + format!("{}", field.index()) + } else { + format!("{}", def.struct_variant().fields[field.index()].name) + }, + ty::TyTuple(_, _) => format!("{}", field.index()), + ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => { + self.describe_field_from_ty(&tnm.ty, field) + } + ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field), + ty::TyClosure(closure_def_id, _) => { + // Convert the def-id into a node-id. node-ids are only valid for + // the local code in the current crate, so this returns an `Option` in case + // the closure comes from another crate. But in that case we wouldn't + // be borrowck'ing it, so we can just unwrap: + let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap(); + let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]); + + self.tcx.hir.name(freevar.var_id()).to_string() + } + _ => { + // Might need a revision when the fields in trait RFC is implemented + // (https://github.com/rust-lang/rfcs/pull/1546) + bug!( + "End-user description not implemented for field access on `{:?}`", + ty.sty + ); + } + } + } + } + + // Retrieve span of given borrow from the current MIR representation + fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span { + self.mir.source_info(borrow.location).span + } +} diff --git a/src/librustc_mir/borrow_check/flow.rs b/src/librustc_mir/borrow_check/flow.rs new file mode 100644 index 00000000000..5e9cee6c4b5 --- /dev/null +++ b/src/librustc_mir/borrow_check/flow.rs @@ -0,0 +1,335 @@ +// 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. + +//! Manages the dataflow bits required for borrowck. +//! +//! FIXME: this might be better as a "generic" fixed-point combinator, +//! but is not as ugly as it is right now. + +use rustc::mir::{BasicBlock, Location}; +use rustc_data_structures::indexed_set::{self, IdxSetBuf}; +use rustc_data_structures::indexed_vec::Idx; + +use dataflow::{BitDenotation, BlockSets, DataflowResults}; +use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; +use dataflow::{EverInitializedLvals, MovingOutStatements}; +use dataflow::Borrows; +use dataflow::move_paths::{HasMoveData, MovePathIndex}; +use std::fmt; + +// (forced to be `pub` due to its use as an associated type below.) +pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> { + pub borrows: FlowInProgress>, + pub inits: FlowInProgress>, + pub uninits: FlowInProgress>, + pub move_outs: FlowInProgress>, + pub ever_inits: FlowInProgress>, +} + +pub struct FlowInProgress +where + BD: BitDenotation, +{ + base_results: DataflowResults, + curr_state: IdxSetBuf, + stmt_gen: IdxSetBuf, + stmt_kill: IdxSetBuf, +} + +impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { + pub fn new( + borrows: FlowInProgress>, + inits: FlowInProgress>, + uninits: FlowInProgress>, + move_outs: FlowInProgress>, + ever_inits: FlowInProgress>, + ) -> Self { + InProgress { + borrows, + inits, + uninits, + move_outs, + ever_inits, + } + } + + fn each_flow( + &mut self, + mut xform_borrows: XB, + mut xform_inits: XI, + mut xform_uninits: XU, + mut xform_move_outs: XM, + mut xform_ever_inits: XE, + ) where + XB: FnMut(&mut FlowInProgress>), + XI: FnMut(&mut FlowInProgress>), + XU: FnMut(&mut FlowInProgress>), + XM: FnMut(&mut FlowInProgress>), + XE: FnMut(&mut FlowInProgress>), + { + xform_borrows(&mut self.borrows); + xform_inits(&mut self.inits); + xform_uninits(&mut self.uninits); + xform_move_outs(&mut self.move_outs); + xform_ever_inits(&mut self.ever_inits); + } + + pub fn reset_to_entry_of(&mut self, bb: BasicBlock) { + self.each_flow( + |b| b.reset_to_entry_of(bb), + |i| i.reset_to_entry_of(bb), + |u| u.reset_to_entry_of(bb), + |m| m.reset_to_entry_of(bb), + |e| e.reset_to_entry_of(bb), + ); + } + + pub fn reconstruct_statement_effect( + &mut self, + location: Location, + ) { + self.each_flow( + |b| b.reconstruct_statement_effect(location), + |i| i.reconstruct_statement_effect(location), + |u| u.reconstruct_statement_effect(location), + |m| m.reconstruct_statement_effect(location), + |e| e.reconstruct_statement_effect(location), + ); + } + + pub fn apply_local_effect(&mut self, _location: Location) { + self.each_flow( + |b| b.apply_local_effect(), + |i| i.apply_local_effect(), + |u| u.apply_local_effect(), + |m| m.apply_local_effect(), + |e| e.apply_local_effect(), + ); + } + + pub fn reconstruct_terminator_effect(&mut self, location: Location) { + self.each_flow( + |b| b.reconstruct_terminator_effect(location), + |i| i.reconstruct_terminator_effect(location), + |u| u.reconstruct_terminator_effect(location), + |m| m.reconstruct_terminator_effect(location), + |e| e.reconstruct_terminator_effect(location), + ); + } +} + +impl<'b, 'gcx, 'tcx> fmt::Display for InProgress<'b, 'gcx, 'tcx> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut s = String::new(); + + s.push_str("borrows in effect: ["); + let mut saw_one = false; + self.borrows.each_state_bit(|borrow| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; + s.push_str(&format!("{}", borrow_data)); + }); + s.push_str("] "); + + s.push_str("borrows generated: ["); + let mut saw_one = false; + self.borrows.each_gen_bit(|borrow| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; + s.push_str(&format!("{}", borrow_data)); + }); + s.push_str("] "); + + s.push_str("inits: ["); + let mut saw_one = false; + self.inits.each_state_bit(|mpi_init| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let move_path = &self.inits.base_results.operator().move_data().move_paths[mpi_init]; + s.push_str(&format!("{}", move_path)); + }); + s.push_str("] "); + + s.push_str("uninits: ["); + let mut saw_one = false; + self.uninits.each_state_bit(|mpi_uninit| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let move_path = + &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit]; + s.push_str(&format!("{}", move_path)); + }); + s.push_str("] "); + + s.push_str("move_out: ["); + let mut saw_one = false; + self.move_outs.each_state_bit(|mpi_move_out| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let move_out = &self.move_outs.base_results.operator().move_data().moves[mpi_move_out]; + s.push_str(&format!("{:?}", move_out)); + }); + s.push_str("] "); + + s.push_str("ever_init: ["); + let mut saw_one = false; + self.ever_inits.each_state_bit(|mpi_ever_init| { + if saw_one { + s.push_str(", "); + }; + saw_one = true; + let ever_init = + &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init]; + s.push_str(&format!("{:?}", ever_init)); + }); + s.push_str("]"); + + fmt::Display::fmt(&s, fmt) + } +} + +impl<'tcx, T> FlowInProgress +where + T: HasMoveData<'tcx> + BitDenotation, +{ + pub fn has_any_child_of(&self, mpi: T::Idx) -> Option { + let move_data = self.base_results.operator().move_data(); + + let mut todo = vec![mpi]; + let mut push_siblings = false; // don't look at siblings of original `mpi`. + while let Some(mpi) = todo.pop() { + if self.curr_state.contains(&mpi) { + return Some(mpi); + } + let move_path = &move_data.move_paths[mpi]; + if let Some(child) = move_path.first_child { + todo.push(child); + } + if push_siblings { + if let Some(sibling) = move_path.next_sibling { + todo.push(sibling); + } + } else { + // after we've processed the original `mpi`, we should + // always traverse the siblings of any of its + // children. + push_siblings = true; + } + } + return None; + } +} + +impl FlowInProgress +where + BD: BitDenotation, +{ + pub fn each_state_bit(&self, f: F) + where + F: FnMut(BD::Idx), + { + self.curr_state + .each_bit(self.base_results.operator().bits_per_block(), f) + } + + fn each_gen_bit(&self, f: F) + where + F: FnMut(BD::Idx), + { + self.stmt_gen + .each_bit(self.base_results.operator().bits_per_block(), f) + } + + pub fn new(results: DataflowResults) -> Self { + let bits_per_block = results.sets().bits_per_block(); + let curr_state = IdxSetBuf::new_empty(bits_per_block); + let stmt_gen = IdxSetBuf::new_empty(bits_per_block); + let stmt_kill = IdxSetBuf::new_empty(bits_per_block); + FlowInProgress { + base_results: results, + curr_state: curr_state, + stmt_gen: stmt_gen, + stmt_kill: stmt_kill, + } + } + + pub fn operator(&self) -> &BD { + self.base_results.operator() + } + + pub fn contains(&self, x: &BD::Idx) -> bool { + self.curr_state.contains(x) + } + + pub fn reset_to_entry_of(&mut self, bb: BasicBlock) { + (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index())); + } + + pub fn reconstruct_statement_effect(&mut self, loc: Location) { + self.stmt_gen.reset_to_empty(); + self.stmt_kill.reset_to_empty(); + let mut ignored = IdxSetBuf::new_empty(0); + let mut sets = BlockSets { + on_entry: &mut ignored, + gen_set: &mut self.stmt_gen, + kill_set: &mut self.stmt_kill, + }; + self.base_results + .operator() + .statement_effect(&mut sets, loc); + } + + pub fn reconstruct_terminator_effect(&mut self, loc: Location) { + self.stmt_gen.reset_to_empty(); + self.stmt_kill.reset_to_empty(); + let mut ignored = IdxSetBuf::new_empty(0); + let mut sets = BlockSets { + on_entry: &mut ignored, + gen_set: &mut self.stmt_gen, + kill_set: &mut self.stmt_kill, + }; + self.base_results + .operator() + .terminator_effect(&mut sets, loc); + } + + pub fn apply_local_effect(&mut self) { + self.curr_state.union(&self.stmt_gen); + self.curr_state.subtract(&self.stmt_kill); + } + + pub fn elems_incoming(&self) -> indexed_set::Elems { + let univ = self.base_results.sets().bits_per_block(); + self.curr_state.elems(univ) + } + + pub fn with_elems_outgoing(&self, f: F) + where + F: FnOnce(indexed_set::Elems), + { + let mut curr_state = self.curr_state.clone(); + curr_state.union(&self.stmt_gen); + curr_state.subtract(&self.stmt_kill); + let univ = self.base_results.sets().bits_per_block(); + f(curr_state.elems(univ)); + } +} diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 3edb9c16023..d3a8e2bce82 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -22,7 +22,7 @@ use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; use rustc::mir::ClosureRegionRequirements; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::indexed_set::{self, IdxSetBuf}; +use rustc_data_structures::indexed_set::{IdxSetBuf}; use rustc_data_structures::indexed_vec::Idx; use syntax::ast; @@ -30,19 +30,23 @@ use syntax_pos::Span; use dataflow::do_dataflow; use dataflow::MoveDataParamEnv; -use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer}; +use dataflow::DataflowResultsConsumer; use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{EverInitializedLvals, MovingOutStatements}; use dataflow::{BorrowData, BorrowIndex, Borrows}; use dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; -use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveOutIndex, MovePathIndex}; +use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; use util::borrowck_errors::{BorrowckErrors, Origin}; -use std::fmt; use std::iter; +use self::flow::{InProgress, FlowInProgress}; +use self::prefixes::PrefixSet; use self::MutateMode::{JustWrite, WriteAndRead}; +mod error_reporting; +mod flow; +mod prefixes; pub(crate) mod nll; pub fn provide(providers: &mut Providers) { @@ -200,20 +204,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( }; let flow_inits = flow_inits; // remove mut - let mut mbcx = MirBorrowckCtxt { - tcx: tcx, - mir: mir, - node_id: id, - move_data: &mdpe.move_data, - param_env: param_env, - locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) { - hir::BodyOwnerKind::Const | - hir::BodyOwnerKind::Static(_) => false, - hir::BodyOwnerKind::Fn => true, - }, - storage_dead_or_drop_error_reported: FxHashSet(), - }; - let flow_borrows = FlowInProgress::new(do_dataflow( tcx, mir, @@ -232,6 +222,20 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( flow_ever_inits, ); + let mut mbcx = MirBorrowckCtxt { + tcx: tcx, + mir: mir, + node_id: id, + move_data: &mdpe.move_data, + param_env: param_env, + locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) { + hir::BodyOwnerKind::Const | + hir::BodyOwnerKind::Static(_) => false, + hir::BodyOwnerKind::Fn => true, + }, + storage_dead_or_drop_error_reported: FxHashSet(), + }; + mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer opt_closure_req @@ -257,25 +261,6 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { storage_dead_or_drop_error_reported: FxHashSet, } -// (forced to be `pub` due to its use as an associated type below.) -pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> { - borrows: FlowInProgress>, - inits: FlowInProgress>, - uninits: FlowInProgress>, - move_outs: FlowInProgress>, - ever_inits: FlowInProgress>, -} - -struct FlowInProgress -where - BD: BitDenotation, -{ - base_results: DataflowResults, - curr_state: IdxSetBuf, - stmt_gen: IdxSetBuf, - stmt_kill: IdxSetBuf, -} - // Check that: // 1. assignments are always made to mutable locations (FIXME: does that still really go here?) // 2. loans made in overlapping scopes do not conflict @@ -289,13 +274,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx } 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), - |i| i.reset_to_entry_of(bb), - |u| u.reset_to_entry_of(bb), - |m| m.reset_to_entry_of(bb), - |e| e.reset_to_entry_of(bb), - ); + flow_state.reset_to_entry_of(bb); } fn reconstruct_statement_effect( @@ -303,23 +282,11 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx location: Location, flow_state: &mut Self::FlowState, ) { - flow_state.each_flow( - |b| b.reconstruct_statement_effect(location), - |i| i.reconstruct_statement_effect(location), - |u| u.reconstruct_statement_effect(location), - |m| m.reconstruct_statement_effect(location), - |e| e.reconstruct_statement_effect(location), - ); + flow_state.reconstruct_statement_effect(location); } - fn apply_local_effect(&mut self, _location: Location, flow_state: &mut Self::FlowState) { - flow_state.each_flow( - |b| b.apply_local_effect(), - |i| i.apply_local_effect(), - |u| u.apply_local_effect(), - |m| m.apply_local_effect(), - |e| e.apply_local_effect(), - ); + fn apply_local_effect(&mut self, location: Location, flow_state: &mut Self::FlowState) { + flow_state.apply_local_effect(location); } fn reconstruct_terminator_effect( @@ -327,13 +294,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx location: Location, flow_state: &mut Self::FlowState, ) { - flow_state.each_flow( - |b| b.reconstruct_terminator_effect(location), - |i| i.reconstruct_terminator_effect(location), - |u| u.reconstruct_terminator_effect(location), - |m| m.reconstruct_terminator_effect(location), - |e| e.reconstruct_terminator_effect(location), - ); + flow_state.reconstruct_terminator_effect(location); } fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) { @@ -560,7 +521,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx // Often, the storage will already have been killed by an explicit // StorageDead, but we don't always emit those (notably on unwind paths), // so this "extra check" serves as a kind of backup. - let domain = flow_state.borrows.base_results.operator(); + let domain = flow_state.borrows.operator(); let data = domain.borrows(); flow_state.borrows.with_elems_outgoing(|borrows| { for i in borrows { @@ -749,7 +710,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ReadKind::Borrow(bk) => { let end_issued_loan_span = flow_state .borrows - .base_results .operator() .opt_region_end_span(&borrow.region); error_reported = true; @@ -769,7 +729,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { WriteKind::MutableBorrow(bk) => { let end_issued_loan_span = flow_state .borrows - .base_results .operator() .opt_region_end_span(&borrow.region); error_reported = true; @@ -784,7 +743,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { WriteKind::StorageDeadOrDrop => { let end_span = flow_state .borrows - .base_results .operator() .opt_region_end_span(&borrow.region); error_reported = true; @@ -1060,7 +1018,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { match self.move_path_closest_to(place) { Ok(mpi) => for ii in &move_data.init_path_map[mpi] { - if flow_state.ever_inits.curr_state.contains(ii) { + if flow_state.ever_inits.contains(ii) { let first_assign_span = self.move_data.inits[*ii].span; self.report_illegal_reassignment(context, (place, span), first_assign_span); break; @@ -1095,7 +1053,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let place = self.base_path(place_span.0); let maybe_uninits = &flow_state.uninits; - let curr_move_outs = &flow_state.move_outs.curr_state; + let curr_move_outs = &flow_state.move_outs; // Bad scenarios: // @@ -1136,7 +1094,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { debug!("check_if_path_is_moved part1 place: {:?}", place); match self.move_path_closest_to(place) { Ok(mpi) => { - if maybe_uninits.curr_state.contains(&mpi) { + if maybe_uninits.contains(&mpi) { self.report_use_of_moved_or_uninitialized( context, desired_action, @@ -1521,6 +1479,32 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } } + + + /// If this is a field projection, and the field is being projected from a closure type, + /// then returns the index of the field being projected. Note that this closure will always + /// be `self` in the current MIR, because that is the only time we directly access the fields + /// of a closure type. + fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option { + match *place { + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Field(field, _ty) => { + let is_projection_from_ty_closure = proj.base + .ty(self.mir, self.tcx) + .to_ty(self.tcx) + .is_closure(); + + if is_projection_from_ty_closure { + Some(field) + } else { + None + } + } + _ => None, + }, + _ => None, + } + } } #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -1886,8 +1870,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // FIXME: analogous code in check_loans first maps `place` to // its base_path. - let domain = flow_state.borrows.base_results.operator(); - let data = domain.borrows(); + let data = flow_state.borrows.operator().borrows(); // check for loan restricting path P being used. Accounts for // borrows of P, P.a.b, etc. @@ -1902,738 +1885,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } -use self::prefixes::PrefixSet; - -/// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an -/// place are formed by stripping away fields and derefs, except that -/// we stop when we reach the deref of a shared reference. [...] " -/// -/// "Shallow prefixes are found by stripping away fields, but stop at -/// any dereference. So: writing a path like `a` is illegal if `a.b` -/// is borrowed. But: writing `a` is legal if `*a` is borrowed, -/// whether or not `a` is a shared or mutable reference. [...] " -mod prefixes { - use super::MirBorrowckCtxt; - - use rustc::hir; - use rustc::ty::{self, TyCtxt}; - use rustc::mir::{Mir, Place, ProjectionElem}; - - pub trait IsPrefixOf<'tcx> { - fn is_prefix_of(&self, other: &Place<'tcx>) -> bool; - } - - impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> { - fn is_prefix_of(&self, other: &Place<'tcx>) -> bool { - let mut cursor = other; - loop { - if self == cursor { - return true; - } - - match *cursor { - Place::Local(_) | Place::Static(_) => return false, - Place::Projection(ref proj) => { - cursor = &proj.base; - } - } - } - } - } - - - pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> { - mir: &'cx Mir<'tcx>, - tcx: TyCtxt<'cx, 'gcx, 'tcx>, - kind: PrefixSet, - next: Option<&'cx Place<'tcx>>, - } - - #[derive(Copy, Clone, PartialEq, Eq, Debug)] - #[allow(dead_code)] - pub(super) enum PrefixSet { - /// Doesn't stop until it returns the base case (a Local or - /// Static prefix). - All, - /// Stops at any dereference. - Shallow, - /// Stops at the deref of a shared reference. - Supporting, - } - - impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - /// Returns an iterator over the prefixes of `place` - /// (inclusive) from longest to smallest, potentially - /// terminating the iteration early based on `kind`. - pub(super) fn prefixes( - &self, - place: &'cx Place<'tcx>, - kind: PrefixSet, - ) -> Prefixes<'cx, 'gcx, 'tcx> { - Prefixes { - next: Some(place), - kind, - mir: self.mir, - tcx: self.tcx, - } - } - } - - impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> { - type Item = &'cx Place<'tcx>; - fn next(&mut self) -> Option { - let mut cursor = self.next?; - - // Post-processing `place`: Enqueue any remaining - // work. Also, `place` may not be a prefix itself, but - // may hold one further down (e.g. we never return - // downcasts here, but may return a base of a downcast). - - 'cursor: loop { - let proj = match *cursor { - Place::Local(_) | // search yielded this leaf - Place::Static(_) => { - self.next = None; - return Some(cursor); - } - - Place::Projection(ref proj) => proj, - }; - - match proj.elem { - ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { - // FIXME: add union handling - self.next = Some(&proj.base); - return Some(cursor); - } - ProjectionElem::Downcast(..) | - ProjectionElem::Subslice { .. } | - ProjectionElem::ConstantIndex { .. } | - ProjectionElem::Index(_) => { - cursor = &proj.base; - continue 'cursor; - } - ProjectionElem::Deref => { - // (handled below) - } - } - - assert_eq!(proj.elem, ProjectionElem::Deref); - - match self.kind { - PrefixSet::Shallow => { - // shallow prefixes are found by stripping away - // fields, but stop at *any* dereference. - // So we can just stop the traversal now. - self.next = None; - return Some(cursor); - } - PrefixSet::All => { - // all prefixes: just blindly enqueue the base - // of the projection - self.next = Some(&proj.base); - return Some(cursor); - } - PrefixSet::Supporting => { - // fall through! - } - } - - assert_eq!(self.kind, PrefixSet::Supporting); - // supporting prefixes: strip away fields and - // derefs, except we stop at the deref of a shared - // reference. - - let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); - match ty.sty { - ty::TyRawPtr(_) | - ty::TyRef( - _, /*rgn*/ - ty::TypeAndMut { - ty: _, - mutbl: hir::MutImmutable, - }, - ) => { - // don't continue traversing over derefs of raw pointers or shared borrows. - self.next = None; - return Some(cursor); - } - - ty::TyRef( - _, /*rgn*/ - ty::TypeAndMut { - ty: _, - mutbl: hir::MutMutable, - }, - ) => { - self.next = Some(&proj.base); - return Some(cursor); - } - - ty::TyAdt(..) if ty.is_box() => { - self.next = Some(&proj.base); - return Some(cursor); - } - - _ => panic!("unknown type fed to Projection Deref."), - } - } - } - } -} - -impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - fn report_use_of_moved_or_uninitialized( - &mut self, - _context: Context, - desired_action: InitializationRequiringAction, - (place, span): (&Place<'tcx>, Span), - mpi: MovePathIndex, - curr_move_out: &IdxSetBuf, - ) { - let mois = self.move_data.path_map[mpi] - .iter() - .filter(|moi| curr_move_out.contains(moi)) - .collect::>(); - - if mois.is_empty() { - let item_msg = match self.describe_place(place) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - self.tcx - .cannot_act_on_uninitialized_variable( - span, - desired_action.as_noun(), - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir, - ) - .span_label(span, format!("use of possibly uninitialized {}", item_msg)) - .emit(); - } else { - let msg = ""; //FIXME: add "partially " or "collaterally " - - let mut err = self.tcx.cannot_act_on_moved_value( - span, - desired_action.as_noun(), - msg, - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir, - ); - - err.span_label( - span, - format!( - "value {} here after move", - desired_action.as_verb_in_past_tense() - ), - ); - for moi in mois { - let move_msg = ""; //FIXME: add " (into closure)" - let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span; - if span == move_span { - err.span_label( - span, - format!("value moved{} here in previous iteration of loop", move_msg), - ); - } else { - err.span_label(move_span, format!("value moved{} here", move_msg)); - }; - } - //FIXME: add note for closure - err.emit(); - } - } - - fn report_move_out_while_borrowed( - &mut self, - _context: Context, - (place, span): (&Place<'tcx>, Span), - borrow: &BorrowData<'tcx>, - ) { - let value_msg = match self.describe_place(place) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - let borrow_msg = match self.describe_place(&borrow.place) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - self.tcx - .cannot_move_when_borrowed( - span, - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir, - ) - .span_label( - self.retrieve_borrow_span(borrow), - format!("borrow of {} occurs here", borrow_msg), - ) - .span_label(span, format!("move out of {} occurs here", value_msg)) - .emit(); - } - - fn report_use_while_mutably_borrowed( - &mut self, - _context: Context, - (place, span): (&Place<'tcx>, Span), - borrow: &BorrowData<'tcx>, - ) { - let mut err = self.tcx.cannot_use_when_mutably_borrowed( - span, - &self.describe_place(place).unwrap_or("_".to_owned()), - self.retrieve_borrow_span(borrow), - &self.describe_place(&borrow.place).unwrap_or("_".to_owned()), - Origin::Mir, - ); - - err.emit(); - } - - /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of - /// the local assigned at `location`. - /// This is done by searching in statements succeeding `location` - /// and originating from `maybe_closure_span`. - fn find_closure_span( - &self, - maybe_closure_span: Span, - location: Location, - ) -> Option<(Span, Span)> { - use rustc::hir::ExprClosure; - use rustc::mir::AggregateKind; - - let local = if let StatementKind::Assign(Place::Local(local), _) = - self.mir[location.block].statements[location.statement_index].kind - { - local - } else { - return None; - }; - - for stmt in &self.mir[location.block].statements[location.statement_index + 1..] { - if maybe_closure_span != stmt.source_info.span { - break; - } - - if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind { - if let AggregateKind::Closure(def_id, _) = **kind { - debug!("find_closure_span: found closure {:?}", places); - - return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { - let args_span = if let ExprClosure(_, _, _, span, _) = - self.tcx.hir.expect_expr(node_id).node - { - span - } else { - return None; - }; - - self.tcx - .with_freevars(node_id, |freevars| { - for (v, place) in freevars.iter().zip(places) { - match *place { - Operand::Copy(Place::Local(l)) | - Operand::Move(Place::Local(l)) if local == l => - { - debug!( - "find_closure_span: found captured local {:?}", - l - ); - return Some(v.span); - } - _ => {} - } - } - None - }) - .map(|var_span| (args_span, var_span)) - } else { - None - }; - } - } - } - - None - } - - fn report_conflicting_borrow( - &mut self, - context: Context, - (place, span): (&Place<'tcx>, Span), - gen_borrow_kind: BorrowKind, - issued_borrow: &BorrowData, - end_issued_loan_span: Option, - ) { - let issued_span = self.retrieve_borrow_span(issued_borrow); - - let new_closure_span = self.find_closure_span(span, context.loc); - let span = new_closure_span.map(|(args, _)| args).unwrap_or(span); - let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location); - let issued_span = old_closure_span - .map(|(args, _)| args) - .unwrap_or(issued_span); - - let desc_place = self.describe_place(place).unwrap_or("_".to_owned()); - - // FIXME: supply non-"" `opt_via` when appropriate - let mut err = match ( - gen_borrow_kind, - "immutable", - "mutable", - issued_borrow.kind, - "immutable", - "mutable", - ) { - (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) | - (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx - .cannot_reborrow_already_borrowed( - span, - &desc_place, - "", - lft, - issued_span, - "it", - rgt, - "", - end_issued_loan_span, - Origin::Mir, - ), - - (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx - .cannot_mutably_borrow_multiply( - span, - &desc_place, - "", - issued_span, - "", - end_issued_loan_span, - Origin::Mir, - ), - - (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx - .cannot_uniquely_borrow_by_two_closures( - span, - &desc_place, - issued_span, - end_issued_loan_span, - Origin::Mir, - ), - - (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure( - span, - &desc_place, - "", - issued_span, - "it", - "", - end_issued_loan_span, - Origin::Mir, - ), - - (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => self.tcx - .cannot_reborrow_already_uniquely_borrowed( - span, - &desc_place, - "", - lft, - issued_span, - "", - end_issued_loan_span, - Origin::Mir, - ), - - (BorrowKind::Mut, _, lft, BorrowKind::Unique, _, _) => self.tcx - .cannot_reborrow_already_uniquely_borrowed( - span, - &desc_place, - "", - lft, - issued_span, - "", - end_issued_loan_span, - Origin::Mir, - ), - - (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(), - }; - - if let Some((_, var_span)) = old_closure_span { - err.span_label( - var_span, - format!( - "previous borrow occurs due to use of `{}` in closure", - desc_place - ), - ); - } - - if let Some((_, var_span)) = new_closure_span { - err.span_label( - var_span, - format!("borrow occurs due to use of `{}` in closure", desc_place), - ); - } - - err.emit(); - } - - fn report_borrowed_value_does_not_live_long_enough( - &mut self, - _: Context, - (place, span): (&Place<'tcx>, Span), - end_span: Option, - ) { - let root_place = self.prefixes(place, PrefixSet::All).last().unwrap(); - let proper_span = match *root_place { - Place::Local(local) => self.mir.local_decls[local].source_info.span, - _ => span, - }; - let mut err = self.tcx - .path_does_not_live_long_enough(span, "borrowed value", Origin::Mir); - err.span_label(proper_span, "temporary value created here"); - err.span_label(span, "temporary value dropped here while still borrowed"); - err.note("consider using a `let` binding to increase its lifetime"); - - if let Some(end) = end_span { - err.span_label(end, "temporary value needs to live until here"); - } - - err.emit(); - } - - fn report_illegal_mutation_of_borrowed( - &mut self, - _: Context, - (place, span): (&Place<'tcx>, Span), - loan: &BorrowData, - ) { - let mut err = self.tcx.cannot_assign_to_borrowed( - span, - self.retrieve_borrow_span(loan), - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir, - ); - - err.emit(); - } - - fn report_illegal_reassignment( - &mut self, - _context: Context, - (place, span): (&Place<'tcx>, Span), - assigned_span: Span, - ) { - let mut err = self.tcx.cannot_reassign_immutable( - span, - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir, - ); - err.span_label(span, "cannot assign twice to immutable variable"); - if span != assigned_span { - let value_msg = match self.describe_place(place) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - err.span_label(assigned_span, format!("first assignment to {}", value_msg)); - } - err.emit(); - } -} - -impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - // End-user visible description of `place` if one can be found. If the - // place is a temporary for instance, None will be returned. - fn describe_place(&self, place: &Place<'tcx>) -> Option { - let mut buf = String::new(); - match self.append_place_to_string(place, &mut buf, false) { - Ok(()) => Some(buf), - Err(()) => None, - } - } - - /// If this is a field projection, and the field is being projected from a closure type, - /// then returns the index of the field being projected. Note that this closure will always - /// be `self` in the current MIR, because that is the only time we directly access the fields - /// of a closure type. - fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option { - match *place { - Place::Projection(ref proj) => match proj.elem { - ProjectionElem::Field(field, _ty) => { - let is_projection_from_ty_closure = proj.base - .ty(self.mir, self.tcx) - .to_ty(self.tcx) - .is_closure(); - - if is_projection_from_ty_closure { - Some(field) - } else { - None - } - } - _ => None, - }, - _ => None, - } - } - - // Appends end-user visible description of `place` to `buf`. - fn append_place_to_string( - &self, - place: &Place<'tcx>, - buf: &mut String, - mut autoderef: bool, - ) -> Result<(), ()> { - match *place { - Place::Local(local) => { - self.append_local_to_string(local, buf)?; - } - Place::Static(ref static_) => { - buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id))); - } - Place::Projection(ref proj) => { - match proj.elem { - ProjectionElem::Deref => { - if let Some(field) = self.is_upvar_field_projection(&proj.base) { - let var_index = field.index(); - let name = self.mir.upvar_decls[var_index].debug_name.to_string(); - if self.mir.upvar_decls[var_index].by_ref { - buf.push_str(&name); - } else { - buf.push_str(&format!("*{}", &name)); - } - } else { - if autoderef { - self.append_place_to_string(&proj.base, buf, autoderef)?; - } else { - buf.push_str(&"*"); - self.append_place_to_string(&proj.base, buf, autoderef)?; - } - } - } - ProjectionElem::Downcast(..) => { - self.append_place_to_string(&proj.base, buf, autoderef)?; - } - ProjectionElem::Field(field, _ty) => { - autoderef = true; - - if let Some(field) = self.is_upvar_field_projection(place) { - let var_index = field.index(); - let name = self.mir.upvar_decls[var_index].debug_name.to_string(); - buf.push_str(&name); - } else { - let field_name = self.describe_field(&proj.base, field); - self.append_place_to_string(&proj.base, buf, autoderef)?; - buf.push_str(&format!(".{}", field_name)); - } - } - ProjectionElem::Index(index) => { - autoderef = true; - - self.append_place_to_string(&proj.base, buf, autoderef)?; - buf.push_str("["); - if let Err(_) = self.append_local_to_string(index, buf) { - buf.push_str(".."); - } - buf.push_str("]"); - } - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { - autoderef = true; - // Since it isn't possible to borrow an element on a particular index and - // then use another while the borrow is held, don't output indices details - // to avoid confusing the end-user - self.append_place_to_string(&proj.base, buf, autoderef)?; - buf.push_str(&"[..]"); - } - }; - } - } - - Ok(()) - } - - // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have - // a name, then `Err` is returned - fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> { - let local = &self.mir.local_decls[local_index]; - match local.name { - Some(name) => { - buf.push_str(&format!("{}", name)); - Ok(()) - } - None => Err(()), - } - } - - // End-user visible description of the `field`nth field of `base` - fn describe_field(&self, base: &Place, field: Field) -> String { - match *base { - Place::Local(local) => { - let local = &self.mir.local_decls[local]; - self.describe_field_from_ty(&local.ty, field) - } - Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), - Place::Projection(ref proj) => match proj.elem { - ProjectionElem::Deref => self.describe_field(&proj.base, field), - ProjectionElem::Downcast(def, variant_index) => { - format!("{}", def.variants[variant_index].fields[field.index()].name) - } - ProjectionElem::Field(_, field_type) => { - self.describe_field_from_ty(&field_type, field) - } - ProjectionElem::Index(..) | - ProjectionElem::ConstantIndex { .. } | - ProjectionElem::Subslice { .. } => { - format!("{}", self.describe_field(&proj.base, field)) - } - }, - } - } - - // End-user visible description of the `field_index`nth field of `ty` - fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String { - if ty.is_box() { - // If the type is a box, the field is described from the boxed type - self.describe_field_from_ty(&ty.boxed_ty(), field) - } else { - match ty.sty { - ty::TyAdt(def, _) => if def.is_enum() { - format!("{}", field.index()) - } else { - format!("{}", def.struct_variant().fields[field.index()].name) - }, - ty::TyTuple(_, _) => format!("{}", field.index()), - ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => { - self.describe_field_from_ty(&tnm.ty, field) - } - ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field), - ty::TyClosure(closure_def_id, _) => { - // Convert the def-id into a node-id. node-ids are only valid for - // the local code in the current crate, so this returns an `Option` in case - // the closure comes from another crate. But in that case we wouldn't - // be borrowck'ing it, so we can just unwrap: - let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap(); - let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]); - - self.tcx.hir.name(freevar.var_id()).to_string() - } - _ => { - // Might need a revision when the fields in trait RFC is implemented - // (https://github.com/rust-lang/rfcs/pull/1546) - bug!( - "End-user description not implemented for field access on `{:?}`", - ty.sty - ); - } - } - } - } - - // Retrieve span of given borrow from the current MIR representation - fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span { - self.mir.source_info(borrow.location).span - } -} impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // FIXME (#16118): function intended to allow the borrow checker @@ -2694,243 +1945,3 @@ impl ContextKind { } } } - -impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { - fn new( - borrows: FlowInProgress>, - inits: FlowInProgress>, - uninits: FlowInProgress>, - move_outs: FlowInProgress>, - ever_inits: FlowInProgress>, - ) -> Self { - InProgress { - borrows, - inits, - uninits, - move_outs, - ever_inits, - } - } - - fn each_flow( - &mut self, - mut xform_borrows: XB, - mut xform_inits: XI, - mut xform_uninits: XU, - mut xform_move_outs: XM, - mut xform_ever_inits: XE, - ) where - XB: FnMut(&mut FlowInProgress>), - XI: FnMut(&mut FlowInProgress>), - XU: FnMut(&mut FlowInProgress>), - XM: FnMut(&mut FlowInProgress>), - XE: FnMut(&mut FlowInProgress>), - { - xform_borrows(&mut self.borrows); - xform_inits(&mut self.inits); - xform_uninits(&mut self.uninits); - xform_move_outs(&mut self.move_outs); - xform_ever_inits(&mut self.ever_inits); - } -} - -impl<'b, 'gcx, 'tcx> fmt::Display for InProgress<'b, 'gcx, 'tcx> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let mut s = String::new(); - - s.push_str("borrows in effect: ["); - let mut saw_one = false; - self.borrows.each_state_bit(|borrow| { - if saw_one { - s.push_str(", "); - }; - saw_one = true; - let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; - s.push_str(&format!("{}", borrow_data)); - }); - s.push_str("] "); - - s.push_str("borrows generated: ["); - let mut saw_one = false; - self.borrows.each_gen_bit(|borrow| { - if saw_one { - s.push_str(", "); - }; - saw_one = true; - let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; - s.push_str(&format!("{}", borrow_data)); - }); - s.push_str("] "); - - s.push_str("inits: ["); - let mut saw_one = false; - self.inits.each_state_bit(|mpi_init| { - if saw_one { - s.push_str(", "); - }; - saw_one = true; - let move_path = &self.inits.base_results.operator().move_data().move_paths[mpi_init]; - s.push_str(&format!("{}", move_path)); - }); - s.push_str("] "); - - s.push_str("uninits: ["); - let mut saw_one = false; - self.uninits.each_state_bit(|mpi_uninit| { - if saw_one { - s.push_str(", "); - }; - saw_one = true; - let move_path = - &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit]; - s.push_str(&format!("{}", move_path)); - }); - s.push_str("] "); - - s.push_str("move_out: ["); - let mut saw_one = false; - self.move_outs.each_state_bit(|mpi_move_out| { - if saw_one { - s.push_str(", "); - }; - saw_one = true; - let move_out = &self.move_outs.base_results.operator().move_data().moves[mpi_move_out]; - s.push_str(&format!("{:?}", move_out)); - }); - s.push_str("] "); - - s.push_str("ever_init: ["); - let mut saw_one = false; - self.ever_inits.each_state_bit(|mpi_ever_init| { - if saw_one { - s.push_str(", "); - }; - saw_one = true; - let ever_init = - &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init]; - s.push_str(&format!("{:?}", ever_init)); - }); - s.push_str("]"); - - fmt::Display::fmt(&s, fmt) - } -} - -impl<'tcx, T> FlowInProgress -where - T: HasMoveData<'tcx> + BitDenotation, -{ - fn has_any_child_of(&self, mpi: T::Idx) -> Option { - let move_data = self.base_results.operator().move_data(); - - let mut todo = vec![mpi]; - let mut push_siblings = false; // don't look at siblings of original `mpi`. - while let Some(mpi) = todo.pop() { - if self.curr_state.contains(&mpi) { - return Some(mpi); - } - let move_path = &move_data.move_paths[mpi]; - if let Some(child) = move_path.first_child { - todo.push(child); - } - if push_siblings { - if let Some(sibling) = move_path.next_sibling { - todo.push(sibling); - } - } else { - // after we've processed the original `mpi`, we should - // always traverse the siblings of any of its - // children. - push_siblings = true; - } - } - return None; - } -} - -impl FlowInProgress -where - BD: BitDenotation, -{ - fn each_state_bit(&self, f: F) - where - F: FnMut(BD::Idx), - { - self.curr_state - .each_bit(self.base_results.operator().bits_per_block(), f) - } - - fn each_gen_bit(&self, f: F) - where - F: FnMut(BD::Idx), - { - self.stmt_gen - .each_bit(self.base_results.operator().bits_per_block(), f) - } - - fn new(results: DataflowResults) -> Self { - let bits_per_block = results.sets().bits_per_block(); - let curr_state = IdxSetBuf::new_empty(bits_per_block); - let stmt_gen = IdxSetBuf::new_empty(bits_per_block); - let stmt_kill = IdxSetBuf::new_empty(bits_per_block); - FlowInProgress { - base_results: results, - curr_state: curr_state, - stmt_gen: stmt_gen, - stmt_kill: stmt_kill, - } - } - - fn reset_to_entry_of(&mut self, bb: BasicBlock) { - (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index())); - } - - fn reconstruct_statement_effect(&mut self, loc: Location) { - self.stmt_gen.reset_to_empty(); - self.stmt_kill.reset_to_empty(); - let mut ignored = IdxSetBuf::new_empty(0); - let mut sets = BlockSets { - on_entry: &mut ignored, - gen_set: &mut self.stmt_gen, - kill_set: &mut self.stmt_kill, - }; - self.base_results - .operator() - .statement_effect(&mut sets, loc); - } - - fn reconstruct_terminator_effect(&mut self, loc: Location) { - self.stmt_gen.reset_to_empty(); - self.stmt_kill.reset_to_empty(); - let mut ignored = IdxSetBuf::new_empty(0); - let mut sets = BlockSets { - on_entry: &mut ignored, - gen_set: &mut self.stmt_gen, - kill_set: &mut self.stmt_kill, - }; - self.base_results - .operator() - .terminator_effect(&mut sets, loc); - } - - fn apply_local_effect(&mut self) { - self.curr_state.union(&self.stmt_gen); - self.curr_state.subtract(&self.stmt_kill); - } - - fn elems_incoming(&self) -> indexed_set::Elems { - let univ = self.base_results.sets().bits_per_block(); - self.curr_state.elems(univ) - } - - fn with_elems_outgoing(&self, f: F) - where - F: FnOnce(indexed_set::Elems), - { - let mut curr_state = self.curr_state.clone(); - curr_state.union(&self.stmt_gen); - curr_state.subtract(&self.stmt_kill); - let univ = self.base_results.sets().bits_per_block(); - f(curr_state.elems(univ)); - } -} diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 42225536357..a954d9f15e6 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -172,7 +172,6 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { "add_liveness_constraints: location={:?} initialized={:?}", location, &self.flow_inits - .base_results .operator() .move_data() .move_paths[mpi_init] diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/src/librustc_mir/borrow_check/prefixes.rs new file mode 100644 index 00000000000..645c935de09 --- /dev/null +++ b/src/librustc_mir/borrow_check/prefixes.rs @@ -0,0 +1,189 @@ +// 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. + +//! From the NLL RFC: "The deep [aka 'supporting'] prefixes for an +//! place are formed by stripping away fields and derefs, except that +//! we stop when we reach the deref of a shared reference. [...] " +//! +//! "Shallow prefixes are found by stripping away fields, but stop at +//! any dereference. So: writing a path like `a` is illegal if `a.b` +//! is borrowed. But: writing `a` is legal if `*a` is borrowed, +//! whether or not `a` is a shared or mutable reference. [...] " + +use super::MirBorrowckCtxt; + +use rustc::hir; +use rustc::ty::{self, TyCtxt}; +use rustc::mir::{Mir, Place, ProjectionElem}; + +pub trait IsPrefixOf<'tcx> { + fn is_prefix_of(&self, other: &Place<'tcx>) -> bool; +} + +impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> { + fn is_prefix_of(&self, other: &Place<'tcx>) -> bool { + let mut cursor = other; + loop { + if self == cursor { + return true; + } + + match *cursor { + Place::Local(_) | Place::Static(_) => return false, + Place::Projection(ref proj) => { + cursor = &proj.base; + } + } + } + } +} + + +pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + mir: &'cx Mir<'tcx>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + kind: PrefixSet, + next: Option<&'cx Place<'tcx>>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[allow(dead_code)] +pub(super) enum PrefixSet { + /// Doesn't stop until it returns the base case (a Local or + /// Static prefix). + All, + /// Stops at any dereference. + Shallow, + /// Stops at the deref of a shared reference. + Supporting, +} + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + /// Returns an iterator over the prefixes of `place` + /// (inclusive) from longest to smallest, potentially + /// terminating the iteration early based on `kind`. + pub(super) fn prefixes( + &self, + place: &'cx Place<'tcx>, + kind: PrefixSet, + ) -> Prefixes<'cx, 'gcx, 'tcx> { + Prefixes { + next: Some(place), + kind, + mir: self.mir, + tcx: self.tcx, + } + } +} + +impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> { + type Item = &'cx Place<'tcx>; + fn next(&mut self) -> Option { + let mut cursor = match self.next { + None => return None, + Some(place) => place, + }; + + // Post-processing `place`: Enqueue any remaining + // work. Also, `place` may not be a prefix itself, but + // may hold one further down (e.g. we never return + // downcasts here, but may return a base of a downcast). + + 'cursor: loop { + let proj = match *cursor { + Place::Local(_) | // search yielded this leaf + Place::Static(_) => { + self.next = None; + return Some(cursor); + } + + Place::Projection(ref proj) => proj, + }; + + match proj.elem { + ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { + // FIXME: add union handling + self.next = Some(&proj.base); + return Some(cursor); + } + ProjectionElem::Downcast(..) | + ProjectionElem::Subslice { .. } | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Index(_) => { + cursor = &proj.base; + continue 'cursor; + } + ProjectionElem::Deref => { + // (handled below) + } + } + + assert_eq!(proj.elem, ProjectionElem::Deref); + + match self.kind { + PrefixSet::Shallow => { + // shallow prefixes are found by stripping away + // fields, but stop at *any* dereference. + // So we can just stop the traversal now. + self.next = None; + return Some(cursor); + } + PrefixSet::All => { + // all prefixes: just blindly enqueue the base + // of the projection + self.next = Some(&proj.base); + return Some(cursor); + } + PrefixSet::Supporting => { + // fall through! + } + } + + assert_eq!(self.kind, PrefixSet::Supporting); + // supporting prefixes: strip away fields and + // derefs, except we stop at the deref of a shared + // reference. + + let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + match ty.sty { + ty::TyRawPtr(_) | + ty::TyRef( + _, /*rgn*/ + ty::TypeAndMut { + ty: _, + mutbl: hir::MutImmutable, + }, + ) => { + // don't continue traversing over derefs of raw pointers or shared borrows. + self.next = None; + return Some(cursor); + } + + ty::TyRef( + _, /*rgn*/ + ty::TypeAndMut { + ty: _, + mutbl: hir::MutMutable, + }, + ) => { + self.next = Some(&proj.base); + return Some(cursor); + } + + ty::TyAdt(..) if ty.is_box() => { + self.next = Some(&proj.base); + return Some(cursor); + } + + _ => panic!("unknown type fed to Projection Deref."), + } + } + } +}