diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 57aa2186927a9..98d9a0a7c6f5e 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1883,6 +1883,18 @@ impl<'tcx> Place<'tcx> { pub fn elem(self, elem: PlaceElem<'tcx>) -> Place<'tcx> { Place::Projection(Box::new(PlaceProjection { base: self, elem })) } + + /// Find the innermost `Local` from this `Place`. + pub fn local(&self) -> Option { + match self { + Place::Local(local) | + Place::Projection(box Projection { + base: Place::Local(local), + elem: ProjectionElem::Deref, + }) => Some(*local), + _ => None, + } + } } impl<'tcx> Debug for Place<'tcx> { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index ea547c592d048..8fb5ed66a82ef 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1324,6 +1324,23 @@ impl_stable_hash_for!(struct DebruijnIndex { private }); /// Region utilities impl RegionKind { + /// Is this region named by the user? + pub fn has_name(&self) -> bool { + match *self { + RegionKind::ReEarlyBound(ebr) => ebr.has_name(), + RegionKind::ReLateBound(_, br) => br.is_named(), + RegionKind::ReFree(fr) => fr.bound_region.is_named(), + RegionKind::ReScope(..) => false, + RegionKind::ReStatic => true, + RegionKind::ReVar(..) => false, + RegionKind::ReSkolemized(_, br) => br.is_named(), + RegionKind::ReEmpty => false, + RegionKind::ReErased => false, + RegionKind::ReClosureBound(..) => false, + RegionKind::ReCanonical(..) => false, + } + } + pub fn is_late_bound(&self) -> bool { match *self { ty::ReLateBound(..) => true, diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 9a1add26bb0b4..59dd90dbd329a 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -34,7 +34,14 @@ use hir; thread_local! { /// Mechanism for highlighting of specific regions for display in NLL region inference errors. /// Contains region to highlight and counter for number to use when highlighting. - static HIGHLIGHT_REGION: Cell> = Cell::new(None) + static HIGHLIGHT_REGION_FOR_REGIONVID: Cell> = Cell::new(None) +} + +thread_local! { + /// Mechanism for highlighting of specific regions for display in NLL's 'borrow does not live + /// long enough' errors. Contains a region to highlight and a counter to use. + static HIGHLIGHT_REGION_FOR_BOUND_REGION: Cell> = + Cell::new(None) } macro_rules! gen_display_debug_body { @@ -564,12 +571,34 @@ pub fn parameterized(f: &mut F, PrintContext::new().parameterized(f, substs, did, projections) } -fn get_highlight_region() -> Option<(RegionVid, usize)> { - HIGHLIGHT_REGION.with(|hr| hr.get()) +fn get_highlight_region_for_regionvid() -> Option<(RegionVid, usize)> { + HIGHLIGHT_REGION_FOR_REGIONVID.with(|hr| hr.get()) } -pub fn with_highlight_region(r: RegionVid, counter: usize, op: impl FnOnce() -> R) -> R { - HIGHLIGHT_REGION.with(|hr| { +pub fn with_highlight_region_for_regionvid( + r: RegionVid, + counter: usize, + op: impl FnOnce() -> R +) -> R { + HIGHLIGHT_REGION_FOR_REGIONVID.with(|hr| { + assert_eq!(hr.get(), None); + hr.set(Some((r, counter))); + let r = op(); + hr.set(None); + r + }) +} + +fn get_highlight_region_for_bound_region() -> Option<(ty::BoundRegion, usize)> { + HIGHLIGHT_REGION_FOR_BOUND_REGION.with(|hr| hr.get()) +} + +pub fn with_highlight_region_for_bound_region( + r: ty::BoundRegion, + counter: usize, + op: impl Fn() -> R +) -> R { + HIGHLIGHT_REGION_FOR_BOUND_REGION.with(|hr| { assert_eq!(hr.get(), None); hr.set(Some((r, counter))); let r = op(); @@ -726,6 +755,15 @@ define_print! { return self.print_debug(f, cx); } + if let Some((region, counter)) = get_highlight_region_for_bound_region() { + if *self == region { + return match *self { + BrNamed(_, name) => write!(f, "{}", name), + BrAnon(_) | BrFresh(_) | BrEnv => write!(f, "'{}", counter) + }; + } + } + match *self { BrNamed(_, name) => write!(f, "{}", name), BrAnon(_) | BrFresh(_) | BrEnv => Ok(()) @@ -748,7 +786,7 @@ define_print! { define_print! { () ty::RegionKind, (self, f, cx) { display { - if cx.is_verbose || get_highlight_region().is_some() { + if cx.is_verbose || get_highlight_region_for_regionvid().is_some() { return self.print_debug(f, cx); } @@ -923,7 +961,7 @@ impl fmt::Debug for ty::FloatVid { impl fmt::Debug for ty::RegionVid { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some((region, counter)) = get_highlight_region() { + if let Some((region, counter)) = get_highlight_region_for_regionvid() { debug!("RegionVid.fmt: region={:?} self={:?} counter={:?}", region, self, counter); return if *self == region { write!(f, "'{:?}", counter) diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 82eca4a18c88e..3fdb7d7f27d7e 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -10,22 +10,26 @@ use borrow_check::{WriteKind, StorageDeadOrDrop}; use borrow_check::prefixes::IsPrefixOf; +use borrow_check::nll::explain_borrow::BorrowExplanation; use rustc::middle::region::ScopeTree; -use rustc::mir::VarBindingForm; -use rustc::mir::{BindingForm, BorrowKind, ClearCrossCrate, Field, Local}; -use rustc::mir::{FakeReadCause, LocalDecl, LocalKind, Location, Operand, Place}; -use rustc::mir::{ProjectionElem, Rvalue, Statement, StatementKind}; +use rustc::mir::{ + self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, FakeReadCause, Field, Local, + LocalDecl, LocalKind, Location, Operand, Place, ProjectionElem, Rvalue, Statement, + StatementKind, TerminatorKind, VarBindingForm, +}; +use rustc::hir; +use rustc::hir::def_id::DefId; use rustc::ty; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc::util::ppaux::with_highlight_region_for_bound_region; use syntax_pos::Span; use super::borrow_set::BorrowData; use super::{Context, MirBorrowckCtxt}; use super::{InitializationRequiringAction, PrefixSet}; -use borrow_check::nll::explain_borrow::BorrowContainsPointReason; use dataflow::drop_flag_effects; use dataflow::move_paths::indexes::MoveOutIndex; use dataflow::move_paths::MovePathIndex; @@ -70,7 +74,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Some(name) => format!("`{}`", name), None => "value".to_owned(), }; - let mut err = self.tcx.cannot_act_on_uninitialized_variable( + let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable( span, desired_action.as_noun(), &self @@ -97,7 +101,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let msg = ""; //FIXME: add "partially " or "collaterally " - let mut err = self.tcx.cannot_act_on_moved_value( + let mut err = self.infcx.tcx.cannot_act_on_moved_value( span, desired_action.as_noun(), msg, @@ -149,9 +153,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if let Some(ty) = self.retrieve_type_for_place(place) { let needs_note = match ty.sty { ty::Closure(id, _) => { - let tables = self.tcx.typeck_tables_of(id); - let node_id = self.tcx.hir.as_local_node_id(id).unwrap(); - let hir_id = self.tcx.hir.node_to_hir_id(node_id); + let tables = self.infcx.tcx.typeck_tables_of(id); + let node_id = self.infcx.tcx.hir.as_local_node_id(id).unwrap(); + let hir_id = self.infcx.tcx.hir.node_to_hir_id(node_id); if tables.closure_kind_origins().get(hir_id).is_some() { false } else { @@ -198,7 +202,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { (place, _span): (&Place<'tcx>, Span), borrow: &BorrowData<'tcx>, ) { - let tcx = self.tcx; + let tcx = self.infcx.tcx; let value_msg = match self.describe_place(place) { Some(name) => format!("`{}`", name), None => "value".to_owned(), @@ -226,7 +230,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { move_spans.var_span_label(&mut err, "move occurs due to use in closure"); - self.explain_why_borrow_contains_point(context, borrow, None, &mut err); + self.explain_why_borrow_contains_point(context, borrow, None) + .emit(self.infcx.tcx, &mut err); err.buffer(&mut self.errors_buffer); } @@ -236,7 +241,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { (place, _span): (&Place<'tcx>, Span), borrow: &BorrowData<'tcx>, ) { - let tcx = self.tcx; + let tcx = self.infcx.tcx; let borrow_spans = self.retrieve_borrow_spans(borrow); let borrow_span = borrow_spans.args_or_use(); @@ -263,7 +268,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { format!("borrow occurs due to use of `{}` in closure", desc_place) }); - self.explain_why_borrow_contains_point(context, borrow, None, &mut err); + self.explain_why_borrow_contains_point(context, borrow, None) + .emit(self.infcx.tcx, &mut err); err.buffer(&mut self.errors_buffer); } @@ -281,7 +287,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let span = borrow_spans.args_or_use(); let desc_place = self.describe_place(place).unwrap_or("_".to_owned()); - let tcx = self.tcx; + let tcx = self.infcx.tcx; // FIXME: supply non-"" `opt_via` when appropriate let mut err = match ( @@ -390,7 +396,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); } - self.explain_why_borrow_contains_point(context, issued_borrow, None, &mut err); + self.explain_why_borrow_contains_point(context, issued_borrow, None) + .emit(self.infcx.tcx, &mut err); err.buffer(&mut self.errors_buffer); } @@ -417,7 +424,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); let drop_span = place_span.1; - let scope_tree = self.tcx.region_scope_tree(self.mir_def_id); + let scope_tree = self.infcx.tcx.region_scope_tree(self.mir_def_id); let root_place = self .prefixes(&borrow.borrowed_place, PrefixSet::All) .last() @@ -445,47 +452,39 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { self.access_place_error_reported .insert((root_place.clone(), borrow_span)); - let borrow_reason = self.find_why_borrow_contains_point(context, borrow); - - if let Some(WriteKind::StorageDeadOrDrop(StorageDeadOrDrop::Destructor)) = kind - { + if let Some(WriteKind::StorageDeadOrDrop(StorageDeadOrDrop::Destructor)) = kind { // If a borrow of path `B` conflicts with drop of `D` (and // we're not in the uninteresting case where `B` is a // prefix of `D`), then report this as a more interesting // destructor conflict. if !borrow.borrowed_place.is_prefix_of(place_span.0) { - self.report_borrow_conflicts_with_destructor( - context, borrow, borrow_reason, place_span, kind); + self.report_borrow_conflicts_with_destructor(context, borrow, place_span, kind); return; } } - let mut err = match &self.describe_place(&borrow.borrowed_place) { - Some(_) if self.is_place_thread_local(root_place) => { - self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span) - } + let err = match &self.describe_place(&borrow.borrowed_place) { + Some(_) if self.is_place_thread_local(root_place) => + self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span), Some(name) => self.report_local_value_does_not_live_long_enough( context, name, &scope_tree, &borrow, - borrow_reason, drop_span, - borrow_span, + borrow_spans, kind.map(|k| (k, place_span.0)), ), None => self.report_temporary_value_does_not_live_long_enough( context, &scope_tree, &borrow, - borrow_reason, drop_span, + borrow_spans, proper_span, ), }; - borrow_spans.args_span_label(&mut err, "value captured here"); - err.buffer(&mut self.errors_buffer); } @@ -495,31 +494,69 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { name: &String, scope_tree: &Lrc, borrow: &BorrowData<'tcx>, - reason: BorrowContainsPointReason<'tcx>, drop_span: Span, - borrow_span: Span, + borrow_spans: UseSpans, kind_place: Option<(WriteKind, &Place<'tcx>)>, ) -> DiagnosticBuilder<'cx> { debug!( "report_local_value_does_not_live_long_enough(\ - {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\ + {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\ )", - context, name, scope_tree, borrow, reason, drop_span, borrow_span + context, name, scope_tree, borrow, drop_span, borrow_spans ); - let mut err = self.tcx.path_does_not_live_long_enough( + let borrow_span = borrow_spans.var_or_use(); + let mut err = self.infcx.tcx.path_does_not_live_long_enough( borrow_span, &format!("`{}`", name), Origin::Mir, ); - err.span_label(borrow_span, "borrowed value does not live long enough"); - err.span_label( - drop_span, - format!("`{}` dropped here while still borrowed", name), - ); + let explanation = self.explain_why_borrow_contains_point(context, borrow, kind_place); + if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { + let region_name = annotation.emit(&mut err); + + err.span_label( + borrow_span, + format!("`{}` would have to be valid for `{}`...", name, region_name) + ); + + if let Some(fn_node_id) = self.infcx.tcx.hir.as_local_node_id(self.mir_def_id) { + err.span_label( + drop_span, + format!( + "...but `{}` will be dropped here, when the function `{}` returns", + name, self.infcx.tcx.hir.name(fn_node_id), + ) + ); + + err.note( + "functions cannot return a borrow to data owned within the function's scope, \ + functions can only return borrows to data passed as arguments", + ); + err.note( + "to learn more, visit ", + ); + } else { + err.span_label( + drop_span, + format!("...but `{}` dropped here while still borrowed", name) + ); + } + + if let BorrowExplanation::MustBeValidFor(..) = explanation { } else { + explanation.emit(self.infcx.tcx, &mut err); + } + } else { + err.span_label(borrow_span, "borrowed value does not live long enough"); + err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name)); + + borrow_spans.args_span_label(&mut err, "value captured here"); + + explanation.emit(self.infcx.tcx, &mut err); + } - self.report_why_borrow_contains_point(&mut err, reason, kind_place); err } @@ -527,36 +564,32 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { &mut self, context: Context, borrow: &BorrowData<'tcx>, - borrow_reason: BorrowContainsPointReason<'tcx>, - place_span: (&Place<'tcx>, Span), + (place, drop_span): (&Place<'tcx>, Span), kind: Option, ) { debug!( "report_borrow_conflicts_with_destructor(\ - {:?}, {:?}, {:?}, {:?} {:?}\ + {:?}, {:?}, ({:?}, {:?}), {:?}\ )", - context, borrow, borrow_reason, place_span, kind, + context, borrow, place, drop_span, kind, ); let borrow_spans = self.retrieve_borrow_spans(borrow); let borrow_span = borrow_spans.var_or_use(); - let mut err = self.tcx.cannot_borrow_across_destructor(borrow_span, Origin::Mir); - - let drop_span = place_span.1; + let mut err = self.infcx.tcx.cannot_borrow_across_destructor(borrow_span, Origin::Mir); let (what_was_dropped, dropped_ty) = { - let place = place_span.0; let desc = match self.describe_place(place) { Some(name) => format!("`{}`", name.as_str()), None => format!("temporary value"), }; - let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); + let ty = place.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx); (desc, ty) }; let label = match dropped_ty.sty { - ty::Adt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() => { + ty::Adt(adt, _) if adt.has_dtor(self.infcx.tcx) && !adt.is_box() => { match self.describe_place(&borrow.borrowed_place) { Some(borrowed) => format!("here, drop of {D} needs exclusive access to `{B}`, \ @@ -571,17 +604,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { }; err.span_label(drop_span, label); - // Only give this note and suggestion if they could be relevant - match borrow_reason { - BorrowContainsPointReason::Liveness {..} - | BorrowContainsPointReason::DropLiveness {..} => { + // Only give this note and suggestion if they could be relevant. + let explanation = self.explain_why_borrow_contains_point( + context, borrow, kind.map(|k| (k, place)), + ); + match explanation { + BorrowExplanation::UsedLater {..} | + BorrowExplanation::UsedLaterWhenDropped {..} => { err.note("consider using a `let` binding to create a longer lived value"); - } - BorrowContainsPointReason::OutlivesFreeRegion {..} => (), + }, + _ => {}, } - self.report_why_borrow_contains_point( - &mut err, borrow_reason, kind.map(|k| (k, place_span.0))); + explanation.emit(self.infcx.tcx, &mut err); err.buffer(&mut self.errors_buffer); } @@ -598,15 +633,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { drop_span, borrow_span ); - let mut err = self - .tcx - .thread_local_value_does_not_live_long_enough(borrow_span, Origin::Mir); + let mut err = self.infcx.tcx.thread_local_value_does_not_live_long_enough( + borrow_span, Origin::Mir + ); err.span_label( borrow_span, "thread-local variables cannot be borrowed beyond the end of the function", ); err.span_label(drop_span, "end of enclosing function is here"); + err } @@ -615,33 +651,37 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { context: Context, scope_tree: &Lrc, borrow: &BorrowData<'tcx>, - reason: BorrowContainsPointReason<'tcx>, drop_span: Span, + borrow_spans: UseSpans, proper_span: Span, ) -> DiagnosticBuilder<'cx> { debug!( "report_temporary_value_does_not_live_long_enough(\ - {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\ + {:?}, {:?}, {:?}, {:?}, {:?}\ )", - context, scope_tree, borrow, reason, drop_span, proper_span + context, scope_tree, borrow, drop_span, proper_span ); - let tcx = self.tcx; + let tcx = self.infcx.tcx; let mut err = tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir); err.span_label(proper_span, "temporary value does not live long enough"); err.span_label(drop_span, "temporary value only lives until here"); - // Only give this note and suggestion if they could be relevant - match reason { - BorrowContainsPointReason::Liveness {..} - | BorrowContainsPointReason::DropLiveness {..} => { + let explanation = self.explain_why_borrow_contains_point(context, borrow, None); + match explanation { + BorrowExplanation::UsedLater(..) | + BorrowExplanation::UsedLaterInLoop(..) | + BorrowExplanation::UsedLaterWhenDropped(..) => { + // Only give this note and suggestion if it could be relevant. err.note("consider using a `let` binding to create a longer lived value"); - } - BorrowContainsPointReason::OutlivesFreeRegion {..} => (), + }, + _ => {}, } + explanation.emit(self.infcx.tcx, &mut err); + + borrow_spans.args_span_label(&mut err, "value captured here"); - self.report_why_borrow_contains_point(&mut err, reason, None); err } @@ -715,7 +755,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // check for inits let mut any_match = false; - drop_flag_effects::for_location_inits(self.tcx, self.mir, self.move_data, l, |m| { + drop_flag_effects::for_location_inits(self.infcx.tcx, self.mir, self.move_data, l, |m| { if m == mpi { any_match = true; } @@ -739,7 +779,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let loan_spans = self.retrieve_borrow_spans(loan); let loan_span = loan_spans.args_or_use(); - let tcx = self.tcx; + let tcx = self.infcx.tcx; let mut err = tcx.cannot_assign_to_borrowed( span, loan_span, @@ -749,7 +789,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { loan_spans.var_span_label(&mut err, "borrow occurs due to use in closure"); - self.explain_why_borrow_contains_point(context, loan, None, &mut err); + self.explain_why_borrow_contains_point(context, loan, None).emit(self.infcx.tcx, &mut err); err.buffer(&mut self.errors_buffer); } @@ -801,7 +841,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Some(decl) => (self.describe_place(err_place), decl.source_info.span), }; - let mut err = self.tcx.cannot_reassign_immutable( + let mut err = self.infcx.tcx.cannot_reassign_immutable( span, place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"), from_arg, @@ -879,13 +919,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { self.append_local_to_string(local, buf)?; } Place::Static(ref static_) => { - buf.push_str(&self.tcx.item_name(static_.def_id).to_string()); + buf.push_str(&self.infcx.tcx.item_name(static_.def_id).to_string()); } Place::Projection(ref proj) => { match proj.elem { ProjectionElem::Deref => { let upvar_field_projection = - place.is_upvar_field_projection(self.mir, &self.tcx); + place.is_upvar_field_projection(self.mir, &self.infcx.tcx); if let Some(field) = upvar_field_projection { let var_index = field.index(); let name = self.mir.upvar_decls[var_index].debug_name.to_string(); @@ -947,7 +987,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { autoderef = true; let upvar_field_projection = - place.is_upvar_field_projection(self.mir, &self.tcx); + place.is_upvar_field_projection(self.mir, &self.infcx.tcx); if let Some(field) = upvar_field_projection { let var_index = field.index(); let name = self.mir.upvar_decls[var_index].debug_name.to_string(); @@ -1062,10 +1102,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // 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(def_id).unwrap(); - let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]); + let node_id = self.infcx.tcx.hir.as_local_node_id(def_id).unwrap(); + let freevar = self.infcx.tcx.with_freevars(node_id, |fv| fv[field.index()]); - self.tcx.hir.name(freevar.var_id()).to_string() + self.infcx.tcx.hir.name(freevar.var_id()).to_string() } _ => { // Might need a revision when the fields in trait RFC is implemented @@ -1098,7 +1138,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// Check if a place is a thread-local static. pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool { if let Place::Static(statik) = place { - let attrs = self.tcx.get_attrs(statik.def_id); + let attrs = self.infcx.tcx.get_attrs(statik.def_id); let is_thread_local = attrs.iter().any(|attr| attr.check_name("thread_local")); debug!( @@ -1126,6 +1166,481 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { None } } + + /// Annotate argument and return type of function and closure with (synthesized) lifetime for + /// borrow of local value that does not live long enough. + fn annotate_argument_and_return_for_borrow( + &self, + borrow: &BorrowData<'tcx>, + ) -> Option { + // Define a fallback for when we can't match a closure. + let fallback = || { + let is_closure = self.infcx.tcx.is_closure(self.mir_def_id); + if is_closure { + None + } else { + let ty = self.infcx.tcx.type_of(self.mir_def_id); + match ty.sty { + ty::TyKind::FnDef(_, _) | ty::TyKind::FnPtr(_) => + self.annotate_fn_sig( + self.mir_def_id, + self.infcx.tcx.fn_sig(self.mir_def_id) + ), + _ => None, + } + } + }; + + // In order to determine whether we need to annotate, we need to check whether the reserve + // place was an assignment into a temporary. + // + // If it was, we check whether or not that temporary is eventually assigned into the return + // place. If it was, we can add annotations about the function's return type and arguments + // and it'll make sense. + let location = borrow.reserve_location; + debug!("annotate_argument_and_return_for_borrow: location={:?}", location); + match &self.mir[location.block].statements.get(location.statement_index) { + Some(&Statement { + kind: StatementKind::Assign(ref reservation, _), + .. + }) => { + debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation); + // Check that the initial assignment of the reserve location is into a temporary. + let mut target = *match reservation { + Place::Local(local) if self.mir.local_kind(*local) == LocalKind::Temp => local, + _ => return None, + }; + + // Next, look through the rest of the block, checking if we are assigning the + // `target` (that is, the place that contains our borrow) to anything. + let mut annotated_closure = None; + for stmt in &self.mir[location.block].statements[location.statement_index + 1..] { + debug!( + "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}", + target, stmt + ); + if let StatementKind::Assign( + Place::Local(assigned_to), + rvalue, + ) = &stmt.kind { + debug!("annotate_argument_and_return_for_borrow: assigned_to={:?} \ + rvalue={:?}", assigned_to, rvalue); + // Check if our `target` was captured by a closure. + if let Rvalue::Aggregate( + box AggregateKind::Closure(def_id, substs), + operands, + ) = rvalue { + for operand in operands { + let assigned_from = match operand { + Operand::Copy(assigned_from) | + Operand::Move(assigned_from) => assigned_from, + _ => continue, + }; + debug!( + "annotate_argument_and_return_for_borrow: assigned_from={:?}", + assigned_from + ); + + // Find the local from the operand. + let assigned_from_local = match assigned_from.local() { + Some(local) => local, + None => continue, + }; + + if assigned_from_local != target { + continue; + } + + // If a closure captured our `target` and then assigned + // into a place then we should annotate the closure in + // case it ends up being assigned into the return place. + annotated_closure = self.annotate_fn_sig( + *def_id, + self.infcx.closure_sig(*def_id, *substs) + ); + debug!( + "annotate_argument_and_return_for_borrow: \ + annotated_closure={:?} assigned_from_local={:?} \ + assigned_to={:?}", + annotated_closure, assigned_from_local, assigned_to + ); + + if *assigned_to == mir::RETURN_PLACE { + // If it was assigned directly into the return place, then + // return now. + return annotated_closure; + } else { + // Otherwise, update the target. + target = *assigned_to; + } + } + + // If none of our closure's operands matched, then skip to the next + // statement. + continue; + } + + // Otherwise, look at other types of assignment. + let assigned_from = match rvalue { + Rvalue::Ref(_, _, assigned_from) => assigned_from, + Rvalue::Use(operand) => match operand { + Operand::Copy(assigned_from) | + Operand::Move(assigned_from) => assigned_from, + _ => continue, + }, + _ => continue, + }; + debug!( + "annotate_argument_and_return_for_borrow: \ + assigned_from={:?}", assigned_from, + ); + + // Find the local from the rvalue. + let assigned_from_local = match assigned_from.local() { + Some(local) => local, + None => continue, + }; + debug!( + "annotate_argument_and_return_for_borrow: \ + assigned_from_local={:?}", assigned_from_local, + ); + + // Check if our local matches the target - if so, we've assigned our + // borrow to a new place. + if assigned_from_local != target { + continue; + } + + // If we assigned our `target` into a new place, then we should + // check if it was the return place. + debug!( + "annotate_argument_and_return_for_borrow: \ + assigned_from_local={:?} assigned_to={:?}", + assigned_from_local, assigned_to + ); + if *assigned_to == mir::RETURN_PLACE { + // If it was then return the annotated closure if there was one, + // else, annotate this function. + return annotated_closure.or_else(fallback); + } + + // If we didn't assign into the return place, then we just update + // the target. + target = *assigned_to; + } + } + + // Check the terminator if we didn't find anything in the statements. + let terminator = &self.mir[location.block].terminator(); + debug!( + "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}", + target, terminator + ); + if let TerminatorKind::Call { + destination: Some((Place::Local(assigned_to), _)), + args, + .. + } = &terminator.kind { + debug!( + "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}", + assigned_to, args + ); + for operand in args { + let assigned_from = match operand { + Operand::Copy(assigned_from) | + Operand::Move(assigned_from) => assigned_from, + _ => continue, + }; + debug!( + "annotate_argument_and_return_for_borrow: assigned_from={:?}", + assigned_from, + ); + + if let Some(assigned_from_local) = assigned_from.local() { + debug!( + "annotate_argument_and_return_for_borrow: assigned_from_local={:?}", + assigned_from_local, + ); + + if *assigned_to == mir::RETURN_PLACE && + assigned_from_local == target + { + return annotated_closure.or_else(fallback); + } + } + } + } + } + _ => {} + } + + // If we haven't found an assignment into the return place, then we need not add + // any annotations. + debug!("annotate_argument_and_return_for_borrow: none found"); + None + } + + /// Annotate the first argument and return type of a function signature if they are + /// references. + fn annotate_fn_sig( + &self, + did: DefId, + sig: ty::PolyFnSig<'tcx>, + ) -> Option { + debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig); + let is_closure = self.infcx.tcx.is_closure(did); + let fn_node_id = self.infcx.tcx.hir.as_local_node_id(did)?; + let fn_decl = self.infcx.tcx.hir.fn_decl(fn_node_id)?; + + // We need to work out which arguments to highlight. We do this by looking + // at the return type, where there are three cases: + // + // 1. If there are named arguments, then we should highlight the return type and + // highlight any of the arguments that are also references with that lifetime. + // If there are no arguments that have the same lifetime as the return type, + // then don't highlight anything. + // 2. The return type is a reference with an anonymous lifetime. If this is + // the case, then we can take advantage of (and teach) the lifetime elision + // rules. + // + // We know that an error is being reported. So the arguments and return type + // must satisfy the elision rules. Therefore, if there is a single argument + // then that means the return type and first (and only) argument have the same + // lifetime and the borrow isn't meeting that, we can highlight the argument + // and return type. + // + // If there are multiple arguments then the first argument must be self (else + // it would not satisfy the elision rules), so we can highlight self and the + // return type. + // 3. The return type is not a reference. In this case, we don't highlight + // anything. + let return_ty = sig.output(); + match return_ty.skip_binder().sty { + ty::TyKind::Ref(return_region, _, _) if return_region.has_name() && !is_closure => { + // This is case 1 from above, return type is a named reference so we need to + // search for relevant arguments. + let mut arguments = Vec::new(); + for (index, argument) in sig.inputs().skip_binder().iter().enumerate() { + if let ty::TyKind::Ref(argument_region, _, _) = argument.sty { + if argument_region == return_region { + // Need to use the `rustc::ty` types to compare against the + // `return_region`. Then use the `rustc::hir` type to get only + // the lifetime span. + match &fn_decl.inputs[index].node { + hir::TyKind::Rptr(lifetime, _) => { + // With access to the lifetime, we can get + // the span of it. + arguments.push((*argument, lifetime.span)); + }, + _ => bug!("ty type is a ref but hir type is not"), + } + } + } + } + + // We need to have arguments. This shouldn't happen, but it's worth checking. + if arguments.is_empty() { + return None; + } + + // We use a mix of the HIR and the Ty types to get information + // as the HIR doesn't have full types for closure arguments. + let return_ty = *sig.output().skip_binder(); + let mut return_span = fn_decl.output.span(); + if let hir::FunctionRetTy::Return(ty) = fn_decl.output { + if let hir::TyKind::Rptr(lifetime, _) = ty.into_inner().node { + return_span = lifetime.span; + } + } + + Some(AnnotatedBorrowFnSignature::NamedFunction { + arguments, + return_ty, + return_span, + }) + }, + ty::TyKind::Ref(_, _, _) if is_closure => { + // This is case 2 from above but only for closures, return type is anonymous + // reference so we select + // the first argument. + let argument_span = fn_decl.inputs.first()?.span; + let argument_ty = sig.inputs().skip_binder().first()?; + + // Closure arguments are wrapped in a tuple, so we need to get the first + // from that. + if let ty::TyKind::Tuple(elems) = argument_ty.sty { + let argument_ty = elems.first()?; + if let ty::TyKind::Ref(_, _, _) = argument_ty.sty { + return Some(AnnotatedBorrowFnSignature::Closure { + argument_ty, + argument_span, + }); + } + } + + None + }, + ty::TyKind::Ref(_, _, _) => { + // This is also case 2 from above but for functions, return type is still an + // anonymous reference so we select the first argument. + let argument_span = fn_decl.inputs.first()?.span; + let argument_ty = sig.inputs().skip_binder().first()?; + + let return_span = fn_decl.output.span(); + let return_ty = *sig.output().skip_binder(); + + // We expect the first argument to be a reference. + match argument_ty.sty { + ty::TyKind::Ref(_, _, _) => {}, + _ => return None, + } + + Some(AnnotatedBorrowFnSignature::AnonymousFunction { + argument_ty, + argument_span, + return_ty, + return_span, + }) + }, + _ => { + // This is case 3 from above, return type is not a reference so don't highlight + // anything. + None + }, + } + } +} + +#[derive(Debug)] +enum AnnotatedBorrowFnSignature<'tcx> { + NamedFunction { + arguments: Vec<(ty::Ty<'tcx>, Span)>, + return_ty: ty::Ty<'tcx>, + return_span: Span, + }, + AnonymousFunction { + argument_ty: ty::Ty<'tcx>, + argument_span: Span, + return_ty: ty::Ty<'tcx>, + return_span: Span, + }, + Closure { + argument_ty: ty::Ty<'tcx>, + argument_span: Span, + } +} + +impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { + /// Annotate the provided diagnostic with information about borrow from the fn signature that + /// helps explain. + fn emit( + &self, + diag: &mut DiagnosticBuilder<'_> + ) -> String { + match self { + AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => { + diag.span_label( + *argument_span, + format!("has type `{}`", self.get_name_for_ty(argument_ty, 0)), + ); + + self.get_region_name_for_ty(argument_ty, 0) + }, + AnnotatedBorrowFnSignature::AnonymousFunction { + argument_ty, + argument_span, + return_ty, + return_span, + } => { + let argument_ty_name = self.get_name_for_ty(argument_ty, 0); + diag.span_label( + *argument_span, + format!("has type `{}`", argument_ty_name) + ); + + let return_ty_name = self.get_name_for_ty(return_ty, 0); + let types_equal = return_ty_name == argument_ty_name; + diag.span_label( + *return_span, + format!( + "{}has type `{}`", + if types_equal { "also " } else { "" }, + return_ty_name, + ) + ); + + diag.note( + "argument and return type have the same lifetime due to lifetime elision rules", + ); + diag.note( + "to learn more, visit ", + ); + + self.get_region_name_for_ty(return_ty, 0) + }, + AnnotatedBorrowFnSignature::NamedFunction { + arguments, + return_ty, + return_span, + } => { + // Region of return type and arguments checked to be the same earlier. + let region_name = self.get_region_name_for_ty(return_ty, 0); + for (_, argument_span) in arguments { + diag.span_label( + *argument_span, + format!("has lifetime `{}`", region_name) + ); + } + + diag.span_label( + *return_span, + format!( + "also has lifetime `{}`", + region_name, + ) + ); + + diag.help( + &format!( + "use data from the highlighted arguments which match the `{}` lifetime of \ + the return type", + region_name, + ), + ); + + region_name + }, + } + } + + /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime + /// name where required. + fn get_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String { + // We need to add synthesized lifetimes where appropriate. We do + // this by hooking into the pretty printer and telling it to label the + // lifetimes without names with the value `'0`. + match ty.sty { + ty::TyKind::Ref(ty::RegionKind::ReLateBound(_, br), _, _) | + ty::TyKind::Ref(ty::RegionKind::ReSkolemized(_, br), _, _) => + with_highlight_region_for_bound_region(*br, counter, || format!("{}", ty)), + _ => format!("{}", ty), + } + } + + /// Return the name of the provided `Ty` (that must be a reference)'s region with a + /// synthesized lifetime name where required. + fn get_region_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String { + match ty.sty { + ty::TyKind::Ref(region, _, _) => match region { + ty::RegionKind::ReLateBound(_, br) | + ty::RegionKind::ReSkolemized(_, br) => + with_highlight_region_for_bound_region(*br, counter, || format!("{}", region)), + _ => format!("{}", region), + } + _ => bug!("ty for annotation of borrow region is not a reference"), + } + } } // The span(s) associated to a use of a place. @@ -1214,9 +1729,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if let AggregateKind::Closure(def_id, _) = **kind { debug!("find_closure_move_span: found closure {:?}", places); - if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { - if let Closure(_, _, _, args_span, _) = self.tcx.hir.expect_expr(node_id).node { - if let Some(var_span) = self.tcx.with_freevars(node_id, |freevars| { + if let Some(node_id) = self.infcx.tcx.hir.as_local_node_id(def_id) { + if let Closure( + _, _, _, args_span, _ + ) = self.infcx.tcx.hir.expect_expr(node_id).node { + if let Some(var_span) = self.infcx.tcx.with_freevars(node_id, |freevars| { for (v, place) in freevars.iter().zip(places) { match place { Operand::Copy(place) | Operand::Move(place) @@ -1253,7 +1770,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans { use self::UseSpans::*; use rustc::hir::ExprKind::Closure; - use rustc::mir::AggregateKind; let local = match self.mir[location.block] .statements @@ -1276,16 +1792,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if let AggregateKind::Closure(def_id, _) = **kind { debug!("find_closure_borrow_span: found closure {:?}", places); - return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { + return if let Some(node_id) = self.infcx.tcx.hir.as_local_node_id(def_id) { let args_span = if let Closure(_, _, _, span, _) = - self.tcx.hir.expect_expr(node_id).node + self.infcx.tcx.hir.expect_expr(node_id).node { span } else { return OtherUse(use_span); }; - self.tcx + self.infcx.tcx .with_freevars(node_id, |freevars| { for (v, place) in freevars.iter().zip(places) { match *place { diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 09fb37bccd96d..06884875598df 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -247,8 +247,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( let dominators = mir.dominators(); let mut mbcx = MirBorrowckCtxt { - tcx: tcx, - mir: mir, + infcx, + mir, mir_def_id: def_id, move_data: &mdpe.move_data, param_env: param_env, @@ -369,7 +369,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( } for diag in mbcx.errors_buffer.drain(..) { - DiagnosticBuilder::new_diagnostic(mbcx.tcx.sess.diagnostic(), diag).emit(); + DiagnosticBuilder::new_diagnostic(mbcx.infcx.tcx.sess.diagnostic(), diag).emit(); } } @@ -384,7 +384,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( } pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { - tcx: TyCtxt<'cx, 'gcx, 'tcx>, + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, mir: &'cx Mir<'tcx>, mir_def_id: DefId, move_data: &'cx MoveData<'tcx>, @@ -612,13 +612,14 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx target: _, unwind: _, } => { - let gcx = self.tcx.global_tcx(); + let gcx = self.infcx.tcx.global_tcx(); // Compute the type with accurate region information. - let drop_place_ty = drop_place.ty(self.mir, self.tcx); + let drop_place_ty = drop_place.ty(self.mir, self.infcx.tcx); // Erase the regions. - let drop_place_ty = self.tcx.erase_regions(&drop_place_ty).to_ty(self.tcx); + let drop_place_ty = self.infcx.tcx.erase_regions(&drop_place_ty) + .to_ty(self.infcx.tcx); // "Lift" into the gcx -- once regions are erased, this type should be in the // global arenas; this "lift" operation basically just asserts that is true, but @@ -953,7 +954,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { return; } - let gcx = self.tcx.global_tcx(); + let gcx = self.infcx.tcx.global_tcx(); let drop_field = |mir: &mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>, (index, field): (usize, ty::Ty<'gcx>)| { let field_ty = gcx.normalize_erasing_regions(mir.param_env, field); @@ -971,7 +972,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // individual fields instead. This way if `foo` has a // destructor but `bar` does not, we will only check for // borrows of `x.foo` and not `x.bar`. See #47703. - ty::Adt(def, substs) if def.is_struct() && !def.has_dtor(self.tcx) => { + ty::Adt(def, substs) if def.is_struct() && !def.has_dtor(self.infcx.tcx) => { def.all_fields() .map(|field| field.ty(gcx, substs)) .enumerate() @@ -991,7 +992,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { && !self.mir.upvar_decls.is_empty() => { substs - .upvar_tys(def, self.tcx) + .upvar_tys(def, self.infcx.tcx) .enumerate() .for_each(|field| drop_field(self, field)); } @@ -1002,7 +1003,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { && !self.mir.upvar_decls.is_empty() => { substs - .upvar_tys(def, self.tcx) + .upvar_tys(def, self.infcx.tcx) .enumerate() .for_each(|field| drop_field(self, field)); } @@ -1168,7 +1169,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); let mut error_reported = false; - let tcx = self.tcx; + let tcx = self.infcx.tcx; let mir = self.mir; let location = self.location_table.start_index(context.loc); let borrow_set = self.borrow_set.clone(); @@ -1206,7 +1207,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut { .. }) => { // Reading from mere reservations of mutable-borrows is OK. if !is_active(&this.dominators, borrow, context.loc) { - assert!(allow_two_phase_borrow(&this.tcx, borrow.kind)); + assert!(allow_two_phase_borrow(&this.infcx.tcx, borrow.kind)); return Control::Continue; } @@ -1338,7 +1339,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), BorrowKind::Unique | BorrowKind::Mut { .. } => { let wk = WriteKind::MutableBorrow(bk); - if allow_two_phase_borrow(&self.tcx, bk) { + if allow_two_phase_borrow(&self.infcx.tcx, bk) { (Deep, Reservation(wk)) } else { (Deep, Write(wk)) @@ -1413,7 +1414,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | AggregateKind::Generator(def_id, _, _) => { let BorrowCheckResult { used_mut_upvars, .. - } = self.tcx.mir_borrowck(def_id); + } = self.infcx.tcx.mir_borrowck(def_id); debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars); for field in used_mut_upvars { // This relies on the current way that by-value @@ -1427,7 +1428,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Operand::Move(ref place @ Place::Projection(_)) | Operand::Copy(ref place @ Place::Projection(_)) => { if let Some(field) = place.is_upvar_field_projection( - self.mir, &self.tcx) { + self.mir, &self.infcx.tcx) { self.used_mut_upvars.push(field); } } @@ -1546,11 +1547,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // that is merged. let sd = if might_be_alive { Deep } else { Shallow(None) }; - if places_conflict::places_conflict(self.tcx, self.mir, place, root_place, sd) { + if places_conflict::places_conflict(self.infcx.tcx, self.mir, place, root_place, sd) { debug!("check_for_invalidation_at_exit({:?}): INVALID", place); // FIXME: should be talking about the region lifetime instead // of just a span here. - let span = self.tcx.sess.source_map().end_point(span); + let span = self.infcx.tcx.sess.source_map().end_point(span); self.report_borrowed_value_does_not_live_long_enough( context, borrow, @@ -1566,7 +1567,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { debug!("check_for_local_borrow({:?})", borrow); if borrow_of_local_data(&borrow.borrowed_place) { - let err = self.tcx + let err = self.infcx.tcx .cannot_borrow_across_generator_yield( self.retrieve_borrow_spans(borrow).var_or_use(), yield_span, @@ -1583,7 +1584,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { span: Span, flow_state: &Flows<'cx, 'gcx, 'tcx>, ) { - if !self.tcx.two_phase_borrows() { + if !self.infcx.tcx.two_phase_borrows() { return; } @@ -1845,7 +1846,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // if type of `P` has a dtor, then // assigning to `P.f` requires `P` itself // be already initialized - let tcx = self.tcx; + let tcx = self.infcx.tcx; match base.ty(self.mir, tcx).to_ty(tcx).sty { ty::Adt(def, _) if def.has_dtor(tcx) => { self.check_if_path_or_subpath_is_moved( @@ -1929,7 +1930,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | Write(wk @ WriteKind::StorageDeadOrDrop(_)) | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared)) => { if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) { - if self.tcx.migrate_borrowck() { + if self.infcx.tcx.migrate_borrowck() { // rust-lang/rust#46908: In pure NLL mode this // code path should be unreachable (and thus // we signal an ICE in the else branch @@ -1952,7 +1953,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { location, ); } else { - self.tcx.sess.delay_span_bug( + self.infcx.tcx.sess.delay_span_bug( span, &format!( "Accessing `{:?}` with the kind `{:?}` shouldn't be possible", @@ -2020,7 +2021,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { place: place @ Place::Projection(_), is_local_mutation_allowed: _, } => { - if let Some(field) = place.is_upvar_field_projection(self.mir, &self.tcx) { + if let Some(field) = place.is_upvar_field_projection(self.mir, &self.infcx.tcx) { self.used_mut_upvars.push(field); } } @@ -2070,7 +2071,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { is_local_mutation_allowed, }), Place::Static(ref static_) => { - if self.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) { + if self.infcx.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) { Err(place) } else { Ok(RootPlace { @@ -2082,7 +2083,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Place::Projection(ref proj) => { match proj.elem { ProjectionElem::Deref => { - let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + let base_ty = proj.base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx); // Check the kind of deref to decide match base_ty.sty { @@ -2094,7 +2095,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // unique path to the `&mut` hir::MutMutable => { let mode = match place.is_upvar_field_projection( - self.mir, &self.tcx) + self.mir, &self.infcx.tcx) { Some(field) if { @@ -2140,7 +2141,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..) => { let upvar_field_projection = place.is_upvar_field_projection( - self.mir, &self.tcx); + self.mir, &self.infcx.tcx); if let Some(field) = upvar_field_projection { let decl = &self.mir.upvar_decls[field.index()]; debug!( diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs index 52d051ebe7ba0..8a97f25ef5813 100644 --- a/src/librustc_mir/borrow_check/move_errors.rs +++ b/src/librustc_mir/borrow_check/move_errors.rs @@ -244,30 +244,31 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let origin = Origin::Mir; debug!("report: original_path={:?} span={:?}, kind={:?} \ original_path.is_upvar_field_projection={:?}", original_path, span, kind, - original_path.is_upvar_field_projection(self.mir, &self.tcx)); + original_path.is_upvar_field_projection(self.mir, &self.infcx.tcx)); ( match kind { IllegalMoveOriginKind::Static => { - self.tcx.cannot_move_out_of(span, "static item", origin) + self.infcx.tcx.cannot_move_out_of(span, "static item", origin) } IllegalMoveOriginKind::BorrowedContent { target_place: place } => { // Inspect the type of the content behind the // borrow to provide feedback about why this // was a move rather than a copy. - let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx); + let ty = place.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx); let is_upvar_field_projection = self.prefixes(&original_path, PrefixSet::All) - .any(|p| p.is_upvar_field_projection(self.mir, &self.tcx) + .any(|p| p.is_upvar_field_projection(self.mir, &self.infcx.tcx) .is_some()); match ty.sty { - ty::Array(..) | ty::Slice(..) => self - .tcx - .cannot_move_out_of_interior_noncopy(span, ty, None, origin), + ty::Array(..) | ty::Slice(..) => + self.infcx.tcx.cannot_move_out_of_interior_noncopy( + span, ty, None, origin + ), ty::Closure(def_id, closure_substs) if !self.mir.upvar_decls.is_empty() && is_upvar_field_projection => { let closure_kind_ty = - closure_substs.closure_kind_ty(def_id, self.tcx); + closure_substs.closure_kind_ty(def_id, self.infcx.tcx); let closure_kind = closure_kind_ty.to_opt_closure_kind(); let place_description = match closure_kind { Some(ty::ClosureKind::Fn) => { @@ -285,18 +286,18 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { place_description={:?}", closure_kind_ty, closure_kind, place_description); - let mut diag = self.tcx.cannot_move_out_of( + let mut diag = self.infcx.tcx.cannot_move_out_of( span, place_description, origin); for prefix in self.prefixes(&original_path, PrefixSet::All) { if let Some(field) = prefix.is_upvar_field_projection( - self.mir, &self.tcx) { + self.mir, &self.infcx.tcx) { let upvar_decl = &self.mir.upvar_decls[field.index()]; let upvar_hir_id = upvar_decl.var_hir_id.assert_crate_local(); let upvar_node_id = - self.tcx.hir.hir_to_node_id(upvar_hir_id); - let upvar_span = self.tcx.hir.span(upvar_node_id); + self.infcx.tcx.hir.hir_to_node_id(upvar_hir_id); + let upvar_span = self.infcx.tcx.hir.span(upvar_node_id); diag.span_label(upvar_span, "captured outer variable"); break; } @@ -304,18 +305,19 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { diag } - _ => self - .tcx - .cannot_move_out_of(span, "borrowed content", origin), + _ => self.infcx.tcx.cannot_move_out_of( + span, "borrowed content", origin + ), } } IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { - self.tcx + self.infcx.tcx .cannot_move_out_of_interior_of_drop(span, ty, origin) } - IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => self - .tcx - .cannot_move_out_of_interior_noncopy(span, ty, Some(*is_index), origin), + IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => + self.infcx.tcx.cannot_move_out_of_interior_noncopy( + span, ty, Some(*is_index), origin + ), }, span, ) @@ -331,7 +333,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { err: &mut DiagnosticBuilder<'a>, span: Span, ) { - let snippet = self.tcx.sess.source_map().span_to_snippet(span).unwrap(); + let snippet = self.infcx.tcx.sess.source_map().span_to_snippet(span).unwrap(); match error { GroupedMoveError::MovesFromPlace { mut binds_to, @@ -394,8 +396,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { .. })) ) = bind_to.is_user_variable { - let pat_snippet = self - .tcx.sess.source_map() + let pat_snippet = self.infcx.tcx.sess.source_map() .span_to_snippet(pat_span) .unwrap(); if pat_snippet.starts_with('&') { diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs index a078aa59a7d5b..30555dbf26082 100644 --- a/src/librustc_mir/borrow_check/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/mutability_errors.rs @@ -71,11 +71,11 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { elem: ProjectionElem::Field(upvar_index, _), }) => { debug_assert!(is_closure_or_generator( - base.ty(self.mir, self.tcx).to_ty(self.tcx) + base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx) )); item_msg = format!("`{}`", access_place_desc.unwrap()); - if access_place.is_upvar_field_projection(self.mir, &self.tcx).is_some() { + if access_place.is_upvar_field_projection(self.mir, &self.infcx.tcx).is_some() { reason = ", as it is not declared as mutable".to_string(); } else { let name = self.mir.upvar_decls[upvar_index.index()].debug_name; @@ -91,11 +91,11 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { item_msg = format!("`{}`", access_place_desc.unwrap()); debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr()); debug_assert!(is_closure_or_generator( - the_place_err.ty(self.mir, self.tcx).to_ty(self.tcx) + the_place_err.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx) )); reason = if access_place.is_upvar_field_projection(self.mir, - &self.tcx).is_some() { + &self.infcx.tcx).is_some() { ", as it is a captured variable in a `Fn` closure".to_string() } else { ", as `Fn` closures cannot mutate their captured variables".to_string() @@ -116,7 +116,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { reason = ", as it is immutable for the pattern guard".to_string(); } else { let pointer_type = - if base.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() { + if base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx).is_region_ptr() { "`&` reference" } else { "`*const` pointer" @@ -145,7 +145,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { reason = String::new(); } else { item_msg = format!("`{}`", access_place_desc.unwrap()); - let static_name = &self.tcx.item_name(*def_id); + let static_name = &self.infcx.tcx.item_name(*def_id); reason = format!(", as `{}` is an immutable static item", static_name); } } @@ -177,14 +177,14 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let span = match error_access { AccessKind::Move => { - err = self.tcx + err = self.infcx.tcx .cannot_move_out_of(span, &(item_msg + &reason), Origin::Mir); act = "move"; acted_on = "moved"; span } AccessKind::Mutate => { - err = self.tcx + err = self.infcx.tcx .cannot_assign(span, &(item_msg + &reason), Origin::Mir); act = "assign"; acted_on = "written"; @@ -196,7 +196,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let borrow_spans = self.borrow_spans(span, location); let borrow_span = borrow_spans.args_or_use(); - err = self.tcx.cannot_borrow_path_as_mutable_because( + err = self.infcx.tcx.cannot_borrow_path_as_mutable_because( borrow_span, &item_msg, &reason, @@ -242,7 +242,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { elem: ProjectionElem::Field(upvar_index, _), }) => { debug_assert!(is_closure_or_generator( - base.ty(self.mir, self.tcx).to_ty(self.tcx) + base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx) )); err.span_label(span, format!("cannot {ACT}", ACT = act)); @@ -250,8 +250,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()] .var_hir_id .assert_crate_local(); - let upvar_node_id = self.tcx.hir.hir_to_node_id(upvar_hir_id); - if let Some(Node::Binding(pat)) = self.tcx.hir.find(upvar_node_id) { + let upvar_node_id = self.infcx.tcx.hir.hir_to_node_id(upvar_hir_id); + if let Some(Node::Binding(pat)) = self.infcx.tcx.hir.find(upvar_node_id) { if let hir::PatKind::Binding( hir::BindingAnnotation::Unannotated, _, @@ -274,7 +274,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { // a local variable, then just suggest the user remove it. Place::Local(_) if { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) { snippet.starts_with("&mut ") } else { false @@ -317,7 +317,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { let local_decl = &self.mir.local_decls[*local]; let suggestion = match local_decl.is_user_variable.as_ref().unwrap() { ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf) => { - Some(suggest_ampmut_self(self.tcx, local_decl)) + Some(suggest_ampmut_self(self.infcx.tcx, local_decl)) } ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm { @@ -325,7 +325,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { opt_ty_info, .. })) => Some(suggest_ampmut( - self.tcx, + self.infcx.tcx, self.mir, *local, local_decl, @@ -337,7 +337,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { .. })) => { let pattern_span = local_decl.source_info.span; - suggest_ref_mut(self.tcx, pattern_span) + suggest_ref_mut(self.infcx.tcx, pattern_span) .map(|replacement| (pattern_span, replacement)) } @@ -426,11 +426,11 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> { .. } ) = &self.mir.basic_blocks()[location.block].terminator { - if self.tcx.parent(id) == self.tcx.lang_items().index_trait() { - + let index_trait = self.infcx.tcx.lang_items().index_trait(); + if self.infcx.tcx.parent(id) == index_trait { let mut found = false; - self.tcx.for_each_relevant_impl( - self.tcx.lang_items().index_mut_trait().unwrap(), + self.infcx.tcx.for_each_relevant_impl( + self.infcx.tcx.lang_items().index_mut_trait().unwrap(), substs.type_at(0), |_relevant_impl| { found = true; diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/find_use.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/find_use.rs index a35117f2e3560..d4adff7c443cb 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/find_use.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/find_use.rs @@ -121,9 +121,7 @@ struct DefUseVisitor<'cx, 'gcx: 'tcx, 'tcx: 'cx> { enum DefUseResult { Def, - UseLive { local: Local }, - UseDrop { local: Local }, } diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs index 61972629e7b85..755148b699253 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -11,26 +11,71 @@ use borrow_check::borrow_set::BorrowData; use borrow_check::nll::region_infer::Cause; use borrow_check::{Context, MirBorrowckCtxt, WriteKind}; -use rustc::mir::{FakeReadCause, Local, Location, Place, TerminatorKind}; +use rustc::ty::{Region, TyCtxt}; +use rustc::mir::{FakeReadCause, Location, Place, TerminatorKind}; use rustc_errors::DiagnosticBuilder; -use rustc::ty::Region; +use syntax_pos::Span; +use syntax_pos::symbol::Symbol; mod find_use; -#[derive(Copy, Clone, Debug)] -pub enum BorrowContainsPointReason<'tcx> { - Liveness { - local: Local, - location: Location, - in_loop: bool, - }, - DropLiveness { - local: Local, - location: Location, - }, - OutlivesFreeRegion { - outlived_region: Option>, - }, +pub(in borrow_check) enum BorrowExplanation<'tcx> { + UsedLater(bool, Option, Span), + UsedLaterInLoop(bool, Span), + UsedLaterWhenDropped(Span, Symbol, bool), + MustBeValidFor(Region<'tcx>), + Unexplained, +} + +impl<'tcx> BorrowExplanation<'tcx> { + pub(in borrow_check) fn emit<'cx, 'gcx>( + &self, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + err: &mut DiagnosticBuilder<'_> + ) { + match *self { + BorrowExplanation::UsedLater(is_in_closure, fake_read_cause, var_or_use_span) => { + let message = if is_in_closure { + "borrow later captured here by closure" + } else if let Some(FakeReadCause::ForLet) = fake_read_cause { + "borrow later stored here" + } else { + "borrow later used here" + }; + err.span_label(var_or_use_span, message); + }, + BorrowExplanation::UsedLaterInLoop(is_in_closure, var_or_use_span) => { + let message = if is_in_closure { + "borrow captured here by closure in later iteration of loop" + } else { + "borrow used here in later iteration of loop" + }; + err.span_label(var_or_use_span, message); + }, + BorrowExplanation::UsedLaterWhenDropped(span, local_name, should_note_order) => { + err.span_label( + span, + format!("borrow later used here, when `{}` is dropped", local_name), + ); + + if should_note_order { + err.note( + "values in a scope are dropped \ + in the opposite order they are defined", + ); + } + }, + BorrowExplanation::MustBeValidFor(region) => { + tcx.note_and_explain_free_region( + err, + "borrowed value must be valid for ", + region, + "...", + ); + }, + _ => {}, + } + } } impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { @@ -53,23 +98,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { context: Context, borrow: &BorrowData<'tcx>, kind_place: Option<(WriteKind, &Place<'tcx>)>, - err: &mut DiagnosticBuilder<'_>, - ) { - let reason = self.find_why_borrow_contains_point(context, borrow); - self.report_why_borrow_contains_point(err, reason, kind_place); - } - - /// Finds the reason that [explain_why_borrow_contains_point] will report - /// but doesn't add it to any message. This is a separate function in case - /// the caller wants to change the error they report based on the reason - /// that will be reported. - pub(in borrow_check) fn find_why_borrow_contains_point( - &self, - context: Context, - borrow: &BorrowData<'tcx> - ) -> BorrowContainsPointReason<'tcx> { - use self::BorrowContainsPointReason::*; - + ) -> BorrowExplanation<'tcx> { debug!( "find_why_borrow_contains_point(context={:?}, borrow={:?})", context, borrow, @@ -77,116 +106,70 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let regioncx = &self.nonlexical_regioncx; let mir = self.mir; - let tcx = self.tcx; + let tcx = self.infcx.tcx; let borrow_region_vid = regioncx.to_region_vid(borrow.region); - debug!( "explain_why_borrow_contains_point: borrow_region_vid={:?}", borrow_region_vid ); let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, context.loc); - debug!( "explain_why_borrow_contains_point: region_sub={:?}", region_sub ); - match find_use::find(mir, regioncx, tcx, region_sub, context.loc) { - Some(Cause::LiveVar(local, location)) => Liveness { - local, - location, - in_loop: self.is_borrow_location_in_loop(context.loc), - }, - Some(Cause::DropVar(local, location)) => DropLiveness { - local, - location, - }, - None => OutlivesFreeRegion { - outlived_region: regioncx.to_error_region(region_sub), - }, - } - } - - /// Adds annotations to `err` for the explanation `reason`. This is a - /// separate method so that the caller can change their error message based - /// on the reason that is going to be reported. - pub (in borrow_check) fn report_why_borrow_contains_point( - &self, - err: &mut DiagnosticBuilder, - reason: BorrowContainsPointReason<'tcx>, - kind_place: Option<(WriteKind, &Place<'tcx>)>, - ) { - use self::BorrowContainsPointReason::*; - - debug!( - "find_why_borrow_contains_point(reason={:?}, kind_place={:?})", - reason, kind_place, - ); - - let mir = self.mir; - - match reason { - Liveness { local, location, in_loop } => { + match find_use::find(mir, regioncx, tcx, region_sub, context.loc) { + Some(Cause::LiveVar(local, location)) => { let span = mir.source_info(location).span; let spans = self.move_spans(&Place::Local(local), location) .or_else(|| self.borrow_spans(span, location)); - let message = if in_loop { - if spans.for_closure() { - "borrow captured here by closure in later iteration of loop" - } else { - "borrow used here in later iteration of loop" - } + + if self.is_borrow_location_in_loop(context.loc) { + BorrowExplanation::UsedLaterInLoop(spans.for_closure(), spans.var_or_use()) } else { - if spans.for_closure() { - "borrow later captured here by closure" - } else { - // Check if the location represents a `FakeRead`, and adapt the error - // message to the `FakeReadCause` it is from: in particular, - // the ones inserted in optimized `let var = ` patterns. - match self.retrieve_fake_read_cause_for_location(&location) { - Some(FakeReadCause::ForLet) => "borrow later stored here", - _ => "borrow later used here" - } - } - }; - err.span_label(spans.var_or_use(), message); + // Check if the location represents a `FakeRead`, and adapt the error + // message to the `FakeReadCause` it is from: in particular, + // the ones inserted in optimized `let var = ` patterns. + BorrowExplanation::UsedLater( + spans.for_closure(), + self.retrieve_fake_read_cause_for_location(&location), + spans.var_or_use() + ) + } } - DropLiveness { local, location } => match &mir.local_decls[local].name { - Some(local_name) => { - err.span_label( - mir.source_info(location).span, - format!("borrow later used here, when `{}` is dropped", local_name), - ); + Some(Cause::DropVar(local, location)) => match &mir.local_decls[local].name { + Some(local_name) => { + let mut should_note_order = false; if let Some((WriteKind::StorageDeadOrDrop(_), place)) = kind_place { if let Place::Local(borrowed_local) = place { let dropped_local_scope = mir.local_decls[local].visibility_scope; let borrowed_local_scope = - mir.local_decls[*borrowed_local].visibility_scope; + mir.local_decls[*borrowed_local].visibility_scope; if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope) { - err.note( - "values in a scope are dropped \ - in the opposite order they are defined", - ); + should_note_order = true; } } } - } - None => {} - } - OutlivesFreeRegion { outlived_region: Some(region) } => { - self.tcx.note_and_explain_free_region( - err, - "borrowed value must be valid for ", - region, - "...", - ); - } - OutlivesFreeRegion { outlived_region: None } => (), + BorrowExplanation::UsedLaterWhenDropped( + mir.source_info(location).span, + *local_name, + should_note_order + ) + }, + + None => BorrowExplanation::Unexplained, + }, + + None => if let Some(region) = regioncx.to_error_region(region_sub) { + BorrowExplanation::MustBeValidFor(region) + } else { + BorrowExplanation::Unexplained + }, } } @@ -262,4 +245,3 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { false } } - diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index d287116029ad1..31d7c7c631e17 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -339,10 +339,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); let counter = &mut 1; - let fr_name = self.give_region_a_name( - infcx, mir, mir_def_id, fr, counter, &mut diag); + let fr_name = self.give_region_a_name(infcx, mir, mir_def_id, fr, counter); + fr_name.highlight_region_name(&mut diag); let outlived_fr_name = self.give_region_a_name( - infcx, mir, mir_def_id, outlived_fr, counter, &mut diag); + infcx, mir, mir_def_id, outlived_fr, counter); + outlived_fr_name.highlight_region_name(&mut diag); let mir_def_name = if infcx.tcx.is_closure(mir_def_id) { "closure" } else { "function" }; @@ -430,10 +431,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Otherwise, we should suggest adding a constraint on the return type. let span = infcx.tcx.def_span(*did); if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) { - let suggestable_fr_name = match fr_name { - RegionName::Named(name) => format!("{}", name), - RegionName::Synthesized(_) => "'_".to_string(), + let suggestable_fr_name = if fr_name.was_named() { + format!("{}", fr_name) + } else { + "'_".to_string() }; + diag.span_suggestion_with_applicability( span, &format!( diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs index f704ee61c8847..be05e006608a2 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs @@ -18,36 +18,109 @@ use rustc::infer::InferCtxt; use rustc::mir::Mir; use rustc::ty::subst::{Substs, UnpackedKind}; use rustc::ty::{self, RegionKind, RegionVid, Ty, TyCtxt}; -use rustc::util::ppaux::with_highlight_region; +use rustc::util::ppaux::with_highlight_region_for_regionvid; use rustc_errors::DiagnosticBuilder; use syntax::ast::{Name, DUMMY_NODE_ID}; use syntax::symbol::keywords; use syntax_pos::Span; use syntax_pos::symbol::InternedString; -/// Name of a region used in error reporting. Variants denote the source of the region name - -/// whether it was synthesized for the error message and therefore should not be used in -/// suggestions; or whether it was found from the region. #[derive(Debug)] -pub(crate) enum RegionName { - Named(InternedString), - Synthesized(InternedString), +crate struct RegionName { + name: InternedString, + source: RegionNameSource, +} + +#[derive(Debug)] +crate enum RegionNameSource { + NamedEarlyBoundRegion(Span), + NamedFreeRegion(Span), + Static, + SynthesizedFreeEnvRegion(Span, String), + CannotMatchHirTy(Span, String), + MatchedHirTy(Span), + MatchedAdtAndSegment(Span), + AnonRegionFromUpvar(Span, String), + AnonRegionFromOutput(Span, String, String), } impl RegionName { - fn as_interned_string(&self) -> &InternedString { - match self { - RegionName::Named(name) | RegionName::Synthesized(name) => name, + #[allow(dead_code)] + crate fn was_named(&self) -> bool { + match self.source { + RegionNameSource::NamedEarlyBoundRegion(..) | + RegionNameSource::NamedFreeRegion(..) | + RegionNameSource::Static => true, + RegionNameSource::SynthesizedFreeEnvRegion(..) | + RegionNameSource::CannotMatchHirTy(..) | + RegionNameSource::MatchedHirTy(..) | + RegionNameSource::MatchedAdtAndSegment(..) | + RegionNameSource::AnonRegionFromUpvar(..) | + RegionNameSource::AnonRegionFromOutput(..) => false, + } + } + + #[allow(dead_code)] + crate fn was_synthesized(&self) -> bool { + !self.was_named() + } + + #[allow(dead_code)] + crate fn name(&self) -> &InternedString { + &self.name + } + + crate fn highlight_region_name( + &self, + diag: &mut DiagnosticBuilder<'_> + ) { + match &self.source { + RegionNameSource::NamedFreeRegion(span) | + RegionNameSource::NamedEarlyBoundRegion(span) => { + diag.span_label( + *span, + format!("lifetime `{}` defined here", self), + ); + }, + RegionNameSource::SynthesizedFreeEnvRegion(span, note) => { + diag.span_label( + *span, + format!("lifetime `{}` represents this closure's body", self), + ); + diag.note(¬e); + }, + RegionNameSource::CannotMatchHirTy(span, type_name) => { + diag.span_label(*span, format!("has type `{}`", type_name)); + }, + RegionNameSource::MatchedHirTy(span) => { + diag.span_label( + *span, + format!("let's call the lifetime of this reference `{}`", self), + ); + }, + RegionNameSource::MatchedAdtAndSegment(span) => { + diag.span_label(*span, format!("let's call this `{}`", self)); + }, + RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => { + diag.span_label( + *span, + format!("lifetime `{}` appears in the type of `{}`", self, upvar_name), + ); + }, + RegionNameSource::AnonRegionFromOutput(span, mir_description, type_name) => { + diag.span_label( + *span, + format!("return type{} is {}", mir_description, type_name), + ); + }, + RegionNameSource::Static => {}, } } } impl Display for RegionName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - RegionName::Named(name) | RegionName::Synthesized(name) => - write!(f, "{}", name), - } + write!(f, "{}", self.name) } } @@ -84,26 +157,25 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir_def_id: DefId, fr: RegionVid, counter: &mut usize, - diag: &mut DiagnosticBuilder, ) -> RegionName { debug!("give_region_a_name(fr={:?}, counter={})", fr, counter); assert!(self.universal_regions.is_universal_region(fr)); - let value = self.give_name_from_error_region(infcx.tcx, mir_def_id, fr, counter, diag) + let value = self.give_name_from_error_region(infcx.tcx, mir_def_id, fr, counter) .or_else(|| { self.give_name_if_anonymous_region_appears_in_arguments( - infcx, mir, mir_def_id, fr, counter, diag, + infcx, mir, mir_def_id, fr, counter, ) }) .or_else(|| { self.give_name_if_anonymous_region_appears_in_upvars( - infcx.tcx, mir, fr, counter, diag, + infcx.tcx, mir, fr, counter, ) }) .or_else(|| { self.give_name_if_anonymous_region_appears_in_output( - infcx, mir, mir_def_id, fr, counter, diag, + infcx, mir, mir_def_id, fr, counter, ) }) .unwrap_or_else(|| span_bug!(mir.span, "can't make a name for free region {:?}", fr)); @@ -122,7 +194,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir_def_id: DefId, fr: RegionVid, counter: &mut usize, - diag: &mut DiagnosticBuilder<'_>, ) -> Option { let error_region = self.to_error_region(fr)?; @@ -130,23 +201,28 @@ impl<'tcx> RegionInferenceContext<'tcx> { match error_region { ty::ReEarlyBound(ebr) => { if ebr.has_name() { - let name = RegionName::Named(ebr.name); - self.highlight_named_span(tcx, error_region, &name, diag); - Some(name) + let span = self.get_named_span(tcx, error_region, &ebr.name); + Some(RegionName { + name: ebr.name, + source: RegionNameSource::NamedEarlyBoundRegion(span) + }) } else { None } } - ty::ReStatic => Some(RegionName::Named( - keywords::StaticLifetime.name().as_interned_str() - )), + ty::ReStatic => Some(RegionName { + name: keywords::StaticLifetime.name().as_interned_str(), + source: RegionNameSource::Static + }), ty::ReFree(free_region) => match free_region.bound_region { ty::BoundRegion::BrNamed(_, name) => { - let name = RegionName::Named(name); - self.highlight_named_span(tcx, error_region, &name, diag); - Some(name) + let span = self.get_named_span(tcx, error_region, &name); + Some(RegionName { + name, + source: RegionNameSource::NamedFreeRegion(span), + }) }, ty::BoundRegion::BrEnv => { @@ -162,13 +238,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { bug!("Closure is not defined by a closure expr"); }; let region_name = self.synthesize_region_name(counter); - diag.span_label( - args_span, - format!( - "lifetime `{}` represents this closure's body", - region_name - ), - ); let closure_kind_ty = substs.closure_kind_ty(def_id, tcx); let note = match closure_kind_ty.to_opt_closure_kind() { @@ -186,9 +255,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { None => bug!("Closure kind not inferred in borrow check"), }; - diag.note(note); - - Some(region_name) + Some(RegionName { + name: region_name, + source: RegionNameSource::SynthesizedFreeEnvRegion( + args_span, + note.to_string() + ), + }) } else { // Can't have BrEnv in functions, constants or generators. bug!("BrEnv outside of closure."); @@ -209,27 +282,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - /// Get the span of a named region. - pub(super) fn get_span_of_named_region( - &self, - tcx: TyCtxt<'_, '_, 'tcx>, - error_region: &RegionKind, - name: &RegionName, - ) -> Span { - let scope = error_region.free_region_binding_scope(tcx); - let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID); - - let span = tcx.sess.source_map().def_span(tcx.hir.span(node)); - if let Some(param) = tcx.hir.get_generics(scope).and_then(|generics| { - generics.get_named(name.as_interned_string()) - }) { - param.span - } else { - span - } - } - - /// Highlight a named span to provide context for error messages that + /// Get a span of a named region to provide context for error messages that /// mention that span, for example: /// /// ``` @@ -243,19 +296,24 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must /// | outlive `'a` /// ``` - fn highlight_named_span( + fn get_named_span( &self, tcx: TyCtxt<'_, '_, 'tcx>, error_region: &RegionKind, - name: &RegionName, - diag: &mut DiagnosticBuilder<'_>, - ) { - let span = self.get_span_of_named_region(tcx, error_region, name); + name: &InternedString, + ) -> Span { + let scope = error_region.free_region_binding_scope(tcx); + let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID); - diag.span_label( - span, - format!("lifetime `{}` defined here", name), - ); + let span = tcx.sess.source_map().def_span(tcx.hir.span(node)); + if let Some(param) = tcx.hir + .get_generics(scope) + .and_then(|generics| generics.get_named(name)) + { + param.span + } else { + span + } } /// Find an argument that contains `fr` and label it with a fully @@ -273,7 +331,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir_def_id: DefId, fr: RegionVid, counter: &mut usize, - diag: &mut DiagnosticBuilder<'_>, ) -> Option { let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs(); let argument_index = self.get_argument_index_for_region(infcx.tcx, fr)?; @@ -288,12 +345,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { arg_ty, argument_index, counter, - diag, ) { return Some(region_name); } - self.give_name_if_we_cannot_match_hir_ty(infcx, mir, fr, arg_ty, counter, diag) + self.give_name_if_we_cannot_match_hir_ty(infcx, mir, fr, arg_ty, counter) } fn give_name_if_we_can_match_hir_ty_from_argument( @@ -305,7 +361,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { argument_ty: Ty<'tcx>, argument_index: usize, counter: &mut usize, - diag: &mut DiagnosticBuilder<'_>, ) -> Option { let mir_node_id = infcx.tcx.hir.as_local_node_id(mir_def_id)?; let fn_decl = infcx.tcx.hir.fn_decl(mir_node_id)?; @@ -320,7 +375,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { needle_fr, argument_ty, counter, - diag, ), _ => self.give_name_if_we_can_match_hir_ty( @@ -329,7 +383,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { argument_ty, argument_hir_ty, counter, - diag, ), } } @@ -352,9 +405,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { needle_fr: RegionVid, argument_ty: Ty<'tcx>, counter: &mut usize, - diag: &mut DiagnosticBuilder<'_>, ) -> Option { - let type_name = with_highlight_region(needle_fr, *counter, || { + let type_name = with_highlight_region_for_regionvid(needle_fr, *counter, || { infcx.extract_type_name(&argument_ty) }); @@ -366,12 +418,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Only add a label if we can confirm that a region was labelled. let argument_index = self.get_argument_index_for_region(infcx.tcx, needle_fr)?; let (_, span) = self.get_argument_name_and_span_for_region(mir, argument_index); - diag.span_label(span, format!("has type `{}`", type_name)); - // This counter value will already have been used, so this function will increment it - // so the next value will be used next and return the region name that would have been - // used. - Some(self.synthesize_region_name(counter)) + Some(RegionName { + // This counter value will already have been used, so this function will increment + // it so the next value will be used next and return the region name that would + // have been used. + name: self.synthesize_region_name(counter), + source: RegionNameSource::CannotMatchHirTy(span, type_name), + }) } else { None }; @@ -407,7 +461,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { argument_ty: Ty<'tcx>, argument_hir_ty: &hir::Ty, counter: &mut usize, - diag: &mut DiagnosticBuilder<'_>, ) -> Option { let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty)> = &mut Vec::new(); @@ -432,15 +485,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { let source_map = tcx.sess.source_map(); let ampersand_span = source_map.start_point(hir_ty.span); - diag.span_label( - ampersand_span, - format!( - "let's call the lifetime of this reference `{}`", - region_name - ), - ); - - return Some(region_name); + return Some(RegionName { + name: region_name, + source: RegionNameSource::MatchedHirTy(ampersand_span), + }); } // Otherwise, let's descend into the referent types. @@ -464,7 +512,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { needle_fr, last_segment, counter, - diag, search_stack, ) { return Some(name); @@ -509,7 +556,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { needle_fr: RegionVid, last_segment: &'hir hir::PathSegment, counter: &mut usize, - diag: &mut DiagnosticBuilder<'_>, search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>, ) -> Option { // Did the user give explicit arguments? (e.g., `Foo<..>`) @@ -521,11 +567,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { | hir::LifetimeName::Underscore => { let region_name = self.synthesize_region_name(counter); let ampersand_span = lifetime.span; - diag.span_label( - ampersand_span, - format!("let's call this `{}`", region_name) - ); - return Some(region_name); + return Some(RegionName { + name: region_name, + source: RegionNameSource::MatchedAdtAndSegment(ampersand_span), + }); } hir::LifetimeName::Implicit => { @@ -600,22 +645,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir: &Mir<'tcx>, fr: RegionVid, counter: &mut usize, - diag: &mut DiagnosticBuilder<'_>, ) -> Option { let upvar_index = self.get_upvar_index_for_region(tcx, fr)?; let (upvar_name, upvar_span) = self.get_upvar_name_and_span_for_region(tcx, mir, upvar_index); let region_name = self.synthesize_region_name(counter); - diag.span_label( - upvar_span, - format!( - "lifetime `{}` appears in the type of `{}`", - region_name, upvar_name - ), - ); - - Some(region_name) + Some(RegionName { + name: region_name, + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()), + }) } /// Check for arguments appearing in the (closure) return type. It @@ -629,7 +668,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { mir_def_id: DefId, fr: RegionVid, counter: &mut usize, - diag: &mut DiagnosticBuilder<'_>, ) -> Option { let tcx = infcx.tcx; @@ -645,7 +683,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { return None; } - let type_name = with_highlight_region(fr, *counter, || infcx.extract_type_name(&return_ty)); + let type_name = with_highlight_region_for_regionvid( + fr, *counter, || infcx.extract_type_name(&return_ty)); let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).expect("non-local mir"); @@ -666,23 +705,25 @@ impl<'tcx> RegionInferenceContext<'tcx> { (mir.span, "") }; - diag.span_label( - return_span, - format!("return type{} is {}", mir_description, type_name), - ); - - // This counter value will already have been used, so this function will increment it - // so the next value will be used next and return the region name that would have been - // used. - Some(self.synthesize_region_name(counter)) + Some(RegionName { + // This counter value will already have been used, so this function will increment it + // so the next value will be used next and return the region name that would have been + // used. + name: self.synthesize_region_name(counter), + source: RegionNameSource::AnonRegionFromOutput( + return_span, + mir_description.to_string(), + type_name + ), + }) } /// Create a synthetic region named `'1`, incrementing the /// counter. - fn synthesize_region_name(&self, counter: &mut usize) -> RegionName { + fn synthesize_region_name(&self, counter: &mut usize) -> InternedString { let c = *counter; *counter += 1; - RegionName::Synthesized(Name::intern(&format!("'{:?}", c)).as_interned_str()) + Name::intern(&format!("'{:?}", c)).as_interned_str() } } diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/src/librustc_mir/borrow_check/prefixes.rs index 8dcc114330621..f73e08eb13521 100644 --- a/src/librustc_mir/borrow_check/prefixes.rs +++ b/src/librustc_mir/borrow_check/prefixes.rs @@ -79,7 +79,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { next: Some(place), kind, mir: self.mir, - tcx: self.tcx, + tcx: self.infcx.tcx, } } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 7a13beb78526c..d6cbe47a66efd 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1848,7 +1848,7 @@ impl<'a> Parser<'a> { P(Ty { id: ast::DUMMY_NODE_ID, node: TyKind::Infer, - span: self.span, + span: self.prev_span, }) }; Ok(Arg { diff --git a/src/test/ui/issues/issue-30438-c.nll.stderr b/src/test/ui/issues/issue-30438-c.nll.stderr index 28e63b2d36b11..3d2c95013ab0f 100644 --- a/src/test/ui/issues/issue-30438-c.nll.stderr +++ b/src/test/ui/issues/issue-30438-c.nll.stderr @@ -1,17 +1,20 @@ error[E0597]: `x` does not live long enough --> $DIR/issue-30438-c.rs:19:5 | +LL | fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y as Trait>::Out where 'z: 'static { + | -- -- also has lifetime `'y` + | | + | has lifetime `'y` +LL | let x = Test { s: "this cannot last" }; LL | &x - | ^^ borrowed value does not live long enough + | ^^ `x` would have to be valid for `'y`... LL | //~^ ERROR: `x` does not live long enough LL | } - | - `x` dropped here while still borrowed - | -note: borrowed value must be valid for the lifetime 'y as defined on the function body at 17:10... - --> $DIR/issue-30438-c.rs:17:10 + | - ...but `x` will be dropped here, when the function `silly` returns | -LL | fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y as Trait>::Out where 'z: 'static { - | ^^ + = help: use data from the highlighted arguments which match the `'y` lifetime of the return type + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit error: aborting due to previous error diff --git a/src/test/ui/nll/borrowed-universal-error-2.stderr b/src/test/ui/nll/borrowed-universal-error-2.stderr index 867e473af2c0e..c35a14ca70443 100644 --- a/src/test/ui/nll/borrowed-universal-error-2.stderr +++ b/src/test/ui/nll/borrowed-universal-error-2.stderr @@ -1,17 +1,20 @@ error[E0597]: `v` does not live long enough --> $DIR/borrowed-universal-error-2.rs:16:5 | +LL | fn foo<'a>(x: &'a (u32,)) -> &'a u32 { + | -- -- also has lifetime `'a` + | | + | has lifetime `'a` +LL | let v = 22; LL | &v - | ^^ borrowed value does not live long enough + | ^^ `v` would have to be valid for `'a`... LL | //~^ ERROR `v` does not live long enough [E0597] LL | } - | - `v` dropped here while still borrowed - | -note: borrowed value must be valid for the lifetime 'a as defined on the function body at 14:8... - --> $DIR/borrowed-universal-error-2.rs:14:8 + | - ...but `v` will be dropped here, when the function `foo` returns | -LL | fn foo<'a>(x: &'a (u32,)) -> &'a u32 { - | ^^ + = help: use data from the highlighted arguments which match the `'a` lifetime of the return type + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit error: aborting due to previous error diff --git a/src/test/ui/nll/issue-52534-1.rs b/src/test/ui/nll/issue-52534-1.rs new file mode 100644 index 0000000000000..cd6c10335ccc6 --- /dev/null +++ b/src/test/ui/nll/issue-52534-1.rs @@ -0,0 +1,53 @@ +// Copyright 2018 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. + +#![feature(nll)] +#![allow(warnings)] + +struct Test; + +impl Test { + fn bar(&self, x: &u32) -> &u32 { + let x = 22; + &x + } +} + +fn foo(x: &u32) -> &u32 { + let x = 22; + &x +} + +fn baz(x: &u32) -> &&u32 { + let x = 22; + &&x +} + +fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 { + let x = 22; + &x +} + +fn foobar<'a>(x: &'a u32) -> &'a u32 { + let x = 22; + &x +} + +fn foobaz<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { + let x = 22; + &x +} + +fn foobarbaz<'a, 'b>(x: &'a u32, y: &'b u32, z: &'a u32) -> &'a u32 { + let x = 22; + &x +} + +fn main() { } diff --git a/src/test/ui/nll/issue-52534-1.stderr b/src/test/ui/nll/issue-52534-1.stderr new file mode 100644 index 0000000000000..e2f8134e3be0e --- /dev/null +++ b/src/test/ui/nll/issue-52534-1.stderr @@ -0,0 +1,140 @@ +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534-1.rs:19:9 + | +LL | fn bar(&self, x: &u32) -> &u32 { + | ----- ---- has type `&'0 u32` + | | + | has type `&'0 Test` +LL | let x = 22; +LL | &x + | ^^ `x` would have to be valid for `'0`... +LL | } + | - ...but `x` will be dropped here, when the function `bar` returns + | + = note: argument and return type have the same lifetime due to lifetime elision rules + = note: to learn more, visit + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit + +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534-1.rs:25:5 + | +LL | fn foo(x: &u32) -> &u32 { + | ---- ---- also has type `&'0 u32` + | | + | has type `&'0 u32` +LL | let x = 22; +LL | &x + | ^^ `x` would have to be valid for `'0`... +LL | } + | - ...but `x` will be dropped here, when the function `foo` returns + | + = note: argument and return type have the same lifetime due to lifetime elision rules + = note: to learn more, visit + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit + +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534-1.rs:30:6 + | +LL | fn baz(x: &u32) -> &&u32 { + | ---- ----- has type `&'0 &'0 u32` + | | + | has type `&'0 u32` +LL | let x = 22; +LL | &&x + | ^^ `x` would have to be valid for `'0`... +LL | } + | - ...but `x` will be dropped here, when the function `baz` returns + | + = note: argument and return type have the same lifetime due to lifetime elision rules + = note: to learn more, visit + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit + +error[E0597]: borrowed value does not live long enough + --> $DIR/issue-52534-1.rs:30:6 + | +LL | &&x + | ^^ temporary value does not live long enough +LL | } + | - temporary value only lives until here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 28:1... + --> $DIR/issue-52534-1.rs:28:1 + | +LL | / fn baz(x: &u32) -> &&u32 { +LL | | let x = 22; +LL | | &&x +LL | | } + | |_^ + +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534-1.rs:35:5 + | +LL | fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 { + | -- -- also has lifetime `'a` + | | + | has lifetime `'a` +LL | let x = 22; +LL | &x + | ^^ `x` would have to be valid for `'a`... +LL | } + | - ...but `x` will be dropped here, when the function `foobazbar` returns + | + = help: use data from the highlighted arguments which match the `'a` lifetime of the return type + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit + +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534-1.rs:40:5 + | +LL | fn foobar<'a>(x: &'a u32) -> &'a u32 { + | -- -- also has lifetime `'a` + | | + | has lifetime `'a` +LL | let x = 22; +LL | &x + | ^^ `x` would have to be valid for `'a`... +LL | } + | - ...but `x` will be dropped here, when the function `foobar` returns + | + = help: use data from the highlighted arguments which match the `'a` lifetime of the return type + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit + +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534-1.rs:45:5 + | +LL | fn foobaz<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { + | -- has lifetime `'a` -- also has lifetime `'a` +LL | let x = 22; +LL | &x + | ^^ `x` would have to be valid for `'a`... +LL | } + | - ...but `x` will be dropped here, when the function `foobaz` returns + | + = help: use data from the highlighted arguments which match the `'a` lifetime of the return type + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit + +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534-1.rs:50:5 + | +LL | fn foobarbaz<'a, 'b>(x: &'a u32, y: &'b u32, z: &'a u32) -> &'a u32 { + | -- -- -- also has lifetime `'a` + | | | + | has lifetime `'a` has lifetime `'a` +LL | let x = 22; +LL | &x + | ^^ `x` would have to be valid for `'a`... +LL | } + | - ...but `x` will be dropped here, when the function `foobarbaz` returns + | + = help: use data from the highlighted arguments which match the `'a` lifetime of the return type + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-52534-2.rs b/src/test/ui/nll/issue-52534-2.rs new file mode 100644 index 0000000000000..4eac6feac0d9c --- /dev/null +++ b/src/test/ui/nll/issue-52534-2.rs @@ -0,0 +1,26 @@ +// Copyright 2018 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. + +#![feature(nll)] +#![allow(warnings)] + +fn foo(x: &u32) -> &u32 { + let y; + + { + let x = 32; + y = &x + } + + println!("{}", y); + x +} + +fn main() { } diff --git a/src/test/ui/nll/issue-52534-2.stderr b/src/test/ui/nll/issue-52534-2.stderr new file mode 100644 index 0000000000000..51cd7c7bf3b40 --- /dev/null +++ b/src/test/ui/nll/issue-52534-2.stderr @@ -0,0 +1,14 @@ +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534-2.rs:19:9 + | +LL | y = &x + | ^^^^^^ borrowed value does not live long enough +LL | } + | - `x` dropped here while still borrowed +LL | +LL | println!("{}", y); + | - borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/nll/issue-52534.rs b/src/test/ui/nll/issue-52534.rs new file mode 100644 index 0000000000000..273c9b3c8020c --- /dev/null +++ b/src/test/ui/nll/issue-52534.rs @@ -0,0 +1,30 @@ +// Copyright 2018 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. + +#![feature(nll)] +#![allow(warnings)] + +fn foo(_: impl FnOnce(&u32) -> &u32) { +} + +fn baz(_: impl FnOnce(&u32, u32) -> &u32) { +} + +fn bar() { + let x = 22; + foo(|a| &x) +} + +fn foobar() { + let y = 22; + baz(|first, second| &y) +} + +fn main() { } diff --git a/src/test/ui/nll/issue-52534.stderr b/src/test/ui/nll/issue-52534.stderr new file mode 100644 index 0000000000000..00d72546ebc4c --- /dev/null +++ b/src/test/ui/nll/issue-52534.stderr @@ -0,0 +1,29 @@ +error[E0597]: `x` does not live long enough + --> $DIR/issue-52534.rs:22:14 + | +LL | foo(|a| &x) + | - ^ `x` would have to be valid for `'0`... + | | + | has type `&'0 u32` +LL | } + | - ...but `x` will be dropped here, when the function `bar` returns + | + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit + +error[E0597]: `y` does not live long enough + --> $DIR/issue-52534.rs:27:26 + | +LL | baz(|first, second| &y) + | ----- ^ `y` would have to be valid for `'0`... + | | + | has type `&'0 u32` +LL | } + | - ...but `y` will be dropped here, when the function `foobar` returns + | + = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments + = note: to learn more, visit + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`.