Skip to content

Commit

Permalink
If the moved value is a mut reference, it is used in a generic functi…
Browse files Browse the repository at this point in the history
…on and it's type is a generic param, it can be reborrowed to avoid moving.

for example:

```rust
struct Y(u32);
// x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`.
fn generic<T>(x: T) {}
```

fixes #127285
  • Loading branch information
surechen committed Jul 11, 2024
1 parent d819876 commit 2b60569
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 23 deletions.
88 changes: 65 additions & 23 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {

if !seen_spans.contains(&move_span) {
if !closure {
self.suggest_ref_or_clone(mpi, &mut err, &mut in_pattern, move_spans);
self.suggest_ref_or_clone(
mpi,
&mut err,
&mut in_pattern,
move_spans,
moved_place.as_ref(),
);
}

let msg_opt = CapturedMessageOpt {
Expand Down Expand Up @@ -258,17 +264,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
err.span_suggestion_verbose(
span.shrink_to_lo(),
format!(
"consider creating a fresh reborrow of {} here",
self.describe_place(moved_place)
.map(|n| format!("`{n}`"))
.unwrap_or_else(|| "the mutable reference".to_string()),
),
"&mut *",
Applicability::MachineApplicable,
);
self.suggest_reborrow(&mut err, span, moved_place);
}
}

Expand Down Expand Up @@ -345,6 +341,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
err: &mut Diag<'infcx>,
in_pattern: &mut bool,
move_spans: UseSpans<'tcx>,
moved_place: PlaceRef<'tcx>,
) {
let move_span = match move_spans {
UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span,
Expand Down Expand Up @@ -434,19 +431,44 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
let parent = self.infcx.tcx.parent_hir_node(expr.hir_id);
let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
&& let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
&& let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id)
{
(def_id.as_local(), args, 1)
(typeck.type_dependent_def_id(parent_expr.hir_id), args, 1)
} else if let hir::Node::Expr(parent_expr) = parent
&& let hir::ExprKind::Call(call, args) = parent_expr.kind
&& let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
{
(def_id.as_local(), args, 0)
(Some(*def_id), args, 0)
} else {
(None, &[][..], 0)
};

// If the moved value is a mut reference, it is used in a
// generic function and it's type is a generic param, it can be
// reborrowed to avoid moving.
// for example:
// struct Y(u32);
// x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`.
let is_sugg_reborrow = || {
if let Some(def_id) = def_id
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
&& let ty::Param(_) =
self.infcx.tcx.fn_sig(def_id).skip_binder().skip_binder().inputs()
[pos + offset]
.kind()
{
let place = &self.move_data.move_paths[mpi].place;
let ty = place.ty(self.body, self.infcx.tcx).ty;
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
return true;
}
}
false
};
let suggest_reborrow = is_sugg_reborrow();

if let Some(def_id) = def_id
&& let node = self.infcx.tcx.hir_node_by_def_id(def_id)
&& let Some(local_def_id) = def_id.as_local()
&& let node = self.infcx.tcx.hir_node_by_def_id(local_def_id)
&& let Some(fn_sig) = node.fn_sig()
&& let Some(ident) = node.ident()
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
Expand All @@ -463,16 +485,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
Some(hir::intravisit::FnKind::Closure) => "closure",
};
span.push_span_label(ident.span, format!("in this {descr}"));
err.span_note(
span,
format!(
"consider changing this parameter type in {descr} `{ident}` to borrow \
instead if owning the value isn't necessary",
),
);
if !suggest_reborrow {
err.span_note(
span,
format!(
"consider changing this parameter type in {descr} `{ident}` to borrow \
instead if owning the value isn't necessary",
),
);
}
}
let place = &self.move_data.move_paths[mpi].place;
let ty = place.ty(self.body, self.infcx.tcx).ty;
if suggest_reborrow {
self.suggest_reborrow(err, expr.span, moved_place);
return;
}
if let hir::Node::Expr(parent_expr) = parent
&& let hir::ExprKind::Call(call_expr, _) = parent_expr.kind
&& let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) =
Expand Down Expand Up @@ -510,6 +538,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
}
}

fn suggest_reborrow(&self, err: &mut Diag<'infcx>, span: Span, moved_place: PlaceRef<'tcx>) {
err.span_suggestion_verbose(
span.shrink_to_lo(),
format!(
"consider creating a fresh reborrow of {} here",
self.describe_place(moved_place)
.map(|n| format!("`{n}`"))
.unwrap_or_else(|| "the mutable reference".to_string()),
),
"&mut *",
Applicability::MachineApplicable,
);
}

fn report_use_of_uninitialized(
&self,
mpi: MovePathIndex,
Expand Down
17 changes: 17 additions & 0 deletions tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//@ run-rustfix

#![allow(dead_code)]

struct X(u32);

impl X {
fn f(&mut self) {
generic(&mut *self);
self.0 += 1;
//~^ ERROR: use of moved value: `self` [E0382]
}
}

fn generic<T>(_x: T) {}

fn main() {}
17 changes: 17 additions & 0 deletions tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//@ run-rustfix

#![allow(dead_code)]

struct X(u32);

impl X {
fn f(&mut self) {
generic(self);
self.0 += 1;
//~^ ERROR: use of moved value: `self` [E0382]
}
}

fn generic<T>(_x: T) {}

fn main() {}
18 changes: 18 additions & 0 deletions tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0382]: use of moved value: `self`
--> $DIR/moved-value-suggest-reborrow-issue-127285.rs:10:9
|
LL | fn f(&mut self) {
| --------- move occurs because `self` has type `&mut X`, which does not implement the `Copy` trait
LL | generic(self);
| ---- value moved here
LL | self.0 += 1;
| ^^^^^^^^^^^ value used here after move
|
help: consider creating a fresh reborrow of `self` here
|
LL | generic(&mut *self);
| ++++++

error: aborting due to 1 previous error

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

0 comments on commit 2b60569

Please sign in to comment.