rust icon indicating copy to clipboard operation
rust copied to clipboard

Detect when move of !Copy value occurs within loop and should likely not be cloned

Open estebank opened this issue 11 months ago • 4 comments

When encountering a move error on a value within a loop of any kind, identify if the moved value belongs to a call expression that should not be cloned and avoid the semantically incorrect suggestion. Also try to suggest moving the call expression outside of the loop instead.

error[E0382]: use of moved value: `vec`
  --> $DIR/recreating-value-in-loop-condition.rs:6:33
   |
LL |     let vec = vec!["one", "two", "three"];
   |         --- move occurs because `vec` has type `Vec<&str>`, which does not implement the `Copy` trait
LL |     while let Some(item) = iter(vec).next() {
   |     ----------------------------^^^--------
   |     |                           |
   |     |                           value moved here, in previous iteration of loop
   |     inside of this loop
   |
note: consider changing this parameter type in function `iter` to borrow instead if owning the value isn't necessary
  --> $DIR/recreating-value-in-loop-condition.rs:1:17
   |
LL | fn iter<T>(vec: Vec<T>) -> impl Iterator<Item = T> {
   |    ----         ^^^^^^ 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 = iter(vec);
LL ~     while let Some(item) = value.next() {
   |

We use the presence of a break in the loop that would be affected by the moved value as a heuristic for "shouldn't be cloned".

Fix https://github.com/rust-lang/rust/issues/121466.


Point at continue and break that might be in the wrong place

Sometimes move errors are because of a misplaced continue, but we didn't surface that anywhere. Now when there are more than one set of nested loops we show them out and point at the continue and break expressions within that might need to go elsewhere.

error[E0382]: use of moved value: `foo`
  --> $DIR/nested-loop-moved-value-wrong-continue.rs:46:18
   |
LL |     for foo in foos {
   |         ---
   |         |
   |         this reinitialization might get skipped
   |         move occurs because `foo` has type `String`, which does not implement the `Copy` trait
...
LL |         for bar in &bars {
   |         ---------------- inside of this loop
...
LL |                 baz.push(foo);
   |                          --- value moved here, in previous iteration of loop
...
LL |         qux.push(foo);
   |                  ^^^ value used here after move
   |
note: verify that your loop breaking logic is correct
  --> $DIR/nested-loop-moved-value-wrong-continue.rs:41:17
   |
LL |     for foo in foos {
   |     ---------------
...
LL |         for bar in &bars {
   |         ----------------
...
LL |                 continue;
   |                 ^^^^^^^^ this `continue` advances the loop at line 33
help: consider moving the expression out of the loop so it is only moved once
   |
LL ~         let mut value = baz.push(foo);
LL ~         for bar in &bars {
LL |
 ...
LL |             if foo == *bar {
LL ~                 value;
   |
help: consider cloning the value if the performance cost is acceptable
   |
LL |                 baz.push(foo.clone());
   |                             ++++++++

Fix https://github.com/rust-lang/rust/issues/92531.

estebank avatar Feb 26 '24 23:02 estebank

r? @cjgillot

rustbot has assigned @cjgillot. They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

rustbot avatar Feb 26 '24 23:02 rustbot

:umbrella: The latest upstream changes (presumably #121489) made this pull request unmergeable. Please resolve the merge conflicts.

bors avatar Feb 28 '24 23:02 bors

I don't currently have the bandwidth to review this. r? compiler

cjgillot avatar Mar 10 '24 12:03 cjgillot

Cool stuff! I don't completely follow the "look for breaks" heuristic but I'll trust you on that. A few comments but looks good overall.

Nadrieril avatar Mar 10 '24 17:03 Nadrieril

@rustbot author

Nadrieril avatar Mar 17 '24 12:03 Nadrieril

Nice work, ty

@bors r+

Nadrieril avatar Mar 17 '24 23:03 Nadrieril

:pushpin: Commit 3b237d7d8ad46259856a42043ae37b25c6496f7f has been approved by Nadrieril

It is now in the queue for this repository.

bors avatar Mar 17 '24 23:03 bors

:hourglass: Testing commit 3b237d7d8ad46259856a42043ae37b25c6496f7f with merge 5608c7f9aaf380bd0c49bbcc350ecf8c5eb4b68c...

bors avatar Mar 18 '24 02:03 bors

:sunny: Test successful - checks-actions Approved by: Nadrieril Pushing 5608c7f9aaf380bd0c49bbcc350ecf8c5eb4b68c to master...

bors avatar Mar 18 '24 04:03 bors

Finished benchmarking commit (5608c7f9aaf380bd0c49bbcc350ecf8c5eb4b68c): comparison URL.

Overall result: ❌✅ regressions and improvements - no action needed

@rustbot label: -perf-regression

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
1.3% [1.3%, 1.3%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-1.1% [-1.1%, -1.1%] 1
All ❌✅ (primary) - - 0

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-4.7% [-5.0%, -4.3%] 2
Improvements ✅
(secondary)
-2.9% [-5.4%, -1.8%] 15
All ❌✅ (primary) -4.7% [-5.0%, -4.3%] 2

Cycles

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
5.6% [1.7%, 11.5%] 8
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 669.037s -> 668.349s (-0.10%) Artifact size: 312.76 MiB -> 312.73 MiB (-0.01%)

rust-timer avatar Mar 18 '24 06:03 rust-timer