Skip to content

Commit 1f78e60

Browse files
committed
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: ```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
1 parent d819876 commit 1f78e60

File tree

4 files changed

+117
-23
lines changed

4 files changed

+117
-23
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+65-23
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
206206

207207
if !seen_spans.contains(&move_span) {
208208
if !closure {
209-
self.suggest_ref_or_clone(mpi, &mut err, &mut in_pattern, move_spans);
209+
self.suggest_ref_or_clone(
210+
mpi,
211+
&mut err,
212+
&mut in_pattern,
213+
move_spans,
214+
moved_place.as_ref(),
215+
);
210216
}
211217

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

@@ -345,6 +341,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
345341
err: &mut Diag<'infcx>,
346342
in_pattern: &mut bool,
347343
move_spans: UseSpans<'tcx>,
344+
moved_place: PlaceRef<'tcx>,
348345
) {
349346
let move_span = match move_spans {
350347
UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span,
@@ -434,19 +431,48 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
434431
let parent = self.infcx.tcx.parent_hir_node(expr.hir_id);
435432
let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
436433
&& let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
437-
&& let Some(def_id) = typeck.type_dependent_def_id(parent_expr.hir_id)
438434
{
439-
(def_id.as_local(), args, 1)
435+
(typeck.type_dependent_def_id(parent_expr.hir_id), args, 1)
440436
} else if let hir::Node::Expr(parent_expr) = parent
441437
&& let hir::ExprKind::Call(call, args) = parent_expr.kind
442438
&& let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()
443439
{
444-
(def_id.as_local(), args, 0)
440+
(Some(*def_id), args, 0)
445441
} else {
446442
(None, &[][..], 0)
447443
};
444+
445+
// If the moved value is a mut reference, it is used in a
446+
// generic function and it's type is a generic param, it can be
447+
// reborrowed to avoid moving.
448+
// for example:
449+
// struct Y(u32);
450+
// x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`.
451+
let is_sugg_reborrow = || {
452+
if let Some(def_id) = def_id
453+
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
454+
&& let ty::Param(_) =
455+
self.infcx.tcx.fn_sig(def_id).skip_binder().skip_binder().inputs()
456+
[pos + offset]
457+
.kind()
458+
{
459+
let place = &self.move_data.move_paths[mpi].place;
460+
let ty = place.ty(self.body, self.infcx.tcx).ty;
461+
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
462+
return true;
463+
}
464+
}
465+
false
466+
};
467+
let suggest_reborrow = is_sugg_reborrow();
468+
if suggest_reborrow {
469+
self.suggest_reborrow(err, expr.span, moved_place);
470+
return;
471+
}
472+
448473
if let Some(def_id) = def_id
449-
&& let node = self.infcx.tcx.hir_node_by_def_id(def_id)
474+
&& let Some(local_def_id) = def_id.as_local()
475+
&& let node = self.infcx.tcx.hir_node_by_def_id(local_def_id)
450476
&& let Some(fn_sig) = node.fn_sig()
451477
&& let Some(ident) = node.ident()
452478
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
@@ -463,13 +489,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
463489
Some(hir::intravisit::FnKind::Closure) => "closure",
464490
};
465491
span.push_span_label(ident.span, format!("in this {descr}"));
466-
err.span_note(
467-
span,
468-
format!(
469-
"consider changing this parameter type in {descr} `{ident}` to borrow \
470-
instead if owning the value isn't necessary",
471-
),
472-
);
492+
if !suggest_reborrow {
493+
err.span_note(
494+
span,
495+
format!(
496+
"consider changing this parameter type in {descr} `{ident}` to borrow \
497+
instead if owning the value isn't necessary",
498+
),
499+
);
500+
}
473501
}
474502
let place = &self.move_data.move_paths[mpi].place;
475503
let ty = place.ty(self.body, self.infcx.tcx).ty;
@@ -510,6 +538,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
510538
}
511539
}
512540

541+
fn suggest_reborrow(&self, err: &mut Diag<'infcx>, span: Span, moved_place: PlaceRef<'tcx>) {
542+
err.span_suggestion_verbose(
543+
span.shrink_to_lo(),
544+
format!(
545+
"consider creating a fresh reborrow of {} here",
546+
self.describe_place(moved_place)
547+
.map(|n| format!("`{n}`"))
548+
.unwrap_or_else(|| "the mutable reference".to_string()),
549+
),
550+
"&mut *",
551+
Applicability::MachineApplicable,
552+
);
553+
}
554+
513555
fn report_use_of_uninitialized(
514556
&self,
515557
mpi: MovePathIndex,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ run-rustfix
2+
3+
#![allow(dead_code)]
4+
5+
struct X(u32);
6+
7+
impl X {
8+
fn f(&mut self) {
9+
generic(&mut *self);
10+
self.0 += 1;
11+
//~^ ERROR: use of moved value: `self` [E0382]
12+
}
13+
}
14+
15+
fn generic<T>(_x: T) {}
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ run-rustfix
2+
3+
#![allow(dead_code)]
4+
5+
struct X(u32);
6+
7+
impl X {
8+
fn f(&mut self) {
9+
generic(self);
10+
self.0 += 1;
11+
//~^ ERROR: use of moved value: `self` [E0382]
12+
}
13+
}
14+
15+
fn generic<T>(_x: T) {}
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0382]: use of moved value: `self`
2+
--> $DIR/moved-value-suggest-reborrow-issue-127285.rs:10:9
3+
|
4+
LL | fn f(&mut self) {
5+
| --------- move occurs because `self` has type `&mut X`, which does not implement the `Copy` trait
6+
LL | generic(self);
7+
| ---- value moved here
8+
LL | self.0 += 1;
9+
| ^^^^^^^^^^^ value used here after move
10+
|
11+
help: consider creating a fresh reborrow of `self` here
12+
|
13+
LL | generic(&mut *self);
14+
| ++++++
15+
16+
error: aborting due to 1 previous error
17+
18+
For more information about this error, try `rustc --explain E0382`.

0 commit comments

Comments
 (0)