@@ -327,10 +327,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
327
327
"closure"
328
328
} ;
329
329
330
- let desc_place = self . describe_place ( place) . unwrap_or_else ( || "_" . to_owned ( ) ) ;
331
- let tcx = self . infcx . tcx ;
332
-
333
- let first_borrow_desc;
330
+ let ( desc_place, msg_place, msg_borrow, union_type_name) =
331
+ self . describe_place_for_conflicting_borrow ( place, & issued_borrow. borrowed_place ) ;
334
332
335
333
let explanation = self . explain_why_borrow_contains_point ( context, issued_borrow, None ) ;
336
334
let second_borrow_desc = if explanation. is_explained ( ) {
@@ -340,6 +338,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
340
338
} ;
341
339
342
340
// FIXME: supply non-"" `opt_via` when appropriate
341
+ let tcx = self . infcx . tcx ;
342
+ let first_borrow_desc;
343
343
let mut err = match (
344
344
gen_borrow_kind,
345
345
"immutable" ,
@@ -353,12 +353,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
353
353
tcx. cannot_reborrow_already_borrowed (
354
354
span,
355
355
& desc_place,
356
- "" ,
356
+ & msg_place ,
357
357
lft,
358
358
issued_span,
359
359
"it" ,
360
360
rgt,
361
- "" ,
361
+ & msg_borrow ,
362
362
None ,
363
363
Origin :: Mir ,
364
364
)
@@ -368,12 +368,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
368
368
tcx. cannot_reborrow_already_borrowed (
369
369
span,
370
370
& desc_place,
371
- "" ,
371
+ & msg_place ,
372
372
lft,
373
373
issued_span,
374
374
"it" ,
375
375
rgt,
376
- "" ,
376
+ & msg_borrow ,
377
377
None ,
378
378
Origin :: Mir ,
379
379
)
@@ -384,9 +384,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
384
384
tcx. cannot_mutably_borrow_multiply (
385
385
span,
386
386
& desc_place,
387
- "" ,
387
+ & msg_place ,
388
388
issued_span,
389
- "" ,
389
+ & msg_borrow ,
390
390
None ,
391
391
Origin :: Mir ,
392
392
)
@@ -510,12 +510,118 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
510
510
) ;
511
511
}
512
512
513
+ if union_type_name != "" {
514
+ err. note ( & format ! (
515
+ "`{}` is a field of the union `{}`, so it overlaps the field `{}`" ,
516
+ msg_place, union_type_name, msg_borrow,
517
+ ) ) ;
518
+ }
519
+
513
520
explanation
514
521
. add_explanation_to_diagnostic ( self . infcx . tcx , self . mir , & mut err, first_borrow_desc) ;
515
522
516
523
err. buffer ( & mut self . errors_buffer ) ;
517
524
}
518
525
526
+ /// Returns the description of the root place for a conflicting borrow and the full
527
+ /// descriptions of the places that caused the conflict.
528
+ ///
529
+ /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
530
+ /// attempted while a shared borrow is live, then this function will return:
531
+ ///
532
+ /// ("x", "", "")
533
+ ///
534
+ /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
535
+ /// a shared borrow of another field `x.y`, then this function will return:
536
+ ///
537
+ /// ("x", "x.z", "x.y")
538
+ ///
539
+ /// In the more complex union case, where the union is a field of a struct, then if a mutable
540
+ /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
541
+ /// another field `x.u.y`, then this function will return:
542
+ ///
543
+ /// ("x.u", "x.u.z", "x.u.y")
544
+ ///
545
+ /// This is used when creating error messages like below:
546
+ ///
547
+ /// > cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
548
+ /// > mutable (via `a.u.s.b`) [E0502]
549
+ pub ( super ) fn describe_place_for_conflicting_borrow (
550
+ & self ,
551
+ first_borrowed_place : & Place < ' tcx > ,
552
+ second_borrowed_place : & Place < ' tcx > ,
553
+ ) -> ( String , String , String , String ) {
554
+ // Define a small closure that we can use to check if the type of a place
555
+ // is a union.
556
+ let is_union = |place : & Place < ' tcx > | -> bool {
557
+ place. ty ( self . mir , self . infcx . tcx )
558
+ . to_ty ( self . infcx . tcx )
559
+ . ty_adt_def ( )
560
+ . map ( |adt| adt. is_union ( ) )
561
+ . unwrap_or ( false )
562
+ } ;
563
+
564
+ // Start with an empty tuple, so we can use the functions on `Option` to reduce some
565
+ // code duplication (particularly around returning an empty description in the failure
566
+ // case).
567
+ Some ( ( ) )
568
+ . filter ( |_| {
569
+ // If we have a conflicting borrow of the same place, then we don't want to add
570
+ // an extraneous "via x.y" to our diagnostics, so filter out this case.
571
+ first_borrowed_place != second_borrowed_place
572
+ } )
573
+ . and_then ( |_| {
574
+ // We're going to want to traverse the first borrowed place to see if we can find
575
+ // field access to a union. If we find that, then we will keep the place of the
576
+ // union being accessed and the field that was being accessed so we can check the
577
+ // second borrowed place for the same union and a access to a different field.
578
+ let mut current = first_borrowed_place;
579
+ while let Place :: Projection ( box PlaceProjection { base, elem } ) = current {
580
+ match elem {
581
+ ProjectionElem :: Field ( field, _) if is_union ( base) => {
582
+ return Some ( ( base, field) ) ;
583
+ } ,
584
+ _ => current = base,
585
+ }
586
+ }
587
+ None
588
+ } )
589
+ . and_then ( |( target_base, target_field) | {
590
+ // With the place of a union and a field access into it, we traverse the second
591
+ // borrowed place and look for a access to a different field of the same union.
592
+ let mut current = second_borrowed_place;
593
+ while let Place :: Projection ( box PlaceProjection { base, elem } ) = current {
594
+ match elem {
595
+ ProjectionElem :: Field ( field, _) if {
596
+ is_union ( base) && field != target_field && base == target_base
597
+ } => {
598
+ let desc_base = self . describe_place ( base)
599
+ . unwrap_or_else ( || "_" . to_owned ( ) ) ;
600
+ let desc_first = self . describe_place ( first_borrowed_place)
601
+ . unwrap_or_else ( || "_" . to_owned ( ) ) ;
602
+ let desc_second = self . describe_place ( second_borrowed_place)
603
+ . unwrap_or_else ( || "_" . to_owned ( ) ) ;
604
+
605
+ // Also compute the name of the union type, eg. `Foo` so we
606
+ // can add a helpful note with it.
607
+ let ty = base. ty ( self . mir , self . infcx . tcx ) . to_ty ( self . infcx . tcx ) ;
608
+
609
+ return Some ( ( desc_base, desc_first, desc_second, ty. to_string ( ) ) ) ;
610
+ } ,
611
+ _ => current = base,
612
+ }
613
+ }
614
+ None
615
+ } )
616
+ . unwrap_or_else ( || {
617
+ // If we didn't find a field access into a union, or both places match, then
618
+ // only return the description of the first place.
619
+ let desc_place = self . describe_place ( first_borrowed_place)
620
+ . unwrap_or_else ( || "_" . to_owned ( ) ) ;
621
+ ( desc_place, "" . to_string ( ) , "" . to_string ( ) , "" . to_string ( ) )
622
+ } )
623
+ }
624
+
519
625
/// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
520
626
///
521
627
/// This means that some data referenced by `borrow` needs to live
0 commit comments