@@ -327,10 +327,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
327327 "closure"
328328 } ;
329329
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 ) ;
334332
335333 let explanation = self . explain_why_borrow_contains_point ( context, issued_borrow, None ) ;
336334 let second_borrow_desc = if explanation. is_explained ( ) {
@@ -340,6 +338,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
340338 } ;
341339
342340 // FIXME: supply non-"" `opt_via` when appropriate
341+ let tcx = self . infcx . tcx ;
342+ let first_borrow_desc;
343343 let mut err = match (
344344 gen_borrow_kind,
345345 "immutable" ,
@@ -353,12 +353,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
353353 tcx. cannot_reborrow_already_borrowed (
354354 span,
355355 & desc_place,
356- "" ,
356+ & msg_place ,
357357 lft,
358358 issued_span,
359359 "it" ,
360360 rgt,
361- "" ,
361+ & msg_borrow ,
362362 None ,
363363 Origin :: Mir ,
364364 )
@@ -368,12 +368,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
368368 tcx. cannot_reborrow_already_borrowed (
369369 span,
370370 & desc_place,
371- "" ,
371+ & msg_place ,
372372 lft,
373373 issued_span,
374374 "it" ,
375375 rgt,
376- "" ,
376+ & msg_borrow ,
377377 None ,
378378 Origin :: Mir ,
379379 )
@@ -384,9 +384,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
384384 tcx. cannot_mutably_borrow_multiply (
385385 span,
386386 & desc_place,
387- "" ,
387+ & msg_place ,
388388 issued_span,
389- "" ,
389+ & msg_borrow ,
390390 None ,
391391 Origin :: Mir ,
392392 )
@@ -510,12 +510,118 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
510510 ) ;
511511 }
512512
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+
513520 explanation
514521 . add_explanation_to_diagnostic ( self . infcx . tcx , self . mir , & mut err, first_borrow_desc) ;
515522
516523 err. buffer ( & mut self . errors_buffer ) ;
517524 }
518525
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+
519625 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
520626 ///
521627 /// This means that some data referenced by `borrow` needs to live
0 commit comments