From 69bded2493c964cb1d5cce4f708d221e242aff41 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 24 Dec 2018 20:22:25 +0100 Subject: [PATCH 1/3] Add union justifications to conflicting borrows. This commit adds justifications to error messages for conflicting borrows of union fields. Where previously an error message would say ``cannot borrow `u.b` as mutable..``, it now says ``cannot borrow `u` (via `u.b`) as mutable..``. --- .../borrow_check/error_reporting.rs | 58 +++++++++++--- ...tderr => borrowck-union-borrow.nll.stderr} | 79 ++++++++----------- src/test/ui/borrowck/borrowck-union-borrow.rs | 38 +++------ ...st.stderr => borrowck-union-borrow.stderr} | 60 +++++++------- src/test/ui/issues/issue-45157.rs | 5 +- src/test/ui/issues/issue-45157.stderr | 10 +-- ...nion-borrow-move-parent-sibling.nll.stderr | 18 ++--- 7 files changed, 141 insertions(+), 127 deletions(-) rename src/test/ui/borrowck/{borrowck-union-borrow.ast.nll.stderr => borrowck-union-borrow.nll.stderr} (50%) rename src/test/ui/borrowck/{borrowck-union-borrow.ast.stderr => borrowck-union-borrow.stderr} (64%) diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index b072e464a2998..6f12ff994c4b2 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -329,10 +329,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { "closure" }; - let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned()); - let tcx = self.infcx.tcx; - - let first_borrow_desc; + let (desc_place, msg_place, msg_borrow) = if issued_borrow.borrowed_place == *place { + let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned()); + (desc_place, "".to_string(), "".to_string()) + } else { + let (desc_place, msg_place) = self.describe_place_for_conflicting_borrow(place); + let (_, msg_borrow) = self.describe_place_for_conflicting_borrow( + &issued_borrow.borrowed_place + ); + (desc_place, msg_place, msg_borrow) + }; let explanation = self.explain_why_borrow_contains_point(context, issued_borrow, None); let second_borrow_desc = if explanation.is_explained() { @@ -342,6 +348,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { }; // FIXME: supply non-"" `opt_via` when appropriate + let tcx = self.infcx.tcx; + let first_borrow_desc; let mut err = match ( gen_borrow_kind, "immutable", @@ -355,12 +363,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { tcx.cannot_reborrow_already_borrowed( span, &desc_place, - "", + &msg_place, lft, issued_span, "it", rgt, - "", + &msg_borrow, None, Origin::Mir, ) @@ -370,12 +378,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { tcx.cannot_reborrow_already_borrowed( span, &desc_place, - "", + &msg_place, lft, issued_span, "it", rgt, - "", + &msg_borrow, None, Origin::Mir, ) @@ -386,9 +394,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { tcx.cannot_mutably_borrow_multiply( span, &desc_place, - "", + &msg_place, issued_span, - "", + &msg_borrow, None, Origin::Mir, ) @@ -518,6 +526,36 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.buffer(&mut self.errors_buffer); } + /// Returns a description of a place and an associated message for the purposes of conflicting + /// borrow diagnostics. + /// + /// If the borrow is of the field `b` of a union `u`, then the return value will be + /// `("u", " (via \`u.b\`)")`. Otherwise, for some variable `a`, the return value will be + /// `("a", "")`. + pub(super) fn describe_place_for_conflicting_borrow( + &self, + place: &Place<'tcx>, + ) -> (String, String) { + place.base_local() + .filter(|local| { + // Filter out non-unions. + self.mir.local_decls[*local].ty + .ty_adt_def() + .map(|adt| adt.is_union()) + .unwrap_or(false) + }) + .and_then(|local| { + let desc_base = self.describe_place(&Place::Local(local)) + .unwrap_or_else(|| "_".to_owned()); + let desc_original = self.describe_place(place) + .unwrap_or_else(|| "_".to_owned()); + return Some((desc_base, format!(" (via `{}`)", desc_original))); + }) + .unwrap_or_else(|| { + (self.describe_place(place).unwrap_or_else(|| "_".to_owned()), "".to_string()) + }) + } + /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`. /// /// This means that some data referenced by `borrow` needs to live diff --git a/src/test/ui/borrowck/borrowck-union-borrow.ast.nll.stderr b/src/test/ui/borrowck/borrowck-union-borrow.nll.stderr similarity index 50% rename from src/test/ui/borrowck/borrowck-union-borrow.ast.nll.stderr rename to src/test/ui/borrowck/borrowck-union-borrow.nll.stderr index 1a2433c8f6afd..ef5dcef04b074 100644 --- a/src/test/ui/borrowck/borrowck-union-borrow.ast.nll.stderr +++ b/src/test/ui/borrowck/borrowck-union-borrow.nll.stderr @@ -1,132 +1,121 @@ error[E0502]: cannot borrow `u.a` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-union-borrow.rs:27:23 + --> $DIR/borrowck-union-borrow.rs:25:23 | LL | let ra = &u.a; | ---- immutable borrow occurs here -LL | let rma = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable +LL | let rma = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable | ^^^^^^^^ mutable borrow occurs here -LL | //[mir]~^ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable LL | drop(ra); | -- immutable borrow later used here error[E0506]: cannot assign to `u.a` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:33:13 + --> $DIR/borrowck-union-borrow.rs:30:13 | LL | let ra = &u.a; | ---- borrow of `u.a` occurs here -LL | u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed +LL | u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed | ^^^^^^^ assignment to borrowed `u.a` occurs here -LL | //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed LL | drop(ra); | -- borrow later used here -error[E0502]: cannot borrow `u.b` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-union-borrow.rs:50:23 +error[E0502]: cannot borrow `u` (via `u.b`) as mutable because it is also borrowed as immutable (via `u.a`) + --> $DIR/borrowck-union-borrow.rs:46:23 | LL | let ra = &u.a; - | ---- immutable borrow occurs here -LL | let rmb = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) - | ^^^^^^^^ mutable borrow occurs here -LL | //[mir]~^ ERROR cannot borrow `u.b` as mutable because it is also borrowed as immutable + | ---- immutable borrow occurs here (via `u.a`) +LL | let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) + | ^^^^^^^^ mutable borrow occurs here (via `u.b`) LL | drop(ra); | -- immutable borrow later used here error[E0506]: cannot assign to `u.b` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:56:13 + --> $DIR/borrowck-union-borrow.rs:51:13 | LL | let ra = &u.a; | ---- borrow of `u.b` occurs here -LL | u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed +LL | u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed | ^^^^^^^ assignment to borrowed `u.b` occurs here -LL | //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed LL | drop(ra); | -- borrow later used here error[E0502]: cannot borrow `u.a` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-union-borrow.rs:63:22 + --> $DIR/borrowck-union-borrow.rs:57:22 | LL | let rma = &mut u.a; | -------- mutable borrow occurs here -LL | let ra = &u.a; //[ast]~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable +LL | let ra = &u.a; //~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable | ^^^^ immutable borrow occurs here -LL | //[mir]~^ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable LL | drop(rma); | --- mutable borrow later used here error[E0503]: cannot use `u.a` because it was mutably borrowed - --> $DIR/borrowck-union-borrow.rs:69:21 + --> $DIR/borrowck-union-borrow.rs:62:21 | LL | let ra = &mut u.a; | -------- borrow of `u.a` occurs here -LL | let a = u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed +LL | let a = u.a; //~ ERROR cannot use `u.a` because it was mutably borrowed | ^^^ use of borrowed `u.a` -LL | //[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed LL | drop(ra); | -- borrow later used here error[E0499]: cannot borrow `u.a` as mutable more than once at a time - --> $DIR/borrowck-union-borrow.rs:75:24 + --> $DIR/borrowck-union-borrow.rs:67:24 | LL | let rma = &mut u.a; | -------- first mutable borrow occurs here -LL | let rma2 = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable more than once at a time +LL | let rma2 = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable more than once at a time | ^^^^^^^^ second mutable borrow occurs here -LL | //[mir]~^ ERROR cannot borrow `u.a` as mutable more than once at a time LL | drop(rma); | --- first borrow later used here error[E0506]: cannot assign to `u.a` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:81:13 + --> $DIR/borrowck-union-borrow.rs:72:13 | LL | let rma = &mut u.a; | -------- borrow of `u.a` occurs here -LL | u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed +LL | u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed | ^^^^^^^ assignment to borrowed `u.a` occurs here -LL | //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed LL | drop(rma); | --- borrow later used here -error[E0502]: cannot borrow `u.b` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-union-borrow.rs:88:22 +error[E0502]: cannot borrow `u` (via `u.b`) as immutable because it is also borrowed as mutable (via `u.a`) + --> $DIR/borrowck-union-borrow.rs:78:22 | LL | let rma = &mut u.a; - | -------- mutable borrow occurs here -LL | let rb = &u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) - | ^^^^ immutable borrow occurs here -LL | //[mir]~^ ERROR cannot borrow `u.b` as immutable because it is also borrowed as mutable + | -------- mutable borrow occurs here (via `u.a`) +LL | let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) + | ^^^^ immutable borrow occurs here (via `u.b`) LL | drop(rma); | --- mutable borrow later used here error[E0503]: cannot use `u.b` because it was mutably borrowed - --> $DIR/borrowck-union-borrow.rs:94:21 + --> $DIR/borrowck-union-borrow.rs:83:21 | LL | let ra = &mut u.a; | -------- borrow of `u.a` occurs here -LL | let b = u.b; //[ast]~ ERROR cannot use `u.b` because it was mutably borrowed +LL | let b = u.b; //~ ERROR cannot use `u.b` because it was mutably borrowed | ^^^ use of borrowed `u.a` -... +LL | LL | drop(ra); | -- borrow later used here -error[E0499]: cannot borrow `u.b` as mutable more than once at a time - --> $DIR/borrowck-union-borrow.rs:101:24 +error[E0499]: cannot borrow `u` (via `u.b`) as mutable more than once at a time + --> $DIR/borrowck-union-borrow.rs:89:24 | LL | let rma = &mut u.a; - | -------- first mutable borrow occurs here -LL | let rmb2 = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time - | ^^^^^^^^ second mutable borrow occurs here -LL | //[mir]~^ ERROR cannot borrow `u.b` as mutable more than once at a time + | -------- first mutable borrow occurs here (via `u.a`) +LL | let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time + | ^^^^^^^^ second mutable borrow occurs here (via `u.b`) LL | drop(rma); | --- first borrow later used here error[E0506]: cannot assign to `u.b` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:107:13 + --> $DIR/borrowck-union-borrow.rs:94:13 | LL | let rma = &mut u.a; | -------- borrow of `u.b` occurs here -LL | u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed +LL | u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed | ^^^^^^^ assignment to borrowed `u.b` occurs here -LL | //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed LL | drop(rma); | --- borrow later used here diff --git a/src/test/ui/borrowck/borrowck-union-borrow.rs b/src/test/ui/borrowck/borrowck-union-borrow.rs index 62647a2f5e85e..8afc0be8b55c5 100644 --- a/src/test/ui/borrowck/borrowck-union-borrow.rs +++ b/src/test/ui/borrowck/borrowck-union-borrow.rs @@ -1,6 +1,4 @@ // ignore-tidy-linelength -// revisions: ast mir -//[mir]compile-flags: -Z borrowck=mir #[derive(Clone, Copy)] union U { @@ -24,14 +22,12 @@ fn main() { } { let ra = &u.a; - let rma = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable - //[mir]~^ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable + let rma = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable drop(ra); } { let ra = &u.a; - u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed - //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed + u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed drop(ra); } // Imm borrow, other field @@ -47,65 +43,55 @@ fn main() { } { let ra = &u.a; - let rmb = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) - //[mir]~^ ERROR cannot borrow `u.b` as mutable because it is also borrowed as immutable + let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) drop(ra); } { let ra = &u.a; - u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed - //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed + u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed drop(ra); } // Mut borrow, same field { let rma = &mut u.a; - let ra = &u.a; //[ast]~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable - //[mir]~^ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable + let ra = &u.a; //~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable drop(rma); } { let ra = &mut u.a; - let a = u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed - //[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed + let a = u.a; //~ ERROR cannot use `u.a` because it was mutably borrowed drop(ra); } { let rma = &mut u.a; - let rma2 = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable more than once at a time - //[mir]~^ ERROR cannot borrow `u.a` as mutable more than once at a time + let rma2 = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable more than once at a time drop(rma); } { let rma = &mut u.a; - u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed - //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed + u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed drop(rma); } // Mut borrow, other field { let rma = &mut u.a; - let rb = &u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) - //[mir]~^ ERROR cannot borrow `u.b` as immutable because it is also borrowed as mutable + let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) drop(rma); } { let ra = &mut u.a; - let b = u.b; //[ast]~ ERROR cannot use `u.b` because it was mutably borrowed - //[mir]~^ ERROR cannot use `u.b` because it was mutably borrowed + let b = u.b; //~ ERROR cannot use `u.b` because it was mutably borrowed drop(ra); } { let rma = &mut u.a; - let rmb2 = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time - //[mir]~^ ERROR cannot borrow `u.b` as mutable more than once at a time + let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time drop(rma); } { let rma = &mut u.a; - u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed - //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed + u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed drop(rma); } } diff --git a/src/test/ui/borrowck/borrowck-union-borrow.ast.stderr b/src/test/ui/borrowck/borrowck-union-borrow.stderr similarity index 64% rename from src/test/ui/borrowck/borrowck-union-borrow.ast.stderr rename to src/test/ui/borrowck/borrowck-union-borrow.stderr index 0f786b81d3a25..f9010d3bf08c4 100644 --- a/src/test/ui/borrowck/borrowck-union-borrow.ast.stderr +++ b/src/test/ui/borrowck/borrowck-union-borrow.stderr @@ -1,115 +1,115 @@ error[E0502]: cannot borrow `u.a` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-union-borrow.rs:27:28 + --> $DIR/borrowck-union-borrow.rs:25:28 | LL | let ra = &u.a; | --- immutable borrow occurs here -LL | let rma = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable +LL | let rma = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable | ^^^ mutable borrow occurs here -... +LL | drop(ra); LL | } | - immutable borrow ends here error[E0506]: cannot assign to `u.a` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:33:13 + --> $DIR/borrowck-union-borrow.rs:30:13 | LL | let ra = &u.a; | --- borrow of `u.a` occurs here -LL | u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed +LL | u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed | ^^^^^^^ assignment to borrowed `u.a` occurs here error[E0502]: cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) - --> $DIR/borrowck-union-borrow.rs:50:28 + --> $DIR/borrowck-union-borrow.rs:46:28 | LL | let ra = &u.a; | --- immutable borrow occurs here (via `u.a`) -LL | let rmb = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) +LL | let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) | ^^^ mutable borrow occurs here (via `u.b`) -... +LL | drop(ra); LL | } | - immutable borrow ends here error[E0506]: cannot assign to `u.b` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:56:13 + --> $DIR/borrowck-union-borrow.rs:51:13 | LL | let ra = &u.a; | --- borrow of `u.b` occurs here -LL | u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed +LL | u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed | ^^^^^^^ assignment to borrowed `u.b` occurs here error[E0502]: cannot borrow `u.a` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-union-borrow.rs:63:23 + --> $DIR/borrowck-union-borrow.rs:57:23 | LL | let rma = &mut u.a; | --- mutable borrow occurs here -LL | let ra = &u.a; //[ast]~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable +LL | let ra = &u.a; //~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable | ^^^ immutable borrow occurs here -... +LL | drop(rma); LL | } | - mutable borrow ends here error[E0503]: cannot use `u.a` because it was mutably borrowed - --> $DIR/borrowck-union-borrow.rs:69:17 + --> $DIR/borrowck-union-borrow.rs:62:17 | LL | let ra = &mut u.a; | --- borrow of `u.a` occurs here -LL | let a = u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed +LL | let a = u.a; //~ ERROR cannot use `u.a` because it was mutably borrowed | ^ use of borrowed `u.a` error[E0499]: cannot borrow `u.a` as mutable more than once at a time - --> $DIR/borrowck-union-borrow.rs:75:29 + --> $DIR/borrowck-union-borrow.rs:67:29 | LL | let rma = &mut u.a; | --- first mutable borrow occurs here -LL | let rma2 = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable more than once at a time +LL | let rma2 = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable more than once at a time | ^^^ second mutable borrow occurs here -... +LL | drop(rma); LL | } | - first borrow ends here error[E0506]: cannot assign to `u.a` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:81:13 + --> $DIR/borrowck-union-borrow.rs:72:13 | LL | let rma = &mut u.a; | --- borrow of `u.a` occurs here -LL | u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed +LL | u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed | ^^^^^^^ assignment to borrowed `u.a` occurs here error[E0502]: cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) - --> $DIR/borrowck-union-borrow.rs:88:23 + --> $DIR/borrowck-union-borrow.rs:78:23 | LL | let rma = &mut u.a; | --- mutable borrow occurs here (via `u.a`) -LL | let rb = &u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) +LL | let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) | ^^^ immutable borrow occurs here (via `u.b`) -... +LL | drop(rma); LL | } | - mutable borrow ends here error[E0503]: cannot use `u.b` because it was mutably borrowed - --> $DIR/borrowck-union-borrow.rs:94:17 + --> $DIR/borrowck-union-borrow.rs:83:17 | LL | let ra = &mut u.a; | --- borrow of `u.a` occurs here -LL | let b = u.b; //[ast]~ ERROR cannot use `u.b` because it was mutably borrowed +LL | let b = u.b; //~ ERROR cannot use `u.b` because it was mutably borrowed | ^ use of borrowed `u.a` error[E0499]: cannot borrow `u` (via `u.b`) as mutable more than once at a time - --> $DIR/borrowck-union-borrow.rs:101:29 + --> $DIR/borrowck-union-borrow.rs:89:29 | LL | let rma = &mut u.a; | --- first mutable borrow occurs here (via `u.a`) -LL | let rmb2 = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time +LL | let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time | ^^^ second mutable borrow occurs here (via `u.b`) -... +LL | drop(rma); LL | } | - first borrow ends here error[E0506]: cannot assign to `u.b` because it is borrowed - --> $DIR/borrowck-union-borrow.rs:107:13 + --> $DIR/borrowck-union-borrow.rs:94:13 | LL | let rma = &mut u.a; | --- borrow of `u.b` occurs here -LL | u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed +LL | u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed | ^^^^^^^ assignment to borrowed `u.b` occurs here error: aborting due to 12 previous errors diff --git a/src/test/ui/issues/issue-45157.rs b/src/test/ui/issues/issue-45157.rs index c2c9ed62d4edc..22ea254a769e8 100644 --- a/src/test/ui/issues/issue-45157.rs +++ b/src/test/ui/issues/issue-45157.rs @@ -1,6 +1,8 @@ #![allow(unused)] #![feature(nll)] +// ignore-tidy-linelength + #[derive(Clone, Copy, Default)] struct S { a: u8, @@ -25,8 +27,7 @@ fn main() { *mref = 22; let nref = &u.z.c; - //~^ ERROR cannot borrow `u.z.c` as immutable because it is also borrowed as mutable [E0502] + //~^ ERROR cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`) [E0502] println!("{} {}", mref, nref) } } - diff --git a/src/test/ui/issues/issue-45157.stderr b/src/test/ui/issues/issue-45157.stderr index 038d6ecf48d29..eadbd608699be 100644 --- a/src/test/ui/issues/issue-45157.stderr +++ b/src/test/ui/issues/issue-45157.stderr @@ -1,12 +1,12 @@ -error[E0502]: cannot borrow `u.z.c` as immutable because it is also borrowed as mutable - --> $DIR/issue-45157.rs:27:20 +error[E0502]: cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`) + --> $DIR/issue-45157.rs:29:20 | LL | let mref = &mut u.s.a; - | ---------- mutable borrow occurs here + | ---------- mutable borrow occurs here (via `u.s.a`) ... LL | let nref = &u.z.c; - | ^^^^^^ immutable borrow occurs here -LL | //~^ ERROR cannot borrow `u.z.c` as immutable because it is also borrowed as mutable [E0502] + | ^^^^^^ immutable borrow occurs here (via `u.z.c`) +LL | //~^ ERROR cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`) [E0502] LL | println!("{} {}", mref, nref) | ---- mutable borrow later used here diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr index 953160db14e38..4bf3b6286aa72 100644 --- a/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr +++ b/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr @@ -1,10 +1,10 @@ -error[E0502]: cannot borrow `u.y` as immutable because it is also borrowed as mutable +error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0`) --> $DIR/union-borrow-move-parent-sibling.rs:15:13 | LL | let a = &mut u.x.0; - | ---------- mutable borrow occurs here + | ---------- mutable borrow occurs here (via `u.x.0`) LL | let b = &u.y; //~ ERROR cannot borrow `u.y` - | ^^^^ immutable borrow occurs here + | ^^^^ immutable borrow occurs here (via `u.y`) LL | use_borrow(a); | - mutable borrow later used here @@ -18,13 +18,13 @@ LL | let b = u.y; //~ ERROR use of moved value: `u.y` | = note: move occurs because `u` has type `U`, which does not implement the `Copy` trait -error[E0502]: cannot borrow `u.y` as immutable because it is also borrowed as mutable +error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0.0`) --> $DIR/union-borrow-move-parent-sibling.rs:28:13 | LL | let a = &mut (u.x.0).0; - | -------------- mutable borrow occurs here + | -------------- mutable borrow occurs here (via `u.x.0.0`) LL | let b = &u.y; //~ ERROR cannot borrow `u.y` - | ^^^^ immutable borrow occurs here + | ^^^^ immutable borrow occurs here (via `u.y`) LL | use_borrow(a); | - mutable borrow later used here @@ -38,13 +38,13 @@ LL | let b = u.y; //~ ERROR use of moved value: `u.y` | = note: move occurs because `u` has type `U`, which does not implement the `Copy` trait -error[E0502]: cannot borrow `u.x` as immutable because it is also borrowed as mutable +error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `*u.y`) --> $DIR/union-borrow-move-parent-sibling.rs:41:13 | LL | let a = &mut *u.y; - | --------- mutable borrow occurs here + | --------- mutable borrow occurs here (via `*u.y`) LL | let b = &u.x; //~ ERROR cannot borrow `u` (via `u.x`) - | ^^^^ immutable borrow occurs here + | ^^^^ immutable borrow occurs here (via `u.x`) LL | use_borrow(a); | - mutable borrow later used here From 388dffe347f86f2c95ffc1fa2f5fa7898b7f8d66 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 4 Jan 2019 20:56:41 +0100 Subject: [PATCH 2/3] Make conflicting borrow description more robust. This commit improves the logic for place descriptions in conflicting borrow errors so that borrows of union fields have better messages even when the unions are embedded in other unions or structs. --- .../borrow_check/error_reporting.rs | 122 +++++++++++++----- src/test/ui/nll/issue-57100.rs | 69 ++++++++++ src/test/ui/nll/issue-57100.stderr | 27 ++++ 3 files changed, 187 insertions(+), 31 deletions(-) create mode 100644 src/test/ui/nll/issue-57100.rs create mode 100644 src/test/ui/nll/issue-57100.stderr diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index 6f12ff994c4b2..aeba56cb7e260 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -329,16 +329,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { "closure" }; - let (desc_place, msg_place, msg_borrow) = if issued_borrow.borrowed_place == *place { - let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned()); - (desc_place, "".to_string(), "".to_string()) - } else { - let (desc_place, msg_place) = self.describe_place_for_conflicting_borrow(place); - let (_, msg_borrow) = self.describe_place_for_conflicting_borrow( - &issued_borrow.borrowed_place - ); - (desc_place, msg_place, msg_borrow) - }; + let (desc_place, msg_place, msg_borrow) = self.describe_place_for_conflicting_borrow( + place, &issued_borrow.borrowed_place, + ); + let via = |msg: String| if msg.is_empty() { msg } else { format!(" (via `{}`)", msg) }; + let msg_place = via(msg_place); + let msg_borrow = via(msg_borrow); let explanation = self.explain_why_borrow_contains_point(context, issued_borrow, None); let second_borrow_desc = if explanation.is_explained() { @@ -526,33 +522,97 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.buffer(&mut self.errors_buffer); } - /// Returns a description of a place and an associated message for the purposes of conflicting - /// borrow diagnostics. + /// Returns the description of the root place for a conflicting borrow and the full + /// descriptions of the places that caused the conflict. + /// + /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is + /// attempted while a shared borrow is live, then this function will return: + /// + /// ("x", "", "") /// - /// If the borrow is of the field `b` of a union `u`, then the return value will be - /// `("u", " (via \`u.b\`)")`. Otherwise, for some variable `a`, the return value will be - /// `("a", "")`. + /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while + /// a shared borrow of another field `x.y`, then this function will return: + /// + /// ("x", "x.z", "x.y") + /// + /// In the more complex union case, where the union is a field of a struct, then if a mutable + /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of + /// another field `x.u.y`, then this function will return: + /// + /// ("x.u", "x.u.z", "x.u.y") + /// + /// This is used when creating error messages like below: + /// + /// > cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as + /// > mutable (via `a.u.s.b`) [E0502] pub(super) fn describe_place_for_conflicting_borrow( &self, - place: &Place<'tcx>, - ) -> (String, String) { - place.base_local() - .filter(|local| { - // Filter out non-unions. - self.mir.local_decls[*local].ty - .ty_adt_def() - .map(|adt| adt.is_union()) - .unwrap_or(false) + first_borrowed_place: &Place<'tcx>, + second_borrowed_place: &Place<'tcx>, + ) -> (String, String, String) { + // Define a small closure that we can use to check if the type of a place + // is a union. + let is_union = |place: &Place<'tcx>| -> bool { + place.ty(self.mir, self.infcx.tcx) + .to_ty(self.infcx.tcx) + .ty_adt_def() + .map(|adt| adt.is_union()) + .unwrap_or(false) + }; + + // Start with an empty tuple, so we can use the functions on `Option` to reduce some + // code duplication (particularly around returning an empty description in the failure + // case). + Some(()) + .filter(|_| { + // If we have a conflicting borrow of the same place, then we don't want to add + // an extraneous "via x.y" to our diagnostics, so filter out this case. + first_borrowed_place != second_borrowed_place }) - .and_then(|local| { - let desc_base = self.describe_place(&Place::Local(local)) - .unwrap_or_else(|| "_".to_owned()); - let desc_original = self.describe_place(place) - .unwrap_or_else(|| "_".to_owned()); - return Some((desc_base, format!(" (via `{}`)", desc_original))); + .and_then(|_| { + // We're going to want to traverse the first borrowed place to see if we can find + // field access to a union. If we find that, then we will keep the place of the + // union being accessed and the field that was being accessed so we can check the + // second borrowed place for the same union and a access to a different field. + let mut current = first_borrowed_place; + while let Place::Projection(box PlaceProjection { base, elem }) = current { + match elem { + ProjectionElem::Field(field, _) if is_union(base) => { + return Some((base, field)); + }, + _ => current = base, + } + } + None + }) + .and_then(|(target_base, target_field)| { + // With the place of a union and a field access into it, we traverse the second + // borrowed place and look for a access to a different field of the same union. + let mut current = second_borrowed_place; + while let Place::Projection(box PlaceProjection { base, elem }) = current { + match elem { + ProjectionElem::Field(field, _) if { + is_union(base) && field != target_field && base == target_base + } => { + let desc_base = self.describe_place(base) + .unwrap_or_else(|| "_".to_owned()); + let desc_first = self.describe_place(first_borrowed_place) + .unwrap_or_else(|| "_".to_owned()); + let desc_second = self.describe_place(second_borrowed_place) + .unwrap_or_else(|| "_".to_owned()); + return Some((desc_base, desc_first, desc_second)); + }, + _ => current = base, + } + } + None }) .unwrap_or_else(|| { - (self.describe_place(place).unwrap_or_else(|| "_".to_owned()), "".to_string()) + // If we didn't find a field access into a union, or both places match, then + // only return the description of the first place. + let desc_place = self.describe_place(first_borrowed_place) + .unwrap_or_else(|| "_".to_owned()); + (desc_place, "".to_string(), "".to_string()) }) } diff --git a/src/test/ui/nll/issue-57100.rs b/src/test/ui/nll/issue-57100.rs new file mode 100644 index 0000000000000..f669fe00956ef --- /dev/null +++ b/src/test/ui/nll/issue-57100.rs @@ -0,0 +1,69 @@ +#![allow(unused)] +#![feature(nll)] + +// ignore-tidy-linelength + +// This tests the error messages for borrows of union fields when the unions are embedded in other +// structs or unions. + +#[derive(Clone, Copy, Default)] +struct Leaf { + l1_u8: u8, + l2_u8: u8, +} + +#[derive(Clone, Copy)] +union First { + f1_leaf: Leaf, + f2_leaf: Leaf, + f3_union: Second, +} + +#[derive(Clone, Copy)] +union Second { + s1_leaf: Leaf, + s2_leaf: Leaf, +} + +struct Root { + r1_u8: u8, + r2_union: First, +} + +// Borrow a different field of the nested union. +fn nested_union() { + unsafe { + let mut r = Root { + r1_u8: 3, + r2_union: First { f3_union: Second { s2_leaf: Leaf { l1_u8: 8, l2_u8: 4 } } } + }; + + let mref = &mut r.r2_union.f3_union.s1_leaf.l1_u8; + // ^^^^^^^ + *mref = 22; + let nref = &r.r2_union.f3_union.s2_leaf.l1_u8; + // ^^^^^^^ + //~^^ ERROR cannot borrow `r.r2_union.f3_union` (via `r.r2_union.f3_union.s2_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f3_union.s1_leaf.l1_u8`) [E0502] + println!("{} {}", mref, nref) + } +} + +// Borrow a different field of the first union. +fn first_union() { + unsafe { + let mut r = Root { + r1_u8: 3, + r2_union: First { f3_union: Second { s2_leaf: Leaf { l1_u8: 8, l2_u8: 4 } } } + }; + + let mref = &mut r.r2_union.f2_leaf.l1_u8; + // ^^^^^^^ + *mref = 22; + let nref = &r.r2_union.f1_leaf.l1_u8; + // ^^^^^^^ + //~^^ ERROR cannot borrow `r.r2_union` (via `r.r2_union.f1_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f2_leaf.l1_u8`) [E0502] + println!("{} {}", mref, nref) + } +} + +fn main() {} diff --git a/src/test/ui/nll/issue-57100.stderr b/src/test/ui/nll/issue-57100.stderr new file mode 100644 index 0000000000000..34dcdfc49478b --- /dev/null +++ b/src/test/ui/nll/issue-57100.stderr @@ -0,0 +1,27 @@ +error[E0502]: cannot borrow `r.r2_union.f3_union` (via `r.r2_union.f3_union.s2_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f3_union.s1_leaf.l1_u8`) + --> $DIR/issue-57100.rs:44:20 + | +LL | let mref = &mut r.r2_union.f3_union.s1_leaf.l1_u8; + | -------------------------------------- mutable borrow occurs here (via `r.r2_union.f3_union.s1_leaf.l1_u8`) +... +LL | let nref = &r.r2_union.f3_union.s2_leaf.l1_u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here (via `r.r2_union.f3_union.s2_leaf.l1_u8`) +... +LL | println!("{} {}", mref, nref) + | ---- mutable borrow later used here + +error[E0502]: cannot borrow `r.r2_union` (via `r.r2_union.f1_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f2_leaf.l1_u8`) + --> $DIR/issue-57100.rs:62:20 + | +LL | let mref = &mut r.r2_union.f2_leaf.l1_u8; + | ----------------------------- mutable borrow occurs here (via `r.r2_union.f2_leaf.l1_u8`) +... +LL | let nref = &r.r2_union.f1_leaf.l1_u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here (via `r.r2_union.f1_leaf.l1_u8`) +... +LL | println!("{} {}", mref, nref) + | ---- mutable borrow later used here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0502`. From c2b477c19af9c5b8241e3d37a9d8a9cf1616750f Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 4 Jan 2019 22:43:51 +0100 Subject: [PATCH 3/3] Improve diagnostic labels and add note. This commit improves diagnostic labels to mention which field a borrow overlaps with and adds a note explaining that the fields overlap. --- src/librustc_borrowck/borrowck/check_loans.rs | 8 +--- .../borrow_check/error_reporting.rs | 26 +++++++---- src/librustc_mir/util/borrowck_errors.rs | 43 ++++++++++++++----- .../borrowck-borrow-from-owned-ptr.stderr | 2 +- .../borrowck-box-insensitivity.ast.stderr | 4 +- .../ui/borrowck/borrowck-box-insensitivity.rs | 4 +- .../borrowck/borrowck-union-borrow.nll.stderr | 10 ++++- .../ui/borrowck/borrowck-union-borrow.stderr | 4 +- src/test/ui/issues/issue-17263.ast.stderr | 2 +- src/test/ui/issues/issue-45157.stderr | 4 +- src/test/ui/nll/issue-57100.stderr | 8 +++- ...nion-borrow-move-parent-sibling.nll.stderr | 12 ++++-- .../union-borrow-move-parent-sibling.stderr | 2 +- 13 files changed, 86 insertions(+), 43 deletions(-) diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 04085387c37f0..cafb29ed99a41 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -557,12 +557,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { if new_loan.loan_path.has_fork(&old_loan.loan_path) && common.is_some() { let nl = self.bccx.loan_path_to_string(&common.unwrap()); let ol = nl.clone(); - let new_loan_msg = format!(" (via `{}`)", - self.bccx.loan_path_to_string( - &new_loan.loan_path)); - let old_loan_msg = format!(" (via `{}`)", - self.bccx.loan_path_to_string( - &old_loan.loan_path)); + let new_loan_msg = self.bccx.loan_path_to_string(&new_loan.loan_path); + let old_loan_msg = self.bccx.loan_path_to_string(&old_loan.loan_path); (nl, ol, new_loan_msg, old_loan_msg) } else { (self.bccx.loan_path_to_string(&new_loan.loan_path), diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index aeba56cb7e260..f6a80d7fae37c 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -329,12 +329,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { "closure" }; - let (desc_place, msg_place, msg_borrow) = self.describe_place_for_conflicting_borrow( - place, &issued_borrow.borrowed_place, - ); - let via = |msg: String| if msg.is_empty() { msg } else { format!(" (via `{}`)", msg) }; - let msg_place = via(msg_place); - let msg_borrow = via(msg_borrow); + let (desc_place, msg_place, msg_borrow, union_type_name) = + self.describe_place_for_conflicting_borrow(place, &issued_borrow.borrowed_place); let explanation = self.explain_why_borrow_contains_point(context, issued_borrow, None); let second_borrow_desc = if explanation.is_explained() { @@ -516,6 +512,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { ); } + if union_type_name != "" { + err.note(&format!( + "`{}` is a field of the union `{}`, so it overlaps the field `{}`", + msg_place, union_type_name, msg_borrow, + )); + } + explanation .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, first_borrow_desc); @@ -549,7 +552,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { &self, first_borrowed_place: &Place<'tcx>, second_borrowed_place: &Place<'tcx>, - ) -> (String, String, String) { + ) -> (String, String, String, String) { // Define a small closure that we can use to check if the type of a place // is a union. let is_union = |place: &Place<'tcx>| -> bool { @@ -600,7 +603,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { .unwrap_or_else(|| "_".to_owned()); let desc_second = self.describe_place(second_borrowed_place) .unwrap_or_else(|| "_".to_owned()); - return Some((desc_base, desc_first, desc_second)); + + // Also compute the name of the union type, eg. `Foo` so we + // can add a helpful note with it. + let ty = base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx); + + return Some((desc_base, desc_first, desc_second, ty.to_string())); }, _ => current = base, } @@ -612,7 +620,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // only return the description of the first place. let desc_place = self.describe_place(first_borrowed_place) .unwrap_or_else(|| "_".to_owned()); - (desc_place, "".to_string(), "".to_string()) + (desc_place, "".to_string(), "".to_string(), "".to_string()) }) } diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 5e55bb4a9b6cf..7ad73aaa3f9a9 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -138,13 +138,15 @@ pub trait BorrowckErrors<'cx>: Sized + Copy { old_load_end_span: Option, o: Origin, ) -> DiagnosticBuilder<'cx> { + let via = |msg: &str| + if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) }; let mut err = struct_span_err!( self, new_loan_span, E0499, "cannot borrow `{}`{} as mutable more than once at a time{OGN}", desc, - opt_via, + via(opt_via), OGN = o ); if old_loan_span == new_loan_span { @@ -164,11 +166,11 @@ pub trait BorrowckErrors<'cx>: Sized + Copy { } else { err.span_label( old_loan_span, - format!("first mutable borrow occurs here{}", old_opt_via), + format!("first mutable borrow occurs here{}", via(old_opt_via)), ); err.span_label( new_loan_span, - format!("second mutable borrow occurs here{}", opt_via), + format!("second mutable borrow occurs here{}", via(opt_via)), ); if let Some(old_load_end_span) = old_load_end_span { err.span_label(old_load_end_span, "first borrow ends here"); @@ -292,27 +294,46 @@ pub trait BorrowckErrors<'cx>: Sized + Copy { old_load_end_span: Option, o: Origin, ) -> DiagnosticBuilder<'cx> { + let via = |msg: &str| + if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) }; let mut err = struct_span_err!( self, span, E0502, - "cannot borrow `{}`{} as {} because {} is also borrowed as {}{}{OGN}", + "cannot borrow `{}`{} as {} because {} is also borrowed \ + as {}{}{OGN}", desc_new, - msg_new, + via(msg_new), kind_new, noun_old, kind_old, - msg_old, + via(msg_old), OGN = o ); - err.span_label(span, format!("{} borrow occurs here{}", kind_new, msg_new)); - err.span_label( - old_span, - format!("{} borrow occurs here{}", kind_old, msg_old), - ); + + if msg_new == "" { + // If `msg_new` is empty, then this isn't a borrow of a union field. + err.span_label(span, format!("{} borrow occurs here", kind_new)); + err.span_label(old_span, format!("{} borrow occurs here", kind_old)); + } else { + // If `msg_new` isn't empty, then this a borrow of a union field. + err.span_label( + span, + format!( + "{} borrow of `{}` -- which overlaps with `{}` -- occurs here", + kind_new, msg_new, msg_old, + ) + ); + err.span_label( + old_span, + format!("{} borrow occurs here{}", kind_old, via(msg_old)), + ); + } + if let Some(old_load_end_span) = old_load_end_span { err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old)); } + self.cancel_if_wrong_origin(err, o) } diff --git a/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.stderr b/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.stderr index 79dc55bbbaeda..e116cb70c1c31 100644 --- a/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.stderr +++ b/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.stderr @@ -138,7 +138,7 @@ error[E0502]: cannot borrow `foo` (via `foo.bar2`) as immutable because `foo` is LL | let bar1 = &mut foo.bar1; | -------- mutable borrow occurs here (via `foo.bar1`) LL | let _foo1 = &foo.bar2; //~ ERROR cannot borrow - | ^^^^^^^^ immutable borrow occurs here (via `foo.bar2`) + | ^^^^^^^^ immutable borrow of `foo.bar2` -- which overlaps with `foo.bar1` -- occurs here LL | *bar1; LL | } | - mutable borrow ends here diff --git a/src/test/ui/borrowck/borrowck-box-insensitivity.ast.stderr b/src/test/ui/borrowck/borrowck-box-insensitivity.ast.stderr index 95b26a5724a41..236064da3e8b6 100644 --- a/src/test/ui/borrowck/borrowck-box-insensitivity.ast.stderr +++ b/src/test/ui/borrowck/borrowck-box-insensitivity.ast.stderr @@ -61,7 +61,7 @@ error[E0502]: cannot borrow `a` (via `a.y`) as immutable because `a` is also bor LL | let _x = &mut a.x; | --- mutable borrow occurs here (via `a.x`) LL | let _y = &a.y; //[ast]~ ERROR cannot borrow - | ^^^ immutable borrow occurs here (via `a.y`) + | ^^^ immutable borrow of `a.y` -- which overlaps with `a.x` -- occurs here ... LL | } | - mutable borrow ends here @@ -72,7 +72,7 @@ error[E0502]: cannot borrow `a` (via `a.y`) as mutable because `a` is also borro LL | let _x = &a.x; | --- immutable borrow occurs here (via `a.x`) LL | let _y = &mut a.y; //[ast]~ ERROR cannot borrow - | ^^^ mutable borrow occurs here (via `a.y`) + | ^^^ mutable borrow of `a.y` -- which overlaps with `a.x` -- occurs here ... LL | } | - immutable borrow ends here diff --git a/src/test/ui/borrowck/borrowck-box-insensitivity.rs b/src/test/ui/borrowck/borrowck-box-insensitivity.rs index 2af97a9fc1d58..e72048d0ea4bc 100644 --- a/src/test/ui/borrowck/borrowck-box-insensitivity.rs +++ b/src/test/ui/borrowck/borrowck-box-insensitivity.rs @@ -83,14 +83,14 @@ fn borrow_after_mut_borrow() { let mut a: Box<_> = box A { x: box 0, y: 1 }; let _x = &mut a.x; let _y = &a.y; //[ast]~ ERROR cannot borrow - //[ast]~^ immutable borrow occurs here (via `a.y`) + //[ast]~^ immutable borrow of `a.y` -- which overlaps with `a.x` -- occurs here use_mut(_x); } fn mut_borrow_after_borrow() { let mut a: Box<_> = box A { x: box 0, y: 1 }; let _x = &a.x; let _y = &mut a.y; //[ast]~ ERROR cannot borrow - //[ast]~^ mutable borrow occurs here (via `a.y`) + //[ast]~^ mutable borrow of `a.y` -- which overlaps with `a.x` -- occurs here use_imm(_x); } fn copy_after_move_nested() { diff --git a/src/test/ui/borrowck/borrowck-union-borrow.nll.stderr b/src/test/ui/borrowck/borrowck-union-borrow.nll.stderr index ef5dcef04b074..5cba30b43b8a0 100644 --- a/src/test/ui/borrowck/borrowck-union-borrow.nll.stderr +++ b/src/test/ui/borrowck/borrowck-union-borrow.nll.stderr @@ -24,9 +24,11 @@ error[E0502]: cannot borrow `u` (via `u.b`) as mutable because it is also borrow LL | let ra = &u.a; | ---- immutable borrow occurs here (via `u.a`) LL | let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) - | ^^^^^^^^ mutable borrow occurs here (via `u.b`) + | ^^^^^^^^ mutable borrow of `u.b` -- which overlaps with `u.a` -- occurs here LL | drop(ra); | -- immutable borrow later used here + | + = note: `u.b` is a field of the union `U`, so it overlaps the field `u.a` error[E0506]: cannot assign to `u.b` because it is borrowed --> $DIR/borrowck-union-borrow.rs:51:13 @@ -84,9 +86,11 @@ error[E0502]: cannot borrow `u` (via `u.b`) as immutable because it is also borr LL | let rma = &mut u.a; | -------- mutable borrow occurs here (via `u.a`) LL | let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) - | ^^^^ immutable borrow occurs here (via `u.b`) + | ^^^^ immutable borrow of `u.b` -- which overlaps with `u.a` -- occurs here LL | drop(rma); | --- mutable borrow later used here + | + = note: `u.b` is a field of the union `U`, so it overlaps the field `u.a` error[E0503]: cannot use `u.b` because it was mutably borrowed --> $DIR/borrowck-union-borrow.rs:83:21 @@ -108,6 +112,8 @@ LL | let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as | ^^^^^^^^ second mutable borrow occurs here (via `u.b`) LL | drop(rma); | --- first borrow later used here + | + = note: `u.b` is a field of the union `U`, so it overlaps the field `u.a` error[E0506]: cannot assign to `u.b` because it is borrowed --> $DIR/borrowck-union-borrow.rs:94:13 diff --git a/src/test/ui/borrowck/borrowck-union-borrow.stderr b/src/test/ui/borrowck/borrowck-union-borrow.stderr index f9010d3bf08c4..ef6a331eda04c 100644 --- a/src/test/ui/borrowck/borrowck-union-borrow.stderr +++ b/src/test/ui/borrowck/borrowck-union-borrow.stderr @@ -23,7 +23,7 @@ error[E0502]: cannot borrow `u` (via `u.b`) as mutable because `u` is also borro LL | let ra = &u.a; | --- immutable borrow occurs here (via `u.a`) LL | let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`) - | ^^^ mutable borrow occurs here (via `u.b`) + | ^^^ mutable borrow of `u.b` -- which overlaps with `u.a` -- occurs here LL | drop(ra); LL | } | - immutable borrow ends here @@ -80,7 +80,7 @@ error[E0502]: cannot borrow `u` (via `u.b`) as immutable because `u` is also bor LL | let rma = &mut u.a; | --- mutable borrow occurs here (via `u.a`) LL | let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`) - | ^^^ immutable borrow occurs here (via `u.b`) + | ^^^ immutable borrow of `u.b` -- which overlaps with `u.a` -- occurs here LL | drop(rma); LL | } | - mutable borrow ends here diff --git a/src/test/ui/issues/issue-17263.ast.stderr b/src/test/ui/issues/issue-17263.ast.stderr index 3d42dcb52f5db..823f2c747d686 100644 --- a/src/test/ui/issues/issue-17263.ast.stderr +++ b/src/test/ui/issues/issue-17263.ast.stderr @@ -13,7 +13,7 @@ error[E0502]: cannot borrow `foo` (via `foo.b`) as immutable because `foo` is al --> $DIR/issue-17263.rs:21:32 | LL | let (c, d) = (&mut foo.a, &foo.b); - | ----- ^^^^^ immutable borrow occurs here (via `foo.b`) + | ----- ^^^^^ immutable borrow of `foo.b` -- which overlaps with `foo.a` -- occurs here | | | mutable borrow occurs here (via `foo.a`) ... diff --git a/src/test/ui/issues/issue-45157.stderr b/src/test/ui/issues/issue-45157.stderr index eadbd608699be..3b15a8dbd9ef8 100644 --- a/src/test/ui/issues/issue-45157.stderr +++ b/src/test/ui/issues/issue-45157.stderr @@ -5,10 +5,12 @@ LL | let mref = &mut u.s.a; | ---------- mutable borrow occurs here (via `u.s.a`) ... LL | let nref = &u.z.c; - | ^^^^^^ immutable borrow occurs here (via `u.z.c`) + | ^^^^^^ immutable borrow of `u.z.c` -- which overlaps with `u.s.a` -- occurs here LL | //~^ ERROR cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`) [E0502] LL | println!("{} {}", mref, nref) | ---- mutable borrow later used here + | + = note: `u.z.c` is a field of the union `U`, so it overlaps the field `u.s.a` error: aborting due to previous error diff --git a/src/test/ui/nll/issue-57100.stderr b/src/test/ui/nll/issue-57100.stderr index 34dcdfc49478b..5d5c86c34875c 100644 --- a/src/test/ui/nll/issue-57100.stderr +++ b/src/test/ui/nll/issue-57100.stderr @@ -5,10 +5,12 @@ LL | let mref = &mut r.r2_union.f3_union.s1_leaf.l1_u8; | -------------------------------------- mutable borrow occurs here (via `r.r2_union.f3_union.s1_leaf.l1_u8`) ... LL | let nref = &r.r2_union.f3_union.s2_leaf.l1_u8; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here (via `r.r2_union.f3_union.s2_leaf.l1_u8`) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow of `r.r2_union.f3_union.s2_leaf.l1_u8` -- which overlaps with `r.r2_union.f3_union.s1_leaf.l1_u8` -- occurs here ... LL | println!("{} {}", mref, nref) | ---- mutable borrow later used here + | + = note: `r.r2_union.f3_union.s2_leaf.l1_u8` is a field of the union `Second`, so it overlaps the field `r.r2_union.f3_union.s1_leaf.l1_u8` error[E0502]: cannot borrow `r.r2_union` (via `r.r2_union.f1_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f2_leaf.l1_u8`) --> $DIR/issue-57100.rs:62:20 @@ -17,10 +19,12 @@ LL | let mref = &mut r.r2_union.f2_leaf.l1_u8; | ----------------------------- mutable borrow occurs here (via `r.r2_union.f2_leaf.l1_u8`) ... LL | let nref = &r.r2_union.f1_leaf.l1_u8; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here (via `r.r2_union.f1_leaf.l1_u8`) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow of `r.r2_union.f1_leaf.l1_u8` -- which overlaps with `r.r2_union.f2_leaf.l1_u8` -- occurs here ... LL | println!("{} {}", mref, nref) | ---- mutable borrow later used here + | + = note: `r.r2_union.f1_leaf.l1_u8` is a field of the union `First`, so it overlaps the field `r.r2_union.f2_leaf.l1_u8` error: aborting due to 2 previous errors diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr index 4bf3b6286aa72..848c3d9bdb017 100644 --- a/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr +++ b/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr @@ -4,9 +4,11 @@ error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borr LL | let a = &mut u.x.0; | ---------- mutable borrow occurs here (via `u.x.0`) LL | let b = &u.y; //~ ERROR cannot borrow `u.y` - | ^^^^ immutable borrow occurs here (via `u.y`) + | ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0` -- occurs here LL | use_borrow(a); | - mutable borrow later used here + | + = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0` error[E0382]: use of moved value: `u` --> $DIR/union-borrow-move-parent-sibling.rs:22:13 @@ -24,9 +26,11 @@ error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borr LL | let a = &mut (u.x.0).0; | -------------- mutable borrow occurs here (via `u.x.0.0`) LL | let b = &u.y; //~ ERROR cannot borrow `u.y` - | ^^^^ immutable borrow occurs here (via `u.y`) + | ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0.0` -- occurs here LL | use_borrow(a); | - mutable borrow later used here + | + = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0.0` error[E0382]: use of moved value: `u` --> $DIR/union-borrow-move-parent-sibling.rs:35:13 @@ -44,9 +48,11 @@ error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borr LL | let a = &mut *u.y; | --------- mutable borrow occurs here (via `*u.y`) LL | let b = &u.x; //~ ERROR cannot borrow `u` (via `u.x`) - | ^^^^ immutable borrow occurs here (via `u.x`) + | ^^^^ immutable borrow of `u.x` -- which overlaps with `*u.y` -- occurs here LL | use_borrow(a); | - mutable borrow later used here + | + = note: `u.x` is a field of the union `U`, so it overlaps the field `*u.y` error[E0382]: use of moved value: `u` --> $DIR/union-borrow-move-parent-sibling.rs:48:13 diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.stderr index c5baf82c74584..9058707e50516 100644 --- a/src/test/ui/union/union-borrow-move-parent-sibling.stderr +++ b/src/test/ui/union/union-borrow-move-parent-sibling.stderr @@ -46,7 +46,7 @@ error[E0502]: cannot borrow `u` (via `u.x`) as immutable because `u` is also bor LL | let a = &mut *u.y; | ---- mutable borrow occurs here (via `*u.y`) LL | let b = &u.x; //~ ERROR cannot borrow `u` (via `u.x`) - | ^^^ immutable borrow occurs here (via `u.x`) + | ^^^ immutable borrow of `u.x` -- which overlaps with `*u.y` -- occurs here LL | use_borrow(a); LL | } | - mutable borrow ends here