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
This commit is contained in:
parent
7d356ebde3
commit
4821b84b92
7 changed files with 128 additions and 41 deletions
|
@ -205,9 +205,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||||
is_loop_move = true;
|
is_loop_move = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut has_suggest_reborrow = false;
|
||||||
if !seen_spans.contains(&move_span) {
|
if !seen_spans.contains(&move_span) {
|
||||||
if !closure {
|
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(),
|
||||||
|
&mut has_suggest_reborrow,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg_opt = CapturedMessageOpt {
|
let msg_opt = CapturedMessageOpt {
|
||||||
|
@ -215,6 +223,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||||
is_loop_message,
|
is_loop_message,
|
||||||
is_move_msg,
|
is_move_msg,
|
||||||
is_loop_move,
|
is_loop_move,
|
||||||
|
has_suggest_reborrow,
|
||||||
maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations
|
maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations
|
||||||
.is_empty(),
|
.is_empty(),
|
||||||
};
|
};
|
||||||
|
@ -259,17 +268,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||||
if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
|
if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
|
||||||
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
|
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
|
||||||
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
|
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
|
||||||
err.span_suggestion_verbose(
|
self.suggest_reborrow(&mut err, span, moved_place);
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,6 +345,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||||
err: &mut Diag<'infcx>,
|
err: &mut Diag<'infcx>,
|
||||||
in_pattern: &mut bool,
|
in_pattern: &mut bool,
|
||||||
move_spans: UseSpans<'tcx>,
|
move_spans: UseSpans<'tcx>,
|
||||||
|
moved_place: PlaceRef<'tcx>,
|
||||||
|
has_suggest_reborrow: &mut bool,
|
||||||
) {
|
) {
|
||||||
let move_span = match move_spans {
|
let move_span = match move_spans {
|
||||||
UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span,
|
UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span,
|
||||||
|
@ -435,20 +436,44 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||||
let parent = self.infcx.tcx.parent_hir_node(expr.hir_id);
|
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 (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent
|
||||||
&& let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
|
&& 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
|
} else if let hir::Node::Expr(parent_expr) = parent
|
||||||
&& let hir::ExprKind::Call(call, args) = parent_expr.kind
|
&& let hir::ExprKind::Call(call, args) = parent_expr.kind
|
||||||
&& let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).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 {
|
} else {
|
||||||
(None, &[][..], 0)
|
(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) {}`.
|
||||||
|
if let Some(def_id) = def_id
|
||||||
|
&& self.infcx.tcx.def_kind(def_id).is_fn_like()
|
||||||
|
&& 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() {
|
||||||
|
*has_suggest_reborrow = true;
|
||||||
|
self.suggest_reborrow(err, expr.span, moved_place);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut can_suggest_clone = true;
|
let mut can_suggest_clone = true;
|
||||||
if let Some(def_id) = def_id
|
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(fn_sig) = node.fn_sig()
|
||||||
&& let Some(ident) = node.ident()
|
&& let Some(ident) = node.ident()
|
||||||
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
|
&& let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
|
||||||
|
@ -622,6 +647,25 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub 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(
|
fn report_use_of_uninitialized(
|
||||||
&self,
|
&self,
|
||||||
mpi: MovePathIndex,
|
mpi: MovePathIndex,
|
||||||
|
|
|
@ -768,10 +768,11 @@ struct CapturedMessageOpt {
|
||||||
is_loop_message: bool,
|
is_loop_message: bool,
|
||||||
is_move_msg: bool,
|
is_move_msg: bool,
|
||||||
is_loop_move: bool,
|
is_loop_move: bool,
|
||||||
|
has_suggest_reborrow: bool,
|
||||||
maybe_reinitialized_locations_is_empty: bool,
|
maybe_reinitialized_locations_is_empty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||||
/// Finds the spans associated to a move or copy of move_place at location.
|
/// Finds the spans associated to a move or copy of move_place at location.
|
||||||
pub(super) fn move_spans(
|
pub(super) fn move_spans(
|
||||||
&self,
|
&self,
|
||||||
|
@ -997,7 +998,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
||||||
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
|
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
|
||||||
fn explain_captures(
|
fn explain_captures(
|
||||||
&mut self,
|
&mut self,
|
||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'infcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
move_span: Span,
|
move_span: Span,
|
||||||
move_spans: UseSpans<'tcx>,
|
move_spans: UseSpans<'tcx>,
|
||||||
|
@ -1009,6 +1010,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
||||||
is_loop_message,
|
is_loop_message,
|
||||||
is_move_msg,
|
is_move_msg,
|
||||||
is_loop_move,
|
is_loop_move,
|
||||||
|
has_suggest_reborrow,
|
||||||
maybe_reinitialized_locations_is_empty,
|
maybe_reinitialized_locations_is_empty,
|
||||||
} = msg_opt;
|
} = msg_opt;
|
||||||
if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
|
if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
|
||||||
|
@ -1182,18 +1184,15 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
|
||||||
if let ty::Ref(_, _, hir::Mutability::Mut) =
|
if let ty::Ref(_, _, hir::Mutability::Mut) =
|
||||||
moved_place.ty(self.body, self.infcx.tcx).ty.kind()
|
moved_place.ty(self.body, self.infcx.tcx).ty.kind()
|
||||||
{
|
{
|
||||||
// If we are in a loop this will be suggested later.
|
// Suggest `reborrow` in other place for following situations:
|
||||||
if !is_loop_move {
|
// 1. If we are in a loop this will be suggested later.
|
||||||
err.span_suggestion_verbose(
|
// 2. If the moved value is a mut reference, it is used in a
|
||||||
|
// generic function and the corresponding arg's type is generic param.
|
||||||
|
if !is_loop_move && !has_suggest_reborrow {
|
||||||
|
self.suggest_reborrow(
|
||||||
|
err,
|
||||||
move_span.shrink_to_lo(),
|
move_span.shrink_to_lo(),
|
||||||
format!(
|
moved_place.as_ref(),
|
||||||
"consider creating a fresh reborrow of {} here",
|
|
||||||
self.describe_place(moved_place.as_ref())
|
|
||||||
.map(|n| format!("`{n}`"))
|
|
||||||
.unwrap_or_else(|| "the mutable reference".to_string()),
|
|
||||||
),
|
|
||||||
"&mut *",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -554,6 +554,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
|
||||||
is_loop_message: false,
|
is_loop_message: false,
|
||||||
is_move_msg: false,
|
is_move_msg: false,
|
||||||
is_loop_move: false,
|
is_loop_move: false,
|
||||||
|
has_suggest_reborrow: false,
|
||||||
maybe_reinitialized_locations_is_empty: true,
|
maybe_reinitialized_locations_is_empty: true,
|
||||||
};
|
};
|
||||||
if let Some(use_spans) = use_spans {
|
if let Some(use_spans) = use_spans {
|
||||||
|
|
|
@ -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() {}
|
|
@ -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() {}
|
|
@ -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`.
|
|
@ -8,19 +8,10 @@ LL | for _ in 0..3 {
|
||||||
LL | Other::handle(value);
|
LL | Other::handle(value);
|
||||||
| ^^^^^ value moved here, in previous iteration of loop
|
| ^^^^^ value moved here, in previous iteration of loop
|
||||||
|
|
|
|
||||||
note: consider changing this parameter type in function `handle` to borrow instead if owning the value isn't necessary
|
help: consider creating a fresh reborrow of `value` here
|
||||||
--> $DIR/mut-borrow-in-loop-2.rs:8:22
|
|
||||||
|
|
|
||||||
LL | fn handle(value: T) -> Self;
|
|
||||||
| ------ ^ this parameter takes ownership of the value
|
|
||||||
| |
|
|
||||||
| in this function
|
|
||||||
help: consider moving the expression out of the loop so it is only moved once
|
|
||||||
|
|
|
||||||
LL ~ let mut value = Other::handle(value);
|
|
||||||
LL ~ for _ in 0..3 {
|
|
||||||
LL ~ value;
|
|
||||||
|
|
|
|
||||||
|
LL | Other::handle(&mut *value);
|
||||||
|
| ++++++
|
||||||
help: consider creating a fresh reborrow of `value` here
|
help: consider creating a fresh reborrow of `value` here
|
||||||
|
|
|
|
||||||
LL | Other::handle(&mut *value);
|
LL | Other::handle(&mut *value);
|
||||||
|
|
Loading…
Add table
Reference in a new issue