@@ -298,6 +298,51 @@ impl LoanPath {
298298 LpExtend ( ref base, _, _) => base. kill_scope ( tcx) ,
299299 }
300300 }
301+
302+ fn has_fork ( & self , other : & LoanPath ) -> bool {
303+ match ( self , other) {
304+ ( & LpExtend ( ref base, _, LpInterior ( id) ) , & LpExtend ( ref base2, _, LpInterior ( id2) ) ) =>
305+ if id == id2 {
306+ base. has_fork ( & * * base2)
307+ } else {
308+ true
309+ } ,
310+ ( & LpExtend ( ref base, _, LpDeref ( _) ) , _) => base. has_fork ( other) ,
311+ ( _, & LpExtend ( ref base, _, LpDeref ( _) ) ) => self . has_fork ( & * * base) ,
312+ _ => false ,
313+ }
314+ }
315+
316+ fn depth ( & self ) -> uint {
317+ match * self {
318+ LpExtend ( ref base, _, LpDeref ( _) ) => base. depth ( ) ,
319+ LpExtend ( ref base, _, LpInterior ( _) ) => base. depth ( ) + 1 ,
320+ _ => 0 ,
321+ }
322+ }
323+
324+ fn common ( & self , other : & LoanPath ) -> Option < LoanPath > {
325+ match ( self , other) {
326+ ( & LpExtend ( ref base, a, LpInterior ( id) ) , & LpExtend ( ref base2, _, LpInterior ( id2) ) ) =>
327+ if id == id2 {
328+ base. common ( & * * base2) . map ( |x| {
329+ let xd = x. depth ( ) ;
330+ if base. depth ( ) == xd && base2. depth ( ) == xd {
331+ LpExtend ( Rc :: new ( x) , a, LpInterior ( id) )
332+ } else {
333+ x
334+ }
335+ } )
336+ } else {
337+ base. common ( & * * base2)
338+ } ,
339+ ( & LpExtend ( ref base, _, LpDeref ( _) ) , _) => base. common ( other) ,
340+ ( _, & LpExtend ( ref other, _, LpDeref ( _) ) ) => self . common ( & * * other) ,
341+ ( & LpVar ( id) , & LpVar ( id2) ) => if id == id2 { Some ( LpVar ( id) ) } else { None } ,
342+ ( & LpUpvar ( id) , & LpUpvar ( id2) ) => if id == id2 { Some ( LpUpvar ( id) ) } else { None } ,
343+ _ => None ,
344+ }
345+ }
301346}
302347
303348pub fn opt_loan_path ( cmt : & mc:: cmt ) -> Option < Rc < LoanPath > > {
@@ -416,24 +461,58 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
416461 MovedInCapture => "capture" ,
417462 } ;
418463
419- match the_move. kind {
464+ let ( ol , moved_lp_msg ) = match the_move. kind {
420465 move_data:: Declared => {
421466 self . tcx . sess . span_err (
422467 use_span,
423468 format ! ( "{} of possibly uninitialized variable: `{}`" ,
424469 verb,
425470 self . loan_path_to_string( lp) ) . as_slice ( ) ) ;
471+ ( self . loan_path_to_string ( moved_lp) ,
472+ String :: new ( ) )
426473 }
427474 _ => {
428- let partially = if lp == moved_lp { "" } else { "partially " } ;
475+ // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
476+ // normally generate a rather confusing message:
477+ //
478+ // error: use of moved value: `x.b`
479+ // note: `x.a` moved here...
480+ //
481+ // What we want to do instead is get the 'common ancestor' of the two moves and
482+ // use that for most of the message instead, giving is something like this:
483+ //
484+ // error: use of moved value: `x`
485+ // note: `x` moved here (through moving `x.a`)...
486+
487+ let common = moved_lp. common ( lp) ;
488+ let has_common = common. is_some ( ) ;
489+ let has_fork = moved_lp. has_fork ( lp) ;
490+ let ( nl, ol, moved_lp_msg) =
491+ if has_fork && has_common {
492+ let nl = self . loan_path_to_string ( & common. unwrap ( ) ) ;
493+ let ol = nl. clone ( ) ;
494+ let moved_lp_msg = format ! ( " (through moving `{}`)" ,
495+ self . loan_path_to_string( moved_lp) ) ;
496+ ( nl, ol, moved_lp_msg)
497+ } else {
498+ ( self . loan_path_to_string ( lp) ,
499+ self . loan_path_to_string ( moved_lp) ,
500+ String :: new ( ) )
501+ } ;
502+
503+ let partial = moved_lp. depth ( ) > lp. depth ( ) ;
504+ let msg = if !has_fork && partial { "partially " }
505+ else if has_fork && !has_common { "collaterally " }
506+ else { "" } ;
429507 self . tcx . sess . span_err (
430508 use_span,
431509 format ! ( "{} of {}moved value: `{}`" ,
432510 verb,
433- partially,
434- self . loan_path_to_string( lp) ) . as_slice ( ) ) ;
511+ msg,
512+ nl) . as_slice ( ) ) ;
513+ ( ol, moved_lp_msg)
435514 }
436- }
515+ } ;
437516
438517 match the_move. kind {
439518 move_data:: Declared => { }
@@ -456,19 +535,21 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
456535 "moved by default (use `copy` to override)" ) ;
457536 self . tcx . sess . span_note (
458537 expr_span,
459- format ! ( "`{}` moved here because it has type `{}`, which is {}" ,
460- self . loan_path_to_string( moved_lp) ,
538+ format ! ( "`{}` moved here{} because it has type `{}`, which is {}" ,
539+ ol,
540+ moved_lp_msg,
461541 expr_ty. user_string( self . tcx) ,
462542 suggestion) . as_slice ( ) ) ;
463543 }
464544
465545 move_data:: MovePat => {
466546 let pat_ty = ty:: node_id_to_type ( self . tcx , the_move. id ) ;
467547 self . tcx . sess . span_note ( self . tcx . map . span ( the_move. id ) ,
468- format ! ( "`{}` moved here because it has type `{}`, \
548+ format ! ( "`{}` moved here{} because it has type `{}`, \
469549 which is moved by default (use `ref` to \
470550 override)",
471- self . loan_path_to_string( moved_lp) ,
551+ ol,
552+ moved_lp_msg,
472553 pat_ty. user_string( self . tcx) ) . as_slice ( ) ) ;
473554 }
474555
@@ -491,9 +572,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
491572 capture that instead to override)") ;
492573 self . tcx . sess . span_note (
493574 expr_span,
494- format ! ( "`{}` moved into closure environment here because it \
575+ format ! ( "`{}` moved into closure environment here{} because it \
495576 has type `{}`, which is {}",
496- self . loan_path_to_string( moved_lp) ,
577+ ol,
578+ moved_lp_msg,
497579 expr_ty. user_string( self . tcx) ,
498580 suggestion) . as_slice ( ) ) ;
499581 }
@@ -602,6 +684,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
602684 span : Span ,
603685 kind : AliasableViolationKind ,
604686 cause : mc:: AliasableReason ) {
687+ let mut is_closure = false ;
605688 let prefix = match kind {
606689 MutabilityViolation => {
607690 "cannot assign to data"
@@ -625,6 +708,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
625708 }
626709
627710 BorrowViolation ( euv:: ClosureInvocation ) => {
711+ is_closure = true ;
628712 "closure invocation"
629713 }
630714
@@ -649,14 +733,20 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
649733 mc:: AliasableManaged => {
650734 self . tcx . sess . span_err (
651735 span,
652- format ! ( "{} in a `@ ` pointer" , prefix) . as_slice ( ) ) ;
736+ format ! ( "{} in a `Gc ` pointer" , prefix) . as_slice ( ) ) ;
653737 }
654738 mc:: AliasableBorrowed => {
655739 self . tcx . sess . span_err (
656740 span,
657741 format ! ( "{} in a `&` reference" , prefix) . as_slice ( ) ) ;
658742 }
659743 }
744+
745+ if is_closure {
746+ self . tcx . sess . span_note (
747+ span,
748+ "closures behind references must be called via `&mut`" ) ;
749+ }
660750 }
661751
662752 pub fn note_and_explain_bckerr ( & self , err : BckError ) {
0 commit comments