1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use core::unicode::property::Pattern_White_Space;
12 use std::fmt::{self, Display};
16 use rustc_errors::{DiagnosticBuilder,Applicability};
19 use borrow_check::MirBorrowckCtxt;
20 use borrow_check::prefixes::PrefixSet;
21 use dataflow::move_paths::{
22 IllegalMoveOrigin, IllegalMoveOriginKind, InitLocation,
23 LookupResult, MoveError, MovePathIndex,
25 use util::borrowck_errors::{BorrowckErrors, Origin};
27 // Often when desugaring a pattern match we may have many individual moves in
28 // MIR that are all part of one operation from the user's point-of-view. For
33 // would move x from the 0 field of some temporary, and y from the 1 field. We
34 // group such errors together for cleaner error reporting.
36 // Errors are kept separate if they are from places with different parent move
37 // paths. For example, this generates two errors:
39 // let (&x, &y) = (&String::new(), &String::new());
41 enum GroupedMoveError<'tcx> {
42 // Place expression can't be moved from,
43 // e.g., match x[0] { s => (), } where x: &[String]
45 original_path: Place<'tcx>,
47 move_from: Place<'tcx>,
48 kind: IllegalMoveOriginKind<'tcx>,
51 // Part of a value expression can't be moved from,
52 // e.g., match &String::new() { &x => (), }
54 original_path: Place<'tcx>,
56 move_from: MovePathIndex,
57 kind: IllegalMoveOriginKind<'tcx>,
60 // Everything that isn't from pattern matching.
62 original_path: Place<'tcx>,
64 kind: IllegalMoveOriginKind<'tcx>,
68 enum BorrowedContentSource {
75 impl Display for BorrowedContentSource {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78 BorrowedContentSource::Arc => write!(f, "an `Arc`"),
79 BorrowedContentSource::Rc => write!(f, "an `Rc`"),
80 BorrowedContentSource::DerefRawPointer => write!(f, "dereference of raw pointer"),
81 BorrowedContentSource::Other => write!(f, "borrowed content"),
86 impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
87 pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) {
88 let grouped_errors = self.group_move_errors(move_errors);
89 for error in grouped_errors {
96 errors: Vec<(Place<'tcx>, MoveError<'tcx>)>
97 ) -> Vec<GroupedMoveError<'tcx>> {
98 let mut grouped_errors = Vec::new();
99 for (original_path, error) in errors {
100 self.append_to_grouped_errors(&mut grouped_errors, original_path, error);
105 fn append_to_grouped_errors(
107 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
108 original_path: Place<'tcx>,
109 error: MoveError<'tcx>,
112 MoveError::UnionMove { .. } => {
113 unimplemented!("don't know how to report union move errors yet.")
115 MoveError::IllegalMove {
116 cannot_move_out_of: IllegalMoveOrigin { location, kind },
118 let stmt_source_info = self.mir.source_info(location);
119 // Note: that the only time we assign a place isn't a temporary
120 // to a user variable is when initializing it.
121 // If that ever stops being the case, then the ever initialized
122 // flow could be used.
123 if let Some(StatementKind::Assign(
125 box Rvalue::Use(Operand::Move(move_from)),
126 )) = self.mir.basic_blocks()[location.block]
128 .get(location.statement_index)
129 .map(|stmt| &stmt.kind)
131 let local_decl = &self.mir.local_decls[*local];
132 // opt_match_place is the
133 // match_span is the span of the expression being matched on
134 // match *x.y { ... } match_place is Some(*x.y)
135 // ^^^^ match_span is the span of *x.y
137 // opt_match_place is None for let [mut] x = ... statements,
138 // whether or not the right-hand side is a place expression
139 if let Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
140 opt_match_place: Some((ref opt_match_place, match_span)),
144 }))) = local_decl.is_user_variable
146 self.append_binding_error(
154 stmt_source_info.span,
159 grouped_errors.push(GroupedMoveError::OtherIllegalMove {
160 span: stmt_source_info.span,
168 fn append_binding_error(
170 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
171 kind: IllegalMoveOriginKind<'tcx>,
172 original_path: Place<'tcx>,
173 move_from: &Place<'tcx>,
175 match_place: &Option<Place<'tcx>>,
177 statement_span: Span,
180 "append_binding_error(match_place={:?}, match_span={:?})",
181 match_place, match_span
184 let from_simple_let = match_place.is_none();
185 let match_place = match_place.as_ref().unwrap_or(move_from);
187 match self.move_data.rev_lookup.find(match_place) {
188 // Error with the match place
189 LookupResult::Parent(_) => {
190 for ge in &mut *grouped_errors {
191 if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge {
192 if match_span == *span {
193 debug!("appending local({:?}) to list", bind_to);
194 if !binds_to.is_empty() {
195 binds_to.push(bind_to);
201 debug!("found a new move error location");
203 // Don't need to point to x in let x = ... .
204 let (binds_to, span) = if from_simple_let {
205 (vec![], statement_span)
207 (vec![bind_to], match_span)
209 grouped_errors.push(GroupedMoveError::MovesFromPlace {
211 move_from: match_place.clone(),
217 // Error with the pattern
218 LookupResult::Exact(_) => {
219 let mpi = match self.move_data.rev_lookup.find(move_from) {
220 LookupResult::Parent(Some(mpi)) => mpi,
221 // move_from should be a projection from match_place.
222 _ => unreachable!("Probably not unreachable..."),
224 for ge in &mut *grouped_errors {
225 if let GroupedMoveError::MovesFromValue {
227 move_from: other_mpi,
232 if match_span == *span && mpi == *other_mpi {
233 debug!("appending local({:?}) to list", bind_to);
234 binds_to.push(bind_to);
239 debug!("found a new move error location");
240 grouped_errors.push(GroupedMoveError::MovesFromValue {
245 binds_to: vec![bind_to],
251 fn report(&mut self, error: GroupedMoveError<'tcx>) {
252 let (mut err, err_span) = {
253 let (span, original_path, kind): (Span, &Place<'tcx>, &IllegalMoveOriginKind) =
255 GroupedMoveError::MovesFromPlace {
261 GroupedMoveError::MovesFromValue { span, ref original_path, ref kind, .. } |
262 GroupedMoveError::OtherIllegalMove { span, ref original_path, ref kind } => {
263 (span, original_path, kind)
266 let origin = Origin::Mir;
267 debug!("report: original_path={:?} span={:?}, kind={:?} \
268 original_path.is_upvar_field_projection={:?}", original_path, span, kind,
269 original_path.is_upvar_field_projection(self.mir, &self.infcx.tcx));
272 IllegalMoveOriginKind::Static => {
273 self.infcx.tcx.cannot_move_out_of(span, "static item", origin)
275 IllegalMoveOriginKind::BorrowedContent { target_place: place } => {
276 // Inspect the type of the content behind the
277 // borrow to provide feedback about why this
278 // was a move rather than a copy.
279 let ty = place.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
280 let is_upvar_field_projection =
281 self.prefixes(&original_path, PrefixSet::All)
282 .any(|p| p.is_upvar_field_projection(self.mir, &self.infcx.tcx)
284 debug!("report: ty={:?}", ty);
286 ty::Array(..) | ty::Slice(..) =>
287 self.infcx.tcx.cannot_move_out_of_interior_noncopy(
288 span, ty, None, origin
290 ty::Closure(def_id, closure_substs)
291 if !self.mir.upvar_decls.is_empty() && is_upvar_field_projection
293 let closure_kind_ty =
294 closure_substs.closure_kind_ty(def_id, self.infcx.tcx);
295 let closure_kind = closure_kind_ty.to_opt_closure_kind();
296 let place_description = match closure_kind {
297 Some(ty::ClosureKind::Fn) => {
298 "captured variable in an `Fn` closure"
300 Some(ty::ClosureKind::FnMut) => {
301 "captured variable in an `FnMut` closure"
303 Some(ty::ClosureKind::FnOnce) => {
304 bug!("closure kind does not match first argument type")
306 None => bug!("closure kind not inferred by borrowck"),
308 debug!("report: closure_kind_ty={:?} closure_kind={:?} \
309 place_description={:?}", closure_kind_ty, closure_kind,
312 let mut diag = self.infcx.tcx.cannot_move_out_of(
313 span, place_description, origin);
315 for prefix in self.prefixes(&original_path, PrefixSet::All) {
316 if let Some(field) = prefix.is_upvar_field_projection(
317 self.mir, &self.infcx.tcx) {
318 let upvar_decl = &self.mir.upvar_decls[field.index()];
320 upvar_decl.var_hir_id.assert_crate_local();
322 self.infcx.tcx.hir().hir_to_node_id(upvar_hir_id);
323 let upvar_span = self.infcx.tcx.hir().span(upvar_node_id);
324 diag.span_label(upvar_span, "captured outer variable");
332 let source = self.borrowed_content_source(place);
333 self.infcx.tcx.cannot_move_out_of(
334 span, &source.to_string(), origin
339 IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
341 .cannot_move_out_of_interior_of_drop(span, ty, origin)
343 IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } =>
344 self.infcx.tcx.cannot_move_out_of_interior_noncopy(
345 span, ty, Some(*is_index), origin
352 self.add_move_hints(error, &mut err, err_span);
353 err.buffer(&mut self.errors_buffer);
358 error: GroupedMoveError<'tcx>,
359 err: &mut DiagnosticBuilder<'a>,
362 let snippet = self.infcx.tcx.sess.source_map().span_to_snippet(span).unwrap();
364 GroupedMoveError::MovesFromPlace {
369 let try_remove_deref = match move_from {
370 Place::Projection(box PlaceProjection {
371 elem: ProjectionElem::Deref,
376 if try_remove_deref && snippet.starts_with('*') {
377 // The snippet doesn't start with `*` in (e.g.) index
378 // expressions `a[b]`, which roughly desugar to
379 // `*Index::index(&a, b)` or
380 // `*IndexMut::index_mut(&mut a, b)`.
381 err.span_suggestion_with_applicability(
383 "consider removing the `*`",
384 snippet[1..].to_owned(),
385 Applicability::Unspecified,
388 err.span_suggestion_with_applicability(
390 "consider borrowing here",
391 format!("&{}", snippet),
392 Applicability::Unspecified,
398 self.add_move_error_details(err, &binds_to);
400 GroupedMoveError::MovesFromValue { mut binds_to, .. } => {
403 self.add_move_error_suggestions(err, &binds_to);
404 self.add_move_error_details(err, &binds_to);
406 // No binding. Nothing to suggest.
407 GroupedMoveError::OtherIllegalMove { .. } => (),
411 fn add_move_error_suggestions(
413 err: &mut DiagnosticBuilder<'a>,
416 let mut suggestions: Vec<(Span, &str, String)> = Vec::new();
417 for local in binds_to {
418 let bind_to = &self.mir.local_decls[*local];
420 ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
424 ) = bind_to.is_user_variable {
425 let pat_snippet = self.infcx.tcx.sess.source_map()
426 .span_to_snippet(pat_span)
428 if pat_snippet.starts_with('&') {
429 let pat_snippet = pat_snippet[1..].trim_start();
432 if pat_snippet.starts_with("mut")
433 && pat_snippet["mut".len()..].starts_with(Pattern_White_Space)
435 suggestion = pat_snippet["mut".len()..].trim_start();
438 suggestion = pat_snippet;
444 suggestion.to_owned(),
449 suggestions.sort_unstable_by_key(|&(span, _, _)| span);
450 suggestions.dedup_by_key(|&mut (span, _, _)| span);
451 for (span, to_remove, suggestion) in suggestions {
452 err.span_suggestion_with_applicability(
454 &format!("consider removing the `{}`", to_remove),
456 Applicability::MachineApplicable,
461 fn add_move_error_details(
463 err: &mut DiagnosticBuilder<'a>,
466 let mut noncopy_var_spans = Vec::new();
467 for (j, local) in binds_to.into_iter().enumerate() {
468 let bind_to = &self.mir.local_decls[*local];
469 let binding_span = bind_to.source_info.span;
472 err.span_label(binding_span, "data moved here");
474 err.span_label(binding_span, "...and here");
477 if binds_to.len() == 1 {
481 "move occurs because `{}` has type `{}`, \
482 which does not implement the `Copy` trait",
483 bind_to.name.unwrap(),
488 noncopy_var_spans.push(binding_span);
492 if binds_to.len() > 1 {
495 "move occurs because these variables have types that \
496 don't implement the `Copy` trait",
501 fn borrowed_content_source(&self, place: &Place<'tcx>) -> BorrowedContentSource {
502 // Look up the provided place and work out the move path index for it,
503 // we'll use this to work back through where this value came from and check whether it
504 // was originally part of an `Rc` or `Arc`.
505 let initial_mpi = match self.move_data.rev_lookup.find(place) {
506 LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => mpi,
507 _ => return BorrowedContentSource::Other,
510 let mut queue = vec![initial_mpi];
511 let mut visited = Vec::new();
512 debug!("borrowed_content_source: queue={:?}", queue);
513 while let Some(mpi) = queue.pop() {
515 "borrowed_content_source: mpi={:?} queue={:?} visited={:?}",
519 // Don't visit the same path twice.
520 if visited.contains(&mpi) {
525 for i in &self.move_data.init_path_map[mpi] {
526 let init = &self.move_data.inits[*i];
527 debug!("borrowed_content_source: init={:?}", init);
528 // We're only interested in statements that initialized a value, not the
529 // initializations from arguments.
530 let loc = match init.location {
531 InitLocation::Statement(stmt) => stmt,
535 let bbd = &self.mir[loc.block];
536 let is_terminator = bbd.statements.len() == loc.statement_index;
537 debug!("borrowed_content_source: loc={:?} is_terminator={:?}", loc, is_terminator);
539 let stmt = &bbd.statements[loc.statement_index];
540 debug!("borrowed_content_source: stmt={:?}", stmt);
541 // We're only interested in assignments (in particular, where the
542 // assignment came from - was it an `Rc` or `Arc`?).
543 if let StatementKind::Assign(_, box Rvalue::Ref(_, _, source)) = &stmt.kind {
544 let ty = source.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
545 let ty = match ty.sty {
546 ty::TyKind::Ref(_, ty, _) => ty,
549 debug!("borrowed_content_source: ty={:?}", ty);
552 return BorrowedContentSource::Arc;
553 } else if ty.is_rc() {
554 return BorrowedContentSource::Rc;
556 queue.push(init.path);
559 } else if let Some(Terminator {
560 kind: TerminatorKind::Call { args, .. },
562 }) = &bbd.terminator {
564 let source = match arg {
565 Operand::Copy(place) | Operand::Move(place) => place,
569 let ty = source.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
570 let ty = match ty.sty {
571 ty::TyKind::Ref(_, ty, _) => ty,
574 debug!("borrowed_content_source: ty={:?}", ty);
577 return BorrowedContentSource::Arc;
578 } else if ty.is_rc() {
579 return BorrowedContentSource::Rc;
581 queue.push(init.path);
588 // If we didn't find an `Arc` or an `Rc`, then check specifically for
589 // a dereference of a place that has the type of a raw pointer.
590 // We can't use `place.ty(..).to_ty(..)` here as that strips away the raw pointer.
591 if let Place::Projection(box Projection {
593 elem: ProjectionElem::Deref,
595 if base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx).is_unsafe_ptr() {
596 return BorrowedContentSource::DerefRawPointer;
600 BorrowedContentSource::Other