diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c26bbb926eaa1..a6484fee72e08 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -205,7 +205,13 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, '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 { @@ -257,17 +263,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, '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); } } @@ -344,6 +340,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { err: &mut Diag<'tcx>, 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, @@ -444,6 +441,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { } else { (None, &[][..], 0) }; + let mut reborrow = false; if let Some(def_id) = def_id && let node = self.infcx.tcx.hir_node_by_def_id(def_id) && let Some(fn_sig) = node.fn_sig() @@ -451,6 +449,29 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) && let Some(arg) = fn_sig.decl.inputs.get(pos + offset) { + // 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(x: T) {}`. + let is_sugg_reborrow = || { + if let Some((def_id, _)) = arg.as_generic_param() + && let Some(generics) = node.generics() + && let Some(def_id) = def_id.as_local() + { + if generics.params.iter().any(|param| param.def_id == def_id) { + 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 + }; + reborrow = is_sugg_reborrow(); + let mut span: MultiSpan = arg.span.into(); span.push_span_label( arg.span, @@ -472,6 +493,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { } let place = &self.move_data.move_paths[mpi].place; let ty = place.ty(self.body, self.infcx.tcx).ty; + if 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, _)) = @@ -509,6 +534,20 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { } } + fn suggest_reborrow(&self, err: &mut Diag<'tcx>, 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, diff --git a/tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.fixed b/tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.fixed new file mode 100644 index 0000000000000..76a49c7c4dbe5 --- /dev/null +++ b/tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.fixed @@ -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(_x: T) {} + +fn main() {} \ No newline at end of file diff --git a/tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.rs b/tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.rs new file mode 100644 index 0000000000000..dd015697fdcdb --- /dev/null +++ b/tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.rs @@ -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(_x: T) {} + +fn main() {} diff --git a/tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.stderr b/tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.stderr new file mode 100644 index 0000000000000..08a296016ad7f --- /dev/null +++ b/tests/ui/borrowck/moved-value-suggest-reborrow-issue-127285.stderr @@ -0,0 +1,25 @@ +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 + | +note: consider changing this parameter type in function `generic` to borrow instead if owning the value isn't necessary + --> $DIR/moved-value-suggest-reborrow-issue-127285.rs:15:19 + | +LL | fn generic(_x: T) {} + | ------- ^ this parameter takes ownership of the value + | | + | in this function +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`.