"rustc_errors",
"rustc_feature",
"rustc_lexer",
- "rustc_target",
"smallvec 1.0.0",
"syntax",
"syntax_pos",
/// Returns a raw pointer to the object `T` pointed to by this `Weak<T>`.
///
- /// It is up to the caller to ensure that the object is still alive when accessing it through
- /// the pointer.
- ///
- /// The pointer may be [`null`] or be dangling in case the object has already been destroyed.
+ /// The pointer is valid only if there are some strong references. The pointer may be dangling
+ /// or even [`null`] otherwise.
///
/// # Examples
///
/// This can be used to safely get a strong reference (by calling [`upgrade`]
/// later) or to deallocate the weak count by dropping the `Weak<T>`.
///
- /// It takes ownership of one weak count. In case a [`null`] is passed, a dangling [`Weak`] is
- /// returned.
+ /// It takes ownership of one weak count (with the exception of pointers created by [`new`],
+ /// as these don't have any corresponding weak count).
///
/// # Safety
///
- /// The pointer must represent one valid weak count. In other words, it must point to `T` which
- /// is or *was* managed by an [`Rc`] and the weak count of that [`Rc`] must not have reached
- /// 0. It is allowed for the strong count to be 0.
+ /// The pointer must have originated from the [`into_raw`] (or [`as_raw`], provided there was
+ /// a corresponding [`forget`] on the `Weak<T>`) and must still own its potential weak reference
+ /// count.
+ ///
+ /// It is allowed for the strong count to be 0 at the time of calling this, but the weak count
+ /// must be non-zero or the pointer must have originated from a dangling `Weak<T>` (one created
+ /// by [`new`]).
///
/// # Examples
///
/// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none());
/// ```
///
- /// [`null`]: ../../std/ptr/fn.null.html
/// [`into_raw`]: struct.Weak.html#method.into_raw
/// [`upgrade`]: struct.Weak.html#method.upgrade
/// [`Rc`]: struct.Rc.html
/// [`Weak`]: struct.Weak.html
+ /// [`as_raw`]: struct.Weak.html#method.as_raw
+ /// [`new`]: struct.Weak.html#method.new
+ /// [`forget`]: ../../std/mem/fn.forget.html
#[unstable(feature = "weak_into_raw", issue = "60728")]
pub unsafe fn from_raw(ptr: *const T) -> Self {
if ptr.is_null() {
/// Returns a raw pointer to the object `T` pointed to by this `Weak<T>`.
///
- /// It is up to the caller to ensure that the object is still alive when accessing it through
- /// the pointer.
- ///
- /// The pointer may be [`null`] or be dangling in case the object has already been destroyed.
+ /// The pointer is valid only if there are some strong references. The pointer may be dangling
+ /// or even [`null`] otherwise.
///
/// # Examples
///
/// This can be used to safely get a strong reference (by calling [`upgrade`]
/// later) or to deallocate the weak count by dropping the `Weak<T>`.
///
- /// It takes ownership of one weak count. In case a [`null`] is passed, a dangling [`Weak`] is
- /// returned.
+ /// It takes ownership of one weak count (with the exception of pointers created by [`new`],
+ /// as these don't have any corresponding weak count).
///
/// # Safety
///
- /// The pointer must represent one valid weak count. In other words, it must point to `T` which
- /// is or *was* managed by an [`Arc`] and the weak count of that [`Arc`] must not have reached
- /// 0. It is allowed for the strong count to be 0.
+ /// The pointer must have originated from the [`into_raw`] (or [`as_raw'], provided there was
+ /// a corresponding [`forget`] on the `Weak<T>`) and must still own its potential weak reference
+ /// count.
+ ///
+ /// It is allowed for the strong count to be 0 at the time of calling this, but the weak count
+ /// must be non-zero or the pointer must have originated from a dangling `Weak<T>` (one created
+ /// by [`new`]).
///
/// # Examples
///
/// assert!(unsafe { Weak::from_raw(raw_2) }.upgrade().is_none());
/// ```
///
- /// [`null`]: ../../std/ptr/fn.null.html
+ /// [`as_raw`]: struct.Weak.html#method.as_raw
+ /// [`new`]: struct.Weak.html#method.new
/// [`into_raw`]: struct.Weak.html#method.into_raw
/// [`upgrade`]: struct.Weak.html#method.upgrade
/// [`Weak`]: struct.Weak.html
/// [`Arc`]: struct.Arc.html
+ /// [`forget`]: ../../std/mem/fn.forget.html
#[unstable(feature = "weak_into_raw", issue = "60728")]
pub unsafe fn from_raw(ptr: *const T) -> Self {
if ptr.is_null() {
/// An iterator over the disjoint matches of a pattern within the given string
/// slice.
///
- /// The pattern can be any type that implements the Pattern trait. Notable
- /// examples are `&str`, [`char`], and closures that determines the split.
+ /// The pattern can be a `&str`, [`char`], or a closure that determines if
+ /// a character matches.
///
/// # Iterator behavior
///
//!
//! Atomic variables are safe to share between threads (they implement [`Sync`])
//! but they do not themselves provide the mechanism for sharing and follow the
-//! [threading model](../../../std/thread/index.html#the-threading-model) of rust.
+//! [threading model](../../../std/thread/index.html#the-threading-model) of Rust.
//! The most common way to share an atomic variable is to put it into an [`Arc`][arc] (an
//! atomically-reference-counted shared pointer).
//!
sub_region,
"...",
);
- err.note(&format!(
- "...so that the {}:\nexpected {}\n found {}",
- sup_trace.cause.as_requirement_str(),
- sup_expected.content(),
- sup_found.content()
+ err.span_note(sup_trace.cause.span, &format!(
+ "...so that the {}",
+ sup_trace.cause.as_requirement_str()
));
+
+ err.note_expected_found(
+ &"",
+ sup_expected,
+ &"",
+ sup_found
+ );
err.emit();
return;
}
match *origin {
infer::Subtype(ref trace) => {
if let Some((expected, found)) = self.values_str(&trace.values) {
- let expected = expected.content();
- let found = found.content();
- err.note(&format!("...so that the {}:\nexpected {}\n found {}",
- trace.cause.as_requirement_str(),
- expected,
- found));
+ err.span_note(
+ trace.cause.span,
+ &format!(
+ "...so that the {}",
+ trace.cause.as_requirement_str()
+ )
+ );
+
+ err.note_expected_found(
+ &"",
+ expected,
+ &"",
+ found
+ );
} else {
// FIXME: this really should be handled at some earlier stage. Our
// handling of region checking when type errors are present is
if !self.empty_path {
write!(self, "::")?;
}
+ if ast::Ident::from_str(&name).is_raw_guess() {
+ write!(self, "r#")?;
+ }
write!(self, "{}", name)?;
// FIXME(eddyb) this will print e.g. `{{closure}}#3`, but it
cursor.position() as size_t
}
- with_codegen(tm, llmod, config.no_builtins, |cpm| {
- let result =
- llvm::LLVMRustPrintModule(cpm, llmod, out_c.as_ptr(), demangle_callback);
- llvm::LLVMDisposePassManager(cpm);
- result.into_result().map_err(|()| {
- let msg = format!("failed to write LLVM IR to {}", out.display());
- llvm_err(diag_handler, &msg)
- })
+ let result =
+ llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback);
+ result.into_result().map_err(|()| {
+ let msg = format!("failed to write LLVM IR to {}", out.display());
+ llvm_err(diag_handler, &msg)
})?;
}
Output: *const c_char,
FileType: FileType)
-> LLVMRustResult;
- pub fn LLVMRustPrintModule(PM: &PassManager<'a>,
- M: &'a Module,
+ pub fn LLVMRustPrintModule(M: &'a Module,
Output: *const c_char,
Demangle: extern fn(*const c_char,
size_t,
+++ /dev/null
-use rustc::hir;
-use rustc::hir::def_id::DefId;
-use rustc::hir::{AsyncGeneratorKind, GeneratorKind};
-use rustc::mir::{
- self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
- FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceBase,
- PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
-};
-use rustc::ty::{self, Ty};
-use rustc::traits::error_reporting::suggest_constraining_type_param;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_index::vec::Idx;
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use syntax_pos::Span;
-use syntax::source_map::DesugaringKind;
-
-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;
-
-#[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, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
- pub(super) fn report_use_of_moved_or_uninitialized(
- &mut self,
- location: Location,
- desired_action: InitializationRequiringAction,
- (moved_place, used_place, span): (PlaceRef<'cx, 'tcx>, PlaceRef<'cx, '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.insert(root_place) {
- debug!(
- "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
- root_place
- );
- return;
- }
-
- 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.cannot_act_on_uninitialized_variable(
- span,
- desired_action.as_noun(),
- &self.describe_place_with_options(moved_place, IncludingDowncast(true))
- .unwrap_or_else(|| "_".to_owned()),
- );
- 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.cannot_act_on_moved_value(
- span,
- desired_action.as_noun(),
- msg,
- self.describe_place_with_options(moved_place, IncludingDowncast(true)),
- );
-
- 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.as_ref()
- && used_place.is_prefix_of(moved_place.as_ref())
- });
- 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.as_ref(), 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),
- );
- 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()),
- );
- }
- if Some(DesugaringKind::ForLoop) == move_span.desugaring_kind() {
- let sess = self.infcx.tcx.sess;
- if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) {
- err.span_suggestion(
- move_span,
- "consider borrowing to avoid moving into the for loop",
- format!("&{}", snippet),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
-
- 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 = Place::ty_from(
- used_place.base,
- used_place.projection,
- *self.body,
- self.infcx.tcx
- ).ty;
- let needs_note = match ty.kind {
- 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.body, self.infcx.tcx).ty;
- let opt_name =
- self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
- let note_msg = match opt_name {
- Some(ref name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
- if let ty::Param(param_ty) = ty.kind {
- let tcx = self.infcx.tcx;
- let generics = tcx.generics_of(self.mir_def_id);
- let param = generics.type_param(¶m_ty, tcx);
- let generics = tcx.hir().get_generics(self.mir_def_id).unwrap();
- suggest_constraining_type_param(
- generics,
- &mut err,
- ¶m.name.as_str(),
- "Copy",
- tcx.sess.source_map(),
- span,
- );
- }
- let span = if let Some(local) = place.as_local() {
- let decl = &self.body.local_decls[local];
- Some(decl.source_info.span)
- } else {
- None
- };
- self.note_type_does_not_implement_copy(
- &mut err,
- ¬e_msg,
- ty,
- span,
- );
- }
-
- if let Some((_, mut old_err)) = self.move_error_reported
- .insert(move_out_indices, (used_place, 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 value_msg = match self.describe_place(place.as_ref()) {
- Some(name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
- let borrow_msg = match self.describe_place(borrow.borrowed_place.as_ref()) {
- 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.as_ref(), location);
- let span = move_spans.args_or_use();
-
- let mut err = self.cannot_move_when_borrowed(
- span,
- &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
- );
- 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.body,
- &self.local_names,
- &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 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.as_ref(), location);
- let span = use_spans.var_or_use();
-
- let mut err = self.cannot_use_when_mutably_borrowed(
- span,
- &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
- borrow_span,
- &self.describe_place(borrow.borrowed_place.as_ref())
- .unwrap_or_else(|| "_".to_owned()),
- );
-
- borrow_spans.var_span_label(&mut err, {
- let place = &borrow.borrowed_place;
- let desc_place =
- self.describe_place(place.as_ref()).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.body,
- &self.local_names,
- &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 first_borrow_desc;
- let mut err = match (
- gen_borrow_kind,
- issued_borrow.kind,
- ) {
- (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
- first_borrow_desc = "mutable ";
- self.cannot_reborrow_already_borrowed(
- span,
- &desc_place,
- &msg_place,
- "immutable",
- issued_span,
- "it",
- "mutable",
- &msg_borrow,
- None,
- )
- }
- (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
- first_borrow_desc = "immutable ";
- self.cannot_reborrow_already_borrowed(
- span,
- &desc_place,
- &msg_place,
- "mutable",
- issued_span,
- "it",
- "immutable",
- &msg_borrow,
- None,
- )
- }
-
- (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
- first_borrow_desc = "first ";
- self.cannot_mutably_borrow_multiply(
- span,
- &desc_place,
- &msg_place,
- issued_span,
- &msg_borrow,
- None,
- )
- }
-
- (BorrowKind::Unique, BorrowKind::Unique) => {
- first_borrow_desc = "first ";
- self.cannot_uniquely_borrow_by_two_closures(
- span,
- &desc_place,
- issued_span,
- None,
- )
- }
-
- (BorrowKind::Mut { .. }, BorrowKind::Shallow)
- | (BorrowKind::Unique, BorrowKind::Shallow) => {
- if let Some(immutable_section_description) = self.classify_immutable_section(
- &issued_borrow.assigned_place,
- ) {
- let mut err = self.cannot_mutate_in_immutable_section(
- span,
- issued_span,
- &desc_place,
- immutable_section_description,
- "mutably borrow",
- );
- borrow_spans.var_span_label(
- &mut err,
- format!(
- "borrow occurs due to use of `{}`{}",
- desc_place,
- borrow_spans.describe(),
- ),
- );
-
- return err;
- } else {
- first_borrow_desc = "immutable ";
- self.cannot_reborrow_already_borrowed(
- span,
- &desc_place,
- &msg_place,
- "mutable",
- issued_span,
- "it",
- "immutable",
- &msg_borrow,
- None,
- )
- }
- }
-
- (BorrowKind::Unique, _) => {
- first_borrow_desc = "first ";
- self.cannot_uniquely_borrow_by_one_closure(
- span,
- container_name,
- &desc_place,
- "",
- issued_span,
- "it",
- "",
- None,
- )
- },
-
- (BorrowKind::Shared, BorrowKind::Unique) => {
- first_borrow_desc = "first ";
- self.cannot_reborrow_already_uniquely_borrowed(
- span,
- container_name,
- &desc_place,
- "",
- "immutable",
- issued_span,
- "",
- None,
- second_borrow_desc,
- )
- }
-
- (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
- first_borrow_desc = "first ";
- self.cannot_reborrow_already_uniquely_borrowed(
- span,
- container_name,
- &desc_place,
- "",
- "mutable",
- issued_span,
- "",
- None,
- second_borrow_desc,
- )
- }
-
- (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.as_ref())
- .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.body,
- &self.local_names,
- &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 union_ty = |place_base, place_projection| {
- let ty = Place::ty_from(
- place_base,
- place_projection,
- *self.body,
- self.infcx.tcx
- ).ty;
- ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
- };
- let describe_place = |place| self.describe_place(place).unwrap_or_else(|| "_".to_owned());
-
- // 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 Place {
- base,
- projection,
- } = first_borrowed_place;
-
- let mut cursor = projection.as_ref();
- while let [proj_base @ .., elem] = cursor {
- cursor = proj_base;
-
- match elem {
- ProjectionElem::Field(field, _) if union_ty(base, proj_base).is_some() => {
- return Some((PlaceRef {
- base: base,
- projection: proj_base,
- }, field));
- },
- _ => {},
- }
- }
- 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 Place {
- base,
- projection,
- } = second_borrowed_place;
-
- let mut cursor = projection.as_ref();
- while let [proj_base @ .., elem] = cursor {
- cursor = proj_base;
-
- if let ProjectionElem::Field(field, _) = elem {
- if let Some(union_ty) = union_ty(base, proj_base) {
- if field != target_field
- && base == target_base.base
- && proj_base == target_base.projection {
- // FIXME when we avoid clone reuse describe_place closure
- let describe_base_place = self.describe_place(PlaceRef {
- base: base,
- projection: proj_base,
- }).unwrap_or_else(|| "_".to_owned());
-
- return Some((
- describe_base_place,
- describe_place(first_borrowed_place.as_ref()),
- describe_place(second_borrowed_place.as_ref()),
- union_ty.to_string(),
- ));
- }
- }
- }
- }
- 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.
- (
- describe_place(first_borrowed_place.as_ref()),
- "".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<WriteKind>,
- ) {
- debug!(
- "report_borrowed_value_does_not_live_long_enough(\
- {:?}, {:?}, {:?}, {:?}\
- )",
- location, borrow, place_span, kind
- );
-
- let drop_span = place_span.1;
- let root_place = self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All)
- .last()
- .unwrap();
-
- let borrow_spans = self.retrieve_borrow_spans(borrow);
- let borrow_span = borrow_spans.var_or_use();
-
- assert!(root_place.projection.is_empty());
- let proper_span = match root_place.base {
- PlaceBase::Local(local) => self.body.local_decls[*local].source_info.span,
- _ => drop_span,
- };
-
- let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
-
- if self.access_place_error_reported
- .contains(&(Place {
- base: root_place.base.clone(),
- projection: root_place_projection,
- }, borrow_span))
- {
- debug!(
- "suppressing access_place error when borrow doesn't live long enough for {:?}",
- borrow_span
- );
- return;
- }
-
- self.access_place_error_reported
- .insert((Place {
- base: root_place.base.clone(),
- projection: root_place_projection,
- }, borrow_span));
-
- if let PlaceBase::Local(local) = borrow.borrowed_place.base {
- if self.body.local_decls[local].is_ref_to_thread_local() {
- let err = self.report_thread_local_value_does_not_live_long_enough(
- drop_span,
- borrow_span,
- );
- err.buffer(&mut self.errors_buffer);
- return;
- }
- };
-
- if let StorageDeadOrDrop::Destructor(dropped_ty) =
- self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
- {
- // 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.as_ref().is_prefix_of(place_span.0.as_ref()) {
- self.report_borrow_conflicts_with_destructor(
- location, borrow, place_span, kind, dropped_ty,
- );
- return;
- }
- }
-
- let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
-
- 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);
-
- debug!(
- "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
- place_desc,
- explanation
- );
- let err = match (place_desc, explanation) {
- // If the outlives constraint comes from inside the closure,
- // for example:
- //
- // let x = 0;
- // let y = &x;
- // Box::new(|| y) as Box<Fn() -> &'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,
- borrow_span,
- region_name,
- category,
- span,
- &format!("`{}`", name),
- ),
- (
- Some(ref name),
- BorrowExplanation::MustBeValidFor {
- category: category @ ConstraintCategory::OpaqueType,
- from_closure: false,
- ref region_name,
- span,
- ..
- },
-
- ) if borrow_spans.for_generator() => self.report_escaping_closure_capture(
- borrow_spans,
- 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,
- &borrow,
- drop_span,
- borrow_spans,
- explanation,
- ),
- (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
- location,
- &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,
- borrow: &BorrowData<'tcx>,
- drop_span: Span,
- borrow_spans: UseSpans,
- explanation: BorrowExplanation,
- ) -> DiagnosticBuilder<'cx> {
- debug!(
- "report_local_value_does_not_live_long_enough(\
- {:?}, {:?}, {:?}, {:?}, {:?}\
- )",
- location, name, 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.path_does_not_live_long_enough(
- borrow_span,
- &format!("`{}`", name),
- );
-
- 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(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 <https://doc.rust-lang.org/book/ch04-02-\
- references-and-borrowing.html#dangling-references>",
- );
- } 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.body,
- &self.local_names,
- &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.body, &self.local_names, &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<WriteKind>,
- 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.cannot_borrow_across_destructor(borrow_span);
-
- let what_was_dropped = match self.describe_place(place.as_ref()) {
- Some(name) => format!("`{}`", name),
- None => String::from("temporary value"),
- };
-
- let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
- 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.body,
- &self.local_names,
- &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.thread_local_value_does_not_live_long_enough(borrow_span);
-
- 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,
- 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, 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 mut err = self.temporary_value_borrowed_for_too_long(proper_span);
- 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.body,
- &self.local_names,
- &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<DiagnosticBuilder<'cx>> {
- 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.body.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 = if let Some(local) = borrow.borrowed_place.as_local() {
- match self.body.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 "
- }
- }
- } else {
- "local data "
- };
- (
- format!("{}`{}`", local_kind, place_desc),
- format!("`{}` is borrowed here", place_desc),
- )
- } else {
- let root_place = self.prefixes(borrow.borrowed_place.as_ref(),
- PrefixSet::All)
- .last()
- .unwrap();
- let local = if let PlaceRef {
- base: PlaceBase::Local(local),
- projection: [],
- } = root_place {
- local
- } else {
- bug!("try_report_cannot_return_reference_to_local: not a local")
- };
- match self.body.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 => (
- "local binding".to_string(),
- "local binding introduced here".to_string(),
- ),
- }
- };
-
- let mut err = self.cannot_return_reference_to_local(
- return_span,
- return_kind,
- reference_desc,
- &place_desc,
- );
-
- if return_span != borrow_span {
- err.span_label(borrow_span, note);
- }
-
- Some(err)
- }
-
- fn report_escaping_closure_capture(
- &mut self,
- use_span: UseSpans,
- var_span: Span,
- fr_name: &RegionName,
- category: ConstraintCategory,
- constraint_span: Span,
- captured_var: &str,
- ) -> DiagnosticBuilder<'cx> {
- let tcx = self.infcx.tcx;
- let args_span = use_span.args_or_use();
- let mut err = self.cannot_capture_in_long_lived_closure(
- args_span,
- captured_var,
- var_span,
- );
-
- let suggestion = match tcx.sess.source_map().span_to_snippet(args_span) {
- Ok(mut string) => {
- if string.starts_with("async ") {
- string.insert_str(6, "move ");
- } else if string.starts_with("async|") {
- string.insert_str(5, " move");
- } else {
- string.insert_str(0, "move ");
- };
- string
- },
- Err(_) => "move |<args>| <body>".to_string()
- };
- let kind = match use_span.generator_kind() {
- Some(generator_kind) => match generator_kind {
- GeneratorKind::Async(async_kind) => match async_kind {
- AsyncGeneratorKind::Block => "async block",
- AsyncGeneratorKind::Closure => "async closure",
- _ => bug!("async block/closure expected, but async funtion found."),
- },
- GeneratorKind::Gen => "generator",
- }
- None => "closure",
- };
- err.span_suggestion(
- args_span,
- &format!(
- "to force the {} to take ownership of {} (and any \
- other referenced variables), use the `move` keyword",
- kind,
- captured_var
- ),
- suggestion,
- Applicability::MachineApplicable,
- );
-
- let msg = match category {
- ConstraintCategory::Return => "closure is returned here".to_string(),
- ConstraintCategory::OpaqueType => "generator is returned here".to_string(),
- ConstraintCategory::CallArgument => {
- fr_name.highlight_region_name(&mut err);
- format!("function requires argument type to outlive `{}`", fr_name)
- }
- _ => bug!("report_escaping_closure_capture called with unexpected constraint \
- category: `{:?}`", category),
- };
- err.span_note(constraint_span, &msg);
- err
- }
-
- fn report_escaping_data(
- &mut self,
- borrow_span: Span,
- name: &Option<String>,
- 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).kind {
- ty::Closure(..) => "closure",
- ty::Generator(..) => "generator",
- _ => bug!("Closure body doesn't have a closure or generator type"),
- }
- } else {
- "function"
- };
-
- let mut err = borrowck_errors::borrowed_data_escapes_closure(
- tcx,
- escape_span,
- escapes_from,
- );
-
- 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<MoveSite> {
- let mut stack = Vec::new();
- stack.extend(self.body.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 = self.body[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.body,
- self.move_data,
- location,
- |m| {
- if m == mpi {
- any_match = true;
- }
- },
- );
- if any_match {
- continue 'dfs;
- }
-
- stack.extend(self.body.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();
-
- if loan.kind == BorrowKind::Shallow {
- if let Some(section) = self.classify_immutable_section(&loan.assigned_place) {
- let mut err = self.cannot_mutate_in_immutable_section(
- span,
- loan_span,
- &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
- section,
- "assign",
- );
- 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 = self.cannot_assign_to_borrowed(
- span,
- loan_span,
- &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
- );
-
- 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.body,
- &self.local_names,
- &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, local_name) = match err_place.as_local() {
- Some(local) => (
- self.body.local_kind(local) == LocalKind::Arg,
- Some(&self.body.local_decls[local]),
- self.local_names[local],
- ),
- None => (false, None, 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 {
- local_info: LocalInfo::User(ClearCrossCrate::Clear),
- ..
- })
- | Some(LocalDecl {
- local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
- opt_match_place: None,
- ..
- }))),
- ..
- })
- | Some(LocalDecl {
- local_info: LocalInfo::StaticRef { .. },
- ..
- })
- | Some(LocalDecl {
- local_info: LocalInfo::Other,
- ..
- })
- | None => (self.describe_place(place.as_ref()), assigned_span),
- Some(decl) => (self.describe_place(err_place.as_ref()), decl.source_info.span),
- };
-
- let mut err = self.cannot_reassign_immutable(
- span,
- place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
- from_arg,
- );
- 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) = local_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: PlaceRef<'cx, 'tcx>) -> StorageDeadOrDrop<'tcx> {
- let tcx = self.infcx.tcx;
- match place.projection {
- [] => {
- StorageDeadOrDrop::LocalStorageDead
- }
- [proj_base @ .., elem] => {
- // FIXME(spastorino) make this iterate
- let base_access = self.classify_drop_access_kind(PlaceRef {
- base: place.base,
- projection: proj_base,
- });
- match elem {
- ProjectionElem::Deref => match base_access {
- StorageDeadOrDrop::LocalStorageDead
- | StorageDeadOrDrop::BoxedStorageDead => {
- assert!(
- Place::ty_from(
- &place.base,
- proj_base,
- *self.body,
- 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 = Place::ty_from(
- &place.base,
- proj_base,
- *self.body,
- tcx
- ).ty;
- match base_ty.kind {
- 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,
- }
- }
- }
- }
-
- /// Describe the reason for the fake borrow that was assigned to `place`.
- fn classify_immutable_section(&self, place: &Place<'tcx>) -> Option<&'static str> {
- use rustc::mir::visit::Visitor;
- struct FakeReadCauseFinder<'a, 'tcx> {
- place: &'a Place<'tcx>,
- cause: Option<FakeReadCause>,
- }
- impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'_, 'tcx> {
- fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
- match statement {
- Statement {
- kind: StatementKind::FakeRead(cause, box ref place),
- ..
- } if *place == *self.place => {
- self.cause = Some(*cause);
- }
- _ => (),
- }
- }
- }
- let mut visitor = FakeReadCauseFinder { place, cause: None };
- visitor.visit_body(self.body);
- match visitor.cause {
- Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
- Some(FakeReadCause::ForIndex) => Some("indexing expression"),
- _ => None,
- }
- }
-
- /// Annotate argument and return type of function and closure with (synthesized) lifetime for
- /// borrow of local value that does not live long enough.
- fn annotate_argument_and_return_for_borrow(
- &self,
- borrow: &BorrowData<'tcx>,
- ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
- // 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.kind {
- 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(box(ref reservation, _)), ..})
- = &self.body[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.as_local() {
- Some(local) if self.body.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.body[location.block].statements[location.statement_index + 1..]
- {
- debug!(
- "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
- target, stmt
- );
- if let StatementKind::Assign(box(place, rvalue)) = &stmt.kind {
- if let Some(assigned_to) = place.as_local() {
- 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_or_deref_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_or_deref_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.body[location.block].terminator();
- debug!(
- "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
- target, terminator
- );
- if let TerminatorKind::Call {
- destination: Some((place, _)),
- args,
- ..
- } = &terminator.kind
- {
- if let Some(assigned_to) = place.as_local() {
- 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_or_deref_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<AnnotatedBorrowFnSignature<'tcx>> {
- 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().kind {
- 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.kind {
- 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].kind {
- // 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.kind {
- 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.kind {
- let argument_ty = elems.first()?.expect_ty();
- if let ty::Ref(_, _, _) = argument_ty.kind {
- 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.kind {
- 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 <https://doc.rust-lang.org/book/ch10-03-\
- lifetime-syntax.html#lifetime-elision>",
- );
-
- 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
- }
- }
- }
-}
--- /dev/null
+use rustc::hir;
+use rustc::hir::def_id::DefId;
+use rustc::hir::{AsyncGeneratorKind, GeneratorKind};
+use rustc::mir::{
+ self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
+ FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceBase,
+ PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
+};
+use rustc::ty::{self, Ty};
+use rustc::traits::error_reporting::suggest_constraining_type_param;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_index::vec::Idx;
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use syntax_pos::Span;
+use syntax::source_map::DesugaringKind;
+
+use crate::dataflow::drop_flag_effects;
+use crate::dataflow::indexes::{MovePathIndex, MoveOutIndex};
+use crate::util::borrowck_errors;
+
+use crate::borrow_check::{
+ prefixes::IsPrefixOf,
+ WriteKind,
+ borrow_set::BorrowData,
+ MirBorrowckCtxt, InitializationRequiringAction, PrefixSet
+};
+
+use super::{
+ IncludingDowncast, UseSpans, RegionName, RegionNameSource,
+ explain_borrow::BorrowExplanation,
+};
+
+#[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, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
+ pub(in crate::borrow_check) fn report_use_of_moved_or_uninitialized(
+ &mut self,
+ location: Location,
+ desired_action: InitializationRequiringAction,
+ (moved_place, used_place, span): (PlaceRef<'cx, 'tcx>, PlaceRef<'cx, '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.insert(root_place) {
+ debug!(
+ "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
+ root_place
+ );
+ return;
+ }
+
+ 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.cannot_act_on_uninitialized_variable(
+ span,
+ desired_action.as_noun(),
+ &self.describe_place_with_options(moved_place, IncludingDowncast(true))
+ .unwrap_or_else(|| "_".to_owned()),
+ );
+ 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.cannot_act_on_moved_value(
+ span,
+ desired_action.as_noun(),
+ msg,
+ self.describe_place_with_options(moved_place, IncludingDowncast(true)),
+ );
+
+ 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.as_ref()
+ && used_place.is_prefix_of(moved_place.as_ref())
+ });
+ 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.as_ref(), 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),
+ );
+ 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()),
+ );
+ }
+ if Some(DesugaringKind::ForLoop) == move_span.desugaring_kind() {
+ let sess = self.infcx.tcx.sess;
+ if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) {
+ err.span_suggestion(
+ move_span,
+ "consider borrowing to avoid moving into the for loop",
+ format!("&{}", snippet),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+
+ 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 = Place::ty_from(
+ used_place.base,
+ used_place.projection,
+ *self.body,
+ self.infcx.tcx
+ ).ty;
+ let needs_note = match ty.kind {
+ 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.body, self.infcx.tcx).ty;
+ let opt_name =
+ self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
+ let note_msg = match opt_name {
+ Some(ref name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+ if let ty::Param(param_ty) = ty.kind {
+ let tcx = self.infcx.tcx;
+ let generics = tcx.generics_of(self.mir_def_id);
+ let param = generics.type_param(¶m_ty, tcx);
+ let generics = tcx.hir().get_generics(self.mir_def_id).unwrap();
+ suggest_constraining_type_param(
+ generics,
+ &mut err,
+ ¶m.name.as_str(),
+ "Copy",
+ tcx.sess.source_map(),
+ span,
+ );
+ }
+ let span = if let Some(local) = place.as_local() {
+ let decl = &self.body.local_decls[local];
+ Some(decl.source_info.span)
+ } else {
+ None
+ };
+ self.note_type_does_not_implement_copy(
+ &mut err,
+ ¬e_msg,
+ ty,
+ span,
+ );
+ }
+
+ if let Some((_, mut old_err)) = self.move_error_reported
+ .insert(move_out_indices, (used_place, err))
+ {
+ // Cancel the old error so it doesn't ICE.
+ old_err.cancel();
+ }
+ }
+ }
+
+ pub(in crate::borrow_check) 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 value_msg = match self.describe_place(place.as_ref()) {
+ Some(name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+ let borrow_msg = match self.describe_place(borrow.borrowed_place.as_ref()) {
+ 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.as_ref(), location);
+ let span = move_spans.args_or_use();
+
+ let mut err = self.cannot_move_when_borrowed(
+ span,
+ &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
+ );
+ 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.body,
+ &self.local_names,
+ &mut err,
+ "",
+ Some(borrow_span),
+ );
+ err.buffer(&mut self.errors_buffer);
+ }
+
+ pub(in crate::borrow_check) fn report_use_while_mutably_borrowed(
+ &mut self,
+ location: Location,
+ (place, _span): (&Place<'tcx>, Span),
+ borrow: &BorrowData<'tcx>,
+ ) -> DiagnosticBuilder<'cx> {
+ 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.as_ref(), location);
+ let span = use_spans.var_or_use();
+
+ let mut err = self.cannot_use_when_mutably_borrowed(
+ span,
+ &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
+ borrow_span,
+ &self.describe_place(borrow.borrowed_place.as_ref())
+ .unwrap_or_else(|| "_".to_owned()),
+ );
+
+ borrow_spans.var_span_label(&mut err, {
+ let place = &borrow.borrowed_place;
+ let desc_place =
+ self.describe_place(place.as_ref()).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.body,
+ &self.local_names,
+ &mut err,
+ "",
+ None,
+ );
+ err
+ }
+
+ pub(in crate::borrow_check) 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 first_borrow_desc;
+ let mut err = match (
+ gen_borrow_kind,
+ issued_borrow.kind,
+ ) {
+ (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
+ first_borrow_desc = "mutable ";
+ self.cannot_reborrow_already_borrowed(
+ span,
+ &desc_place,
+ &msg_place,
+ "immutable",
+ issued_span,
+ "it",
+ "mutable",
+ &msg_borrow,
+ None,
+ )
+ }
+ (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
+ first_borrow_desc = "immutable ";
+ self.cannot_reborrow_already_borrowed(
+ span,
+ &desc_place,
+ &msg_place,
+ "mutable",
+ issued_span,
+ "it",
+ "immutable",
+ &msg_borrow,
+ None,
+ )
+ }
+
+ (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
+ first_borrow_desc = "first ";
+ self.cannot_mutably_borrow_multiply(
+ span,
+ &desc_place,
+ &msg_place,
+ issued_span,
+ &msg_borrow,
+ None,
+ )
+ }
+
+ (BorrowKind::Unique, BorrowKind::Unique) => {
+ first_borrow_desc = "first ";
+ self.cannot_uniquely_borrow_by_two_closures(
+ span,
+ &desc_place,
+ issued_span,
+ None,
+ )
+ }
+
+ (BorrowKind::Mut { .. }, BorrowKind::Shallow)
+ | (BorrowKind::Unique, BorrowKind::Shallow) => {
+ if let Some(immutable_section_description) = self.classify_immutable_section(
+ &issued_borrow.assigned_place,
+ ) {
+ let mut err = self.cannot_mutate_in_immutable_section(
+ span,
+ issued_span,
+ &desc_place,
+ immutable_section_description,
+ "mutably borrow",
+ );
+ borrow_spans.var_span_label(
+ &mut err,
+ format!(
+ "borrow occurs due to use of `{}`{}",
+ desc_place,
+ borrow_spans.describe(),
+ ),
+ );
+
+ return err;
+ } else {
+ first_borrow_desc = "immutable ";
+ self.cannot_reborrow_already_borrowed(
+ span,
+ &desc_place,
+ &msg_place,
+ "mutable",
+ issued_span,
+ "it",
+ "immutable",
+ &msg_borrow,
+ None,
+ )
+ }
+ }
+
+ (BorrowKind::Unique, _) => {
+ first_borrow_desc = "first ";
+ self.cannot_uniquely_borrow_by_one_closure(
+ span,
+ container_name,
+ &desc_place,
+ "",
+ issued_span,
+ "it",
+ "",
+ None,
+ )
+ },
+
+ (BorrowKind::Shared, BorrowKind::Unique) => {
+ first_borrow_desc = "first ";
+ self.cannot_reborrow_already_uniquely_borrowed(
+ span,
+ container_name,
+ &desc_place,
+ "",
+ "immutable",
+ issued_span,
+ "",
+ None,
+ second_borrow_desc,
+ )
+ }
+
+ (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
+ first_borrow_desc = "first ";
+ self.cannot_reborrow_already_uniquely_borrowed(
+ span,
+ container_name,
+ &desc_place,
+ "",
+ "mutable",
+ issued_span,
+ "",
+ None,
+ second_borrow_desc,
+ )
+ }
+
+ (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.as_ref())
+ .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.body,
+ &self.local_names,
+ &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(in crate::borrow_check) 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 union_ty = |place_base, place_projection| {
+ let ty = Place::ty_from(
+ place_base,
+ place_projection,
+ *self.body,
+ self.infcx.tcx
+ ).ty;
+ ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty)
+ };
+ let describe_place = |place| self.describe_place(place).unwrap_or_else(|| "_".to_owned());
+
+ // 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 Place {
+ base,
+ projection,
+ } = first_borrowed_place;
+
+ let mut cursor = projection.as_ref();
+ while let [proj_base @ .., elem] = cursor {
+ cursor = proj_base;
+
+ match elem {
+ ProjectionElem::Field(field, _) if union_ty(base, proj_base).is_some() => {
+ return Some((PlaceRef {
+ base: base,
+ projection: proj_base,
+ }, field));
+ },
+ _ => {},
+ }
+ }
+ 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 Place {
+ base,
+ projection,
+ } = second_borrowed_place;
+
+ let mut cursor = projection.as_ref();
+ while let [proj_base @ .., elem] = cursor {
+ cursor = proj_base;
+
+ if let ProjectionElem::Field(field, _) = elem {
+ if let Some(union_ty) = union_ty(base, proj_base) {
+ if field != target_field
+ && base == target_base.base
+ && proj_base == target_base.projection {
+ // FIXME when we avoid clone reuse describe_place closure
+ let describe_base_place = self.describe_place(PlaceRef {
+ base: base,
+ projection: proj_base,
+ }).unwrap_or_else(|| "_".to_owned());
+
+ return Some((
+ describe_base_place,
+ describe_place(first_borrowed_place.as_ref()),
+ describe_place(second_borrowed_place.as_ref()),
+ union_ty.to_string(),
+ ));
+ }
+ }
+ }
+ }
+ 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.
+ (
+ describe_place(first_borrowed_place.as_ref()),
+ "".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(in crate::borrow_check) fn report_borrowed_value_does_not_live_long_enough(
+ &mut self,
+ location: Location,
+ borrow: &BorrowData<'tcx>,
+ place_span: (&Place<'tcx>, Span),
+ kind: Option<WriteKind>,
+ ) {
+ debug!(
+ "report_borrowed_value_does_not_live_long_enough(\
+ {:?}, {:?}, {:?}, {:?}\
+ )",
+ location, borrow, place_span, kind
+ );
+
+ let drop_span = place_span.1;
+ let root_place = self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All)
+ .last()
+ .unwrap();
+
+ let borrow_spans = self.retrieve_borrow_spans(borrow);
+ let borrow_span = borrow_spans.var_or_use();
+
+ assert!(root_place.projection.is_empty());
+ let proper_span = match root_place.base {
+ PlaceBase::Local(local) => self.body.local_decls[*local].source_info.span,
+ _ => drop_span,
+ };
+
+ let root_place_projection = self.infcx.tcx.intern_place_elems(root_place.projection);
+
+ if self.access_place_error_reported
+ .contains(&(Place {
+ base: root_place.base.clone(),
+ projection: root_place_projection,
+ }, borrow_span))
+ {
+ debug!(
+ "suppressing access_place error when borrow doesn't live long enough for {:?}",
+ borrow_span
+ );
+ return;
+ }
+
+ self.access_place_error_reported
+ .insert((Place {
+ base: root_place.base.clone(),
+ projection: root_place_projection,
+ }, borrow_span));
+
+ if let PlaceBase::Local(local) = borrow.borrowed_place.base {
+ if self.body.local_decls[local].is_ref_to_thread_local() {
+ let err = self.report_thread_local_value_does_not_live_long_enough(
+ drop_span,
+ borrow_span,
+ );
+ err.buffer(&mut self.errors_buffer);
+ return;
+ }
+ };
+
+ if let StorageDeadOrDrop::Destructor(dropped_ty) =
+ self.classify_drop_access_kind(borrow.borrowed_place.as_ref())
+ {
+ // 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.as_ref().is_prefix_of(place_span.0.as_ref()) {
+ self.report_borrow_conflicts_with_destructor(
+ location, borrow, place_span, kind, dropped_ty,
+ );
+ return;
+ }
+ }
+
+ let place_desc = self.describe_place(borrow.borrowed_place.as_ref());
+
+ 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);
+
+ debug!(
+ "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
+ place_desc,
+ explanation
+ );
+ let err = match (place_desc, explanation) {
+ // If the outlives constraint comes from inside the closure,
+ // for example:
+ //
+ // let x = 0;
+ // let y = &x;
+ // Box::new(|| y) as Box<Fn() -> &'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,
+ borrow_span,
+ region_name,
+ category,
+ span,
+ &format!("`{}`", name),
+ ),
+ (
+ Some(ref name),
+ BorrowExplanation::MustBeValidFor {
+ category: category @ ConstraintCategory::OpaqueType,
+ from_closure: false,
+ ref region_name,
+ span,
+ ..
+ },
+
+ ) if borrow_spans.for_generator() => self.report_escaping_closure_capture(
+ borrow_spans,
+ 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,
+ &borrow,
+ drop_span,
+ borrow_spans,
+ explanation,
+ ),
+ (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
+ location,
+ &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,
+ borrow: &BorrowData<'tcx>,
+ drop_span: Span,
+ borrow_spans: UseSpans,
+ explanation: BorrowExplanation,
+ ) -> DiagnosticBuilder<'cx> {
+ debug!(
+ "report_local_value_does_not_live_long_enough(\
+ {:?}, {:?}, {:?}, {:?}, {:?}\
+ )",
+ location, name, 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.path_does_not_live_long_enough(
+ borrow_span,
+ &format!("`{}`", name),
+ );
+
+ 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(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 <https://doc.rust-lang.org/book/ch04-02-\
+ references-and-borrowing.html#dangling-references>",
+ );
+ } 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.body,
+ &self.local_names,
+ &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.body, &self.local_names, &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<WriteKind>,
+ 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.cannot_borrow_across_destructor(borrow_span);
+
+ let what_was_dropped = match self.describe_place(place.as_ref()) {
+ Some(name) => format!("`{}`", name),
+ None => String::from("temporary value"),
+ };
+
+ let label = match self.describe_place(borrow.borrowed_place.as_ref()) {
+ 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.body,
+ &self.local_names,
+ &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.thread_local_value_does_not_live_long_enough(borrow_span);
+
+ 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,
+ 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, 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 mut err = self.temporary_value_borrowed_for_too_long(proper_span);
+ 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.body,
+ &self.local_names,
+ &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<DiagnosticBuilder<'cx>> {
+ 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.body.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 = if let Some(local) = borrow.borrowed_place.as_local() {
+ match self.body.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 "
+ }
+ }
+ } else {
+ "local data "
+ };
+ (
+ format!("{}`{}`", local_kind, place_desc),
+ format!("`{}` is borrowed here", place_desc),
+ )
+ } else {
+ let root_place = self.prefixes(borrow.borrowed_place.as_ref(),
+ PrefixSet::All)
+ .last()
+ .unwrap();
+ let local = if let PlaceRef {
+ base: PlaceBase::Local(local),
+ projection: [],
+ } = root_place {
+ local
+ } else {
+ bug!("try_report_cannot_return_reference_to_local: not a local")
+ };
+ match self.body.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 => (
+ "local binding".to_string(),
+ "local binding introduced here".to_string(),
+ ),
+ }
+ };
+
+ let mut err = self.cannot_return_reference_to_local(
+ return_span,
+ return_kind,
+ reference_desc,
+ &place_desc,
+ );
+
+ if return_span != borrow_span {
+ err.span_label(borrow_span, note);
+ }
+
+ Some(err)
+ }
+
+ fn report_escaping_closure_capture(
+ &mut self,
+ use_span: UseSpans,
+ var_span: Span,
+ fr_name: &RegionName,
+ category: ConstraintCategory,
+ constraint_span: Span,
+ captured_var: &str,
+ ) -> DiagnosticBuilder<'cx> {
+ let tcx = self.infcx.tcx;
+ let args_span = use_span.args_or_use();
+ let mut err = self.cannot_capture_in_long_lived_closure(
+ args_span,
+ captured_var,
+ var_span,
+ );
+
+ let suggestion = match tcx.sess.source_map().span_to_snippet(args_span) {
+ Ok(mut string) => {
+ if string.starts_with("async ") {
+ string.insert_str(6, "move ");
+ } else if string.starts_with("async|") {
+ string.insert_str(5, " move");
+ } else {
+ string.insert_str(0, "move ");
+ };
+ string
+ },
+ Err(_) => "move |<args>| <body>".to_string()
+ };
+ let kind = match use_span.generator_kind() {
+ Some(generator_kind) => match generator_kind {
+ GeneratorKind::Async(async_kind) => match async_kind {
+ AsyncGeneratorKind::Block => "async block",
+ AsyncGeneratorKind::Closure => "async closure",
+ _ => bug!("async block/closure expected, but async funtion found."),
+ },
+ GeneratorKind::Gen => "generator",
+ }
+ None => "closure",
+ };
+ err.span_suggestion(
+ args_span,
+ &format!(
+ "to force the {} to take ownership of {} (and any \
+ other referenced variables), use the `move` keyword",
+ kind,
+ captured_var
+ ),
+ suggestion,
+ Applicability::MachineApplicable,
+ );
+
+ let msg = match category {
+ ConstraintCategory::Return => "closure is returned here".to_string(),
+ ConstraintCategory::OpaqueType => "generator is returned here".to_string(),
+ ConstraintCategory::CallArgument => {
+ fr_name.highlight_region_name(&mut err);
+ format!("function requires argument type to outlive `{}`", fr_name)
+ }
+ _ => bug!("report_escaping_closure_capture called with unexpected constraint \
+ category: `{:?}`", category),
+ };
+ err.span_note(constraint_span, &msg);
+ err
+ }
+
+ fn report_escaping_data(
+ &mut self,
+ borrow_span: Span,
+ name: &Option<String>,
+ 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).kind {
+ ty::Closure(..) => "closure",
+ ty::Generator(..) => "generator",
+ _ => bug!("Closure body doesn't have a closure or generator type"),
+ }
+ } else {
+ "function"
+ };
+
+ let mut err = borrowck_errors::borrowed_data_escapes_closure(
+ tcx,
+ escape_span,
+ escapes_from,
+ );
+
+ 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<MoveSite> {
+ let mut stack = Vec::new();
+ stack.extend(self.body.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 = self.body[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.body,
+ self.move_data,
+ location,
+ |m| {
+ if m == mpi {
+ any_match = true;
+ }
+ },
+ );
+ if any_match {
+ continue 'dfs;
+ }
+
+ stack.extend(self.body.predecessor_locations(location).map(|predecessor| {
+ let back_edge = location.dominates(predecessor, &self.dominators);
+ (predecessor, is_back_edge || back_edge)
+ }));
+ }
+
+ result
+ }
+
+ pub(in crate::borrow_check) 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();
+
+ if loan.kind == BorrowKind::Shallow {
+ if let Some(section) = self.classify_immutable_section(&loan.assigned_place) {
+ let mut err = self.cannot_mutate_in_immutable_section(
+ span,
+ loan_span,
+ &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
+ section,
+ "assign",
+ );
+ 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 = self.cannot_assign_to_borrowed(
+ span,
+ loan_span,
+ &self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
+ );
+
+ 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.body,
+ &self.local_names,
+ &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(in crate::borrow_check) 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, local_name) = match err_place.as_local() {
+ Some(local) => (
+ self.body.local_kind(local) == LocalKind::Arg,
+ Some(&self.body.local_decls[local]),
+ self.local_names[local],
+ ),
+ None => (false, None, 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 {
+ local_info: LocalInfo::User(ClearCrossCrate::Clear),
+ ..
+ })
+ | Some(LocalDecl {
+ local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+ opt_match_place: None,
+ ..
+ }))),
+ ..
+ })
+ | Some(LocalDecl {
+ local_info: LocalInfo::StaticRef { .. },
+ ..
+ })
+ | Some(LocalDecl {
+ local_info: LocalInfo::Other,
+ ..
+ })
+ | None => (self.describe_place(place.as_ref()), assigned_span),
+ Some(decl) => (self.describe_place(err_place.as_ref()), decl.source_info.span),
+ };
+
+ let mut err = self.cannot_reassign_immutable(
+ span,
+ place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
+ from_arg,
+ );
+ 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) = local_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: PlaceRef<'cx, 'tcx>) -> StorageDeadOrDrop<'tcx> {
+ let tcx = self.infcx.tcx;
+ match place.projection {
+ [] => {
+ StorageDeadOrDrop::LocalStorageDead
+ }
+ [proj_base @ .., elem] => {
+ // FIXME(spastorino) make this iterate
+ let base_access = self.classify_drop_access_kind(PlaceRef {
+ base: place.base,
+ projection: proj_base,
+ });
+ match elem {
+ ProjectionElem::Deref => match base_access {
+ StorageDeadOrDrop::LocalStorageDead
+ | StorageDeadOrDrop::BoxedStorageDead => {
+ assert!(
+ Place::ty_from(
+ &place.base,
+ proj_base,
+ *self.body,
+ 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 = Place::ty_from(
+ &place.base,
+ proj_base,
+ *self.body,
+ tcx
+ ).ty;
+ match base_ty.kind {
+ 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,
+ }
+ }
+ }
+ }
+
+ /// Describe the reason for the fake borrow that was assigned to `place`.
+ fn classify_immutable_section(&self, place: &Place<'tcx>) -> Option<&'static str> {
+ use rustc::mir::visit::Visitor;
+ struct FakeReadCauseFinder<'a, 'tcx> {
+ place: &'a Place<'tcx>,
+ cause: Option<FakeReadCause>,
+ }
+ impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'_, 'tcx> {
+ fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
+ match statement {
+ Statement {
+ kind: StatementKind::FakeRead(cause, box ref place),
+ ..
+ } if *place == *self.place => {
+ self.cause = Some(*cause);
+ }
+ _ => (),
+ }
+ }
+ }
+ let mut visitor = FakeReadCauseFinder { place, cause: None };
+ visitor.visit_body(self.body);
+ match visitor.cause {
+ Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
+ Some(FakeReadCause::ForIndex) => Some("indexing expression"),
+ _ => None,
+ }
+ }
+
+ /// Annotate argument and return type of function and closure with (synthesized) lifetime for
+ /// borrow of local value that does not live long enough.
+ fn annotate_argument_and_return_for_borrow(
+ &self,
+ borrow: &BorrowData<'tcx>,
+ ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
+ // 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.kind {
+ 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(box(ref reservation, _)), ..})
+ = &self.body[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.as_local() {
+ Some(local) if self.body.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.body[location.block].statements[location.statement_index + 1..]
+ {
+ debug!(
+ "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
+ target, stmt
+ );
+ if let StatementKind::Assign(box(place, rvalue)) = &stmt.kind {
+ if let Some(assigned_to) = place.as_local() {
+ 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_or_deref_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_or_deref_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.body[location.block].terminator();
+ debug!(
+ "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
+ target, terminator
+ );
+ if let TerminatorKind::Call {
+ destination: Some((place, _)),
+ args,
+ ..
+ } = &terminator.kind
+ {
+ if let Some(assigned_to) = place.as_local() {
+ 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_or_deref_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<AnnotatedBorrowFnSignature<'tcx>> {
+ 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().kind {
+ 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.kind {
+ 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].kind {
+ // 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.kind {
+ 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.kind {
+ let argument_ty = elems.first()?.expect_ty();
+ if let ty::Ref(_, _, _) = argument_ty.kind {
+ 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.kind {
+ 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(in crate::borrow_check) 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 <https://doc.rust-lang.org/book/ch10-03-\
+ lifetime-syntax.html#lifetime-elision>",
+ );
+
+ 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
+ }
+ }
+ }
+}
--- /dev/null
+use std::collections::VecDeque;
+
+use crate::borrow_check::borrow_set::BorrowData;
+use crate::borrow_check::nll::region_infer::Cause;
+use crate::borrow_check::nll::ConstraintDescription;
+use crate::borrow_check::{MirBorrowckCtxt, WriteKind};
+use rustc::mir::{
+ CastKind, ConstraintCategory, FakeReadCause, Local, Location, Body, Operand, Place, Rvalue,
+ Statement, StatementKind, TerminatorKind,
+};
+use rustc::ty::{self, TyCtxt};
+use rustc::ty::adjustment::{PointerCast};
+use rustc_index::vec::IndexVec;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::DiagnosticBuilder;
+use syntax_pos::Span;
+use syntax_pos::symbol::Symbol;
+
+use super::{UseSpans, find_use, RegionName};
+
+#[derive(Debug)]
+pub(in crate::borrow_check) enum BorrowExplanation {
+ UsedLater(LaterUseKind, Span),
+ UsedLaterInLoop(LaterUseKind, Span),
+ UsedLaterWhenDropped {
+ drop_loc: Location,
+ dropped_local: Local,
+ should_note_order: bool,
+ },
+ MustBeValidFor {
+ category: ConstraintCategory,
+ from_closure: bool,
+ span: Span,
+ region_name: RegionName,
+ opt_place_desc: Option<String>,
+ },
+ Unexplained,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub(in crate::borrow_check) enum LaterUseKind {
+ TraitCapture,
+ ClosureCapture,
+ Call,
+ FakeLetRead,
+ Other,
+}
+
+impl BorrowExplanation {
+ pub(in crate::borrow_check) fn is_explained(&self) -> bool {
+ match self {
+ BorrowExplanation::Unexplained => false,
+ _ => true,
+ }
+ }
+ pub(in crate::borrow_check) fn add_explanation_to_diagnostic<'tcx>(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ local_names: &IndexVec<Local, Option<Symbol>>,
+ err: &mut DiagnosticBuilder<'_>,
+ borrow_desc: &str,
+ borrow_span: Option<Span>,
+ ) {
+ match *self {
+ BorrowExplanation::UsedLater(later_use_kind, var_or_use_span) => {
+ let message = match later_use_kind {
+ LaterUseKind::TraitCapture => "captured here by trait object",
+ LaterUseKind::ClosureCapture => "captured here by closure",
+ LaterUseKind::Call => "used by call",
+ LaterUseKind::FakeLetRead => "stored here",
+ LaterUseKind::Other => "used here",
+ };
+ if !borrow_span.map(|sp| sp.overlaps(var_or_use_span)).unwrap_or(false) {
+ err.span_label(
+ var_or_use_span,
+ format!("{}borrow later {}", borrow_desc, message),
+ );
+ }
+ }
+ BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span) => {
+ let message = match later_use_kind {
+ LaterUseKind::TraitCapture => {
+ "borrow captured here by trait object, in later iteration of loop"
+ }
+ LaterUseKind::ClosureCapture => {
+ "borrow captured here by closure, in later iteration of loop"
+ }
+ LaterUseKind::Call => "borrow used by call, in later iteration of loop",
+ LaterUseKind::FakeLetRead => "borrow later stored here",
+ LaterUseKind::Other => "borrow used here, in later iteration of loop",
+ };
+ err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message));
+ }
+ BorrowExplanation::UsedLaterWhenDropped {
+ drop_loc,
+ dropped_local,
+ should_note_order,
+ } => {
+ let local_decl = &body.local_decls[dropped_local];
+ let (dtor_desc, type_desc) = match local_decl.ty.kind {
+ // If type is an ADT that implements Drop, then
+ // simplify output by reporting just the ADT name.
+ ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() => (
+ "`Drop` code",
+ format!("type `{}`", tcx.def_path_str(adt.did)),
+ ),
+
+ // Otherwise, just report the whole type (and use
+ // the intentionally fuzzy phrase "destructor")
+ ty::Closure(..) => ("destructor", "closure".to_owned()),
+ ty::Generator(..) => ("destructor", "generator".to_owned()),
+
+ _ => ("destructor", format!("type `{}`", local_decl.ty)),
+ };
+
+ match local_names[dropped_local] {
+ Some(local_name) if !local_decl.from_compiler_desugaring() => {
+ let message = format!(
+ "{B}borrow might be used here, when `{LOC}` is dropped \
+ and runs the {DTOR} for {TYPE}",
+ B = borrow_desc,
+ LOC = local_name,
+ TYPE = type_desc,
+ DTOR = dtor_desc
+ );
+ err.span_label(body.source_info(drop_loc).span, message);
+
+ if should_note_order {
+ err.note(
+ "values in a scope are dropped \
+ in the opposite order they are defined",
+ );
+ }
+ }
+ _ => {
+ err.span_label(
+ local_decl.source_info.span,
+ format!(
+ "a temporary with access to the {B}borrow \
+ is created here ...",
+ B = borrow_desc
+ ),
+ );
+ let message = format!(
+ "... and the {B}borrow might be used here, \
+ when that temporary is dropped \
+ and runs the {DTOR} for {TYPE}",
+ B = borrow_desc,
+ TYPE = type_desc,
+ DTOR = dtor_desc
+ );
+ err.span_label(body.source_info(drop_loc).span, message);
+
+ if let Some(info) = &local_decl.is_block_tail {
+ // FIXME: use span_suggestion instead, highlighting the
+ // whole block tail expression.
+ let msg = if info.tail_result_is_ignored {
+ "The temporary is part of an expression at the end of a block. \
+ Consider adding semicolon after the expression so its temporaries \
+ are dropped sooner, before the local variables declared by the \
+ block are dropped."
+ } else {
+ "The temporary is part of an expression at the end of a block. \
+ Consider forcing this temporary to be dropped sooner, before \
+ the block's local variables are dropped. \
+ For example, you could save the expression's value in a new \
+ local variable `x` and then make `x` be the expression \
+ at the end of the block."
+ };
+
+ err.note(msg);
+ }
+ }
+ }
+ }
+ BorrowExplanation::MustBeValidFor {
+ category,
+ span,
+ ref region_name,
+ ref opt_place_desc,
+ from_closure: _,
+ } => {
+ region_name.highlight_region_name(err);
+
+ if let Some(desc) = opt_place_desc {
+ err.span_label(
+ span,
+ format!(
+ "{}requires that `{}` is borrowed for `{}`",
+ category.description(),
+ desc,
+ region_name,
+ ),
+ );
+ } else {
+ err.span_label(
+ span,
+ format!(
+ "{}requires that {}borrow lasts for `{}`",
+ category.description(),
+ borrow_desc,
+ region_name,
+ ),
+ );
+ };
+ }
+ _ => {}
+ }
+ }
+}
+
+impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
+ /// Returns structured explanation for *why* the borrow contains the
+ /// point from `location`. This is key for the "3-point errors"
+ /// [described in the NLL RFC][d].
+ ///
+ /// # Parameters
+ ///
+ /// - `borrow`: the borrow in question
+ /// - `location`: where the borrow occurs
+ /// - `kind_place`: if Some, this describes the statement that triggered the error.
+ /// - first half is the kind of write, if any, being performed
+ /// - second half is the place being accessed
+ ///
+ /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
+ pub(in crate::borrow_check) fn explain_why_borrow_contains_point(
+ &self,
+ location: Location,
+ borrow: &BorrowData<'tcx>,
+ kind_place: Option<(WriteKind, &Place<'tcx>)>,
+ ) -> BorrowExplanation {
+ debug!(
+ "explain_why_borrow_contains_point(location={:?}, borrow={:?}, kind_place={:?})",
+ location, borrow, kind_place
+ );
+
+ let regioncx = &self.nonlexical_regioncx;
+ let body: &Body<'_> = &self.body;
+ let tcx = self.infcx.tcx;
+
+ let borrow_region_vid = borrow.region;
+ debug!(
+ "explain_why_borrow_contains_point: borrow_region_vid={:?}",
+ borrow_region_vid
+ );
+
+ let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, location);
+ debug!(
+ "explain_why_borrow_contains_point: region_sub={:?}",
+ region_sub
+ );
+
+ match find_use::find(body, regioncx, tcx, region_sub, location) {
+ Some(Cause::LiveVar(local, location)) => {
+ let span = body.source_info(location).span;
+ let spans = self
+ .move_spans(Place::from(local).as_ref(), location)
+ .or_else(|| self.borrow_spans(span, location));
+
+ let borrow_location = location;
+ if self.is_use_in_later_iteration_of_loop(borrow_location, location) {
+ let later_use = self.later_use_kind(borrow, spans, location);
+ BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1)
+ } else {
+ // Check if the location represents a `FakeRead`, and adapt the error
+ // message to the `FakeReadCause` it is from: in particular,
+ // the ones inserted in optimized `let var = <expr>` patterns.
+ let later_use = self.later_use_kind(borrow, spans, location);
+ BorrowExplanation::UsedLater(later_use.0, later_use.1)
+ }
+ }
+
+ Some(Cause::DropVar(local, location)) => {
+ let mut should_note_order = false;
+ if self.local_names[local].is_some() {
+ if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
+ if let Some(borrowed_local) = place.as_local() {
+ if self.local_names[borrowed_local].is_some()
+ && local != borrowed_local
+ {
+ should_note_order = true;
+ }
+ }
+ }
+ }
+
+ BorrowExplanation::UsedLaterWhenDropped {
+ drop_loc: location,
+ dropped_local: local,
+ should_note_order,
+ }
+ }
+
+ None => {
+ if let Some(region) = regioncx.to_error_region_vid(borrow_region_vid) {
+ let (category, from_closure, span, region_name) =
+ self.nonlexical_regioncx.free_region_constraint_info(
+ &self.body,
+ &self.local_names,
+ &self.upvars,
+ self.mir_def_id,
+ self.infcx,
+ borrow_region_vid,
+ region,
+ );
+ if let Some(region_name) = region_name {
+ let opt_place_desc =
+ self.describe_place(borrow.borrowed_place.as_ref());
+ BorrowExplanation::MustBeValidFor {
+ category,
+ from_closure,
+ span,
+ region_name,
+ opt_place_desc,
+ }
+ } else {
+ debug!("explain_why_borrow_contains_point: \
+ Could not generate a region name");
+ BorrowExplanation::Unexplained
+ }
+ } else {
+ debug!("explain_why_borrow_contains_point: \
+ Could not generate an error region vid");
+ BorrowExplanation::Unexplained
+ }
+ }
+ }
+ }
+
+ /// true if `borrow_location` can reach `use_location` by going through a loop and
+ /// `use_location` is also inside of that loop
+ fn is_use_in_later_iteration_of_loop(
+ &self,
+ borrow_location: Location,
+ use_location: Location,
+ ) -> bool {
+ let back_edge = self.reach_through_backedge(borrow_location, use_location);
+ back_edge.map_or(false, |back_edge| {
+ self.can_reach_head_of_loop(use_location, back_edge)
+ })
+ }
+
+ /// Returns the outmost back edge if `from` location can reach `to` location passing through
+ /// that back edge
+ fn reach_through_backedge(&self, from: Location, to: Location) -> Option<Location> {
+ let mut visited_locations = FxHashSet::default();
+ let mut pending_locations = VecDeque::new();
+ visited_locations.insert(from);
+ pending_locations.push_back(from);
+ debug!("reach_through_backedge: from={:?} to={:?}", from, to,);
+
+ let mut outmost_back_edge = None;
+ while let Some(location) = pending_locations.pop_front() {
+ debug!(
+ "reach_through_backedge: location={:?} outmost_back_edge={:?}
+ pending_locations={:?} visited_locations={:?}",
+ location, outmost_back_edge, pending_locations, visited_locations
+ );
+
+ if location == to && outmost_back_edge.is_some() {
+ // We've managed to reach the use location
+ debug!("reach_through_backedge: found!");
+ return outmost_back_edge;
+ }
+
+ let block = &self.body.basic_blocks()[location.block];
+
+ if location.statement_index < block.statements.len() {
+ let successor = location.successor_within_block();
+ if visited_locations.insert(successor) {
+ pending_locations.push_back(successor);
+ }
+ } else {
+ pending_locations.extend(
+ block
+ .terminator()
+ .successors()
+ .map(|bb| Location {
+ statement_index: 0,
+ block: *bb,
+ })
+ .filter(|s| visited_locations.insert(*s))
+ .map(|s| {
+ if self.is_back_edge(location, s) {
+ match outmost_back_edge {
+ None => {
+ outmost_back_edge = Some(location);
+ }
+
+ Some(back_edge)
+ if location.dominates(back_edge, &self.dominators) =>
+ {
+ outmost_back_edge = Some(location);
+ }
+
+ Some(_) => {}
+ }
+ }
+
+ s
+ }),
+ );
+ }
+ }
+
+ None
+ }
+
+ /// true if `from` location can reach `loop_head` location and `loop_head` dominates all the
+ /// intermediate nodes
+ fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool {
+ self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default())
+ }
+
+ fn find_loop_head_dfs(
+ &self,
+ from: Location,
+ loop_head: Location,
+ visited_locations: &mut FxHashSet<Location>,
+ ) -> bool {
+ visited_locations.insert(from);
+
+ if from == loop_head {
+ return true;
+ }
+
+ if loop_head.dominates(from, &self.dominators) {
+ let block = &self.body.basic_blocks()[from.block];
+
+ if from.statement_index < block.statements.len() {
+ let successor = from.successor_within_block();
+
+ if !visited_locations.contains(&successor)
+ && self.find_loop_head_dfs(successor, loop_head, visited_locations)
+ {
+ return true;
+ }
+ } else {
+ for bb in block.terminator().successors() {
+ let successor = Location {
+ statement_index: 0,
+ block: *bb,
+ };
+
+ if !visited_locations.contains(&successor)
+ && self.find_loop_head_dfs(successor, loop_head, visited_locations)
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ false
+ }
+
+ /// True if an edge `source -> target` is a backedge -- in other words, if the target
+ /// dominates the source.
+ fn is_back_edge(&self, source: Location, target: Location) -> bool {
+ target.dominates(source, &self.dominators)
+ }
+
+ /// Determine how the borrow was later used.
+ fn later_use_kind(
+ &self,
+ borrow: &BorrowData<'tcx>,
+ use_spans: UseSpans,
+ location: Location,
+ ) -> (LaterUseKind, Span) {
+ match use_spans {
+ UseSpans::ClosureUse { var_span, .. } => {
+ // Used in a closure.
+ (LaterUseKind::ClosureCapture, var_span)
+ }
+ UseSpans::OtherUse(span) => {
+ let block = &self.body.basic_blocks()[location.block];
+
+ let kind = if let Some(&Statement {
+ kind: StatementKind::FakeRead(FakeReadCause::ForLet, _),
+ ..
+ }) = block.statements.get(location.statement_index)
+ {
+ LaterUseKind::FakeLetRead
+ } else if self.was_captured_by_trait_object(borrow) {
+ LaterUseKind::TraitCapture
+ } else if location.statement_index == block.statements.len() {
+ if let TerminatorKind::Call {
+ ref func,
+ from_hir_call: true,
+ ..
+ } = block.terminator().kind
+ {
+ // Just point to the function, to reduce the chance of overlapping spans.
+ let function_span = match func {
+ Operand::Constant(c) => c.span,
+ Operand::Copy(place) |
+ Operand::Move(place) => {
+ if let Some(l) = place.as_local() {
+ let local_decl = &self.body.local_decls[l];
+ if self.local_names[l].is_none() {
+ local_decl.source_info.span
+ } else {
+ span
+ }
+ } else {
+ span
+ }
+ }
+ };
+ return (LaterUseKind::Call, function_span);
+ } else {
+ LaterUseKind::Other
+ }
+ } else {
+ LaterUseKind::Other
+ };
+
+ (kind, span)
+ }
+ }
+ }
+
+ /// Checks if a borrowed value was captured by a trait object. We do this by
+ /// looking forward in the MIR from the reserve location and checking if we see
+ /// a unsized cast to a trait object on our data.
+ fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool {
+ // Start at the reserve location, find the place that we want to see cast to a trait object.
+ let location = borrow.reserve_location;
+ let block = &self.body[location.block];
+ let stmt = block.statements.get(location.statement_index);
+ debug!(
+ "was_captured_by_trait_object: location={:?} stmt={:?}",
+ location, stmt
+ );
+
+ // We make a `queue` vector that has the locations we want to visit. As of writing, this
+ // will only ever have one item at any given time, but by using a vector, we can pop from
+ // it which simplifies the termination logic.
+ let mut queue = vec![location];
+ let mut target = if let Some(&Statement {
+ kind: StatementKind::Assign(box(ref place, _)),
+ ..
+ }) = stmt {
+ if let Some(local) = place.as_local() {
+ local
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ };
+
+ debug!(
+ "was_captured_by_trait: target={:?} queue={:?}",
+ target, queue
+ );
+ while let Some(current_location) = queue.pop() {
+ debug!("was_captured_by_trait: target={:?}", target);
+ let block = &self.body[current_location.block];
+ // We need to check the current location to find out if it is a terminator.
+ let is_terminator = current_location.statement_index == block.statements.len();
+ if !is_terminator {
+ let stmt = &block.statements[current_location.statement_index];
+ debug!("was_captured_by_trait_object: stmt={:?}", stmt);
+
+ // The only kind of statement that we care about is assignments...
+ if let StatementKind::Assign(box(place, rvalue)) = &stmt.kind {
+ let into = match place.local_or_deref_local() {
+ Some(into) => into,
+ None => {
+ // Continue at the next location.
+ queue.push(current_location.successor_within_block());
+ continue;
+ }
+ };
+
+ match rvalue {
+ // If we see a use, we should check whether it is our data, and if so
+ // update the place that we're looking for to that new place.
+ Rvalue::Use(operand) => match operand {
+ Operand::Copy(place)
+ | Operand::Move(place) => {
+ if let Some(from) = place.as_local() {
+ if from == target {
+ target = into;
+ }
+ }
+ }
+ _ => {}
+ },
+ // If we see a unsized cast, then if it is our data we should check
+ // whether it is being cast to a trait object.
+ Rvalue::Cast(
+ CastKind::Pointer(PointerCast::Unsize), operand, ty
+ ) => match operand {
+ Operand::Copy(place)
+ | Operand::Move(place) => {
+ if let Some(from) = place.as_local() {
+ if from == target {
+ debug!("was_captured_by_trait_object: ty={:?}", ty);
+ // Check the type for a trait object.
+ return match ty.kind {
+ // `&dyn Trait`
+ ty::Ref(_, ty, _) if ty.is_trait() => true,
+ // `Box<dyn Trait>`
+ _ if ty.is_box() && ty.boxed_ty().is_trait() => true,
+ // `dyn Trait`
+ _ if ty.is_trait() => true,
+ // Anything else.
+ _ => false,
+ };
+ }
+ }
+ return false;
+ }
+ _ => return false,
+ },
+ _ => {}
+ }
+ }
+
+ // Continue at the next location.
+ queue.push(current_location.successor_within_block());
+ } else {
+ // The only thing we need to do for terminators is progress to the next block.
+ let terminator = block.terminator();
+ debug!("was_captured_by_trait_object: terminator={:?}", terminator);
+
+ if let TerminatorKind::Call {
+ destination: Some((place, block)),
+ args,
+ ..
+ } = &terminator.kind {
+ if let Some(dest) = place.as_local() {
+ debug!(
+ "was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
+ target, dest, args
+ );
+ // Check if one of the arguments to this function is the target place.
+ let found_target = args.iter().any(|arg| {
+ if let Operand::Move(place) = arg {
+ if let Some(potential) = place.as_local() {
+ potential == target
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ });
+
+ // If it is, follow this to the next block and update the target.
+ if found_target {
+ target = dest;
+ queue.push(block.start_location());
+ }
+ }
+ }
+ }
+
+ debug!("was_captured_by_trait: queue={:?}", queue);
+ }
+
+ // We didn't find anything and ran out of locations to check.
+ false
+ }
+}
--- /dev/null
+use std::collections::VecDeque;
+use std::rc::Rc;
+
+use crate::borrow_check::nll::region_infer::{Cause, RegionInferenceContext};
+use crate::borrow_check::nll::ToRegionVid;
+use crate::util::liveness::{self, DefUse};
+use rustc::mir::visit::{MirVisitable, PlaceContext, Visitor};
+use rustc::mir::{Local, Location, Body};
+use rustc::ty::{RegionVid, TyCtxt};
+use rustc_data_structures::fx::FxHashSet;
+
+crate fn find<'tcx>(
+ body: &Body<'tcx>,
+ regioncx: &Rc<RegionInferenceContext<'tcx>>,
+ tcx: TyCtxt<'tcx>,
+ region_vid: RegionVid,
+ start_point: Location,
+) -> Option<Cause> {
+ let mut uf = UseFinder {
+ body,
+ regioncx,
+ tcx,
+ region_vid,
+ start_point,
+ };
+
+ uf.find()
+}
+
+struct UseFinder<'cx, 'tcx> {
+ body: &'cx Body<'tcx>,
+ regioncx: &'cx Rc<RegionInferenceContext<'tcx>>,
+ tcx: TyCtxt<'tcx>,
+ region_vid: RegionVid,
+ start_point: Location,
+}
+
+impl<'cx, 'tcx> UseFinder<'cx, 'tcx> {
+ fn find(&mut self) -> Option<Cause> {
+ let mut queue = VecDeque::new();
+ let mut visited = FxHashSet::default();
+
+ queue.push_back(self.start_point);
+ while let Some(p) = queue.pop_front() {
+ if !self.regioncx.region_contains(self.region_vid, p) {
+ continue;
+ }
+
+ if !visited.insert(p) {
+ continue;
+ }
+
+ let block_data = &self.body[p.block];
+
+ match self.def_use(p, block_data.visitable(p.statement_index)) {
+ Some(DefUseResult::Def) => {}
+
+ Some(DefUseResult::UseLive { local }) => {
+ return Some(Cause::LiveVar(local, p));
+ }
+
+ Some(DefUseResult::UseDrop { local }) => {
+ return Some(Cause::DropVar(local, p));
+ }
+
+ None => {
+ if p.statement_index < block_data.statements.len() {
+ queue.push_back(p.successor_within_block());
+ } else {
+ queue.extend(
+ block_data
+ .terminator()
+ .successors()
+ .filter(|&bb| Some(&Some(*bb)) != block_data.terminator().unwind())
+ .map(|&bb| Location {
+ statement_index: 0,
+ block: bb,
+ }),
+ );
+ }
+ }
+ }
+ }
+
+ None
+ }
+
+ fn def_use(&self, location: Location, thing: &dyn MirVisitable<'tcx>) -> Option<DefUseResult> {
+ let mut visitor = DefUseVisitor {
+ body: self.body,
+ tcx: self.tcx,
+ region_vid: self.region_vid,
+ def_use_result: None,
+ };
+
+ thing.apply(location, &mut visitor);
+
+ visitor.def_use_result
+ }
+}
+
+struct DefUseVisitor<'cx, 'tcx> {
+ body: &'cx Body<'tcx>,
+ tcx: TyCtxt<'tcx>,
+ region_vid: RegionVid,
+ def_use_result: Option<DefUseResult>,
+}
+
+enum DefUseResult {
+ Def,
+ UseLive { local: Local },
+ UseDrop { local: Local },
+}
+
+impl<'cx, 'tcx> Visitor<'tcx> for DefUseVisitor<'cx, 'tcx> {
+ fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
+ let local_ty = self.body.local_decls[local].ty;
+
+ let mut found_it = false;
+ self.tcx.for_each_free_region(&local_ty, |r| {
+ if r.to_region_vid() == self.region_vid {
+ found_it = true;
+ }
+ });
+
+ if found_it {
+ self.def_use_result = match liveness::categorize(context) {
+ Some(DefUse::Def) => Some(DefUseResult::Def),
+ Some(DefUse::Use) => Some(DefUseResult::UseLive { local }),
+ Some(DefUse::Drop) => Some(DefUseResult::UseDrop { local }),
+ None => None,
+ };
+ }
+ }
+}
--- /dev/null
+//! Borrow checker diagnostics.
+
+use rustc::hir;
+use rustc::hir::def::Namespace;
+use rustc::hir::def_id::DefId;
+use rustc::hir::GeneratorKind;
+use rustc::mir::{
+ AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand,
+ Place, PlaceBase, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind,
+ Static, StaticKind, Terminator, TerminatorKind,
+};
+use rustc::ty::{self, DefIdTree, Ty, TyCtxt};
+use rustc::ty::layout::VariantIdx;
+use rustc::ty::print::Print;
+use rustc_errors::DiagnosticBuilder;
+use syntax_pos::Span;
+
+use super::borrow_set::BorrowData;
+use super::MirBorrowckCtxt;
+use crate::dataflow::move_paths::{InitLocation, LookupResult};
+
+mod find_use;
+mod var_name;
+mod region_name;
+mod outlives_suggestion;
+
+mod conflict_errors;
+mod move_errors;
+mod mutability_errors;
+mod region_errors;
+mod explain_borrow;
+
+crate use mutability_errors::AccessKind;
+crate use region_name::{RegionName, RegionNameSource, RegionErrorNamingCtx};
+crate use region_errors::{ErrorReportingCtx, ErrorConstraintInfo};
+crate use outlives_suggestion::OutlivesSuggestionBuilder;
+
+pub(super) struct IncludingDowncast(pub(super) bool);
+
+impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
+ /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
+ /// is moved after being invoked.
+ ///
+ /// ```text
+ /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
+ /// its environment
+ /// --> $DIR/issue-42065.rs:16:29
+ /// |
+ /// LL | for (key, value) in dict {
+ /// | ^^^^
+ /// ```
+ pub(super) fn add_moved_or_invoked_closure_note(
+ &self,
+ location: Location,
+ place: PlaceRef<'cx, 'tcx>,
+ diag: &mut DiagnosticBuilder<'_>,
+ ) {
+ debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
+ let mut target = place.local_or_deref_local();
+ for stmt in &self.body[location.block].statements[location.statement_index..] {
+ debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
+ if let StatementKind::Assign(box(into, Rvalue::Use(from))) = &stmt.kind {
+ debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
+ match from {
+ Operand::Copy(ref place) |
+ Operand::Move(ref place) if target == place.local_or_deref_local() =>
+ target = into.local_or_deref_local(),
+ _ => {},
+ }
+ }
+ }
+
+ // Check if we are attempting to call a closure after it has been invoked.
+ let terminator = self.body[location.block].terminator();
+ debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
+ if let TerminatorKind::Call {
+ func: Operand::Constant(box Constant {
+ literal: ty::Const {
+ ty: &ty::TyS { kind: ty::FnDef(id, _), .. },
+ ..
+ },
+ ..
+ }),
+ args,
+ ..
+ } = &terminator.kind {
+ debug!("add_moved_or_invoked_closure_note: id={:?}", id);
+ if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
+ let closure = match args.first() {
+ Some(Operand::Copy(ref place)) |
+ Some(Operand::Move(ref place)) if target == place.local_or_deref_local() =>
+ place.local_or_deref_local().unwrap(),
+ _ => return,
+ };
+
+ debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
+ if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind {
+ let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap();
+
+ if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
+ .closure_kind_origins()
+ .get(hir_id)
+ {
+ diag.span_note(
+ *span,
+ &format!(
+ "closure cannot be invoked more than once because it moves the \
+ variable `{}` out of its environment",
+ name,
+ ),
+ );
+ return;
+ }
+ }
+ }
+ }
+
+ // Check if we are just moving a closure after it has been invoked.
+ if let Some(target) = target {
+ if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind {
+ let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap();
+
+ if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
+ .closure_kind_origins()
+ .get(hir_id)
+ {
+ diag.span_note(
+ *span,
+ &format!(
+ "closure cannot be moved more than once as it is not `Copy` due to \
+ moving the variable `{}` out of its environment",
+ name
+ ),
+ );
+ }
+ }
+ }
+ }
+
+ /// End-user visible description of `place` if one can be found. If the
+ /// place is a temporary for instance, None will be returned.
+ pub(super) fn describe_place(&self, place_ref: PlaceRef<'cx, 'tcx>) -> Option<String> {
+ self.describe_place_with_options(place_ref, IncludingDowncast(false))
+ }
+
+ /// End-user visible description of `place` if one can be found. If the
+ /// place is a temporary for instance, None will be returned.
+ /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
+ /// `Downcast` and `IncludingDowncast` is true
+ pub(super) fn describe_place_with_options(
+ &self,
+ place: PlaceRef<'cx, 'tcx>,
+ including_downcast: IncludingDowncast,
+ ) -> Option<String> {
+ let mut buf = String::new();
+ match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
+ Ok(()) => Some(buf),
+ Err(()) => None,
+ }
+ }
+
+ /// Appends end-user visible description of `place` to `buf`.
+ fn append_place_to_string(
+ &self,
+ place: PlaceRef<'cx, 'tcx>,
+ buf: &mut String,
+ mut autoderef: bool,
+ including_downcast: &IncludingDowncast,
+ ) -> Result<(), ()> {
+ match place {
+ PlaceRef {
+ base: PlaceBase::Local(local),
+ projection: [],
+ } => {
+ self.append_local_to_string(*local, buf)?;
+ }
+ PlaceRef {
+ base:
+ PlaceBase::Static(box Static {
+ kind: StaticKind::Promoted(..),
+ ..
+ }),
+ projection: [],
+ } => {
+ buf.push_str("promoted");
+ }
+ PlaceRef {
+ base:
+ PlaceBase::Static(box Static {
+ kind: StaticKind::Static,
+ def_id,
+ ..
+ }),
+ projection: [],
+ } => {
+ buf.push_str(&self.infcx.tcx.item_name(*def_id).to_string());
+ }
+ PlaceRef {
+ base: &PlaceBase::Local(local),
+ projection: [ProjectionElem::Deref]
+ } if self.body.local_decls[local].is_ref_for_guard() => {
+ self.append_place_to_string(
+ PlaceRef {
+ base: &PlaceBase::Local(local),
+ projection: &[],
+ },
+ buf,
+ autoderef,
+ &including_downcast,
+ )?;
+ },
+ PlaceRef {
+ base: &PlaceBase::Local(local),
+ projection: [ProjectionElem::Deref]
+ } if self.body.local_decls[local].is_ref_to_static() => {
+ let local_info = &self.body.local_decls[local].local_info;
+ if let LocalInfo::StaticRef { def_id, .. } = *local_info {
+ buf.push_str(&self.infcx.tcx.item_name(def_id).as_str());
+ } else {
+ unreachable!();
+ }
+ },
+ PlaceRef {
+ base,
+ projection: [proj_base @ .., elem],
+ } => {
+ match elem {
+ ProjectionElem::Deref => {
+ let upvar_field_projection =
+ self.is_upvar_field_projection(place);
+ if let Some(field) = upvar_field_projection {
+ let var_index = field.index();
+ let name = self.upvars[var_index].name.to_string();
+ if self.upvars[var_index].by_ref {
+ buf.push_str(&name);
+ } else {
+ buf.push_str(&format!("*{}", &name));
+ }
+ } else {
+ if autoderef {
+ // FIXME turn this recursion into iteration
+ self.append_place_to_string(
+ PlaceRef {
+ base,
+ projection: proj_base,
+ },
+ buf,
+ autoderef,
+ &including_downcast,
+ )?;
+ } else {
+ match (proj_base, base) {
+ _ => {
+ buf.push_str(&"*");
+ self.append_place_to_string(
+ PlaceRef {
+ base,
+ projection: proj_base,
+ },
+ buf,
+ autoderef,
+ &including_downcast,
+ )?;
+ }
+ }
+ }
+ }
+ }
+ ProjectionElem::Downcast(..) => {
+ self.append_place_to_string(
+ PlaceRef {
+ base,
+ projection: proj_base,
+ },
+ buf,
+ autoderef,
+ &including_downcast,
+ )?;
+ if including_downcast.0 {
+ return Err(());
+ }
+ }
+ ProjectionElem::Field(field, _ty) => {
+ autoderef = true;
+
+ let upvar_field_projection =
+ self.is_upvar_field_projection(place);
+ if let Some(field) = upvar_field_projection {
+ let var_index = field.index();
+ let name = self.upvars[var_index].name.to_string();
+ buf.push_str(&name);
+ } else {
+ let field_name = self.describe_field(PlaceRef {
+ base,
+ projection: proj_base,
+ }, *field);
+ self.append_place_to_string(
+ PlaceRef {
+ base,
+ projection: proj_base,
+ },
+ buf,
+ autoderef,
+ &including_downcast,
+ )?;
+ buf.push_str(&format!(".{}", field_name));
+ }
+ }
+ ProjectionElem::Index(index) => {
+ autoderef = true;
+
+ self.append_place_to_string(
+ PlaceRef {
+ base,
+ projection: proj_base,
+ },
+ buf,
+ autoderef,
+ &including_downcast,
+ )?;
+ buf.push_str("[");
+ if self.append_local_to_string(*index, buf).is_err() {
+ buf.push_str("_");
+ }
+ buf.push_str("]");
+ }
+ ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
+ autoderef = true;
+ // Since it isn't possible to borrow an element on a particular index and
+ // then use another while the borrow is held, don't output indices details
+ // to avoid confusing the end-user
+ self.append_place_to_string(
+ PlaceRef {
+ base,
+ projection: proj_base,
+ },
+ buf,
+ autoderef,
+ &including_downcast,
+ )?;
+ buf.push_str(&"[..]");
+ }
+ };
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
+ /// a name, or its name was generated by the compiler, then `Err` is returned
+ fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
+ let decl = &self.body.local_decls[local];
+ match self.local_names[local] {
+ Some(name) if !decl.from_compiler_desugaring() => {
+ buf.push_str(&name.as_str());
+ Ok(())
+ }
+ _ => Err(()),
+ }
+ }
+
+ /// End-user visible description of the `field`nth field of `base`
+ fn describe_field(&self, place: PlaceRef<'cx, 'tcx>, field: Field) -> String {
+ // FIXME Place2 Make this work iteratively
+ match place {
+ PlaceRef {
+ base: PlaceBase::Local(local),
+ projection: [],
+ } => {
+ let local = &self.body.local_decls[*local];
+ self.describe_field_from_ty(&local.ty, field, None)
+ }
+ PlaceRef {
+ base: PlaceBase::Static(static_),
+ projection: [],
+ } =>
+ self.describe_field_from_ty(&static_.ty, field, None),
+ PlaceRef {
+ base,
+ projection: [proj_base @ .., elem],
+ } => match elem {
+ ProjectionElem::Deref => {
+ self.describe_field(PlaceRef {
+ base,
+ projection: proj_base,
+ }, field)
+ }
+ ProjectionElem::Downcast(_, variant_index) => {
+ let base_ty = Place::ty_from(
+ place.base,
+ place.projection,
+ *self.body,
+ self.infcx.tcx).ty;
+ self.describe_field_from_ty(&base_ty, field, Some(*variant_index))
+ }
+ ProjectionElem::Field(_, field_type) => {
+ self.describe_field_from_ty(&field_type, field, None)
+ }
+ ProjectionElem::Index(..)
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. } => {
+ self.describe_field(PlaceRef {
+ base,
+ projection: proj_base,
+ }, field)
+ }
+ },
+ }
+ }
+
+ /// End-user visible description of the `field_index`nth field of `ty`
+ fn describe_field_from_ty(
+ &self,
+ ty: Ty<'_>,
+ field: Field,
+ variant_index: Option<VariantIdx>
+ ) -> String {
+ if ty.is_box() {
+ // If the type is a box, the field is described from the boxed type
+ self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index)
+ } else {
+ match ty.kind {
+ ty::Adt(def, _) => {
+ let variant = if let Some(idx) = variant_index {
+ assert!(def.is_enum());
+ &def.variants[idx]
+ } else {
+ def.non_enum_variant()
+ };
+ variant.fields[field.index()]
+ .ident
+ .to_string()
+ },
+ ty::Tuple(_) => field.index().to_string(),
+ ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
+ self.describe_field_from_ty(&ty, field, variant_index)
+ }
+ ty::Array(ty, _) | ty::Slice(ty) =>
+ self.describe_field_from_ty(&ty, field, variant_index),
+ ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
+ // `tcx.upvars(def_id)` returns an `Option`, which is `None` in case
+ // the closure comes from another crate. But in that case we wouldn't
+ // be borrowck'ing it, so we can just unwrap:
+ let (&var_id, _) = self.infcx.tcx.upvars(def_id).unwrap()
+ .get_index(field.index()).unwrap();
+
+ self.infcx.tcx.hir().name(var_id).to_string()
+ }
+ _ => {
+ // Might need a revision when the fields in trait RFC is implemented
+ // (https://github.com/rust-lang/rfcs/pull/1546)
+ bug!(
+ "End-user description not implemented for field access on `{:?}`",
+ ty
+ );
+ }
+ }
+ }
+ }
+
+ /// Add a note that a type does not implement `Copy`
+ pub(super) fn note_type_does_not_implement_copy(
+ &self,
+ err: &mut DiagnosticBuilder<'a>,
+ place_desc: &str,
+ ty: Ty<'tcx>,
+ span: Option<Span>,
+ ) {
+ let message = format!(
+ "move occurs because {} has type `{}`, which does not implement the `Copy` trait",
+ place_desc,
+ ty,
+ );
+ if let Some(span) = span {
+ err.span_label(span, message);
+ } else {
+ err.note(&message);
+ }
+ }
+
+ pub(super) fn borrowed_content_source(
+ &self,
+ deref_base: PlaceRef<'cx, 'tcx>,
+ ) -> BorrowedContentSource<'tcx> {
+ let tcx = self.infcx.tcx;
+
+ // Look up the provided place and work out the move path index for it,
+ // we'll use this to check whether it was originally from an overloaded
+ // operator.
+ match self.move_data.rev_lookup.find(deref_base) {
+ LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
+ debug!("borrowed_content_source: mpi={:?}", mpi);
+
+ for i in &self.move_data.init_path_map[mpi] {
+ let init = &self.move_data.inits[*i];
+ debug!("borrowed_content_source: init={:?}", init);
+ // We're only interested in statements that initialized a value, not the
+ // initializations from arguments.
+ let loc = match init.location {
+ InitLocation::Statement(stmt) => stmt,
+ _ => continue,
+ };
+
+ let bbd = &self.body[loc.block];
+ let is_terminator = bbd.statements.len() == loc.statement_index;
+ debug!(
+ "borrowed_content_source: loc={:?} is_terminator={:?}",
+ loc,
+ is_terminator,
+ );
+ if !is_terminator {
+ continue;
+ } else if let Some(Terminator {
+ kind: TerminatorKind::Call {
+ ref func,
+ from_hir_call: false,
+ ..
+ },
+ ..
+ }) = bbd.terminator {
+ if let Some(source) = BorrowedContentSource::from_call(
+ func.ty(*self.body, tcx),
+ tcx
+ ) {
+ return source;
+ }
+ }
+ }
+ }
+ // Base is a `static` so won't be from an overloaded operator
+ _ => (),
+ };
+
+ // If we didn't find an overloaded deref or index, then assume it's a
+ // built in deref and check the type of the base.
+ let base_ty = Place::ty_from(
+ deref_base.base,
+ deref_base.projection,
+ *self.body,
+ tcx
+ ).ty;
+ if base_ty.is_unsafe_ptr() {
+ BorrowedContentSource::DerefRawPointer
+ } else if base_ty.is_mutable_ptr() {
+ BorrowedContentSource::DerefMutableRef
+ } else {
+ BorrowedContentSource::DerefSharedRef
+ }
+ }
+}
+
+impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
+ /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
+ /// name where required.
+ 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);
+
+ // We need to add synthesized lifetimes where appropriate. We do
+ // this by hooking into the pretty printer and telling it to label the
+ // lifetimes without names with the value `'0`.
+ match ty.kind {
+ ty::Ref(ty::RegionKind::ReLateBound(_, br), _, _)
+ | ty::Ref(
+ ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
+ _,
+ _,
+ ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter),
+ _ => {}
+ }
+
+ let _ = ty.print(printer);
+ s
+ }
+
+ /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
+ /// synthesized lifetime name where required.
+ 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);
+
+ let region = match ty.kind {
+ ty::Ref(region, _, _) => {
+ match region {
+ ty::RegionKind::ReLateBound(_, br)
+ | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
+ printer.region_highlight_mode.highlighting_bound_region(*br, counter)
+ }
+ _ => {}
+ }
+
+ region
+ }
+ _ => bug!("ty for annotation of borrow region is not a reference"),
+ };
+
+ let _ = region.print(printer);
+ s
+ }
+}
+
+// The span(s) associated to a use of a place.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub(super) enum UseSpans {
+ // The access is caused by capturing a variable for a closure.
+ ClosureUse {
+ // This is true if the captured variable was from a generator.
+ generator_kind: Option<GeneratorKind>,
+ // The span of the args of the closure, including the `move` keyword if
+ // it's present.
+ args_span: Span,
+ // The span of the first use of the captured variable inside the closure.
+ var_span: Span,
+ },
+ // This access has a single span associated to it: common case.
+ OtherUse(Span),
+}
+
+impl UseSpans {
+ pub(super) fn args_or_use(self) -> Span {
+ match self {
+ UseSpans::ClosureUse {
+ args_span: span, ..
+ }
+ | UseSpans::OtherUse(span) => span,
+ }
+ }
+
+ pub(super) fn var_or_use(self) -> Span {
+ match self {
+ UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
+ }
+ }
+
+ pub(super) fn generator_kind(self) -> Option<GeneratorKind> {
+ match self {
+ UseSpans::ClosureUse { generator_kind, .. } => generator_kind,
+ _ => None,
+ }
+ }
+
+ // Add a span label to the arguments of the closure, if it exists.
+ pub(super) fn args_span_label(
+ self,
+ err: &mut DiagnosticBuilder<'_>,
+ message: impl Into<String>,
+ ) {
+ if let UseSpans::ClosureUse { args_span, .. } = self {
+ err.span_label(args_span, message);
+ }
+ }
+
+ // Add a span label to the use of the captured variable, if it exists.
+ pub(super) fn var_span_label(
+ self,
+ err: &mut DiagnosticBuilder<'_>,
+ message: impl Into<String>,
+ ) {
+ if let UseSpans::ClosureUse { var_span, .. } = self {
+ err.span_label(var_span, message);
+ }
+ }
+
+ /// Returns `false` if this place is not used in a closure.
+ pub(super) fn for_closure(&self) -> bool {
+ match *self {
+ UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(),
+ _ => false,
+ }
+ }
+
+ /// Returns `false` if this place is not used in a generator.
+ pub(super) fn for_generator(&self) -> bool {
+ match *self {
+ UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(),
+ _ => false,
+ }
+ }
+
+ /// Describe the span associated with a use of a place.
+ pub(super) fn describe(&self) -> String {
+ match *self {
+ UseSpans::ClosureUse { generator_kind, .. } => if generator_kind.is_some() {
+ " in generator".to_string()
+ } else {
+ " in closure".to_string()
+ },
+ _ => "".to_string(),
+ }
+ }
+
+ pub(super) fn or_else<F>(self, if_other: F) -> Self
+ where
+ F: FnOnce() -> Self,
+ {
+ match self {
+ closure @ UseSpans::ClosureUse { .. } => closure,
+ UseSpans::OtherUse(_) => if_other(),
+ }
+ }
+}
+
+pub(super) enum BorrowedContentSource<'tcx> {
+ DerefRawPointer,
+ DerefMutableRef,
+ DerefSharedRef,
+ OverloadedDeref(Ty<'tcx>),
+ OverloadedIndex(Ty<'tcx>),
+}
+
+impl BorrowedContentSource<'tcx> {
+ pub(super) fn describe_for_unnamed_place(&self) -> String {
+ match *self {
+ BorrowedContentSource::DerefRawPointer => format!("a raw pointer"),
+ BorrowedContentSource::DerefSharedRef => format!("a shared reference"),
+ BorrowedContentSource::DerefMutableRef => {
+ format!("a mutable reference")
+ }
+ BorrowedContentSource::OverloadedDeref(ty) => {
+ if ty.is_rc() {
+ format!("an `Rc`")
+ } else if ty.is_arc() {
+ format!("an `Arc`")
+ } else {
+ format!("dereference of `{}`", ty)
+ }
+ }
+ BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty),
+ }
+ }
+
+ pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
+ match *self {
+ BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
+ BorrowedContentSource::DerefSharedRef => Some("shared reference"),
+ BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
+ // Overloaded deref and index operators should be evaluated into a
+ // temporary. So we don't need a description here.
+ BorrowedContentSource::OverloadedDeref(_)
+ | BorrowedContentSource::OverloadedIndex(_) => None
+ }
+ }
+
+ pub(super) fn describe_for_immutable_place(&self) -> String {
+ match *self {
+ BorrowedContentSource::DerefRawPointer => format!("a `*const` pointer"),
+ BorrowedContentSource::DerefSharedRef => format!("a `&` reference"),
+ BorrowedContentSource::DerefMutableRef => {
+ bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
+ },
+ BorrowedContentSource::OverloadedDeref(ty) => {
+ if ty.is_rc() {
+ format!("an `Rc`")
+ } else if ty.is_arc() {
+ format!("an `Arc`")
+ } else {
+ format!("a dereference of `{}`", ty)
+ }
+ }
+ BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty),
+ }
+ }
+
+ fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
+ match func.kind {
+ ty::FnDef(def_id, substs) => {
+ let trait_id = tcx.trait_of_item(def_id)?;
+
+ let lang_items = tcx.lang_items();
+ if Some(trait_id) == lang_items.deref_trait()
+ || Some(trait_id) == lang_items.deref_mut_trait()
+ {
+ Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0)))
+ } else if Some(trait_id) == lang_items.index_trait()
+ || Some(trait_id) == lang_items.index_mut_trait()
+ {
+ Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0)))
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+ }
+}
+
+impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
+ /// Finds the spans associated to a move or copy of move_place at location.
+ pub(super) fn move_spans(
+ &self,
+ moved_place: PlaceRef<'cx, 'tcx>, // Could also be an upvar.
+ location: Location,
+ ) -> UseSpans {
+ use self::UseSpans::*;
+
+ let stmt = match self.body[location.block].statements.get(location.statement_index) {
+ Some(stmt) => stmt,
+ None => return OtherUse(self.body.source_info(location).span),
+ };
+
+ debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
+ if let StatementKind::Assign(
+ box(_, Rvalue::Aggregate(ref kind, ref places))
+ ) = stmt.kind {
+ let def_id = match kind {
+ box AggregateKind::Closure(def_id, _)
+ | box AggregateKind::Generator(def_id, _, _) => def_id,
+ _ => return OtherUse(stmt.source_info.span),
+ };
+
+ debug!(
+ "move_spans: def_id={:?} places={:?}",
+ def_id, places
+ );
+ if let Some((args_span, generator_kind, var_span))
+ = self.closure_span(*def_id, moved_place, places) {
+ return ClosureUse {
+ generator_kind,
+ args_span,
+ var_span,
+ };
+ }
+ }
+
+ OtherUse(stmt.source_info.span)
+ }
+
+ /// Finds the span of arguments of a closure (within `maybe_closure_span`)
+ /// and its usage of the local assigned at `location`.
+ /// This is done by searching in statements succeeding `location`
+ /// and originating from `maybe_closure_span`.
+ pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
+ use self::UseSpans::*;
+ debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
+
+ let target = match self.body[location.block]
+ .statements
+ .get(location.statement_index)
+ {
+ Some(&Statement {
+ kind: StatementKind::Assign(box(ref place, _)),
+ ..
+ }) => {
+ if let Some(local) = place.as_local() {
+ local
+ } else {
+ return OtherUse(use_span);
+ }
+ }
+ _ => return OtherUse(use_span),
+ };
+
+ if self.body.local_kind(target) != LocalKind::Temp {
+ // operands are always temporaries.
+ return OtherUse(use_span);
+ }
+
+ for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
+ if let StatementKind::Assign(
+ box(_, Rvalue::Aggregate(ref kind, ref places))
+ ) = stmt.kind {
+ let (def_id, is_generator) = match kind {
+ box AggregateKind::Closure(def_id, _) => (def_id, false),
+ box AggregateKind::Generator(def_id, _, _) => (def_id, true),
+ _ => continue,
+ };
+
+ debug!(
+ "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
+ def_id, is_generator, places
+ );
+ if let Some((args_span, generator_kind, var_span)) = self.closure_span(
+ *def_id, Place::from(target).as_ref(), places
+ ) {
+ return ClosureUse {
+ generator_kind,
+ args_span,
+ var_span,
+ };
+ } else {
+ return OtherUse(use_span);
+ }
+ }
+
+ if use_span != stmt.source_info.span {
+ break;
+ }
+ }
+
+ OtherUse(use_span)
+ }
+
+ /// Finds the span of a captured variable within a closure or generator.
+ fn closure_span(
+ &self,
+ def_id: DefId,
+ target_place: PlaceRef<'cx, 'tcx>,
+ places: &Vec<Operand<'tcx>>,
+ ) -> Option<(Span, Option<GeneratorKind>, Span)> {
+ debug!(
+ "closure_span: def_id={:?} target_place={:?} places={:?}",
+ def_id, target_place, places
+ );
+ let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id)?;
+ let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
+ debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
+ if let hir::ExprKind::Closure(
+ .., body_id, args_span, _
+ ) = expr {
+ for (upvar, place) in self.infcx.tcx.upvars(def_id)?.values().zip(places) {
+ match place {
+ Operand::Copy(place) |
+ Operand::Move(place) if target_place == place.as_ref() => {
+ debug!("closure_span: found captured local {:?}", place);
+ let body = self.infcx.tcx.hir().body(*body_id);
+ let generator_kind = body.generator_kind();
+ return Some((*args_span, generator_kind, upvar.span));
+ },
+ _ => {}
+ }
+ }
+
+ }
+ None
+ }
+
+ /// Helper to retrieve span(s) of given borrow from the current MIR
+ /// representation
+ pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans {
+ let span = self.body.source_info(borrow.reserve_location).span;
+ self.borrow_spans(span, borrow.reserve_location)
+ }
+}
--- /dev/null
+use rustc::mir::*;
+use rustc::ty;
+use rustc_errors::{DiagnosticBuilder,Applicability};
+use syntax_pos::Span;
+
+use crate::borrow_check::MirBorrowckCtxt;
+use crate::borrow_check::prefixes::PrefixSet;
+use crate::borrow_check::diagnostics::UseSpans;
+use crate::dataflow::move_paths::{
+ IllegalMoveOrigin, IllegalMoveOriginKind,
+ LookupResult, MoveError, MovePathIndex,
+};
+
+// Often when desugaring a pattern match we may have many individual moves in
+// MIR that are all part of one operation from the user's point-of-view. For
+// example:
+//
+// let (x, y) = foo()
+//
+// would move x from the 0 field of some temporary, and y from the 1 field. We
+// group such errors together for cleaner error reporting.
+//
+// Errors are kept separate if they are from places with different parent move
+// paths. For example, this generates two errors:
+//
+// let (&x, &y) = (&String::new(), &String::new());
+#[derive(Debug)]
+enum GroupedMoveError<'tcx> {
+ // Place expression can't be moved from,
+ // e.g., match x[0] { s => (), } where x: &[String]
+ MovesFromPlace {
+ original_path: Place<'tcx>,
+ span: Span,
+ move_from: Place<'tcx>,
+ kind: IllegalMoveOriginKind<'tcx>,
+ binds_to: Vec<Local>,
+ },
+ // Part of a value expression can't be moved from,
+ // e.g., match &String::new() { &x => (), }
+ MovesFromValue {
+ original_path: Place<'tcx>,
+ span: Span,
+ move_from: MovePathIndex,
+ kind: IllegalMoveOriginKind<'tcx>,
+ binds_to: Vec<Local>,
+ },
+ // Everything that isn't from pattern matching.
+ OtherIllegalMove {
+ original_path: Place<'tcx>,
+ use_spans: UseSpans,
+ kind: IllegalMoveOriginKind<'tcx>,
+ },
+}
+
+impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
+ pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) {
+ let grouped_errors = self.group_move_errors(move_errors);
+ for error in grouped_errors {
+ self.report(error);
+ }
+ }
+
+ fn group_move_errors(
+ &self,
+ errors: Vec<(Place<'tcx>, MoveError<'tcx>)>
+ ) -> Vec<GroupedMoveError<'tcx>> {
+ let mut grouped_errors = Vec::new();
+ for (original_path, error) in errors {
+ self.append_to_grouped_errors(&mut grouped_errors, original_path, error);
+ }
+ grouped_errors
+ }
+
+ fn append_to_grouped_errors(
+ &self,
+ grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
+ original_path: Place<'tcx>,
+ error: MoveError<'tcx>,
+ ) {
+ match error {
+ MoveError::UnionMove { .. } => {
+ unimplemented!("don't know how to report union move errors yet.")
+ }
+ MoveError::IllegalMove {
+ cannot_move_out_of: IllegalMoveOrigin { location, kind },
+ } => {
+ // Note: that the only time we assign a place isn't a temporary
+ // to a user variable is when initializing it.
+ // If that ever stops being the case, then the ever initialized
+ // flow could be used.
+ if let Some(StatementKind::Assign(
+ box(place, Rvalue::Use(Operand::Move(move_from)))
+ )) = self.body.basic_blocks()[location.block]
+ .statements
+ .get(location.statement_index)
+ .map(|stmt| &stmt.kind)
+ {
+ if let Some(local) = place.as_local() {
+ let local_decl = &self.body.local_decls[local];
+ // opt_match_place is the
+ // match_span is the span of the expression being matched on
+ // match *x.y { ... } match_place is Some(*x.y)
+ // ^^^^ match_span is the span of *x.y
+ //
+ // opt_match_place is None for let [mut] x = ... statements,
+ // whether or not the right-hand side is a place expression
+ if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
+ VarBindingForm {
+ opt_match_place: Some((ref opt_match_place, match_span)),
+ binding_mode: _,
+ opt_ty_info: _,
+ pat_span: _,
+ },
+ ))) = local_decl.local_info {
+ let stmt_source_info = self.body.source_info(location);
+ self.append_binding_error(
+ grouped_errors,
+ kind,
+ original_path,
+ move_from,
+ local,
+ opt_match_place,
+ match_span,
+ stmt_source_info.span,
+ );
+ return;
+ }
+ }
+ }
+
+ let move_spans = self.move_spans(original_path.as_ref(), location);
+ grouped_errors.push(GroupedMoveError::OtherIllegalMove {
+ use_spans: move_spans,
+ original_path,
+ kind,
+ });
+ }
+ }
+ }
+
+ fn append_binding_error(
+ &self,
+ grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
+ kind: IllegalMoveOriginKind<'tcx>,
+ original_path: Place<'tcx>,
+ move_from: &Place<'tcx>,
+ bind_to: Local,
+ match_place: &Option<Place<'tcx>>,
+ match_span: Span,
+ statement_span: Span,
+ ) {
+ debug!(
+ "append_binding_error(match_place={:?}, match_span={:?})",
+ match_place, match_span
+ );
+
+ let from_simple_let = match_place.is_none();
+ let match_place = match_place.as_ref().unwrap_or(move_from);
+
+ match self.move_data.rev_lookup.find(match_place.as_ref()) {
+ // Error with the match place
+ LookupResult::Parent(_) => {
+ for ge in &mut *grouped_errors {
+ if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge {
+ if match_span == *span {
+ debug!("appending local({:?}) to list", bind_to);
+ if !binds_to.is_empty() {
+ binds_to.push(bind_to);
+ }
+ return;
+ }
+ }
+ }
+ debug!("found a new move error location");
+
+ // Don't need to point to x in let x = ... .
+ let (binds_to, span) = if from_simple_let {
+ (vec![], statement_span)
+ } else {
+ (vec![bind_to], match_span)
+ };
+ grouped_errors.push(GroupedMoveError::MovesFromPlace {
+ span,
+ move_from: match_place.clone(),
+ original_path,
+ kind,
+ binds_to,
+ });
+ }
+ // Error with the pattern
+ LookupResult::Exact(_) => {
+ let mpi = match self.move_data.rev_lookup.find(move_from.as_ref()) {
+ LookupResult::Parent(Some(mpi)) => mpi,
+ // move_from should be a projection from match_place.
+ _ => unreachable!("Probably not unreachable..."),
+ };
+ for ge in &mut *grouped_errors {
+ if let GroupedMoveError::MovesFromValue {
+ span,
+ move_from: other_mpi,
+ binds_to,
+ ..
+ } = ge
+ {
+ if match_span == *span && mpi == *other_mpi {
+ debug!("appending local({:?}) to list", bind_to);
+ binds_to.push(bind_to);
+ return;
+ }
+ }
+ }
+ debug!("found a new move error location");
+ grouped_errors.push(GroupedMoveError::MovesFromValue {
+ span: match_span,
+ move_from: mpi,
+ original_path,
+ kind,
+ binds_to: vec![bind_to],
+ });
+ }
+ };
+ }
+
+ fn report(&mut self, error: GroupedMoveError<'tcx>) {
+ let (mut err, err_span) = {
+ let (span, original_path, kind): (Span, &Place<'tcx>, &IllegalMoveOriginKind<'_>) =
+ match error {
+ GroupedMoveError::MovesFromPlace { span, ref original_path, ref kind, .. } |
+ GroupedMoveError::MovesFromValue { span, ref original_path, ref kind, .. } => {
+ (span, original_path, kind)
+ }
+ GroupedMoveError::OtherIllegalMove {
+ use_spans,
+ ref original_path,
+ ref kind
+ } => {
+ (use_spans.args_or_use(), original_path, kind)
+ },
+ };
+ debug!("report: original_path={:?} span={:?}, kind={:?} \
+ original_path.is_upvar_field_projection={:?}", original_path, span, kind,
+ self.is_upvar_field_projection(original_path.as_ref()));
+ (
+ match kind {
+ IllegalMoveOriginKind::Static => {
+ unreachable!();
+ }
+ IllegalMoveOriginKind::BorrowedContent { target_place } => {
+ self.report_cannot_move_from_borrowed_content(
+ original_path,
+ target_place,
+ span,
+ )
+ }
+ IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
+ self.cannot_move_out_of_interior_of_drop(span, ty)
+ }
+ IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } =>
+ self.cannot_move_out_of_interior_noncopy(
+ span, ty, Some(*is_index),
+ ),
+ },
+ span,
+ )
+ };
+
+ self.add_move_hints(error, &mut err, err_span);
+ err.buffer(&mut self.errors_buffer);
+ }
+
+ fn report_cannot_move_from_static(
+ &mut self,
+ place: &Place<'tcx>,
+ span: Span
+ ) -> DiagnosticBuilder<'a> {
+ let description = if place.projection.len() == 1 {
+ format!("static item `{}`", self.describe_place(place.as_ref()).unwrap())
+ } else {
+ let base_static = PlaceRef {
+ base: &place.base,
+ projection: &[ProjectionElem::Deref],
+ };
+
+ format!(
+ "`{:?}` as `{:?}` is a static item",
+ self.describe_place(place.as_ref()).unwrap(),
+ self.describe_place(base_static).unwrap(),
+ )
+ };
+
+ self.cannot_move_out_of(span, &description)
+ }
+
+ fn report_cannot_move_from_borrowed_content(
+ &mut self,
+ move_place: &Place<'tcx>,
+ deref_target_place: &Place<'tcx>,
+ span: Span,
+ ) -> DiagnosticBuilder<'a> {
+ // Inspect the type of the content behind the
+ // borrow to provide feedback about why this
+ // was a move rather than a copy.
+ let ty = deref_target_place.ty(*self.body, self.infcx.tcx).ty;
+ let upvar_field = self.prefixes(move_place.as_ref(), PrefixSet::All)
+ .find_map(|p| self.is_upvar_field_projection(p));
+
+ let deref_base = match deref_target_place.projection.as_ref() {
+ &[ref proj_base @ .., ProjectionElem::Deref] => {
+ PlaceRef {
+ base: &deref_target_place.base,
+ projection: &proj_base,
+ }
+ }
+ _ => bug!("deref_target_place is not a deref projection"),
+ };
+
+ if let PlaceRef {
+ base: PlaceBase::Local(local),
+ projection: [],
+ } = deref_base {
+ let decl = &self.body.local_decls[*local];
+ if decl.is_ref_for_guard() {
+ let mut err = self.cannot_move_out_of(
+ span,
+ &format!("`{}` in pattern guard", self.local_names[*local].unwrap()),
+ );
+ err.note(
+ "variables bound in patterns cannot be moved from \
+ until after the end of the pattern guard");
+ return err;
+ } else if decl.is_ref_to_static() {
+ return self.report_cannot_move_from_static(move_place, span);
+ }
+ }
+
+ debug!("report: ty={:?}", ty);
+ let mut err = match ty.kind {
+ ty::Array(..) | ty::Slice(..) =>
+ self.cannot_move_out_of_interior_noncopy(span, ty, None),
+ ty::Closure(def_id, closure_substs)
+ if def_id == self.mir_def_id && upvar_field.is_some()
+ => {
+ let closure_kind_ty = closure_substs
+ .as_closure().kind_ty(def_id, self.infcx.tcx);
+ let closure_kind = closure_kind_ty.to_opt_closure_kind();
+ let capture_description = match closure_kind {
+ Some(ty::ClosureKind::Fn) => {
+ "captured variable in an `Fn` closure"
+ }
+ Some(ty::ClosureKind::FnMut) => {
+ "captured variable in an `FnMut` closure"
+ }
+ Some(ty::ClosureKind::FnOnce) => {
+ bug!("closure kind does not match first argument type")
+ }
+ None => bug!("closure kind not inferred by borrowck"),
+ };
+
+ let upvar = &self.upvars[upvar_field.unwrap().index()];
+ let upvar_hir_id = upvar.var_hir_id;
+ let upvar_name = upvar.name;
+ let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id);
+
+ let place_name = self.describe_place(move_place.as_ref()).unwrap();
+
+ let place_description = if self
+ .is_upvar_field_projection(move_place.as_ref())
+ .is_some()
+ {
+ format!("`{}`, a {}", place_name, capture_description)
+ } else {
+ format!(
+ "`{}`, as `{}` is a {}",
+ place_name,
+ upvar_name,
+ capture_description,
+ )
+ };
+
+ debug!(
+ "report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}",
+ closure_kind_ty, closure_kind, place_description,
+ );
+
+ let mut diag = self.cannot_move_out_of(span, &place_description);
+
+ diag.span_label(upvar_span, "captured outer variable");
+
+ diag
+ }
+ _ => {
+ let source = self.borrowed_content_source(deref_base);
+ match (
+ self.describe_place(move_place.as_ref()),
+ source.describe_for_named_place(),
+ ) {
+ (Some(place_desc), Some(source_desc)) => {
+ self.cannot_move_out_of(
+ span,
+ &format!("`{}` which is behind a {}", place_desc, source_desc),
+ )
+ }
+ (_, _) => {
+ self.cannot_move_out_of(
+ span,
+ &source.describe_for_unnamed_place(),
+ )
+ }
+ }
+ },
+ };
+ let move_ty = format!(
+ "{:?}",
+ move_place.ty(*self.body, self.infcx.tcx).ty,
+ );
+ if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
+ let is_option = move_ty.starts_with("std::option::Option");
+ let is_result = move_ty.starts_with("std::result::Result");
+ if is_option || is_result {
+ err.span_suggestion(
+ span,
+ &format!("consider borrowing the `{}`'s content", if is_option {
+ "Option"
+ } else {
+ "Result"
+ }),
+ format!("{}.as_ref()", snippet),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ err
+ }
+
+ fn add_move_hints(
+ &self,
+ error: GroupedMoveError<'tcx>,
+ err: &mut DiagnosticBuilder<'a>,
+ span: Span,
+ ) {
+ match error {
+ GroupedMoveError::MovesFromPlace {
+ mut binds_to,
+ move_from,
+ ..
+ } => {
+ if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
+ err.span_suggestion(
+ span,
+ "consider borrowing here",
+ format!("&{}", snippet),
+ Applicability::Unspecified,
+ );
+ }
+
+ if binds_to.is_empty() {
+ let place_ty = move_from.ty(*self.body, self.infcx.tcx).ty;
+ let place_desc = match self.describe_place(move_from.as_ref()) {
+ Some(desc) => format!("`{}`", desc),
+ None => format!("value"),
+ };
+
+ self.note_type_does_not_implement_copy(
+ err,
+ &place_desc,
+ place_ty,
+ Some(span)
+ );
+ } else {
+ binds_to.sort();
+ binds_to.dedup();
+
+ self.add_move_error_details(err, &binds_to);
+ }
+ }
+ GroupedMoveError::MovesFromValue { mut binds_to, .. } => {
+ binds_to.sort();
+ binds_to.dedup();
+ self.add_move_error_suggestions(err, &binds_to);
+ self.add_move_error_details(err, &binds_to);
+ }
+ // No binding. Nothing to suggest.
+ GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
+ let span = use_spans.var_or_use();
+ let place_ty = original_path.ty(*self.body, self.infcx.tcx).ty;
+ let place_desc = match self.describe_place(original_path.as_ref()) {
+ Some(desc) => format!("`{}`", desc),
+ None => format!("value"),
+ };
+ self.note_type_does_not_implement_copy(
+ err,
+ &place_desc,
+ place_ty,
+ Some(span),
+ );
+
+ use_spans.args_span_label(err, format!("move out of {} occurs here", place_desc));
+ use_spans.var_span_label(
+ err,
+ format!("move occurs due to use{}", use_spans.describe()),
+ );
+ },
+ }
+ }
+
+ fn add_move_error_suggestions(
+ &self,
+ err: &mut DiagnosticBuilder<'a>,
+ binds_to: &[Local],
+ ) {
+ let mut suggestions: Vec<(Span, &str, String)> = Vec::new();
+ for local in binds_to {
+ let bind_to = &self.body.local_decls[*local];
+ if let LocalInfo::User(
+ ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+ pat_span,
+ ..
+ }))
+ ) = bind_to.local_info {
+ if let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span)
+ {
+ if pat_snippet.starts_with('&') {
+ let pat_snippet = pat_snippet[1..].trim_start();
+ let suggestion;
+ let to_remove;
+ if pat_snippet.starts_with("mut")
+ && pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace)
+ {
+ suggestion = pat_snippet["mut".len()..].trim_start();
+ to_remove = "&mut";
+ } else {
+ suggestion = pat_snippet;
+ to_remove = "&";
+ }
+ suggestions.push((
+ pat_span,
+ to_remove,
+ suggestion.to_owned(),
+ ));
+ }
+ }
+ }
+ }
+ suggestions.sort_unstable_by_key(|&(span, _, _)| span);
+ suggestions.dedup_by_key(|&mut (span, _, _)| span);
+ for (span, to_remove, suggestion) in suggestions {
+ err.span_suggestion(
+ span,
+ &format!("consider removing the `{}`", to_remove),
+ suggestion,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
+ fn add_move_error_details(
+ &self,
+ err: &mut DiagnosticBuilder<'a>,
+ binds_to: &[Local],
+ ) {
+ for (j, local) in binds_to.into_iter().enumerate() {
+ let bind_to = &self.body.local_decls[*local];
+ let binding_span = bind_to.source_info.span;
+
+ if j == 0 {
+ err.span_label(binding_span, "data moved here");
+ } else {
+ err.span_label(binding_span, "...and here");
+ }
+
+ if binds_to.len() == 1 {
+ self.note_type_does_not_implement_copy(
+ err,
+ &format!("`{}`", self.local_names[*local].unwrap()),
+ bind_to.ty,
+ Some(binding_span)
+ );
+ }
+ }
+
+ if binds_to.len() > 1 {
+ err.note("move occurs because these variables have types that \
+ don't implement the `Copy` trait",
+ );
+ }
+ }
+}
--- /dev/null
+use rustc::hir;
+use rustc::hir::Node;
+use rustc::mir::{self, ClearCrossCrate, Local, LocalInfo, Location, ReadOnlyBodyCache};
+use rustc::mir::{Mutability, Place, PlaceRef, PlaceBase, ProjectionElem};
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc_index::vec::Idx;
+use syntax_pos::Span;
+use syntax_pos::symbol::kw;
+
+use crate::borrow_check::MirBorrowckCtxt;
+use crate::borrow_check::diagnostics::BorrowedContentSource;
+use crate::util::collect_writes::FindAssignments;
+use rustc_errors::Applicability;
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub(crate) enum AccessKind {
+ MutableBorrow,
+ Mutate,
+}
+
+impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
+ pub(crate) fn report_mutability_error(
+ &mut self,
+ access_place: &Place<'tcx>,
+ span: Span,
+ the_place_err: PlaceRef<'cx, 'tcx>,
+ error_access: AccessKind,
+ location: Location,
+ ) {
+ debug!(
+ "report_mutability_error(\
+ access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
+ )",
+ access_place, span, the_place_err, error_access, location,
+ );
+
+ let mut err;
+ let item_msg;
+ let reason;
+ let mut opt_source = None;
+ let access_place_desc = self.describe_place(access_place.as_ref());
+ debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
+
+ match the_place_err {
+ PlaceRef {
+ base: PlaceBase::Local(local),
+ projection: [],
+ } => {
+ item_msg = format!("`{}`", access_place_desc.unwrap());
+ if access_place.as_local().is_some() {
+ reason = ", as it is not declared as mutable".to_string();
+ } else {
+ let name = self.local_names[*local]
+ .expect("immutable unnamed local");
+ reason = format!(", as `{}` is not declared as mutable", name);
+ }
+ }
+
+ PlaceRef {
+ base: _,
+ projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
+ } => {
+ debug_assert!(is_closure_or_generator(
+ Place::ty_from(
+ &the_place_err.base,
+ proj_base,
+ *self.body,
+ self.infcx.tcx
+ ).ty));
+
+ item_msg = format!("`{}`", access_place_desc.unwrap());
+ if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
+ reason = ", as it is not declared as mutable".to_string();
+ } else {
+ let name = self.upvars[upvar_index.index()].name;
+ reason = format!(", as `{}` is not declared as mutable", name);
+ }
+ }
+
+ PlaceRef {
+ base: &PlaceBase::Local(local),
+ projection: [ProjectionElem::Deref],
+ } if self.body.local_decls[local].is_ref_for_guard() => {
+ item_msg = format!("`{}`", access_place_desc.unwrap());
+ reason = ", as it is immutable for the pattern guard".to_string();
+ }
+ PlaceRef {
+ base: &PlaceBase::Local(local),
+ projection: [ProjectionElem::Deref],
+ } if self.body.local_decls[local].is_ref_to_static() => {
+ if access_place.projection.len() == 1 {
+ item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
+ reason = String::new();
+ } else {
+ item_msg = format!("`{}`", access_place_desc.unwrap());
+ let local_info = &self.body.local_decls[local].local_info;
+ if let LocalInfo::StaticRef { def_id, .. } = *local_info {
+ let static_name = &self.infcx.tcx.item_name(def_id);
+ reason = format!(", as `{}` is an immutable static item", static_name);
+ } else {
+ bug!("is_ref_to_static return true, but not ref to static?");
+ }
+ }
+ }
+ PlaceRef {
+ base: _,
+ projection: [proj_base @ .., ProjectionElem::Deref],
+ } => {
+ if the_place_err.base == &PlaceBase::Local(Local::new(1)) &&
+ proj_base.is_empty() &&
+ !self.upvars.is_empty() {
+ item_msg = format!("`{}`", access_place_desc.unwrap());
+ debug_assert!(self.body.local_decls[Local::new(1)].ty.is_region_ptr());
+ debug_assert!(is_closure_or_generator(
+ Place::ty_from(
+ the_place_err.base,
+ the_place_err.projection,
+ *self.body,
+ self.infcx.tcx
+ )
+ .ty
+ ));
+
+ reason =
+ if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
+ ", as it is a captured variable in a `Fn` closure".to_string()
+ } else {
+ ", as `Fn` closures cannot mutate their captured variables".to_string()
+ }
+ } else {
+ let source = self.borrowed_content_source(PlaceRef {
+ base: the_place_err.base,
+ projection: proj_base,
+ });
+ let pointer_type = source.describe_for_immutable_place();
+ opt_source = Some(source);
+ if let Some(desc) = access_place_desc {
+ item_msg = format!("`{}`", desc);
+ reason = match error_access {
+ AccessKind::Mutate => format!(" which is behind {}", pointer_type),
+ AccessKind::MutableBorrow => {
+ format!(", as it is behind {}", pointer_type)
+ }
+ }
+ } else {
+ item_msg = format!("data in {}", pointer_type);
+ reason = String::new();
+ }
+ }
+ }
+
+ PlaceRef {
+ base: PlaceBase::Static(_),
+ ..
+ }
+ | PlaceRef {
+ base: _,
+ projection: [.., ProjectionElem::Index(_)],
+ }
+ | PlaceRef {
+ base: _,
+ projection: [.., ProjectionElem::ConstantIndex { .. }],
+ }
+ | PlaceRef {
+ base: _,
+ projection: [.., ProjectionElem::Subslice { .. }],
+ }
+ | PlaceRef {
+ base: _,
+ projection: [.., ProjectionElem::Downcast(..)],
+ } => bug!("Unexpected immutable place."),
+ }
+
+ debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
+
+ // `act` and `acted_on` are strings that let us abstract over
+ // the verbs used in some diagnostic messages.
+ let act;
+ let acted_on;
+
+ let span = match error_access {
+ AccessKind::Mutate => {
+ err = self.cannot_assign(span, &(item_msg + &reason));
+ act = "assign";
+ acted_on = "written";
+ span
+ }
+ AccessKind::MutableBorrow => {
+ act = "borrow as mutable";
+ acted_on = "borrowed as mutable";
+
+ let borrow_spans = self.borrow_spans(span, location);
+ let borrow_span = borrow_spans.args_or_use();
+ err = self.cannot_borrow_path_as_mutable_because(
+ borrow_span,
+ &item_msg,
+ &reason,
+ );
+ borrow_spans.var_span_label(
+ &mut err,
+ format!(
+ "mutable borrow occurs due to use of `{}` in closure",
+ // always Some() if the message is printed.
+ self.describe_place(access_place.as_ref()).unwrap_or_default(),
+ )
+ );
+ borrow_span
+ }
+ };
+
+ debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
+
+ match the_place_err {
+ // Suggest making an existing shared borrow in a struct definition a mutable borrow.
+ //
+ // This is applicable when we have a deref of a field access to a deref of a local -
+ // something like `*((*_1).0`. The local that we get will be a reference to the
+ // struct we've got a field access of (it must be a reference since there's a deref
+ // after the field access).
+ PlaceRef {
+ base,
+ projection: [proj_base @ ..,
+ ProjectionElem::Deref,
+ ProjectionElem::Field(field, _),
+ ProjectionElem::Deref,
+ ],
+ } => {
+ err.span_label(span, format!("cannot {ACT}", ACT = act));
+
+ if let Some((span, message)) = annotate_struct_field(
+ self.infcx.tcx,
+ Place::ty_from(base, proj_base, *self.body, self.infcx.tcx).ty,
+ field,
+ ) {
+ err.span_suggestion(
+ span,
+ "consider changing this to be mutable",
+ message,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ },
+
+ // Suggest removing a `&mut` from the use of a mutable reference.
+ PlaceRef {
+ base: PlaceBase::Local(local),
+ projection: [],
+ } if {
+ self.body.local_decls.get(*local).map(|local_decl| {
+ if let LocalInfo::User(ClearCrossCrate::Set(
+ mir::BindingForm::ImplicitSelf(kind)
+ )) = local_decl.local_info {
+ // Check if the user variable is a `&mut self` and we can therefore
+ // suggest removing the `&mut`.
+ //
+ // Deliberately fall into this case for all implicit self types,
+ // so that we don't fall in to the next case with them.
+ kind == mir::ImplicitSelfKind::MutRef
+ } else if Some(kw::SelfLower) == self.local_names[*local] {
+ // Otherwise, check if the name is the self kewyord - in which case
+ // we have an explicit self. Do the same thing in this case and check
+ // for a `self: &mut Self` to suggest removing the `&mut`.
+ if let ty::Ref(
+ _, _, hir::Mutability::Mutable
+ ) = local_decl.ty.kind {
+ true
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }).unwrap_or(false)
+ } => {
+ err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_label(span, "try removing `&mut` here");
+ },
+
+ // We want to suggest users use `let mut` for local (user
+ // variable) mutations...
+ PlaceRef {
+ base: PlaceBase::Local(local),
+ projection: [],
+ } if self.body.local_decls[*local].can_be_made_mutable() => {
+ // ... but it doesn't make sense to suggest it on
+ // variables that are `ref x`, `ref mut x`, `&self`,
+ // or `&mut self` (such variables are simply not
+ // mutable).
+ let local_decl = &self.body.local_decls[*local];
+ assert_eq!(local_decl.mutability, Mutability::Not);
+
+ err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_suggestion(
+ local_decl.source_info.span,
+ "consider changing this to be mutable",
+ format!("mut {}", self.local_names[*local].unwrap()),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ // Also suggest adding mut for upvars
+ PlaceRef {
+ base,
+ projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
+ } => {
+ debug_assert!(is_closure_or_generator(
+ Place::ty_from(base, proj_base, *self.body, self.infcx.tcx).ty
+ ));
+
+ err.span_label(span, format!("cannot {ACT}", ACT = act));
+
+ let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id;
+ if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
+ {
+ if let hir::PatKind::Binding(
+ hir::BindingAnnotation::Unannotated,
+ _,
+ upvar_ident,
+ _,
+ ) = pat.kind
+ {
+ err.span_suggestion(
+ upvar_ident.span,
+ "consider changing this to be mutable",
+ format!("mut {}", upvar_ident.name),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+
+ // complete hack to approximate old AST-borrowck
+ // diagnostic: if the span starts with a mutable borrow of
+ // a local variable, then just suggest the user remove it.
+ PlaceRef {
+ base: PlaceBase::Local(_),
+ projection: [],
+ } if {
+ if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
+ snippet.starts_with("&mut ")
+ } else {
+ false
+ }
+ } =>
+ {
+ err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_label(span, "try removing `&mut` here");
+ }
+
+ PlaceRef {
+ base: PlaceBase::Local(local),
+ projection: [ProjectionElem::Deref],
+ } if self.body.local_decls[*local].is_ref_for_guard() => {
+ err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.note(
+ "variables bound in patterns are immutable until the end of the pattern guard",
+ );
+ }
+
+ // We want to point out when a `&` can be readily replaced
+ // with an `&mut`.
+ //
+ // FIXME: can this case be generalized to work for an
+ // arbitrary base for the projection?
+ PlaceRef {
+ base: PlaceBase::Local(local),
+ projection: [ProjectionElem::Deref],
+ } if self.body.local_decls[*local].is_user_variable() =>
+ {
+ let local_decl = &self.body.local_decls[*local];
+ let suggestion = match local_decl.local_info {
+ LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(_))) => {
+ Some(suggest_ampmut_self(self.infcx.tcx, local_decl))
+ }
+
+ LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
+ mir::VarBindingForm {
+ binding_mode: ty::BindingMode::BindByValue(_),
+ opt_ty_info,
+ ..
+ },
+ ))) => Some(suggest_ampmut(
+ self.infcx.tcx,
+ self.body,
+ *local,
+ local_decl,
+ opt_ty_info,
+ )),
+
+ LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
+ mir::VarBindingForm {
+ binding_mode: ty::BindingMode::BindByReference(_),
+ ..
+ },
+ ))) => {
+ let pattern_span = local_decl.source_info.span;
+ suggest_ref_mut(self.infcx.tcx, pattern_span)
+ .map(|replacement| (pattern_span, replacement))
+ }
+
+ LocalInfo::User(ClearCrossCrate::Clear) => bug!("saw cleared local state"),
+
+ _ => unreachable!(),
+ };
+
+ let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
+ ("&", "reference")
+ } else {
+ ("*const", "pointer")
+ };
+
+ if let Some((err_help_span, suggested_code)) = suggestion {
+ err.span_suggestion(
+ err_help_span,
+ &format!("consider changing this to be a mutable {}", pointer_desc),
+ suggested_code,
+ Applicability::MachineApplicable,
+ );
+ }
+
+ match self.local_names[*local] {
+ Some(name) if !local_decl.from_compiler_desugaring() => {
+ err.span_label(
+ span,
+ format!(
+ "`{NAME}` is a `{SIGIL}` {DESC}, \
+ so the data it refers to cannot be {ACTED_ON}",
+ NAME = name,
+ SIGIL = pointer_sigil,
+ DESC = pointer_desc,
+ ACTED_ON = acted_on
+ ),
+ );
+ }
+ _ => {
+ err.span_label(
+ span,
+ format!(
+ "cannot {ACT} through `{SIGIL}` {DESC}",
+ ACT = act,
+ SIGIL = pointer_sigil,
+ DESC = pointer_desc
+ ),
+ );
+ }
+ }
+ }
+
+ PlaceRef {
+ base,
+ projection: [ProjectionElem::Deref],
+ // FIXME document what is this 1 magic number about
+ } if *base == PlaceBase::Local(Local::new(1)) &&
+ !self.upvars.is_empty() =>
+ {
+ err.span_label(span, format!("cannot {ACT}", ACT = act));
+ err.span_help(
+ self.body.span,
+ "consider changing this to accept closures that implement `FnMut`"
+ );
+ }
+
+ PlaceRef {
+ base: _,
+ projection: [.., ProjectionElem::Deref],
+ } => {
+ err.span_label(span, format!("cannot {ACT}", ACT = act));
+
+ match opt_source {
+ Some(BorrowedContentSource::OverloadedDeref(ty)) => {
+ err.help(
+ &format!(
+ "trait `DerefMut` is required to modify through a dereference, \
+ but it is not implemented for `{}`",
+ ty,
+ ),
+ );
+ },
+ Some(BorrowedContentSource::OverloadedIndex(ty)) => {
+ err.help(
+ &format!(
+ "trait `IndexMut` is required to modify indexed content, \
+ but it is not implemented for `{}`",
+ ty,
+ ),
+ );
+ }
+ _ => (),
+ }
+ }
+
+ _ => {
+ err.span_label(span, format!("cannot {ACT}", ACT = act));
+ }
+ }
+
+ err.buffer(&mut self.errors_buffer);
+ }
+}
+
+fn suggest_ampmut_self<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ local_decl: &mir::LocalDecl<'tcx>,
+) -> (Span, String) {
+ let sp = local_decl.source_info.span;
+ (sp, match tcx.sess.source_map().span_to_snippet(sp) {
+ Ok(snippet) => {
+ let lt_pos = snippet.find('\'');
+ if let Some(lt_pos) = lt_pos {
+ format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
+ } else {
+ "&mut self".to_string()
+ }
+ }
+ _ => "&mut self".to_string()
+ })
+}
+
+// When we want to suggest a user change a local variable to be a `&mut`, there
+// are three potential "obvious" things to highlight:
+//
+// let ident [: Type] [= RightHandSideExpression];
+// ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
+// (1.) (2.) (3.)
+//
+// We can always fallback on highlighting the first. But chances are good that
+// the user experience will be better if we highlight one of the others if possible;
+// for example, if the RHS is present and the Type is not, then the type is going to
+// be inferred *from* the RHS, which means we should highlight that (and suggest
+// that they borrow the RHS mutably).
+//
+// This implementation attempts to emulate AST-borrowck prioritization
+// by trying (3.), then (2.) and finally falling back on (1.).
+fn suggest_ampmut<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: ReadOnlyBodyCache<'_, 'tcx>,
+ local: Local,
+ local_decl: &mir::LocalDecl<'tcx>,
+ opt_ty_info: Option<Span>,
+) -> (Span, String) {
+ let locations = body.find_assignments(local);
+ if !locations.is_empty() {
+ let assignment_rhs_span = body.source_info(locations[0]).span;
+ if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) {
+ if let (true, Some(ws_pos)) = (
+ src.starts_with("&'"),
+ src.find(|c: char| -> bool { c.is_whitespace() }),
+ ) {
+ let lt_name = &src[1..ws_pos];
+ let ty = &src[ws_pos..];
+ return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
+ } else if src.starts_with('&') {
+ let borrowed_expr = &src[1..];
+ return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
+ }
+ }
+ }
+
+ let highlight_span = match opt_ty_info {
+ // if this is a variable binding with an explicit type,
+ // try to highlight that for the suggestion.
+ Some(ty_span) => ty_span,
+
+ // otherwise, just highlight the span associated with
+ // the (MIR) LocalDecl.
+ None => local_decl.source_info.span,
+ };
+
+ if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) {
+ if let (true, Some(ws_pos)) = (
+ src.starts_with("&'"),
+ src.find(|c: char| -> bool { c.is_whitespace() }),
+ ) {
+ let lt_name = &src[1..ws_pos];
+ let ty = &src[ws_pos..];
+ return (highlight_span, format!("&{} mut{}", lt_name, ty));
+ }
+ }
+
+ let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
+ assert_eq!(ty_mut.mutbl, hir::Mutability::Immutable);
+ (highlight_span,
+ if local_decl.ty.is_region_ptr() {
+ format!("&mut {}", ty_mut.ty)
+ } else {
+ format!("*mut {}", ty_mut.ty)
+ })
+}
+
+fn is_closure_or_generator(ty: Ty<'_>) -> bool {
+ ty.is_closure() || ty.is_generator()
+}
+
+/// Adds a suggestion to a struct definition given a field access to a local.
+/// This function expects the local to be a reference to a struct in order to produce a suggestion.
+///
+/// ```text
+/// LL | s: &'a String
+/// | ---------- use `&'a mut String` here to make mutable
+/// ```
+fn annotate_struct_field(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ field: &mir::Field,
+) -> Option<(Span, String)> {
+ // Expect our local to be a reference to a struct of some kind.
+ if let ty::Ref(_, ty, _) = ty.kind {
+ if let ty::Adt(def, _) = ty.kind {
+ let field = def.all_fields().nth(field.index())?;
+ // Use the HIR types to construct the diagnostic message.
+ let hir_id = tcx.hir().as_local_hir_id(field.did)?;
+ let node = tcx.hir().find(hir_id)?;
+ // Now we're dealing with the actual struct that we're going to suggest a change to,
+ // we can expect a field that is an immutable reference to a type.
+ if let hir::Node::Field(field) = node {
+ if let hir::TyKind::Rptr(lifetime, hir::MutTy {
+ mutbl: hir::Mutability::Immutable,
+ ref ty
+ }) = field.ty.kind {
+ // Get the snippets in two parts - the named lifetime (if there is one) and
+ // type being referenced, that way we can reconstruct the snippet without loss
+ // of detail.
+ let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
+ let lifetime_snippet = if !lifetime.is_elided() {
+ format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
+ } else {
+ String::new()
+ };
+
+ return Some((
+ field.ty.span,
+ format!(
+ "&{}mut {}",
+ lifetime_snippet, &*type_snippet,
+ ),
+ ));
+ }
+ }
+ }
+ }
+
+ None
+}
+
+/// If possible, suggest replacing `ref` with `ref mut`.
+fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
+ let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
+ if hi_src.starts_with("ref")
+ && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace)
+ {
+ let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
+ Some(replacement)
+ } else {
+ None
+ }
+}
--- /dev/null
+//! Contains utilities for generating suggestions for borrowck errors related to unsatisified
+//! outlives constraints.
+
+use std::collections::BTreeMap;
+
+use log::debug;
+use rustc::{hir::def_id::DefId, infer::InferCtxt, ty::RegionVid};
+use rustc::mir::{Body, Local};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{Diagnostic, DiagnosticBuilder};
+use rustc_index::vec::IndexVec;
+use syntax_pos::symbol::Symbol;
+
+use smallvec::SmallVec;
+
+use crate::borrow_check::nll::region_infer::RegionInferenceContext;
+
+use super::{
+ RegionName, RegionNameSource, ErrorConstraintInfo, ErrorReportingCtx, RegionErrorNamingCtx,
+};
+
+/// The different things we could suggest.
+enum SuggestedConstraint {
+ /// Outlives(a, [b, c, d, ...]) => 'a: 'b + 'c + 'd + ...
+ Outlives(RegionName, SmallVec<[RegionName; 2]>),
+
+ /// 'a = 'b
+ Equal(RegionName, RegionName),
+
+ /// 'a: 'static i.e. 'a = 'static and the user should just use 'static
+ Static(RegionName),
+}
+
+/// Collects information about outlives constraints that needed to be added for a given MIR node
+/// corresponding to a function definition.
+///
+/// Adds a help note suggesting adding a where clause with the needed constraints.
+pub struct OutlivesSuggestionBuilder<'a> {
+ /// The MIR DefId of the fn with the lifetime error.
+ mir_def_id: DefId,
+
+ local_names: &'a IndexVec<Local, Option<Symbol>>,
+
+ /// The list of outlives constraints that need to be added. Specifically, we map each free
+ /// region to all other regions that it must outlive. I will use the shorthand `fr:
+ /// outlived_frs`. Not all of these regions will already have names necessarily. Some could be
+ /// implicit free regions that we inferred. These will need to be given names in the final
+ /// suggestion message.
+ constraints_to_add: BTreeMap<RegionVid, Vec<RegionVid>>,
+}
+
+impl OutlivesSuggestionBuilder<'a> {
+ /// Create a new builder for the given MIR node representing a fn definition.
+ crate fn new(
+ mir_def_id: DefId,
+ local_names: &'a IndexVec<Local, Option<Symbol>>,
+ ) -> Self {
+ OutlivesSuggestionBuilder {
+ mir_def_id,
+ local_names,
+ constraints_to_add: BTreeMap::default(),
+ }
+ }
+
+ /// Returns `true` iff the `RegionNameSource` is a valid source for an outlives
+ /// suggestion.
+ //
+ // FIXME: Currently, we only report suggestions if the `RegionNameSource` is an early-bound
+ // region or a named region, avoiding using regions with synthetic names altogether. This
+ // allows us to avoid giving impossible suggestions (e.g. adding bounds to closure args).
+ // We can probably be less conservative, since some inferred free regions are namable (e.g.
+ // the user can explicitly name them. To do this, we would allow some regions whose names
+ // come from `MatchedAdtAndSegment`, being careful to filter out bad suggestions, such as
+ // naming the `'self` lifetime in methods, etc.
+ fn region_name_is_suggestable(name: &RegionName) -> bool {
+ match name.source {
+ RegionNameSource::NamedEarlyBoundRegion(..)
+ | RegionNameSource::NamedFreeRegion(..)
+ | RegionNameSource::Static => true,
+
+ // Don't give suggestions for upvars, closure return types, or other unnamable
+ // regions.
+ RegionNameSource::SynthesizedFreeEnvRegion(..)
+ | RegionNameSource::CannotMatchHirTy(..)
+ | RegionNameSource::MatchedHirTy(..)
+ | RegionNameSource::MatchedAdtAndSegment(..)
+ | RegionNameSource::AnonRegionFromUpvar(..)
+ | RegionNameSource::AnonRegionFromOutput(..)
+ | RegionNameSource::AnonRegionFromYieldTy(..)
+ | RegionNameSource::AnonRegionFromAsyncFn(..) => {
+ debug!("Region {:?} is NOT suggestable", name);
+ false
+ }
+ }
+ }
+
+ /// Returns a name for the region if it is suggestable. See `region_name_is_suggestable`.
+ fn region_vid_to_name(
+ &self,
+ errctx: &ErrorReportingCtx<'_, '_, '_>,
+ renctx: &mut RegionErrorNamingCtx,
+ region: RegionVid,
+ ) -> Option<RegionName> {
+ errctx
+ .region_infcx
+ .give_region_a_name(errctx, renctx, region)
+ .filter(Self::region_name_is_suggestable)
+ }
+
+ /// Compiles a list of all suggestions to be printed in the final big suggestion.
+ fn compile_all_suggestions<'tcx>(
+ &self,
+ body: &Body<'tcx>,
+ region_infcx: &RegionInferenceContext<'tcx>,
+ infcx: &InferCtxt<'_, 'tcx>,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> SmallVec<[SuggestedConstraint; 2]> {
+ let mut suggested = SmallVec::new();
+
+ // Keep track of variables that we have already suggested unifying so that we don't print
+ // out silly duplicate messages.
+ let mut unified_already = FxHashSet::default();
+
+ let errctx = ErrorReportingCtx {
+ region_infcx,
+ infcx,
+ body,
+ mir_def_id: self.mir_def_id,
+ local_names: self.local_names,
+
+ // We should not be suggesting naming upvars, so we pass in a dummy set of upvars that
+ // should never be used.
+ upvars: &[],
+ };
+
+ for (fr, outlived) in &self.constraints_to_add {
+ let fr_name = if let Some(fr_name) = self.region_vid_to_name(&errctx, renctx, *fr) {
+ fr_name
+ } else {
+ continue;
+ };
+
+ let outlived = outlived
+ .iter()
+ // if there is a `None`, we will just omit that constraint
+ .filter_map(|fr| {
+ self.region_vid_to_name(&errctx, renctx, *fr).map(|rname| (fr, rname))
+ })
+ .collect::<Vec<_>>();
+
+ // No suggestable outlived lifetimes.
+ if outlived.is_empty() {
+ continue;
+ }
+
+ // There are three types of suggestions we can make:
+ // 1) Suggest a bound: 'a: 'b
+ // 2) Suggest replacing 'a with 'static. If any of `outlived` is `'static`, then we
+ // should just replace 'a with 'static.
+ // 3) Suggest unifying 'a with 'b if we have both 'a: 'b and 'b: 'a
+
+ if outlived.iter().any(|(_, outlived_name)| {
+ if let RegionNameSource::Static = outlived_name.source {
+ true
+ } else {
+ false
+ }
+ }) {
+ suggested.push(SuggestedConstraint::Static(fr_name));
+ } else {
+ // We want to isolate out all lifetimes that should be unified and print out
+ // separate messages for them.
+
+ let (unified, other): (Vec<_>, Vec<_>) = outlived.into_iter().partition(
+ // Do we have both 'fr: 'r and 'r: 'fr?
+ |(r, _)| {
+ self.constraints_to_add
+ .get(r)
+ .map(|r_outlived| r_outlived.as_slice().contains(fr))
+ .unwrap_or(false)
+ },
+ );
+
+ for (r, bound) in unified.into_iter() {
+ if !unified_already.contains(fr) {
+ suggested.push(SuggestedConstraint::Equal(fr_name.clone(), bound));
+ unified_already.insert(r);
+ }
+ }
+
+ if !other.is_empty() {
+ let other =
+ other.iter().map(|(_, rname)| rname.clone()).collect::<SmallVec<_>>();
+ suggested.push(SuggestedConstraint::Outlives(fr_name, other))
+ }
+ }
+ }
+
+ suggested
+ }
+
+ /// Add the outlives constraint `fr: outlived_fr` to the set of constraints we need to suggest.
+ crate fn collect_constraint(&mut self, fr: RegionVid, outlived_fr: RegionVid) {
+ debug!("Collected {:?}: {:?}", fr, outlived_fr);
+
+ // Add to set of constraints for final help note.
+ self.constraints_to_add.entry(fr).or_insert(Vec::new()).push(outlived_fr);
+ }
+
+ /// Emit an intermediate note on the given `Diagnostic` if the involved regions are
+ /// suggestable.
+ crate fn intermediate_suggestion(
+ &mut self,
+ errctx: &ErrorReportingCtx<'_, '_, '_>,
+ errci: &ErrorConstraintInfo,
+ renctx: &mut RegionErrorNamingCtx,
+ diag: &mut DiagnosticBuilder<'_>,
+ ) {
+ // Emit an intermediate note.
+ let fr_name = self.region_vid_to_name(errctx, renctx, errci.fr);
+ let outlived_fr_name = self.region_vid_to_name(errctx, renctx, errci.outlived_fr);
+
+ if let (Some(fr_name), Some(outlived_fr_name)) = (fr_name, outlived_fr_name) {
+ if let RegionNameSource::Static = outlived_fr_name.source {
+ diag.help(&format!("consider replacing `{}` with `'static`", fr_name));
+ } else {
+ diag.help(&format!(
+ "consider adding the following bound: `{}: {}`",
+ fr_name, outlived_fr_name
+ ));
+ }
+ }
+ }
+
+ /// If there is a suggestion to emit, add a diagnostic to the buffer. This is the final
+ /// suggestion including all collected constraints.
+ crate fn add_suggestion<'tcx>(
+ &self,
+ body: &Body<'tcx>,
+ region_infcx: &RegionInferenceContext<'tcx>,
+ infcx: &InferCtxt<'_, 'tcx>,
+ errors_buffer: &mut Vec<Diagnostic>,
+ renctx: &mut RegionErrorNamingCtx,
+ ) {
+ // No constraints to add? Done.
+ if self.constraints_to_add.is_empty() {
+ debug!("No constraints to suggest.");
+ return;
+ }
+
+ // If there is only one constraint to suggest, then we already suggested it in the
+ // intermediate suggestion above.
+ if self.constraints_to_add.len() == 1 {
+ debug!("Only 1 suggestion. Skipping.");
+ return;
+ }
+
+ // Get all suggestable constraints.
+ let suggested = self.compile_all_suggestions(body, region_infcx, infcx, renctx);
+
+ // If there are no suggestable constraints...
+ if suggested.is_empty() {
+ debug!("Only 1 suggestable constraint. Skipping.");
+ return;
+ }
+
+ // If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a
+ // list of diagnostics.
+ let mut diag = if suggested.len() == 1 {
+ infcx.tcx.sess.diagnostic().struct_help(&match suggested.last().unwrap() {
+ SuggestedConstraint::Outlives(a, bs) => {
+ let bs: SmallVec<[String; 2]> = bs.iter().map(|r| format!("{}", r)).collect();
+ format!("add bound `{}: {}`", a, bs.join(" + "))
+ }
+
+ SuggestedConstraint::Equal(a, b) => {
+ format!("`{}` and `{}` must be the same: replace one with the other", a, b)
+ }
+ SuggestedConstraint::Static(a) => format!("replace `{}` with `'static`", a),
+ })
+ } else {
+ // Create a new diagnostic.
+ let mut diag = infcx
+ .tcx
+ .sess
+ .diagnostic()
+ .struct_help("the following changes may resolve your lifetime errors");
+
+ // Add suggestions.
+ for constraint in suggested {
+ match constraint {
+ SuggestedConstraint::Outlives(a, bs) => {
+ let bs: SmallVec<[String; 2]> =
+ bs.iter().map(|r| format!("{}", r)).collect();
+ diag.help(&format!("add bound `{}: {}`", a, bs.join(" + ")));
+ }
+ SuggestedConstraint::Equal(a, b) => {
+ diag.help(&format!(
+ "`{}` and `{}` must be the same: replace one with the other",
+ a, b
+ ));
+ }
+ SuggestedConstraint::Static(a) => {
+ diag.help(&format!("replace `{}` with `'static`", a));
+ }
+ }
+ }
+
+ diag
+ };
+
+ // We want this message to appear after other messages on the mir def.
+ let mir_span = infcx.tcx.def_span(self.mir_def_id);
+ diag.sort_span = mir_span.shrink_to_hi();
+
+ // Buffer the diagnostic
+ diag.buffer(errors_buffer);
+ }
+}
--- /dev/null
+use crate::borrow_check::nll::constraints::OutlivesConstraint;
+use crate::borrow_check::nll::region_infer::RegionInferenceContext;
+use crate::borrow_check::nll::type_check::Locations;
+use crate::borrow_check::nll::universal_regions::DefiningTy;
+use crate::borrow_check::nll::ConstraintDescription;
+use crate::borrow_check::Upvar;
+use crate::util::borrowck_errors;
+use rustc::hir::def_id::DefId;
+use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
+use rustc::infer::InferCtxt;
+use rustc::infer::NLLRegionVariableOrigin;
+use rustc::mir::{ConstraintCategory, Local, Location, Body};
+use rustc::ty::{self, RegionVid};
+use rustc_index::vec::IndexVec;
+use rustc_errors::DiagnosticBuilder;
+use std::collections::VecDeque;
+use syntax::errors::Applicability;
+use syntax::symbol::kw;
+use syntax_pos::Span;
+use syntax_pos::symbol::Symbol;
+
+use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource, RegionErrorNamingCtx};
+
+impl ConstraintDescription for ConstraintCategory {
+ fn description(&self) -> &'static str {
+ // Must end with a space. Allows for empty names to be provided.
+ match self {
+ ConstraintCategory::Assignment => "assignment ",
+ ConstraintCategory::Return => "returning this value ",
+ ConstraintCategory::Yield => "yielding this value ",
+ ConstraintCategory::UseAsConst => "using this value as a constant ",
+ ConstraintCategory::UseAsStatic => "using this value as a static ",
+ ConstraintCategory::Cast => "cast ",
+ ConstraintCategory::CallArgument => "argument ",
+ ConstraintCategory::TypeAnnotation => "type annotation ",
+ ConstraintCategory::ClosureBounds => "closure body ",
+ ConstraintCategory::SizedBound => "proving this value is `Sized` ",
+ ConstraintCategory::CopyBound => "copying this value ",
+ ConstraintCategory::OpaqueType => "opaque type ",
+ ConstraintCategory::Boring
+ | ConstraintCategory::BoringNoLocation
+ | ConstraintCategory::Internal => "",
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+enum Trace {
+ StartRegion,
+ FromOutlivesConstraint(OutlivesConstraint),
+ NotVisited,
+}
+
+/// Various pieces of state used when reporting borrow checker errors.
+pub struct ErrorReportingCtx<'a, 'b, 'tcx> {
+ /// The region inference context used for borrow chekcing this MIR body.
+ pub(super) region_infcx: &'b RegionInferenceContext<'tcx>,
+
+ /// The inference context used for type checking.
+ pub(super) infcx: &'b InferCtxt<'a, 'tcx>,
+
+ /// The MIR def we are reporting errors on.
+ pub(super) mir_def_id: DefId,
+
+ /// The MIR body we are reporting errors on (for convenience).
+ pub(super) body: &'b Body<'tcx>,
+
+ /// User variable names for MIR locals (where applicable).
+ pub(super) local_names: &'b IndexVec<Local, Option<Symbol>>,
+
+ /// Any upvars for the MIR body we have kept track of during borrow checking.
+ pub(super) upvars: &'b [Upvar],
+}
+
+/// Information about the various region constraints involved in a borrow checker error.
+#[derive(Clone, Debug)]
+pub struct ErrorConstraintInfo {
+ // fr: outlived_fr
+ pub(super) fr: RegionVid,
+ pub(super) fr_is_local: bool,
+ pub(super) outlived_fr: RegionVid,
+ pub(super) outlived_fr_is_local: bool,
+
+ // Category and span for best blame constraint
+ pub(super) category: ConstraintCategory,
+ pub(super) span: Span,
+}
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+ /// Tries to find the best constraint to blame for the fact that
+ /// `R: from_region`, where `R` is some region that meets
+ /// `target_test`. This works by following the constraint graph,
+ /// creating a constraint path that forces `R` to outlive
+ /// `from_region`, and then finding the best choices within that
+ /// path to blame.
+ fn best_blame_constraint(
+ &self,
+ body: &Body<'tcx>,
+ from_region: RegionVid,
+ from_region_origin: NLLRegionVariableOrigin,
+ target_test: impl Fn(RegionVid) -> bool,
+ ) -> (ConstraintCategory, bool, Span) {
+ debug!("best_blame_constraint(from_region={:?}, from_region_origin={:?})",
+ from_region, from_region_origin);
+
+ // Find all paths
+ let (path, target_region) =
+ self.find_constraint_paths_between_regions(from_region, target_test)
+ .unwrap();
+ debug!(
+ "best_blame_constraint: path={:#?}",
+ path.iter()
+ .map(|&c| format!(
+ "{:?} ({:?}: {:?})",
+ c,
+ self.constraint_sccs.scc(c.sup),
+ self.constraint_sccs.scc(c.sub),
+ ))
+ .collect::<Vec<_>>()
+ );
+
+ // Classify each of the constraints along the path.
+ let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path.iter()
+ .map(|constraint| {
+ if constraint.category == ConstraintCategory::ClosureBounds {
+ self.retrieve_closure_constraint_info(body, &constraint)
+ } else {
+ (constraint.category, false, constraint.locations.span(body))
+ }
+ })
+ .collect();
+ debug!(
+ "best_blame_constraint: categorized_path={:#?}",
+ categorized_path
+ );
+
+ // To find the best span to cite, we first try to look for the
+ // final constraint that is interesting and where the `sup` is
+ // not unified with the ultimate target region. The reason
+ // for this is that we have a chain of constraints that lead
+ // from the source to the target region, something like:
+ //
+ // '0: '1 ('0 is the source)
+ // '1: '2
+ // '2: '3
+ // '3: '4
+ // '4: '5
+ // '5: '6 ('6 is the target)
+ //
+ // Some of those regions are unified with `'6` (in the same
+ // SCC). We want to screen those out. After that point, the
+ // "closest" constraint we have to the end is going to be the
+ // most likely to be the point where the value escapes -- but
+ // we still want to screen for an "interesting" point to
+ // highlight (e.g., a call site or something).
+ let target_scc = self.constraint_sccs.scc(target_region);
+ let mut range = 0..path.len();
+
+ // As noted above, when reporting an error, there is typically a chain of constraints
+ // leading from some "source" region which must outlive some "target" region.
+ // In most cases, we prefer to "blame" the constraints closer to the target --
+ // but there is one exception. When constraints arise from higher-ranked subtyping,
+ // we generally prefer to blame the source value,
+ // as the "target" in this case tends to be some type annotation that the user gave.
+ // Therefore, if we find that the region origin is some instantiation
+ // of a higher-ranked region, we start our search from the "source" point
+ // rather than the "target", and we also tweak a few other things.
+ //
+ // An example might be this bit of Rust code:
+ //
+ // ```rust
+ // let x: fn(&'static ()) = |_| {};
+ // let y: for<'a> fn(&'a ()) = x;
+ // ```
+ //
+ // In MIR, this will be converted into a combination of assignments and type ascriptions.
+ // In particular, the 'static is imposed through a type ascription:
+ //
+ // ```rust
+ // x = ...;
+ // AscribeUserType(x, fn(&'static ())
+ // y = x;
+ // ```
+ //
+ // We wind up ultimately with constraints like
+ //
+ // ```rust
+ // !a: 'temp1 // from the `y = x` statement
+ // 'temp1: 'temp2
+ // 'temp2: 'static // from the AscribeUserType
+ // ```
+ //
+ // and here we prefer to blame the source (the y = x statement).
+ let blame_source = match from_region_origin {
+ NLLRegionVariableOrigin::FreeRegion
+ | NLLRegionVariableOrigin::Existential { from_forall: false } => {
+ true
+ }
+ NLLRegionVariableOrigin::Placeholder(_)
+ | NLLRegionVariableOrigin::Existential { from_forall: true } => {
+ false
+ }
+ };
+
+ let find_region = |i: &usize| {
+ let constraint = path[*i];
+
+ let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
+
+ if blame_source {
+ match categorized_path[*i].0 {
+ ConstraintCategory::OpaqueType | ConstraintCategory::Boring |
+ ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false,
+ ConstraintCategory::TypeAnnotation | ConstraintCategory::Return |
+ ConstraintCategory::Yield => true,
+ _ => constraint_sup_scc != target_scc,
+ }
+ } else {
+ match categorized_path[*i].0 {
+ ConstraintCategory::OpaqueType | ConstraintCategory::Boring |
+ ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false,
+ _ => true
+ }
+ }
+ };
+
+ let best_choice = if blame_source {
+ range.rev().find(find_region)
+ } else {
+ range.find(find_region)
+ };
+
+ debug!("best_blame_constraint: best_choice={:?} blame_source={}",
+ best_choice, blame_source);
+
+ if let Some(i) = best_choice {
+ if let Some(next) = categorized_path.get(i + 1) {
+ if categorized_path[i].0 == ConstraintCategory::Return
+ && next.0 == ConstraintCategory::OpaqueType
+ {
+ // The return expression is being influenced by the return type being
+ // impl Trait, point at the return type and not the return expr.
+ return *next;
+ }
+ }
+ return categorized_path[i];
+ }
+
+ // If that search fails, that is.. unusual. Maybe everything
+ // is in the same SCC or something. In that case, find what
+ // appears to be the most interesting point to report to the
+ // user via an even more ad-hoc guess.
+ categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
+ debug!("`: sorted_path={:#?}", categorized_path);
+
+ *categorized_path.first().unwrap()
+ }
+
+ /// Walks the graph of constraints (where `'a: 'b` is considered
+ /// an edge `'a -> 'b`) to find all paths from `from_region` to
+ /// `to_region`. The paths are accumulated into the vector
+ /// `results`. The paths are stored as a series of
+ /// `ConstraintIndex` values -- in other words, a list of *edges*.
+ ///
+ /// Returns: a series of constraints as well as the region `R`
+ /// that passed the target test.
+ fn find_constraint_paths_between_regions(
+ &self,
+ from_region: RegionVid,
+ target_test: impl Fn(RegionVid) -> bool,
+ ) -> Option<(Vec<OutlivesConstraint>, RegionVid)> {
+ let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
+ context[from_region] = Trace::StartRegion;
+
+ // Use a deque so that we do a breadth-first search. We will
+ // stop at the first match, which ought to be the shortest
+ // path (fewest constraints).
+ let mut deque = VecDeque::new();
+ deque.push_back(from_region);
+
+ while let Some(r) = deque.pop_front() {
+ debug!(
+ "find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
+ from_region,
+ r,
+ self.region_value_str(r),
+ );
+
+ // Check if we reached the region we were looking for. If so,
+ // we can reconstruct the path that led to it and return it.
+ if target_test(r) {
+ let mut result = vec![];
+ let mut p = r;
+ loop {
+ match context[p] {
+ Trace::NotVisited => {
+ bug!("found unvisited region {:?} on path to {:?}", p, r)
+ }
+
+ Trace::FromOutlivesConstraint(c) => {
+ result.push(c);
+ p = c.sup;
+ }
+
+ Trace::StartRegion => {
+ result.reverse();
+ return Some((result, r));
+ }
+ }
+ }
+ }
+
+ // Otherwise, walk over the outgoing constraints and
+ // enqueue any regions we find, keeping track of how we
+ // reached them.
+
+ // A constraint like `'r: 'x` can come from our constraint
+ // graph.
+ let fr_static = self.universal_regions.fr_static;
+ let outgoing_edges_from_graph = self.constraint_graph
+ .outgoing_edges(r, &self.constraints, fr_static);
+
+ // Always inline this closure because it can be hot.
+ let mut handle_constraint = #[inline(always)] |constraint: OutlivesConstraint| {
+ debug_assert_eq!(constraint.sup, r);
+ let sub_region = constraint.sub;
+ if let Trace::NotVisited = context[sub_region] {
+ context[sub_region] = Trace::FromOutlivesConstraint(constraint);
+ deque.push_back(sub_region);
+ }
+ };
+
+ // This loop can be hot.
+ for constraint in outgoing_edges_from_graph {
+ handle_constraint(constraint);
+ }
+
+ // Member constraints can also give rise to `'r: 'x` edges that
+ // were not part of the graph initially, so watch out for those.
+ // (But they are extremely rare; this loop is very cold.)
+ for constraint in self.applied_member_constraints(r) {
+ let p_c = &self.member_constraints[constraint.member_constraint_index];
+ let constraint = OutlivesConstraint {
+ sup: r,
+ sub: constraint.min_choice,
+ locations: Locations::All(p_c.definition_span),
+ category: ConstraintCategory::OpaqueType,
+ };
+ handle_constraint(constraint);
+ }
+ }
+
+ None
+ }
+
+ /// Report an error because the universal region `fr` was required to outlive
+ /// `outlived_fr` but it is not known to do so. For example:
+ ///
+ /// ```
+ /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
+ /// ```
+ ///
+ /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
+ pub(in crate::borrow_check) fn report_error<'a>(
+ &'a self,
+ body: &Body<'tcx>,
+ local_names: &IndexVec<Local, Option<Symbol>>,
+ upvars: &[Upvar],
+ infcx: &'a InferCtxt<'a, 'tcx>,
+ mir_def_id: DefId,
+ fr: RegionVid,
+ fr_origin: NLLRegionVariableOrigin,
+ outlived_fr: RegionVid,
+ outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> DiagnosticBuilder<'a> {
+ debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
+
+ let (category, _, span) = self.best_blame_constraint(body, fr, fr_origin, |r| {
+ self.provides_universal_region(r, fr, outlived_fr)
+ });
+
+ debug!("report_error: category={:?} {:?}", category, span);
+ // Check if we can use one of the "nice region errors".
+ if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
+ let tables = infcx.tcx.typeck_tables_of(mir_def_id);
+ let nice = NiceRegionError::new_from_span(infcx, span, o, f, Some(tables));
+ if let Some(diag) = nice.try_report_from_nll() {
+ return diag;
+ }
+ }
+
+ let (fr_is_local, outlived_fr_is_local): (bool, bool) = (
+ self.universal_regions.is_local_free_region(fr),
+ self.universal_regions.is_local_free_region(outlived_fr),
+ );
+
+ debug!(
+ "report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
+ fr_is_local, outlived_fr_is_local, category
+ );
+
+ let errctx = ErrorReportingCtx {
+ region_infcx: self,
+ infcx,
+ mir_def_id,
+ body,
+ local_names,
+ upvars,
+ };
+
+ let errci = ErrorConstraintInfo {
+ fr, outlived_fr, fr_is_local, outlived_fr_is_local, category, span
+ };
+
+ match (category, fr_is_local, outlived_fr_is_local) {
+ (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => {
+ self.report_fnmut_error(&errctx, &errci, renctx)
+ }
+ (ConstraintCategory::Assignment, true, false)
+ | (ConstraintCategory::CallArgument, true, false) => {
+ let mut db = self.report_escaping_data_error(&errctx, &errci, renctx);
+
+ outlives_suggestion.intermediate_suggestion(&errctx, &errci, renctx, &mut db);
+ outlives_suggestion.collect_constraint(fr, outlived_fr);
+
+ db
+ }
+ _ => {
+ let mut db = self.report_general_error(&errctx, &errci, renctx);
+
+ outlives_suggestion.intermediate_suggestion(&errctx, &errci, renctx, &mut db);
+ outlives_suggestion.collect_constraint(fr, outlived_fr);
+
+ db
+ }
+ }
+ }
+
+ /// We have a constraint `fr1: fr2` that is not satisfied, where
+ /// `fr2` represents some universal region. Here, `r` is some
+ /// region where we know that `fr1: r` and this function has the
+ /// job of determining whether `r` is "to blame" for the fact that
+ /// `fr1: fr2` is required.
+ ///
+ /// This is true under two conditions:
+ ///
+ /// - `r == fr2`
+ /// - `fr2` is `'static` and `r` is some placeholder in a universe
+ /// that cannot be named by `fr1`; in that case, we will require
+ /// that `fr1: 'static` because it is the only way to `fr1: r` to
+ /// be satisfied. (See `add_incompatible_universe`.)
+ fn provides_universal_region(&self, r: RegionVid, fr1: RegionVid, fr2: RegionVid) -> bool {
+ debug!(
+ "provides_universal_region(r={:?}, fr1={:?}, fr2={:?})",
+ r, fr1, fr2
+ );
+ let result = {
+ r == fr2 || {
+ fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r)
+ }
+ };
+ debug!("provides_universal_region: result = {:?}", result);
+ result
+ }
+
+ /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
+ /// This function expects `fr` to be local and `outlived_fr` to not be local.
+ ///
+ /// ```text
+ /// error: captured variable cannot escape `FnMut` closure body
+ /// --> $DIR/issue-53040.rs:15:8
+ /// |
+ /// LL | || &mut v;
+ /// | -- ^^^^^^ creates a reference to a captured variable which escapes the closure body
+ /// | |
+ /// | inferred to be a `FnMut` closure
+ /// |
+ /// = note: `FnMut` closures only have access to their captured variables while they are
+ /// executing...
+ /// = note: ...therefore, returned references to captured variables will escape the closure
+ /// ```
+ fn report_fnmut_error(
+ &self,
+ errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
+ errci: &ErrorConstraintInfo,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> DiagnosticBuilder<'_> {
+ let ErrorConstraintInfo {
+ outlived_fr, span, ..
+ } = errci;
+
+ let mut diag = errctx
+ .infcx
+ .tcx
+ .sess
+ .struct_span_err(*span, "captured variable cannot escape `FnMut` closure body");
+
+ // We should check if the return type of this closure is in fact a closure - in that
+ // case, we can special case the error further.
+ let return_type_is_closure = self.universal_regions.unnormalized_output_ty.is_closure();
+ let message = if return_type_is_closure {
+ "returns a closure that contains a reference to a captured variable, which then \
+ escapes the closure body"
+ } else {
+ "returns a reference to a captured variable which escapes the closure body"
+ };
+
+ diag.span_label(*span, message);
+
+ match self.give_region_a_name(errctx, renctx, *outlived_fr).unwrap().source {
+ RegionNameSource::NamedEarlyBoundRegion(fr_span)
+ | RegionNameSource::NamedFreeRegion(fr_span)
+ | RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _)
+ | RegionNameSource::CannotMatchHirTy(fr_span, _)
+ | RegionNameSource::MatchedHirTy(fr_span)
+ | RegionNameSource::MatchedAdtAndSegment(fr_span)
+ | RegionNameSource::AnonRegionFromUpvar(fr_span, _)
+ | RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => {
+ diag.span_label(fr_span, "inferred to be a `FnMut` closure");
+ }
+ _ => {}
+ }
+
+ diag.note(
+ "`FnMut` closures only have access to their captured variables while they are \
+ executing...",
+ );
+ diag.note("...therefore, they cannot allow references to captured variables to escape");
+
+ diag
+ }
+
+ /// Reports a error specifically for when data is escaping a closure.
+ ///
+ /// ```text
+ /// error: borrowed data escapes outside of function
+ /// --> $DIR/lifetime-bound-will-change-warning.rs:44:5
+ /// |
+ /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) {
+ /// | - `x` is a reference that is only valid in the function body
+ /// LL | // but ref_obj will not, so warn.
+ /// LL | ref_obj(x)
+ /// | ^^^^^^^^^^ `x` escapes the function body here
+ /// ```
+ fn report_escaping_data_error(
+ &self,
+ errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
+ errci: &ErrorConstraintInfo,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> DiagnosticBuilder<'_> {
+ let ErrorReportingCtx {
+ infcx, body, upvars, local_names, ..
+ } = errctx;
+
+ let ErrorConstraintInfo {
+ span, category, ..
+ } = errci;
+
+ let fr_name_and_span =
+ self.get_var_name_and_span_for_region(infcx.tcx, body, local_names, upvars, errci.fr);
+ let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
+ infcx.tcx,
+ body,
+ local_names,
+ upvars,
+ errci.outlived_fr,
+ );
+
+ let escapes_from = match self.universal_regions.defining_ty {
+ DefiningTy::Closure(..) => "closure",
+ DefiningTy::Generator(..) => "generator",
+ DefiningTy::FnDef(..) => "function",
+ DefiningTy::Const(..) => "const",
+ };
+
+ // Revert to the normal error in these cases.
+ // Assignments aren't "escapes" in function items.
+ if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none())
+ || (*category == ConstraintCategory::Assignment && escapes_from == "function")
+ || escapes_from == "const"
+ {
+ return self.report_general_error(
+ errctx,
+ &ErrorConstraintInfo {
+ fr_is_local: true,
+ outlived_fr_is_local: false,
+ .. *errci
+ },
+ renctx,
+ );
+ }
+
+ let mut diag = borrowck_errors::borrowed_data_escapes_closure(
+ infcx.tcx,
+ *span,
+ escapes_from,
+ );
+
+ if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
+ diag.span_label(
+ outlived_fr_span,
+ format!(
+ "`{}` is declared here, outside of the {} body",
+ outlived_fr_name, escapes_from
+ ),
+ );
+ }
+
+ if let Some((Some(fr_name), fr_span)) = fr_name_and_span {
+ diag.span_label(
+ fr_span,
+ format!(
+ "`{}` is a reference that is only valid in the {} body",
+ fr_name, escapes_from
+ ),
+ );
+
+ diag.span_label(
+ *span,
+ format!("`{}` escapes the {} body here", fr_name, escapes_from),
+ );
+ }
+
+ diag
+ }
+
+ /// Reports a region inference error for the general case with named/synthesized lifetimes to
+ /// explain what is happening.
+ ///
+ /// ```text
+ /// error: unsatisfied lifetime constraints
+ /// --> $DIR/regions-creating-enums3.rs:17:5
+ /// |
+ /// LL | fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> {
+ /// | -- -- lifetime `'b` defined here
+ /// | |
+ /// | lifetime `'a` defined here
+ /// LL | ast::add(x, y)
+ /// | ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
+ /// | is returning data with lifetime `'b`
+ /// ```
+ fn report_general_error(
+ &self,
+ errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
+ errci: &ErrorConstraintInfo,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> DiagnosticBuilder<'_> {
+ let ErrorReportingCtx {
+ infcx, mir_def_id, ..
+ } = errctx;
+ let ErrorConstraintInfo {
+ fr, fr_is_local, outlived_fr, outlived_fr_is_local, span, category, ..
+ } = errci;
+
+ let mut diag = infcx.tcx.sess.struct_span_err(
+ *span,
+ "lifetime may not live long enough"
+ );
+
+ let mir_def_name = if infcx.tcx.is_closure(*mir_def_id) {
+ "closure"
+ } else {
+ "function"
+ };
+
+ let fr_name = self.give_region_a_name(errctx, renctx, *fr).unwrap();
+ fr_name.highlight_region_name(&mut diag);
+ let outlived_fr_name = self.give_region_a_name(errctx, renctx, *outlived_fr).unwrap();
+ outlived_fr_name.highlight_region_name(&mut diag);
+
+ match (category, outlived_fr_is_local, fr_is_local) {
+ (ConstraintCategory::Return, true, _) => {
+ diag.span_label(
+ *span,
+ format!(
+ "{} was supposed to return data with lifetime `{}` but it is returning \
+ data with lifetime `{}`",
+ mir_def_name, outlived_fr_name, fr_name
+ ),
+ );
+ }
+ _ => {
+ diag.span_label(
+ *span,
+ format!(
+ "{}requires that `{}` must outlive `{}`",
+ category.description(),
+ fr_name,
+ outlived_fr_name,
+ ),
+ );
+ }
+ }
+
+ self.add_static_impl_trait_suggestion(infcx, &mut diag, *fr, fr_name, *outlived_fr);
+
+ diag
+ }
+
+ /// Adds a suggestion to errors where a `impl Trait` is returned.
+ ///
+ /// ```text
+ /// help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as
+ /// a constraint
+ /// |
+ /// LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
+ /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ /// ```
+ fn add_static_impl_trait_suggestion(
+ &self,
+ infcx: &InferCtxt<'_, 'tcx>,
+ diag: &mut DiagnosticBuilder<'_>,
+ fr: RegionVid,
+ // We need to pass `fr_name` - computing it again will label it twice.
+ fr_name: RegionName,
+ outlived_fr: RegionVid,
+ ) {
+ if let (Some(f), Some(ty::RegionKind::ReStatic)) =
+ (self.to_error_region(fr), self.to_error_region(outlived_fr))
+ {
+ if let Some((ty::TyS {
+ kind: ty::Opaque(did, substs),
+ ..
+ }, _)) = infcx
+ .tcx
+ .is_suitable_region(f)
+ .map(|r| r.def_id)
+ .map(|id| infcx.tcx.return_type_impl_trait(id))
+ .unwrap_or(None)
+ {
+ // Check whether or not the impl trait return type is intended to capture
+ // data with the static lifetime.
+ //
+ // eg. check for `impl Trait + 'static` instead of `impl Trait`.
+ let has_static_predicate = {
+ let predicates_of = infcx.tcx.predicates_of(*did);
+ let bounds = predicates_of.instantiate(infcx.tcx, substs);
+
+ let mut found = false;
+ for predicate in bounds.predicates {
+ if let ty::Predicate::TypeOutlives(binder) = predicate {
+ if let ty::OutlivesPredicate(_, ty::RegionKind::ReStatic) =
+ binder.skip_binder()
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ found
+ };
+
+ debug!(
+ "add_static_impl_trait_suggestion: has_static_predicate={:?}",
+ has_static_predicate
+ );
+ let static_str = kw::StaticLifetime;
+ // If there is a static predicate, then the only sensible suggestion is to replace
+ // fr with `'static`.
+ if has_static_predicate {
+ diag.help(&format!(
+ "consider replacing `{}` with `{}`",
+ fr_name, static_str,
+ ));
+ } else {
+ // Otherwise, we should suggest adding a constraint on the return type.
+ let span = infcx.tcx.def_span(*did);
+ if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
+ let suggestable_fr_name = if fr_name.was_named() {
+ fr_name.to_string()
+ } else {
+ "'_".to_string()
+ };
+
+ diag.span_suggestion(
+ span,
+ &format!(
+ "to allow this `impl Trait` to capture borrowed data with lifetime \
+ `{}`, add `{}` as a constraint",
+ fr_name, suggestable_fr_name,
+ ),
+ format!("{} + {}", snippet, suggestable_fr_name),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+ }
+ }
+
+ crate fn free_region_constraint_info(
+ &self,
+ body: &Body<'tcx>,
+ local_names: &IndexVec<Local, Option<Symbol>>,
+ upvars: &[Upvar],
+ mir_def_id: DefId,
+ infcx: &InferCtxt<'_, 'tcx>,
+ borrow_region: RegionVid,
+ outlived_region: RegionVid,
+ ) -> (ConstraintCategory, bool, Span, Option<RegionName>) {
+ let (category, from_closure, span) = self.best_blame_constraint(
+ body,
+ borrow_region,
+ NLLRegionVariableOrigin::FreeRegion,
+ |r| self.provides_universal_region(r, borrow_region, outlived_region)
+ );
+
+ let mut renctx = RegionErrorNamingCtx::new();
+ let errctx = ErrorReportingCtx {
+ infcx, body, local_names, upvars, mir_def_id,
+ region_infcx: self,
+ };
+ let outlived_fr_name = self.give_region_a_name(&errctx, &mut renctx, outlived_region);
+
+ (category, from_closure, span, outlived_fr_name)
+ }
+
+ // Finds some region R such that `fr1: R` and `R` is live at
+ // `elem`.
+ crate fn find_sub_region_live_at(
+ &self,
+ fr1: RegionVid,
+ elem: Location,
+ ) -> RegionVid {
+ debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
+ self.find_constraint_paths_between_regions(fr1, |r| {
+ // First look for some `r` such that `fr1: r` and `r` is live at `elem`
+ debug!(
+ "find_sub_region_live_at: liveness_constraints for {:?} are {:?}",
+ r,
+ self.liveness_constraints.region_value_str(r),
+ );
+ self.liveness_constraints.contains(r, elem)
+ }).or_else(|| {
+ // If we fail to find that, we may find some `r` such that
+ // `fr1: r` and `r` is a placeholder from some universe
+ // `fr1` cannot name. This would force `fr1` to be
+ // `'static`.
+ self.find_constraint_paths_between_regions(fr1, |r| {
+ self.cannot_name_placeholder(fr1, r)
+ })
+ })
+ .or_else(|| {
+ // If we fail to find THAT, it may be that `fr1` is a
+ // placeholder that cannot "fit" into its SCC. In that
+ // case, there should be some `r` where `fr1: r`, both
+ // `fr1` and `r` are in the same SCC, and `fr1` is a
+ // placeholder that `r` cannot name. We can blame that
+ // edge.
+ self.find_constraint_paths_between_regions(fr1, |r| {
+ self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r)
+ && self.cannot_name_placeholder(r, fr1)
+ })
+ })
+ .map(|(_path, r)| r)
+ .unwrap()
+ }
+
+ // Finds a good span to blame for the fact that `fr1` outlives `fr2`.
+ crate fn find_outlives_blame_span(
+ &self,
+ body: &Body<'tcx>,
+ fr1: RegionVid,
+ fr1_origin: NLLRegionVariableOrigin,
+ fr2: RegionVid,
+ ) -> (ConstraintCategory, Span) {
+ let (category, _, span) = self.best_blame_constraint(
+ body,
+ fr1,
+ fr1_origin,
+ |r| self.provides_universal_region(r, fr1, fr2),
+ );
+ (category, span)
+ }
+
+ fn retrieve_closure_constraint_info(
+ &self,
+ body: &Body<'tcx>,
+ constraint: &OutlivesConstraint,
+ ) -> (ConstraintCategory, bool, Span) {
+ let loc = match constraint.locations {
+ Locations::All(span) => return (constraint.category, false, span),
+ Locations::Single(loc) => loc,
+ };
+
+ let opt_span_category =
+ self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub));
+ opt_span_category
+ .map(|&(category, span)| (category, true, span))
+ .unwrap_or((constraint.category, false, body.source_info(loc).span))
+ }
+
+ /// Returns `true` if a closure is inferred to be an `FnMut` closure.
+ crate fn is_closure_fn_mut(&self, infcx: &InferCtxt<'_, 'tcx>, fr: RegionVid) -> bool {
+ if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
+ if let ty::BoundRegion::BrEnv = free_region.bound_region {
+ if let DefiningTy::Closure(def_id, substs) = self.universal_regions.defining_ty {
+ let closure_kind_ty = substs.as_closure().kind_ty(def_id, infcx.tcx);
+ return Some(ty::ClosureKind::FnMut) == closure_kind_ty.to_opt_closure_kind();
+ }
+ }
+ }
+
+ false
+ }
+
+ /// If `r2` represents a placeholder region, then this returns
+ /// `true` if `r1` cannot name that placeholder in its
+ /// value; otherwise, returns `false`.
+ fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
+ debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2);
+
+ match self.definitions[r2].origin {
+ NLLRegionVariableOrigin::Placeholder(placeholder) => {
+ let universe1 = self.definitions[r1].universe;
+ debug!(
+ "cannot_name_value_of: universe1={:?} placeholder={:?}",
+ universe1, placeholder
+ );
+ universe1.cannot_name(placeholder.universe)
+ }
+
+ NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential { .. } => {
+ false
+ }
+ }
+ }
+}
--- /dev/null
+use std::fmt::{self, Display};
+
+use rustc::hir;
+use rustc::hir::def::{Res, DefKind};
+use rustc::hir::def_id::DefId;
+use rustc::infer::InferCtxt;
+use rustc::mir::{Local, Body};
+use rustc::ty::subst::{SubstsRef, GenericArgKind};
+use rustc::ty::{self, RegionVid, Ty, TyCtxt};
+use rustc::ty::print::RegionHighlightMode;
+use rustc_index::vec::IndexVec;
+use rustc_errors::DiagnosticBuilder;
+use syntax::symbol::kw;
+use rustc_data_structures::fx::FxHashMap;
+use syntax_pos::{Span, symbol::Symbol, DUMMY_SP};
+
+use crate::borrow_check::{
+ nll::region_infer::RegionInferenceContext,
+ nll::universal_regions::DefiningTy,
+ nll::ToRegionVid,
+ Upvar,
+};
+
+use super::region_errors::ErrorReportingCtx;
+
+/// A name for a particular region used in emitting diagnostics. This name could be a generated
+/// name like `'1`, a name used by the user like `'a`, or a name like `'static`.
+#[derive(Debug, Clone)]
+crate struct RegionName {
+ /// The name of the region (interned).
+ crate name: Symbol,
+ /// Where the region comes from.
+ crate source: RegionNameSource,
+}
+
+/// Denotes the source of a region that is named by a `RegionName`. For example, a free region that
+/// was named by the user would get `NamedFreeRegion` and `'static` lifetime would get `Static`.
+/// This helps to print the right kinds of diagnostics.
+#[derive(Debug, Clone)]
+crate enum RegionNameSource {
+ /// A bound (not free) region that was substituted at the def site (not an HRTB).
+ NamedEarlyBoundRegion(Span),
+ /// A free region that the user has a name (`'a`) for.
+ NamedFreeRegion(Span),
+ /// The `'static` region.
+ Static,
+ /// The free region corresponding to the environment of a closure.
+ SynthesizedFreeEnvRegion(Span, String),
+ /// The region name corresponds to a region where the type annotation is completely missing
+ /// from the code, e.g. in a closure arguments `|x| { ... }`, where `x` is a reference.
+ CannotMatchHirTy(Span, String),
+ /// The region name corresponds a reference that was found by traversing the type in the HIR.
+ MatchedHirTy(Span),
+ /// A region name from the generics list of a struct/enum/union.
+ MatchedAdtAndSegment(Span),
+ /// The region corresponding to a closure upvar.
+ AnonRegionFromUpvar(Span, String),
+ /// The region corresponding to the return type of a closure.
+ AnonRegionFromOutput(Span, String, String),
+ /// The region from a type yielded by a generator.
+ AnonRegionFromYieldTy(Span, String),
+ /// An anonymous region from an async fn.
+ AnonRegionFromAsyncFn(Span),
+}
+
+/// Records region names that have been assigned before so that we can use the same ones in later
+/// diagnostics.
+#[derive(Debug, Clone)]
+crate struct RegionErrorNamingCtx {
+ /// Record the region names generated for each region in the given
+ /// MIR def so that we can reuse them later in help/error messages.
+ renctx: FxHashMap<RegionVid, RegionName>,
+
+ /// The counter for generating new region names.
+ counter: usize,
+}
+
+impl RegionErrorNamingCtx {
+ crate fn new() -> Self {
+ Self {
+ counter: 1,
+ renctx: FxHashMap::default(),
+ }
+ }
+
+ /// Get the name of `region` if it has previously been named.
+ crate fn get(&self, region: &RegionVid) -> Option<&RegionName> {
+ self.renctx.get(region)
+ }
+
+ /// Give `region` the name `name`.
+ crate fn insert(&mut self, region: RegionVid, name: RegionName) {
+ self.renctx.insert(region, name);
+ }
+
+ /// Creates a synthetic region named `'N`, where `N` is the next value of the counter. Then,
+ /// increment the counter.
+ ///
+ /// The name is not memoized. A separate call to `insert` should be made later. (Currently,
+ /// this happens at the end of `give_region_a_name`).
+ crate fn synthesize_region_name(&mut self) -> Symbol {
+ let c = self.counter;
+ self.counter += 1;
+
+ Symbol::intern(&format!("'{:?}", c))
+ }
+}
+
+impl RegionName {
+ crate fn was_named(&self) -> bool {
+ match self.source {
+ RegionNameSource::NamedEarlyBoundRegion(..) |
+ RegionNameSource::NamedFreeRegion(..) |
+ RegionNameSource::Static => true,
+ RegionNameSource::SynthesizedFreeEnvRegion(..) |
+ RegionNameSource::CannotMatchHirTy(..) |
+ RegionNameSource::MatchedHirTy(..) |
+ RegionNameSource::MatchedAdtAndSegment(..) |
+ RegionNameSource::AnonRegionFromUpvar(..) |
+ RegionNameSource::AnonRegionFromOutput(..) |
+ RegionNameSource::AnonRegionFromYieldTy(..) |
+ RegionNameSource::AnonRegionFromAsyncFn(..) => false,
+ }
+ }
+
+ crate fn highlight_region_name(&self, diag: &mut DiagnosticBuilder<'_>) {
+ match &self.source {
+ RegionNameSource::NamedFreeRegion(span)
+ | RegionNameSource::NamedEarlyBoundRegion(span) => {
+ diag.span_label(*span, format!("lifetime `{}` defined here", self));
+ }
+ RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
+ diag.span_label(
+ *span,
+ format!("lifetime `{}` represents this closure's body", self),
+ );
+ diag.note(¬e);
+ }
+ RegionNameSource::CannotMatchHirTy(span, type_name) => {
+ diag.span_label(*span, format!("has type `{}`", type_name));
+ }
+ RegionNameSource::MatchedHirTy(span) |
+ RegionNameSource::AnonRegionFromAsyncFn(span) => {
+ diag.span_label(
+ *span,
+ format!("let's call the lifetime of this reference `{}`", self),
+ );
+ }
+ RegionNameSource::MatchedAdtAndSegment(span) => {
+ diag.span_label(*span, format!("let's call this `{}`", self));
+ }
+ RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => {
+ diag.span_label(
+ *span,
+ format!(
+ "lifetime `{}` appears in the type of `{}`",
+ self, upvar_name
+ ),
+ );
+ }
+ RegionNameSource::AnonRegionFromOutput(span, mir_description, type_name) => {
+ diag.span_label(
+ *span,
+ format!("return type{} is {}", mir_description, type_name),
+ );
+ },
+ RegionNameSource::AnonRegionFromYieldTy(span, type_name) => {
+ diag.span_label(
+ *span,
+ format!("yield type is {}", type_name),
+ );
+ }
+ RegionNameSource::Static => {},
+ }
+ }
+}
+
+impl Display for RegionName {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.name)
+ }
+}
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+ /// Maps from an internal MIR region vid to something that we can
+ /// report to the user. In some cases, the region vids will map
+ /// directly to lifetimes that the user has a name for (e.g.,
+ /// `'static`). But frequently they will not, in which case we
+ /// have to find some way to identify the lifetime to the user. To
+ /// that end, this function takes a "diagnostic" so that it can
+ /// create auxiliary notes as needed.
+ ///
+ /// Example (function arguments):
+ ///
+ /// Suppose we are trying to give a name to the lifetime of the
+ /// reference `x`:
+ ///
+ /// ```
+ /// fn foo(x: &u32) { .. }
+ /// ```
+ ///
+ /// This function would create a label like this:
+ ///
+ /// ```
+ /// | fn foo(x: &u32) { .. }
+ /// ------- fully elaborated type of `x` is `&'1 u32`
+ /// ```
+ ///
+ /// and then return the name `'1` for us to use.
+ crate fn give_region_a_name(
+ &self,
+ errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
+ renctx: &mut RegionErrorNamingCtx,
+ fr: RegionVid,
+ ) -> Option<RegionName> {
+ let ErrorReportingCtx {
+ infcx, body, mir_def_id, local_names, upvars, ..
+ } = errctx;
+
+ debug!("give_region_a_name(fr={:?}, counter={:?})", fr, renctx.counter);
+
+ assert!(self.universal_regions.is_universal_region(fr));
+
+ if let Some(value) = renctx.get(&fr) {
+ return Some(value.clone());
+ }
+
+ let value = self
+ .give_name_from_error_region(infcx.tcx, *mir_def_id, fr, renctx)
+ .or_else(|| {
+ self.give_name_if_anonymous_region_appears_in_arguments(
+ infcx, body, local_names, *mir_def_id, fr, renctx,
+ )
+ })
+ .or_else(|| {
+ self.give_name_if_anonymous_region_appears_in_upvars(
+ infcx.tcx, upvars, fr, renctx
+ )
+ })
+ .or_else(|| {
+ self.give_name_if_anonymous_region_appears_in_output(
+ infcx, body, *mir_def_id, fr, renctx,
+ )
+ })
+ .or_else(|| {
+ self.give_name_if_anonymous_region_appears_in_yield_ty(
+ infcx, body, *mir_def_id, fr, renctx,
+ )
+ });
+
+ if let Some(ref value) = value {
+ renctx.insert(fr, value.clone());
+ }
+
+ debug!("give_region_a_name: gave name {:?}", value);
+ value
+ }
+
+ /// Checks for the case where `fr` maps to something that the
+ /// *user* has a name for. In that case, we'll be able to map
+ /// `fr` to a `Region<'tcx>`, and that region will be one of
+ /// named variants.
+ fn give_name_from_error_region(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ mir_def_id: DefId,
+ fr: RegionVid,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> Option<RegionName> {
+ let error_region = self.to_error_region(fr)?;
+
+ debug!("give_region_a_name: error_region = {:?}", error_region);
+ match error_region {
+ ty::ReEarlyBound(ebr) => {
+ if ebr.has_name() {
+ let span = tcx.hir().span_if_local(ebr.def_id).unwrap_or(DUMMY_SP);
+ Some(RegionName {
+ name: ebr.name,
+ source: RegionNameSource::NamedEarlyBoundRegion(span),
+ })
+ } else {
+ None
+ }
+ }
+
+ ty::ReStatic => Some(RegionName {
+ name: kw::StaticLifetime,
+ source: RegionNameSource::Static
+ }),
+
+ ty::ReFree(free_region) => match free_region.bound_region {
+ ty::BoundRegion::BrNamed(region_def_id, name) => {
+ // Get the span to point to, even if we don't use the name.
+ let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP);
+ debug!("bound region named: {:?}, is_named: {:?}",
+ name, free_region.bound_region.is_named());
+
+ if free_region.bound_region.is_named() {
+ // A named region that is actually named.
+ Some(RegionName {
+ name,
+ source: RegionNameSource::NamedFreeRegion(span),
+ })
+ } else {
+ // If we spuriously thought that the region is named, we should let the
+ // system generate a true name for error messages. Currently this can
+ // happen if we have an elided name in an async fn for example: the
+ // compiler will generate a region named `'_`, but reporting such a name is
+ // not actually useful, so we synthesize a name for it instead.
+ let name = renctx.synthesize_region_name();
+ Some(RegionName {
+ name,
+ source: RegionNameSource::AnonRegionFromAsyncFn(span),
+ })
+ }
+ }
+
+ ty::BoundRegion::BrEnv => {
+ let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
+ let def_ty = self.universal_regions.defining_ty;
+
+ if let DefiningTy::Closure(def_id, substs) = def_ty {
+ let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) =
+ tcx.hir().expect_expr(mir_hir_id).kind
+ {
+ span
+ } else {
+ bug!("Closure is not defined by a closure expr");
+ };
+ let region_name = renctx.synthesize_region_name();
+
+ let closure_kind_ty = substs.as_closure().kind_ty(def_id, tcx);
+ let note = match closure_kind_ty.to_opt_closure_kind() {
+ Some(ty::ClosureKind::Fn) => {
+ "closure implements `Fn`, so references to captured variables \
+ can't escape the closure"
+ }
+ Some(ty::ClosureKind::FnMut) => {
+ "closure implements `FnMut`, so references to captured variables \
+ can't escape the closure"
+ }
+ Some(ty::ClosureKind::FnOnce) => {
+ bug!("BrEnv in a `FnOnce` closure");
+ }
+ None => bug!("Closure kind not inferred in borrow check"),
+ };
+
+ Some(RegionName {
+ name: region_name,
+ source: RegionNameSource::SynthesizedFreeEnvRegion(
+ args_span,
+ note.to_string(),
+ ),
+ })
+ } else {
+ // Can't have BrEnv in functions, constants or generators.
+ bug!("BrEnv outside of closure.");
+ }
+ }
+
+ ty::BoundRegion::BrAnon(_) => None,
+ },
+
+ ty::ReLateBound(..)
+ | ty::ReScope(..)
+ | ty::ReVar(..)
+ | ty::RePlaceholder(..)
+ | ty::ReEmpty
+ | ty::ReErased
+ | ty::ReClosureBound(..) => None,
+ }
+ }
+
+ /// Finds an argument that contains `fr` and label it with a fully
+ /// elaborated type, returning something like `'1`. Result looks
+ /// like:
+ ///
+ /// ```
+ /// | fn foo(x: &u32) { .. }
+ /// ------- fully elaborated type of `x` is `&'1 u32`
+ /// ```
+ fn give_name_if_anonymous_region_appears_in_arguments(
+ &self,
+ infcx: &InferCtxt<'_, 'tcx>,
+ body: &Body<'tcx>,
+ local_names: &IndexVec<Local, Option<Symbol>>,
+ mir_def_id: DefId,
+ fr: RegionVid,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> Option<RegionName> {
+ let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
+ let argument_index = self.get_argument_index_for_region(infcx.tcx, fr)?;
+
+ let arg_ty =
+ self.universal_regions.unnormalized_input_tys[implicit_inputs + argument_index];
+ if let Some(region_name) = self.give_name_if_we_can_match_hir_ty_from_argument(
+ infcx,
+ mir_def_id,
+ fr,
+ arg_ty,
+ argument_index,
+ renctx,
+ ) {
+ return Some(region_name);
+ }
+
+ self.give_name_if_we_cannot_match_hir_ty(infcx, body, local_names, fr, arg_ty, renctx)
+ }
+
+ fn give_name_if_we_can_match_hir_ty_from_argument(
+ &self,
+ infcx: &InferCtxt<'_, 'tcx>,
+ mir_def_id: DefId,
+ needle_fr: RegionVid,
+ argument_ty: Ty<'tcx>,
+ argument_index: usize,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> Option<RegionName> {
+ let mir_hir_id = infcx.tcx.hir().as_local_hir_id(mir_def_id)?;
+ let fn_decl = infcx.tcx.hir().fn_decl_by_hir_id(mir_hir_id)?;
+ let argument_hir_ty: &hir::Ty = fn_decl.inputs.get(argument_index)?;
+ match argument_hir_ty.kind {
+ // This indicates a variable with no type annotation, like
+ // `|x|`... in that case, we can't highlight the type but
+ // must highlight the variable.
+ // NOTE(eddyb) this is handled in/by the sole caller
+ // (`give_name_if_anonymous_region_appears_in_arguments`).
+ hir::TyKind::Infer => None,
+
+ _ => self.give_name_if_we_can_match_hir_ty(
+ infcx.tcx,
+ needle_fr,
+ argument_ty,
+ argument_hir_ty,
+ renctx,
+ ),
+ }
+ }
+
+ /// Attempts to highlight the specific part of a type in an argument
+ /// that has no type annotation.
+ /// For example, we might produce an annotation like this:
+ ///
+ /// ```
+ /// | foo(|a, b| b)
+ /// | - -
+ /// | | |
+ /// | | has type `&'1 u32`
+ /// | has type `&'2 u32`
+ /// ```
+ fn give_name_if_we_cannot_match_hir_ty(
+ &self,
+ infcx: &InferCtxt<'_, 'tcx>,
+ body: &Body<'tcx>,
+ local_names: &IndexVec<Local, Option<Symbol>>,
+ needle_fr: RegionVid,
+ argument_ty: Ty<'tcx>,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> Option<RegionName> {
+ let counter = renctx.counter;
+ let mut highlight = RegionHighlightMode::default();
+ highlight.highlighting_region_vid(needle_fr, counter);
+ let type_name = infcx.extract_type_name(&argument_ty, Some(highlight)).0;
+
+ debug!(
+ "give_name_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}",
+ type_name, needle_fr
+ );
+ let assigned_region_name = if type_name.find(&format!("'{}", counter)).is_some() {
+ // Only add a label if we can confirm that a region was labelled.
+ let argument_index = self.get_argument_index_for_region(infcx.tcx, needle_fr)?;
+ let (_, span) =
+ self.get_argument_name_and_span_for_region(body, local_names, argument_index);
+
+ Some(RegionName {
+ // This counter value will already have been used, so this function will increment
+ // it so the next value will be used next and return the region name that would
+ // have been used.
+ name: renctx.synthesize_region_name(),
+ source: RegionNameSource::CannotMatchHirTy(span, type_name),
+ })
+ } else {
+ None
+ };
+
+ assigned_region_name
+ }
+
+ /// Attempts to highlight the specific part of a type annotation
+ /// that contains the anonymous reference we want to give a name
+ /// to. For example, we might produce an annotation like this:
+ ///
+ /// ```
+ /// | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item = &T>> {
+ /// | - let's call the lifetime of this reference `'1`
+ /// ```
+ ///
+ /// the way this works is that we match up `argument_ty`, which is
+ /// a `Ty<'tcx>` (the internal form of the type) with
+ /// `argument_hir_ty`, a `hir::Ty` (the syntax of the type
+ /// annotation). We are descending through the types stepwise,
+ /// looking in to find the region `needle_fr` in the internal
+ /// type. Once we find that, we can use the span of the `hir::Ty`
+ /// to add the highlight.
+ ///
+ /// This is a somewhat imperfect process, so along the way we also
+ /// keep track of the **closest** type we've found. If we fail to
+ /// find the exact `&` or `'_` to highlight, then we may fall back
+ /// to highlighting that closest type instead.
+ fn give_name_if_we_can_match_hir_ty(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ needle_fr: RegionVid,
+ argument_ty: Ty<'tcx>,
+ argument_hir_ty: &hir::Ty,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> Option<RegionName> {
+ let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty)> =
+ &mut vec![(argument_ty, argument_hir_ty)];
+
+ while let Some((ty, hir_ty)) = search_stack.pop() {
+ match (&ty.kind, &hir_ty.kind) {
+ // Check if the `argument_ty` is `&'X ..` where `'X`
+ // is the region we are looking for -- if so, and we have a `&T`
+ // on the RHS, then we want to highlight the `&` like so:
+ //
+ // &
+ // - let's call the lifetime of this reference `'1`
+ (
+ ty::Ref(region, referent_ty, _),
+ hir::TyKind::Rptr(_lifetime, referent_hir_ty),
+ ) => {
+ if region.to_region_vid() == needle_fr {
+ let region_name = renctx.synthesize_region_name();
+
+ // Just grab the first character, the `&`.
+ let source_map = tcx.sess.source_map();
+ let ampersand_span = source_map.start_point(hir_ty.span);
+
+ return Some(RegionName {
+ name: region_name,
+ source: RegionNameSource::MatchedHirTy(ampersand_span),
+ });
+ }
+
+ // Otherwise, let's descend into the referent types.
+ search_stack.push((referent_ty, &referent_hir_ty.ty));
+ }
+
+ // Match up something like `Foo<'1>`
+ (
+ ty::Adt(_adt_def, substs),
+ hir::TyKind::Path(hir::QPath::Resolved(None, path)),
+ ) => {
+ match path.res {
+ // Type parameters of the type alias have no reason to
+ // be the same as those of the ADT.
+ // FIXME: We should be able to do something similar to
+ // match_adt_and_segment in this case.
+ Res::Def(DefKind::TyAlias, _) => (),
+ _ => if let Some(last_segment) = path.segments.last() {
+ if let Some(name) = self.match_adt_and_segment(
+ substs,
+ needle_fr,
+ last_segment,
+ renctx,
+ search_stack,
+ ) {
+ return Some(name);
+ }
+ }
+ }
+ }
+
+ // The following cases don't have lifetimes, so we
+ // just worry about trying to match up the rustc type
+ // with the HIR types:
+ (ty::Tuple(elem_tys), hir::TyKind::Tup(elem_hir_tys)) => {
+ search_stack.extend(elem_tys.iter().map(|k| k.expect_ty()).zip(elem_hir_tys));
+ }
+
+ (ty::Slice(elem_ty), hir::TyKind::Slice(elem_hir_ty))
+ | (ty::Array(elem_ty, _), hir::TyKind::Array(elem_hir_ty, _)) => {
+ search_stack.push((elem_ty, elem_hir_ty));
+ }
+
+ (ty::RawPtr(mut_ty), hir::TyKind::Ptr(mut_hir_ty)) => {
+ search_stack.push((mut_ty.ty, &mut_hir_ty.ty));
+ }
+
+ _ => {
+ // FIXME there are other cases that we could trace
+ }
+ }
+ }
+
+ return None;
+ }
+
+ /// We've found an enum/struct/union type with the substitutions
+ /// `substs` and -- in the HIR -- a path type with the final
+ /// segment `last_segment`. Try to find a `'_` to highlight in
+ /// the generic args (or, if not, to produce new zipped pairs of
+ /// types+hir to search through).
+ fn match_adt_and_segment<'hir>(
+ &self,
+ substs: SubstsRef<'tcx>,
+ needle_fr: RegionVid,
+ last_segment: &'hir hir::PathSegment,
+ renctx: &mut RegionErrorNamingCtx,
+ search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>,
+ ) -> Option<RegionName> {
+ // Did the user give explicit arguments? (e.g., `Foo<..>`)
+ let args = last_segment.args.as_ref()?;
+ let lifetime =
+ self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?;
+ match lifetime.name {
+ hir::LifetimeName::Param(_)
+ | hir::LifetimeName::Error
+ | hir::LifetimeName::Static
+ | hir::LifetimeName::Underscore => {
+ let region_name = renctx.synthesize_region_name();
+ let ampersand_span = lifetime.span;
+ Some(RegionName {
+ name: region_name,
+ source: RegionNameSource::MatchedAdtAndSegment(ampersand_span),
+ })
+ }
+
+ hir::LifetimeName::ImplicitObjectLifetimeDefault
+ | hir::LifetimeName::Implicit => {
+ // In this case, the user left off the lifetime; so
+ // they wrote something like:
+ //
+ // ```
+ // x: Foo<T>
+ // ```
+ //
+ // where the fully elaborated form is `Foo<'_, '1,
+ // T>`. We don't consider this a match; instead we let
+ // the "fully elaborated" type fallback above handle
+ // it.
+ None
+ }
+ }
+ }
+
+ /// We've found an enum/struct/union type with the substitutions
+ /// `substs` and -- in the HIR -- a path with the generic
+ /// arguments `args`. If `needle_fr` appears in the args, return
+ /// the `hir::Lifetime` that corresponds to it. If not, push onto
+ /// `search_stack` the types+hir to search through.
+ fn try_match_adt_and_generic_args<'hir>(
+ &self,
+ substs: SubstsRef<'tcx>,
+ needle_fr: RegionVid,
+ args: &'hir hir::GenericArgs,
+ search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>,
+ ) -> Option<&'hir hir::Lifetime> {
+ for (kind, hir_arg) in substs.iter().zip(&args.args) {
+ match (kind.unpack(), hir_arg) {
+ (GenericArgKind::Lifetime(r), hir::GenericArg::Lifetime(lt)) => {
+ if r.to_region_vid() == needle_fr {
+ return Some(lt);
+ }
+ }
+
+ (GenericArgKind::Type(ty), hir::GenericArg::Type(hir_ty)) => {
+ search_stack.push((ty, hir_ty));
+ }
+
+ (GenericArgKind::Const(_ct), hir::GenericArg::Const(_hir_ct)) => {
+ // Lifetimes cannot be found in consts, so we don't need
+ // to search anything here.
+ }
+
+ (GenericArgKind::Lifetime(_), _)
+ | (GenericArgKind::Type(_), _)
+ | (GenericArgKind::Const(_), _) => {
+ // I *think* that HIR lowering should ensure this
+ // doesn't happen, even in erroneous
+ // programs. Else we should use delay-span-bug.
+ span_bug!(
+ hir_arg.span(),
+ "unmatched subst and hir arg: found {:?} vs {:?}",
+ kind,
+ hir_arg,
+ );
+ }
+ }
+ }
+
+ None
+ }
+
+ /// Finds a closure upvar that contains `fr` and label it with a
+ /// fully elaborated type, returning something like `'1`. Result
+ /// looks like:
+ ///
+ /// ```
+ /// | let x = Some(&22);
+ /// - fully elaborated type of `x` is `Option<&'1 u32>`
+ /// ```
+ fn give_name_if_anonymous_region_appears_in_upvars(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ upvars: &[Upvar],
+ fr: RegionVid,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> Option<RegionName> {
+ let upvar_index = self.get_upvar_index_for_region(tcx, fr)?;
+ let (upvar_name, upvar_span) =
+ self.get_upvar_name_and_span_for_region(tcx, upvars, upvar_index);
+ let region_name = renctx.synthesize_region_name();
+
+ Some(RegionName {
+ name: region_name,
+ source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()),
+ })
+ }
+
+ /// Checks for arguments appearing in the (closure) return type. It
+ /// must be a closure since, in a free fn, such an argument would
+ /// have to either also appear in an argument (if using elision)
+ /// or be early bound (named, not in argument).
+ fn give_name_if_anonymous_region_appears_in_output(
+ &self,
+ infcx: &InferCtxt<'_, 'tcx>,
+ body: &Body<'tcx>,
+ mir_def_id: DefId,
+ fr: RegionVid,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> Option<RegionName> {
+ let tcx = infcx.tcx;
+
+ let return_ty = self.universal_regions.unnormalized_output_ty;
+ debug!(
+ "give_name_if_anonymous_region_appears_in_output: return_ty = {:?}",
+ return_ty
+ );
+ if !tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) {
+ return None;
+ }
+
+ let mut highlight = RegionHighlightMode::default();
+ highlight.highlighting_region_vid(fr, renctx.counter);
+ let type_name = infcx.extract_type_name(&return_ty, Some(highlight)).0;
+
+ let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
+
+ let (return_span, mir_description) = match tcx.hir().get(mir_hir_id) {
+ hir::Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Closure(_, return_ty, _, span, gen_move),
+ ..
+ }) => (
+ match return_ty.output {
+ hir::FunctionRetTy::DefaultReturn(_) => tcx.sess.source_map().end_point(*span),
+ hir::FunctionRetTy::Return(_) => return_ty.output.span(),
+ },
+ if gen_move.is_some() {
+ " of generator"
+ } else {
+ " of closure"
+ },
+ ),
+ hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Method(method_sig, _),
+ ..
+ }) => (method_sig.decl.output.span(), ""),
+ _ => (body.span, ""),
+ };
+
+ Some(RegionName {
+ // This counter value will already have been used, so this function will increment it
+ // so the next value will be used next and return the region name that would have been
+ // used.
+ name: renctx.synthesize_region_name(),
+ source: RegionNameSource::AnonRegionFromOutput(
+ return_span,
+ mir_description.to_string(),
+ type_name,
+ ),
+ })
+ }
+
+ fn give_name_if_anonymous_region_appears_in_yield_ty(
+ &self,
+ infcx: &InferCtxt<'_, 'tcx>,
+ body: &Body<'tcx>,
+ mir_def_id: DefId,
+ fr: RegionVid,
+ renctx: &mut RegionErrorNamingCtx,
+ ) -> Option<RegionName> {
+ // Note: generators from `async fn` yield `()`, so we don't have to
+ // worry about them here.
+ let yield_ty = self.universal_regions.yield_ty?;
+ debug!(
+ "give_name_if_anonymous_region_appears_in_yield_ty: yield_ty = {:?}",
+ yield_ty,
+ );
+
+ let tcx = infcx.tcx;
+
+ if !tcx.any_free_region_meets(&yield_ty, |r| r.to_region_vid() == fr) {
+ return None;
+ }
+
+ let mut highlight = RegionHighlightMode::default();
+ highlight.highlighting_region_vid(fr, renctx.counter);
+ let type_name = infcx.extract_type_name(&yield_ty, Some(highlight)).0;
+
+ let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
+
+ let yield_span = match tcx.hir().get(mir_hir_id) {
+ hir::Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Closure(_, _, _, span, _),
+ ..
+ }) => (
+ tcx.sess.source_map().end_point(*span)
+ ),
+ _ => body.span,
+ };
+
+ debug!(
+ "give_name_if_anonymous_region_appears_in_yield_ty: \
+ type_name = {:?}, yield_span = {:?}",
+ yield_span,
+ type_name,
+ );
+
+ Some(RegionName {
+ name: renctx.synthesize_region_name(),
+ source: RegionNameSource::AnonRegionFromYieldTy(yield_span, type_name),
+ })
+ }
+}
--- /dev/null
+use crate::borrow_check::nll::region_infer::RegionInferenceContext;
+use crate::borrow_check::nll::ToRegionVid;
+use crate::borrow_check::Upvar;
+use rustc::mir::{Local, Body};
+use rustc::ty::{RegionVid, TyCtxt};
+use rustc_index::vec::{Idx, IndexVec};
+use syntax::source_map::Span;
+use syntax_pos::symbol::Symbol;
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+ crate fn get_var_name_and_span_for_region(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ local_names: &IndexVec<Local, Option<Symbol>>,
+ upvars: &[Upvar],
+ fr: RegionVid,
+ ) -> Option<(Option<Symbol>, Span)> {
+ debug!("get_var_name_and_span_for_region(fr={:?})", fr);
+ assert!(self.universal_regions.is_universal_region(fr));
+
+ debug!("get_var_name_and_span_for_region: attempting upvar");
+ self.get_upvar_index_for_region(tcx, fr)
+ .map(|index| {
+ let (name, span) =
+ self.get_upvar_name_and_span_for_region(tcx, upvars, index);
+ (Some(name), span)
+ })
+ .or_else(|| {
+ debug!("get_var_name_and_span_for_region: attempting argument");
+ self.get_argument_index_for_region(tcx, fr).map(|index| {
+ self.get_argument_name_and_span_for_region(body, local_names, index)
+ })
+ })
+ }
+
+ /// Search the upvars (if any) to find one that references fr. Return its index.
+ crate fn get_upvar_index_for_region(&self, tcx: TyCtxt<'tcx>, fr: RegionVid) -> Option<usize> {
+ let upvar_index = self
+ .universal_regions
+ .defining_ty
+ .upvar_tys(tcx)
+ .position(|upvar_ty| {
+ debug!("get_upvar_index_for_region: upvar_ty={:?}", upvar_ty);
+ tcx.any_free_region_meets(&upvar_ty, |r| {
+ let r = r.to_region_vid();
+ debug!("get_upvar_index_for_region: r={:?} fr={:?}", r, fr);
+ r == fr
+ })
+ })?;
+
+ let upvar_ty = self
+ .universal_regions
+ .defining_ty
+ .upvar_tys(tcx)
+ .nth(upvar_index);
+
+ debug!(
+ "get_upvar_index_for_region: found {:?} in upvar {} which has type {:?}",
+ fr, upvar_index, upvar_ty,
+ );
+
+ Some(upvar_index)
+ }
+
+ /// Given the index of an upvar, finds its name and the span from where it was
+ /// declared.
+ crate fn get_upvar_name_and_span_for_region(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ upvars: &[Upvar],
+ upvar_index: usize,
+ ) -> (Symbol, Span) {
+ let upvar_hir_id = upvars[upvar_index].var_hir_id;
+ debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id);
+
+ let upvar_name = tcx.hir().name(upvar_hir_id);
+ let upvar_span = tcx.hir().span(upvar_hir_id);
+ debug!("get_upvar_name_and_span_for_region: upvar_name={:?} upvar_span={:?}",
+ upvar_name, upvar_span);
+
+ (upvar_name, upvar_span)
+ }
+
+ /// Search the argument types for one that references fr (which should be a free region).
+ /// Returns Some(_) with the index of the input if one is found.
+ ///
+ /// N.B., in the case of a closure, the index is indexing into the signature as seen by the
+ /// user - in particular, index 0 is not the implicit self parameter.
+ crate fn get_argument_index_for_region(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ fr: RegionVid,
+ ) -> Option<usize> {
+ let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
+ let argument_index = self
+ .universal_regions
+ .unnormalized_input_tys
+ .iter()
+ .skip(implicit_inputs)
+ .position(|arg_ty| {
+ debug!(
+ "get_argument_index_for_region: arg_ty = {:?}",
+ arg_ty
+ );
+ tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr)
+ })?;
+
+ debug!(
+ "get_argument_index_for_region: found {:?} in argument {} which has type {:?}",
+ fr, argument_index, self.universal_regions.unnormalized_input_tys[argument_index],
+ );
+
+ Some(argument_index)
+ }
+
+ /// Given the index of an argument, finds its name (if any) and the span from where it was
+ /// declared.
+ crate fn get_argument_name_and_span_for_region(
+ &self,
+ body: &Body<'tcx>,
+ local_names: &IndexVec<Local, Option<Symbol>>,
+ argument_index: usize,
+ ) -> (Option<Symbol>, Span) {
+ let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
+ let argument_local = Local::new(implicit_inputs + argument_index + 1);
+ debug!("get_argument_name_and_span_for_region: argument_local={:?}", argument_local);
+
+ let argument_name = local_names[argument_local];
+ let argument_span = body.local_decls[argument_local].source_info.span;
+ debug!("get_argument_name_and_span_for_region: argument_name={:?} argument_span={:?}",
+ argument_name, argument_span);
+
+ (argument_name, argument_span)
+ }
+}
+++ /dev/null
-use rustc::hir;
-use rustc::hir::def::Namespace;
-use rustc::hir::def_id::DefId;
-use rustc::hir::GeneratorKind;
-use rustc::mir::{
- AggregateKind, Constant, Field, Local, LocalInfo, LocalKind, Location, Operand,
- Place, PlaceBase, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind,
- Static, StaticKind, Terminator, TerminatorKind,
-};
-use rustc::ty::{self, DefIdTree, Ty, TyCtxt};
-use rustc::ty::layout::VariantIdx;
-use rustc::ty::print::Print;
-use rustc_errors::DiagnosticBuilder;
-use syntax_pos::Span;
-
-use super::borrow_set::BorrowData;
-use super::MirBorrowckCtxt;
-use crate::dataflow::move_paths::{InitLocation, LookupResult};
-
-pub(super) struct IncludingDowncast(pub(super) bool);
-
-impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
- /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
- /// is moved after being invoked.
- ///
- /// ```text
- /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
- /// its environment
- /// --> $DIR/issue-42065.rs:16:29
- /// |
- /// LL | for (key, value) in dict {
- /// | ^^^^
- /// ```
- pub(super) fn add_moved_or_invoked_closure_note(
- &self,
- location: Location,
- place: PlaceRef<'cx, 'tcx>,
- diag: &mut DiagnosticBuilder<'_>,
- ) {
- debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
- let mut target = place.local_or_deref_local();
- for stmt in &self.body[location.block].statements[location.statement_index..] {
- debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
- if let StatementKind::Assign(box(into, Rvalue::Use(from))) = &stmt.kind {
- debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
- match from {
- Operand::Copy(ref place) |
- Operand::Move(ref place) if target == place.local_or_deref_local() =>
- target = into.local_or_deref_local(),
- _ => {},
- }
- }
- }
-
- // Check if we are attempting to call a closure after it has been invoked.
- let terminator = self.body[location.block].terminator();
- debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
- if let TerminatorKind::Call {
- func: Operand::Constant(box Constant {
- literal: ty::Const {
- ty: &ty::TyS { kind: ty::FnDef(id, _), .. },
- ..
- },
- ..
- }),
- args,
- ..
- } = &terminator.kind {
- debug!("add_moved_or_invoked_closure_note: id={:?}", id);
- if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
- let closure = match args.first() {
- Some(Operand::Copy(ref place)) |
- Some(Operand::Move(ref place)) if target == place.local_or_deref_local() =>
- place.local_or_deref_local().unwrap(),
- _ => return,
- };
-
- debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
- if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind {
- let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap();
-
- if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
- .closure_kind_origins()
- .get(hir_id)
- {
- diag.span_note(
- *span,
- &format!(
- "closure cannot be invoked more than once because it moves the \
- variable `{}` out of its environment",
- name,
- ),
- );
- return;
- }
- }
- }
- }
-
- // Check if we are just moving a closure after it has been invoked.
- if let Some(target) = target {
- if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind {
- let hir_id = self.infcx.tcx.hir().as_local_hir_id(did).unwrap();
-
- if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
- .closure_kind_origins()
- .get(hir_id)
- {
- diag.span_note(
- *span,
- &format!(
- "closure cannot be moved more than once as it is not `Copy` due to \
- moving the variable `{}` out of its environment",
- name
- ),
- );
- }
- }
- }
- }
-
- /// End-user visible description of `place` if one can be found. If the
- /// place is a temporary for instance, None will be returned.
- pub(super) fn describe_place(&self, place_ref: PlaceRef<'cx, 'tcx>) -> Option<String> {
- self.describe_place_with_options(place_ref, IncludingDowncast(false))
- }
-
- /// End-user visible description of `place` if one can be found. If the
- /// place is a temporary for instance, None will be returned.
- /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
- /// `Downcast` and `IncludingDowncast` is true
- pub(super) fn describe_place_with_options(
- &self,
- place: PlaceRef<'cx, 'tcx>,
- including_downcast: IncludingDowncast,
- ) -> Option<String> {
- let mut buf = String::new();
- match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
- Ok(()) => Some(buf),
- Err(()) => None,
- }
- }
-
- /// Appends end-user visible description of `place` to `buf`.
- fn append_place_to_string(
- &self,
- place: PlaceRef<'cx, 'tcx>,
- buf: &mut String,
- mut autoderef: bool,
- including_downcast: &IncludingDowncast,
- ) -> Result<(), ()> {
- match place {
- PlaceRef {
- base: PlaceBase::Local(local),
- projection: [],
- } => {
- self.append_local_to_string(*local, buf)?;
- }
- PlaceRef {
- base:
- PlaceBase::Static(box Static {
- kind: StaticKind::Promoted(..),
- ..
- }),
- projection: [],
- } => {
- buf.push_str("promoted");
- }
- PlaceRef {
- base:
- PlaceBase::Static(box Static {
- kind: StaticKind::Static,
- def_id,
- ..
- }),
- projection: [],
- } => {
- buf.push_str(&self.infcx.tcx.item_name(*def_id).to_string());
- }
- PlaceRef {
- base: &PlaceBase::Local(local),
- projection: [ProjectionElem::Deref]
- } if self.body.local_decls[local].is_ref_for_guard() => {
- self.append_place_to_string(
- PlaceRef {
- base: &PlaceBase::Local(local),
- projection: &[],
- },
- buf,
- autoderef,
- &including_downcast,
- )?;
- },
- PlaceRef {
- base: &PlaceBase::Local(local),
- projection: [ProjectionElem::Deref]
- } if self.body.local_decls[local].is_ref_to_static() => {
- let local_info = &self.body.local_decls[local].local_info;
- if let LocalInfo::StaticRef { def_id, .. } = *local_info {
- buf.push_str(&self.infcx.tcx.item_name(def_id).as_str());
- } else {
- unreachable!();
- }
- },
- PlaceRef {
- base,
- projection: [proj_base @ .., elem],
- } => {
- match elem {
- ProjectionElem::Deref => {
- let upvar_field_projection =
- self.is_upvar_field_projection(place);
- if let Some(field) = upvar_field_projection {
- let var_index = field.index();
- let name = self.upvars[var_index].name.to_string();
- if self.upvars[var_index].by_ref {
- buf.push_str(&name);
- } else {
- buf.push_str(&format!("*{}", &name));
- }
- } else {
- if autoderef {
- // FIXME turn this recursion into iteration
- self.append_place_to_string(
- PlaceRef {
- base,
- projection: proj_base,
- },
- buf,
- autoderef,
- &including_downcast,
- )?;
- } else {
- match (proj_base, base) {
- _ => {
- buf.push_str(&"*");
- self.append_place_to_string(
- PlaceRef {
- base,
- projection: proj_base,
- },
- buf,
- autoderef,
- &including_downcast,
- )?;
- }
- }
- }
- }
- }
- ProjectionElem::Downcast(..) => {
- self.append_place_to_string(
- PlaceRef {
- base,
- projection: proj_base,
- },
- buf,
- autoderef,
- &including_downcast,
- )?;
- if including_downcast.0 {
- return Err(());
- }
- }
- ProjectionElem::Field(field, _ty) => {
- autoderef = true;
-
- let upvar_field_projection =
- self.is_upvar_field_projection(place);
- if let Some(field) = upvar_field_projection {
- let var_index = field.index();
- let name = self.upvars[var_index].name.to_string();
- buf.push_str(&name);
- } else {
- let field_name = self.describe_field(PlaceRef {
- base,
- projection: proj_base,
- }, *field);
- self.append_place_to_string(
- PlaceRef {
- base,
- projection: proj_base,
- },
- buf,
- autoderef,
- &including_downcast,
- )?;
- buf.push_str(&format!(".{}", field_name));
- }
- }
- ProjectionElem::Index(index) => {
- autoderef = true;
-
- self.append_place_to_string(
- PlaceRef {
- base,
- projection: proj_base,
- },
- buf,
- autoderef,
- &including_downcast,
- )?;
- buf.push_str("[");
- if self.append_local_to_string(*index, buf).is_err() {
- buf.push_str("_");
- }
- buf.push_str("]");
- }
- ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
- autoderef = true;
- // Since it isn't possible to borrow an element on a particular index and
- // then use another while the borrow is held, don't output indices details
- // to avoid confusing the end-user
- self.append_place_to_string(
- PlaceRef {
- base,
- projection: proj_base,
- },
- buf,
- autoderef,
- &including_downcast,
- )?;
- buf.push_str(&"[..]");
- }
- };
- }
- }
-
- Ok(())
- }
-
- /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
- /// a name, or its name was generated by the compiler, then `Err` is returned
- fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
- let decl = &self.body.local_decls[local];
- match self.local_names[local] {
- Some(name) if !decl.from_compiler_desugaring() => {
- buf.push_str(&name.as_str());
- Ok(())
- }
- _ => Err(()),
- }
- }
-
- /// End-user visible description of the `field`nth field of `base`
- fn describe_field(&self, place: PlaceRef<'cx, 'tcx>, field: Field) -> String {
- // FIXME Place2 Make this work iteratively
- match place {
- PlaceRef {
- base: PlaceBase::Local(local),
- projection: [],
- } => {
- let local = &self.body.local_decls[*local];
- self.describe_field_from_ty(&local.ty, field, None)
- }
- PlaceRef {
- base: PlaceBase::Static(static_),
- projection: [],
- } =>
- self.describe_field_from_ty(&static_.ty, field, None),
- PlaceRef {
- base,
- projection: [proj_base @ .., elem],
- } => match elem {
- ProjectionElem::Deref => {
- self.describe_field(PlaceRef {
- base,
- projection: proj_base,
- }, field)
- }
- ProjectionElem::Downcast(_, variant_index) => {
- let base_ty = Place::ty_from(
- place.base,
- place.projection,
- *self.body,
- self.infcx.tcx).ty;
- self.describe_field_from_ty(&base_ty, field, Some(*variant_index))
- }
- ProjectionElem::Field(_, field_type) => {
- self.describe_field_from_ty(&field_type, field, None)
- }
- ProjectionElem::Index(..)
- | ProjectionElem::ConstantIndex { .. }
- | ProjectionElem::Subslice { .. } => {
- self.describe_field(PlaceRef {
- base,
- projection: proj_base,
- }, field)
- }
- },
- }
- }
-
- /// End-user visible description of the `field_index`nth field of `ty`
- fn describe_field_from_ty(
- &self,
- ty: Ty<'_>,
- field: Field,
- variant_index: Option<VariantIdx>
- ) -> String {
- if ty.is_box() {
- // If the type is a box, the field is described from the boxed type
- self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index)
- } else {
- match ty.kind {
- ty::Adt(def, _) => {
- let variant = if let Some(idx) = variant_index {
- assert!(def.is_enum());
- &def.variants[idx]
- } else {
- def.non_enum_variant()
- };
- variant.fields[field.index()]
- .ident
- .to_string()
- },
- ty::Tuple(_) => field.index().to_string(),
- ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
- self.describe_field_from_ty(&ty, field, variant_index)
- }
- ty::Array(ty, _) | ty::Slice(ty) =>
- self.describe_field_from_ty(&ty, field, variant_index),
- ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
- // `tcx.upvars(def_id)` returns an `Option`, which is `None` in case
- // the closure comes from another crate. But in that case we wouldn't
- // be borrowck'ing it, so we can just unwrap:
- let (&var_id, _) = self.infcx.tcx.upvars(def_id).unwrap()
- .get_index(field.index()).unwrap();
-
- self.infcx.tcx.hir().name(var_id).to_string()
- }
- _ => {
- // Might need a revision when the fields in trait RFC is implemented
- // (https://github.com/rust-lang/rfcs/pull/1546)
- bug!(
- "End-user description not implemented for field access on `{:?}`",
- ty
- );
- }
- }
- }
- }
-
- /// Add a note that a type does not implement `Copy`
- pub(super) fn note_type_does_not_implement_copy(
- &self,
- err: &mut DiagnosticBuilder<'a>,
- place_desc: &str,
- ty: Ty<'tcx>,
- span: Option<Span>,
- ) {
- let message = format!(
- "move occurs because {} has type `{}`, which does not implement the `Copy` trait",
- place_desc,
- ty,
- );
- if let Some(span) = span {
- err.span_label(span, message);
- } else {
- err.note(&message);
- }
- }
-
- pub(super) fn borrowed_content_source(
- &self,
- deref_base: PlaceRef<'cx, 'tcx>,
- ) -> BorrowedContentSource<'tcx> {
- let tcx = self.infcx.tcx;
-
- // Look up the provided place and work out the move path index for it,
- // we'll use this to check whether it was originally from an overloaded
- // operator.
- match self.move_data.rev_lookup.find(deref_base) {
- LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => {
- debug!("borrowed_content_source: mpi={:?}", mpi);
-
- for i in &self.move_data.init_path_map[mpi] {
- let init = &self.move_data.inits[*i];
- debug!("borrowed_content_source: init={:?}", init);
- // We're only interested in statements that initialized a value, not the
- // initializations from arguments.
- let loc = match init.location {
- InitLocation::Statement(stmt) => stmt,
- _ => continue,
- };
-
- let bbd = &self.body[loc.block];
- let is_terminator = bbd.statements.len() == loc.statement_index;
- debug!(
- "borrowed_content_source: loc={:?} is_terminator={:?}",
- loc,
- is_terminator,
- );
- if !is_terminator {
- continue;
- } else if let Some(Terminator {
- kind: TerminatorKind::Call {
- ref func,
- from_hir_call: false,
- ..
- },
- ..
- }) = bbd.terminator {
- if let Some(source) = BorrowedContentSource::from_call(
- func.ty(*self.body, tcx),
- tcx
- ) {
- return source;
- }
- }
- }
- }
- // Base is a `static` so won't be from an overloaded operator
- _ => (),
- };
-
- // If we didn't find an overloaded deref or index, then assume it's a
- // built in deref and check the type of the base.
- let base_ty = Place::ty_from(
- deref_base.base,
- deref_base.projection,
- *self.body,
- tcx
- ).ty;
- if base_ty.is_unsafe_ptr() {
- BorrowedContentSource::DerefRawPointer
- } else if base_ty.is_mutable_ptr() {
- BorrowedContentSource::DerefMutableRef
- } else {
- BorrowedContentSource::DerefSharedRef
- }
- }
-}
-
-impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
- /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
- /// name where required.
- 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);
-
- // We need to add synthesized lifetimes where appropriate. We do
- // this by hooking into the pretty printer and telling it to label the
- // lifetimes without names with the value `'0`.
- match ty.kind {
- ty::Ref(ty::RegionKind::ReLateBound(_, br), _, _)
- | ty::Ref(
- ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
- _,
- _,
- ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter),
- _ => {}
- }
-
- let _ = ty.print(printer);
- s
- }
-
- /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
- /// synthesized lifetime name where required.
- 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);
-
- let region = match ty.kind {
- ty::Ref(region, _, _) => {
- match region {
- ty::RegionKind::ReLateBound(_, br)
- | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
- printer.region_highlight_mode.highlighting_bound_region(*br, counter)
- }
- _ => {}
- }
-
- region
- }
- _ => bug!("ty for annotation of borrow region is not a reference"),
- };
-
- let _ = region.print(printer);
- s
- }
-}
-
-// The span(s) associated to a use of a place.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub(super) enum UseSpans {
- // The access is caused by capturing a variable for a closure.
- ClosureUse {
- // This is true if the captured variable was from a generator.
- generator_kind: Option<GeneratorKind>,
- // The span of the args of the closure, including the `move` keyword if
- // it's present.
- args_span: Span,
- // The span of the first use of the captured variable inside the closure.
- var_span: Span,
- },
- // This access has a single span associated to it: common case.
- OtherUse(Span),
-}
-
-impl UseSpans {
- pub(super) fn args_or_use(self) -> Span {
- match self {
- UseSpans::ClosureUse {
- args_span: span, ..
- }
- | UseSpans::OtherUse(span) => span,
- }
- }
-
- pub(super) fn var_or_use(self) -> Span {
- match self {
- UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
- }
- }
-
- pub(super) fn generator_kind(self) -> Option<GeneratorKind> {
- match self {
- UseSpans::ClosureUse { generator_kind, .. } => generator_kind,
- _ => None,
- }
- }
-
- // Add a span label to the arguments of the closure, if it exists.
- pub(super) fn args_span_label(
- self,
- err: &mut DiagnosticBuilder<'_>,
- message: impl Into<String>,
- ) {
- if let UseSpans::ClosureUse { args_span, .. } = self {
- err.span_label(args_span, message);
- }
- }
-
- // Add a span label to the use of the captured variable, if it exists.
- pub(super) fn var_span_label(
- self,
- err: &mut DiagnosticBuilder<'_>,
- message: impl Into<String>,
- ) {
- if let UseSpans::ClosureUse { var_span, .. } = self {
- err.span_label(var_span, message);
- }
- }
-
- /// Returns `false` if this place is not used in a closure.
- pub(super) fn for_closure(&self) -> bool {
- match *self {
- UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_none(),
- _ => false,
- }
- }
-
- /// Returns `false` if this place is not used in a generator.
- pub(super) fn for_generator(&self) -> bool {
- match *self {
- UseSpans::ClosureUse { generator_kind, .. } => generator_kind.is_some(),
- _ => false,
- }
- }
-
- /// Describe the span associated with a use of a place.
- pub(super) fn describe(&self) -> String {
- match *self {
- UseSpans::ClosureUse { generator_kind, .. } => if generator_kind.is_some() {
- " in generator".to_string()
- } else {
- " in closure".to_string()
- },
- _ => "".to_string(),
- }
- }
-
- pub(super) fn or_else<F>(self, if_other: F) -> Self
- where
- F: FnOnce() -> Self,
- {
- match self {
- closure @ UseSpans::ClosureUse { .. } => closure,
- UseSpans::OtherUse(_) => if_other(),
- }
- }
-}
-
-pub(super) enum BorrowedContentSource<'tcx> {
- DerefRawPointer,
- DerefMutableRef,
- DerefSharedRef,
- OverloadedDeref(Ty<'tcx>),
- OverloadedIndex(Ty<'tcx>),
-}
-
-impl BorrowedContentSource<'tcx> {
- pub(super) fn describe_for_unnamed_place(&self) -> String {
- match *self {
- BorrowedContentSource::DerefRawPointer => format!("a raw pointer"),
- BorrowedContentSource::DerefSharedRef => format!("a shared reference"),
- BorrowedContentSource::DerefMutableRef => {
- format!("a mutable reference")
- }
- BorrowedContentSource::OverloadedDeref(ty) => {
- if ty.is_rc() {
- format!("an `Rc`")
- } else if ty.is_arc() {
- format!("an `Arc`")
- } else {
- format!("dereference of `{}`", ty)
- }
- }
- BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty),
- }
- }
-
- pub(super) fn describe_for_named_place(&self) -> Option<&'static str> {
- match *self {
- BorrowedContentSource::DerefRawPointer => Some("raw pointer"),
- BorrowedContentSource::DerefSharedRef => Some("shared reference"),
- BorrowedContentSource::DerefMutableRef => Some("mutable reference"),
- // Overloaded deref and index operators should be evaluated into a
- // temporary. So we don't need a description here.
- BorrowedContentSource::OverloadedDeref(_)
- | BorrowedContentSource::OverloadedIndex(_) => None
- }
- }
-
- pub(super) fn describe_for_immutable_place(&self) -> String {
- match *self {
- BorrowedContentSource::DerefRawPointer => format!("a `*const` pointer"),
- BorrowedContentSource::DerefSharedRef => format!("a `&` reference"),
- BorrowedContentSource::DerefMutableRef => {
- bug!("describe_for_immutable_place: DerefMutableRef isn't immutable")
- },
- BorrowedContentSource::OverloadedDeref(ty) => {
- if ty.is_rc() {
- format!("an `Rc`")
- } else if ty.is_arc() {
- format!("an `Arc`")
- } else {
- format!("a dereference of `{}`", ty)
- }
- }
- BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty),
- }
- }
-
- fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Self> {
- match func.kind {
- ty::FnDef(def_id, substs) => {
- let trait_id = tcx.trait_of_item(def_id)?;
-
- let lang_items = tcx.lang_items();
- if Some(trait_id) == lang_items.deref_trait()
- || Some(trait_id) == lang_items.deref_mut_trait()
- {
- Some(BorrowedContentSource::OverloadedDeref(substs.type_at(0)))
- } else if Some(trait_id) == lang_items.index_trait()
- || Some(trait_id) == lang_items.index_mut_trait()
- {
- Some(BorrowedContentSource::OverloadedIndex(substs.type_at(0)))
- } else {
- None
- }
- }
- _ => None,
- }
- }
-}
-
-impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
- /// Finds the spans associated to a move or copy of move_place at location.
- pub(super) fn move_spans(
- &self,
- moved_place: PlaceRef<'cx, 'tcx>, // Could also be an upvar.
- location: Location,
- ) -> UseSpans {
- use self::UseSpans::*;
-
- let stmt = match self.body[location.block].statements.get(location.statement_index) {
- Some(stmt) => stmt,
- None => return OtherUse(self.body.source_info(location).span),
- };
-
- debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
- if let StatementKind::Assign(
- box(_, Rvalue::Aggregate(ref kind, ref places))
- ) = stmt.kind {
- let def_id = match kind {
- box AggregateKind::Closure(def_id, _)
- | box AggregateKind::Generator(def_id, _, _) => def_id,
- _ => return OtherUse(stmt.source_info.span),
- };
-
- debug!(
- "move_spans: def_id={:?} places={:?}",
- def_id, places
- );
- if let Some((args_span, generator_kind, var_span))
- = self.closure_span(*def_id, moved_place, places) {
- return ClosureUse {
- generator_kind,
- args_span,
- var_span,
- };
- }
- }
-
- OtherUse(stmt.source_info.span)
- }
-
- /// Finds the span of arguments of a closure (within `maybe_closure_span`)
- /// and its usage of the local assigned at `location`.
- /// This is done by searching in statements succeeding `location`
- /// and originating from `maybe_closure_span`.
- pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
- use self::UseSpans::*;
- debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
-
- let target = match self.body[location.block]
- .statements
- .get(location.statement_index)
- {
- Some(&Statement {
- kind: StatementKind::Assign(box(ref place, _)),
- ..
- }) => {
- if let Some(local) = place.as_local() {
- local
- } else {
- return OtherUse(use_span);
- }
- }
- _ => return OtherUse(use_span),
- };
-
- if self.body.local_kind(target) != LocalKind::Temp {
- // operands are always temporaries.
- return OtherUse(use_span);
- }
-
- for stmt in &self.body[location.block].statements[location.statement_index + 1..] {
- if let StatementKind::Assign(
- box(_, Rvalue::Aggregate(ref kind, ref places))
- ) = stmt.kind {
- let (def_id, is_generator) = match kind {
- box AggregateKind::Closure(def_id, _) => (def_id, false),
- box AggregateKind::Generator(def_id, _, _) => (def_id, true),
- _ => continue,
- };
-
- debug!(
- "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
- def_id, is_generator, places
- );
- if let Some((args_span, generator_kind, var_span)) = self.closure_span(
- *def_id, Place::from(target).as_ref(), places
- ) {
- return ClosureUse {
- generator_kind,
- args_span,
- var_span,
- };
- } else {
- return OtherUse(use_span);
- }
- }
-
- if use_span != stmt.source_info.span {
- break;
- }
- }
-
- OtherUse(use_span)
- }
-
- /// Finds the span of a captured variable within a closure or generator.
- fn closure_span(
- &self,
- def_id: DefId,
- target_place: PlaceRef<'cx, 'tcx>,
- places: &Vec<Operand<'tcx>>,
- ) -> Option<(Span, Option<GeneratorKind>, Span)> {
- debug!(
- "closure_span: def_id={:?} target_place={:?} places={:?}",
- def_id, target_place, places
- );
- let hir_id = self.infcx.tcx.hir().as_local_hir_id(def_id)?;
- let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
- debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
- if let hir::ExprKind::Closure(
- .., body_id, args_span, _
- ) = expr {
- for (upvar, place) in self.infcx.tcx.upvars(def_id)?.values().zip(places) {
- match place {
- Operand::Copy(place) |
- Operand::Move(place) if target_place == place.as_ref() => {
- debug!("closure_span: found captured local {:?}", place);
- let body = self.infcx.tcx.hir().body(*body_id);
- let generator_kind = body.generator_kind();
- return Some((*args_span, generator_kind, upvar.span));
- },
- _ => {}
- }
- }
-
- }
- None
- }
-
- /// Helper to retrieve span(s) of given borrow from the current MIR
- /// representation
- pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans {
- let span = self.body.source_info(borrow.reserve_location).span;
- self.borrow_spans(span, borrow.reserve_location)
- }
-}
use self::location::LocationTable;
use self::prefixes::PrefixSet;
use self::MutateMode::{JustWrite, WriteAndRead};
-use self::mutability_errors::AccessKind;
+use self::diagnostics::AccessKind;
use self::path_utils::*;
crate mod borrow_set;
-mod error_reporting;
+mod diagnostics;
mod flows;
mod location;
-mod conflict_errors;
-mod move_errors;
-mod mutability_errors;
mod path_utils;
crate mod place_ext;
crate mod places_conflict;
+++ /dev/null
-use rustc::mir::*;
-use rustc::ty;
-use rustc_errors::{DiagnosticBuilder,Applicability};
-use syntax_pos::Span;
-
-use crate::borrow_check::MirBorrowckCtxt;
-use crate::borrow_check::prefixes::PrefixSet;
-use crate::borrow_check::error_reporting::UseSpans;
-use crate::dataflow::move_paths::{
- IllegalMoveOrigin, IllegalMoveOriginKind,
- LookupResult, MoveError, MovePathIndex,
-};
-
-// Often when desugaring a pattern match we may have many individual moves in
-// MIR that are all part of one operation from the user's point-of-view. For
-// example:
-//
-// let (x, y) = foo()
-//
-// would move x from the 0 field of some temporary, and y from the 1 field. We
-// group such errors together for cleaner error reporting.
-//
-// Errors are kept separate if they are from places with different parent move
-// paths. For example, this generates two errors:
-//
-// let (&x, &y) = (&String::new(), &String::new());
-#[derive(Debug)]
-enum GroupedMoveError<'tcx> {
- // Place expression can't be moved from,
- // e.g., match x[0] { s => (), } where x: &[String]
- MovesFromPlace {
- original_path: Place<'tcx>,
- span: Span,
- move_from: Place<'tcx>,
- kind: IllegalMoveOriginKind<'tcx>,
- binds_to: Vec<Local>,
- },
- // Part of a value expression can't be moved from,
- // e.g., match &String::new() { &x => (), }
- MovesFromValue {
- original_path: Place<'tcx>,
- span: Span,
- move_from: MovePathIndex,
- kind: IllegalMoveOriginKind<'tcx>,
- binds_to: Vec<Local>,
- },
- // Everything that isn't from pattern matching.
- OtherIllegalMove {
- original_path: Place<'tcx>,
- use_spans: UseSpans,
- kind: IllegalMoveOriginKind<'tcx>,
- },
-}
-
-impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
- pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) {
- let grouped_errors = self.group_move_errors(move_errors);
- for error in grouped_errors {
- self.report(error);
- }
- }
-
- fn group_move_errors(
- &self,
- errors: Vec<(Place<'tcx>, MoveError<'tcx>)>
- ) -> Vec<GroupedMoveError<'tcx>> {
- let mut grouped_errors = Vec::new();
- for (original_path, error) in errors {
- self.append_to_grouped_errors(&mut grouped_errors, original_path, error);
- }
- grouped_errors
- }
-
- fn append_to_grouped_errors(
- &self,
- grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
- original_path: Place<'tcx>,
- error: MoveError<'tcx>,
- ) {
- match error {
- MoveError::UnionMove { .. } => {
- unimplemented!("don't know how to report union move errors yet.")
- }
- MoveError::IllegalMove {
- cannot_move_out_of: IllegalMoveOrigin { location, kind },
- } => {
- // Note: that the only time we assign a place isn't a temporary
- // to a user variable is when initializing it.
- // If that ever stops being the case, then the ever initialized
- // flow could be used.
- if let Some(StatementKind::Assign(
- box(place, Rvalue::Use(Operand::Move(move_from)))
- )) = self.body.basic_blocks()[location.block]
- .statements
- .get(location.statement_index)
- .map(|stmt| &stmt.kind)
- {
- if let Some(local) = place.as_local() {
- let local_decl = &self.body.local_decls[local];
- // opt_match_place is the
- // match_span is the span of the expression being matched on
- // match *x.y { ... } match_place is Some(*x.y)
- // ^^^^ match_span is the span of *x.y
- //
- // opt_match_place is None for let [mut] x = ... statements,
- // whether or not the right-hand side is a place expression
- if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
- VarBindingForm {
- opt_match_place: Some((ref opt_match_place, match_span)),
- binding_mode: _,
- opt_ty_info: _,
- pat_span: _,
- },
- ))) = local_decl.local_info {
- let stmt_source_info = self.body.source_info(location);
- self.append_binding_error(
- grouped_errors,
- kind,
- original_path,
- move_from,
- local,
- opt_match_place,
- match_span,
- stmt_source_info.span,
- );
- return;
- }
- }
- }
-
- let move_spans = self.move_spans(original_path.as_ref(), location);
- grouped_errors.push(GroupedMoveError::OtherIllegalMove {
- use_spans: move_spans,
- original_path,
- kind,
- });
- }
- }
- }
-
- fn append_binding_error(
- &self,
- grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
- kind: IllegalMoveOriginKind<'tcx>,
- original_path: Place<'tcx>,
- move_from: &Place<'tcx>,
- bind_to: Local,
- match_place: &Option<Place<'tcx>>,
- match_span: Span,
- statement_span: Span,
- ) {
- debug!(
- "append_binding_error(match_place={:?}, match_span={:?})",
- match_place, match_span
- );
-
- let from_simple_let = match_place.is_none();
- let match_place = match_place.as_ref().unwrap_or(move_from);
-
- match self.move_data.rev_lookup.find(match_place.as_ref()) {
- // Error with the match place
- LookupResult::Parent(_) => {
- for ge in &mut *grouped_errors {
- if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge {
- if match_span == *span {
- debug!("appending local({:?}) to list", bind_to);
- if !binds_to.is_empty() {
- binds_to.push(bind_to);
- }
- return;
- }
- }
- }
- debug!("found a new move error location");
-
- // Don't need to point to x in let x = ... .
- let (binds_to, span) = if from_simple_let {
- (vec![], statement_span)
- } else {
- (vec![bind_to], match_span)
- };
- grouped_errors.push(GroupedMoveError::MovesFromPlace {
- span,
- move_from: match_place.clone(),
- original_path,
- kind,
- binds_to,
- });
- }
- // Error with the pattern
- LookupResult::Exact(_) => {
- let mpi = match self.move_data.rev_lookup.find(move_from.as_ref()) {
- LookupResult::Parent(Some(mpi)) => mpi,
- // move_from should be a projection from match_place.
- _ => unreachable!("Probably not unreachable..."),
- };
- for ge in &mut *grouped_errors {
- if let GroupedMoveError::MovesFromValue {
- span,
- move_from: other_mpi,
- binds_to,
- ..
- } = ge
- {
- if match_span == *span && mpi == *other_mpi {
- debug!("appending local({:?}) to list", bind_to);
- binds_to.push(bind_to);
- return;
- }
- }
- }
- debug!("found a new move error location");
- grouped_errors.push(GroupedMoveError::MovesFromValue {
- span: match_span,
- move_from: mpi,
- original_path,
- kind,
- binds_to: vec![bind_to],
- });
- }
- };
- }
-
- fn report(&mut self, error: GroupedMoveError<'tcx>) {
- let (mut err, err_span) = {
- let (span, original_path, kind): (Span, &Place<'tcx>, &IllegalMoveOriginKind<'_>) =
- match error {
- GroupedMoveError::MovesFromPlace { span, ref original_path, ref kind, .. } |
- GroupedMoveError::MovesFromValue { span, ref original_path, ref kind, .. } => {
- (span, original_path, kind)
- }
- GroupedMoveError::OtherIllegalMove {
- use_spans,
- ref original_path,
- ref kind
- } => {
- (use_spans.args_or_use(), original_path, kind)
- },
- };
- debug!("report: original_path={:?} span={:?}, kind={:?} \
- original_path.is_upvar_field_projection={:?}", original_path, span, kind,
- self.is_upvar_field_projection(original_path.as_ref()));
- (
- match kind {
- IllegalMoveOriginKind::Static => {
- unreachable!();
- }
- IllegalMoveOriginKind::BorrowedContent { target_place } => {
- self.report_cannot_move_from_borrowed_content(
- original_path,
- target_place,
- span,
- )
- }
- IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
- self.cannot_move_out_of_interior_of_drop(span, ty)
- }
- IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } =>
- self.cannot_move_out_of_interior_noncopy(
- span, ty, Some(*is_index),
- ),
- },
- span,
- )
- };
-
- self.add_move_hints(error, &mut err, err_span);
- err.buffer(&mut self.errors_buffer);
- }
-
- fn report_cannot_move_from_static(
- &mut self,
- place: &Place<'tcx>,
- span: Span
- ) -> DiagnosticBuilder<'a> {
- let description = if place.projection.len() == 1 {
- format!("static item `{}`", self.describe_place(place.as_ref()).unwrap())
- } else {
- let base_static = PlaceRef {
- base: &place.base,
- projection: &[ProjectionElem::Deref],
- };
-
- format!(
- "`{:?}` as `{:?}` is a static item",
- self.describe_place(place.as_ref()).unwrap(),
- self.describe_place(base_static).unwrap(),
- )
- };
-
- self.cannot_move_out_of(span, &description)
- }
-
- fn report_cannot_move_from_borrowed_content(
- &mut self,
- move_place: &Place<'tcx>,
- deref_target_place: &Place<'tcx>,
- span: Span,
- ) -> DiagnosticBuilder<'a> {
- // Inspect the type of the content behind the
- // borrow to provide feedback about why this
- // was a move rather than a copy.
- let ty = deref_target_place.ty(*self.body, self.infcx.tcx).ty;
- let upvar_field = self.prefixes(move_place.as_ref(), PrefixSet::All)
- .find_map(|p| self.is_upvar_field_projection(p));
-
- let deref_base = match deref_target_place.projection.as_ref() {
- &[ref proj_base @ .., ProjectionElem::Deref] => {
- PlaceRef {
- base: &deref_target_place.base,
- projection: &proj_base,
- }
- }
- _ => bug!("deref_target_place is not a deref projection"),
- };
-
- if let PlaceRef {
- base: PlaceBase::Local(local),
- projection: [],
- } = deref_base {
- let decl = &self.body.local_decls[*local];
- if decl.is_ref_for_guard() {
- let mut err = self.cannot_move_out_of(
- span,
- &format!("`{}` in pattern guard", self.local_names[*local].unwrap()),
- );
- err.note(
- "variables bound in patterns cannot be moved from \
- until after the end of the pattern guard");
- return err;
- } else if decl.is_ref_to_static() {
- return self.report_cannot_move_from_static(move_place, span);
- }
- }
-
- debug!("report: ty={:?}", ty);
- let mut err = match ty.kind {
- ty::Array(..) | ty::Slice(..) =>
- self.cannot_move_out_of_interior_noncopy(span, ty, None),
- ty::Closure(def_id, closure_substs)
- if def_id == self.mir_def_id && upvar_field.is_some()
- => {
- let closure_kind_ty = closure_substs
- .as_closure().kind_ty(def_id, self.infcx.tcx);
- let closure_kind = closure_kind_ty.to_opt_closure_kind();
- let capture_description = match closure_kind {
- Some(ty::ClosureKind::Fn) => {
- "captured variable in an `Fn` closure"
- }
- Some(ty::ClosureKind::FnMut) => {
- "captured variable in an `FnMut` closure"
- }
- Some(ty::ClosureKind::FnOnce) => {
- bug!("closure kind does not match first argument type")
- }
- None => bug!("closure kind not inferred by borrowck"),
- };
-
- let upvar = &self.upvars[upvar_field.unwrap().index()];
- let upvar_hir_id = upvar.var_hir_id;
- let upvar_name = upvar.name;
- let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id);
-
- let place_name = self.describe_place(move_place.as_ref()).unwrap();
-
- let place_description = if self
- .is_upvar_field_projection(move_place.as_ref())
- .is_some()
- {
- format!("`{}`, a {}", place_name, capture_description)
- } else {
- format!(
- "`{}`, as `{}` is a {}",
- place_name,
- upvar_name,
- capture_description,
- )
- };
-
- debug!(
- "report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}",
- closure_kind_ty, closure_kind, place_description,
- );
-
- let mut diag = self.cannot_move_out_of(span, &place_description);
-
- diag.span_label(upvar_span, "captured outer variable");
-
- diag
- }
- _ => {
- let source = self.borrowed_content_source(deref_base);
- match (
- self.describe_place(move_place.as_ref()),
- source.describe_for_named_place(),
- ) {
- (Some(place_desc), Some(source_desc)) => {
- self.cannot_move_out_of(
- span,
- &format!("`{}` which is behind a {}", place_desc, source_desc),
- )
- }
- (_, _) => {
- self.cannot_move_out_of(
- span,
- &source.describe_for_unnamed_place(),
- )
- }
- }
- },
- };
- let move_ty = format!(
- "{:?}",
- move_place.ty(*self.body, self.infcx.tcx).ty,
- );
- if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
- let is_option = move_ty.starts_with("std::option::Option");
- let is_result = move_ty.starts_with("std::result::Result");
- if is_option || is_result {
- err.span_suggestion(
- span,
- &format!("consider borrowing the `{}`'s content", if is_option {
- "Option"
- } else {
- "Result"
- }),
- format!("{}.as_ref()", snippet),
- Applicability::MaybeIncorrect,
- );
- }
- }
- err
- }
-
- fn add_move_hints(
- &self,
- error: GroupedMoveError<'tcx>,
- err: &mut DiagnosticBuilder<'a>,
- span: Span,
- ) {
- match error {
- GroupedMoveError::MovesFromPlace {
- mut binds_to,
- move_from,
- ..
- } => {
- if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
- err.span_suggestion(
- span,
- "consider borrowing here",
- format!("&{}", snippet),
- Applicability::Unspecified,
- );
- }
-
- if binds_to.is_empty() {
- let place_ty = move_from.ty(*self.body, self.infcx.tcx).ty;
- let place_desc = match self.describe_place(move_from.as_ref()) {
- Some(desc) => format!("`{}`", desc),
- None => format!("value"),
- };
-
- self.note_type_does_not_implement_copy(
- err,
- &place_desc,
- place_ty,
- Some(span)
- );
- } else {
- binds_to.sort();
- binds_to.dedup();
-
- self.add_move_error_details(err, &binds_to);
- }
- }
- GroupedMoveError::MovesFromValue { mut binds_to, .. } => {
- binds_to.sort();
- binds_to.dedup();
- self.add_move_error_suggestions(err, &binds_to);
- self.add_move_error_details(err, &binds_to);
- }
- // No binding. Nothing to suggest.
- GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
- let span = use_spans.var_or_use();
- let place_ty = original_path.ty(*self.body, self.infcx.tcx).ty;
- let place_desc = match self.describe_place(original_path.as_ref()) {
- Some(desc) => format!("`{}`", desc),
- None => format!("value"),
- };
- self.note_type_does_not_implement_copy(
- err,
- &place_desc,
- place_ty,
- Some(span),
- );
-
- use_spans.args_span_label(err, format!("move out of {} occurs here", place_desc));
- use_spans.var_span_label(
- err,
- format!("move occurs due to use{}", use_spans.describe()),
- );
- },
- }
- }
-
- fn add_move_error_suggestions(
- &self,
- err: &mut DiagnosticBuilder<'a>,
- binds_to: &[Local],
- ) {
- let mut suggestions: Vec<(Span, &str, String)> = Vec::new();
- for local in binds_to {
- let bind_to = &self.body.local_decls[*local];
- if let LocalInfo::User(
- ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
- pat_span,
- ..
- }))
- ) = bind_to.local_info {
- if let Ok(pat_snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(pat_span)
- {
- if pat_snippet.starts_with('&') {
- let pat_snippet = pat_snippet[1..].trim_start();
- let suggestion;
- let to_remove;
- if pat_snippet.starts_with("mut")
- && pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace)
- {
- suggestion = pat_snippet["mut".len()..].trim_start();
- to_remove = "&mut";
- } else {
- suggestion = pat_snippet;
- to_remove = "&";
- }
- suggestions.push((
- pat_span,
- to_remove,
- suggestion.to_owned(),
- ));
- }
- }
- }
- }
- suggestions.sort_unstable_by_key(|&(span, _, _)| span);
- suggestions.dedup_by_key(|&mut (span, _, _)| span);
- for (span, to_remove, suggestion) in suggestions {
- err.span_suggestion(
- span,
- &format!("consider removing the `{}`", to_remove),
- suggestion,
- Applicability::MachineApplicable,
- );
- }
- }
-
- fn add_move_error_details(
- &self,
- err: &mut DiagnosticBuilder<'a>,
- binds_to: &[Local],
- ) {
- for (j, local) in binds_to.into_iter().enumerate() {
- let bind_to = &self.body.local_decls[*local];
- let binding_span = bind_to.source_info.span;
-
- if j == 0 {
- err.span_label(binding_span, "data moved here");
- } else {
- err.span_label(binding_span, "...and here");
- }
-
- if binds_to.len() == 1 {
- self.note_type_does_not_implement_copy(
- err,
- &format!("`{}`", self.local_names[*local].unwrap()),
- bind_to.ty,
- Some(binding_span)
- );
- }
- }
-
- if binds_to.len() > 1 {
- err.note("move occurs because these variables have types that \
- don't implement the `Copy` trait",
- );
- }
- }
-}
+++ /dev/null
-use rustc::hir;
-use rustc::hir::Node;
-use rustc::mir::{self, ClearCrossCrate, Local, LocalInfo, Location, ReadOnlyBodyCache};
-use rustc::mir::{Mutability, Place, PlaceRef, PlaceBase, ProjectionElem};
-use rustc::ty::{self, Ty, TyCtxt};
-use rustc_index::vec::Idx;
-use syntax_pos::Span;
-use syntax_pos::symbol::kw;
-
-use crate::borrow_check::MirBorrowckCtxt;
-use crate::borrow_check::error_reporting::BorrowedContentSource;
-use crate::util::collect_writes::FindAssignments;
-use rustc_errors::Applicability;
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub(super) enum AccessKind {
- MutableBorrow,
- Mutate,
-}
-
-impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
- pub(super) fn report_mutability_error(
- &mut self,
- access_place: &Place<'tcx>,
- span: Span,
- the_place_err: PlaceRef<'cx, 'tcx>,
- error_access: AccessKind,
- location: Location,
- ) {
- debug!(
- "report_mutability_error(\
- access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
- )",
- access_place, span, the_place_err, error_access, location,
- );
-
- let mut err;
- let item_msg;
- let reason;
- let mut opt_source = None;
- let access_place_desc = self.describe_place(access_place.as_ref());
- debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
-
- match the_place_err {
- PlaceRef {
- base: PlaceBase::Local(local),
- projection: [],
- } => {
- item_msg = format!("`{}`", access_place_desc.unwrap());
- if access_place.as_local().is_some() {
- reason = ", as it is not declared as mutable".to_string();
- } else {
- let name = self.local_names[*local]
- .expect("immutable unnamed local");
- reason = format!(", as `{}` is not declared as mutable", name);
- }
- }
-
- PlaceRef {
- base: _,
- projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
- } => {
- debug_assert!(is_closure_or_generator(
- Place::ty_from(
- &the_place_err.base,
- proj_base,
- *self.body,
- self.infcx.tcx
- ).ty));
-
- item_msg = format!("`{}`", access_place_desc.unwrap());
- if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
- reason = ", as it is not declared as mutable".to_string();
- } else {
- let name = self.upvars[upvar_index.index()].name;
- reason = format!(", as `{}` is not declared as mutable", name);
- }
- }
-
- PlaceRef {
- base: &PlaceBase::Local(local),
- projection: [ProjectionElem::Deref],
- } if self.body.local_decls[local].is_ref_for_guard() => {
- item_msg = format!("`{}`", access_place_desc.unwrap());
- reason = ", as it is immutable for the pattern guard".to_string();
- }
- PlaceRef {
- base: &PlaceBase::Local(local),
- projection: [ProjectionElem::Deref],
- } if self.body.local_decls[local].is_ref_to_static() => {
- if access_place.projection.len() == 1 {
- item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
- reason = String::new();
- } else {
- item_msg = format!("`{}`", access_place_desc.unwrap());
- let local_info = &self.body.local_decls[local].local_info;
- if let LocalInfo::StaticRef { def_id, .. } = *local_info {
- let static_name = &self.infcx.tcx.item_name(def_id);
- reason = format!(", as `{}` is an immutable static item", static_name);
- } else {
- bug!("is_ref_to_static return true, but not ref to static?");
- }
- }
- }
- PlaceRef {
- base: _,
- projection: [proj_base @ .., ProjectionElem::Deref],
- } => {
- if the_place_err.base == &PlaceBase::Local(Local::new(1)) &&
- proj_base.is_empty() &&
- !self.upvars.is_empty() {
- item_msg = format!("`{}`", access_place_desc.unwrap());
- debug_assert!(self.body.local_decls[Local::new(1)].ty.is_region_ptr());
- debug_assert!(is_closure_or_generator(
- Place::ty_from(
- the_place_err.base,
- the_place_err.projection,
- *self.body,
- self.infcx.tcx
- )
- .ty
- ));
-
- reason =
- if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
- ", as it is a captured variable in a `Fn` closure".to_string()
- } else {
- ", as `Fn` closures cannot mutate their captured variables".to_string()
- }
- } else {
- let source = self.borrowed_content_source(PlaceRef {
- base: the_place_err.base,
- projection: proj_base,
- });
- let pointer_type = source.describe_for_immutable_place();
- opt_source = Some(source);
- if let Some(desc) = access_place_desc {
- item_msg = format!("`{}`", desc);
- reason = match error_access {
- AccessKind::Mutate => format!(" which is behind {}", pointer_type),
- AccessKind::MutableBorrow => {
- format!(", as it is behind {}", pointer_type)
- }
- }
- } else {
- item_msg = format!("data in {}", pointer_type);
- reason = String::new();
- }
- }
- }
-
- PlaceRef {
- base: PlaceBase::Static(_),
- ..
- }
- | PlaceRef {
- base: _,
- projection: [.., ProjectionElem::Index(_)],
- }
- | PlaceRef {
- base: _,
- projection: [.., ProjectionElem::ConstantIndex { .. }],
- }
- | PlaceRef {
- base: _,
- projection: [.., ProjectionElem::Subslice { .. }],
- }
- | PlaceRef {
- base: _,
- projection: [.., ProjectionElem::Downcast(..)],
- } => bug!("Unexpected immutable place."),
- }
-
- debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
-
- // `act` and `acted_on` are strings that let us abstract over
- // the verbs used in some diagnostic messages.
- let act;
- let acted_on;
-
- let span = match error_access {
- AccessKind::Mutate => {
- err = self.cannot_assign(span, &(item_msg + &reason));
- act = "assign";
- acted_on = "written";
- span
- }
- AccessKind::MutableBorrow => {
- act = "borrow as mutable";
- acted_on = "borrowed as mutable";
-
- let borrow_spans = self.borrow_spans(span, location);
- let borrow_span = borrow_spans.args_or_use();
- err = self.cannot_borrow_path_as_mutable_because(
- borrow_span,
- &item_msg,
- &reason,
- );
- borrow_spans.var_span_label(
- &mut err,
- format!(
- "mutable borrow occurs due to use of `{}` in closure",
- // always Some() if the message is printed.
- self.describe_place(access_place.as_ref()).unwrap_or_default(),
- )
- );
- borrow_span
- }
- };
-
- debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
-
- match the_place_err {
- // Suggest making an existing shared borrow in a struct definition a mutable borrow.
- //
- // This is applicable when we have a deref of a field access to a deref of a local -
- // something like `*((*_1).0`. The local that we get will be a reference to the
- // struct we've got a field access of (it must be a reference since there's a deref
- // after the field access).
- PlaceRef {
- base,
- projection: [proj_base @ ..,
- ProjectionElem::Deref,
- ProjectionElem::Field(field, _),
- ProjectionElem::Deref,
- ],
- } => {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
-
- if let Some((span, message)) = annotate_struct_field(
- self.infcx.tcx,
- Place::ty_from(base, proj_base, *self.body, self.infcx.tcx).ty,
- field,
- ) {
- err.span_suggestion(
- span,
- "consider changing this to be mutable",
- message,
- Applicability::MaybeIncorrect,
- );
- }
- },
-
- // Suggest removing a `&mut` from the use of a mutable reference.
- PlaceRef {
- base: PlaceBase::Local(local),
- projection: [],
- } if {
- self.body.local_decls.get(*local).map(|local_decl| {
- if let LocalInfo::User(ClearCrossCrate::Set(
- mir::BindingForm::ImplicitSelf(kind)
- )) = local_decl.local_info {
- // Check if the user variable is a `&mut self` and we can therefore
- // suggest removing the `&mut`.
- //
- // Deliberately fall into this case for all implicit self types,
- // so that we don't fall in to the next case with them.
- kind == mir::ImplicitSelfKind::MutRef
- } else if Some(kw::SelfLower) == self.local_names[*local] {
- // Otherwise, check if the name is the self kewyord - in which case
- // we have an explicit self. Do the same thing in this case and check
- // for a `self: &mut Self` to suggest removing the `&mut`.
- if let ty::Ref(
- _, _, hir::Mutability::Mutable
- ) = local_decl.ty.kind {
- true
- } else {
- false
- }
- } else {
- false
- }
- }).unwrap_or(false)
- } => {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
- err.span_label(span, "try removing `&mut` here");
- },
-
- // We want to suggest users use `let mut` for local (user
- // variable) mutations...
- PlaceRef {
- base: PlaceBase::Local(local),
- projection: [],
- } if self.body.local_decls[*local].can_be_made_mutable() => {
- // ... but it doesn't make sense to suggest it on
- // variables that are `ref x`, `ref mut x`, `&self`,
- // or `&mut self` (such variables are simply not
- // mutable).
- let local_decl = &self.body.local_decls[*local];
- assert_eq!(local_decl.mutability, Mutability::Not);
-
- err.span_label(span, format!("cannot {ACT}", ACT = act));
- err.span_suggestion(
- local_decl.source_info.span,
- "consider changing this to be mutable",
- format!("mut {}", self.local_names[*local].unwrap()),
- Applicability::MachineApplicable,
- );
- }
-
- // Also suggest adding mut for upvars
- PlaceRef {
- base,
- projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
- } => {
- debug_assert!(is_closure_or_generator(
- Place::ty_from(base, proj_base, *self.body, self.infcx.tcx).ty
- ));
-
- err.span_label(span, format!("cannot {ACT}", ACT = act));
-
- let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id;
- if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
- {
- if let hir::PatKind::Binding(
- hir::BindingAnnotation::Unannotated,
- _,
- upvar_ident,
- _,
- ) = pat.kind
- {
- err.span_suggestion(
- upvar_ident.span,
- "consider changing this to be mutable",
- format!("mut {}", upvar_ident.name),
- Applicability::MachineApplicable,
- );
- }
- }
- }
-
- // complete hack to approximate old AST-borrowck
- // diagnostic: if the span starts with a mutable borrow of
- // a local variable, then just suggest the user remove it.
- PlaceRef {
- base: PlaceBase::Local(_),
- projection: [],
- } if {
- if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
- snippet.starts_with("&mut ")
- } else {
- false
- }
- } =>
- {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
- err.span_label(span, "try removing `&mut` here");
- }
-
- PlaceRef {
- base: PlaceBase::Local(local),
- projection: [ProjectionElem::Deref],
- } if self.body.local_decls[*local].is_ref_for_guard() => {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
- err.note(
- "variables bound in patterns are immutable until the end of the pattern guard",
- );
- }
-
- // We want to point out when a `&` can be readily replaced
- // with an `&mut`.
- //
- // FIXME: can this case be generalized to work for an
- // arbitrary base for the projection?
- PlaceRef {
- base: PlaceBase::Local(local),
- projection: [ProjectionElem::Deref],
- } if self.body.local_decls[*local].is_user_variable() =>
- {
- let local_decl = &self.body.local_decls[*local];
- let suggestion = match local_decl.local_info {
- LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(_))) => {
- Some(suggest_ampmut_self(self.infcx.tcx, local_decl))
- }
-
- LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
- mir::VarBindingForm {
- binding_mode: ty::BindingMode::BindByValue(_),
- opt_ty_info,
- ..
- },
- ))) => Some(suggest_ampmut(
- self.infcx.tcx,
- self.body,
- *local,
- local_decl,
- opt_ty_info,
- )),
-
- LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
- mir::VarBindingForm {
- binding_mode: ty::BindingMode::BindByReference(_),
- ..
- },
- ))) => {
- let pattern_span = local_decl.source_info.span;
- suggest_ref_mut(self.infcx.tcx, pattern_span)
- .map(|replacement| (pattern_span, replacement))
- }
-
- LocalInfo::User(ClearCrossCrate::Clear) => bug!("saw cleared local state"),
-
- _ => unreachable!(),
- };
-
- let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
- ("&", "reference")
- } else {
- ("*const", "pointer")
- };
-
- if let Some((err_help_span, suggested_code)) = suggestion {
- err.span_suggestion(
- err_help_span,
- &format!("consider changing this to be a mutable {}", pointer_desc),
- suggested_code,
- Applicability::MachineApplicable,
- );
- }
-
- match self.local_names[*local] {
- Some(name) if !local_decl.from_compiler_desugaring() => {
- err.span_label(
- span,
- format!(
- "`{NAME}` is a `{SIGIL}` {DESC}, \
- so the data it refers to cannot be {ACTED_ON}",
- NAME = name,
- SIGIL = pointer_sigil,
- DESC = pointer_desc,
- ACTED_ON = acted_on
- ),
- );
- }
- _ => {
- err.span_label(
- span,
- format!(
- "cannot {ACT} through `{SIGIL}` {DESC}",
- ACT = act,
- SIGIL = pointer_sigil,
- DESC = pointer_desc
- ),
- );
- }
- }
- }
-
- PlaceRef {
- base,
- projection: [ProjectionElem::Deref],
- // FIXME document what is this 1 magic number about
- } if *base == PlaceBase::Local(Local::new(1)) &&
- !self.upvars.is_empty() =>
- {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
- err.span_help(
- self.body.span,
- "consider changing this to accept closures that implement `FnMut`"
- );
- }
-
- PlaceRef {
- base: _,
- projection: [.., ProjectionElem::Deref],
- } => {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
-
- match opt_source {
- Some(BorrowedContentSource::OverloadedDeref(ty)) => {
- err.help(
- &format!(
- "trait `DerefMut` is required to modify through a dereference, \
- but it is not implemented for `{}`",
- ty,
- ),
- );
- },
- Some(BorrowedContentSource::OverloadedIndex(ty)) => {
- err.help(
- &format!(
- "trait `IndexMut` is required to modify indexed content, \
- but it is not implemented for `{}`",
- ty,
- ),
- );
- }
- _ => (),
- }
- }
-
- _ => {
- err.span_label(span, format!("cannot {ACT}", ACT = act));
- }
- }
-
- err.buffer(&mut self.errors_buffer);
- }
-}
-
-fn suggest_ampmut_self<'tcx>(
- tcx: TyCtxt<'tcx>,
- local_decl: &mir::LocalDecl<'tcx>,
-) -> (Span, String) {
- let sp = local_decl.source_info.span;
- (sp, match tcx.sess.source_map().span_to_snippet(sp) {
- Ok(snippet) => {
- let lt_pos = snippet.find('\'');
- if let Some(lt_pos) = lt_pos {
- format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
- } else {
- "&mut self".to_string()
- }
- }
- _ => "&mut self".to_string()
- })
-}
-
-// When we want to suggest a user change a local variable to be a `&mut`, there
-// are three potential "obvious" things to highlight:
-//
-// let ident [: Type] [= RightHandSideExpression];
-// ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
-// (1.) (2.) (3.)
-//
-// We can always fallback on highlighting the first. But chances are good that
-// the user experience will be better if we highlight one of the others if possible;
-// for example, if the RHS is present and the Type is not, then the type is going to
-// be inferred *from* the RHS, which means we should highlight that (and suggest
-// that they borrow the RHS mutably).
-//
-// This implementation attempts to emulate AST-borrowck prioritization
-// by trying (3.), then (2.) and finally falling back on (1.).
-fn suggest_ampmut<'tcx>(
- tcx: TyCtxt<'tcx>,
- body: ReadOnlyBodyCache<'_, 'tcx>,
- local: Local,
- local_decl: &mir::LocalDecl<'tcx>,
- opt_ty_info: Option<Span>,
-) -> (Span, String) {
- let locations = body.find_assignments(local);
- if !locations.is_empty() {
- let assignment_rhs_span = body.source_info(locations[0]).span;
- if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) {
- if let (true, Some(ws_pos)) = (
- src.starts_with("&'"),
- src.find(|c: char| -> bool { c.is_whitespace() }),
- ) {
- let lt_name = &src[1..ws_pos];
- let ty = &src[ws_pos..];
- return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
- } else if src.starts_with('&') {
- let borrowed_expr = &src[1..];
- return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
- }
- }
- }
-
- let highlight_span = match opt_ty_info {
- // if this is a variable binding with an explicit type,
- // try to highlight that for the suggestion.
- Some(ty_span) => ty_span,
-
- // otherwise, just highlight the span associated with
- // the (MIR) LocalDecl.
- None => local_decl.source_info.span,
- };
-
- if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) {
- if let (true, Some(ws_pos)) = (
- src.starts_with("&'"),
- src.find(|c: char| -> bool { c.is_whitespace() }),
- ) {
- let lt_name = &src[1..ws_pos];
- let ty = &src[ws_pos..];
- return (highlight_span, format!("&{} mut{}", lt_name, ty));
- }
- }
-
- let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
- assert_eq!(ty_mut.mutbl, hir::Mutability::Immutable);
- (highlight_span,
- if local_decl.ty.is_region_ptr() {
- format!("&mut {}", ty_mut.ty)
- } else {
- format!("*mut {}", ty_mut.ty)
- })
-}
-
-fn is_closure_or_generator(ty: Ty<'_>) -> bool {
- ty.is_closure() || ty.is_generator()
-}
-
-/// Adds a suggestion to a struct definition given a field access to a local.
-/// This function expects the local to be a reference to a struct in order to produce a suggestion.
-///
-/// ```text
-/// LL | s: &'a String
-/// | ---------- use `&'a mut String` here to make mutable
-/// ```
-fn annotate_struct_field(
- tcx: TyCtxt<'tcx>,
- ty: Ty<'tcx>,
- field: &mir::Field,
-) -> Option<(Span, String)> {
- // Expect our local to be a reference to a struct of some kind.
- if let ty::Ref(_, ty, _) = ty.kind {
- if let ty::Adt(def, _) = ty.kind {
- let field = def.all_fields().nth(field.index())?;
- // Use the HIR types to construct the diagnostic message.
- let hir_id = tcx.hir().as_local_hir_id(field.did)?;
- let node = tcx.hir().find(hir_id)?;
- // Now we're dealing with the actual struct that we're going to suggest a change to,
- // we can expect a field that is an immutable reference to a type.
- if let hir::Node::Field(field) = node {
- if let hir::TyKind::Rptr(lifetime, hir::MutTy {
- mutbl: hir::Mutability::Immutable,
- ref ty
- }) = field.ty.kind {
- // Get the snippets in two parts - the named lifetime (if there is one) and
- // type being referenced, that way we can reconstruct the snippet without loss
- // of detail.
- let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
- let lifetime_snippet = if !lifetime.is_elided() {
- format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
- } else {
- String::new()
- };
-
- return Some((
- field.ty.span,
- format!(
- "&{}mut {}",
- lifetime_snippet, &*type_snippet,
- ),
- ));
- }
- }
- }
- }
-
- None
-}
-
-/// If possible, suggest replacing `ref` with `ref mut`.
-fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
- let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
- if hi_src.starts_with("ref")
- && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace)
- {
- let replacement = format!("ref mut{}", &hi_src["ref".len()..]);
- Some(replacement)
- } else {
- None
- }
-}
+++ /dev/null
-use std::collections::VecDeque;
-use std::rc::Rc;
-
-use crate::borrow_check::nll::region_infer::{Cause, RegionInferenceContext};
-use crate::borrow_check::nll::ToRegionVid;
-use crate::util::liveness::{self, DefUse};
-use rustc::mir::visit::{MirVisitable, PlaceContext, Visitor};
-use rustc::mir::{Local, Location, Body};
-use rustc::ty::{RegionVid, TyCtxt};
-use rustc_data_structures::fx::FxHashSet;
-
-crate fn find<'tcx>(
- body: &Body<'tcx>,
- regioncx: &Rc<RegionInferenceContext<'tcx>>,
- tcx: TyCtxt<'tcx>,
- region_vid: RegionVid,
- start_point: Location,
-) -> Option<Cause> {
- let mut uf = UseFinder {
- body,
- regioncx,
- tcx,
- region_vid,
- start_point,
- };
-
- uf.find()
-}
-
-struct UseFinder<'cx, 'tcx> {
- body: &'cx Body<'tcx>,
- regioncx: &'cx Rc<RegionInferenceContext<'tcx>>,
- tcx: TyCtxt<'tcx>,
- region_vid: RegionVid,
- start_point: Location,
-}
-
-impl<'cx, 'tcx> UseFinder<'cx, 'tcx> {
- fn find(&mut self) -> Option<Cause> {
- let mut queue = VecDeque::new();
- let mut visited = FxHashSet::default();
-
- queue.push_back(self.start_point);
- while let Some(p) = queue.pop_front() {
- if !self.regioncx.region_contains(self.region_vid, p) {
- continue;
- }
-
- if !visited.insert(p) {
- continue;
- }
-
- let block_data = &self.body[p.block];
-
- match self.def_use(p, block_data.visitable(p.statement_index)) {
- Some(DefUseResult::Def) => {}
-
- Some(DefUseResult::UseLive { local }) => {
- return Some(Cause::LiveVar(local, p));
- }
-
- Some(DefUseResult::UseDrop { local }) => {
- return Some(Cause::DropVar(local, p));
- }
-
- None => {
- if p.statement_index < block_data.statements.len() {
- queue.push_back(p.successor_within_block());
- } else {
- queue.extend(
- block_data
- .terminator()
- .successors()
- .filter(|&bb| Some(&Some(*bb)) != block_data.terminator().unwind())
- .map(|&bb| Location {
- statement_index: 0,
- block: bb,
- }),
- );
- }
- }
- }
- }
-
- None
- }
-
- fn def_use(&self, location: Location, thing: &dyn MirVisitable<'tcx>) -> Option<DefUseResult> {
- let mut visitor = DefUseVisitor {
- body: self.body,
- tcx: self.tcx,
- region_vid: self.region_vid,
- def_use_result: None,
- };
-
- thing.apply(location, &mut visitor);
-
- visitor.def_use_result
- }
-}
-
-struct DefUseVisitor<'cx, 'tcx> {
- body: &'cx Body<'tcx>,
- tcx: TyCtxt<'tcx>,
- region_vid: RegionVid,
- def_use_result: Option<DefUseResult>,
-}
-
-enum DefUseResult {
- Def,
- UseLive { local: Local },
- UseDrop { local: Local },
-}
-
-impl<'cx, 'tcx> Visitor<'tcx> for DefUseVisitor<'cx, 'tcx> {
- fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
- let local_ty = self.body.local_decls[local].ty;
-
- let mut found_it = false;
- self.tcx.for_each_free_region(&local_ty, |r| {
- if r.to_region_vid() == self.region_vid {
- found_it = true;
- }
- });
-
- if found_it {
- self.def_use_result = match liveness::categorize(context) {
- Some(DefUse::Def) => Some(DefUseResult::Def),
- Some(DefUse::Use) => Some(DefUseResult::UseLive { local }),
- Some(DefUse::Drop) => Some(DefUseResult::UseDrop { local }),
- None => None,
- };
- }
- }
-}
+++ /dev/null
-use std::collections::VecDeque;
-
-use crate::borrow_check::borrow_set::BorrowData;
-use crate::borrow_check::error_reporting::UseSpans;
-use crate::borrow_check::nll::region_infer::{Cause, RegionName};
-use crate::borrow_check::nll::ConstraintDescription;
-use crate::borrow_check::{MirBorrowckCtxt, WriteKind};
-use rustc::mir::{
- CastKind, ConstraintCategory, FakeReadCause, Local, Location, Body, Operand, Place, Rvalue,
- Statement, StatementKind, TerminatorKind,
-};
-use rustc::ty::{self, TyCtxt};
-use rustc::ty::adjustment::{PointerCast};
-use rustc_index::vec::IndexVec;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::DiagnosticBuilder;
-use syntax_pos::Span;
-use syntax_pos::symbol::Symbol;
-
-mod find_use;
-
-#[derive(Debug)]
-pub(in crate::borrow_check) enum BorrowExplanation {
- UsedLater(LaterUseKind, Span),
- UsedLaterInLoop(LaterUseKind, Span),
- UsedLaterWhenDropped {
- drop_loc: Location,
- dropped_local: Local,
- should_note_order: bool,
- },
- MustBeValidFor {
- category: ConstraintCategory,
- from_closure: bool,
- span: Span,
- region_name: RegionName,
- opt_place_desc: Option<String>,
- },
- Unexplained,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub(in crate::borrow_check) enum LaterUseKind {
- TraitCapture,
- ClosureCapture,
- Call,
- FakeLetRead,
- Other,
-}
-
-impl BorrowExplanation {
- pub(in crate::borrow_check) fn is_explained(&self) -> bool {
- match self {
- BorrowExplanation::Unexplained => false,
- _ => true,
- }
- }
- pub(in crate::borrow_check) fn add_explanation_to_diagnostic<'tcx>(
- &self,
- tcx: TyCtxt<'tcx>,
- body: &Body<'tcx>,
- local_names: &IndexVec<Local, Option<Symbol>>,
- err: &mut DiagnosticBuilder<'_>,
- borrow_desc: &str,
- borrow_span: Option<Span>,
- ) {
- match *self {
- BorrowExplanation::UsedLater(later_use_kind, var_or_use_span) => {
- let message = match later_use_kind {
- LaterUseKind::TraitCapture => "captured here by trait object",
- LaterUseKind::ClosureCapture => "captured here by closure",
- LaterUseKind::Call => "used by call",
- LaterUseKind::FakeLetRead => "stored here",
- LaterUseKind::Other => "used here",
- };
- if !borrow_span.map(|sp| sp.overlaps(var_or_use_span)).unwrap_or(false) {
- err.span_label(
- var_or_use_span,
- format!("{}borrow later {}", borrow_desc, message),
- );
- }
- }
- BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span) => {
- let message = match later_use_kind {
- LaterUseKind::TraitCapture => {
- "borrow captured here by trait object, in later iteration of loop"
- }
- LaterUseKind::ClosureCapture => {
- "borrow captured here by closure, in later iteration of loop"
- }
- LaterUseKind::Call => "borrow used by call, in later iteration of loop",
- LaterUseKind::FakeLetRead => "borrow later stored here",
- LaterUseKind::Other => "borrow used here, in later iteration of loop",
- };
- err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message));
- }
- BorrowExplanation::UsedLaterWhenDropped {
- drop_loc,
- dropped_local,
- should_note_order,
- } => {
- let local_decl = &body.local_decls[dropped_local];
- let (dtor_desc, type_desc) = match local_decl.ty.kind {
- // If type is an ADT that implements Drop, then
- // simplify output by reporting just the ADT name.
- ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() => (
- "`Drop` code",
- format!("type `{}`", tcx.def_path_str(adt.did)),
- ),
-
- // Otherwise, just report the whole type (and use
- // the intentionally fuzzy phrase "destructor")
- ty::Closure(..) => ("destructor", "closure".to_owned()),
- ty::Generator(..) => ("destructor", "generator".to_owned()),
-
- _ => ("destructor", format!("type `{}`", local_decl.ty)),
- };
-
- match local_names[dropped_local] {
- Some(local_name) if !local_decl.from_compiler_desugaring() => {
- let message = format!(
- "{B}borrow might be used here, when `{LOC}` is dropped \
- and runs the {DTOR} for {TYPE}",
- B = borrow_desc,
- LOC = local_name,
- TYPE = type_desc,
- DTOR = dtor_desc
- );
- err.span_label(body.source_info(drop_loc).span, message);
-
- if should_note_order {
- err.note(
- "values in a scope are dropped \
- in the opposite order they are defined",
- );
- }
- }
- _ => {
- err.span_label(
- local_decl.source_info.span,
- format!(
- "a temporary with access to the {B}borrow \
- is created here ...",
- B = borrow_desc
- ),
- );
- let message = format!(
- "... and the {B}borrow might be used here, \
- when that temporary is dropped \
- and runs the {DTOR} for {TYPE}",
- B = borrow_desc,
- TYPE = type_desc,
- DTOR = dtor_desc
- );
- err.span_label(body.source_info(drop_loc).span, message);
-
- if let Some(info) = &local_decl.is_block_tail {
- // FIXME: use span_suggestion instead, highlighting the
- // whole block tail expression.
- let msg = if info.tail_result_is_ignored {
- "The temporary is part of an expression at the end of a block. \
- Consider adding semicolon after the expression so its temporaries \
- are dropped sooner, before the local variables declared by the \
- block are dropped."
- } else {
- "The temporary is part of an expression at the end of a block. \
- Consider forcing this temporary to be dropped sooner, before \
- the block's local variables are dropped. \
- For example, you could save the expression's value in a new \
- local variable `x` and then make `x` be the expression \
- at the end of the block."
- };
-
- err.note(msg);
- }
- }
- }
- }
- BorrowExplanation::MustBeValidFor {
- category,
- span,
- ref region_name,
- ref opt_place_desc,
- from_closure: _,
- } => {
- region_name.highlight_region_name(err);
-
- if let Some(desc) = opt_place_desc {
- err.span_label(
- span,
- format!(
- "{}requires that `{}` is borrowed for `{}`",
- category.description(),
- desc,
- region_name,
- ),
- );
- } else {
- err.span_label(
- span,
- format!(
- "{}requires that {}borrow lasts for `{}`",
- category.description(),
- borrow_desc,
- region_name,
- ),
- );
- };
- }
- _ => {}
- }
- }
-}
-
-impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
- /// Returns structured explanation for *why* the borrow contains the
- /// point from `location`. This is key for the "3-point errors"
- /// [described in the NLL RFC][d].
- ///
- /// # Parameters
- ///
- /// - `borrow`: the borrow in question
- /// - `location`: where the borrow occurs
- /// - `kind_place`: if Some, this describes the statement that triggered the error.
- /// - first half is the kind of write, if any, being performed
- /// - second half is the place being accessed
- ///
- /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
- pub(in crate::borrow_check) fn explain_why_borrow_contains_point(
- &self,
- location: Location,
- borrow: &BorrowData<'tcx>,
- kind_place: Option<(WriteKind, &Place<'tcx>)>,
- ) -> BorrowExplanation {
- debug!(
- "explain_why_borrow_contains_point(location={:?}, borrow={:?}, kind_place={:?})",
- location, borrow, kind_place
- );
-
- let regioncx = &self.nonlexical_regioncx;
- let body: &Body<'_> = &self.body;
- let tcx = self.infcx.tcx;
-
- let borrow_region_vid = borrow.region;
- debug!(
- "explain_why_borrow_contains_point: borrow_region_vid={:?}",
- borrow_region_vid
- );
-
- let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, location);
- debug!(
- "explain_why_borrow_contains_point: region_sub={:?}",
- region_sub
- );
-
- match find_use::find(body, regioncx, tcx, region_sub, location) {
- Some(Cause::LiveVar(local, location)) => {
- let span = body.source_info(location).span;
- let spans = self
- .move_spans(Place::from(local).as_ref(), location)
- .or_else(|| self.borrow_spans(span, location));
-
- let borrow_location = location;
- if self.is_use_in_later_iteration_of_loop(borrow_location, location) {
- let later_use = self.later_use_kind(borrow, spans, location);
- BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1)
- } else {
- // Check if the location represents a `FakeRead`, and adapt the error
- // message to the `FakeReadCause` it is from: in particular,
- // the ones inserted in optimized `let var = <expr>` patterns.
- let later_use = self.later_use_kind(borrow, spans, location);
- BorrowExplanation::UsedLater(later_use.0, later_use.1)
- }
- }
-
- Some(Cause::DropVar(local, location)) => {
- let mut should_note_order = false;
- if self.local_names[local].is_some() {
- if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
- if let Some(borrowed_local) = place.as_local() {
- if self.local_names[borrowed_local].is_some()
- && local != borrowed_local
- {
- should_note_order = true;
- }
- }
- }
- }
-
- BorrowExplanation::UsedLaterWhenDropped {
- drop_loc: location,
- dropped_local: local,
- should_note_order,
- }
- }
-
- None => {
- if let Some(region) = regioncx.to_error_region_vid(borrow_region_vid) {
- let (category, from_closure, span, region_name) =
- self.nonlexical_regioncx.free_region_constraint_info(
- &self.body,
- &self.local_names,
- &self.upvars,
- self.mir_def_id,
- self.infcx,
- borrow_region_vid,
- region,
- );
- if let Some(region_name) = region_name {
- let opt_place_desc =
- self.describe_place(borrow.borrowed_place.as_ref());
- BorrowExplanation::MustBeValidFor {
- category,
- from_closure,
- span,
- region_name,
- opt_place_desc,
- }
- } else {
- debug!("explain_why_borrow_contains_point: \
- Could not generate a region name");
- BorrowExplanation::Unexplained
- }
- } else {
- debug!("explain_why_borrow_contains_point: \
- Could not generate an error region vid");
- BorrowExplanation::Unexplained
- }
- }
- }
- }
-
- /// true if `borrow_location` can reach `use_location` by going through a loop and
- /// `use_location` is also inside of that loop
- fn is_use_in_later_iteration_of_loop(
- &self,
- borrow_location: Location,
- use_location: Location,
- ) -> bool {
- let back_edge = self.reach_through_backedge(borrow_location, use_location);
- back_edge.map_or(false, |back_edge| {
- self.can_reach_head_of_loop(use_location, back_edge)
- })
- }
-
- /// Returns the outmost back edge if `from` location can reach `to` location passing through
- /// that back edge
- fn reach_through_backedge(&self, from: Location, to: Location) -> Option<Location> {
- let mut visited_locations = FxHashSet::default();
- let mut pending_locations = VecDeque::new();
- visited_locations.insert(from);
- pending_locations.push_back(from);
- debug!("reach_through_backedge: from={:?} to={:?}", from, to,);
-
- let mut outmost_back_edge = None;
- while let Some(location) = pending_locations.pop_front() {
- debug!(
- "reach_through_backedge: location={:?} outmost_back_edge={:?}
- pending_locations={:?} visited_locations={:?}",
- location, outmost_back_edge, pending_locations, visited_locations
- );
-
- if location == to && outmost_back_edge.is_some() {
- // We've managed to reach the use location
- debug!("reach_through_backedge: found!");
- return outmost_back_edge;
- }
-
- let block = &self.body.basic_blocks()[location.block];
-
- if location.statement_index < block.statements.len() {
- let successor = location.successor_within_block();
- if visited_locations.insert(successor) {
- pending_locations.push_back(successor);
- }
- } else {
- pending_locations.extend(
- block
- .terminator()
- .successors()
- .map(|bb| Location {
- statement_index: 0,
- block: *bb,
- })
- .filter(|s| visited_locations.insert(*s))
- .map(|s| {
- if self.is_back_edge(location, s) {
- match outmost_back_edge {
- None => {
- outmost_back_edge = Some(location);
- }
-
- Some(back_edge)
- if location.dominates(back_edge, &self.dominators) =>
- {
- outmost_back_edge = Some(location);
- }
-
- Some(_) => {}
- }
- }
-
- s
- }),
- );
- }
- }
-
- None
- }
-
- /// true if `from` location can reach `loop_head` location and `loop_head` dominates all the
- /// intermediate nodes
- fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool {
- self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default())
- }
-
- fn find_loop_head_dfs(
- &self,
- from: Location,
- loop_head: Location,
- visited_locations: &mut FxHashSet<Location>,
- ) -> bool {
- visited_locations.insert(from);
-
- if from == loop_head {
- return true;
- }
-
- if loop_head.dominates(from, &self.dominators) {
- let block = &self.body.basic_blocks()[from.block];
-
- if from.statement_index < block.statements.len() {
- let successor = from.successor_within_block();
-
- if !visited_locations.contains(&successor)
- && self.find_loop_head_dfs(successor, loop_head, visited_locations)
- {
- return true;
- }
- } else {
- for bb in block.terminator().successors() {
- let successor = Location {
- statement_index: 0,
- block: *bb,
- };
-
- if !visited_locations.contains(&successor)
- && self.find_loop_head_dfs(successor, loop_head, visited_locations)
- {
- return true;
- }
- }
- }
- }
-
- false
- }
-
- /// True if an edge `source -> target` is a backedge -- in other words, if the target
- /// dominates the source.
- fn is_back_edge(&self, source: Location, target: Location) -> bool {
- target.dominates(source, &self.dominators)
- }
-
- /// Determine how the borrow was later used.
- fn later_use_kind(
- &self,
- borrow: &BorrowData<'tcx>,
- use_spans: UseSpans,
- location: Location,
- ) -> (LaterUseKind, Span) {
- match use_spans {
- UseSpans::ClosureUse { var_span, .. } => {
- // Used in a closure.
- (LaterUseKind::ClosureCapture, var_span)
- }
- UseSpans::OtherUse(span) => {
- let block = &self.body.basic_blocks()[location.block];
-
- let kind = if let Some(&Statement {
- kind: StatementKind::FakeRead(FakeReadCause::ForLet, _),
- ..
- }) = block.statements.get(location.statement_index)
- {
- LaterUseKind::FakeLetRead
- } else if self.was_captured_by_trait_object(borrow) {
- LaterUseKind::TraitCapture
- } else if location.statement_index == block.statements.len() {
- if let TerminatorKind::Call {
- ref func,
- from_hir_call: true,
- ..
- } = block.terminator().kind
- {
- // Just point to the function, to reduce the chance of overlapping spans.
- let function_span = match func {
- Operand::Constant(c) => c.span,
- Operand::Copy(place) |
- Operand::Move(place) => {
- if let Some(l) = place.as_local() {
- let local_decl = &self.body.local_decls[l];
- if self.local_names[l].is_none() {
- local_decl.source_info.span
- } else {
- span
- }
- } else {
- span
- }
- }
- };
- return (LaterUseKind::Call, function_span);
- } else {
- LaterUseKind::Other
- }
- } else {
- LaterUseKind::Other
- };
-
- (kind, span)
- }
- }
- }
-
- /// Checks if a borrowed value was captured by a trait object. We do this by
- /// looking forward in the MIR from the reserve location and checking if we see
- /// a unsized cast to a trait object on our data.
- fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool {
- // Start at the reserve location, find the place that we want to see cast to a trait object.
- let location = borrow.reserve_location;
- let block = &self.body[location.block];
- let stmt = block.statements.get(location.statement_index);
- debug!(
- "was_captured_by_trait_object: location={:?} stmt={:?}",
- location, stmt
- );
-
- // We make a `queue` vector that has the locations we want to visit. As of writing, this
- // will only ever have one item at any given time, but by using a vector, we can pop from
- // it which simplifies the termination logic.
- let mut queue = vec![location];
- let mut target = if let Some(&Statement {
- kind: StatementKind::Assign(box(ref place, _)),
- ..
- }) = stmt {
- if let Some(local) = place.as_local() {
- local
- } else {
- return false;
- }
- } else {
- return false;
- };
-
- debug!(
- "was_captured_by_trait: target={:?} queue={:?}",
- target, queue
- );
- while let Some(current_location) = queue.pop() {
- debug!("was_captured_by_trait: target={:?}", target);
- let block = &self.body[current_location.block];
- // We need to check the current location to find out if it is a terminator.
- let is_terminator = current_location.statement_index == block.statements.len();
- if !is_terminator {
- let stmt = &block.statements[current_location.statement_index];
- debug!("was_captured_by_trait_object: stmt={:?}", stmt);
-
- // The only kind of statement that we care about is assignments...
- if let StatementKind::Assign(box(place, rvalue)) = &stmt.kind {
- let into = match place.local_or_deref_local() {
- Some(into) => into,
- None => {
- // Continue at the next location.
- queue.push(current_location.successor_within_block());
- continue;
- }
- };
-
- match rvalue {
- // If we see a use, we should check whether it is our data, and if so
- // update the place that we're looking for to that new place.
- Rvalue::Use(operand) => match operand {
- Operand::Copy(place)
- | Operand::Move(place) => {
- if let Some(from) = place.as_local() {
- if from == target {
- target = into;
- }
- }
- }
- _ => {}
- },
- // If we see a unsized cast, then if it is our data we should check
- // whether it is being cast to a trait object.
- Rvalue::Cast(
- CastKind::Pointer(PointerCast::Unsize), operand, ty
- ) => match operand {
- Operand::Copy(place)
- | Operand::Move(place) => {
- if let Some(from) = place.as_local() {
- if from == target {
- debug!("was_captured_by_trait_object: ty={:?}", ty);
- // Check the type for a trait object.
- return match ty.kind {
- // `&dyn Trait`
- ty::Ref(_, ty, _) if ty.is_trait() => true,
- // `Box<dyn Trait>`
- _ if ty.is_box() && ty.boxed_ty().is_trait() => true,
- // `dyn Trait`
- _ if ty.is_trait() => true,
- // Anything else.
- _ => false,
- };
- }
- }
- return false;
- }
- _ => return false,
- },
- _ => {}
- }
- }
-
- // Continue at the next location.
- queue.push(current_location.successor_within_block());
- } else {
- // The only thing we need to do for terminators is progress to the next block.
- let terminator = block.terminator();
- debug!("was_captured_by_trait_object: terminator={:?}", terminator);
-
- if let TerminatorKind::Call {
- destination: Some((place, block)),
- args,
- ..
- } = &terminator.kind {
- if let Some(dest) = place.as_local() {
- debug!(
- "was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
- target, dest, args
- );
- // Check if one of the arguments to this function is the target place.
- let found_target = args.iter().any(|arg| {
- if let Operand::Move(place) = arg {
- if let Some(potential) = place.as_local() {
- potential == target
- } else {
- false
- }
- } else {
- false
- }
- });
-
- // If it is, follow this to the next block and update the target.
- if found_target {
- target = dest;
- queue.push(block.start_location());
- }
- }
- }
- }
-
- debug!("was_captured_by_trait: queue={:?}", queue);
- }
-
- // We didn't find anything and ran out of locations to check.
- false
- }
-}
use crate::util::pretty;
mod constraint_generation;
-pub mod explain_borrow;
mod facts;
mod invalidation;
-crate mod region_infer;
mod renumber;
-crate mod type_check;
-mod universal_regions;
-mod constraints;
mod member_constraints;
+crate mod constraints;
+crate mod universal_regions;
+crate mod type_check;
+crate mod region_infer;
+
use self::facts::AllFacts;
use self::region_infer::RegionInferenceContext;
use self::universal_regions::UniversalRegions;
+++ /dev/null
-use crate::borrow_check::nll::constraints::OutlivesConstraint;
-use crate::borrow_check::nll::region_infer::RegionInferenceContext;
-use crate::borrow_check::nll::type_check::Locations;
-use crate::borrow_check::nll::universal_regions::DefiningTy;
-use crate::borrow_check::nll::ConstraintDescription;
-use crate::borrow_check::Upvar;
-use crate::util::borrowck_errors;
-use rustc::hir::def_id::DefId;
-use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
-use rustc::infer::InferCtxt;
-use rustc::infer::NLLRegionVariableOrigin;
-use rustc::mir::{ConstraintCategory, Local, Location, Body};
-use rustc::ty::{self, RegionVid};
-use rustc_index::vec::IndexVec;
-use rustc_errors::DiagnosticBuilder;
-use std::collections::VecDeque;
-use syntax::errors::Applicability;
-use syntax::symbol::kw;
-use syntax_pos::Span;
-use syntax_pos::symbol::Symbol;
-
-use self::outlives_suggestion::OutlivesSuggestionBuilder;
-
-pub mod outlives_suggestion;
-
-mod region_name;
-mod var_name;
-
-crate use self::region_name::{RegionName, RegionNameSource, RegionErrorNamingCtx};
-
-impl ConstraintDescription for ConstraintCategory {
- fn description(&self) -> &'static str {
- // Must end with a space. Allows for empty names to be provided.
- match self {
- ConstraintCategory::Assignment => "assignment ",
- ConstraintCategory::Return => "returning this value ",
- ConstraintCategory::Yield => "yielding this value ",
- ConstraintCategory::UseAsConst => "using this value as a constant ",
- ConstraintCategory::UseAsStatic => "using this value as a static ",
- ConstraintCategory::Cast => "cast ",
- ConstraintCategory::CallArgument => "argument ",
- ConstraintCategory::TypeAnnotation => "type annotation ",
- ConstraintCategory::ClosureBounds => "closure body ",
- ConstraintCategory::SizedBound => "proving this value is `Sized` ",
- ConstraintCategory::CopyBound => "copying this value ",
- ConstraintCategory::OpaqueType => "opaque type ",
- ConstraintCategory::Boring
- | ConstraintCategory::BoringNoLocation
- | ConstraintCategory::Internal => "",
- }
- }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq)]
-enum Trace {
- StartRegion,
- FromOutlivesConstraint(OutlivesConstraint),
- NotVisited,
-}
-
-/// Various pieces of state used when reporting borrow checker errors.
-pub struct ErrorReportingCtx<'a, 'b, 'tcx> {
- /// The region inference context used for borrow chekcing this MIR body.
- region_infcx: &'b RegionInferenceContext<'tcx>,
-
- /// The inference context used for type checking.
- infcx: &'b InferCtxt<'a, 'tcx>,
-
- /// The MIR def we are reporting errors on.
- mir_def_id: DefId,
-
- /// The MIR body we are reporting errors on (for convenience).
- body: &'b Body<'tcx>,
-
- /// User variable names for MIR locals (where applicable).
- local_names: &'b IndexVec<Local, Option<Symbol>>,
-
- /// Any upvars for the MIR body we have kept track of during borrow checking.
- upvars: &'b [Upvar],
-}
-
-/// Information about the various region constraints involved in a borrow checker error.
-#[derive(Clone, Debug)]
-pub struct ErrorConstraintInfo {
- // fr: outlived_fr
- fr: RegionVid,
- fr_is_local: bool,
- outlived_fr: RegionVid,
- outlived_fr_is_local: bool,
-
- // Category and span for best blame constraint
- category: ConstraintCategory,
- span: Span,
-}
-
-impl<'tcx> RegionInferenceContext<'tcx> {
- /// Tries to find the best constraint to blame for the fact that
- /// `R: from_region`, where `R` is some region that meets
- /// `target_test`. This works by following the constraint graph,
- /// creating a constraint path that forces `R` to outlive
- /// `from_region`, and then finding the best choices within that
- /// path to blame.
- fn best_blame_constraint(
- &self,
- body: &Body<'tcx>,
- from_region: RegionVid,
- from_region_origin: NLLRegionVariableOrigin,
- target_test: impl Fn(RegionVid) -> bool,
- ) -> (ConstraintCategory, bool, Span) {
- debug!("best_blame_constraint(from_region={:?}, from_region_origin={:?})",
- from_region, from_region_origin);
-
- // Find all paths
- let (path, target_region) =
- self.find_constraint_paths_between_regions(from_region, target_test)
- .unwrap();
- debug!(
- "best_blame_constraint: path={:#?}",
- path.iter()
- .map(|&c| format!(
- "{:?} ({:?}: {:?})",
- c,
- self.constraint_sccs.scc(c.sup),
- self.constraint_sccs.scc(c.sub),
- ))
- .collect::<Vec<_>>()
- );
-
- // Classify each of the constraints along the path.
- let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path.iter()
- .map(|constraint| {
- if constraint.category == ConstraintCategory::ClosureBounds {
- self.retrieve_closure_constraint_info(body, &constraint)
- } else {
- (constraint.category, false, constraint.locations.span(body))
- }
- })
- .collect();
- debug!(
- "best_blame_constraint: categorized_path={:#?}",
- categorized_path
- );
-
- // To find the best span to cite, we first try to look for the
- // final constraint that is interesting and where the `sup` is
- // not unified with the ultimate target region. The reason
- // for this is that we have a chain of constraints that lead
- // from the source to the target region, something like:
- //
- // '0: '1 ('0 is the source)
- // '1: '2
- // '2: '3
- // '3: '4
- // '4: '5
- // '5: '6 ('6 is the target)
- //
- // Some of those regions are unified with `'6` (in the same
- // SCC). We want to screen those out. After that point, the
- // "closest" constraint we have to the end is going to be the
- // most likely to be the point where the value escapes -- but
- // we still want to screen for an "interesting" point to
- // highlight (e.g., a call site or something).
- let target_scc = self.constraint_sccs.scc(target_region);
- let mut range = 0..path.len();
-
- // As noted above, when reporting an error, there is typically a chain of constraints
- // leading from some "source" region which must outlive some "target" region.
- // In most cases, we prefer to "blame" the constraints closer to the target --
- // but there is one exception. When constraints arise from higher-ranked subtyping,
- // we generally prefer to blame the source value,
- // as the "target" in this case tends to be some type annotation that the user gave.
- // Therefore, if we find that the region origin is some instantiation
- // of a higher-ranked region, we start our search from the "source" point
- // rather than the "target", and we also tweak a few other things.
- //
- // An example might be this bit of Rust code:
- //
- // ```rust
- // let x: fn(&'static ()) = |_| {};
- // let y: for<'a> fn(&'a ()) = x;
- // ```
- //
- // In MIR, this will be converted into a combination of assignments and type ascriptions.
- // In particular, the 'static is imposed through a type ascription:
- //
- // ```rust
- // x = ...;
- // AscribeUserType(x, fn(&'static ())
- // y = x;
- // ```
- //
- // We wind up ultimately with constraints like
- //
- // ```rust
- // !a: 'temp1 // from the `y = x` statement
- // 'temp1: 'temp2
- // 'temp2: 'static // from the AscribeUserType
- // ```
- //
- // and here we prefer to blame the source (the y = x statement).
- let blame_source = match from_region_origin {
- NLLRegionVariableOrigin::FreeRegion
- | NLLRegionVariableOrigin::Existential { from_forall: false } => {
- true
- }
- NLLRegionVariableOrigin::Placeholder(_)
- | NLLRegionVariableOrigin::Existential { from_forall: true } => {
- false
- }
- };
-
- let find_region = |i: &usize| {
- let constraint = path[*i];
-
- let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
-
- if blame_source {
- match categorized_path[*i].0 {
- ConstraintCategory::OpaqueType | ConstraintCategory::Boring |
- ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false,
- ConstraintCategory::TypeAnnotation | ConstraintCategory::Return |
- ConstraintCategory::Yield => true,
- _ => constraint_sup_scc != target_scc,
- }
- } else {
- match categorized_path[*i].0 {
- ConstraintCategory::OpaqueType | ConstraintCategory::Boring |
- ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false,
- _ => true
- }
- }
- };
-
- let best_choice = if blame_source {
- range.rev().find(find_region)
- } else {
- range.find(find_region)
- };
-
- debug!("best_blame_constraint: best_choice={:?} blame_source={}",
- best_choice, blame_source);
-
- if let Some(i) = best_choice {
- if let Some(next) = categorized_path.get(i + 1) {
- if categorized_path[i].0 == ConstraintCategory::Return
- && next.0 == ConstraintCategory::OpaqueType
- {
- // The return expression is being influenced by the return type being
- // impl Trait, point at the return type and not the return expr.
- return *next;
- }
- }
- return categorized_path[i];
- }
-
- // If that search fails, that is.. unusual. Maybe everything
- // is in the same SCC or something. In that case, find what
- // appears to be the most interesting point to report to the
- // user via an even more ad-hoc guess.
- categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
- debug!("`: sorted_path={:#?}", categorized_path);
-
- *categorized_path.first().unwrap()
- }
-
- /// Walks the graph of constraints (where `'a: 'b` is considered
- /// an edge `'a -> 'b`) to find all paths from `from_region` to
- /// `to_region`. The paths are accumulated into the vector
- /// `results`. The paths are stored as a series of
- /// `ConstraintIndex` values -- in other words, a list of *edges*.
- ///
- /// Returns: a series of constraints as well as the region `R`
- /// that passed the target test.
- fn find_constraint_paths_between_regions(
- &self,
- from_region: RegionVid,
- target_test: impl Fn(RegionVid) -> bool,
- ) -> Option<(Vec<OutlivesConstraint>, RegionVid)> {
- let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
- context[from_region] = Trace::StartRegion;
-
- // Use a deque so that we do a breadth-first search. We will
- // stop at the first match, which ought to be the shortest
- // path (fewest constraints).
- let mut deque = VecDeque::new();
- deque.push_back(from_region);
-
- while let Some(r) = deque.pop_front() {
- debug!(
- "find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
- from_region,
- r,
- self.region_value_str(r),
- );
-
- // Check if we reached the region we were looking for. If so,
- // we can reconstruct the path that led to it and return it.
- if target_test(r) {
- let mut result = vec![];
- let mut p = r;
- loop {
- match context[p] {
- Trace::NotVisited => {
- bug!("found unvisited region {:?} on path to {:?}", p, r)
- }
-
- Trace::FromOutlivesConstraint(c) => {
- result.push(c);
- p = c.sup;
- }
-
- Trace::StartRegion => {
- result.reverse();
- return Some((result, r));
- }
- }
- }
- }
-
- // Otherwise, walk over the outgoing constraints and
- // enqueue any regions we find, keeping track of how we
- // reached them.
-
- // A constraint like `'r: 'x` can come from our constraint
- // graph.
- let fr_static = self.universal_regions.fr_static;
- let outgoing_edges_from_graph = self.constraint_graph
- .outgoing_edges(r, &self.constraints, fr_static);
-
- // Always inline this closure because it can be hot.
- let mut handle_constraint = #[inline(always)] |constraint: OutlivesConstraint| {
- debug_assert_eq!(constraint.sup, r);
- let sub_region = constraint.sub;
- if let Trace::NotVisited = context[sub_region] {
- context[sub_region] = Trace::FromOutlivesConstraint(constraint);
- deque.push_back(sub_region);
- }
- };
-
- // This loop can be hot.
- for constraint in outgoing_edges_from_graph {
- handle_constraint(constraint);
- }
-
- // Member constraints can also give rise to `'r: 'x` edges that
- // were not part of the graph initially, so watch out for those.
- // (But they are extremely rare; this loop is very cold.)
- for constraint in self.applied_member_constraints(r) {
- let p_c = &self.member_constraints[constraint.member_constraint_index];
- let constraint = OutlivesConstraint {
- sup: r,
- sub: constraint.min_choice,
- locations: Locations::All(p_c.definition_span),
- category: ConstraintCategory::OpaqueType,
- };
- handle_constraint(constraint);
- }
- }
-
- None
- }
-
- /// Report an error because the universal region `fr` was required to outlive
- /// `outlived_fr` but it is not known to do so. For example:
- ///
- /// ```
- /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
- /// ```
- ///
- /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
- pub(super) fn report_error<'a>(
- &'a self,
- body: &Body<'tcx>,
- local_names: &IndexVec<Local, Option<Symbol>>,
- upvars: &[Upvar],
- infcx: &'a InferCtxt<'a, 'tcx>,
- mir_def_id: DefId,
- fr: RegionVid,
- fr_origin: NLLRegionVariableOrigin,
- outlived_fr: RegionVid,
- outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
- renctx: &mut RegionErrorNamingCtx,
- ) -> DiagnosticBuilder<'a> {
- debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
-
- let (category, _, span) = self.best_blame_constraint(body, fr, fr_origin, |r| {
- self.provides_universal_region(r, fr, outlived_fr)
- });
-
- debug!("report_error: category={:?} {:?}", category, span);
- // Check if we can use one of the "nice region errors".
- if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
- let tables = infcx.tcx.typeck_tables_of(mir_def_id);
- let nice = NiceRegionError::new_from_span(infcx, span, o, f, Some(tables));
- if let Some(diag) = nice.try_report_from_nll() {
- return diag;
- }
- }
-
- let (fr_is_local, outlived_fr_is_local): (bool, bool) = (
- self.universal_regions.is_local_free_region(fr),
- self.universal_regions.is_local_free_region(outlived_fr),
- );
-
- debug!(
- "report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
- fr_is_local, outlived_fr_is_local, category
- );
-
- let errctx = ErrorReportingCtx {
- region_infcx: self,
- infcx,
- mir_def_id,
- body,
- local_names,
- upvars,
- };
-
- let errci = ErrorConstraintInfo {
- fr, outlived_fr, fr_is_local, outlived_fr_is_local, category, span
- };
-
- match (category, fr_is_local, outlived_fr_is_local) {
- (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => {
- self.report_fnmut_error(&errctx, &errci, renctx)
- }
- (ConstraintCategory::Assignment, true, false)
- | (ConstraintCategory::CallArgument, true, false) => {
- let mut db = self.report_escaping_data_error(&errctx, &errci, renctx);
-
- outlives_suggestion.intermediate_suggestion(&errctx, &errci, renctx, &mut db);
- outlives_suggestion.collect_constraint(fr, outlived_fr);
-
- db
- }
- _ => {
- let mut db = self.report_general_error(&errctx, &errci, renctx);
-
- outlives_suggestion.intermediate_suggestion(&errctx, &errci, renctx, &mut db);
- outlives_suggestion.collect_constraint(fr, outlived_fr);
-
- db
- }
- }
- }
-
- /// We have a constraint `fr1: fr2` that is not satisfied, where
- /// `fr2` represents some universal region. Here, `r` is some
- /// region where we know that `fr1: r` and this function has the
- /// job of determining whether `r` is "to blame" for the fact that
- /// `fr1: fr2` is required.
- ///
- /// This is true under two conditions:
- ///
- /// - `r == fr2`
- /// - `fr2` is `'static` and `r` is some placeholder in a universe
- /// that cannot be named by `fr1`; in that case, we will require
- /// that `fr1: 'static` because it is the only way to `fr1: r` to
- /// be satisfied. (See `add_incompatible_universe`.)
- fn provides_universal_region(&self, r: RegionVid, fr1: RegionVid, fr2: RegionVid) -> bool {
- debug!(
- "provides_universal_region(r={:?}, fr1={:?}, fr2={:?})",
- r, fr1, fr2
- );
- let result = {
- r == fr2 || {
- fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r)
- }
- };
- debug!("provides_universal_region: result = {:?}", result);
- result
- }
-
- /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
- /// This function expects `fr` to be local and `outlived_fr` to not be local.
- ///
- /// ```text
- /// error: captured variable cannot escape `FnMut` closure body
- /// --> $DIR/issue-53040.rs:15:8
- /// |
- /// LL | || &mut v;
- /// | -- ^^^^^^ creates a reference to a captured variable which escapes the closure body
- /// | |
- /// | inferred to be a `FnMut` closure
- /// |
- /// = note: `FnMut` closures only have access to their captured variables while they are
- /// executing...
- /// = note: ...therefore, returned references to captured variables will escape the closure
- /// ```
- fn report_fnmut_error(
- &self,
- errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
- errci: &ErrorConstraintInfo,
- renctx: &mut RegionErrorNamingCtx,
- ) -> DiagnosticBuilder<'_> {
- let ErrorConstraintInfo {
- outlived_fr, span, ..
- } = errci;
-
- let mut diag = errctx
- .infcx
- .tcx
- .sess
- .struct_span_err(*span, "captured variable cannot escape `FnMut` closure body");
-
- // We should check if the return type of this closure is in fact a closure - in that
- // case, we can special case the error further.
- let return_type_is_closure = self.universal_regions.unnormalized_output_ty.is_closure();
- let message = if return_type_is_closure {
- "returns a closure that contains a reference to a captured variable, which then \
- escapes the closure body"
- } else {
- "returns a reference to a captured variable which escapes the closure body"
- };
-
- diag.span_label(*span, message);
-
- match self.give_region_a_name(errctx, renctx, *outlived_fr).unwrap().source {
- RegionNameSource::NamedEarlyBoundRegion(fr_span)
- | RegionNameSource::NamedFreeRegion(fr_span)
- | RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _)
- | RegionNameSource::CannotMatchHirTy(fr_span, _)
- | RegionNameSource::MatchedHirTy(fr_span)
- | RegionNameSource::MatchedAdtAndSegment(fr_span)
- | RegionNameSource::AnonRegionFromUpvar(fr_span, _)
- | RegionNameSource::AnonRegionFromOutput(fr_span, _, _) => {
- diag.span_label(fr_span, "inferred to be a `FnMut` closure");
- }
- _ => {}
- }
-
- diag.note(
- "`FnMut` closures only have access to their captured variables while they are \
- executing...",
- );
- diag.note("...therefore, they cannot allow references to captured variables to escape");
-
- diag
- }
-
- /// Reports a error specifically for when data is escaping a closure.
- ///
- /// ```text
- /// error: borrowed data escapes outside of function
- /// --> $DIR/lifetime-bound-will-change-warning.rs:44:5
- /// |
- /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) {
- /// | - `x` is a reference that is only valid in the function body
- /// LL | // but ref_obj will not, so warn.
- /// LL | ref_obj(x)
- /// | ^^^^^^^^^^ `x` escapes the function body here
- /// ```
- fn report_escaping_data_error(
- &self,
- errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
- errci: &ErrorConstraintInfo,
- renctx: &mut RegionErrorNamingCtx,
- ) -> DiagnosticBuilder<'_> {
- let ErrorReportingCtx {
- infcx, body, upvars, local_names, ..
- } = errctx;
-
- let ErrorConstraintInfo {
- span, category, ..
- } = errci;
-
- let fr_name_and_span =
- self.get_var_name_and_span_for_region(infcx.tcx, body, local_names, upvars, errci.fr);
- let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
- infcx.tcx,
- body,
- local_names,
- upvars,
- errci.outlived_fr,
- );
-
- let escapes_from = match self.universal_regions.defining_ty {
- DefiningTy::Closure(..) => "closure",
- DefiningTy::Generator(..) => "generator",
- DefiningTy::FnDef(..) => "function",
- DefiningTy::Const(..) => "const",
- };
-
- // Revert to the normal error in these cases.
- // Assignments aren't "escapes" in function items.
- if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none())
- || (*category == ConstraintCategory::Assignment && escapes_from == "function")
- || escapes_from == "const"
- {
- return self.report_general_error(
- errctx,
- &ErrorConstraintInfo {
- fr_is_local: true,
- outlived_fr_is_local: false,
- .. *errci
- },
- renctx,
- );
- }
-
- let mut diag = borrowck_errors::borrowed_data_escapes_closure(
- infcx.tcx,
- *span,
- escapes_from,
- );
-
- if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
- diag.span_label(
- outlived_fr_span,
- format!(
- "`{}` is declared here, outside of the {} body",
- outlived_fr_name, escapes_from
- ),
- );
- }
-
- if let Some((Some(fr_name), fr_span)) = fr_name_and_span {
- diag.span_label(
- fr_span,
- format!(
- "`{}` is a reference that is only valid in the {} body",
- fr_name, escapes_from
- ),
- );
-
- diag.span_label(
- *span,
- format!("`{}` escapes the {} body here", fr_name, escapes_from),
- );
- }
-
- diag
- }
-
- /// Reports a region inference error for the general case with named/synthesized lifetimes to
- /// explain what is happening.
- ///
- /// ```text
- /// error: unsatisfied lifetime constraints
- /// --> $DIR/regions-creating-enums3.rs:17:5
- /// |
- /// LL | fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> {
- /// | -- -- lifetime `'b` defined here
- /// | |
- /// | lifetime `'a` defined here
- /// LL | ast::add(x, y)
- /// | ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
- /// | is returning data with lifetime `'b`
- /// ```
- fn report_general_error(
- &self,
- errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
- errci: &ErrorConstraintInfo,
- renctx: &mut RegionErrorNamingCtx,
- ) -> DiagnosticBuilder<'_> {
- let ErrorReportingCtx {
- infcx, mir_def_id, ..
- } = errctx;
- let ErrorConstraintInfo {
- fr, fr_is_local, outlived_fr, outlived_fr_is_local, span, category, ..
- } = errci;
-
- let mut diag = infcx.tcx.sess.struct_span_err(
- *span,
- "lifetime may not live long enough"
- );
-
- let mir_def_name = if infcx.tcx.is_closure(*mir_def_id) {
- "closure"
- } else {
- "function"
- };
-
- let fr_name = self.give_region_a_name(errctx, renctx, *fr).unwrap();
- fr_name.highlight_region_name(&mut diag);
- let outlived_fr_name = self.give_region_a_name(errctx, renctx, *outlived_fr).unwrap();
- outlived_fr_name.highlight_region_name(&mut diag);
-
- match (category, outlived_fr_is_local, fr_is_local) {
- (ConstraintCategory::Return, true, _) => {
- diag.span_label(
- *span,
- format!(
- "{} was supposed to return data with lifetime `{}` but it is returning \
- data with lifetime `{}`",
- mir_def_name, outlived_fr_name, fr_name
- ),
- );
- }
- _ => {
- diag.span_label(
- *span,
- format!(
- "{}requires that `{}` must outlive `{}`",
- category.description(),
- fr_name,
- outlived_fr_name,
- ),
- );
- }
- }
-
- self.add_static_impl_trait_suggestion(infcx, &mut diag, *fr, fr_name, *outlived_fr);
-
- diag
- }
-
- /// Adds a suggestion to errors where a `impl Trait` is returned.
- ///
- /// ```text
- /// help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as
- /// a constraint
- /// |
- /// LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
- /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- /// ```
- fn add_static_impl_trait_suggestion(
- &self,
- infcx: &InferCtxt<'_, 'tcx>,
- diag: &mut DiagnosticBuilder<'_>,
- fr: RegionVid,
- // We need to pass `fr_name` - computing it again will label it twice.
- fr_name: RegionName,
- outlived_fr: RegionVid,
- ) {
- if let (Some(f), Some(ty::RegionKind::ReStatic)) =
- (self.to_error_region(fr), self.to_error_region(outlived_fr))
- {
- if let Some((ty::TyS {
- kind: ty::Opaque(did, substs),
- ..
- }, _)) = infcx
- .tcx
- .is_suitable_region(f)
- .map(|r| r.def_id)
- .map(|id| infcx.tcx.return_type_impl_trait(id))
- .unwrap_or(None)
- {
- // Check whether or not the impl trait return type is intended to capture
- // data with the static lifetime.
- //
- // eg. check for `impl Trait + 'static` instead of `impl Trait`.
- let has_static_predicate = {
- let predicates_of = infcx.tcx.predicates_of(*did);
- let bounds = predicates_of.instantiate(infcx.tcx, substs);
-
- let mut found = false;
- for predicate in bounds.predicates {
- if let ty::Predicate::TypeOutlives(binder) = predicate {
- if let ty::OutlivesPredicate(_, ty::RegionKind::ReStatic) =
- binder.skip_binder()
- {
- found = true;
- break;
- }
- }
- }
-
- found
- };
-
- debug!(
- "add_static_impl_trait_suggestion: has_static_predicate={:?}",
- has_static_predicate
- );
- let static_str = kw::StaticLifetime;
- // If there is a static predicate, then the only sensible suggestion is to replace
- // fr with `'static`.
- if has_static_predicate {
- diag.help(&format!(
- "consider replacing `{}` with `{}`",
- fr_name, static_str,
- ));
- } else {
- // Otherwise, we should suggest adding a constraint on the return type.
- let span = infcx.tcx.def_span(*did);
- if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
- let suggestable_fr_name = if fr_name.was_named() {
- fr_name.to_string()
- } else {
- "'_".to_string()
- };
-
- diag.span_suggestion(
- span,
- &format!(
- "to allow this `impl Trait` to capture borrowed data with lifetime \
- `{}`, add `{}` as a constraint",
- fr_name, suggestable_fr_name,
- ),
- format!("{} + {}", snippet, suggestable_fr_name),
- Applicability::MachineApplicable,
- );
- }
- }
- }
- }
- }
-
- crate fn free_region_constraint_info(
- &self,
- body: &Body<'tcx>,
- local_names: &IndexVec<Local, Option<Symbol>>,
- upvars: &[Upvar],
- mir_def_id: DefId,
- infcx: &InferCtxt<'_, 'tcx>,
- borrow_region: RegionVid,
- outlived_region: RegionVid,
- ) -> (ConstraintCategory, bool, Span, Option<RegionName>) {
- let (category, from_closure, span) = self.best_blame_constraint(
- body,
- borrow_region,
- NLLRegionVariableOrigin::FreeRegion,
- |r| self.provides_universal_region(r, borrow_region, outlived_region)
- );
-
- let mut renctx = RegionErrorNamingCtx::new();
- let errctx = ErrorReportingCtx {
- infcx, body, local_names, upvars, mir_def_id,
- region_infcx: self,
- };
- let outlived_fr_name = self.give_region_a_name(&errctx, &mut renctx, outlived_region);
-
- (category, from_closure, span, outlived_fr_name)
- }
-
- // Finds some region R such that `fr1: R` and `R` is live at
- // `elem`.
- crate fn find_sub_region_live_at(
- &self,
- fr1: RegionVid,
- elem: Location,
- ) -> RegionVid {
- debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
- self.find_constraint_paths_between_regions(fr1, |r| {
- // First look for some `r` such that `fr1: r` and `r` is live at `elem`
- debug!(
- "find_sub_region_live_at: liveness_constraints for {:?} are {:?}",
- r,
- self.liveness_constraints.region_value_str(r),
- );
- self.liveness_constraints.contains(r, elem)
- }).or_else(|| {
- // If we fail to find that, we may find some `r` such that
- // `fr1: r` and `r` is a placeholder from some universe
- // `fr1` cannot name. This would force `fr1` to be
- // `'static`.
- self.find_constraint_paths_between_regions(fr1, |r| {
- self.cannot_name_placeholder(fr1, r)
- })
- })
- .or_else(|| {
- // If we fail to find THAT, it may be that `fr1` is a
- // placeholder that cannot "fit" into its SCC. In that
- // case, there should be some `r` where `fr1: r`, both
- // `fr1` and `r` are in the same SCC, and `fr1` is a
- // placeholder that `r` cannot name. We can blame that
- // edge.
- self.find_constraint_paths_between_regions(fr1, |r| {
- self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r)
- && self.cannot_name_placeholder(r, fr1)
- })
- })
- .map(|(_path, r)| r)
- .unwrap()
- }
-
- // Finds a good span to blame for the fact that `fr1` outlives `fr2`.
- crate fn find_outlives_blame_span(
- &self,
- body: &Body<'tcx>,
- fr1: RegionVid,
- fr1_origin: NLLRegionVariableOrigin,
- fr2: RegionVid,
- ) -> (ConstraintCategory, Span) {
- let (category, _, span) = self.best_blame_constraint(
- body,
- fr1,
- fr1_origin,
- |r| self.provides_universal_region(r, fr1, fr2),
- );
- (category, span)
- }
-
- fn retrieve_closure_constraint_info(
- &self,
- body: &Body<'tcx>,
- constraint: &OutlivesConstraint,
- ) -> (ConstraintCategory, bool, Span) {
- let loc = match constraint.locations {
- Locations::All(span) => return (constraint.category, false, span),
- Locations::Single(loc) => loc,
- };
-
- let opt_span_category =
- self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub));
- opt_span_category
- .map(|&(category, span)| (category, true, span))
- .unwrap_or((constraint.category, false, body.source_info(loc).span))
- }
-
- /// Returns `true` if a closure is inferred to be an `FnMut` closure.
- crate fn is_closure_fn_mut(&self, infcx: &InferCtxt<'_, 'tcx>, fr: RegionVid) -> bool {
- if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
- if let ty::BoundRegion::BrEnv = free_region.bound_region {
- if let DefiningTy::Closure(def_id, substs) = self.universal_regions.defining_ty {
- let closure_kind_ty = substs.as_closure().kind_ty(def_id, infcx.tcx);
- return Some(ty::ClosureKind::FnMut) == closure_kind_ty.to_opt_closure_kind();
- }
- }
- }
-
- false
- }
-
- /// If `r2` represents a placeholder region, then this returns
- /// `true` if `r1` cannot name that placeholder in its
- /// value; otherwise, returns `false`.
- fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
- debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2);
-
- match self.definitions[r2].origin {
- NLLRegionVariableOrigin::Placeholder(placeholder) => {
- let universe1 = self.definitions[r1].universe;
- debug!(
- "cannot_name_value_of: universe1={:?} placeholder={:?}",
- universe1, placeholder
- );
- universe1.cannot_name(placeholder.universe)
- }
-
- NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential { .. } => {
- false
- }
- }
- }
-}
+++ /dev/null
-//! Contains utilities for generating suggestions for borrowck errors related to unsatisified
-//! outlives constraints.
-
-use std::collections::BTreeMap;
-
-use log::debug;
-use rustc::{hir::def_id::DefId, infer::InferCtxt, ty::RegionVid};
-use rustc::mir::{Body, Local};
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Diagnostic, DiagnosticBuilder};
-use rustc_index::vec::IndexVec;
-use syntax_pos::symbol::Symbol;
-
-use smallvec::SmallVec;
-
-use crate::borrow_check::nll::region_infer::{
- error_reporting::{
- region_name::{RegionName, RegionNameSource},
- ErrorConstraintInfo, ErrorReportingCtx, RegionErrorNamingCtx,
- },
- RegionInferenceContext,
-};
-
-/// The different things we could suggest.
-enum SuggestedConstraint {
- /// Outlives(a, [b, c, d, ...]) => 'a: 'b + 'c + 'd + ...
- Outlives(RegionName, SmallVec<[RegionName; 2]>),
-
- /// 'a = 'b
- Equal(RegionName, RegionName),
-
- /// 'a: 'static i.e. 'a = 'static and the user should just use 'static
- Static(RegionName),
-}
-
-/// Collects information about outlives constraints that needed to be added for a given MIR node
-/// corresponding to a function definition.
-///
-/// Adds a help note suggesting adding a where clause with the needed constraints.
-pub struct OutlivesSuggestionBuilder<'a> {
- /// The MIR DefId of the fn with the lifetime error.
- mir_def_id: DefId,
-
- local_names: &'a IndexVec<Local, Option<Symbol>>,
-
- /// The list of outlives constraints that need to be added. Specifically, we map each free
- /// region to all other regions that it must outlive. I will use the shorthand `fr:
- /// outlived_frs`. Not all of these regions will already have names necessarily. Some could be
- /// implicit free regions that we inferred. These will need to be given names in the final
- /// suggestion message.
- constraints_to_add: BTreeMap<RegionVid, Vec<RegionVid>>,
-}
-
-impl OutlivesSuggestionBuilder<'a> {
- /// Create a new builder for the given MIR node representing a fn definition.
- crate fn new(
- mir_def_id: DefId,
- local_names: &'a IndexVec<Local, Option<Symbol>>,
- ) -> Self {
- OutlivesSuggestionBuilder {
- mir_def_id,
- local_names,
- constraints_to_add: BTreeMap::default(),
- }
- }
-
- /// Returns `true` iff the `RegionNameSource` is a valid source for an outlives
- /// suggestion.
- //
- // FIXME: Currently, we only report suggestions if the `RegionNameSource` is an early-bound
- // region or a named region, avoiding using regions with synthetic names altogether. This
- // allows us to avoid giving impossible suggestions (e.g. adding bounds to closure args).
- // We can probably be less conservative, since some inferred free regions are namable (e.g.
- // the user can explicitly name them. To do this, we would allow some regions whose names
- // come from `MatchedAdtAndSegment`, being careful to filter out bad suggestions, such as
- // naming the `'self` lifetime in methods, etc.
- fn region_name_is_suggestable(name: &RegionName) -> bool {
- match name.source {
- RegionNameSource::NamedEarlyBoundRegion(..)
- | RegionNameSource::NamedFreeRegion(..)
- | RegionNameSource::Static => true,
-
- // Don't give suggestions for upvars, closure return types, or other unnamable
- // regions.
- RegionNameSource::SynthesizedFreeEnvRegion(..)
- | RegionNameSource::CannotMatchHirTy(..)
- | RegionNameSource::MatchedHirTy(..)
- | RegionNameSource::MatchedAdtAndSegment(..)
- | RegionNameSource::AnonRegionFromUpvar(..)
- | RegionNameSource::AnonRegionFromOutput(..)
- | RegionNameSource::AnonRegionFromYieldTy(..)
- | RegionNameSource::AnonRegionFromAsyncFn(..) => {
- debug!("Region {:?} is NOT suggestable", name);
- false
- }
- }
- }
-
- /// Returns a name for the region if it is suggestable. See `region_name_is_suggestable`.
- fn region_vid_to_name(
- &self,
- errctx: &ErrorReportingCtx<'_, '_, '_>,
- renctx: &mut RegionErrorNamingCtx,
- region: RegionVid,
- ) -> Option<RegionName> {
- errctx
- .region_infcx
- .give_region_a_name(errctx, renctx, region)
- .filter(Self::region_name_is_suggestable)
- }
-
- /// Compiles a list of all suggestions to be printed in the final big suggestion.
- fn compile_all_suggestions<'tcx>(
- &self,
- body: &Body<'tcx>,
- region_infcx: &RegionInferenceContext<'tcx>,
- infcx: &InferCtxt<'_, 'tcx>,
- renctx: &mut RegionErrorNamingCtx,
- ) -> SmallVec<[SuggestedConstraint; 2]> {
- let mut suggested = SmallVec::new();
-
- // Keep track of variables that we have already suggested unifying so that we don't print
- // out silly duplicate messages.
- let mut unified_already = FxHashSet::default();
-
- let errctx = ErrorReportingCtx {
- region_infcx,
- infcx,
- body,
- mir_def_id: self.mir_def_id,
- local_names: self.local_names,
-
- // We should not be suggesting naming upvars, so we pass in a dummy set of upvars that
- // should never be used.
- upvars: &[],
- };
-
- for (fr, outlived) in &self.constraints_to_add {
- let fr_name = if let Some(fr_name) = self.region_vid_to_name(&errctx, renctx, *fr) {
- fr_name
- } else {
- continue;
- };
-
- let outlived = outlived
- .iter()
- // if there is a `None`, we will just omit that constraint
- .filter_map(|fr| {
- self.region_vid_to_name(&errctx, renctx, *fr).map(|rname| (fr, rname))
- })
- .collect::<Vec<_>>();
-
- // No suggestable outlived lifetimes.
- if outlived.is_empty() {
- continue;
- }
-
- // There are three types of suggestions we can make:
- // 1) Suggest a bound: 'a: 'b
- // 2) Suggest replacing 'a with 'static. If any of `outlived` is `'static`, then we
- // should just replace 'a with 'static.
- // 3) Suggest unifying 'a with 'b if we have both 'a: 'b and 'b: 'a
-
- if outlived.iter().any(|(_, outlived_name)| {
- if let RegionNameSource::Static = outlived_name.source {
- true
- } else {
- false
- }
- }) {
- suggested.push(SuggestedConstraint::Static(fr_name));
- } else {
- // We want to isolate out all lifetimes that should be unified and print out
- // separate messages for them.
-
- let (unified, other): (Vec<_>, Vec<_>) = outlived.into_iter().partition(
- // Do we have both 'fr: 'r and 'r: 'fr?
- |(r, _)| {
- self.constraints_to_add
- .get(r)
- .map(|r_outlived| r_outlived.as_slice().contains(fr))
- .unwrap_or(false)
- },
- );
-
- for (r, bound) in unified.into_iter() {
- if !unified_already.contains(fr) {
- suggested.push(SuggestedConstraint::Equal(fr_name.clone(), bound));
- unified_already.insert(r);
- }
- }
-
- if !other.is_empty() {
- let other =
- other.iter().map(|(_, rname)| rname.clone()).collect::<SmallVec<_>>();
- suggested.push(SuggestedConstraint::Outlives(fr_name, other))
- }
- }
- }
-
- suggested
- }
-
- /// Add the outlives constraint `fr: outlived_fr` to the set of constraints we need to suggest.
- crate fn collect_constraint(&mut self, fr: RegionVid, outlived_fr: RegionVid) {
- debug!("Collected {:?}: {:?}", fr, outlived_fr);
-
- // Add to set of constraints for final help note.
- self.constraints_to_add.entry(fr).or_insert(Vec::new()).push(outlived_fr);
- }
-
- /// Emit an intermediate note on the given `Diagnostic` if the involved regions are
- /// suggestable.
- crate fn intermediate_suggestion(
- &mut self,
- errctx: &ErrorReportingCtx<'_, '_, '_>,
- errci: &ErrorConstraintInfo,
- renctx: &mut RegionErrorNamingCtx,
- diag: &mut DiagnosticBuilder<'_>,
- ) {
- // Emit an intermediate note.
- let fr_name = self.region_vid_to_name(errctx, renctx, errci.fr);
- let outlived_fr_name = self.region_vid_to_name(errctx, renctx, errci.outlived_fr);
-
- if let (Some(fr_name), Some(outlived_fr_name)) = (fr_name, outlived_fr_name) {
- if let RegionNameSource::Static = outlived_fr_name.source {
- diag.help(&format!("consider replacing `{}` with `'static`", fr_name));
- } else {
- diag.help(&format!(
- "consider adding the following bound: `{}: {}`",
- fr_name, outlived_fr_name
- ));
- }
- }
- }
-
- /// If there is a suggestion to emit, add a diagnostic to the buffer. This is the final
- /// suggestion including all collected constraints.
- crate fn add_suggestion<'tcx>(
- &self,
- body: &Body<'tcx>,
- region_infcx: &RegionInferenceContext<'tcx>,
- infcx: &InferCtxt<'_, 'tcx>,
- errors_buffer: &mut Vec<Diagnostic>,
- renctx: &mut RegionErrorNamingCtx,
- ) {
- // No constraints to add? Done.
- if self.constraints_to_add.is_empty() {
- debug!("No constraints to suggest.");
- return;
- }
-
- // If there is only one constraint to suggest, then we already suggested it in the
- // intermediate suggestion above.
- if self.constraints_to_add.len() == 1 {
- debug!("Only 1 suggestion. Skipping.");
- return;
- }
-
- // Get all suggestable constraints.
- let suggested = self.compile_all_suggestions(body, region_infcx, infcx, renctx);
-
- // If there are no suggestable constraints...
- if suggested.is_empty() {
- debug!("Only 1 suggestable constraint. Skipping.");
- return;
- }
-
- // If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a
- // list of diagnostics.
- let mut diag = if suggested.len() == 1 {
- infcx.tcx.sess.diagnostic().struct_help(&match suggested.last().unwrap() {
- SuggestedConstraint::Outlives(a, bs) => {
- let bs: SmallVec<[String; 2]> = bs.iter().map(|r| format!("{}", r)).collect();
- format!("add bound `{}: {}`", a, bs.join(" + "))
- }
-
- SuggestedConstraint::Equal(a, b) => {
- format!("`{}` and `{}` must be the same: replace one with the other", a, b)
- }
- SuggestedConstraint::Static(a) => format!("replace `{}` with `'static`", a),
- })
- } else {
- // Create a new diagnostic.
- let mut diag = infcx
- .tcx
- .sess
- .diagnostic()
- .struct_help("the following changes may resolve your lifetime errors");
-
- // Add suggestions.
- for constraint in suggested {
- match constraint {
- SuggestedConstraint::Outlives(a, bs) => {
- let bs: SmallVec<[String; 2]> =
- bs.iter().map(|r| format!("{}", r)).collect();
- diag.help(&format!("add bound `{}: {}`", a, bs.join(" + ")));
- }
- SuggestedConstraint::Equal(a, b) => {
- diag.help(&format!(
- "`{}` and `{}` must be the same: replace one with the other",
- a, b
- ));
- }
- SuggestedConstraint::Static(a) => {
- diag.help(&format!("replace `{}` with `'static`", a));
- }
- }
- }
-
- diag
- };
-
- // We want this message to appear after other messages on the mir def.
- let mir_span = infcx.tcx.def_span(self.mir_def_id);
- diag.sort_span = mir_span.shrink_to_hi();
-
- // Buffer the diagnostic
- diag.buffer(errors_buffer);
- }
-}
+++ /dev/null
-use std::fmt::{self, Display};
-
-use crate::borrow_check::nll::region_infer::{
- RegionInferenceContext,
- error_reporting::ErrorReportingCtx,
-};
-use crate::borrow_check::nll::universal_regions::DefiningTy;
-use crate::borrow_check::nll::ToRegionVid;
-use crate::borrow_check::Upvar;
-use rustc::hir;
-use rustc::hir::def::{Res, DefKind};
-use rustc::hir::def_id::DefId;
-use rustc::infer::InferCtxt;
-use rustc::mir::{Local, Body};
-use rustc::ty::subst::{SubstsRef, GenericArgKind};
-use rustc::ty::{self, RegionVid, Ty, TyCtxt};
-use rustc::ty::print::RegionHighlightMode;
-use rustc_index::vec::IndexVec;
-use rustc_errors::DiagnosticBuilder;
-use syntax::symbol::kw;
-use rustc_data_structures::fx::FxHashMap;
-use syntax_pos::{Span, symbol::Symbol, DUMMY_SP};
-
-/// A name for a particular region used in emitting diagnostics. This name could be a generated
-/// name like `'1`, a name used by the user like `'a`, or a name like `'static`.
-#[derive(Debug, Clone)]
-crate struct RegionName {
- /// The name of the region (interned).
- crate name: Symbol,
- /// Where the region comes from.
- crate source: RegionNameSource,
-}
-
-/// Denotes the source of a region that is named by a `RegionName`. For example, a free region that
-/// was named by the user would get `NamedFreeRegion` and `'static` lifetime would get `Static`.
-/// This helps to print the right kinds of diagnostics.
-#[derive(Debug, Clone)]
-crate enum RegionNameSource {
- /// A bound (not free) region that was substituted at the def site (not an HRTB).
- NamedEarlyBoundRegion(Span),
- /// A free region that the user has a name (`'a`) for.
- NamedFreeRegion(Span),
- /// The `'static` region.
- Static,
- /// The free region corresponding to the environment of a closure.
- SynthesizedFreeEnvRegion(Span, String),
- /// The region name corresponds to a region where the type annotation is completely missing
- /// from the code, e.g. in a closure arguments `|x| { ... }`, where `x` is a reference.
- CannotMatchHirTy(Span, String),
- /// The region name corresponds a reference that was found by traversing the type in the HIR.
- MatchedHirTy(Span),
- /// A region name from the generics list of a struct/enum/union.
- MatchedAdtAndSegment(Span),
- /// The region corresponding to a closure upvar.
- AnonRegionFromUpvar(Span, String),
- /// The region corresponding to the return type of a closure.
- AnonRegionFromOutput(Span, String, String),
- /// The region from a type yielded by a generator.
- AnonRegionFromYieldTy(Span, String),
- /// An anonymous region from an async fn.
- AnonRegionFromAsyncFn(Span),
-}
-
-/// Records region names that have been assigned before so that we can use the same ones in later
-/// diagnostics.
-#[derive(Debug, Clone)]
-crate struct RegionErrorNamingCtx {
- /// Record the region names generated for each region in the given
- /// MIR def so that we can reuse them later in help/error messages.
- renctx: FxHashMap<RegionVid, RegionName>,
-
- /// The counter for generating new region names.
- counter: usize,
-}
-
-impl RegionErrorNamingCtx {
- crate fn new() -> Self {
- Self {
- counter: 1,
- renctx: FxHashMap::default(),
- }
- }
-
- /// Get the name of `region` if it has previously been named.
- crate fn get(&self, region: &RegionVid) -> Option<&RegionName> {
- self.renctx.get(region)
- }
-
- /// Give `region` the name `name`.
- crate fn insert(&mut self, region: RegionVid, name: RegionName) {
- self.renctx.insert(region, name);
- }
-
- /// Creates a synthetic region named `'N`, where `N` is the next value of the counter. Then,
- /// increment the counter.
- ///
- /// The name is not memoized. A separate call to `insert` should be made later. (Currently,
- /// this happens at the end of `give_region_a_name`).
- crate fn synthesize_region_name(&mut self) -> Symbol {
- let c = self.counter;
- self.counter += 1;
-
- Symbol::intern(&format!("'{:?}", c))
- }
-}
-
-impl RegionName {
- crate fn was_named(&self) -> bool {
- match self.source {
- RegionNameSource::NamedEarlyBoundRegion(..) |
- RegionNameSource::NamedFreeRegion(..) |
- RegionNameSource::Static => true,
- RegionNameSource::SynthesizedFreeEnvRegion(..) |
- RegionNameSource::CannotMatchHirTy(..) |
- RegionNameSource::MatchedHirTy(..) |
- RegionNameSource::MatchedAdtAndSegment(..) |
- RegionNameSource::AnonRegionFromUpvar(..) |
- RegionNameSource::AnonRegionFromOutput(..) |
- RegionNameSource::AnonRegionFromYieldTy(..) |
- RegionNameSource::AnonRegionFromAsyncFn(..) => false,
- }
- }
-
- crate fn highlight_region_name(&self, diag: &mut DiagnosticBuilder<'_>) {
- match &self.source {
- RegionNameSource::NamedFreeRegion(span)
- | RegionNameSource::NamedEarlyBoundRegion(span) => {
- diag.span_label(*span, format!("lifetime `{}` defined here", self));
- }
- RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
- diag.span_label(
- *span,
- format!("lifetime `{}` represents this closure's body", self),
- );
- diag.note(¬e);
- }
- RegionNameSource::CannotMatchHirTy(span, type_name) => {
- diag.span_label(*span, format!("has type `{}`", type_name));
- }
- RegionNameSource::MatchedHirTy(span) |
- RegionNameSource::AnonRegionFromAsyncFn(span) => {
- diag.span_label(
- *span,
- format!("let's call the lifetime of this reference `{}`", self),
- );
- }
- RegionNameSource::MatchedAdtAndSegment(span) => {
- diag.span_label(*span, format!("let's call this `{}`", self));
- }
- RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => {
- diag.span_label(
- *span,
- format!(
- "lifetime `{}` appears in the type of `{}`",
- self, upvar_name
- ),
- );
- }
- RegionNameSource::AnonRegionFromOutput(span, mir_description, type_name) => {
- diag.span_label(
- *span,
- format!("return type{} is {}", mir_description, type_name),
- );
- },
- RegionNameSource::AnonRegionFromYieldTy(span, type_name) => {
- diag.span_label(
- *span,
- format!("yield type is {}", type_name),
- );
- }
- RegionNameSource::Static => {},
- }
- }
-}
-
-impl Display for RegionName {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.name)
- }
-}
-
-impl<'tcx> RegionInferenceContext<'tcx> {
- /// Maps from an internal MIR region vid to something that we can
- /// report to the user. In some cases, the region vids will map
- /// directly to lifetimes that the user has a name for (e.g.,
- /// `'static`). But frequently they will not, in which case we
- /// have to find some way to identify the lifetime to the user. To
- /// that end, this function takes a "diagnostic" so that it can
- /// create auxiliary notes as needed.
- ///
- /// Example (function arguments):
- ///
- /// Suppose we are trying to give a name to the lifetime of the
- /// reference `x`:
- ///
- /// ```
- /// fn foo(x: &u32) { .. }
- /// ```
- ///
- /// This function would create a label like this:
- ///
- /// ```
- /// | fn foo(x: &u32) { .. }
- /// ------- fully elaborated type of `x` is `&'1 u32`
- /// ```
- ///
- /// and then return the name `'1` for us to use.
- crate fn give_region_a_name(
- &self,
- errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
- renctx: &mut RegionErrorNamingCtx,
- fr: RegionVid,
- ) -> Option<RegionName> {
- let ErrorReportingCtx {
- infcx, body, mir_def_id, local_names, upvars, ..
- } = errctx;
-
- debug!("give_region_a_name(fr={:?}, counter={:?})", fr, renctx.counter);
-
- assert!(self.universal_regions.is_universal_region(fr));
-
- if let Some(value) = renctx.get(&fr) {
- return Some(value.clone());
- }
-
- let value = self
- .give_name_from_error_region(infcx.tcx, *mir_def_id, fr, renctx)
- .or_else(|| {
- self.give_name_if_anonymous_region_appears_in_arguments(
- infcx, body, local_names, *mir_def_id, fr, renctx,
- )
- })
- .or_else(|| {
- self.give_name_if_anonymous_region_appears_in_upvars(
- infcx.tcx, upvars, fr, renctx
- )
- })
- .or_else(|| {
- self.give_name_if_anonymous_region_appears_in_output(
- infcx, body, *mir_def_id, fr, renctx,
- )
- })
- .or_else(|| {
- self.give_name_if_anonymous_region_appears_in_yield_ty(
- infcx, body, *mir_def_id, fr, renctx,
- )
- });
-
- if let Some(ref value) = value {
- renctx.insert(fr, value.clone());
- }
-
- debug!("give_region_a_name: gave name {:?}", value);
- value
- }
-
- /// Checks for the case where `fr` maps to something that the
- /// *user* has a name for. In that case, we'll be able to map
- /// `fr` to a `Region<'tcx>`, and that region will be one of
- /// named variants.
- fn give_name_from_error_region(
- &self,
- tcx: TyCtxt<'tcx>,
- mir_def_id: DefId,
- fr: RegionVid,
- renctx: &mut RegionErrorNamingCtx,
- ) -> Option<RegionName> {
- let error_region = self.to_error_region(fr)?;
-
- debug!("give_region_a_name: error_region = {:?}", error_region);
- match error_region {
- ty::ReEarlyBound(ebr) => {
- if ebr.has_name() {
- let span = tcx.hir().span_if_local(ebr.def_id).unwrap_or(DUMMY_SP);
- Some(RegionName {
- name: ebr.name,
- source: RegionNameSource::NamedEarlyBoundRegion(span),
- })
- } else {
- None
- }
- }
-
- ty::ReStatic => Some(RegionName {
- name: kw::StaticLifetime,
- source: RegionNameSource::Static
- }),
-
- ty::ReFree(free_region) => match free_region.bound_region {
- ty::BoundRegion::BrNamed(region_def_id, name) => {
- // Get the span to point to, even if we don't use the name.
- let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP);
- debug!("bound region named: {:?}, is_named: {:?}",
- name, free_region.bound_region.is_named());
-
- if free_region.bound_region.is_named() {
- // A named region that is actually named.
- Some(RegionName {
- name,
- source: RegionNameSource::NamedFreeRegion(span),
- })
- } else {
- // If we spuriously thought that the region is named, we should let the
- // system generate a true name for error messages. Currently this can
- // happen if we have an elided name in an async fn for example: the
- // compiler will generate a region named `'_`, but reporting such a name is
- // not actually useful, so we synthesize a name for it instead.
- let name = renctx.synthesize_region_name();
- Some(RegionName {
- name,
- source: RegionNameSource::AnonRegionFromAsyncFn(span),
- })
- }
- }
-
- ty::BoundRegion::BrEnv => {
- let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
- let def_ty = self.universal_regions.defining_ty;
-
- if let DefiningTy::Closure(def_id, substs) = def_ty {
- let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) =
- tcx.hir().expect_expr(mir_hir_id).kind
- {
- span
- } else {
- bug!("Closure is not defined by a closure expr");
- };
- let region_name = renctx.synthesize_region_name();
-
- let closure_kind_ty = substs.as_closure().kind_ty(def_id, tcx);
- let note = match closure_kind_ty.to_opt_closure_kind() {
- Some(ty::ClosureKind::Fn) => {
- "closure implements `Fn`, so references to captured variables \
- can't escape the closure"
- }
- Some(ty::ClosureKind::FnMut) => {
- "closure implements `FnMut`, so references to captured variables \
- can't escape the closure"
- }
- Some(ty::ClosureKind::FnOnce) => {
- bug!("BrEnv in a `FnOnce` closure");
- }
- None => bug!("Closure kind not inferred in borrow check"),
- };
-
- Some(RegionName {
- name: region_name,
- source: RegionNameSource::SynthesizedFreeEnvRegion(
- args_span,
- note.to_string(),
- ),
- })
- } else {
- // Can't have BrEnv in functions, constants or generators.
- bug!("BrEnv outside of closure.");
- }
- }
-
- ty::BoundRegion::BrAnon(_) => None,
- },
-
- ty::ReLateBound(..)
- | ty::ReScope(..)
- | ty::ReVar(..)
- | ty::RePlaceholder(..)
- | ty::ReEmpty
- | ty::ReErased
- | ty::ReClosureBound(..) => None,
- }
- }
-
- /// Finds an argument that contains `fr` and label it with a fully
- /// elaborated type, returning something like `'1`. Result looks
- /// like:
- ///
- /// ```
- /// | fn foo(x: &u32) { .. }
- /// ------- fully elaborated type of `x` is `&'1 u32`
- /// ```
- fn give_name_if_anonymous_region_appears_in_arguments(
- &self,
- infcx: &InferCtxt<'_, 'tcx>,
- body: &Body<'tcx>,
- local_names: &IndexVec<Local, Option<Symbol>>,
- mir_def_id: DefId,
- fr: RegionVid,
- renctx: &mut RegionErrorNamingCtx,
- ) -> Option<RegionName> {
- let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
- let argument_index = self.get_argument_index_for_region(infcx.tcx, fr)?;
-
- let arg_ty =
- self.universal_regions.unnormalized_input_tys[implicit_inputs + argument_index];
- if let Some(region_name) = self.give_name_if_we_can_match_hir_ty_from_argument(
- infcx,
- mir_def_id,
- fr,
- arg_ty,
- argument_index,
- renctx,
- ) {
- return Some(region_name);
- }
-
- self.give_name_if_we_cannot_match_hir_ty(infcx, body, local_names, fr, arg_ty, renctx)
- }
-
- fn give_name_if_we_can_match_hir_ty_from_argument(
- &self,
- infcx: &InferCtxt<'_, 'tcx>,
- mir_def_id: DefId,
- needle_fr: RegionVid,
- argument_ty: Ty<'tcx>,
- argument_index: usize,
- renctx: &mut RegionErrorNamingCtx,
- ) -> Option<RegionName> {
- let mir_hir_id = infcx.tcx.hir().as_local_hir_id(mir_def_id)?;
- let fn_decl = infcx.tcx.hir().fn_decl_by_hir_id(mir_hir_id)?;
- let argument_hir_ty: &hir::Ty = fn_decl.inputs.get(argument_index)?;
- match argument_hir_ty.kind {
- // This indicates a variable with no type annotation, like
- // `|x|`... in that case, we can't highlight the type but
- // must highlight the variable.
- // NOTE(eddyb) this is handled in/by the sole caller
- // (`give_name_if_anonymous_region_appears_in_arguments`).
- hir::TyKind::Infer => None,
-
- _ => self.give_name_if_we_can_match_hir_ty(
- infcx.tcx,
- needle_fr,
- argument_ty,
- argument_hir_ty,
- renctx,
- ),
- }
- }
-
- /// Attempts to highlight the specific part of a type in an argument
- /// that has no type annotation.
- /// For example, we might produce an annotation like this:
- ///
- /// ```
- /// | foo(|a, b| b)
- /// | - -
- /// | | |
- /// | | has type `&'1 u32`
- /// | has type `&'2 u32`
- /// ```
- fn give_name_if_we_cannot_match_hir_ty(
- &self,
- infcx: &InferCtxt<'_, 'tcx>,
- body: &Body<'tcx>,
- local_names: &IndexVec<Local, Option<Symbol>>,
- needle_fr: RegionVid,
- argument_ty: Ty<'tcx>,
- renctx: &mut RegionErrorNamingCtx,
- ) -> Option<RegionName> {
- let counter = renctx.counter;
- let mut highlight = RegionHighlightMode::default();
- highlight.highlighting_region_vid(needle_fr, counter);
- let type_name = infcx.extract_type_name(&argument_ty, Some(highlight)).0;
-
- debug!(
- "give_name_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}",
- type_name, needle_fr
- );
- let assigned_region_name = if type_name.find(&format!("'{}", counter)).is_some() {
- // Only add a label if we can confirm that a region was labelled.
- let argument_index = self.get_argument_index_for_region(infcx.tcx, needle_fr)?;
- let (_, span) =
- self.get_argument_name_and_span_for_region(body, local_names, argument_index);
-
- Some(RegionName {
- // This counter value will already have been used, so this function will increment
- // it so the next value will be used next and return the region name that would
- // have been used.
- name: renctx.synthesize_region_name(),
- source: RegionNameSource::CannotMatchHirTy(span, type_name),
- })
- } else {
- None
- };
-
- assigned_region_name
- }
-
- /// Attempts to highlight the specific part of a type annotation
- /// that contains the anonymous reference we want to give a name
- /// to. For example, we might produce an annotation like this:
- ///
- /// ```
- /// | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item = &T>> {
- /// | - let's call the lifetime of this reference `'1`
- /// ```
- ///
- /// the way this works is that we match up `argument_ty`, which is
- /// a `Ty<'tcx>` (the internal form of the type) with
- /// `argument_hir_ty`, a `hir::Ty` (the syntax of the type
- /// annotation). We are descending through the types stepwise,
- /// looking in to find the region `needle_fr` in the internal
- /// type. Once we find that, we can use the span of the `hir::Ty`
- /// to add the highlight.
- ///
- /// This is a somewhat imperfect process, so along the way we also
- /// keep track of the **closest** type we've found. If we fail to
- /// find the exact `&` or `'_` to highlight, then we may fall back
- /// to highlighting that closest type instead.
- fn give_name_if_we_can_match_hir_ty(
- &self,
- tcx: TyCtxt<'tcx>,
- needle_fr: RegionVid,
- argument_ty: Ty<'tcx>,
- argument_hir_ty: &hir::Ty,
- renctx: &mut RegionErrorNamingCtx,
- ) -> Option<RegionName> {
- let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty)> =
- &mut vec![(argument_ty, argument_hir_ty)];
-
- while let Some((ty, hir_ty)) = search_stack.pop() {
- match (&ty.kind, &hir_ty.kind) {
- // Check if the `argument_ty` is `&'X ..` where `'X`
- // is the region we are looking for -- if so, and we have a `&T`
- // on the RHS, then we want to highlight the `&` like so:
- //
- // &
- // - let's call the lifetime of this reference `'1`
- (
- ty::Ref(region, referent_ty, _),
- hir::TyKind::Rptr(_lifetime, referent_hir_ty),
- ) => {
- if region.to_region_vid() == needle_fr {
- let region_name = renctx.synthesize_region_name();
-
- // Just grab the first character, the `&`.
- let source_map = tcx.sess.source_map();
- let ampersand_span = source_map.start_point(hir_ty.span);
-
- return Some(RegionName {
- name: region_name,
- source: RegionNameSource::MatchedHirTy(ampersand_span),
- });
- }
-
- // Otherwise, let's descend into the referent types.
- search_stack.push((referent_ty, &referent_hir_ty.ty));
- }
-
- // Match up something like `Foo<'1>`
- (
- ty::Adt(_adt_def, substs),
- hir::TyKind::Path(hir::QPath::Resolved(None, path)),
- ) => {
- match path.res {
- // Type parameters of the type alias have no reason to
- // be the same as those of the ADT.
- // FIXME: We should be able to do something similar to
- // match_adt_and_segment in this case.
- Res::Def(DefKind::TyAlias, _) => (),
- _ => if let Some(last_segment) = path.segments.last() {
- if let Some(name) = self.match_adt_and_segment(
- substs,
- needle_fr,
- last_segment,
- renctx,
- search_stack,
- ) {
- return Some(name);
- }
- }
- }
- }
-
- // The following cases don't have lifetimes, so we
- // just worry about trying to match up the rustc type
- // with the HIR types:
- (ty::Tuple(elem_tys), hir::TyKind::Tup(elem_hir_tys)) => {
- search_stack.extend(elem_tys.iter().map(|k| k.expect_ty()).zip(elem_hir_tys));
- }
-
- (ty::Slice(elem_ty), hir::TyKind::Slice(elem_hir_ty))
- | (ty::Array(elem_ty, _), hir::TyKind::Array(elem_hir_ty, _)) => {
- search_stack.push((elem_ty, elem_hir_ty));
- }
-
- (ty::RawPtr(mut_ty), hir::TyKind::Ptr(mut_hir_ty)) => {
- search_stack.push((mut_ty.ty, &mut_hir_ty.ty));
- }
-
- _ => {
- // FIXME there are other cases that we could trace
- }
- }
- }
-
- return None;
- }
-
- /// We've found an enum/struct/union type with the substitutions
- /// `substs` and -- in the HIR -- a path type with the final
- /// segment `last_segment`. Try to find a `'_` to highlight in
- /// the generic args (or, if not, to produce new zipped pairs of
- /// types+hir to search through).
- fn match_adt_and_segment<'hir>(
- &self,
- substs: SubstsRef<'tcx>,
- needle_fr: RegionVid,
- last_segment: &'hir hir::PathSegment,
- renctx: &mut RegionErrorNamingCtx,
- search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>,
- ) -> Option<RegionName> {
- // Did the user give explicit arguments? (e.g., `Foo<..>`)
- let args = last_segment.args.as_ref()?;
- let lifetime =
- self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?;
- match lifetime.name {
- hir::LifetimeName::Param(_)
- | hir::LifetimeName::Error
- | hir::LifetimeName::Static
- | hir::LifetimeName::Underscore => {
- let region_name = renctx.synthesize_region_name();
- let ampersand_span = lifetime.span;
- Some(RegionName {
- name: region_name,
- source: RegionNameSource::MatchedAdtAndSegment(ampersand_span),
- })
- }
-
- hir::LifetimeName::ImplicitObjectLifetimeDefault
- | hir::LifetimeName::Implicit => {
- // In this case, the user left off the lifetime; so
- // they wrote something like:
- //
- // ```
- // x: Foo<T>
- // ```
- //
- // where the fully elaborated form is `Foo<'_, '1,
- // T>`. We don't consider this a match; instead we let
- // the "fully elaborated" type fallback above handle
- // it.
- None
- }
- }
- }
-
- /// We've found an enum/struct/union type with the substitutions
- /// `substs` and -- in the HIR -- a path with the generic
- /// arguments `args`. If `needle_fr` appears in the args, return
- /// the `hir::Lifetime` that corresponds to it. If not, push onto
- /// `search_stack` the types+hir to search through.
- fn try_match_adt_and_generic_args<'hir>(
- &self,
- substs: SubstsRef<'tcx>,
- needle_fr: RegionVid,
- args: &'hir hir::GenericArgs,
- search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>,
- ) -> Option<&'hir hir::Lifetime> {
- for (kind, hir_arg) in substs.iter().zip(&args.args) {
- match (kind.unpack(), hir_arg) {
- (GenericArgKind::Lifetime(r), hir::GenericArg::Lifetime(lt)) => {
- if r.to_region_vid() == needle_fr {
- return Some(lt);
- }
- }
-
- (GenericArgKind::Type(ty), hir::GenericArg::Type(hir_ty)) => {
- search_stack.push((ty, hir_ty));
- }
-
- (GenericArgKind::Const(_ct), hir::GenericArg::Const(_hir_ct)) => {
- // Lifetimes cannot be found in consts, so we don't need
- // to search anything here.
- }
-
- (GenericArgKind::Lifetime(_), _)
- | (GenericArgKind::Type(_), _)
- | (GenericArgKind::Const(_), _) => {
- // I *think* that HIR lowering should ensure this
- // doesn't happen, even in erroneous
- // programs. Else we should use delay-span-bug.
- span_bug!(
- hir_arg.span(),
- "unmatched subst and hir arg: found {:?} vs {:?}",
- kind,
- hir_arg,
- );
- }
- }
- }
-
- None
- }
-
- /// Finds a closure upvar that contains `fr` and label it with a
- /// fully elaborated type, returning something like `'1`. Result
- /// looks like:
- ///
- /// ```
- /// | let x = Some(&22);
- /// - fully elaborated type of `x` is `Option<&'1 u32>`
- /// ```
- fn give_name_if_anonymous_region_appears_in_upvars(
- &self,
- tcx: TyCtxt<'tcx>,
- upvars: &[Upvar],
- fr: RegionVid,
- renctx: &mut RegionErrorNamingCtx,
- ) -> Option<RegionName> {
- let upvar_index = self.get_upvar_index_for_region(tcx, fr)?;
- let (upvar_name, upvar_span) =
- self.get_upvar_name_and_span_for_region(tcx, upvars, upvar_index);
- let region_name = renctx.synthesize_region_name();
-
- Some(RegionName {
- name: region_name,
- source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()),
- })
- }
-
- /// Checks for arguments appearing in the (closure) return type. It
- /// must be a closure since, in a free fn, such an argument would
- /// have to either also appear in an argument (if using elision)
- /// or be early bound (named, not in argument).
- fn give_name_if_anonymous_region_appears_in_output(
- &self,
- infcx: &InferCtxt<'_, 'tcx>,
- body: &Body<'tcx>,
- mir_def_id: DefId,
- fr: RegionVid,
- renctx: &mut RegionErrorNamingCtx,
- ) -> Option<RegionName> {
- let tcx = infcx.tcx;
-
- let return_ty = self.universal_regions.unnormalized_output_ty;
- debug!(
- "give_name_if_anonymous_region_appears_in_output: return_ty = {:?}",
- return_ty
- );
- if !tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) {
- return None;
- }
-
- let mut highlight = RegionHighlightMode::default();
- highlight.highlighting_region_vid(fr, renctx.counter);
- let type_name = infcx.extract_type_name(&return_ty, Some(highlight)).0;
-
- let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
-
- let (return_span, mir_description) = match tcx.hir().get(mir_hir_id) {
- hir::Node::Expr(hir::Expr {
- kind: hir::ExprKind::Closure(_, return_ty, _, span, gen_move),
- ..
- }) => (
- match return_ty.output {
- hir::FunctionRetTy::DefaultReturn(_) => tcx.sess.source_map().end_point(*span),
- hir::FunctionRetTy::Return(_) => return_ty.output.span(),
- },
- if gen_move.is_some() {
- " of generator"
- } else {
- " of closure"
- },
- ),
- hir::Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Method(method_sig, _),
- ..
- }) => (method_sig.decl.output.span(), ""),
- _ => (body.span, ""),
- };
-
- Some(RegionName {
- // This counter value will already have been used, so this function will increment it
- // so the next value will be used next and return the region name that would have been
- // used.
- name: renctx.synthesize_region_name(),
- source: RegionNameSource::AnonRegionFromOutput(
- return_span,
- mir_description.to_string(),
- type_name,
- ),
- })
- }
-
- fn give_name_if_anonymous_region_appears_in_yield_ty(
- &self,
- infcx: &InferCtxt<'_, 'tcx>,
- body: &Body<'tcx>,
- mir_def_id: DefId,
- fr: RegionVid,
- renctx: &mut RegionErrorNamingCtx,
- ) -> Option<RegionName> {
- // Note: generators from `async fn` yield `()`, so we don't have to
- // worry about them here.
- let yield_ty = self.universal_regions.yield_ty?;
- debug!(
- "give_name_if_anonymous_region_appears_in_yield_ty: yield_ty = {:?}",
- yield_ty,
- );
-
- let tcx = infcx.tcx;
-
- if !tcx.any_free_region_meets(&yield_ty, |r| r.to_region_vid() == fr) {
- return None;
- }
-
- let mut highlight = RegionHighlightMode::default();
- highlight.highlighting_region_vid(fr, renctx.counter);
- let type_name = infcx.extract_type_name(&yield_ty, Some(highlight)).0;
-
- let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
-
- let yield_span = match tcx.hir().get(mir_hir_id) {
- hir::Node::Expr(hir::Expr {
- kind: hir::ExprKind::Closure(_, _, _, span, _),
- ..
- }) => (
- tcx.sess.source_map().end_point(*span)
- ),
- _ => body.span,
- };
-
- debug!(
- "give_name_if_anonymous_region_appears_in_yield_ty: \
- type_name = {:?}, yield_span = {:?}",
- yield_span,
- type_name,
- );
-
- Some(RegionName {
- name: renctx.synthesize_region_name(),
- source: RegionNameSource::AnonRegionFromYieldTy(yield_span, type_name),
- })
- }
-}
+++ /dev/null
-use crate::borrow_check::nll::region_infer::RegionInferenceContext;
-use crate::borrow_check::nll::ToRegionVid;
-use crate::borrow_check::Upvar;
-use rustc::mir::{Local, Body};
-use rustc::ty::{RegionVid, TyCtxt};
-use rustc_index::vec::{Idx, IndexVec};
-use syntax::source_map::Span;
-use syntax_pos::symbol::Symbol;
-
-impl<'tcx> RegionInferenceContext<'tcx> {
- crate fn get_var_name_and_span_for_region(
- &self,
- tcx: TyCtxt<'tcx>,
- body: &Body<'tcx>,
- local_names: &IndexVec<Local, Option<Symbol>>,
- upvars: &[Upvar],
- fr: RegionVid,
- ) -> Option<(Option<Symbol>, Span)> {
- debug!("get_var_name_and_span_for_region(fr={:?})", fr);
- assert!(self.universal_regions.is_universal_region(fr));
-
- debug!("get_var_name_and_span_for_region: attempting upvar");
- self.get_upvar_index_for_region(tcx, fr)
- .map(|index| {
- let (name, span) =
- self.get_upvar_name_and_span_for_region(tcx, upvars, index);
- (Some(name), span)
- })
- .or_else(|| {
- debug!("get_var_name_and_span_for_region: attempting argument");
- self.get_argument_index_for_region(tcx, fr).map(|index| {
- self.get_argument_name_and_span_for_region(body, local_names, index)
- })
- })
- }
-
- /// Search the upvars (if any) to find one that references fr. Return its index.
- crate fn get_upvar_index_for_region(&self, tcx: TyCtxt<'tcx>, fr: RegionVid) -> Option<usize> {
- let upvar_index = self
- .universal_regions
- .defining_ty
- .upvar_tys(tcx)
- .position(|upvar_ty| {
- debug!("get_upvar_index_for_region: upvar_ty={:?}", upvar_ty);
- tcx.any_free_region_meets(&upvar_ty, |r| {
- let r = r.to_region_vid();
- debug!("get_upvar_index_for_region: r={:?} fr={:?}", r, fr);
- r == fr
- })
- })?;
-
- let upvar_ty = self
- .universal_regions
- .defining_ty
- .upvar_tys(tcx)
- .nth(upvar_index);
-
- debug!(
- "get_upvar_index_for_region: found {:?} in upvar {} which has type {:?}",
- fr, upvar_index, upvar_ty,
- );
-
- Some(upvar_index)
- }
-
- /// Given the index of an upvar, finds its name and the span from where it was
- /// declared.
- crate fn get_upvar_name_and_span_for_region(
- &self,
- tcx: TyCtxt<'tcx>,
- upvars: &[Upvar],
- upvar_index: usize,
- ) -> (Symbol, Span) {
- let upvar_hir_id = upvars[upvar_index].var_hir_id;
- debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id);
-
- let upvar_name = tcx.hir().name(upvar_hir_id);
- let upvar_span = tcx.hir().span(upvar_hir_id);
- debug!("get_upvar_name_and_span_for_region: upvar_name={:?} upvar_span={:?}",
- upvar_name, upvar_span);
-
- (upvar_name, upvar_span)
- }
-
- /// Search the argument types for one that references fr (which should be a free region).
- /// Returns Some(_) with the index of the input if one is found.
- ///
- /// N.B., in the case of a closure, the index is indexing into the signature as seen by the
- /// user - in particular, index 0 is not the implicit self parameter.
- crate fn get_argument_index_for_region(
- &self,
- tcx: TyCtxt<'tcx>,
- fr: RegionVid,
- ) -> Option<usize> {
- let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
- let argument_index = self
- .universal_regions
- .unnormalized_input_tys
- .iter()
- .skip(implicit_inputs)
- .position(|arg_ty| {
- debug!(
- "get_argument_index_for_region: arg_ty = {:?}",
- arg_ty
- );
- tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr)
- })?;
-
- debug!(
- "get_argument_index_for_region: found {:?} in argument {} which has type {:?}",
- fr, argument_index, self.universal_regions.unnormalized_input_tys[argument_index],
- );
-
- Some(argument_index)
- }
-
- /// Given the index of an argument, finds its name (if any) and the span from where it was
- /// declared.
- crate fn get_argument_name_and_span_for_region(
- &self,
- body: &Body<'tcx>,
- local_names: &IndexVec<Local, Option<Symbol>>,
- argument_index: usize,
- ) -> (Option<Symbol>, Span) {
- let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
- let argument_local = Local::new(implicit_inputs + argument_index + 1);
- debug!("get_argument_name_and_span_for_region: argument_local={:?}", argument_local);
-
- let argument_name = local_names[argument_local];
- let argument_span = body.local_decls[argument_local].source_info.span;
- debug!("get_argument_name_and_span_for_region: argument_name={:?} argument_span={:?}",
- argument_name, argument_span);
-
- (argument_name, argument_span)
- }
-}
use std::rc::Rc;
-use crate::borrow_check::nll::{
- constraints::{
- graph::NormalConstraintGraph,
- ConstraintSccIndex,
- OutlivesConstraint,
- OutlivesConstraintSet,
- },
- member_constraints::{MemberConstraintSet, NllMemberConstraintIndex},
- region_infer::values::{
- PlaceholderIndices, RegionElement, ToElementIndex
- },
- region_infer::error_reporting::outlives_suggestion::OutlivesSuggestionBuilder,
- type_check::{free_region_relations::UniversalRegionRelations, Locations},
-};
-use crate::borrow_check::Upvar;
-
use rustc::hir::def_id::DefId;
use rustc::infer::canonical::QueryOutlivesConstraint;
use rustc::infer::opaque_types;
use syntax_pos::Span;
use syntax_pos::symbol::Symbol;
-crate use self::error_reporting::{RegionName, RegionNameSource, RegionErrorNamingCtx};
+use crate::borrow_check::{
+ nll::{
+ constraints::{
+ graph::NormalConstraintGraph,
+ ConstraintSccIndex,
+ OutlivesConstraint,
+ OutlivesConstraintSet,
+ },
+ member_constraints::{MemberConstraintSet, NllMemberConstraintIndex},
+ region_infer::values::{
+ PlaceholderIndices, RegionElement, ToElementIndex
+ },
+ type_check::{free_region_relations::UniversalRegionRelations, Locations},
+ },
+ diagnostics::{
+ OutlivesSuggestionBuilder, RegionErrorNamingCtx,
+ },
+ Upvar,
+};
+
use self::values::{LivenessValues, RegionValueElements, RegionValues};
use super::universal_regions::UniversalRegions;
use super::ToRegionVid;
mod dump_mir;
-mod error_reporting;
mod graphviz;
pub mod values;
/// variables are identified by their index (`RegionVid`). The
/// definition contains information about where the region came
/// from as well as its final inferred value.
- definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
+ pub(in crate::borrow_check) definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
/// The liveness constraints added to each region. For most
/// regions, these start out empty and steadily grow, though for
/// each universally quantified region R they start out containing
/// the entire CFG and `end(R)`.
- liveness_constraints: LivenessValues<RegionVid>,
+ pub(in crate::borrow_check) liveness_constraints: LivenessValues<RegionVid>,
/// The outlives constraints computed by the type-check.
- constraints: Rc<OutlivesConstraintSet>,
+ pub(in crate::borrow_check) constraints: Rc<OutlivesConstraintSet>,
/// The constraint-set, but in graph form, making it easy to traverse
/// the constraints adjacent to a particular region. Used to construct
/// the SCC (see `constraint_sccs`) and for error reporting.
- constraint_graph: Rc<NormalConstraintGraph>,
+ pub(in crate::borrow_check) constraint_graph: Rc<NormalConstraintGraph>,
/// The SCC computed from `constraints` and the constraint
/// graph. We have an edge from SCC A to SCC B if `A: B`. Used to
/// compute the values of each region.
- constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
+ pub(in crate::borrow_check) constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B`
/// exists if `B: A`. Computed lazilly.
- rev_constraint_graph: Option<Rc<VecGraph<ConstraintSccIndex>>>,
+ pub(in crate::borrow_check) rev_constraint_graph:
+ Option<Rc<VecGraph<ConstraintSccIndex>>>,
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
- member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
+ pub(in crate::borrow_check) member_constraints:
+ Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
/// Records the member constraints that we applied to each scc.
/// This is useful for error reporting. Once constraint
/// propagation is done, this vector is sorted according to
/// `member_region_scc`.
- member_constraints_applied: Vec<AppliedMemberConstraint>,
+ pub(in crate::borrow_check) member_constraints_applied: Vec<AppliedMemberConstraint>,
/// Map closure bounds to a `Span` that should be used for error reporting.
- closure_bounds_mapping:
+ pub(in crate::borrow_check) closure_bounds_mapping:
FxHashMap<Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>>,
/// Contains the minimum universe of any variable within the same
/// SCC. We will ensure that no SCC contains values that are not
/// visible from this index.
- scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
+ pub(in crate::borrow_check) scc_universes:
+ IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
/// Contains a "representative" from each SCC. This will be the
/// minimal RegionVid belonging to that universe. It is used as a
/// of its SCC and be sure that -- if they have the same repr --
/// they *must* be equal (though not having the same repr does not
/// mean they are unequal).
- scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
+ pub(in crate::borrow_check) scc_representatives:
+ IndexVec<ConstraintSccIndex, ty::RegionVid>,
/// The final inferred values of the region variables; we compute
/// one value per SCC. To get the value for any given *region*,
/// you first find which scc it is a part of.
- scc_values: RegionValues<ConstraintSccIndex>,
+ pub(in crate::borrow_check) scc_values: RegionValues<ConstraintSccIndex>,
/// Type constraints that we check after solving.
- type_tests: Vec<TypeTest<'tcx>>,
+ pub(in crate::borrow_check) type_tests: Vec<TypeTest<'tcx>>,
/// Information about the universally quantified regions in scope
/// on this function.
- universal_regions: Rc<UniversalRegions<'tcx>>,
+ pub (in crate::borrow_check) universal_regions: Rc<UniversalRegions<'tcx>>,
/// Information about how the universally quantified regions in
/// scope on this function relate to one another.
- universal_region_relations: Rc<UniversalRegionRelations<'tcx>>,
+ pub(in crate::borrow_check) universal_region_relations:
+ Rc<UniversalRegionRelations<'tcx>>,
}
/// Each time that `apply_member_constraint` is successful, it appends
/// with `'R: 'O` where `'R` is the pick-region and `'O` is the
/// minimal viable option.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
-struct AppliedMemberConstraint {
+pub(crate) struct AppliedMemberConstraint {
/// The SCC that was affected. (The "member region".)
///
/// The vector if `AppliedMemberConstraint` elements is kept sorted
/// by this field.
- member_region_scc: ConstraintSccIndex,
+ pub(in crate::borrow_check) member_region_scc: ConstraintSccIndex,
/// The "best option" that `apply_member_constraint` found -- this was
/// added as an "ad-hoc" lower-bound to `member_region_scc`.
- min_choice: ty::RegionVid,
+ pub(in crate::borrow_check) min_choice: ty::RegionVid,
/// The "member constraint index" -- we can find out details about
/// the constraint from
/// `set.member_constraints[member_constraint_index]`.
- member_constraint_index: NllMemberConstraintIndex,
+ pub(in crate::borrow_check) member_constraint_index: NllMemberConstraintIndex,
}
-struct RegionDefinition<'tcx> {
+pub(crate) struct RegionDefinition<'tcx> {
/// What kind of variable is this -- a free region? existential
/// variable? etc. (See the `NLLRegionVariableOrigin` for more
/// info.)
- origin: NLLRegionVariableOrigin,
+ pub(in crate::borrow_check) origin: NLLRegionVariableOrigin,
/// Which universe is this region variable defined in? This is
/// most often `ty::UniverseIndex::ROOT`, but when we encounter
/// forall-quantifiers like `for<'a> { 'a = 'b }`, we would create
/// the variable for `'a` in a fresh universe that extends ROOT.
- universe: ty::UniverseIndex,
+ pub(in crate::borrow_check) universe: ty::UniverseIndex,
/// If this is 'static or an early-bound region, then this is
/// `Some(X)` where `X` is the name of the region.
- external_name: Option<ty::Region<'tcx>>,
+ pub(in crate::borrow_check) external_name: Option<ty::Region<'tcx>>,
}
/// N.B., the variants in `Cause` are intentionally ordered. Lower
/// Once region solving has completed, this function will return
/// the member constraints that were applied to the value of a given
/// region `r`. See `AppliedMemberConstraint`.
- fn applied_member_constraints(&self, r: impl ToRegionVid) -> &[AppliedMemberConstraint] {
+ pub(in crate::borrow_check) fn applied_member_constraints(
+ &self, r: impl ToRegionVid
+ ) -> &[AppliedMemberConstraint] {
let scc = self.constraint_sccs.scc(r.to_region_vid());
binary_search_util::binary_search_slice(
&self.member_constraints_applied,
[dependencies]
bitflags = "1.0"
log = "0.4"
-syntax_pos = { path = "../libsyntax_pos" }
-syntax = { path = "../libsyntax" }
-errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_feature = { path = "../librustc_feature" }
rustc_lexer = { path = "../librustc_lexer" }
-rustc_target = { path = "../librustc_target" }
-smallvec = { version = "1.0", features = ["union", "may_dangle"] }
+rustc_errors = { path = "../librustc_errors" }
rustc_error_codes = { path = "../librustc_error_codes" }
+smallvec = { version = "1.0", features = ["union", "may_dangle"] }
+syntax_pos = { path = "../libsyntax_pos" }
+syntax = { path = "../libsyntax" }
use crate::validate_attr;
use rustc_feature::Features;
+use rustc_errors::Applicability;
use syntax::attr::HasAttrs;
use syntax::feature_gate::{feature_err, get_features};
use syntax::attr;
use syntax::util::map_in_place::MapInPlace;
use syntax_pos::symbol::sym;
-use errors::Applicability;
use smallvec::SmallVec;
/// A folder that strips out items that do not belong in the current configuration.
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::{FatalError, DiagnosticBuilder};
+use rustc_lexer::Base;
+use rustc_lexer::unescape;
use syntax::token::{self, Token, TokenKind};
use syntax::sess::ParseSess;
-use syntax::symbol::{sym, Symbol};
use syntax::util::comments;
-
-use errors::{FatalError, DiagnosticBuilder};
+use syntax_pos::symbol::{sym, Symbol};
use syntax_pos::{BytePos, Pos, Span};
-use rustc_lexer::Base;
-use rustc_lexer::unescape;
use std::char;
use std::convert::TryInto;
-use rustc_data_structures::sync::Lrc;
use log::debug;
mod tokentrees;
-use rustc_data_structures::fx::FxHashMap;
-use syntax_pos::Span;
-
use super::{StringReader, UnmatchedBrace};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::PResult;
use syntax::print::pprust::token_to_string;
use syntax::token::{self, Token};
use syntax::tokenstream::{DelimSpan, IsJoint::{self, *}, TokenStream, TokenTree, TreeAndJoint};
-
-use errors::PResult;
+use syntax_pos::Span;
impl<'a> StringReader<'a> {
crate fn into_token_trees(self) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) {
// http://www.unicode.org/Public/security/10.0.0/confusables.txt
use super::StringReader;
-use errors::{Applicability, DiagnosticBuilder};
-use syntax_pos::{BytePos, Pos, Span, symbol::kw};
use crate::token;
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use syntax_pos::{BytePos, Pos, Span, symbol::kw};
#[rustfmt::skip] // for line breaks
const UNICODE_ARRAY: &[(char, &str, char)] = &[
use syntax::token::{self, Nonterminal};
use syntax::tokenstream::{self, TokenStream, TokenTree};
-use errors::{PResult, FatalError, Level, Diagnostic};
+use rustc_errors::{PResult, FatalError, Level, Diagnostic};
use rustc_data_structures::sync::Lrc;
use syntax_pos::{Span, SourceFile, FileName};
macro_rules! panictry_buffer {
($handler:expr, $e:expr) => ({
use std::result::Result::{Ok, Err};
- use errors::FatalError;
+ use rustc_errors::FatalError;
match $e {
Ok(e) => e,
Err(errs) => {
use super::{SeqSep, Parser, TokenType, PathStyle};
+use rustc_errors::PResult;
use syntax::attr;
use syntax::ast;
use syntax::util::comments;
use syntax::token::{self, Nonterminal};
use syntax_pos::{Span, Symbol};
-use errors::PResult;
use log::debug;
use super::{BlockMode, PathStyle, SemiColonMode, TokenType, TokenExpectType, SeqSep, Parser};
-use syntax::ast::{
- self, Param, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind,
- Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind,
-};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{self, PResult, Applicability, DiagnosticBuilder, Handler, pluralize};
+use rustc_error_codes::*;
+use syntax::ast::{self, Param, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item};
+use syntax::ast::{ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind};
use syntax::token::{self, TokenKind, token_can_begin_expr};
use syntax::print::pprust;
use syntax::ptr::P;
-use syntax::symbol::{kw, sym};
use syntax::ThinVec;
use syntax::util::parser::AssocOp;
use syntax::struct_span_err;
-
-use errors::{PResult, Applicability, DiagnosticBuilder, pluralize};
-use rustc_data_structures::fx::FxHashSet;
+use syntax_pos::symbol::{kw, sym};
use syntax_pos::{Span, DUMMY_SP, MultiSpan, SpanSnippetError};
+
use log::{debug, trace};
use std::mem;
-use rustc_error_codes::*;
-
const TURBOFISH: &'static str = "use `::<...>` instead of `<...>` to specify type arguments";
/// Creates a placeholder argument.
}
impl Error {
- fn span_err<S: Into<MultiSpan>>(
+ fn span_err(
self,
- sp: S,
- handler: &errors::Handler,
+ sp: impl Into<MultiSpan>,
+ handler: &Handler,
) -> DiagnosticBuilder<'_> {
match self {
Error::FileNotFoundForModule {
self.sess.span_diagnostic.span_bug(sp, m)
}
- pub(super) fn diagnostic(&self) -> &'a errors::Handler {
+ pub(super) fn diagnostic(&self) -> &'a Handler {
&self.sess.span_diagnostic
}
use super::diagnostics::Error;
use crate::maybe_recover_from_interpolated_ty_qpath;
-use syntax::ast::{
- self, DUMMY_NODE_ID, Attribute, AttrStyle, Ident, CaptureBy, BlockCheckMode,
- Expr, ExprKind, RangeLimits, Label, Movability, IsAsync, Arm, Ty, TyKind,
- FunctionRetTy, Param, FnDecl, BinOpKind, BinOp, UnOp, Mac, AnonConst, Field, Lit,
-};
+use rustc_data_structures::thin_vec::ThinVec;
+use rustc_errors::{PResult, Applicability};
+use syntax::ast::{self, DUMMY_NODE_ID, Attribute, AttrStyle, Ident, CaptureBy, BlockCheckMode};
+use syntax::ast::{Expr, ExprKind, RangeLimits, Label, Movability, IsAsync, Arm, Ty, TyKind};
+use syntax::ast::{FunctionRetTy, Param, FnDecl, BinOpKind, BinOp, UnOp, Mac, AnonConst, Field, Lit};
use syntax::token::{self, Token, TokenKind};
use syntax::print::pprust;
use syntax::ptr::P;
-use syntax::source_map::{self, Span};
use syntax::util::classify;
use syntax::util::literal::LitError;
use syntax::util::parser::{AssocOp, Fixity, prec_let_scrutinee_needs_par};
-use syntax_pos::symbol::{kw, sym};
-use syntax_pos::Symbol;
-use errors::{PResult, Applicability};
+use syntax_pos::source_map::{self, Span};
+use syntax_pos::symbol::{kw, sym, Symbol};
use std::mem;
-use rustc_data_structures::thin_vec::ThinVec;
/// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression
/// dropped into the token stream, which happens while parsing the result of
use super::Parser;
+use rustc_errors::PResult;
use syntax::ast::{self, WhereClause, GenericParam, GenericParamKind, GenericBounds, Attribute};
use syntax::token;
use syntax::source_map::DUMMY_SP;
use syntax_pos::symbol::{kw, sym};
-use errors::PResult;
impl<'a> Parser<'a> {
/// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
use crate::maybe_whole;
+use rustc_errors::{PResult, Applicability, DiagnosticBuilder, StashKey};
+use rustc_error_codes::*;
use syntax::ast::{self, DUMMY_NODE_ID, Ident, Attribute, AttrKind, AttrStyle, AnonConst, Item};
use syntax::ast::{ItemKind, ImplItem, ImplItemKind, TraitItem, TraitItemKind, UseTree, UseTreeKind};
use syntax::ast::{PathSegment, IsAuto, Constness, IsAsync, Unsafety, Defaultness, Extern, StrLit};
use syntax::ThinVec;
use syntax::token;
use syntax::tokenstream::{DelimSpan, TokenTree, TokenStream};
-use syntax::source_map::{self, respan, Span};
use syntax::struct_span_err;
use syntax_pos::BytePos;
+use syntax_pos::source_map::{self, respan, Span};
use syntax_pos::symbol::{kw, sym, Symbol};
-use rustc_error_codes::*;
-
use log::debug;
use std::mem;
-use errors::{PResult, Applicability, DiagnosticBuilder, StashKey};
pub(super) type ItemInfo = (Ident, ItemKind, Option<Vec<Attribute>>);
use crate::{Directory, DirectoryOwnership};
use crate::lexer::UnmatchedBrace;
-use syntax::ast::{
- self, DUMMY_NODE_ID, AttrStyle, Attribute, CrateSugar, Extern, Ident, StrLit,
- IsAsync, MacArgs, MacDelimiter, Mutability, Visibility, VisibilityKind, Unsafety,
-};
-
+use rustc_errors::{PResult, Applicability, DiagnosticBuilder, FatalError};
+use rustc_data_structures::thin_vec::ThinVec;
+use syntax::ast::{self, DUMMY_NODE_ID, AttrStyle, Attribute, CrateSugar, Extern, Ident, StrLit};
+use syntax::ast::{IsAsync, MacArgs, MacDelimiter, Mutability, Visibility, VisibilityKind, Unsafety};
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::token::{self, Token, TokenKind, DelimToken};
use syntax::tokenstream::{self, DelimSpan, TokenTree, TokenStream, TreeAndJoint};
use syntax::sess::ParseSess;
-use syntax::source_map::respan;
use syntax::struct_span_err;
use syntax::util::comments::{doc_comment_style, strip_doc_comment_decoration};
+use syntax_pos::source_map::respan;
use syntax_pos::symbol::{kw, sym, Symbol};
use syntax_pos::{Span, BytePos, DUMMY_SP, FileName};
-use rustc_data_structures::thin_vec::ThinVec;
-use errors::{PResult, Applicability, DiagnosticBuilder, FatalError};
use log::debug;
use std::borrow::Cow;
use crate::{new_sub_parser_from_file, DirectoryOwnership};
+use rustc_errors::PResult;
use syntax::attr;
use syntax::ast::{self, Ident, Attribute, ItemKind, Mod, Crate};
use syntax::token::{self, TokenKind};
-use syntax::source_map::{SourceMap, Span, DUMMY_SP, FileName};
-
+use syntax_pos::source_map::{SourceMap, Span, DUMMY_SP, FileName};
use syntax_pos::symbol::sym;
-use errors::PResult;
use std::path::{self, Path, PathBuf};
// `./<id>.rs` and `./<id>/mod.rs`.
let relative_prefix_string;
let relative_prefix = if let Some(ident) = relative {
- relative_prefix_string = format!("{}{}", ident, path::MAIN_SEPARATOR);
+ relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR);
&relative_prefix_string
} else {
""
};
- let mod_name = id.to_string();
+ let mod_name = id.name.to_string();
let default_path_str = format!("{}{}.rs", relative_prefix, mod_name);
let secondary_path_str = format!("{}{}{}mod.rs",
relative_prefix, mod_name, path::MAIN_SEPARATOR);
use super::{Parser, PathStyle};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
+use rustc_errors::{PResult, Applicability, DiagnosticBuilder};
use syntax::ast::{self, Attribute, Pat, PatKind, FieldPat, RangeEnd, RangeSyntax, Mac};
use syntax::ast::{BindingMode, Ident, Mutability, Path, QSelf, Expr, ExprKind};
use syntax::mut_visit::{noop_visit_pat, noop_visit_mac, MutVisitor};
use syntax::print::pprust;
use syntax::ThinVec;
use syntax::token;
-use syntax::source_map::{respan, Span, Spanned};
+use syntax_pos::source_map::{respan, Span, Spanned};
use syntax_pos::symbol::{kw, sym};
-use errors::{PResult, Applicability, DiagnosticBuilder};
type Expected = Option<&'static str>;
use super::{Parser, TokenType};
use crate::maybe_whole;
+use rustc_errors::{PResult, Applicability, pluralize};
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
use syntax::ast::MacArgs;
use syntax::ThinVec;
use syntax::token::{self, Token};
-use syntax::source_map::{Span, BytePos};
+use syntax_pos::source_map::{Span, BytePos};
use syntax_pos::symbol::{kw, sym};
use std::mem;
use log::debug;
-use errors::{PResult, Applicability, pluralize};
/// Specifies how to parse a path.
#[derive(Copy, Clone, PartialEq)]
use crate::maybe_whole;
use crate::DirectoryOwnership;
+use rustc_errors::{PResult, Applicability};
use syntax::ThinVec;
use syntax::ptr::P;
use syntax::ast;
use syntax::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac};
use syntax::util::classify;
use syntax::token;
-use syntax::source_map::{respan, Span};
-use syntax::symbol::{kw, sym};
+use syntax_pos::source_map::{respan, Span};
+use syntax_pos::symbol::{kw, sym};
use std::mem;
-use errors::{PResult, Applicability};
impl<'a> Parser<'a> {
/// Parses a statement. This stops just before trailing semicolons on everything but items.
use crate::{maybe_whole, maybe_recover_from_interpolated_ty_qpath};
+use rustc_errors::{PResult, Applicability, pluralize};
+use rustc_error_codes::*;
use syntax::ptr::P;
use syntax::ast::{self, Ty, TyKind, MutTy, BareFnTy, FunctionRetTy, GenericParam, Lifetime, Ident};
use syntax::ast::{TraitBoundModifier, TraitObjectSyntax, GenericBound, GenericBounds, PolyTraitRef};
use syntax::ast::{Mutability, AnonConst, Mac};
use syntax::token::{self, Token};
-use syntax::source_map::Span;
use syntax::struct_span_fatal;
+use syntax_pos::source_map::Span;
use syntax_pos::symbol::kw;
-use errors::{PResult, Applicability, pluralize};
-
-use rustc_error_codes::*;
-
/// Returns `true` if `IDENT t` can start a type -- `IDENT::a::b`, `IDENT<u8, u8>`,
/// `IDENT<<u8 as Trait>::AssocTy>`.
///
//! Meta-syntax validation logic of attributes for post-expansion.
-use errors::{PResult, Applicability};
+use rustc_errors::{PResult, Applicability};
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MetaItem, MetaItemKind};
use syntax::attr::mk_name_value_item_str;
}
}
- pub fn demand_eqtype_pat(
+ pub fn demand_eqtype_pat_diag(
&self,
cause_span: Span,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
match_expr_span: Option<Span>,
- ) {
+ ) -> Option<DiagnosticBuilder<'tcx>> {
let cause = if let Some(span) = match_expr_span {
self.cause(
cause_span,
} else {
self.misc(cause_span)
};
- self.demand_eqtype_with_origin(&cause, expected, actual).map(|mut err| err.emit());
+ self.demand_eqtype_with_origin(&cause, expected, actual)
}
+ pub fn demand_eqtype_pat(
+ &self,
+ cause_span: Span,
+ expected: Ty<'tcx>,
+ actual: Ty<'tcx>,
+ match_expr_span: Option<Span>,
+ ) {
+ self.demand_eqtype_pat_diag(cause_span, expected, actual, match_expr_span)
+ .map(|mut err| err.emit());
+ }
pub fn demand_coerce(&self,
expr: &hir::Expr,
// If this is a break with a value, we need to type-check
// the expression. Get an expected type from the loop context.
let opt_coerce_to = {
+ // We should release `enclosing_breakables` before the `check_expr_with_hint`
+ // below, so can't move this block of code to the enclosing scope and share
+ // `ctxt` with the second `encloding_breakables` borrow below.
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
- enclosing_breakables.find_breakable(target_id)
- .coerce
- .as_ref()
- .map(|coerce| coerce.expected_ty())
+ match enclosing_breakables.opt_find_breakable(target_id) {
+ Some(ctxt) =>
+ ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()),
+ None => { // Avoid ICE when `break` is inside a closure (#65383).
+ self.tcx.sess.delay_span_bug(
+ expr.span,
+ "break was outside loop, but no error was emitted",
+ );
+ return tcx.types.err;
+ }
+ }
};
// If the loop context is not a `loop { }`, then break with
let pat_ty = pat_ty.fn_sig(tcx).output();
let pat_ty = pat_ty.no_bound_vars().expect("expected fn type");
- self.demand_eqtype_pat(pat.span, expected, pat_ty, match_arm_pat_span);
+ // Type-check the tuple struct pattern against the expected type.
+ let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, match_arm_pat_span);
+ let had_err = diag.is_some();
+ diag.map(|mut err| err.emit());
// Type-check subpatterns.
if subpats.len() == variant.fields.len()
}
} else {
// Pattern has wrong number of fields.
- self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected);
+ self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err);
on_error();
return tcx.types.err;
}
res: Res,
qpath: &hir::QPath,
subpats: &'tcx [P<Pat>],
- fields: &[ty::FieldDef],
- expected: Ty<'tcx>
+ fields: &'tcx [ty::FieldDef],
+ expected: Ty<'tcx>,
+ had_err: bool,
) {
let subpats_ending = pluralize!(subpats.len());
let fields_ending = pluralize!(fields.len());
// More generally, the expected type wants a tuple variant with one field of an
// N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern
// with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`.
- let missing_parenthesis = match expected.kind {
- ty::Adt(_, substs) if fields.len() == 1 => {
- let field_ty = fields[0].ty(self.tcx, substs);
+ let missing_parenthesis = match (&expected.kind, fields, had_err) {
+ // #67037: only do this if we could sucessfully type-check the expected type against
+ // the tuple struct pattern. Otherwise the substs could get out of range on e.g.,
+ // `let P() = U;` where `P != U` with `struct P<T>(T);`.
+ (ty::Adt(_, substs), [field], false) => {
+ let field_ty = self.field_ty(pat_span, field, substs);
match field_ty.kind {
ty::Tuple(_) => field_ty.tuple_fields().count() == subpats.len(),
_ => false,
impl fmt::Debug for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.is_raw_guess() {
+ write!(f, "r#")?;
+ }
write!(f, "{}{:?}", self.name, self.span.ctxt())
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.is_raw_guess() {
+ write!(f, "r#")?;
+ }
fmt::Display::fmt(&self.name, f)
}
}
}
};
-class RustPrintModulePass : public ModulePass {
- raw_ostream* OS;
- DemangleFn Demangle;
-public:
- static char ID;
- RustPrintModulePass() : ModulePass(ID), OS(nullptr), Demangle(nullptr) {}
- RustPrintModulePass(raw_ostream &OS, DemangleFn Demangle)
- : ModulePass(ID), OS(&OS), Demangle(Demangle) {}
-
- bool runOnModule(Module &M) override {
- RustAssemblyAnnotationWriter AW(Demangle);
-
- M.print(*OS, &AW, false);
-
- return false;
- }
-
- void getAnalysisUsage(AnalysisUsage &AU) const override {
- AU.setPreservesAll();
- }
-
- static StringRef name() { return "RustPrintModulePass"; }
-};
-
} // namespace
-namespace llvm {
- void initializeRustPrintModulePassPass(PassRegistry&);
-}
-
-char RustPrintModulePass::ID = 0;
-INITIALIZE_PASS(RustPrintModulePass, "print-rust-module",
- "Print rust module to stderr", false, false)
-
extern "C" LLVMRustResult
-LLVMRustPrintModule(LLVMPassManagerRef PMR, LLVMModuleRef M,
- const char *Path, DemangleFn Demangle) {
- llvm::legacy::PassManager *PM = unwrap<llvm::legacy::PassManager>(PMR);
+LLVMRustPrintModule(LLVMModuleRef M, const char *Path, DemangleFn Demangle) {
std::string ErrorInfo;
-
std::error_code EC;
raw_fd_ostream OS(Path, EC, sys::fs::F_None);
if (EC)
return LLVMRustResult::Failure;
}
+ RustAssemblyAnnotationWriter AAW(Demangle);
formatted_raw_ostream FOS(OS);
-
- PM->add(new RustPrintModulePass(FOS, Demangle));
-
- PM->run(*unwrap(M));
+ unwrap(M)->print(FOS, &AAW);
return LLVMRustResult::Success;
}
|
LL | fn baz<'a,'b>(x: Type<'a>) -> Type<'static> {
| ^^
- = note: ...so that the expression is assignable:
- expected Type<'_>
- found Type<'a>
+note: ...so that the expression is assignable
+ --> $DIR/project-fn-ret-invariant.rs:48:13
+ |
+LL | bar(foo, x)
+ | ^
+ = note: expected `Type<'_>`
+ found `Type<'a>`
= note: but, the lifetime must be valid for the static lifetime...
- = note: ...so that the expression is assignable:
- expected Type<'static>
- found Type<'_>
+note: ...so that the expression is assignable
+ --> $DIR/project-fn-ret-invariant.rs:48:4
+ |
+LL | bar(foo, x)
+ | ^^^^^^^^^^^
+ = note: expected `Type<'static>`
+ found `Type<'_>`
error: aborting due to previous error
|
LL | let _ = ap.with_copy(|ap| { ap });
| ^^^^^^^^^^^
- = note: ...so that the expression is assignable:
- expected core::ffi::VaList<'_, '_>
- found core::ffi::VaList<'_, '_>
+note: ...so that the expression is assignable
+ --> $DIR/variadic-ffi-4.rs:16:33
+ |
+LL | let _ = ap.with_copy(|ap| { ap });
+ | ^^
+ = note: expected `core::ffi::VaList<'_, '_>`
+ found `core::ffi::VaList<'_, '_>`
note: but, the lifetime must be valid for the method call at 16:13...
--> $DIR/variadic-ffi-4.rs:16:13
|
|
LL | fn with_dyn_debug_static<'a>(x: Box<dyn Debug + 'a>) {
| ^^
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<dyn std::fmt::Debug>
- found std::boxed::Box<(dyn std::fmt::Debug + 'a)>
+note: ...so that the expression is assignable
+ --> $DIR/dyn-trait.rs:20:16
+ |
+LL | static_val(x);
+ | ^
+ = note: expected `std::boxed::Box<dyn std::fmt::Debug>`
+ found `std::boxed::Box<(dyn std::fmt::Debug + 'a)>`
= note: but, the lifetime must be valid for the static lifetime...
- = note: ...so that the types are compatible:
- expected StaticTrait
- found StaticTrait
+note: ...so that the types are compatible
+ --> $DIR/dyn-trait.rs:20:5
+ |
+LL | static_val(x);
+ | ^^^^^^^^^^
+ = note: expected `StaticTrait`
+ found `StaticTrait`
error: aborting due to previous error
|
LL | trait T<'a> {
| ^^
- = note: ...so that the types are compatible:
- expected &'a Self
- found &Self
+note: ...so that the types are compatible
+ --> $DIR/issue-16683.rs:4:14
+ |
+LL | self.a();
+ | ^
+ = note: expected `&'a Self`
+ found `&Self`
error: aborting due to previous error
|
LL | trait Foo<'a> {
| ^^
- = note: ...so that the types are compatible:
- expected &'a Self
- found &Self
+note: ...so that the types are compatible
+ --> $DIR/issue-17758.rs:7:14
+ |
+LL | self.foo();
+ | ^^^
+ = note: expected `&'a Self`
+ found `&Self`
error: aborting due to previous error
|
LL | impl<'a> Publisher<'a> for MyStruct<'a> {
| ^^
- = note: ...so that the types are compatible:
- expected Publisher<'_>
- found Publisher<'_>
+note: ...so that the types are compatible
+ --> $DIR/issue-20831-debruijn.rs:28:5
+ |
+LL | / fn subscribe(&mut self, t : Box<dyn Subscriber<Input=<Self as Publisher>::Output> + 'a>) {
+LL | | // Not obvious, but there is an implicit lifetime here -------^
+LL | |
+LL | |
+... |
+LL | | self.sub = t;
+LL | | }
+ | |_____^
+ = note: expected `Publisher<'_>`
+ found `Publisher<'_>`
error: aborting due to 3 previous errors
|
LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
| ^^
- = note: ...so that the types are compatible:
- expected (&&(T,),)
- found (&&'a (T,),)
+note: ...so that the types are compatible
+ --> $DIR/issue-52213.rs:2:11
+ |
+LL | match (&t,) {
+ | ^^^^^
+ = note: expected `(&&(T,),)`
+ found `(&&'a (T,),)`
note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 1:27...
--> $DIR/issue-52213.rs:1:27
|
LL | Box::new(self.out_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<(dyn std::iter::Iterator<Item = <Self as Graph<'a>>::Node> + 'static)>
- found std::boxed::Box<dyn std::iter::Iterator<Item = <Self as Graph<'a>>::Node>>
+note: ...so that the expression is assignable
+ --> $DIR/issue-55796.rs:16:9
+ |
+LL | Box::new(self.out_edges(u).map(|e| e.target()))
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `std::boxed::Box<(dyn std::iter::Iterator<Item = <Self as Graph<'a>>::Node> + 'static)>`
+ found `std::boxed::Box<dyn std::iter::Iterator<Item = <Self as Graph<'a>>::Node>>`
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> $DIR/issue-55796.rs:21:9
LL | Box::new(self.in_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<(dyn std::iter::Iterator<Item = <Self as Graph<'a>>::Node> + 'static)>
- found std::boxed::Box<dyn std::iter::Iterator<Item = <Self as Graph<'a>>::Node>>
+note: ...so that the expression is assignable
+ --> $DIR/issue-55796.rs:21:9
+ |
+LL | Box::new(self.in_edges(u).map(|e| e.target()))
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `std::boxed::Box<(dyn std::iter::Iterator<Item = <Self as Graph<'a>>::Node> + 'static)>`
+ found `std::boxed::Box<dyn std::iter::Iterator<Item = <Self as Graph<'a>>::Node>>`
error: aborting due to 2 previous errors
--- /dev/null
+#![allow(non_camel_case_types)]
+
+trait r#async {
+ fn r#struct(&self) {
+ println!("async");
+ }
+}
+
+trait r#await {
+ fn r#struct(&self) {
+ println!("await");
+ }
+}
+
+struct r#fn {}
+
+impl r#async for r#fn {}
+impl r#await for r#fn {}
+
+fn main() {
+ r#fn {}.r#struct(); //~ ERROR multiple applicable items in scope
+}
--- /dev/null
+error[E0034]: multiple applicable items in scope
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:21:13
+ |
+LL | r#fn {}.r#struct();
+ | ^^^^^^^^ multiple `r#struct` found
+ |
+note: candidate #1 is defined in an impl of the trait `async` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:4:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+ = help: to disambiguate the method call, write `async::r#struct(r#fn {})` instead
+note: candidate #2 is defined in an impl of the trait `await` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:10:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+ = help: to disambiguate the method call, write `await::r#struct(r#fn {})` instead
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0034`.
--- /dev/null
+// Breaks with values inside closures used to ICE (#66863)
+
+fn main() {
+ 'some_label: loop {
+ || break 'some_label (); //~ ERROR: `break` inside of a closure
+ }
+}
--- /dev/null
+error[E0267]: `break` inside of a closure
+ --> $DIR/issue-66702-break-outside-loop-val.rs:5:12
+ |
+LL | || break 'some_label ();
+ | -- ^^^^^^^^^^^^^^^^^^^^ cannot `break` inside of a closure
+ | |
+ | enclosing closure
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0267`.
--- /dev/null
+// Regression test for #67037.
+//
+// In type checking patterns, E0023 occurs when the tuple pattern and the expected
+// tuple pattern have different number of fields. For example, as below, `P()`,
+// the tuple struct pattern, has 0 fields, but requires 1 field.
+//
+// In emitting E0023, we try to see if this is a case of e.g., `Some(a, b, c)` but where
+// the scrutinee was of type `Some((a, b, c))`, and suggest that parenthesis be added.
+//
+// However, we did not account for the expected type being different than the tuple pattern type.
+// This caused an issue when the tuple pattern type (`P<T>`) was generic.
+// Specifically, we tried deriving the 0th field's type using the `substs` of the expected type.
+// When attempting to substitute `T`, there was no such substitution, so "out of range" occured.
+
+struct U {} // 0 type parameters offered
+struct P<T>(T); // 1 type parameter wanted
+
+fn main() {
+ let P() = U {}; //~ ERROR mismatched types
+ //~^ ERROR this pattern has 0 fields, but the corresponding tuple struct has 1 field
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:9
+ |
+LL | let P() = U {};
+ | ^^^ expected struct `U`, found struct `P`
+ |
+ = note: expected struct `U`
+ found struct `P<_>`
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 1 field
+ --> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:9
+ |
+LL | struct P<T>(T); // 1 type parameter wanted
+ | --------------- tuple struct defined here
+...
+LL | let P() = U {};
+ | ^^^ expected 1 field, found 0
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0023, E0308.
+For more information about an error, try `rustc --explain E0023`.
|
LL | impl Foo<'_> {
| ^^
- = note: ...so that the expression is assignable:
- expected Foo<'_>
- found Foo<'_>
+note: ...so that the expression is assignable
+ --> $DIR/issue-55394.rs:9:9
+ |
+LL | Foo { bar }
+ | ^^^^^^^^^^^
+ = note: expected `Foo<'_>`
+ found `Foo<'_>`
error: aborting due to previous error
|
LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {}
| ^^
- = note: ...so that the types are compatible:
- expected Visitor<'d>
- found Visitor<'_>
+note: ...so that the types are compatible
+ --> $DIR/normalization-bounds-error.rs:12:1
+ |
+LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `Visitor<'d>`
+ found `Visitor<'_>`
error: aborting due to previous error
LL | | C { f: b }
LL | | }
| |_____^
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<std::boxed::Box<&isize>>
- found std::boxed::Box<std::boxed::Box<&isize>>
+note: ...so that the expression is assignable
+ --> $DIR/type-alias-free-regions.rs:17:16
+ |
+LL | C { f: b }
+ | ^
+ = note: expected `std::boxed::Box<std::boxed::Box<&isize>>`
+ found `std::boxed::Box<std::boxed::Box<&isize>>`
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 15:6...
--> $DIR/type-alias-free-regions.rs:15:6
|
LL | impl<'a> FromBox<'a> for C<'a> {
| ^^
- = note: ...so that the expression is assignable:
- expected C<'a>
- found C<'_>
+note: ...so that the expression is assignable
+ --> $DIR/type-alias-free-regions.rs:17:9
+ |
+LL | C { f: b }
+ | ^^^^^^^^^^
+ = note: expected `C<'a>`
+ found `C<'_>`
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> $DIR/type-alias-free-regions.rs:27:16
LL | | C { f: Box::new(b.0) }
LL | | }
| |_____^
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<&isize>
- found std::boxed::Box<&isize>
+note: ...so that the expression is assignable
+ --> $DIR/type-alias-free-regions.rs:27:25
+ |
+LL | C { f: Box::new(b.0) }
+ | ^^^
+ = note: expected `std::boxed::Box<&isize>`
+ found `std::boxed::Box<&isize>`
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 25:6...
--> $DIR/type-alias-free-regions.rs:25:6
|
LL | impl<'a> FromTuple<'a> for C<'a> {
| ^^
- = note: ...so that the expression is assignable:
- expected C<'a>
- found C<'_>
+note: ...so that the expression is assignable
+ --> $DIR/type-alias-free-regions.rs:27:9
+ |
+LL | C { f: Box::new(b.0) }
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `C<'a>`
+ found `C<'_>`
error: aborting due to 2 previous errors
|
LL | fn foo<'a>(_: &'a u32) -> &'static u32 {
| ^^
- = note: ...so that the types are compatible:
- expected Foo<'_>
- found Foo<'a>
+note: ...so that the types are compatible
+ --> $DIR/constant-in-expr-inherent-1.rs:8:5
+ |
+LL | <Foo<'a>>::C
+ | ^^^^^^^^^^^^
+ = note: expected `Foo<'_>`
+ found `Foo<'a>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that reference does not outlive borrowed content
--> $DIR/constant-in-expr-inherent-1.rs:8:5
|
LL | fn foo<'a, T: Foo<'a>>() -> &'static u32 {
| ^^
- = note: ...so that the types are compatible:
- expected Foo<'_>
- found Foo<'a>
+note: ...so that the types are compatible
+ --> $DIR/constant-in-expr-trait-item-3.rs:10:5
+ |
+LL | T::C
+ | ^^^^
+ = note: expected `Foo<'_>`
+ found `Foo<'a>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that reference does not outlive borrowed content
--> $DIR/constant-in-expr-trait-item-3.rs:10:5
|
LL | fn load3<'a,'b>(ss: &'a dyn SomeTrait) -> &'b dyn SomeTrait {
| ^^
- = note: ...so that the expression is assignable:
- expected &'b (dyn SomeTrait + 'b)
- found &dyn SomeTrait
+note: ...so that the expression is assignable
+ --> $DIR/object-lifetime-default-elision.rs:71:5
+ |
+LL | ss
+ | ^^
+ = note: expected `&'b (dyn SomeTrait + 'b)`
+ found `&dyn SomeTrait`
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> $DIR/object-lifetime-default-elision.rs:71:5
|
LL | fn load3<'a,'b>(ss: &'a dyn SomeTrait) -> &'b dyn SomeTrait {
| ^^
- = note: ...so that the expression is assignable:
- expected &'b (dyn SomeTrait + 'b)
- found &dyn SomeTrait
+note: ...so that the expression is assignable
+ --> $DIR/object-lifetime-default-elision.rs:71:5
+ |
+LL | ss
+ | ^^
+ = note: expected `&'b (dyn SomeTrait + 'b)`
+ found `&dyn SomeTrait`
error: aborting due to 2 previous errors
}
fn test_if_2() {
- let _ = r#if; //~ ERROR cannot find value `if` in this scope
+ let _ = r#if; //~ ERROR cannot find value `r#if` in this scope
}
fn test_struct_2() {
- let _ = r#struct; //~ ERROR cannot find value `struct` in this scope
+ let _ = r#struct; //~ ERROR cannot find value `r#struct` in this scope
}
fn test_union_2() {
LL | r#union Test;
| ^^^^ expected one of 8 possible tokens
-error[E0425]: cannot find value `if` in this scope
+error[E0425]: cannot find value `r#if` in this scope
--> $DIR/raw-literal-keywords.rs:14:13
|
LL | let _ = r#if;
| ^^^^ not found in this scope
-error[E0425]: cannot find value `struct` in this scope
+error[E0425]: cannot find value `r#struct` in this scope
--> $DIR/raw-literal-keywords.rs:18:13
|
LL | let _ = r#struct;
|
LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
| ^^
- = note: ...so that the expression is assignable:
- expected &[u8]
- found &'a [u8]
+note: ...so that the expression is assignable
+ --> $DIR/region-object-lifetime-in-coercion.rs:26:14
+ |
+LL | Box::new(v)
+ | ^
+ = note: expected `&[u8]`
+ found `&'a [u8]`
note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 25:9...
--> $DIR/region-object-lifetime-in-coercion.rs:25:9
|
LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
| ^^
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<(dyn Foo + 'b)>
- found std::boxed::Box<dyn Foo>
+note: ...so that the expression is assignable
+ --> $DIR/region-object-lifetime-in-coercion.rs:26:5
+ |
+LL | Box::new(v)
+ | ^^^^^^^^^^^
+ = note: expected `std::boxed::Box<(dyn Foo + 'b)>`
+ found `std::boxed::Box<dyn Foo>`
error: aborting due to 4 previous errors
|
LL | impl<'a> Foo<'static> for &'a i32 {
| ^^
- = note: ...so that the types are compatible:
- expected Foo<'static>
- found Foo<'static>
+note: ...so that the types are compatible
+ --> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:14:10
+ |
+LL | impl<'a> Foo<'static> for &'a i32 {
+ | ^^^^^^^^^^^^
+ = note: expected `Foo<'static>`
+ found `Foo<'static>`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `&i32` will meet its required lifetime bounds
--> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:14:10
|
LL | impl<'a,'b> Foo<'b> for &'a i64 {
| ^^
- = note: ...so that the types are compatible:
- expected Foo<'b>
- found Foo<'_>
+note: ...so that the types are compatible
+ --> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:19:13
+ |
+LL | impl<'a,'b> Foo<'b> for &'a i64 {
+ | ^^^^^^^
+ = note: expected `Foo<'b>`
+ found `Foo<'_>`
note: but, the lifetime must be valid for the lifetime `'b` as defined on the impl at 19:9...
--> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:19:9
|
|
LL | impl<'a> Foo for &'a i32 {
| ^^
- = note: ...so that the types are compatible:
- expected Foo
- found Foo
+note: ...so that the types are compatible
+ --> $DIR/regions-assoc-type-static-bound-in-trait-not-met.rs:9:10
+ |
+LL | impl<'a> Foo for &'a i32 {
+ | ^^^
+ = note: expected `Foo`
+ found `Foo`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `&i32` will meet its required lifetime bounds
--> $DIR/regions-assoc-type-static-bound-in-trait-not-met.rs:9:10
LL | box B(&*v) as Box<dyn X>
| ^^^
= note: but, the lifetime must be valid for the static lifetime...
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<(dyn X + 'static)>
- found std::boxed::Box<dyn X>
+note: ...so that the expression is assignable
+ --> $DIR/regions-close-object-into-object-2.rs:10:5
+ |
+LL | box B(&*v) as Box<dyn X>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `std::boxed::Box<(dyn X + 'static)>`
+ found `std::boxed::Box<dyn X>`
error: aborting due to previous error
LL | box B(&*v) as Box<dyn X>
| ^^^
= note: but, the lifetime must be valid for the static lifetime...
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<(dyn X + 'static)>
- found std::boxed::Box<dyn X>
+note: ...so that the expression is assignable
+ --> $DIR/regions-close-object-into-object-4.rs:10:5
+ |
+LL | box B(&*v) as Box<dyn X>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `std::boxed::Box<(dyn X + 'static)>`
+ found `std::boxed::Box<dyn X>`
error: aborting due to previous error
|
LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
| ^^
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<(dyn SomeTrait + 'c)>
- found std::boxed::Box<dyn SomeTrait>
+note: ...so that the expression is assignable
+ --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
+ |
+LL | box v as Box<dyn SomeTrait + 'a>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `std::boxed::Box<(dyn SomeTrait + 'c)>`
+ found `std::boxed::Box<dyn SomeTrait>`
error: aborting due to previous error
|
LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
| ^^
- = note: ...so that the expression is assignable:
- expected &Ast<'_>
- found &Ast<'a>
+note: ...so that the expression is assignable
+ --> $DIR/regions-creating-enums4.rs:7:14
+ |
+LL | Ast::Add(x, y)
+ | ^
+ = note: expected `&Ast<'_>`
+ found `&Ast<'a>`
note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 6:19...
--> $DIR/regions-creating-enums4.rs:6:19
|
LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
| ^^
- = note: ...so that the expression is assignable:
- expected Ast<'b>
- found Ast<'_>
+note: ...so that the expression is assignable
+ --> $DIR/regions-creating-enums4.rs:7:5
+ |
+LL | Ast::Add(x, y)
+ | ^^^^^^^^^^^^^^
+ = note: expected `Ast<'b>`
+ found `Ast<'_>`
error: aborting due to previous error
|
LL | s.f(|p| p)
| ^^^^^
- = note: ...so that the expression is assignable:
- expected &i32
- found &i32
+note: ...so that the expression is assignable
+ --> $DIR/regions-escape-method.rs:15:13
+ |
+LL | s.f(|p| p)
+ | ^
+ = note: expected `&i32`
+ found `&i32`
note: but, the lifetime must be valid for the method call at 15:5...
--> $DIR/regions-escape-method.rs:15:5
|
|
LL | with(|o| o)
| ^^^^^
- = note: ...so that the expression is assignable:
- expected &isize
- found &isize
+note: ...so that the expression is assignable
+ --> $DIR/regions-escape-via-trait-or-not.rs:18:14
+ |
+LL | with(|o| o)
+ | ^
+ = note: expected `&isize`
+ found `&isize`
note: but, the lifetime must be valid for the expression at 18:5...
--> $DIR/regions-escape-via-trait-or-not.rs:18:5
|
LL | | return z;
LL | | }));
| |_____^
- = note: ...so that the types are compatible:
- expected &isize
- found &isize
+note: ...so that the types are compatible
+ --> $DIR/regions-nested-fns.rs:13:76
+ |
+LL | ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
+ | ____________________________________________________________________________^
+LL | | if false { return x; }
+LL | | if false { return ay; }
+LL | | return z;
+LL | | }));
+ | |_____^
+ = note: expected `&isize`
+ found `&isize`
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> $DIR/regions-nested-fns.rs:14:27
|
LL | fn bar<'a, 'b>()
| ^^
- = note: ...so that the types are compatible:
- expected Project<'a, 'b>
- found Project<'_, '_>
+note: ...so that the types are compatible
+ --> $DIR/regions-normalize-in-where-clause-list.rs:22:1
+ |
+LL | / fn bar<'a, 'b>()
+LL | | where <() as Project<'a, 'b>>::Item : Eq
+LL | | {
+LL | | }
+ | |_^
+ = note: expected `Project<'a, 'b>`
+ found `Project<'_, '_>`
error: aborting due to previous error
|
LL | with(|o| o)
| ^^^^^
- = note: ...so that the expression is assignable:
- expected &isize
- found &isize
+note: ...so that the expression is assignable
+ --> $DIR/regions-ret-borrowed-1.rs:10:14
+ |
+LL | with(|o| o)
+ | ^
+ = note: expected `&isize`
+ found `&isize`
note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 9:14...
--> $DIR/regions-ret-borrowed-1.rs:9:14
|
|
LL | with(|o| o)
| ^^^^^
- = note: ...so that the expression is assignable:
- expected &isize
- found &isize
+note: ...so that the expression is assignable
+ --> $DIR/regions-ret-borrowed.rs:13:14
+ |
+LL | with(|o| o)
+ | ^
+ = note: expected `&isize`
+ found `&isize`
note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 12:14...
--> $DIR/regions-ret-borrowed.rs:12:14
|
|
LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
| ^^
- = note: ...so that the expression is assignable:
- expected &'b mut (dyn Dummy + 'b)
- found &mut (dyn Dummy + 'b)
+note: ...so that the expression is assignable
+ --> $DIR/regions-trait-object-subtyping.rs:15:5
+ |
+LL | x
+ | ^
+ = note: expected `&'b mut (dyn Dummy + 'b)`
+ found `&mut (dyn Dummy + 'b)`
error[E0308]: mismatched types
--> $DIR/regions-trait-object-subtyping.rs:22:5
|
LL | struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 }
| ^^^
- = note: ...so that the types are compatible:
- expected W<'l1, 'l2>
- found W<'_, '_>
+note: ...so that the types are compatible
+ --> $DIR/reject-specialized-drops-8142.rs:54:1
+ |
+LL | impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `W<'l1, 'l2>`
+ found `W<'_, '_>`
error: aborting due to 8 previous errors
fn main() {
foo::let(); //~ ERROR expected identifier, found keyword `let`
- r#break(); //~ ERROR cannot find function `break` in this scope
+ r#break(); //~ ERROR cannot find function `r#break` in this scope
}
LL | foo::r#let();
| ^^^^^
-error[E0425]: cannot find function `break` in this scope
+error[E0425]: cannot find function `r#break` in this scope
--> $DIR/raw-name-use-suggestion.rs:8:5
|
LL | r#break();
|
LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> {
| ^^
- = note: ...so that the types are compatible:
- expected T1<'a>
- found T1<'_>
+note: ...so that the types are compatible
+ --> $DIR/trait-impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:13
+ |
+LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> {
+ | ^^^^^^^^^^
+ = note: expected `T1<'a>`
+ found `T1<'_>`
error: aborting due to previous error
LL | Box::new(items.iter())
| ^^^^^
= note: but, the lifetime must be valid for the static lifetime...
- = note: ...so that the expression is assignable:
- expected std::boxed::Box<(dyn std::iter::Iterator<Item = &T> + 'static)>
- found std::boxed::Box<dyn std::iter::Iterator<Item = &T>>
+note: ...so that the expression is assignable
+ --> $DIR/dyn-trait-underscore.rs:8:5
+ |
+LL | Box::new(items.iter())
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `std::boxed::Box<(dyn std::iter::Iterator<Item = &T> + 'static)>`
+ found `std::boxed::Box<dyn std::iter::Iterator<Item = &T>>`
error: aborting due to previous error