diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index 1c2289c758de..6ba3f5054797 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -25,7 +25,7 @@ use rustc_data_structures::indexed_set::{self, IdxSetBuf}; use rustc_data_structures::indexed_vec::{Idx}; use syntax::ast::{self}; -use syntax_pos::{DUMMY_SP, Span}; +use syntax_pos::{DUMMY_SP, Span, MultiSpan}; use dataflow::{do_dataflow}; use dataflow::{MoveDataParamEnv}; @@ -287,11 +287,14 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx } StatementKind::StorageDead(local) => { - if self.storage_drop_or_dead_error_reported.insert(local) { - self.access_lvalue(ContextKind::StorageDead.new(location), - (&Lvalue::Local(local), span), - (Shallow(None), Write(WriteKind::StorageDead)), - flow_state); + if !self.storage_drop_or_dead_error_reported.contains(&local) { + let error_reported = self.access_lvalue(ContextKind::StorageDead.new(location), + (&Lvalue::Local(local), span), + (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), flow_state); + + if error_reported { + self.storage_drop_or_dead_error_reported.insert(local); + } } } } @@ -435,24 +438,30 @@ enum ReadKind { #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum WriteKind { - StorageDead, + StorageDeadOrDrop, MutableBorrow(BorrowKind), Mutate, Move, } impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + /// Checks an access to the given lvalue to see if it is allowed. Examines the set of borrows + /// that are in scope, as well as which paths have been initialized, to ensure that (a) the + /// lvalue is initialized and (b) it is not borrowed in some way that would prevent this + /// access. + /// + /// Returns true if an error is reported, false otherwise. fn access_lvalue(&mut self, context: Context, lvalue_span: (&Lvalue<'tcx>, Span), kind: (ShallowOrDeep, ReadOrWrite), - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { - + flow_state: &InProgress<'cx, 'gcx, 'tcx>) -> bool { let (sd, rw) = kind; // Check permissions self.check_access_permissions(lvalue_span, rw); + let mut error_reported = false; self.each_borrow_involving_path( context, (sd, lvalue_span.0), flow_state, |this, _index, borrow, common_prefix| { match (rw, borrow.kind) { @@ -462,13 +471,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => { match kind { - ReadKind::Copy => + ReadKind::Copy => { + error_reported = true; this.report_use_while_mutably_borrowed( - context, lvalue_span, borrow), + context, lvalue_span, borrow) + }, ReadKind::Borrow(bk) => { let end_issued_loan_span = flow_state.borrows.base_results.operator().opt_region_end_span( &borrow.region); + error_reported = true; this.report_conflicting_borrow( context, common_prefix, lvalue_span, bk, &borrow, end_issued_loan_span) @@ -482,22 +494,35 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let end_issued_loan_span = flow_state.borrows.base_results.operator().opt_region_end_span( &borrow.region); + error_reported = true; this.report_conflicting_borrow( context, common_prefix, lvalue_span, bk, &borrow, end_issued_loan_span) } - WriteKind::StorageDead | - WriteKind::Mutate => + WriteKind::StorageDeadOrDrop => { + let end_span = + flow_state.borrows.base_results.operator().opt_region_end_span( + &borrow.region); + error_reported = true; + this.report_borrowed_value_does_not_live_long_enough( + context, lvalue_span, end_span) + }, + WriteKind::Mutate => { + error_reported = true; this.report_illegal_mutation_of_borrowed( - context, lvalue_span, borrow), - WriteKind::Move => + context, lvalue_span, borrow) + }, + WriteKind::Move => { + error_reported = true; this.report_move_out_while_borrowed( - context, lvalue_span, &borrow), + context, lvalue_span, &borrow) + }, } Control::Break } } }); + error_reported } fn mutate_lvalue(&mut self, @@ -614,20 +639,34 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // Check if error has already been reported to stop duplicate reporting. let has_storage_drop_or_dead_error_reported = match *lvalue { - Lvalue::Local(local) => self.storage_drop_or_dead_error_reported.insert(local), + Lvalue::Local(local) => self.storage_drop_or_dead_error_reported.contains(&local), _ => false, }; + // If the error has been reported already, then we don't need the access_lvalue call and we + // can set error_reported to false. if !has_storage_drop_or_dead_error_reported { + let error_reported; + if moves_by_default { + let kind = match consume_via_drop { + ConsumeKind::Drop => WriteKind::StorageDeadOrDrop, + _ => WriteKind::Move, + }; // move of lvalue: check if this is move of already borrowed path - self.access_lvalue(context, lvalue_span, (Deep, Write(WriteKind::Move)), - flow_state); + error_reported = self.access_lvalue(context, lvalue_span, + (Deep, Write(kind)), flow_state); } else { // copy of lvalue: check if this is "copy of frozen path" // (FIXME: see check_loans.rs) - self.access_lvalue(context, lvalue_span, (Deep, Read(ReadKind::Copy)), - flow_state); + error_reported = self.access_lvalue(context, lvalue_span, + (Deep, Read(ReadKind::Copy)), flow_state); + } + + if error_reported { + if let Lvalue::Local(local) = *lvalue { + self.storage_drop_or_dead_error_reported.insert(local); + } } } @@ -1477,6 +1516,29 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); } + fn report_borrowed_value_does_not_live_long_enough(&mut self, + _: Context, + (lvalue, span): (&Lvalue, Span), + end_span: Option) { + let proper_span = match *lvalue { + Lvalue::Local(local) => self.mir.local_decls[local].source_info.span, + _ => span + }; + + let mut err = self.tcx.path_does_not_live_long_enough(proper_span, + "borrowed value", Origin::Mir); + err.span = MultiSpan::from_span(proper_span); + 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, (lvalue, span): (&Lvalue<'tcx>, Span),