|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 | 11 | use core::unicode::property::Pattern_White_Space;
|
| 12 | +use std::fmt::{self, Display}; |
| 13 | + |
12 | 14 | use rustc::mir::*;
|
13 | 15 | use rustc::ty;
|
14 | 16 | use rustc_errors::{DiagnosticBuilder,Applicability};
|
15 | 17 | use syntax_pos::Span;
|
16 | 18 |
|
17 | 19 | use borrow_check::MirBorrowckCtxt;
|
18 | 20 | use borrow_check::prefixes::PrefixSet;
|
19 |
| -use dataflow::move_paths::{IllegalMoveOrigin, IllegalMoveOriginKind}; |
20 |
| -use dataflow::move_paths::{LookupResult, MoveError, MovePathIndex}; |
| 21 | +use dataflow::move_paths::{ |
| 22 | + IllegalMoveOrigin, IllegalMoveOriginKind, InitLocation, |
| 23 | + LookupResult, MoveError, MovePathIndex, |
| 24 | +}; |
21 | 25 | use util::borrowck_errors::{BorrowckErrors, Origin};
|
22 | 26 |
|
23 | 27 | // Often when desugaring a pattern match we may have many individual moves in
|
@@ -61,6 +65,22 @@ enum GroupedMoveError<'tcx> {
|
61 | 65 | },
|
62 | 66 | }
|
63 | 67 |
|
| 68 | +enum BorrowedContentSource { |
| 69 | + Arc, |
| 70 | + Rc, |
| 71 | + Other, |
| 72 | +} |
| 73 | + |
| 74 | +impl Display for BorrowedContentSource { |
| 75 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 76 | + match *self { |
| 77 | + BorrowedContentSource::Arc => write!(f, "an `Arc`"), |
| 78 | + BorrowedContentSource::Rc => write!(f, "an `Rc`"), |
| 79 | + BorrowedContentSource::Other => write!(f, "borrowed content"), |
| 80 | + } |
| 81 | + } |
| 82 | +} |
| 83 | + |
64 | 84 | impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
|
65 | 85 | pub(crate) fn report_move_errors(&mut self, move_errors: Vec<(Place<'tcx>, MoveError<'tcx>)>) {
|
66 | 86 | let grouped_errors = self.group_move_errors(move_errors);
|
@@ -305,9 +325,12 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
|
305 | 325 |
|
306 | 326 | diag
|
307 | 327 | }
|
308 |
| - _ => self.infcx.tcx.cannot_move_out_of( |
309 |
| - span, "borrowed content", origin |
310 |
| - ), |
| 328 | + _ => { |
| 329 | + let source = self.borrowed_content_source(place); |
| 330 | + self.infcx.tcx.cannot_move_out_of( |
| 331 | + span, &format!("{}", source), origin |
| 332 | + ) |
| 333 | + }, |
311 | 334 | }
|
312 | 335 | }
|
313 | 336 | IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
|
@@ -471,4 +494,94 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
|
471 | 494 | );
|
472 | 495 | }
|
473 | 496 | }
|
| 497 | + |
| 498 | + fn borrowed_content_source(&self, place: &Place<'tcx>) -> BorrowedContentSource { |
| 499 | + // Look up the provided place and work out the move path index for it, |
| 500 | + // we'll use this to work back through where this value came from and check whether it |
| 501 | + // was originally part of an `Rc` or `Arc`. |
| 502 | + let initial_mpi = match self.move_data.rev_lookup.find(place) { |
| 503 | + LookupResult::Exact(mpi) | LookupResult::Parent(Some(mpi)) => mpi, |
| 504 | + _ => return BorrowedContentSource::Other, |
| 505 | + }; |
| 506 | + |
| 507 | + let mut queue = vec![initial_mpi]; |
| 508 | + let mut visited = Vec::new(); |
| 509 | + debug!("borrowed_content_source: queue={:?}", queue); |
| 510 | + while let Some(mpi) = queue.pop() { |
| 511 | + debug!( |
| 512 | + "borrowed_content_source: mpi={:?} queue={:?} visited={:?}", |
| 513 | + mpi, queue, visited |
| 514 | + ); |
| 515 | + |
| 516 | + // Don't visit the same path twice. |
| 517 | + if visited.contains(&mpi) { |
| 518 | + continue; |
| 519 | + } |
| 520 | + visited.push(mpi); |
| 521 | + |
| 522 | + for i in &self.move_data.init_path_map[mpi] { |
| 523 | + let init = &self.move_data.inits[*i]; |
| 524 | + debug!("borrowed_content_source: init={:?}", init); |
| 525 | + // We're only interested in statements that initialized a value, not the |
| 526 | + // initializations from arguments. |
| 527 | + let loc = match init.location { |
| 528 | + InitLocation::Statement(stmt) => stmt, |
| 529 | + _ => continue, |
| 530 | + }; |
| 531 | + |
| 532 | + let bbd = &self.mir[loc.block]; |
| 533 | + let is_terminator = bbd.statements.len() == loc.statement_index; |
| 534 | + debug!("borrowed_content_source: loc={:?} is_terminator={:?}", loc, is_terminator); |
| 535 | + if !is_terminator { |
| 536 | + let stmt = &bbd.statements[loc.statement_index]; |
| 537 | + debug!("borrowed_content_source: stmt={:?}", stmt); |
| 538 | + // We're only interested in assignments (in particular, where the |
| 539 | + // assignment came from - was it an `Rc` or `Arc`?). |
| 540 | + if let StatementKind::Assign(_, box Rvalue::Ref(_, _, source)) = &stmt.kind { |
| 541 | + let ty = source.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx); |
| 542 | + let ty = match ty.sty { |
| 543 | + ty::TyKind::Ref(_, ty, _) => ty, |
| 544 | + _ => ty, |
| 545 | + }; |
| 546 | + debug!("borrowed_content_source: ty={:?}", ty); |
| 547 | + |
| 548 | + if ty.is_arc() { |
| 549 | + return BorrowedContentSource::Arc; |
| 550 | + } else if ty.is_rc() { |
| 551 | + return BorrowedContentSource::Rc; |
| 552 | + } else { |
| 553 | + queue.push(init.path); |
| 554 | + } |
| 555 | + } |
| 556 | + } else if let Some(Terminator { |
| 557 | + kind: TerminatorKind::Call { args, .. }, |
| 558 | + .. |
| 559 | + }) = &bbd.terminator { |
| 560 | + for arg in args { |
| 561 | + let source = match arg { |
| 562 | + Operand::Copy(place) | Operand::Move(place) => place, |
| 563 | + _ => continue, |
| 564 | + }; |
| 565 | + |
| 566 | + let ty = source.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx); |
| 567 | + let ty = match ty.sty { |
| 568 | + ty::TyKind::Ref(_, ty, _) => ty, |
| 569 | + _ => ty, |
| 570 | + }; |
| 571 | + debug!("borrowed_content_source: ty={:?}", ty); |
| 572 | + |
| 573 | + if ty.is_arc() { |
| 574 | + return BorrowedContentSource::Arc; |
| 575 | + } else if ty.is_rc() { |
| 576 | + return BorrowedContentSource::Rc; |
| 577 | + } else { |
| 578 | + queue.push(init.path); |
| 579 | + } |
| 580 | + } |
| 581 | + } |
| 582 | + } |
| 583 | + } |
| 584 | + |
| 585 | + BorrowedContentSource::Other |
| 586 | + } |
474 | 587 | }
|
0 commit comments