Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solve a error .clone() suggestion when moving a mutable reference #127579

Merged
merged 1 commit into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 60 additions & 16 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,25 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
is_loop_move = true;
}

let mut has_suggest_reborrow = false;
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(),
&mut has_suggest_reborrow,
);
}

let msg_opt = CapturedMessageOpt {
is_partial_move,
is_loop_message,
is_move_msg,
is_loop_move,
has_suggest_reborrow,
maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations
.is_empty(),
};
Expand Down Expand Up @@ -259,17 +268,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 @@ -346,6 +345,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
err: &mut Diag<'infcx>,
in_pattern: &mut bool,
move_spans: UseSpans<'tcx>,
moved_place: PlaceRef<'tcx>,
has_suggest_reborrow: &mut bool,
) {
let move_span = match move_spans {
UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span,
Expand Down Expand Up @@ -435,20 +436,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) {}`.
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;
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 Down Expand Up @@ -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(
&self,
mpi: MovePathIndex,
Expand Down
25 changes: 12 additions & 13 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,10 +768,11 @@ struct CapturedMessageOpt {
is_loop_message: bool,
is_move_msg: bool,
is_loop_move: bool,
has_suggest_reborrow: 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.
pub(super) fn move_spans(
&self,
Expand Down Expand Up @@ -997,7 +998,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn explain_captures(
&mut self,
err: &mut Diag<'_>,
err: &mut Diag<'infcx>,
span: Span,
move_span: Span,
move_spans: UseSpans<'tcx>,
Expand All @@ -1009,6 +1010,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
is_loop_message,
is_move_msg,
is_loop_move,
has_suggest_reborrow,
maybe_reinitialized_locations_is_empty,
} = msg_opt;
if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
Expand Down Expand Up @@ -1182,18 +1184,15 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
if let ty::Ref(_, _, hir::Mutability::Mut) =
moved_place.ty(self.body, self.infcx.tcx).ty.kind()
{
// If we are in a loop this will be suggested later.
if !is_loop_move {
err.span_suggestion_verbose(
// Suggest `reborrow` in other place for following situations:
// 1. If we are in a loop this will be suggested later.
// 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(),
format!(
"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,
moved_place.as_ref(),
);
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/diagnostics/move_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
is_loop_message: false,
is_move_msg: false,
is_loop_move: false,
has_suggest_reborrow: false,
maybe_reinitialized_locations_is_empty: true,
};
if let Some(use_spans) = use_spans {
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`.
15 changes: 3 additions & 12 deletions tests/ui/borrowck/mut-borrow-in-loop-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,10 @@ LL | for _ in 0..3 {
LL | Other::handle(value);
| ^^^^^ 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
--> $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;
help: consider creating a fresh reborrow of `value` here
|
LL | Other::handle(&mut *value);
| ++++++
help: consider creating a fresh reborrow of `value` here
|
LL | Other::handle(&mut *value);
Expand Down
Loading