Skip to content

Commit 84a4a07

Browse files
committed
auto merge of #17434 : P1start/rust/borrowck-messages, r=nikomatsakis
This was originally part of #17215. Closes #15506. Closes #15630. Closes #17263. This also partially implements #15838.
2 parents dd7f00d + 02c6ebd commit 84a4a07

12 files changed

+299
-72
lines changed

src/librustc/middle/borrowck/check_loans.rs

+57-27
Original file line numberDiff line numberDiff line change
@@ -400,50 +400,82 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
400400
for restr_path in loan1.restricted_paths.iter() {
401401
if *restr_path != loan2_base_path { continue; }
402402

403-
let old_pronoun = if new_loan.loan_path == old_loan.loan_path {
403+
// If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
404+
// normally generate a rather confusing message (in this case, for multiple mutable
405+
// borrows):
406+
//
407+
// error: cannot borrow `x.b` as mutable more than once at a time
408+
// note: previous borrow of `x.a` occurs here; the mutable borrow prevents
409+
// subsequent moves, borrows, or modification of `x.a` until the borrow ends
410+
//
411+
// What we want to do instead is get the 'common ancestor' of the two borrow paths and
412+
// use that for most of the message instead, giving is something like this:
413+
//
414+
// error: cannot borrow `x` as mutable more than once at a time
415+
// note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
416+
// borrow prevents subsequent moves, borrows, or modification of `x` until the
417+
// borrow ends
418+
419+
let common = new_loan.loan_path.common(&*old_loan.loan_path);
420+
let (nl, ol, new_loan_msg, old_loan_msg) =
421+
if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() {
422+
let nl = self.bccx.loan_path_to_string(&common.unwrap());
423+
let ol = nl.clone();
424+
let new_loan_msg = format!(" (here through borrowing `{}`)",
425+
self.bccx.loan_path_to_string(
426+
&*new_loan.loan_path));
427+
let old_loan_msg = format!(" (through borrowing `{}`)",
428+
self.bccx.loan_path_to_string(
429+
&*old_loan.loan_path));
430+
(nl, ol, new_loan_msg, old_loan_msg)
431+
} else {
432+
(self.bccx.loan_path_to_string(&*new_loan.loan_path),
433+
self.bccx.loan_path_to_string(&*old_loan.loan_path),
434+
String::new(), String::new())
435+
};
436+
437+
let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
404438
"it".to_string()
405439
} else {
406-
format!("`{}`",
407-
self.bccx.loan_path_to_string(&*old_loan.loan_path))
440+
format!("`{}`", ol)
408441
};
409442

410443
match (new_loan.kind, old_loan.kind) {
411444
(ty::MutBorrow, ty::MutBorrow) => {
412445
self.bccx.span_err(
413446
new_loan.span,
414-
format!("cannot borrow `{}` as mutable \
447+
format!("cannot borrow `{}`{} as mutable \
415448
more than once at a time",
416-
self.bccx.loan_path_to_string(
417-
&*new_loan.loan_path)).as_slice());
449+
nl, new_loan_msg).as_slice())
418450
}
419451

420452
(ty::UniqueImmBorrow, _) => {
421453
self.bccx.span_err(
422454
new_loan.span,
423455
format!("closure requires unique access to `{}` \
424-
but {} is already borrowed",
425-
self.bccx.loan_path_to_string(&*new_loan.loan_path),
426-
old_pronoun).as_slice());
456+
but {} is already borrowed{}",
457+
nl, ol_pronoun, old_loan_msg).as_slice());
427458
}
428459

429460
(_, ty::UniqueImmBorrow) => {
430461
self.bccx.span_err(
431462
new_loan.span,
432-
format!("cannot borrow `{}` as {} because \
463+
format!("cannot borrow `{}`{} as {} because \
433464
previous closure requires unique access",
434-
self.bccx.loan_path_to_string(&*new_loan.loan_path),
435-
new_loan.kind.to_user_str()).as_slice());
465+
nl, new_loan_msg, new_loan.kind.to_user_str()).as_slice());
436466
}
437467

438468
(_, _) => {
439469
self.bccx.span_err(
440470
new_loan.span,
441-
format!("cannot borrow `{}` as {} because \
442-
{} is also borrowed as {}",
443-
self.bccx.loan_path_to_string(&*new_loan.loan_path),
471+
format!("cannot borrow `{}`{} as {} because \
472+
{} is also borrowed as {}{}",
473+
nl,
474+
new_loan_msg,
444475
new_loan.kind.to_user_str(),
445-
old_pronoun,
446-
old_loan.kind.to_user_str()).as_slice());
476+
ol_pronoun,
477+
old_loan.kind.to_user_str(),
478+
old_loan_msg).as_slice());
447479
}
448480
}
449481

@@ -452,8 +484,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
452484
self.bccx.span_note(
453485
span,
454486
format!("borrow occurs due to use of `{}` in closure",
455-
self.bccx.loan_path_to_string(
456-
&*new_loan.loan_path)).as_slice());
487+
nl).as_slice());
457488
}
458489
_ => { }
459490
}
@@ -463,30 +494,29 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
463494
format!("the mutable borrow prevents subsequent \
464495
moves, borrows, or modification of `{0}` \
465496
until the borrow ends",
466-
self.bccx.loan_path_to_string(
467-
&*old_loan.loan_path))
497+
ol)
468498
}
469499

470500
ty::ImmBorrow => {
471501
format!("the immutable borrow prevents subsequent \
472502
moves or mutable borrows of `{0}` \
473503
until the borrow ends",
474-
self.bccx.loan_path_to_string(&*old_loan.loan_path))
504+
ol)
475505
}
476506

477507
ty::UniqueImmBorrow => {
478508
format!("the unique capture prevents subsequent \
479509
moves or borrows of `{0}` \
480510
until the borrow ends",
481-
self.bccx.loan_path_to_string(&*old_loan.loan_path))
511+
ol)
482512
}
483513
};
484514

485515
let borrow_summary = match old_loan.cause {
486516
euv::ClosureCapture(_) => {
487-
format!("previous borrow of `{}` occurs here due to \
517+
format!("previous borrow of `{}` occurs here{} due to \
488518
use in closure",
489-
self.bccx.loan_path_to_string(&*old_loan.loan_path))
519+
ol, old_loan_msg)
490520
}
491521

492522
euv::OverloadedOperator(..) |
@@ -496,8 +526,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
496526
euv::ForLoop(..) |
497527
euv::RefBinding(..) |
498528
euv::MatchDiscriminant(..) => {
499-
format!("previous borrow of `{}` occurs here",
500-
self.bccx.loan_path_to_string(&*old_loan.loan_path))
529+
format!("previous borrow of `{}` occurs here{}",
530+
ol, old_loan_msg)
501531
}
502532
};
503533

src/librustc/middle/borrowck/mod.rs

+102-12
Original file line numberDiff line numberDiff line change
@@ -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

303348
pub 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

Comments
 (0)