From df5d07217271401217fce2d79f3ec75fa0e2b207 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 4 May 2019 08:40:07 +0100 Subject: [PATCH] Move conflict error reporting to its own module --- .../borrow_check/conflict_errors.rs | 1991 ++++++++++++++++ .../borrow_check/error_reporting.rs | 1998 +---------------- src/librustc_mir/borrow_check/mod.rs | 1 + 3 files changed, 2002 insertions(+), 1988 deletions(-) create mode 100644 src/librustc_mir/borrow_check/conflict_errors.rs diff --git a/src/librustc_mir/borrow_check/conflict_errors.rs b/src/librustc_mir/borrow_check/conflict_errors.rs new file mode 100644 index 00000000000..19f29f346f9 --- /dev/null +++ b/src/librustc_mir/borrow_check/conflict_errors.rs @@ -0,0 +1,1991 @@ +use rustc::hir; +use rustc::hir::def_id::DefId; +use rustc::middle::region::ScopeTree; +use rustc::mir::{ + self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, Local, + LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, PlaceProjection, + ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm, +}; +use rustc::ty::{self, Ty}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::indexed_vec::Idx; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use syntax_pos::Span; +use syntax::source_map::CompilerDesugaringKind; + +use super::nll::explain_borrow::BorrowExplanation; +use super::nll::region_infer::{RegionName, RegionNameSource}; +use super::prefixes::IsPrefixOf; +use super::WriteKind; +use super::borrow_set::BorrowData; +use super::MirBorrowckCtxt; +use super::{InitializationRequiringAction, PrefixSet}; +use super::error_reporting::{IncludingDowncast, UseSpans}; +use crate::dataflow::drop_flag_effects; +use crate::dataflow::indexes::{MovePathIndex, MoveOutIndex}; +use crate::util::borrowck_errors::{BorrowckErrors, Origin}; + +#[derive(Debug)] +struct MoveSite { + /// Index of the "move out" that we found. The `MoveData` can + /// then tell us where the move occurred. + moi: MoveOutIndex, + + /// `true` if we traversed a back edge while walking from the point + /// of error to the move site. + traversed_back_edge: bool +} + +/// Which case a StorageDeadOrDrop is for. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum StorageDeadOrDrop<'tcx> { + LocalStorageDead, + BoxedStorageDead, + Destructor(Ty<'tcx>), +} + +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + pub(super) fn report_use_of_moved_or_uninitialized( + &mut self, + location: Location, + desired_action: InitializationRequiringAction, + (moved_place, used_place, span): (&Place<'tcx>, &Place<'tcx>, Span), + mpi: MovePathIndex, + ) { + debug!( + "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \ + moved_place={:?} used_place={:?} span={:?} mpi={:?}", + location, desired_action, moved_place, used_place, span, mpi + ); + + let use_spans = self.move_spans(moved_place, location) + .or_else(|| self.borrow_spans(span, location)); + let span = use_spans.args_or_use(); + + let move_site_vec = self.get_moved_indexes(location, mpi); + debug!( + "report_use_of_moved_or_uninitialized: move_site_vec={:?}", + move_site_vec + ); + let move_out_indices: Vec<_> = move_site_vec + .iter() + .map(|move_site| move_site.moi) + .collect(); + + if move_out_indices.is_empty() { + let root_place = self.prefixes(&used_place, PrefixSet::All).last().unwrap(); + + if self.uninitialized_error_reported.contains(root_place) { + debug!( + "report_use_of_moved_or_uninitialized place: error about {:?} suppressed", + root_place + ); + return; + } + + self.uninitialized_error_reported.insert(root_place.clone()); + + let item_msg = match self.describe_place_with_options(used_place, + IncludingDowncast(true)) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable( + span, + desired_action.as_noun(), + &self.describe_place_with_options(moved_place, IncludingDowncast(true)) + .unwrap_or_else(|| "_".to_owned()), + Origin::Mir, + ); + err.span_label(span, format!("use of possibly uninitialized {}", item_msg)); + + use_spans.var_span_label( + &mut err, + format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), + ); + + err.buffer(&mut self.errors_buffer); + } else { + if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) { + if self.prefixes(&reported_place, PrefixSet::All) + .any(|p| p == used_place) + { + debug!( + "report_use_of_moved_or_uninitialized place: error suppressed \ + mois={:?}", + move_out_indices + ); + return; + } + } + + let msg = ""; //FIXME: add "partially " or "collaterally " + + let mut err = self.infcx.tcx.cannot_act_on_moved_value( + span, + desired_action.as_noun(), + msg, + self.describe_place_with_options(&moved_place, IncludingDowncast(true)), + Origin::Mir, + ); + + self.add_moved_or_invoked_closure_note( + location, + used_place, + &mut err, + ); + + let mut is_loop_move = false; + let is_partial_move = move_site_vec.iter().any(|move_site| { + let move_out = self.move_data.moves[(*move_site).moi]; + let moved_place = &self.move_data.move_paths[move_out.path].place; + used_place != moved_place && used_place.is_prefix_of(moved_place) + }); + for move_site in &move_site_vec { + let move_out = self.move_data.moves[(*move_site).moi]; + let moved_place = &self.move_data.move_paths[move_out.path].place; + + let move_spans = self.move_spans(moved_place, move_out.source); + let move_span = move_spans.args_or_use(); + + let move_msg = if move_spans.for_closure() { + " into closure" + } else { + "" + }; + + if span == move_span { + err.span_label( + span, + format!("value moved{} here, in previous iteration of loop", move_msg), + ); + if Some(CompilerDesugaringKind::ForLoop) == span.compiler_desugaring_kind() { + if let Ok(snippet) = self.infcx.tcx.sess.source_map() + .span_to_snippet(span) + { + err.span_suggestion( + move_span, + "consider borrowing this to avoid moving it into the for loop", + format!("&{}", snippet), + Applicability::MaybeIncorrect, + ); + } + } + is_loop_move = true; + } else if move_site.traversed_back_edge { + err.span_label( + move_span, + format!( + "value moved{} here, in previous iteration of loop", + move_msg + ), + ); + } else { + err.span_label(move_span, format!("value moved{} here", move_msg)); + move_spans.var_span_label( + &mut err, + format!("variable moved due to use{}", move_spans.describe()), + ); + }; + } + + use_spans.var_span_label( + &mut err, + format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), + ); + + if !is_loop_move { + err.span_label( + span, + format!( + "value {} here {}", + desired_action.as_verb_in_past_tense(), + if is_partial_move { "after partial move" } else { "after move" }, + ), + ); + } + + let ty = used_place.ty(self.mir, self.infcx.tcx).ty; + let needs_note = match ty.sty { + ty::Closure(id, _) => { + let tables = self.infcx.tcx.typeck_tables_of(id); + let hir_id = self.infcx.tcx.hir().as_local_hir_id(id).unwrap(); + + tables.closure_kind_origins().get(hir_id).is_none() + } + _ => true, + }; + + if needs_note { + let mpi = self.move_data.moves[move_out_indices[0]].path; + let place = &self.move_data.move_paths[mpi].place; + + let ty = place.ty(self.mir, self.infcx.tcx).ty; + let opt_name = self.describe_place_with_options(place, IncludingDowncast(true)); + let note_msg = match opt_name { + Some(ref name) => format!("`{}`", name), + None => "value".to_owned(), + }; + if let ty::Param(param_ty) = ty.sty { + let tcx = self.infcx.tcx; + let generics = tcx.generics_of(self.mir_def_id); + let def_id = generics.type_param(¶m_ty, tcx).def_id; + if let Some(sp) = tcx.hir().span_if_local(def_id) { + err.span_label( + sp, + "consider adding a `Copy` constraint to this type argument", + ); + } + } + if let Place::Base(PlaceBase::Local(local)) = place { + let decl = &self.mir.local_decls[*local]; + err.span_label( + decl.source_info.span, + format!( + "move occurs because {} has type `{}`, \ + which does not implement the `Copy` trait", + note_msg, ty, + )); + } else { + err.note(&format!( + "move occurs because {} has type `{}`, \ + which does not implement the `Copy` trait", + note_msg, ty + )); + } + } + + if let Some((_, mut old_err)) = self.move_error_reported + .insert(move_out_indices, (used_place.clone(), err)) + { + // Cancel the old error so it doesn't ICE. + old_err.cancel(); + } + } + } + + pub(super) fn report_move_out_while_borrowed( + &mut self, + location: Location, + (place, span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { + debug!( + "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}", + location, place, span, borrow + ); + let tcx = self.infcx.tcx; + let value_msg = match self.describe_place(place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + let borrow_msg = match self.describe_place(&borrow.borrowed_place) { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + + let borrow_spans = self.retrieve_borrow_spans(borrow); + let borrow_span = borrow_spans.args_or_use(); + + let move_spans = self.move_spans(place, location); + let span = move_spans.args_or_use(); + + let mut err = tcx.cannot_move_when_borrowed( + span, + &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), + Origin::Mir, + ); + err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg)); + err.span_label(span, format!("move out of {} occurs here", value_msg)); + + borrow_spans.var_span_label( + &mut err, + format!("borrow occurs due to use{}", borrow_spans.describe()) + ); + + move_spans.var_span_label( + &mut err, + format!("move occurs due to use{}", move_spans.describe()) + ); + + self.explain_why_borrow_contains_point( + location, + borrow, + None, + ).add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", Some(borrow_span)); + err.buffer(&mut self.errors_buffer); + } + + pub(super) fn report_use_while_mutably_borrowed( + &mut self, + location: Location, + (place, _span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) -> DiagnosticBuilder<'cx> { + let tcx = self.infcx.tcx; + + let borrow_spans = self.retrieve_borrow_spans(borrow); + let borrow_span = borrow_spans.args_or_use(); + + // Conflicting borrows are reported separately, so only check for move + // captures. + let use_spans = self.move_spans(place, location); + let span = use_spans.var_or_use(); + + let mut err = tcx.cannot_use_when_mutably_borrowed( + span, + &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), + borrow_span, + &self.describe_place(&borrow.borrowed_place) + .unwrap_or_else(|| "_".to_owned()), + Origin::Mir, + ); + + borrow_spans.var_span_label(&mut err, { + let place = &borrow.borrowed_place; + let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned()); + + format!("borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe()) + }); + + self.explain_why_borrow_contains_point(location, borrow, None) + .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); + err + } + + pub(super) fn report_conflicting_borrow( + &mut self, + location: Location, + (place, span): (&Place<'tcx>, Span), + gen_borrow_kind: BorrowKind, + issued_borrow: &BorrowData<'tcx>, + ) -> DiagnosticBuilder<'cx> { + let issued_spans = self.retrieve_borrow_spans(issued_borrow); + let issued_span = issued_spans.args_or_use(); + + let borrow_spans = self.borrow_spans(span, location); + let span = borrow_spans.args_or_use(); + + let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() { + "generator" + } else { + "closure" + }; + + let (desc_place, msg_place, msg_borrow, union_type_name) = + self.describe_place_for_conflicting_borrow(place, &issued_borrow.borrowed_place); + + let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None); + let second_borrow_desc = if explanation.is_explained() { + "second " + } else { + "" + }; + + // FIXME: supply non-"" `opt_via` when appropriate + let tcx = self.infcx.tcx; + let first_borrow_desc; + let mut err = match ( + gen_borrow_kind, + "immutable", + "mutable", + issued_borrow.kind, + "immutable", + "mutable", + ) { + (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt) => { + first_borrow_desc = "mutable "; + tcx.cannot_reborrow_already_borrowed( + span, + &desc_place, + &msg_place, + lft, + issued_span, + "it", + rgt, + &msg_borrow, + None, + Origin::Mir, + ) + } + (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => { + first_borrow_desc = "immutable "; + tcx.cannot_reborrow_already_borrowed( + span, + &desc_place, + &msg_place, + lft, + issued_span, + "it", + rgt, + &msg_borrow, + None, + Origin::Mir, + ) + } + + (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => { + first_borrow_desc = "first "; + tcx.cannot_mutably_borrow_multiply( + span, + &desc_place, + &msg_place, + issued_span, + &msg_borrow, + None, + Origin::Mir, + ) + } + + (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => { + first_borrow_desc = "first "; + tcx.cannot_uniquely_borrow_by_two_closures( + span, + &desc_place, + issued_span, + None, + Origin::Mir, + ) + } + + (BorrowKind::Mut { .. }, _, _, BorrowKind::Shallow, _, _) + | (BorrowKind::Unique, _, _, BorrowKind::Shallow, _, _) => { + let mut err = tcx.cannot_mutate_in_match_guard( + span, + issued_span, + &desc_place, + "mutably borrow", + Origin::Mir, + ); + borrow_spans.var_span_label( + &mut err, + format!( + "borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe() + ), + ); + + return err; + } + + (BorrowKind::Unique, _, _, _, _, _) => { + first_borrow_desc = "first "; + tcx.cannot_uniquely_borrow_by_one_closure( + span, + container_name, + &desc_place, + "", + issued_span, + "it", + "", + None, + Origin::Mir, + ) + }, + + (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => { + first_borrow_desc = "first "; + tcx.cannot_reborrow_already_uniquely_borrowed( + span, + container_name, + &desc_place, + "", + lft, + issued_span, + "", + None, + second_borrow_desc, + Origin::Mir, + ) + } + + (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => { + first_borrow_desc = "first "; + tcx.cannot_reborrow_already_uniquely_borrowed( + span, + container_name, + &desc_place, + "", + lft, + issued_span, + "", + None, + second_borrow_desc, + Origin::Mir, + ) + } + + (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) + | (BorrowKind::Shared, _, _, BorrowKind::Shallow, _, _) + | (BorrowKind::Shallow, _, _, BorrowKind::Mut { .. }, _, _) + | (BorrowKind::Shallow, _, _, BorrowKind::Unique, _, _) + | (BorrowKind::Shallow, _, _, BorrowKind::Shared, _, _) + | (BorrowKind::Shallow, _, _, BorrowKind::Shallow, _, _) => unreachable!(), + }; + + if issued_spans == borrow_spans { + borrow_spans.var_span_label( + &mut err, + format!("borrows occur due to use of `{}`{}", desc_place, borrow_spans.describe()), + ); + } else { + let borrow_place = &issued_borrow.borrowed_place; + let borrow_place_desc = self.describe_place(borrow_place) + .unwrap_or_else(|| "_".to_owned()); + issued_spans.var_span_label( + &mut err, + format!( + "first borrow occurs due to use of `{}`{}", + borrow_place_desc, + issued_spans.describe(), + ), + ); + + borrow_spans.var_span_label( + &mut err, + format!( + "second borrow occurs due to use of `{}`{}", + desc_place, + borrow_spans.describe(), + ), + ); + } + + if union_type_name != "" { + err.note(&format!( + "`{}` is a field of the union `{}`, so it overlaps the field `{}`", + msg_place, union_type_name, msg_borrow, + )); + } + + explanation.add_explanation_to_diagnostic( + self.infcx.tcx, + self.mir, + &mut err, + first_borrow_desc, + None, + ); + + err + } + + /// Returns the description of the root place for a conflicting borrow and the full + /// descriptions of the places that caused the conflict. + /// + /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is + /// attempted while a shared borrow is live, then this function will return: + /// + /// ("x", "", "") + /// + /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while + /// a shared borrow of another field `x.y`, then this function will return: + /// + /// ("x", "x.z", "x.y") + /// + /// In the more complex union case, where the union is a field of a struct, then if a mutable + /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of + /// another field `x.u.y`, then this function will return: + /// + /// ("x.u", "x.u.z", "x.u.y") + /// + /// This is used when creating error messages like below: + /// + /// > cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as + /// > mutable (via `a.u.s.b`) [E0502] + pub(super) fn describe_place_for_conflicting_borrow( + &self, + first_borrowed_place: &Place<'tcx>, + second_borrowed_place: &Place<'tcx>, + ) -> (String, String, String, String) { + // Define a small closure that we can use to check if the type of a place + // is a union. + let is_union = |place: &Place<'tcx>| -> bool { + place.ty(self.mir, self.infcx.tcx).ty + .ty_adt_def() + .map(|adt| adt.is_union()) + .unwrap_or(false) + }; + + // Start with an empty tuple, so we can use the functions on `Option` to reduce some + // code duplication (particularly around returning an empty description in the failure + // case). + Some(()) + .filter(|_| { + // If we have a conflicting borrow of the same place, then we don't want to add + // an extraneous "via x.y" to our diagnostics, so filter out this case. + first_borrowed_place != second_borrowed_place + }) + .and_then(|_| { + // We're going to want to traverse the first borrowed place to see if we can find + // field access to a union. If we find that, then we will keep the place of the + // union being accessed and the field that was being accessed so we can check the + // second borrowed place for the same union and a access to a different field. + let mut current = first_borrowed_place; + while let Place::Projection(box PlaceProjection { base, elem }) = current { + match elem { + ProjectionElem::Field(field, _) if is_union(base) => { + return Some((base, field)); + }, + _ => current = base, + } + } + None + }) + .and_then(|(target_base, target_field)| { + // With the place of a union and a field access into it, we traverse the second + // borrowed place and look for a access to a different field of the same union. + let mut current = second_borrowed_place; + while let Place::Projection(box PlaceProjection { base, elem }) = current { + match elem { + ProjectionElem::Field(field, _) if { + is_union(base) && field != target_field && base == target_base + } => { + let desc_base = self.describe_place(base) + .unwrap_or_else(|| "_".to_owned()); + let desc_first = self.describe_place(first_borrowed_place) + .unwrap_or_else(|| "_".to_owned()); + let desc_second = self.describe_place(second_borrowed_place) + .unwrap_or_else(|| "_".to_owned()); + + // Also compute the name of the union type, eg. `Foo` so we + // can add a helpful note with it. + let ty = base.ty(self.mir, self.infcx.tcx).ty; + + return Some((desc_base, desc_first, desc_second, ty.to_string())); + }, + _ => current = base, + } + } + None + }) + .unwrap_or_else(|| { + // If we didn't find a field access into a union, or both places match, then + // only return the description of the first place. + let desc_place = self.describe_place(first_borrowed_place) + .unwrap_or_else(|| "_".to_owned()); + (desc_place, "".to_string(), "".to_string(), "".to_string()) + }) + } + + /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`. + /// + /// This means that some data referenced by `borrow` needs to live + /// past the point where the StorageDeadOrDrop of `place` occurs. + /// This is usually interpreted as meaning that `place` has too + /// short a lifetime. (But sometimes it is more useful to report + /// it as a more direct conflict between the execution of a + /// `Drop::drop` with an aliasing borrow.) + pub(super) fn report_borrowed_value_does_not_live_long_enough( + &mut self, + location: Location, + borrow: &BorrowData<'tcx>, + place_span: (&Place<'tcx>, Span), + kind: Option, + ) { + debug!( + "report_borrowed_value_does_not_live_long_enough(\ + {:?}, {:?}, {:?}, {:?}\ + )", + location, borrow, place_span, kind + ); + + let drop_span = place_span.1; + let scope_tree = self.infcx.tcx.region_scope_tree(self.mir_def_id); + let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All) + .last() + .unwrap(); + + let borrow_spans = self.retrieve_borrow_spans(borrow); + let borrow_span = borrow_spans.var_or_use(); + + let proper_span = match *root_place { + Place::Base(PlaceBase::Local(local)) => self.mir.local_decls[local].source_info.span, + _ => drop_span, + }; + + if self.access_place_error_reported + .contains(&(root_place.clone(), borrow_span)) + { + debug!( + "suppressing access_place error when borrow doesn't live long enough for {:?}", + borrow_span + ); + return; + } + + self.access_place_error_reported + .insert((root_place.clone(), borrow_span)); + + if let StorageDeadOrDrop::Destructor(dropped_ty) = + self.classify_drop_access_kind(&borrow.borrowed_place) + { + // 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( + location, borrow, place_span, kind, dropped_ty, + ); + return; + } + } + + let place_desc = self.describe_place(&borrow.borrowed_place); + + let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0)); + let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place); + + let err = match (place_desc, explanation) { + (Some(_), _) if self.is_place_thread_local(root_place) => { + self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span) + } + // If the outlives constraint comes from inside the closure, + // for example: + // + // let x = 0; + // let y = &x; + // Box::new(|| y) as Box &'static i32> + // + // then just use the normal error. The closure isn't escaping + // and `move` will not help here. + ( + Some(ref name), + BorrowExplanation::MustBeValidFor { + category: category @ ConstraintCategory::Return, + from_closure: false, + ref region_name, + span, + .. + }, + ) + | ( + Some(ref name), + BorrowExplanation::MustBeValidFor { + category: category @ ConstraintCategory::CallArgument, + from_closure: false, + ref region_name, + span, + .. + }, + ) if borrow_spans.for_closure() => self.report_escaping_closure_capture( + borrow_spans.args_or_use(), + borrow_span, + region_name, + category, + span, + &format!("`{}`", name), + ), + ( + ref name, + BorrowExplanation::MustBeValidFor { + category: ConstraintCategory::Assignment, + from_closure: false, + region_name: RegionName { + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name), + .. + }, + span, + .. + }, + ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span), + (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( + location, + &name, + &scope_tree, + &borrow, + drop_span, + borrow_spans, + explanation, + ), + (None, explanation) => self.report_temporary_value_does_not_live_long_enough( + location, + &scope_tree, + &borrow, + drop_span, + borrow_spans, + proper_span, + explanation, + ), + }; + + err.buffer(&mut self.errors_buffer); + } + + fn report_local_value_does_not_live_long_enough( + &mut self, + location: Location, + name: &str, + scope_tree: &'tcx ScopeTree, + borrow: &BorrowData<'tcx>, + drop_span: Span, + borrow_spans: UseSpans, + explanation: BorrowExplanation, + ) -> DiagnosticBuilder<'cx> { + debug!( + "report_local_value_does_not_live_long_enough(\ + {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\ + )", + location, name, scope_tree, borrow, drop_span, borrow_spans + ); + + let borrow_span = borrow_spans.var_or_use(); + if let BorrowExplanation::MustBeValidFor { + category, + span, + ref opt_place_desc, + from_closure: false, + .. + } = explanation { + if let Some(diag) = self.try_report_cannot_return_reference_to_local( + borrow, + borrow_span, + span, + category, + opt_place_desc.as_ref(), + ) { + return diag; + } + } + + let mut err = self.infcx.tcx.path_does_not_live_long_enough( + borrow_span, + &format!("`{}`", name), + Origin::Mir, + ); + + if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { + let region_name = annotation.emit(self, &mut err); + + err.span_label( + borrow_span, + format!("`{}` would have to be valid for `{}`...", name, region_name), + ); + + if let Some(fn_hir_id) = self.infcx.tcx.hir().as_local_hir_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_by_hir_id(fn_hir_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.add_explanation_to_diagnostic( + self.infcx.tcx, + self.mir, + &mut err, + "", + None, + ); + } + } 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), + ); + + let within = if borrow_spans.for_generator() { + " by generator" + } else { + "" + }; + + borrow_spans.args_span_label( + &mut err, + format!("value captured here{}", within), + ); + + explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); + } + + err + } + + fn report_borrow_conflicts_with_destructor( + &mut self, + location: Location, + borrow: &BorrowData<'tcx>, + (place, drop_span): (&Place<'tcx>, Span), + kind: Option, + dropped_ty: Ty<'tcx>, + ) { + debug!( + "report_borrow_conflicts_with_destructor(\ + {:?}, {:?}, ({:?}, {:?}), {:?}\ + )", + location, 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.infcx + .tcx + .cannot_borrow_across_destructor(borrow_span, Origin::Mir); + + let what_was_dropped = match self.describe_place(place) { + Some(name) => format!("`{}`", name.as_str()), + None => String::from("temporary value"), + }; + + let label = match self.describe_place(&borrow.borrowed_place) { + Some(borrowed) => format!( + "here, drop of {D} needs exclusive access to `{B}`, \ + because the type `{T}` implements the `Drop` trait", + D = what_was_dropped, + T = dropped_ty, + B = borrowed + ), + None => format!( + "here is drop of {D}; whose type `{T}` implements the `Drop` trait", + D = what_was_dropped, + T = dropped_ty + ), + }; + err.span_label(drop_span, label); + + // Only give this note and suggestion if they could be relevant. + let explanation = + self.explain_why_borrow_contains_point(location, 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"); + } + _ => {} + } + + explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); + + err.buffer(&mut self.errors_buffer); + } + + fn report_thread_local_value_does_not_live_long_enough( + &mut self, + drop_span: Span, + borrow_span: Span, + ) -> DiagnosticBuilder<'cx> { + debug!( + "report_thread_local_value_does_not_live_long_enough(\ + {:?}, {:?}\ + )", + drop_span, borrow_span + ); + + 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 + } + + fn report_temporary_value_does_not_live_long_enough( + &mut self, + location: Location, + scope_tree: &'tcx ScopeTree, + borrow: &BorrowData<'tcx>, + drop_span: Span, + borrow_spans: UseSpans, + proper_span: Span, + explanation: BorrowExplanation, + ) -> DiagnosticBuilder<'cx> { + debug!( + "report_temporary_value_does_not_live_long_enough(\ + {:?}, {:?}, {:?}, {:?}, {:?}\ + )", + location, scope_tree, borrow, drop_span, proper_span + ); + + if let BorrowExplanation::MustBeValidFor { + category, + span, + from_closure: false, + .. + } = explanation { + if let Some(diag) = self.try_report_cannot_return_reference_to_local( + borrow, + proper_span, + span, + category, + None, + ) { + return diag; + } + } + + let tcx = self.infcx.tcx; + let mut err = tcx.temporary_value_borrowed_for_too_long(proper_span, Origin::Mir); + err.span_label( + proper_span, + "creates a temporary which is freed while still in use", + ); + err.span_label( + drop_span, + "temporary value is freed at the end of this statement", + ); + + 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"); + } + _ => {} + } + explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); + + let within = if borrow_spans.for_generator() { + " by generator" + } else { + "" + }; + + borrow_spans.args_span_label( + &mut err, + format!("value captured here{}", within), + ); + + err + } + + fn try_report_cannot_return_reference_to_local( + &self, + borrow: &BorrowData<'tcx>, + borrow_span: Span, + return_span: Span, + category: ConstraintCategory, + opt_place_desc: Option<&String>, + ) -> Option> { + let tcx = self.infcx.tcx; + + let return_kind = match category { + ConstraintCategory::Return => "return", + ConstraintCategory::Yield => "yield", + _ => return None, + }; + + // FIXME use a better heuristic than Spans + let reference_desc = if return_span == self.mir.source_info(borrow.reserve_location).span { + "reference to" + } else { + "value referencing" + }; + + let (place_desc, note) = if let Some(place_desc) = opt_place_desc { + let local_kind = match borrow.borrowed_place { + Place::Base(PlaceBase::Local(local)) => { + match self.mir.local_kind(local) { + LocalKind::ReturnPointer + | LocalKind::Temp => bug!("temporary or return pointer with a name"), + LocalKind::Var => "local variable ", + LocalKind::Arg + if !self.upvars.is_empty() + && local == Local::new(1) => { + "variable captured by `move` " + } + LocalKind::Arg => { + "function parameter " + } + } + } + _ => "local data ", + }; + ( + format!("{}`{}`", local_kind, place_desc), + format!("`{}` is borrowed here", place_desc), + ) + } else { + let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All) + .last() + .unwrap(); + let local = if let Place::Base(PlaceBase::Local(local)) = *root_place { + local + } else { + bug!("try_report_cannot_return_reference_to_local: not a local") + }; + match self.mir.local_kind(local) { + LocalKind::ReturnPointer | LocalKind::Temp => { + ( + "temporary value".to_string(), + "temporary value created here".to_string(), + ) + } + LocalKind::Arg => { + ( + "function parameter".to_string(), + "function parameter borrowed here".to_string(), + ) + }, + LocalKind::Var => bug!("local variable without a name"), + } + }; + + let mut err = tcx.cannot_return_reference_to_local( + return_span, + return_kind, + reference_desc, + &place_desc, + Origin::Mir, + ); + + if return_span != borrow_span { + err.span_label(borrow_span, note); + } + + Some(err) + } + + fn report_escaping_closure_capture( + &mut self, + args_span: Span, + var_span: Span, + fr_name: &RegionName, + category: ConstraintCategory, + constraint_span: Span, + captured_var: &str, + ) -> DiagnosticBuilder<'cx> { + let tcx = self.infcx.tcx; + + let mut err = tcx.cannot_capture_in_long_lived_closure( + args_span, + captured_var, + var_span, + Origin::Mir, + ); + + let suggestion = match tcx.sess.source_map().span_to_snippet(args_span) { + Ok(string) => format!("move {}", string), + Err(_) => "move || ".to_string() + }; + + err.span_suggestion( + args_span, + &format!("to force the closure to take ownership of {} (and any \ + other referenced variables), use the `move` keyword", + captured_var), + suggestion, + Applicability::MachineApplicable, + ); + + match category { + ConstraintCategory::Return => { + err.span_note(constraint_span, "closure is returned here"); + } + ConstraintCategory::CallArgument => { + fr_name.highlight_region_name(&mut err); + err.span_note( + constraint_span, + &format!("function requires argument type to outlive `{}`", fr_name), + ); + } + _ => bug!("report_escaping_closure_capture called with unexpected constraint \ + category: `{:?}`", category), + } + err + } + + fn report_escaping_data( + &mut self, + borrow_span: Span, + name: &Option, + upvar_span: Span, + upvar_name: &str, + escape_span: Span, + ) -> DiagnosticBuilder<'cx> { + let tcx = self.infcx.tcx; + + let escapes_from = if tcx.is_closure(self.mir_def_id) { + let tables = tcx.typeck_tables_of(self.mir_def_id); + let mir_hir_id = tcx.hir().def_index_to_hir_id(self.mir_def_id.index); + match tables.node_type(mir_hir_id).sty { + ty::Closure(..) => "closure", + ty::Generator(..) => "generator", + _ => bug!("Closure body doesn't have a closure or generator type"), + } + } else { + "function" + }; + + let mut err = tcx.borrowed_data_escapes_closure(escape_span, escapes_from, Origin::Mir); + + err.span_label( + upvar_span, + format!( + "`{}` is declared here, outside of the {} body", + upvar_name, escapes_from + ), + ); + + err.span_label( + borrow_span, + format!( + "borrow is only valid in the {} body", + escapes_from + ), + ); + + if let Some(name) = name { + err.span_label( + escape_span, + format!("reference to `{}` escapes the {} body here", name, escapes_from), + ); + } else { + err.span_label( + escape_span, + format!("reference escapes the {} body here", escapes_from), + ); + } + + err + } + + fn get_moved_indexes(&mut self, location: Location, mpi: MovePathIndex) -> Vec { + let mir = self.mir; + + let mut stack = Vec::new(); + stack.extend(mir.predecessor_locations(location).map(|predecessor| { + let is_back_edge = location.dominates(predecessor, &self.dominators); + (predecessor, is_back_edge) + })); + + let mut visited = FxHashSet::default(); + let mut result = vec![]; + + 'dfs: while let Some((location, is_back_edge)) = stack.pop() { + debug!( + "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})", + location, is_back_edge + ); + + if !visited.insert(location) { + continue; + } + + // check for moves + let stmt_kind = mir[location.block] + .statements + .get(location.statement_index) + .map(|s| &s.kind); + if let Some(StatementKind::StorageDead(..)) = stmt_kind { + // this analysis only tries to find moves explicitly + // written by the user, so we ignore the move-outs + // created by `StorageDead` and at the beginning + // of a function. + } else { + // If we are found a use of a.b.c which was in error, then we want to look for + // moves not only of a.b.c but also a.b and a. + // + // Note that the moves data already includes "parent" paths, so we don't have to + // worry about the other case: that is, if there is a move of a.b.c, it is already + // marked as a move of a.b and a as well, so we will generate the correct errors + // there. + let mut mpis = vec![mpi]; + let move_paths = &self.move_data.move_paths; + mpis.extend(move_paths[mpi].parents(move_paths)); + + for moi in &self.move_data.loc_map[location] { + debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi); + if mpis.contains(&self.move_data.moves[*moi].path) { + debug!("report_use_of_moved_or_uninitialized: found"); + result.push(MoveSite { + moi: *moi, + traversed_back_edge: is_back_edge, + }); + + // Strictly speaking, we could continue our DFS here. There may be + // other moves that can reach the point of error. But it is kind of + // confusing to highlight them. + // + // Example: + // + // ``` + // let a = vec![]; + // let b = a; + // let c = a; + // drop(a); // <-- current point of error + // ``` + // + // Because we stop the DFS here, we only highlight `let c = a`, + // and not `let b = a`. We will of course also report an error at + // `let c = a` which highlights `let b = a` as the move. + continue 'dfs; + } + } + } + + // check for inits + let mut any_match = false; + drop_flag_effects::for_location_inits( + self.infcx.tcx, + self.mir, + self.move_data, + location, + |m| { + if m == mpi { + any_match = true; + } + }, + ); + if any_match { + continue 'dfs; + } + + stack.extend(mir.predecessor_locations(location).map(|predecessor| { + let back_edge = location.dominates(predecessor, &self.dominators); + (predecessor, is_back_edge || back_edge) + })); + } + + result + } + + pub(super) fn report_illegal_mutation_of_borrowed( + &mut self, + location: Location, + (place, span): (&Place<'tcx>, Span), + loan: &BorrowData<'tcx>, + ) { + let loan_spans = self.retrieve_borrow_spans(loan); + let loan_span = loan_spans.args_or_use(); + + let tcx = self.infcx.tcx; + if loan.kind == BorrowKind::Shallow { + let mut err = tcx.cannot_mutate_in_match_guard( + span, + loan_span, + &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), + "assign", + Origin::Mir, + ); + loan_spans.var_span_label( + &mut err, + format!("borrow occurs due to use{}", loan_spans.describe()), + ); + + err.buffer(&mut self.errors_buffer); + + return; + } + + let mut err = tcx.cannot_assign_to_borrowed( + span, + loan_span, + &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), + Origin::Mir, + ); + + loan_spans.var_span_label( + &mut err, + format!("borrow occurs due to use{}", loan_spans.describe()), + ); + + self.explain_why_borrow_contains_point(location, loan, None) + .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); + + err.buffer(&mut self.errors_buffer); + } + + /// Reports an illegal reassignment; for example, an assignment to + /// (part of) a non-`mut` local that occurs potentially after that + /// local has already been initialized. `place` is the path being + /// assigned; `err_place` is a place providing a reason why + /// `place` is not mutable (e.g., the non-`mut` local `x` in an + /// assignment to `x.f`). + pub(super) fn report_illegal_reassignment( + &mut self, + _location: Location, + (place, span): (&Place<'tcx>, Span), + assigned_span: Span, + err_place: &Place<'tcx>, + ) { + let (from_arg, local_decl) = if let Place::Base(PlaceBase::Local(local)) = *err_place { + if let LocalKind::Arg = self.mir.local_kind(local) { + (true, Some(&self.mir.local_decls[local])) + } else { + (false, Some(&self.mir.local_decls[local])) + } + } else { + (false, None) + }; + + // If root local is initialized immediately (everything apart from let + // PATTERN;) then make the error refer to that local, rather than the + // place being assigned later. + let (place_description, assigned_span) = match local_decl { + Some(LocalDecl { + is_user_variable: Some(ClearCrossCrate::Clear), + .. + }) + | Some(LocalDecl { + is_user_variable: + Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + opt_match_place: None, + .. + }))), + .. + }) + | Some(LocalDecl { + is_user_variable: None, + .. + }) + | None => (self.describe_place(place), assigned_span), + Some(decl) => (self.describe_place(err_place), decl.source_info.span), + }; + + let mut err = self.infcx.tcx.cannot_reassign_immutable( + span, + place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"), + from_arg, + Origin::Mir, + ); + let msg = if from_arg { + "cannot assign to immutable argument" + } else { + "cannot assign twice to immutable variable" + }; + if span != assigned_span { + if !from_arg { + let value_msg = match place_description { + Some(name) => format!("`{}`", name), + None => "value".to_owned(), + }; + err.span_label(assigned_span, format!("first assignment to {}", value_msg)); + } + } + if let Some(decl) = local_decl { + if let Some(name) = decl.name { + if decl.can_be_made_mutable() { + err.span_suggestion( + decl.source_info.span, + "make this binding mutable", + format!("mut {}", name), + Applicability::MachineApplicable, + ); + } + } + } + err.span_label(span, msg); + err.buffer(&mut self.errors_buffer); + } + + fn classify_drop_access_kind(&self, place: &Place<'tcx>) -> StorageDeadOrDrop<'tcx> { + let tcx = self.infcx.tcx; + match place { + Place::Base(PlaceBase::Local(_)) | + Place::Base(PlaceBase::Static(_)) => { + StorageDeadOrDrop::LocalStorageDead + } + Place::Projection(box PlaceProjection { base, elem }) => { + let base_access = self.classify_drop_access_kind(base); + match elem { + ProjectionElem::Deref => match base_access { + StorageDeadOrDrop::LocalStorageDead + | StorageDeadOrDrop::BoxedStorageDead => { + assert!( + base.ty(self.mir, tcx).ty.is_box(), + "Drop of value behind a reference or raw pointer" + ); + StorageDeadOrDrop::BoxedStorageDead + } + StorageDeadOrDrop::Destructor(_) => base_access, + }, + ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { + let base_ty = base.ty(self.mir, tcx).ty; + match base_ty.sty { + ty::Adt(def, _) if def.has_dtor(tcx) => { + // Report the outermost adt with a destructor + match base_access { + StorageDeadOrDrop::Destructor(_) => base_access, + StorageDeadOrDrop::LocalStorageDead + | StorageDeadOrDrop::BoxedStorageDead => { + StorageDeadOrDrop::Destructor(base_ty) + } + } + } + _ => base_access, + } + } + + ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Index(_) => base_access, + } + } + } + } + + /// 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::FnDef(_, _) | ty::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 + ); + if let Some(&Statement { kind: StatementKind::Assign(ref reservation, _), ..}) + = &self.mir[location.block].statements.get(location.statement_index) + { + 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::Base(PlaceBase::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::Base(PlaceBase::Local(assigned_to)), + box 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::Base(PlaceBase::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_hir_id = self.infcx.tcx.hir().as_local_hir_id(did)?; + let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_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::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::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. + if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].node { + // With access to the lifetime, we can get + // the span of it. + arguments.push((*argument, lifetime.span)); + } else { + 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::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::Tuple(elems) = argument_ty.sty { + let argument_ty = elems.first()?.expect_ty(); + if let ty::Ref(_, _, _) = argument_ty.sty { + return Some(AnnotatedBorrowFnSignature::Closure { + argument_ty, + argument_span, + }); + } + } + + None + } + ty::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::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<'tcx>, Span)>, + return_ty: Ty<'tcx>, + return_span: Span, + }, + AnonymousFunction { + argument_ty: Ty<'tcx>, + argument_span: Span, + return_ty: Ty<'tcx>, + return_span: Span, + }, + Closure { + argument_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. + pub(super) fn emit( + &self, + cx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, + diag: &mut DiagnosticBuilder<'_>, + ) -> String { + match self { + AnnotatedBorrowFnSignature::Closure { + argument_ty, + argument_span, + } => { + diag.span_label( + *argument_span, + format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)), + ); + + cx.get_region_name_for_ty(argument_ty, 0) + } + AnnotatedBorrowFnSignature::AnonymousFunction { + argument_ty, + argument_span, + return_ty, + return_span, + } => { + let argument_ty_name = cx.get_name_for_ty(argument_ty, 0); + diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name)); + + let return_ty_name = cx.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 ", + ); + + cx.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 = cx.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 + } + } + } +} diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index d7841807624..e7eef813e33 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -1,1505 +1,22 @@ -use crate::borrow_check::nll::explain_borrow::BorrowExplanation; -use crate::borrow_check::nll::region_infer::{RegionName, RegionNameSource}; -use crate::borrow_check::prefixes::IsPrefixOf; -use crate::borrow_check::WriteKind; use rustc::hir; use rustc::hir::def::Namespace; use rustc::hir::def_id::DefId; -use rustc::middle::region::ScopeTree; use rustc::mir::{ - self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, Constant, - ConstraintCategory, Field, Local, LocalDecl, LocalKind, Location, Operand, - Place, PlaceBase, PlaceProjection, ProjectionElem, Rvalue, Statement, StatementKind, - Static, StaticKind, TerminatorKind, VarBindingForm, + AggregateKind, BindingForm, ClearCrossCrate, Constant, Field, Local, + LocalKind, Location, Operand, Place, PlaceBase, ProjectionElem, Rvalue, + Statement, StatementKind, Static, StaticKind, TerminatorKind, }; use rustc::ty::{self, DefIdTree, Ty}; use rustc::ty::layout::VariantIdx; use rustc::ty::print::Print; -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::indexed_vec::Idx; -use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_errors::DiagnosticBuilder; use syntax_pos::Span; -use syntax::source_map::CompilerDesugaringKind; use syntax::symbol::sym; use super::borrow_set::BorrowData; use super::{MirBorrowckCtxt}; -use super::{InitializationRequiringAction, PrefixSet}; -use crate::dataflow::drop_flag_effects; -use crate::dataflow::indexes::{MovePathIndex, MoveOutIndex}; -use crate::util::borrowck_errors::{BorrowckErrors, Origin}; -#[derive(Debug)] -struct MoveSite { - /// Index of the "move out" that we found. The `MoveData` can - /// then tell us where the move occurred. - moi: MoveOutIndex, - - /// `true` if we traversed a back edge while walking from the point - /// of error to the move site. - traversed_back_edge: bool -} - -impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - pub(super) fn report_use_of_moved_or_uninitialized( - &mut self, - location: Location, - desired_action: InitializationRequiringAction, - (moved_place, used_place, span): (&Place<'tcx>, &Place<'tcx>, Span), - mpi: MovePathIndex, - ) { - debug!( - "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \ - moved_place={:?} used_place={:?} span={:?} mpi={:?}", - location, desired_action, moved_place, used_place, span, mpi - ); - - let use_spans = self.move_spans(moved_place, location) - .or_else(|| self.borrow_spans(span, location)); - let span = use_spans.args_or_use(); - - let move_site_vec = self.get_moved_indexes(location, mpi); - debug!( - "report_use_of_moved_or_uninitialized: move_site_vec={:?}", - move_site_vec - ); - let move_out_indices: Vec<_> = move_site_vec - .iter() - .map(|move_site| move_site.moi) - .collect(); - - if move_out_indices.is_empty() { - let root_place = self.prefixes(&used_place, PrefixSet::All).last().unwrap(); - - if self.uninitialized_error_reported.contains(root_place) { - debug!( - "report_use_of_moved_or_uninitialized place: error about {:?} suppressed", - root_place - ); - return; - } - - self.uninitialized_error_reported.insert(root_place.clone()); - - let item_msg = match self.describe_place_with_options(used_place, - IncludingDowncast(true)) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable( - span, - desired_action.as_noun(), - &self.describe_place_with_options(moved_place, IncludingDowncast(true)) - .unwrap_or_else(|| "_".to_owned()), - Origin::Mir, - ); - err.span_label(span, format!("use of possibly uninitialized {}", item_msg)); - - use_spans.var_span_label( - &mut err, - format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), - ); - - err.buffer(&mut self.errors_buffer); - } else { - if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) { - if self.prefixes(&reported_place, PrefixSet::All) - .any(|p| p == used_place) - { - debug!( - "report_use_of_moved_or_uninitialized place: error suppressed \ - mois={:?}", - move_out_indices - ); - return; - } - } - - let msg = ""; //FIXME: add "partially " or "collaterally " - - let mut err = self.infcx.tcx.cannot_act_on_moved_value( - span, - desired_action.as_noun(), - msg, - self.describe_place_with_options(&moved_place, IncludingDowncast(true)), - Origin::Mir, - ); - - self.add_moved_or_invoked_closure_note( - location, - used_place, - &mut err, - ); - - let mut is_loop_move = false; - let is_partial_move = move_site_vec.iter().any(|move_site| { - let move_out = self.move_data.moves[(*move_site).moi]; - let moved_place = &self.move_data.move_paths[move_out.path].place; - used_place != moved_place && used_place.is_prefix_of(moved_place) - }); - for move_site in &move_site_vec { - let move_out = self.move_data.moves[(*move_site).moi]; - let moved_place = &self.move_data.move_paths[move_out.path].place; - - let move_spans = self.move_spans(moved_place, move_out.source); - let move_span = move_spans.args_or_use(); - - let move_msg = if move_spans.for_closure() { - " into closure" - } else { - "" - }; - - if span == move_span { - err.span_label( - span, - format!("value moved{} here, in previous iteration of loop", move_msg), - ); - if Some(CompilerDesugaringKind::ForLoop) == span.compiler_desugaring_kind() { - if let Ok(snippet) = self.infcx.tcx.sess.source_map() - .span_to_snippet(span) - { - err.span_suggestion( - move_span, - "consider borrowing this to avoid moving it into the for loop", - format!("&{}", snippet), - Applicability::MaybeIncorrect, - ); - } - } - is_loop_move = true; - } else if move_site.traversed_back_edge { - err.span_label( - move_span, - format!( - "value moved{} here, in previous iteration of loop", - move_msg - ), - ); - } else { - err.span_label(move_span, format!("value moved{} here", move_msg)); - move_spans.var_span_label( - &mut err, - format!("variable moved due to use{}", move_spans.describe()), - ); - }; - } - - use_spans.var_span_label( - &mut err, - format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), - ); - - if !is_loop_move { - err.span_label( - span, - format!( - "value {} here {}", - desired_action.as_verb_in_past_tense(), - if is_partial_move { "after partial move" } else { "after move" }, - ), - ); - } - - let ty = used_place.ty(self.mir, self.infcx.tcx).ty; - let needs_note = match ty.sty { - ty::Closure(id, _) => { - let tables = self.infcx.tcx.typeck_tables_of(id); - let hir_id = self.infcx.tcx.hir().as_local_hir_id(id).unwrap(); - - tables.closure_kind_origins().get(hir_id).is_none() - } - _ => true, - }; - - if needs_note { - let mpi = self.move_data.moves[move_out_indices[0]].path; - let place = &self.move_data.move_paths[mpi].place; - - let ty = place.ty(self.mir, self.infcx.tcx).ty; - let opt_name = self.describe_place_with_options(place, IncludingDowncast(true)); - let note_msg = match opt_name { - Some(ref name) => format!("`{}`", name), - None => "value".to_owned(), - }; - if let ty::Param(param_ty) = ty.sty { - let tcx = self.infcx.tcx; - let generics = tcx.generics_of(self.mir_def_id); - let def_id = generics.type_param(¶m_ty, tcx).def_id; - if let Some(sp) = tcx.hir().span_if_local(def_id) { - err.span_label( - sp, - "consider adding a `Copy` constraint to this type argument", - ); - } - } - if let Place::Base(PlaceBase::Local(local)) = place { - let decl = &self.mir.local_decls[*local]; - err.span_label( - decl.source_info.span, - format!( - "move occurs because {} has type `{}`, \ - which does not implement the `Copy` trait", - note_msg, ty, - )); - } else { - err.note(&format!( - "move occurs because {} has type `{}`, \ - which does not implement the `Copy` trait", - note_msg, ty - )); - } - } - - if let Some((_, mut old_err)) = self.move_error_reported - .insert(move_out_indices, (used_place.clone(), err)) - { - // Cancel the old error so it doesn't ICE. - old_err.cancel(); - } - } - } - - pub(super) fn report_move_out_while_borrowed( - &mut self, - location: Location, - (place, span): (&Place<'tcx>, Span), - borrow: &BorrowData<'tcx>, - ) { - debug!( - "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}", - location, place, span, borrow - ); - let tcx = self.infcx.tcx; - let value_msg = match self.describe_place(place) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - let borrow_msg = match self.describe_place(&borrow.borrowed_place) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - - let borrow_spans = self.retrieve_borrow_spans(borrow); - let borrow_span = borrow_spans.args_or_use(); - - let move_spans = self.move_spans(place, location); - let span = move_spans.args_or_use(); - - let mut err = tcx.cannot_move_when_borrowed( - span, - &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), - Origin::Mir, - ); - err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg)); - err.span_label(span, format!("move out of {} occurs here", value_msg)); - - borrow_spans.var_span_label( - &mut err, - format!("borrow occurs due to use{}", borrow_spans.describe()) - ); - - move_spans.var_span_label( - &mut err, - format!("move occurs due to use{}", move_spans.describe()) - ); - - self.explain_why_borrow_contains_point( - location, - borrow, - None, - ).add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", Some(borrow_span)); - err.buffer(&mut self.errors_buffer); - } - - pub(super) fn report_use_while_mutably_borrowed( - &mut self, - location: Location, - (place, _span): (&Place<'tcx>, Span), - borrow: &BorrowData<'tcx>, - ) -> DiagnosticBuilder<'cx> { - let tcx = self.infcx.tcx; - - let borrow_spans = self.retrieve_borrow_spans(borrow); - let borrow_span = borrow_spans.args_or_use(); - - // Conflicting borrows are reported separately, so only check for move - // captures. - let use_spans = self.move_spans(place, location); - let span = use_spans.var_or_use(); - - let mut err = tcx.cannot_use_when_mutably_borrowed( - span, - &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), - borrow_span, - &self.describe_place(&borrow.borrowed_place) - .unwrap_or_else(|| "_".to_owned()), - Origin::Mir, - ); - - borrow_spans.var_span_label(&mut err, { - let place = &borrow.borrowed_place; - let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned()); - - format!("borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe()) - }); - - self.explain_why_borrow_contains_point(location, borrow, None) - .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); - err - } - - pub(super) fn report_conflicting_borrow( - &mut self, - location: Location, - (place, span): (&Place<'tcx>, Span), - gen_borrow_kind: BorrowKind, - issued_borrow: &BorrowData<'tcx>, - ) -> DiagnosticBuilder<'cx> { - let issued_spans = self.retrieve_borrow_spans(issued_borrow); - let issued_span = issued_spans.args_or_use(); - - let borrow_spans = self.borrow_spans(span, location); - let span = borrow_spans.args_or_use(); - - let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() { - "generator" - } else { - "closure" - }; - - let (desc_place, msg_place, msg_borrow, union_type_name) = - self.describe_place_for_conflicting_borrow(place, &issued_borrow.borrowed_place); - - let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None); - let second_borrow_desc = if explanation.is_explained() { - "second " - } else { - "" - }; - - // FIXME: supply non-"" `opt_via` when appropriate - let tcx = self.infcx.tcx; - let first_borrow_desc; - let mut err = match ( - gen_borrow_kind, - "immutable", - "mutable", - issued_borrow.kind, - "immutable", - "mutable", - ) { - (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt) => { - first_borrow_desc = "mutable "; - tcx.cannot_reborrow_already_borrowed( - span, - &desc_place, - &msg_place, - lft, - issued_span, - "it", - rgt, - &msg_borrow, - None, - Origin::Mir, - ) - } - (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => { - first_borrow_desc = "immutable "; - tcx.cannot_reborrow_already_borrowed( - span, - &desc_place, - &msg_place, - lft, - issued_span, - "it", - rgt, - &msg_borrow, - None, - Origin::Mir, - ) - } - - (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => { - first_borrow_desc = "first "; - tcx.cannot_mutably_borrow_multiply( - span, - &desc_place, - &msg_place, - issued_span, - &msg_borrow, - None, - Origin::Mir, - ) - } - - (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => { - first_borrow_desc = "first "; - tcx.cannot_uniquely_borrow_by_two_closures( - span, - &desc_place, - issued_span, - None, - Origin::Mir, - ) - } - - (BorrowKind::Mut { .. }, _, _, BorrowKind::Shallow, _, _) - | (BorrowKind::Unique, _, _, BorrowKind::Shallow, _, _) => { - let mut err = tcx.cannot_mutate_in_match_guard( - span, - issued_span, - &desc_place, - "mutably borrow", - Origin::Mir, - ); - borrow_spans.var_span_label( - &mut err, - format!( - "borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe() - ), - ); - - return err; - } - - (BorrowKind::Unique, _, _, _, _, _) => { - first_borrow_desc = "first "; - tcx.cannot_uniquely_borrow_by_one_closure( - span, - container_name, - &desc_place, - "", - issued_span, - "it", - "", - None, - Origin::Mir, - ) - }, - - (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => { - first_borrow_desc = "first "; - tcx.cannot_reborrow_already_uniquely_borrowed( - span, - container_name, - &desc_place, - "", - lft, - issued_span, - "", - None, - second_borrow_desc, - Origin::Mir, - ) - } - - (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => { - first_borrow_desc = "first "; - tcx.cannot_reborrow_already_uniquely_borrowed( - span, - container_name, - &desc_place, - "", - lft, - issued_span, - "", - None, - second_borrow_desc, - Origin::Mir, - ) - } - - (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) - | (BorrowKind::Shared, _, _, BorrowKind::Shallow, _, _) - | (BorrowKind::Shallow, _, _, BorrowKind::Mut { .. }, _, _) - | (BorrowKind::Shallow, _, _, BorrowKind::Unique, _, _) - | (BorrowKind::Shallow, _, _, BorrowKind::Shared, _, _) - | (BorrowKind::Shallow, _, _, BorrowKind::Shallow, _, _) => unreachable!(), - }; - - if issued_spans == borrow_spans { - borrow_spans.var_span_label( - &mut err, - format!("borrows occur due to use of `{}`{}", desc_place, borrow_spans.describe()), - ); - } else { - let borrow_place = &issued_borrow.borrowed_place; - let borrow_place_desc = self.describe_place(borrow_place) - .unwrap_or_else(|| "_".to_owned()); - issued_spans.var_span_label( - &mut err, - format!( - "first borrow occurs due to use of `{}`{}", - borrow_place_desc, - issued_spans.describe(), - ), - ); - - borrow_spans.var_span_label( - &mut err, - format!( - "second borrow occurs due to use of `{}`{}", - desc_place, - borrow_spans.describe(), - ), - ); - } - - if union_type_name != "" { - err.note(&format!( - "`{}` is a field of the union `{}`, so it overlaps the field `{}`", - msg_place, union_type_name, msg_borrow, - )); - } - - explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.mir, - &mut err, - first_borrow_desc, - None, - ); - - err - } - - /// Returns the description of the root place for a conflicting borrow and the full - /// descriptions of the places that caused the conflict. - /// - /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is - /// attempted while a shared borrow is live, then this function will return: - /// - /// ("x", "", "") - /// - /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while - /// a shared borrow of another field `x.y`, then this function will return: - /// - /// ("x", "x.z", "x.y") - /// - /// In the more complex union case, where the union is a field of a struct, then if a mutable - /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of - /// another field `x.u.y`, then this function will return: - /// - /// ("x.u", "x.u.z", "x.u.y") - /// - /// This is used when creating error messages like below: - /// - /// > cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as - /// > mutable (via `a.u.s.b`) [E0502] - pub(super) fn describe_place_for_conflicting_borrow( - &self, - first_borrowed_place: &Place<'tcx>, - second_borrowed_place: &Place<'tcx>, - ) -> (String, String, String, String) { - // Define a small closure that we can use to check if the type of a place - // is a union. - let is_union = |place: &Place<'tcx>| -> bool { - place.ty(self.mir, self.infcx.tcx).ty - .ty_adt_def() - .map(|adt| adt.is_union()) - .unwrap_or(false) - }; - - // Start with an empty tuple, so we can use the functions on `Option` to reduce some - // code duplication (particularly around returning an empty description in the failure - // case). - Some(()) - .filter(|_| { - // If we have a conflicting borrow of the same place, then we don't want to add - // an extraneous "via x.y" to our diagnostics, so filter out this case. - first_borrowed_place != second_borrowed_place - }) - .and_then(|_| { - // We're going to want to traverse the first borrowed place to see if we can find - // field access to a union. If we find that, then we will keep the place of the - // union being accessed and the field that was being accessed so we can check the - // second borrowed place for the same union and a access to a different field. - let mut current = first_borrowed_place; - while let Place::Projection(box PlaceProjection { base, elem }) = current { - match elem { - ProjectionElem::Field(field, _) if is_union(base) => { - return Some((base, field)); - }, - _ => current = base, - } - } - None - }) - .and_then(|(target_base, target_field)| { - // With the place of a union and a field access into it, we traverse the second - // borrowed place and look for a access to a different field of the same union. - let mut current = second_borrowed_place; - while let Place::Projection(box PlaceProjection { base, elem }) = current { - match elem { - ProjectionElem::Field(field, _) if { - is_union(base) && field != target_field && base == target_base - } => { - let desc_base = self.describe_place(base) - .unwrap_or_else(|| "_".to_owned()); - let desc_first = self.describe_place(first_borrowed_place) - .unwrap_or_else(|| "_".to_owned()); - let desc_second = self.describe_place(second_borrowed_place) - .unwrap_or_else(|| "_".to_owned()); - - // Also compute the name of the union type, eg. `Foo` so we - // can add a helpful note with it. - let ty = base.ty(self.mir, self.infcx.tcx).ty; - - return Some((desc_base, desc_first, desc_second, ty.to_string())); - }, - _ => current = base, - } - } - None - }) - .unwrap_or_else(|| { - // If we didn't find a field access into a union, or both places match, then - // only return the description of the first place. - let desc_place = self.describe_place(first_borrowed_place) - .unwrap_or_else(|| "_".to_owned()); - (desc_place, "".to_string(), "".to_string(), "".to_string()) - }) - } - - /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`. - /// - /// This means that some data referenced by `borrow` needs to live - /// past the point where the StorageDeadOrDrop of `place` occurs. - /// This is usually interpreted as meaning that `place` has too - /// short a lifetime. (But sometimes it is more useful to report - /// it as a more direct conflict between the execution of a - /// `Drop::drop` with an aliasing borrow.) - pub(super) fn report_borrowed_value_does_not_live_long_enough( - &mut self, - location: Location, - borrow: &BorrowData<'tcx>, - place_span: (&Place<'tcx>, Span), - kind: Option, - ) { - debug!( - "report_borrowed_value_does_not_live_long_enough(\ - {:?}, {:?}, {:?}, {:?}\ - )", - location, borrow, place_span, kind - ); - - let drop_span = place_span.1; - let scope_tree = self.infcx.tcx.region_scope_tree(self.mir_def_id); - let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All) - .last() - .unwrap(); - - let borrow_spans = self.retrieve_borrow_spans(borrow); - let borrow_span = borrow_spans.var_or_use(); - - let proper_span = match *root_place { - Place::Base(PlaceBase::Local(local)) => self.mir.local_decls[local].source_info.span, - _ => drop_span, - }; - - if self.access_place_error_reported - .contains(&(root_place.clone(), borrow_span)) - { - debug!( - "suppressing access_place error when borrow doesn't live long enough for {:?}", - borrow_span - ); - return; - } - - self.access_place_error_reported - .insert((root_place.clone(), borrow_span)); - - if let StorageDeadOrDrop::Destructor(dropped_ty) = - self.classify_drop_access_kind(&borrow.borrowed_place) - { - // 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( - location, borrow, place_span, kind, dropped_ty, - ); - return; - } - } - - let place_desc = self.describe_place(&borrow.borrowed_place); - - let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0)); - let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place); - - let err = match (place_desc, explanation) { - (Some(_), _) if self.is_place_thread_local(root_place) => { - self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span) - } - // If the outlives constraint comes from inside the closure, - // for example: - // - // let x = 0; - // let y = &x; - // Box::new(|| y) as Box &'static i32> - // - // then just use the normal error. The closure isn't escaping - // and `move` will not help here. - ( - Some(ref name), - BorrowExplanation::MustBeValidFor { - category: category @ ConstraintCategory::Return, - from_closure: false, - ref region_name, - span, - .. - }, - ) - | ( - Some(ref name), - BorrowExplanation::MustBeValidFor { - category: category @ ConstraintCategory::CallArgument, - from_closure: false, - ref region_name, - span, - .. - }, - ) if borrow_spans.for_closure() => self.report_escaping_closure_capture( - borrow_spans.args_or_use(), - borrow_span, - region_name, - category, - span, - &format!("`{}`", name), - ), - ( - ref name, - BorrowExplanation::MustBeValidFor { - category: ConstraintCategory::Assignment, - from_closure: false, - region_name: RegionName { - source: RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name), - .. - }, - span, - .. - }, - ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span), - (Some(name), explanation) => self.report_local_value_does_not_live_long_enough( - location, - &name, - &scope_tree, - &borrow, - drop_span, - borrow_spans, - explanation, - ), - (None, explanation) => self.report_temporary_value_does_not_live_long_enough( - location, - &scope_tree, - &borrow, - drop_span, - borrow_spans, - proper_span, - explanation, - ), - }; - - err.buffer(&mut self.errors_buffer); - } - - fn report_local_value_does_not_live_long_enough( - &mut self, - location: Location, - name: &str, - scope_tree: &'tcx ScopeTree, - borrow: &BorrowData<'tcx>, - drop_span: Span, - borrow_spans: UseSpans, - explanation: BorrowExplanation, - ) -> DiagnosticBuilder<'cx> { - debug!( - "report_local_value_does_not_live_long_enough(\ - {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\ - )", - location, name, scope_tree, borrow, drop_span, borrow_spans - ); - - let borrow_span = borrow_spans.var_or_use(); - if let BorrowExplanation::MustBeValidFor { - category, - span, - ref opt_place_desc, - from_closure: false, - .. - } = explanation { - if let Some(diag) = self.try_report_cannot_return_reference_to_local( - borrow, - borrow_span, - span, - category, - opt_place_desc.as_ref(), - ) { - return diag; - } - } - - let mut err = self.infcx.tcx.path_does_not_live_long_enough( - borrow_span, - &format!("`{}`", name), - Origin::Mir, - ); - - if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { - let region_name = annotation.emit(self, &mut err); - - err.span_label( - borrow_span, - format!("`{}` would have to be valid for `{}`...", name, region_name), - ); - - if let Some(fn_hir_id) = self.infcx.tcx.hir().as_local_hir_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_by_hir_id(fn_hir_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.add_explanation_to_diagnostic( - self.infcx.tcx, - self.mir, - &mut err, - "", - None, - ); - } - } 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), - ); - - let within = if borrow_spans.for_generator() { - " by generator" - } else { - "" - }; - - borrow_spans.args_span_label( - &mut err, - format!("value captured here{}", within), - ); - - explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); - } - - err - } - - fn report_borrow_conflicts_with_destructor( - &mut self, - location: Location, - borrow: &BorrowData<'tcx>, - (place, drop_span): (&Place<'tcx>, Span), - kind: Option, - dropped_ty: Ty<'tcx>, - ) { - debug!( - "report_borrow_conflicts_with_destructor(\ - {:?}, {:?}, ({:?}, {:?}), {:?}\ - )", - location, 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.infcx - .tcx - .cannot_borrow_across_destructor(borrow_span, Origin::Mir); - - let what_was_dropped = match self.describe_place(place) { - Some(name) => format!("`{}`", name.as_str()), - None => String::from("temporary value"), - }; - - let label = match self.describe_place(&borrow.borrowed_place) { - Some(borrowed) => format!( - "here, drop of {D} needs exclusive access to `{B}`, \ - because the type `{T}` implements the `Drop` trait", - D = what_was_dropped, - T = dropped_ty, - B = borrowed - ), - None => format!( - "here is drop of {D}; whose type `{T}` implements the `Drop` trait", - D = what_was_dropped, - T = dropped_ty - ), - }; - err.span_label(drop_span, label); - - // Only give this note and suggestion if they could be relevant. - let explanation = - self.explain_why_borrow_contains_point(location, 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"); - } - _ => {} - } - - explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); - - err.buffer(&mut self.errors_buffer); - } - - fn report_thread_local_value_does_not_live_long_enough( - &mut self, - drop_span: Span, - borrow_span: Span, - ) -> DiagnosticBuilder<'cx> { - debug!( - "report_thread_local_value_does_not_live_long_enough(\ - {:?}, {:?}\ - )", - drop_span, borrow_span - ); - - 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 - } - - fn report_temporary_value_does_not_live_long_enough( - &mut self, - location: Location, - scope_tree: &'tcx ScopeTree, - borrow: &BorrowData<'tcx>, - drop_span: Span, - borrow_spans: UseSpans, - proper_span: Span, - explanation: BorrowExplanation, - ) -> DiagnosticBuilder<'cx> { - debug!( - "report_temporary_value_does_not_live_long_enough(\ - {:?}, {:?}, {:?}, {:?}, {:?}\ - )", - location, scope_tree, borrow, drop_span, proper_span - ); - - if let BorrowExplanation::MustBeValidFor { - category, - span, - from_closure: false, - .. - } = explanation { - if let Some(diag) = self.try_report_cannot_return_reference_to_local( - borrow, - proper_span, - span, - category, - None, - ) { - return diag; - } - } - - let tcx = self.infcx.tcx; - let mut err = tcx.temporary_value_borrowed_for_too_long(proper_span, Origin::Mir); - err.span_label( - proper_span, - "creates a temporary which is freed while still in use", - ); - err.span_label( - drop_span, - "temporary value is freed at the end of this statement", - ); - - 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"); - } - _ => {} - } - explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); - - let within = if borrow_spans.for_generator() { - " by generator" - } else { - "" - }; - - borrow_spans.args_span_label( - &mut err, - format!("value captured here{}", within), - ); - - err - } - - fn try_report_cannot_return_reference_to_local( - &self, - borrow: &BorrowData<'tcx>, - borrow_span: Span, - return_span: Span, - category: ConstraintCategory, - opt_place_desc: Option<&String>, - ) -> Option> { - let tcx = self.infcx.tcx; - - let return_kind = match category { - ConstraintCategory::Return => "return", - ConstraintCategory::Yield => "yield", - _ => return None, - }; - - // FIXME use a better heuristic than Spans - let reference_desc = if return_span == self.mir.source_info(borrow.reserve_location).span { - "reference to" - } else { - "value referencing" - }; - - let (place_desc, note) = if let Some(place_desc) = opt_place_desc { - let local_kind = match borrow.borrowed_place { - Place::Base(PlaceBase::Local(local)) => { - match self.mir.local_kind(local) { - LocalKind::ReturnPointer - | LocalKind::Temp => bug!("temporary or return pointer with a name"), - LocalKind::Var => "local variable ", - LocalKind::Arg - if !self.upvars.is_empty() - && local == Local::new(1) => { - "variable captured by `move` " - } - LocalKind::Arg => { - "function parameter " - } - } - } - _ => "local data ", - }; - ( - format!("{}`{}`", local_kind, place_desc), - format!("`{}` is borrowed here", place_desc), - ) - } else { - let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All) - .last() - .unwrap(); - let local = if let Place::Base(PlaceBase::Local(local)) = *root_place { - local - } else { - bug!("try_report_cannot_return_reference_to_local: not a local") - }; - match self.mir.local_kind(local) { - LocalKind::ReturnPointer | LocalKind::Temp => { - ( - "temporary value".to_string(), - "temporary value created here".to_string(), - ) - } - LocalKind::Arg => { - ( - "function parameter".to_string(), - "function parameter borrowed here".to_string(), - ) - }, - LocalKind::Var => bug!("local variable without a name"), - } - }; - - let mut err = tcx.cannot_return_reference_to_local( - return_span, - return_kind, - reference_desc, - &place_desc, - Origin::Mir, - ); - - if return_span != borrow_span { - err.span_label(borrow_span, note); - } - - Some(err) - } - - fn report_escaping_closure_capture( - &mut self, - args_span: Span, - var_span: Span, - fr_name: &RegionName, - category: ConstraintCategory, - constraint_span: Span, - captured_var: &str, - ) -> DiagnosticBuilder<'cx> { - let tcx = self.infcx.tcx; - - let mut err = tcx.cannot_capture_in_long_lived_closure( - args_span, - captured_var, - var_span, - Origin::Mir, - ); - - let suggestion = match tcx.sess.source_map().span_to_snippet(args_span) { - Ok(string) => format!("move {}", string), - Err(_) => "move || ".to_string() - }; - - err.span_suggestion( - args_span, - &format!("to force the closure to take ownership of {} (and any \ - other referenced variables), use the `move` keyword", - captured_var), - suggestion, - Applicability::MachineApplicable, - ); - - match category { - ConstraintCategory::Return => { - err.span_note(constraint_span, "closure is returned here"); - } - ConstraintCategory::CallArgument => { - fr_name.highlight_region_name(&mut err); - err.span_note( - constraint_span, - &format!("function requires argument type to outlive `{}`", fr_name), - ); - } - _ => bug!("report_escaping_closure_capture called with unexpected constraint \ - category: `{:?}`", category), - } - err - } - - fn report_escaping_data( - &mut self, - borrow_span: Span, - name: &Option, - upvar_span: Span, - upvar_name: &str, - escape_span: Span, - ) -> DiagnosticBuilder<'cx> { - let tcx = self.infcx.tcx; - - let escapes_from = if tcx.is_closure(self.mir_def_id) { - let tables = tcx.typeck_tables_of(self.mir_def_id); - let mir_hir_id = tcx.hir().def_index_to_hir_id(self.mir_def_id.index); - match tables.node_type(mir_hir_id).sty { - ty::Closure(..) => "closure", - ty::Generator(..) => "generator", - _ => bug!("Closure body doesn't have a closure or generator type"), - } - } else { - "function" - }; - - let mut err = tcx.borrowed_data_escapes_closure(escape_span, escapes_from, Origin::Mir); - - err.span_label( - upvar_span, - format!( - "`{}` is declared here, outside of the {} body", - upvar_name, escapes_from - ), - ); - - err.span_label( - borrow_span, - format!( - "borrow is only valid in the {} body", - escapes_from - ), - ); - - if let Some(name) = name { - err.span_label( - escape_span, - format!("reference to `{}` escapes the {} body here", name, escapes_from), - ); - } else { - err.span_label( - escape_span, - format!("reference escapes the {} body here", escapes_from), - ); - } - - err - } - - fn get_moved_indexes(&mut self, location: Location, mpi: MovePathIndex) -> Vec { - let mir = self.mir; - - let mut stack = Vec::new(); - stack.extend(mir.predecessor_locations(location).map(|predecessor| { - let is_back_edge = location.dominates(predecessor, &self.dominators); - (predecessor, is_back_edge) - })); - - let mut visited = FxHashSet::default(); - let mut result = vec![]; - - 'dfs: while let Some((location, is_back_edge)) = stack.pop() { - debug!( - "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})", - location, is_back_edge - ); - - if !visited.insert(location) { - continue; - } - - // check for moves - let stmt_kind = mir[location.block] - .statements - .get(location.statement_index) - .map(|s| &s.kind); - if let Some(StatementKind::StorageDead(..)) = stmt_kind { - // this analysis only tries to find moves explicitly - // written by the user, so we ignore the move-outs - // created by `StorageDead` and at the beginning - // of a function. - } else { - // If we are found a use of a.b.c which was in error, then we want to look for - // moves not only of a.b.c but also a.b and a. - // - // Note that the moves data already includes "parent" paths, so we don't have to - // worry about the other case: that is, if there is a move of a.b.c, it is already - // marked as a move of a.b and a as well, so we will generate the correct errors - // there. - let mut mpis = vec![mpi]; - let move_paths = &self.move_data.move_paths; - mpis.extend(move_paths[mpi].parents(move_paths)); - - for moi in &self.move_data.loc_map[location] { - debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi); - if mpis.contains(&self.move_data.moves[*moi].path) { - debug!("report_use_of_moved_or_uninitialized: found"); - result.push(MoveSite { - moi: *moi, - traversed_back_edge: is_back_edge, - }); - - // Strictly speaking, we could continue our DFS here. There may be - // other moves that can reach the point of error. But it is kind of - // confusing to highlight them. - // - // Example: - // - // ``` - // let a = vec![]; - // let b = a; - // let c = a; - // drop(a); // <-- current point of error - // ``` - // - // Because we stop the DFS here, we only highlight `let c = a`, - // and not `let b = a`. We will of course also report an error at - // `let c = a` which highlights `let b = a` as the move. - continue 'dfs; - } - } - } - - // check for inits - let mut any_match = false; - drop_flag_effects::for_location_inits( - self.infcx.tcx, - self.mir, - self.move_data, - location, - |m| { - if m == mpi { - any_match = true; - } - }, - ); - if any_match { - continue 'dfs; - } - - stack.extend(mir.predecessor_locations(location).map(|predecessor| { - let back_edge = location.dominates(predecessor, &self.dominators); - (predecessor, is_back_edge || back_edge) - })); - } - - result - } - - pub(super) fn report_illegal_mutation_of_borrowed( - &mut self, - location: Location, - (place, span): (&Place<'tcx>, Span), - loan: &BorrowData<'tcx>, - ) { - let loan_spans = self.retrieve_borrow_spans(loan); - let loan_span = loan_spans.args_or_use(); - - let tcx = self.infcx.tcx; - if loan.kind == BorrowKind::Shallow { - let mut err = tcx.cannot_mutate_in_match_guard( - span, - loan_span, - &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), - "assign", - Origin::Mir, - ); - loan_spans.var_span_label( - &mut err, - format!("borrow occurs due to use{}", loan_spans.describe()), - ); - - err.buffer(&mut self.errors_buffer); - - return; - } - - let mut err = tcx.cannot_assign_to_borrowed( - span, - loan_span, - &self.describe_place(place).unwrap_or_else(|| "_".to_owned()), - Origin::Mir, - ); - - loan_spans.var_span_label( - &mut err, - format!("borrow occurs due to use{}", loan_spans.describe()), - ); - - self.explain_why_borrow_contains_point(location, loan, None) - .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None); - - err.buffer(&mut self.errors_buffer); - } - - /// Reports an illegal reassignment; for example, an assignment to - /// (part of) a non-`mut` local that occurs potentially after that - /// local has already been initialized. `place` is the path being - /// assigned; `err_place` is a place providing a reason why - /// `place` is not mutable (e.g., the non-`mut` local `x` in an - /// assignment to `x.f`). - pub(super) fn report_illegal_reassignment( - &mut self, - _location: Location, - (place, span): (&Place<'tcx>, Span), - assigned_span: Span, - err_place: &Place<'tcx>, - ) { - let (from_arg, local_decl) = if let Place::Base(PlaceBase::Local(local)) = *err_place { - if let LocalKind::Arg = self.mir.local_kind(local) { - (true, Some(&self.mir.local_decls[local])) - } else { - (false, Some(&self.mir.local_decls[local])) - } - } else { - (false, None) - }; - - // If root local is initialized immediately (everything apart from let - // PATTERN;) then make the error refer to that local, rather than the - // place being assigned later. - let (place_description, assigned_span) = match local_decl { - Some(LocalDecl { - is_user_variable: Some(ClearCrossCrate::Clear), - .. - }) - | Some(LocalDecl { - is_user_variable: - Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - opt_match_place: None, - .. - }))), - .. - }) - | Some(LocalDecl { - is_user_variable: None, - .. - }) - | None => (self.describe_place(place), assigned_span), - Some(decl) => (self.describe_place(err_place), decl.source_info.span), - }; - - let mut err = self.infcx.tcx.cannot_reassign_immutable( - span, - place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"), - from_arg, - Origin::Mir, - ); - let msg = if from_arg { - "cannot assign to immutable argument" - } else { - "cannot assign twice to immutable variable" - }; - if span != assigned_span { - if !from_arg { - let value_msg = match place_description { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - err.span_label(assigned_span, format!("first assignment to {}", value_msg)); - } - } - if let Some(decl) = local_decl { - if let Some(name) = decl.name { - if decl.can_be_made_mutable() { - err.span_suggestion( - decl.source_info.span, - "make this binding mutable", - format!("mut {}", name), - Applicability::MachineApplicable, - ); - } - } - } - err.span_label(span, msg); - err.buffer(&mut self.errors_buffer); - } -} - -pub(super) struct IncludingDowncast(bool); - -/// Which case a StorageDeadOrDrop is for. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum StorageDeadOrDrop<'tcx> { - LocalStorageDead, - BoxedStorageDead, - Destructor(Ty<'tcx>), -} +pub(super) struct IncludingDowncast(pub(super) bool); impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { @@ -1866,507 +383,12 @@ pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool { false } } - - fn classify_drop_access_kind(&self, place: &Place<'tcx>) -> StorageDeadOrDrop<'tcx> { - let tcx = self.infcx.tcx; - match place { - Place::Base(PlaceBase::Local(_)) | - Place::Base(PlaceBase::Static(_)) => { - StorageDeadOrDrop::LocalStorageDead - } - Place::Projection(box PlaceProjection { base, elem }) => { - let base_access = self.classify_drop_access_kind(base); - match elem { - ProjectionElem::Deref => match base_access { - StorageDeadOrDrop::LocalStorageDead - | StorageDeadOrDrop::BoxedStorageDead => { - assert!( - base.ty(self.mir, tcx).ty.is_box(), - "Drop of value behind a reference or raw pointer" - ); - StorageDeadOrDrop::BoxedStorageDead - } - StorageDeadOrDrop::Destructor(_) => base_access, - }, - ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { - let base_ty = base.ty(self.mir, tcx).ty; - match base_ty.sty { - ty::Adt(def, _) if def.has_dtor(tcx) => { - // Report the outermost adt with a destructor - match base_access { - StorageDeadOrDrop::Destructor(_) => base_access, - StorageDeadOrDrop::LocalStorageDead - | StorageDeadOrDrop::BoxedStorageDead => { - StorageDeadOrDrop::Destructor(base_ty) - } - } - } - _ => base_access, - } - } - - ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::Index(_) => base_access, - } - } - } - } - - /// 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::FnDef(_, _) | ty::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 - ); - if let Some(&Statement { kind: StatementKind::Assign(ref reservation, _), ..}) - = &self.mir[location.block].statements.get(location.statement_index) - { - 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::Base(PlaceBase::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::Base(PlaceBase::Local(assigned_to)), - box 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::Base(PlaceBase::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_hir_id = self.infcx.tcx.hir().as_local_hir_id(did)?; - let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_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::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::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. - if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].node { - // With access to the lifetime, we can get - // the span of it. - arguments.push((*argument, lifetime.span)); - } else { - 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::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::Tuple(elems) = argument_ty.sty { - let argument_ty = elems.first()?.expect_ty(); - if let ty::Ref(_, _, _) = argument_ty.sty { - return Some(AnnotatedBorrowFnSignature::Closure { - argument_ty, - argument_span, - }); - } - } - - None - } - ty::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::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<'tcx>, Span)>, - return_ty: Ty<'tcx>, - return_span: Span, - }, - AnonymousFunction { - argument_ty: Ty<'tcx>, - argument_span: Span, - return_ty: Ty<'tcx>, - return_span: Span, - }, - Closure { - argument_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, - cx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, - diag: &mut DiagnosticBuilder<'_>, - ) -> String { - match self { - AnnotatedBorrowFnSignature::Closure { - argument_ty, - argument_span, - } => { - diag.span_label( - *argument_span, - format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)), - ); - - cx.get_region_name_for_ty(argument_ty, 0) - } - AnnotatedBorrowFnSignature::AnonymousFunction { - argument_ty, - argument_span, - return_ty, - return_span, - } => { - let argument_ty_name = cx.get_name_for_ty(argument_ty, 0); - diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name)); - - let return_ty_name = cx.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 ", - ); - - cx.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 = cx.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 - } - } - } } impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// 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<'tcx>, counter: usize) -> String { + pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { let mut s = String::new(); let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); @@ -2389,7 +411,7 @@ fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { /// Returns 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<'tcx>, counter: usize) -> String { + pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String { let mut s = String::new(); let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS); @@ -2469,7 +491,7 @@ pub(super) fn var_span_label( } /// Returns `false` if this place is not used in a closure. - fn for_closure(&self) -> bool { + pub(super) fn for_closure(&self) -> bool { match *self { UseSpans::ClosureUse { is_generator, .. } => !is_generator, _ => false, @@ -2477,7 +499,7 @@ fn for_closure(&self) -> bool { } /// Returns `false` if this place is not used in a generator. - fn for_generator(&self) -> bool { + pub(super) fn for_generator(&self) -> bool { match *self { UseSpans::ClosureUse { is_generator, .. } => is_generator, _ => false, @@ -2485,7 +507,7 @@ fn for_generator(&self) -> bool { } /// Describe the span associated with a use of a place. - fn describe(&self) -> String { + pub(super) fn describe(&self) -> String { match *self { UseSpans::ClosureUse { is_generator, .. } => if is_generator { " in generator".to_string() diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index fc1f5eb5d5a..eb8e5fa5e25 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -54,6 +54,7 @@ mod error_reporting; mod flows; mod location; +mod conflict_errors; mod move_errors; mod mutability_errors; mod path_utils; -- 2.44.0