Skip to content

Commit de90afc

Browse files
committed
Explain method-call move errors in loops
PR #73708 added a more detailed explanation of move errors that occur due to a call to a method that takes `self`. This PR extends that logic to work when a move error occurs due to a method call in the previous iteration of a loop.
1 parent ddf2cc7 commit de90afc

File tree

4 files changed

+90
-76
lines changed

4 files changed

+90
-76
lines changed

compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs

+68-74
Original file line numberDiff line numberDiff line change
@@ -151,95 +151,88 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
151151

152152
let move_msg = if move_spans.for_closure() { " into closure" } else { "" };
153153

154+
let loop_message = if location == move_out.source || move_site.traversed_back_edge {
155+
", in previous iteration of loop"
156+
} else {
157+
""
158+
};
159+
154160
if location == move_out.source {
155-
err.span_label(
156-
span,
157-
format!(
158-
"value {}moved{} here, in previous iteration of loop",
159-
partially_str, move_msg
160-
),
161-
);
162161
is_loop_move = true;
163-
} else if move_site.traversed_back_edge {
164-
err.span_label(
165-
move_span,
166-
format!(
167-
"value {}moved{} here, in previous iteration of loop",
168-
partially_str, move_msg
169-
),
170-
);
171-
} else {
172-
if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } =
173-
move_spans
174-
{
175-
let place_name = self
176-
.describe_place(moved_place.as_ref())
177-
.map(|n| format!("`{}`", n))
178-
.unwrap_or_else(|| "value".to_owned());
179-
match kind {
180-
FnSelfUseKind::FnOnceCall => {
162+
}
163+
164+
if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans {
165+
let place_name = self
166+
.describe_place(moved_place.as_ref())
167+
.map(|n| format!("`{}`", n))
168+
.unwrap_or_else(|| "value".to_owned());
169+
match kind {
170+
FnSelfUseKind::FnOnceCall => {
171+
err.span_label(
172+
fn_call_span,
173+
&format!(
174+
"{} {}moved due to this call{}",
175+
place_name, partially_str, loop_message
176+
),
177+
);
178+
err.span_note(
179+
var_span,
180+
"this value implements `FnOnce`, which causes it to be moved when called",
181+
);
182+
}
183+
FnSelfUseKind::Operator { self_arg } => {
184+
err.span_label(
185+
fn_call_span,
186+
&format!(
187+
"{} {}moved due to usage in operator{}",
188+
place_name, partially_str, loop_message
189+
),
190+
);
191+
if self.fn_self_span_reported.insert(fn_span) {
192+
err.span_note(
193+
self_arg.span,
194+
"calling this operator moves the left-hand side",
195+
);
196+
}
197+
}
198+
FnSelfUseKind::Normal { self_arg, implicit_into_iter } => {
199+
if implicit_into_iter {
181200
err.span_label(
182201
fn_call_span,
183202
&format!(
184-
"{} {}moved due to this call",
185-
place_name, partially_str
203+
"{} {}moved due to this implicit call to `.into_iter()`{}",
204+
place_name, partially_str, loop_message
186205
),
187206
);
188-
err.span_note(
189-
var_span,
190-
"this value implements `FnOnce`, which causes it to be moved when called",
191-
);
192-
}
193-
FnSelfUseKind::Operator { self_arg } => {
207+
} else {
194208
err.span_label(
195209
fn_call_span,
196210
&format!(
197-
"{} {}moved due to usage in operator",
198-
place_name, partially_str
211+
"{} {}moved due to this method call{}",
212+
place_name, partially_str, loop_message
199213
),
200214
);
201-
if self.fn_self_span_reported.insert(fn_span) {
202-
err.span_note(
203-
self_arg.span,
204-
"calling this operator moves the left-hand side",
205-
);
206-
}
207215
}
208-
FnSelfUseKind::Normal { self_arg, implicit_into_iter } => {
209-
if implicit_into_iter {
210-
err.span_label(
211-
fn_call_span,
212-
&format!(
213-
"{} {}moved due to this implicit call to `.into_iter()`",
214-
place_name, partially_str
215-
),
216-
);
217-
} else {
218-
err.span_label(
219-
fn_call_span,
220-
&format!(
221-
"{} {}moved due to this method call",
222-
place_name, partially_str
223-
),
224-
);
225-
}
226-
// Avoid pointing to the same function in multiple different
227-
// error messages
228-
if self.fn_self_span_reported.insert(self_arg.span) {
229-
err.span_note(
230-
self_arg.span,
231-
&format!("this function consumes the receiver `self` by taking ownership of it, which moves {}", place_name)
232-
);
233-
}
216+
// Avoid pointing to the same function in multiple different
217+
// error messages
218+
if self.fn_self_span_reported.insert(self_arg.span) {
219+
err.span_note(
220+
self_arg.span,
221+
&format!("this function consumes the receiver `self` by taking ownership of it, which moves {}", place_name)
222+
);
234223
}
235-
// Deref::deref takes &self, which cannot cause a move
236-
FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
237224
}
238-
} else {
239-
err.span_label(
240-
move_span,
241-
format!("value {}moved{} here", partially_str, move_msg),
242-
);
225+
// Deref::deref takes &self, which cannot cause a move
226+
FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
227+
}
228+
} else {
229+
err.span_label(
230+
move_span,
231+
format!("value {}moved{} here{}", partially_str, move_msg, loop_message),
232+
);
233+
// If the move error occurs due to a loop, don't show
234+
// another message for the same span
235+
if loop_message.is_empty() {
243236
move_spans.var_span_label(
244237
&mut err,
245238
format!(
@@ -250,6 +243,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
250243
);
251244
}
252245
}
246+
253247
if let UseSpans::PatUse(span) = move_spans {
254248
err.span_suggestion_verbose(
255249
span.shrink_to_lo(),

src/test/ui/moves/move-fn-self-receiver.rs

+5
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ fn move_out(val: Container) {
6969
let container = Container(vec![]);
7070
for _val in container.custom_into_iter() {}
7171
container; //~ ERROR use of moved
72+
73+
let foo2 = Foo;
74+
loop {
75+
foo2.use_self(); //~ ERROR use of moved
76+
}
7277
}
7378

7479
fn main() {}

src/test/ui/moves/move-fn-self-receiver.stderr

+10-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,16 @@ note: this function consumes the receiver `self` by taking ownership of it, whic
152152
LL | fn custom_into_iter(self) -> impl Iterator<Item = bool> {
153153
| ^^^^
154154

155-
error: aborting due to 11 previous errors
155+
error[E0382]: use of moved value: `foo2`
156+
--> $DIR/move-fn-self-receiver.rs:75:9
157+
|
158+
LL | let foo2 = Foo;
159+
| ---- move occurs because `foo2` has type `Foo`, which does not implement the `Copy` trait
160+
LL | loop {
161+
LL | foo2.use_self();
162+
| ^^^^ ---------- `foo2` moved due to this method call, in previous iteration of loop
163+
164+
error: aborting due to 12 previous errors
156165

157166
Some errors have detailed explanations: E0382, E0505.
158167
For more information about an error, try `rustc --explain E0382`.

src/test/ui/suggestions/borrow-for-loop-head.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,14 @@ LL | for i in &a {
1515
LL | for j in a {
1616
| ^
1717
| |
18-
| value moved here, in previous iteration of loop
18+
| `a` moved due to this implicit call to `.into_iter()`, in previous iteration of loop
1919
| help: consider borrowing to avoid moving into the for loop: `&a`
20+
|
21+
note: this function consumes the receiver `self` by taking ownership of it, which moves `a`
22+
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
23+
|
24+
LL | fn into_iter(self) -> Self::IntoIter;
25+
| ^^^^
2026

2127
error: aborting due to 2 previous errors
2228

0 commit comments

Comments
 (0)