Skip to content

Commit

Permalink
Detect borrow error involving sub-slices and suggest split_at_mut
Browse files Browse the repository at this point in the history
```
error[E0499]: cannot borrow `foo` as mutable more than once at a time
  --> $DIR/suggest-split-at-mut.rs:13:18
   |
LL |     let a = &mut foo[..2];
   |                  --- first mutable borrow occurs here
LL |     let b = &mut foo[2..];
   |                  ^^^ second mutable borrow occurs here
LL |     a[0] = 5;
   |     ---- first borrow later used here
   |
   = help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
```

Address most of rust-lang#58792.

For follow up work, we should emit a structured suggestion for cases where we can identify the exact `let (a, b) = foo.split_at_mut(2);` call that is needed.
  • Loading branch information
estebank committed Apr 24, 2024
1 parent c2f2db7 commit 7af17f7
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 18 deletions.
41 changes: 25 additions & 16 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&mut err,
place,
issued_borrow.borrowed_place,
span,
issued_span,
);
self.suggest_using_closure_argument_instead_of_capture(
&mut err,
Expand Down Expand Up @@ -1974,10 +1976,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err: &mut Diag<'_>,
place: Place<'tcx>,
borrowed_place: Place<'tcx>,
span: Span,
issued_span: Span,
) {
let tcx = self.infcx.tcx;
let hir = tcx.hir();

if let ([ProjectionElem::Index(index1)], [ProjectionElem::Index(index2)])
| (
[ProjectionElem::Deref, ProjectionElem::Index(index1)],
Expand All @@ -1986,28 +1989,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
{
let mut note_default_suggestion = || {
err.help(
"consider using `.split_at_mut(position)` or similar method to obtain \
two mutable non-overlapping sub-slices",
"consider using `.split_at_mut(position)` or similar method to obtain two \
mutable non-overlapping sub-slices",
)
.help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
};

let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else {
note_default_suggestion();
return;
.help(
"consider using `.swap(index_1, index_2)` to swap elements at the specified \
indices",
);
};

let mut expr_finder =
FindExprBySpan::new(self.body.local_decls[*index1].source_info.span);
expr_finder.visit_expr(hir.body(body_id).value);
let Some(index1) = expr_finder.result else {
let Some(index1) = self.find_expr(self.body.local_decls[*index1].source_info.span)
else {
note_default_suggestion();
return;
};

expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span);
expr_finder.visit_expr(hir.body(body_id).value);
let Some(index2) = expr_finder.result else {
let Some(index2) = self.find_expr(self.body.local_decls[*index2].source_info.span)
else {
note_default_suggestion();
return;
};
Expand Down Expand Up @@ -2065,7 +2063,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
format!("{obj_str}.swap({index1_str}, {index2_str})"),
Applicability::MachineApplicable,
);
return;
}
let Some(index1) = self.find_expr(span) else { return };
let hir::Node::Expr(parent) = tcx.parent_hir_node(index1.hir_id) else { return };
let hir::ExprKind::Index(..) = parent.kind else { return };
let Some(index2) = self.find_expr(issued_span) else { return };
let hir::Node::Expr(parent) = tcx.parent_hir_node(index2.hir_id) else { return };
let hir::ExprKind::Index(..) = parent.kind else { return };
err.help(
"use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping \
sub-slices",
);
}

/// Suggest using `while let` for call `next` on an iterator in a for loop.
Expand Down
4 changes: 4 additions & 0 deletions tests/ui/borrowck/borrowck-overloaded-index-autoderef.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ LL | let q = &mut f[&s];
| ^ second mutable borrow occurs here
LL | p.use_mut();
| - first borrow later used here
|
= help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices

error[E0499]: cannot borrow `f.foo` as mutable more than once at a time
--> $DIR/borrowck-overloaded-index-autoderef.rs:53:18
Expand All @@ -27,6 +29,8 @@ LL | let q = &mut f.foo[&s];
| ^^^^^ second mutable borrow occurs here
LL | p.use_mut();
| - first borrow later used here
|
= help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices

error[E0502]: cannot borrow `f.foo` as mutable because it is also borrowed as immutable
--> $DIR/borrowck-overloaded-index-autoderef.rs:65:18
Expand Down
16 changes: 15 additions & 1 deletion tests/ui/suggestions/suggest-split-at-mut.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
fn main() {
fn foo() {
let mut foo = [1, 2, 3, 4];
let a = &mut foo[2];
let b = &mut foo[3]; //~ ERROR cannot borrow `foo[_]` as mutable more than once at a time
*a = 5;
*b = 6;
println!("{:?} {:?}", a, b);
}

fn bar() {
let mut foo = [1,2,3,4];
let a = &mut foo[..2];
let b = &mut foo[2..]; //~ ERROR cannot borrow `foo` as mutable more than once at a time
a[0] = 5;
b[0] = 6;
println!("{:?} {:?}", a, b);
}

fn main() {
foo();
bar();
}
14 changes: 13 additions & 1 deletion tests/ui/suggestions/suggest-split-at-mut.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ LL | *a = 5;
= help: consider using `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices
= help: consider using `.swap(index_1, index_2)` to swap elements at the specified indices

error: aborting due to 1 previous error
error[E0499]: cannot borrow `foo` as mutable more than once at a time
--> $DIR/suggest-split-at-mut.rs:13:18
|
LL | let a = &mut foo[..2];
| --- first mutable borrow occurs here
LL | let b = &mut foo[2..];
| ^^^ second mutable borrow occurs here
LL | a[0] = 5;
| ---- first borrow later used here
|
= help: use `.split_at_mut(position)` or similar method to obtain two mutable non-overlapping sub-slices

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0499`.

0 comments on commit 7af17f7

Please sign in to comment.