]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/borrow_check/move_errors.rs
New upstream version 1.32.0+dfsg1
[rustc.git] / src / librustc_mir / borrow_check / move_errors.rs
CommitLineData
8faf50e0
XL
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.
4//
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.
10
b7449926 11use core::unicode::property::Pattern_White_Space;
0bf4aa26
XL
12use std::fmt::{self, Display};
13
8faf50e0
XL
14use rustc::mir::*;
15use rustc::ty;
0bf4aa26 16use rustc_errors::{DiagnosticBuilder,Applicability};
8faf50e0
XL
17use syntax_pos::Span;
18
19use borrow_check::MirBorrowckCtxt;
b7449926 20use borrow_check::prefixes::PrefixSet;
0bf4aa26
XL
21use dataflow::move_paths::{
22 IllegalMoveOrigin, IllegalMoveOriginKind, InitLocation,
23 LookupResult, MoveError, MovePathIndex,
24};
8faf50e0
XL
25use util::borrowck_errors::{BorrowckErrors, Origin};
26
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
29// example:
30//
31// let (x, y) = foo()
32//
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.
35//
36// Errors are kept separate if they are from places with different parent move
37// paths. For example, this generates two errors:
38//
39// let (&x, &y) = (&String::new(), &String::new());
40#[derive(Debug)]
41enum GroupedMoveError<'tcx> {
b7449926 42 // Place expression can't be moved from,
8faf50e0 43 // e.g. match x[0] { s => (), } where x: &[String]
b7449926
XL
44 MovesFromPlace {
45 original_path: Place<'tcx>,
8faf50e0
XL
46 span: Span,
47 move_from: Place<'tcx>,
48 kind: IllegalMoveOriginKind<'tcx>,
49 binds_to: Vec<Local>,
50 },
b7449926 51 // Part of a value expression can't be moved from,
8faf50e0 52 // e.g. match &String::new() { &x => (), }
b7449926
XL
53 MovesFromValue {
54 original_path: Place<'tcx>,
8faf50e0
XL
55 span: Span,
56 move_from: MovePathIndex,
57 kind: IllegalMoveOriginKind<'tcx>,
58 binds_to: Vec<Local>,
59 },
60 // Everything that isn't from pattern matching.
61 OtherIllegalMove {
b7449926 62 original_path: Place<'tcx>,
8faf50e0
XL
63 span: Span,
64 kind: IllegalMoveOriginKind<'tcx>,
65 },
66}
67
0bf4aa26
XL
68enum BorrowedContentSource {
69 Arc,
70 Rc,
71 DerefRawPointer,
72 Other,
73}
74
75impl Display for BorrowedContentSource {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 match *self {
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"),
82 }
83 }
84}
85
8faf50e0 86impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
b7449926 87 pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) {
8faf50e0
XL
88 let grouped_errors = self.group_move_errors(move_errors);
89 for error in grouped_errors {
90 self.report(error);
91 }
92 }
93
b7449926
XL
94 fn group_move_errors(
95 &self,
96 errors: Vec<(Place<'tcx>, MoveError<'tcx>)>
97 ) -> Vec<GroupedMoveError<'tcx>> {
8faf50e0 98 let mut grouped_errors = Vec::new();
b7449926
XL
99 for (original_path, error) in errors {
100 self.append_to_grouped_errors(&mut grouped_errors, original_path, error);
8faf50e0
XL
101 }
102 grouped_errors
103 }
104
105 fn append_to_grouped_errors(
106 &self,
107 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
b7449926 108 original_path: Place<'tcx>,
8faf50e0
XL
109 error: MoveError<'tcx>,
110 ) {
111 match error {
112 MoveError::UnionMove { .. } => {
113 unimplemented!("don't know how to report union move errors yet.")
114 }
115 MoveError::IllegalMove {
116 cannot_move_out_of: IllegalMoveOrigin { location, kind },
117 } => {
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(
124 Place::Local(local),
0bf4aa26 125 box Rvalue::Use(Operand::Move(move_from)),
8faf50e0
XL
126 )) = self.mir.basic_blocks()[location.block]
127 .statements
128 .get(location.statement_index)
129 .map(|stmt| &stmt.kind)
130 {
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
136 //
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)),
141 binding_mode: _,
142 opt_ty_info: _,
b7449926 143 pat_span: _,
8faf50e0
XL
144 }))) = local_decl.is_user_variable
145 {
146 self.append_binding_error(
147 grouped_errors,
148 kind,
b7449926 149 original_path,
8faf50e0
XL
150 move_from,
151 *local,
152 opt_match_place,
153 match_span,
154 stmt_source_info.span,
155 );
156 return;
157 }
158 }
159 grouped_errors.push(GroupedMoveError::OtherIllegalMove {
160 span: stmt_source_info.span,
b7449926 161 original_path,
8faf50e0
XL
162 kind,
163 });
164 }
165 }
166 }
167
168 fn append_binding_error(
169 &self,
170 grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
171 kind: IllegalMoveOriginKind<'tcx>,
b7449926 172 original_path: Place<'tcx>,
8faf50e0
XL
173 move_from: &Place<'tcx>,
174 bind_to: Local,
175 match_place: &Option<Place<'tcx>>,
176 match_span: Span,
177 statement_span: Span,
178 ) {
179 debug!(
b7449926 180 "append_binding_error(match_place={:?}, match_span={:?})",
8faf50e0
XL
181 match_place, match_span
182 );
183
184 let from_simple_let = match_place.is_none();
185 let match_place = match_place.as_ref().unwrap_or(move_from);
186
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 {
b7449926 191 if let GroupedMoveError::MovesFromPlace { span, binds_to, .. } = ge {
8faf50e0
XL
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);
196 }
197 return;
198 }
199 }
200 }
201 debug!("found a new move error location");
202
203 // Don't need to point to x in let x = ... .
204 let (binds_to, span) = if from_simple_let {
205 (vec![], statement_span)
206 } else {
207 (vec![bind_to], match_span)
208 };
b7449926 209 grouped_errors.push(GroupedMoveError::MovesFromPlace {
8faf50e0
XL
210 span,
211 move_from: match_place.clone(),
b7449926 212 original_path,
8faf50e0
XL
213 kind,
214 binds_to,
215 });
216 }
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..."),
223 };
224 for ge in &mut *grouped_errors {
b7449926 225 if let GroupedMoveError::MovesFromValue {
8faf50e0
XL
226 span,
227 move_from: other_mpi,
228 binds_to,
229 ..
230 } = ge
231 {
232 if match_span == *span && mpi == *other_mpi {
233 debug!("appending local({:?}) to list", bind_to);
234 binds_to.push(bind_to);
235 return;
236 }
237 }
238 }
239 debug!("found a new move error location");
b7449926 240 grouped_errors.push(GroupedMoveError::MovesFromValue {
8faf50e0
XL
241 span: match_span,
242 move_from: mpi,
b7449926 243 original_path,
8faf50e0
XL
244 kind,
245 binds_to: vec![bind_to],
246 });
247 }
248 };
249 }
250
251 fn report(&mut self, error: GroupedMoveError<'tcx>) {
252 let (mut err, err_span) = {
b7449926
XL
253 let (span, original_path, kind): (Span, &Place<'tcx>, &IllegalMoveOriginKind) =
254 match error {
255 GroupedMoveError::MovesFromPlace {
256 span,
257 ref original_path,
258 ref kind,
259 ..
260 } |
261 GroupedMoveError::MovesFromValue { span, ref original_path, ref kind, .. } |
262 GroupedMoveError::OtherIllegalMove { span, ref original_path, ref kind } => {
263 (span, original_path, kind)
264 },
265 };
8faf50e0 266 let origin = Origin::Mir;
b7449926
XL
267 debug!("report: original_path={:?} span={:?}, kind={:?} \
268 original_path.is_upvar_field_projection={:?}", original_path, span, kind,
0bf4aa26 269 original_path.is_upvar_field_projection(self.mir, &self.infcx.tcx));
8faf50e0
XL
270 (
271 match kind {
272 IllegalMoveOriginKind::Static => {
0bf4aa26 273 self.infcx.tcx.cannot_move_out_of(span, "static item", origin)
8faf50e0
XL
274 }
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.
0bf4aa26 279 let ty = place.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
b7449926
XL
280 let is_upvar_field_projection =
281 self.prefixes(&original_path, PrefixSet::All)
0bf4aa26 282 .any(|p| p.is_upvar_field_projection(self.mir, &self.infcx.tcx)
b7449926 283 .is_some());
0bf4aa26 284 debug!("report: ty={:?}", ty);
8faf50e0 285 match ty.sty {
0bf4aa26
XL
286 ty::Array(..) | ty::Slice(..) =>
287 self.infcx.tcx.cannot_move_out_of_interior_noncopy(
288 span, ty, None, origin
289 ),
b7449926
XL
290 ty::Closure(def_id, closure_substs)
291 if !self.mir.upvar_decls.is_empty() && is_upvar_field_projection
292 => {
8faf50e0 293 let closure_kind_ty =
0bf4aa26 294 closure_substs.closure_kind_ty(def_id, self.infcx.tcx);
8faf50e0
XL
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"
299 }
300 Some(ty::ClosureKind::FnMut) => {
301 "captured variable in an `FnMut` closure"
302 }
303 Some(ty::ClosureKind::FnOnce) => {
304 bug!("closure kind does not match first argument type")
305 }
306 None => bug!("closure kind not inferred by borrowck"),
307 };
b7449926
XL
308 debug!("report: closure_kind_ty={:?} closure_kind={:?} \
309 place_description={:?}", closure_kind_ty, closure_kind,
310 place_description);
311
0bf4aa26 312 let mut diag = self.infcx.tcx.cannot_move_out_of(
b7449926
XL
313 span, place_description, origin);
314
315 for prefix in self.prefixes(&original_path, PrefixSet::All) {
316 if let Some(field) = prefix.is_upvar_field_projection(
0bf4aa26 317 self.mir, &self.infcx.tcx) {
b7449926
XL
318 let upvar_decl = &self.mir.upvar_decls[field.index()];
319 let upvar_hir_id =
320 upvar_decl.var_hir_id.assert_crate_local();
321 let upvar_node_id =
0bf4aa26
XL
322 self.infcx.tcx.hir.hir_to_node_id(upvar_hir_id);
323 let upvar_span = self.infcx.tcx.hir.span(upvar_node_id);
b7449926
XL
324 diag.span_label(upvar_span, "captured outer variable");
325 break;
326 }
327 }
328
329 diag
8faf50e0 330 }
0bf4aa26
XL
331 _ => {
332 let source = self.borrowed_content_source(place);
333 self.infcx.tcx.cannot_move_out_of(
334 span, &source.to_string(), origin
335 )
336 },
8faf50e0
XL
337 }
338 }
339 IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
0bf4aa26 340 self.infcx.tcx
8faf50e0
XL
341 .cannot_move_out_of_interior_of_drop(span, ty, origin)
342 }
0bf4aa26
XL
343 IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } =>
344 self.infcx.tcx.cannot_move_out_of_interior_noncopy(
345 span, ty, Some(*is_index), origin
346 ),
8faf50e0
XL
347 },
348 span,
349 )
350 };
351
352 self.add_move_hints(error, &mut err, err_span);
353 err.buffer(&mut self.errors_buffer);
354 }
355
356 fn add_move_hints(
357 &self,
358 error: GroupedMoveError<'tcx>,
359 err: &mut DiagnosticBuilder<'a>,
360 span: Span,
361 ) {
0bf4aa26 362 let snippet = self.infcx.tcx.sess.source_map().span_to_snippet(span).unwrap();
8faf50e0 363 match error {
b7449926 364 GroupedMoveError::MovesFromPlace {
8faf50e0
XL
365 mut binds_to,
366 move_from,
367 ..
368 } => {
b7449926
XL
369 let try_remove_deref = match move_from {
370 Place::Projection(box PlaceProjection {
371 elem: ProjectionElem::Deref,
372 ..
373 }) => true,
374 _ => false,
375 };
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)`.
0bf4aa26 381 err.span_suggestion_with_applicability(
b7449926
XL
382 span,
383 "consider removing the `*`",
384 snippet[1..].to_owned(),
0bf4aa26 385 Applicability::Unspecified,
b7449926
XL
386 );
387 } else {
0bf4aa26 388 err.span_suggestion_with_applicability(
b7449926
XL
389 span,
390 "consider borrowing here",
391 format!("&{}", snippet),
0bf4aa26 392 Applicability::Unspecified,
b7449926 393 );
8faf50e0 394 }
b7449926
XL
395
396 binds_to.sort();
397 binds_to.dedup();
398 self.add_move_error_details(err, &binds_to);
8faf50e0 399 }
b7449926 400 GroupedMoveError::MovesFromValue { mut binds_to, .. } => {
8faf50e0
XL
401 binds_to.sort();
402 binds_to.dedup();
b7449926
XL
403 self.add_move_error_suggestions(err, &binds_to);
404 self.add_move_error_details(err, &binds_to);
405 }
406 // No binding. Nothing to suggest.
407 GroupedMoveError::OtherIllegalMove { .. } => (),
408 }
409 }
8faf50e0 410
b7449926
XL
411 fn add_move_error_suggestions(
412 &self,
413 err: &mut DiagnosticBuilder<'a>,
414 binds_to: &[Local],
415 ) {
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];
419 if let Some(
420 ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
421 pat_span,
422 ..
423 }))
424 ) = bind_to.is_user_variable {
0bf4aa26 425 let pat_snippet = self.infcx.tcx.sess.source_map()
b7449926
XL
426 .span_to_snippet(pat_span)
427 .unwrap();
428 if pat_snippet.starts_with('&') {
429 let pat_snippet = pat_snippet[1..].trim_left();
430 let suggestion;
431 let to_remove;
432 if pat_snippet.starts_with("mut")
433 && pat_snippet["mut".len()..].starts_with(Pattern_White_Space)
434 {
435 suggestion = pat_snippet["mut".len()..].trim_left();
436 to_remove = "&mut";
437 } else {
438 suggestion = pat_snippet;
439 to_remove = "&";
8faf50e0 440 }
b7449926
XL
441 suggestions.push((
442 pat_span,
443 to_remove,
444 suggestion.to_owned(),
445 ));
8faf50e0
XL
446 }
447 }
b7449926
XL
448 }
449 suggestions.sort_unstable_by_key(|&(span, _, _)| span);
450 suggestions.dedup_by_key(|&mut (span, _, _)| span);
451 for (span, to_remove, suggestion) in suggestions {
0bf4aa26 452 err.span_suggestion_with_applicability(
b7449926
XL
453 span,
454 &format!("consider removing the `{}`", to_remove),
0bf4aa26
XL
455 suggestion,
456 Applicability::MachineApplicable,
b7449926 457 );
8faf50e0
XL
458 }
459 }
460
b7449926
XL
461 fn add_move_error_details(
462 &self,
463 err: &mut DiagnosticBuilder<'a>,
464 binds_to: &[Local],
465 ) {
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;
470
471 if j == 0 {
0bf4aa26 472 err.span_label(binding_span, "data moved here");
b7449926 473 } else {
0bf4aa26 474 err.span_label(binding_span, "...and here");
b7449926 475 }
8faf50e0 476
b7449926
XL
477 if binds_to.len() == 1 {
478 err.span_note(
479 binding_span,
480 &format!(
481 "move occurs because `{}` has type `{}`, \
482 which does not implement the `Copy` trait",
483 bind_to.name.unwrap(),
484 bind_to.ty
485 ),
486 );
487 } else {
488 noncopy_var_spans.push(binding_span);
8faf50e0 489 }
b7449926
XL
490 }
491
492 if binds_to.len() > 1 {
493 err.span_note(
494 noncopy_var_spans,
495 "move occurs because these variables have types that \
496 don't implement the `Copy` trait",
497 );
8faf50e0
XL
498 }
499 }
0bf4aa26
XL
500
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,
508 };
509
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() {
514 debug!(
515 "borrowed_content_source: mpi={:?} queue={:?} visited={:?}",
516 mpi, queue, visited
517 );
518
519 // Don't visit the same path twice.
520 if visited.contains(&mpi) {
521 continue;
522 }
523 visited.push(mpi);
524
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,
532 _ => continue,
533 };
534
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);
538 if !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,
547 _ => ty,
548 };
549 debug!("borrowed_content_source: ty={:?}", ty);
550
551 if ty.is_arc() {
552 return BorrowedContentSource::Arc;
553 } else if ty.is_rc() {
554 return BorrowedContentSource::Rc;
555 } else {
556 queue.push(init.path);
557 }
558 }
559 } else if let Some(Terminator {
560 kind: TerminatorKind::Call { args, .. },
561 ..
562 }) = &bbd.terminator {
563 for arg in args {
564 let source = match arg {
565 Operand::Copy(place) | Operand::Move(place) => place,
566 _ => continue,
567 };
568
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,
572 _ => ty,
573 };
574 debug!("borrowed_content_source: ty={:?}", ty);
575
576 if ty.is_arc() {
577 return BorrowedContentSource::Arc;
578 } else if ty.is_rc() {
579 return BorrowedContentSource::Rc;
580 } else {
581 queue.push(init.path);
582 }
583 }
584 }
585 }
586 }
587
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 {
592 base,
593 elem: ProjectionElem::Deref,
594 }) = place {
595 if base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx).is_unsafe_ptr() {
596 return BorrowedContentSource::DerefRawPointer;
597 }
598 }
599
600 BorrowedContentSource::Other
601 }
8faf50e0 602}