Skip to content

Commit db00608

Browse files
authored
Unrolled build for rust-lang#132172
Rollup merge of rust-lang#132172 - dianne:suggest-borrow-generic, r=matthewjasper borrowck diagnostics: suggest borrowing function inputs in generic positions # Summary This generalizes borrowck's existing suggestions to borrow instead of moving when passing by-value to a function that's generic in that input. Previously, this was special-cased to `AsRef`/`Borrow`-like traits and `Fn`-like traits. This PR changes it to test if, for a moved place with type `T`, that the callee's signature and clauses don't break if you substitute in `&T` or `&mut T`. For instance, it now works with `Read`/`Write`-like traits. Fixes rust-lang#131413 # Incidental changes - No longer spuriously suggests mutable borrows of closures in some situations (see e.g. the tests in [tests/ui/closures/2229_closure_analysis/](https://github.com/rust-lang/rust/compare/master...dianne:rust:suggest-borrow-generic?expand=1#diff-8dfb200c559f0995d0f2ffa2f23bc6f8041b263e264e5c329a1f4171769787c0)). - No longer suggests cloning closures that implement `Fn`, since they can be borrowed (see e.g. [tests/ui/moves/borrow-closures-instead-of-move.stderr](https://github.com/rust-lang/rust/compare/master...dianne:rust:suggest-borrow-generic?expand=1#diff-5db268aac405eec56d099a72d8b58ac46dab523cf013e29008104840168577fb)). This keeps the behavior to suppress suggestions of `fn_once.clone()()`. I think it might make sense to suggest it with a "but this might not be your desired behavior" caveat, as is done when something is used after being consumed as the receiver for a method call. That's probably out of the scope of this PR though. # Limitations and possible improvements - This doesn't work for receivers of method calls. This is a small change, and I have it implemented locally, but I'm not sure it's useful on its own. In most cases I've found borrowing the receiver would change the call's output type (see below). In other cases (e.g. `Iterator::sum`), borrowing the receiver isn't useful since it's consumed. - This doesn't work when it would change the call's output type. In general, I figure inserting references into the output type is an unwanted change. However, this also means it doesn't work in cases where the new output type would be effectively the same as the old one. For example, from the rand crate, the iterator returned by [`Rng::sample_iter`](https://docs.rs/rand/latest/rand/trait.Rng.html#method.sample_iter) is effectively the same (modulo regions) whether you borrow or consume the receiver `Rng`, so common usage involves borrowing it. I'm not sure whether the best approach is to add a more complex check of approximate equivalence, to forego checking the call's output type and give spurious suggestions, or to leave it as-is. - This doesn't work when it would change the call's other input types. Instead, it could suggest borrowing any others that have the same parameter type (but only when suggesting shared borrows). I think this would be a pretty easy change, but I don't think it's very useful so long as the normalized output type can't change. I'm happy to implement any of these (or other potential improvements to this), but I'm not sure which are common enough patterns to justify the added complexity. Please let me know if any sound worthwhile.
2 parents c82e0df + 2ab8480 commit db00608

14 files changed

+396
-233
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+186-182
Large diffs are not rendered by default.

tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr

-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t
1111
|
1212
LL | if let MultiVariant::Point(ref mut x, _) = point {
1313
| ^^^^^
14-
help: consider mutably borrowing `c`
15-
|
16-
LL | let a = &mut c;
17-
| ++++
1814

1915
error: aborting due to 1 previous error
2016

tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr

-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t
1111
|
1212
LL | let SingleVariant::Point(ref mut x, _) = point;
1313
| ^^^^^
14-
help: consider mutably borrowing `c`
15-
|
16-
LL | let b = &mut c;
17-
| ++++
1814

1915
error: aborting due to 1 previous error
2016

tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr

-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t
1111
|
1212
LL | x.y.a += 1;
1313
| ^^^^^
14-
help: consider mutably borrowing `hello`
15-
|
16-
LL | let b = &mut hello;
17-
| ++++
1814

1915
error: aborting due to 1 previous error
2016

tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr

-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t
1111
|
1212
LL | x.0 += 1;
1313
| ^^^
14-
help: consider mutably borrowing `hello`
15-
|
16-
LL | let b = &mut hello;
17-
| ++++
1814

1915
error: aborting due to 1 previous error
2016

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! auxiliary definitons for suggest-borrow-for-generic-arg.rs, to ensure the suggestion works on
2+
//! functions defined in other crates.
3+
4+
use std::io::{self, Read, Write};
5+
use std::iter::Sum;
6+
7+
pub fn write_stuff<W: Write>(mut writer: W) -> io::Result<()> {
8+
writeln!(writer, "stuff")
9+
}
10+
11+
pub fn read_and_discard<R: Read>(mut reader: R) -> io::Result<()> {
12+
let mut buf = Vec::new();
13+
reader.read_to_end(&mut buf).map(|_| ())
14+
}
15+
16+
pub fn sum_three<I: IntoIterator>(iter: I) -> <I as IntoIterator>::Item
17+
where <I as IntoIterator>::Item: Sum
18+
{
19+
iter.into_iter().take(3).sum()
20+
}

tests/ui/moves/borrow-closures-instead-of-move.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
fn takes_fn(f: impl Fn()) { //~ HELP if `impl Fn()` implemented `Clone`
1+
fn takes_fn(f: impl Fn()) {
22
loop {
33
takes_fnonce(f);
44
//~^ ERROR use of moved value

tests/ui/moves/borrow-closures-instead-of-move.stderr

-22
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,6 @@ LL | loop {
88
LL | takes_fnonce(f);
99
| ^ value moved here, in previous iteration of loop
1010
|
11-
note: consider changing this parameter type in function `takes_fnonce` to borrow instead if owning the value isn't necessary
12-
--> $DIR/borrow-closures-instead-of-move.rs:34:20
13-
|
14-
LL | fn takes_fnonce(_: impl FnOnce()) {}
15-
| ------------ ^^^^^^^^^^^^^ this parameter takes ownership of the value
16-
| |
17-
| in this function
18-
help: if `impl Fn()` implemented `Clone`, you could clone the value
19-
--> $DIR/borrow-closures-instead-of-move.rs:1:16
20-
|
21-
LL | fn takes_fn(f: impl Fn()) {
22-
| ^^^^^^^^^ consider constraining this type parameter with `Clone`
23-
LL | loop {
24-
LL | takes_fnonce(f);
25-
| - you could clone this value
2611
help: consider borrowing `f`
2712
|
2813
LL | takes_fnonce(&f);
@@ -40,13 +25,6 @@ LL | takes_fnonce(m);
4025
LL | takes_fnonce(m);
4126
| ^ value used here after move
4227
|
43-
note: consider changing this parameter type in function `takes_fnonce` to borrow instead if owning the value isn't necessary
44-
--> $DIR/borrow-closures-instead-of-move.rs:34:20
45-
|
46-
LL | fn takes_fnonce(_: impl FnOnce()) {}
47-
| ------------ ^^^^^^^^^^^^^ this parameter takes ownership of the value
48-
| |
49-
| in this function
5028
help: if `impl FnMut()` implemented `Clone`, you could clone the value
5129
--> $DIR/borrow-closures-instead-of-move.rs:9:20
5230
|

tests/ui/moves/moved-value-on-as-ref-arg.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ LL | foo(bar);
88
LL | let _baa = bar;
99
| ^^^ value used here after move
1010
|
11-
help: borrow the value to avoid moving it
11+
help: consider borrowing `bar`
1212
|
1313
LL | foo(&bar);
1414
| +
@@ -31,7 +31,7 @@ LL | struct Bar;
3131
...
3232
LL | qux(bar);
3333
| --- you could clone this value
34-
help: borrow the value to avoid moving it
34+
help: consider mutably borrowing `bar`
3535
|
3636
LL | qux(&mut bar);
3737
| ++++
@@ -46,7 +46,7 @@ LL | bat(bar);
4646
LL | let _baa = bar;
4747
| ^^^ value used here after move
4848
|
49-
help: borrow the value to avoid moving it
49+
help: consider borrowing `bar`
5050
|
5151
LL | bat(&bar);
5252
| +
@@ -69,7 +69,7 @@ LL | struct Bar;
6969
...
7070
LL | baz(bar);
7171
| --- you could clone this value
72-
help: borrow the value to avoid moving it
72+
help: consider mutably borrowing `bar`
7373
|
7474
LL | baz(&mut bar);
7575
| ++++

tests/ui/moves/moves-based-on-type-no-recursive-stack-closure.stderr

-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ LL | fn conspirator<F>(mut f: F) where F: FnMut(&mut R, bool) {
2424
| ^ consider constraining this type parameter with `Clone`
2525
LL | let mut r = R {c: Box::new(f)};
2626
| - you could clone this value
27-
help: consider mutably borrowing `f`
28-
|
29-
LL | let mut r = R {c: Box::new(&mut f)};
30-
| ++++
3127

3228
error: aborting due to 2 previous errors
3329

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//! Test suggetions to borrow generic arguments instead of moving. Tests for other instances of this
2+
//! can be found in `moved-value-on-as-ref-arg.rs` and `borrow-closures-instead-of-move.rs`
3+
//@ run-rustfix
4+
//@ aux-crate:aux=suggest-borrow-for-generic-arg-aux.rs
5+
//@ edition: 2021
6+
7+
#![allow(unused_mut)]
8+
use std::io::{self, Write};
9+
10+
// test for `std::io::Write` (#131413)
11+
fn test_write() -> io::Result<()> {
12+
let mut stdout = io::stdout();
13+
aux::write_stuff(&stdout)?; //~ HELP consider borrowing `stdout`
14+
writeln!(stdout, "second line")?; //~ ERROR borrow of moved value: `stdout`
15+
16+
let mut buf = Vec::new();
17+
aux::write_stuff(&mut buf.clone())?; //~ HELP consider mutably borrowing `buf`
18+
//~^ HELP consider cloning the value
19+
writeln!(buf, "second_line") //~ ERROR borrow of moved value: `buf`
20+
}
21+
22+
/// test for `std::io::Read` (#131413)
23+
fn test_read() -> io::Result<()> {
24+
let stdin = io::stdin();
25+
aux::read_and_discard(&stdin)?; //~ HELP consider borrowing `stdin`
26+
aux::read_and_discard(stdin)?; //~ ERROR use of moved value: `stdin`
27+
28+
let mut bytes = std::collections::VecDeque::from([1, 2, 3, 4, 5, 6]);
29+
aux::read_and_discard(&mut bytes.clone())?; //~ HELP consider mutably borrowing `bytes`
30+
//~^ HELP consider cloning the value
31+
aux::read_and_discard(bytes) //~ ERROR use of moved value: `bytes`
32+
}
33+
34+
/// test that suggestions work with projection types in the callee's signature
35+
fn test_projections() {
36+
let mut iter = [1, 2, 3, 4, 5, 6].into_iter();
37+
let _six: usize = aux::sum_three(&mut iter.clone()); //~ HELP consider mutably borrowing `iter`
38+
//~^ HELP consider cloning the value
39+
let _fifteen: usize = aux::sum_three(iter); //~ ERROR use of moved value: `iter`
40+
}
41+
42+
fn main() {
43+
test_write().unwrap();
44+
test_read().unwrap();
45+
test_projections();
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//! Test suggetions to borrow generic arguments instead of moving. Tests for other instances of this
2+
//! can be found in `moved-value-on-as-ref-arg.rs` and `borrow-closures-instead-of-move.rs`
3+
//@ run-rustfix
4+
//@ aux-crate:aux=suggest-borrow-for-generic-arg-aux.rs
5+
//@ edition: 2021
6+
7+
#![allow(unused_mut)]
8+
use std::io::{self, Write};
9+
10+
// test for `std::io::Write` (#131413)
11+
fn test_write() -> io::Result<()> {
12+
let mut stdout = io::stdout();
13+
aux::write_stuff(stdout)?; //~ HELP consider borrowing `stdout`
14+
writeln!(stdout, "second line")?; //~ ERROR borrow of moved value: `stdout`
15+
16+
let mut buf = Vec::new();
17+
aux::write_stuff(buf)?; //~ HELP consider mutably borrowing `buf`
18+
//~^ HELP consider cloning the value
19+
writeln!(buf, "second_line") //~ ERROR borrow of moved value: `buf`
20+
}
21+
22+
/// test for `std::io::Read` (#131413)
23+
fn test_read() -> io::Result<()> {
24+
let stdin = io::stdin();
25+
aux::read_and_discard(stdin)?; //~ HELP consider borrowing `stdin`
26+
aux::read_and_discard(stdin)?; //~ ERROR use of moved value: `stdin`
27+
28+
let mut bytes = std::collections::VecDeque::from([1, 2, 3, 4, 5, 6]);
29+
aux::read_and_discard(bytes)?; //~ HELP consider mutably borrowing `bytes`
30+
//~^ HELP consider cloning the value
31+
aux::read_and_discard(bytes) //~ ERROR use of moved value: `bytes`
32+
}
33+
34+
/// test that suggestions work with projection types in the callee's signature
35+
fn test_projections() {
36+
let mut iter = [1, 2, 3, 4, 5, 6].into_iter();
37+
let _six: usize = aux::sum_three(iter); //~ HELP consider mutably borrowing `iter`
38+
//~^ HELP consider cloning the value
39+
let _fifteen: usize = aux::sum_three(iter); //~ ERROR use of moved value: `iter`
40+
}
41+
42+
fn main() {
43+
test_write().unwrap();
44+
test_read().unwrap();
45+
test_projections();
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
error[E0382]: borrow of moved value: `stdout`
2+
--> $DIR/suggest-borrow-for-generic-arg.rs:14:14
3+
|
4+
LL | let mut stdout = io::stdout();
5+
| ---------- move occurs because `stdout` has type `Stdout`, which does not implement the `Copy` trait
6+
LL | aux::write_stuff(stdout)?;
7+
| ------ value moved here
8+
LL | writeln!(stdout, "second line")?;
9+
| ^^^^^^ value borrowed here after move
10+
|
11+
help: consider borrowing `stdout`
12+
|
13+
LL | aux::write_stuff(&stdout)?;
14+
| +
15+
16+
error[E0382]: borrow of moved value: `buf`
17+
--> $DIR/suggest-borrow-for-generic-arg.rs:19:14
18+
|
19+
LL | let mut buf = Vec::new();
20+
| ------- move occurs because `buf` has type `Vec<u8>`, which does not implement the `Copy` trait
21+
LL | aux::write_stuff(buf)?;
22+
| --- value moved here
23+
LL |
24+
LL | writeln!(buf, "second_line")
25+
| ^^^ value borrowed here after move
26+
|
27+
help: consider mutably borrowing `buf`
28+
|
29+
LL | aux::write_stuff(&mut buf)?;
30+
| ++++
31+
help: consider cloning the value if the performance cost is acceptable
32+
|
33+
LL | aux::write_stuff(buf.clone())?;
34+
| ++++++++
35+
36+
error[E0382]: use of moved value: `stdin`
37+
--> $DIR/suggest-borrow-for-generic-arg.rs:26:27
38+
|
39+
LL | let stdin = io::stdin();
40+
| ----- move occurs because `stdin` has type `Stdin`, which does not implement the `Copy` trait
41+
LL | aux::read_and_discard(stdin)?;
42+
| ----- value moved here
43+
LL | aux::read_and_discard(stdin)?;
44+
| ^^^^^ value used here after move
45+
|
46+
help: consider borrowing `stdin`
47+
|
48+
LL | aux::read_and_discard(&stdin)?;
49+
| +
50+
51+
error[E0382]: use of moved value: `bytes`
52+
--> $DIR/suggest-borrow-for-generic-arg.rs:31:27
53+
|
54+
LL | let mut bytes = std::collections::VecDeque::from([1, 2, 3, 4, 5, 6]);
55+
| --------- move occurs because `bytes` has type `VecDeque<u8>`, which does not implement the `Copy` trait
56+
LL | aux::read_and_discard(bytes)?;
57+
| ----- value moved here
58+
LL |
59+
LL | aux::read_and_discard(bytes)
60+
| ^^^^^ value used here after move
61+
|
62+
help: consider mutably borrowing `bytes`
63+
|
64+
LL | aux::read_and_discard(&mut bytes)?;
65+
| ++++
66+
help: consider cloning the value if the performance cost is acceptable
67+
|
68+
LL | aux::read_and_discard(bytes.clone())?;
69+
| ++++++++
70+
71+
error[E0382]: use of moved value: `iter`
72+
--> $DIR/suggest-borrow-for-generic-arg.rs:39:42
73+
|
74+
LL | let mut iter = [1, 2, 3, 4, 5, 6].into_iter();
75+
| -------- move occurs because `iter` has type `std::array::IntoIter<usize, 6>`, which does not implement the `Copy` trait
76+
LL | let _six: usize = aux::sum_three(iter);
77+
| ---- value moved here
78+
LL |
79+
LL | let _fifteen: usize = aux::sum_three(iter);
80+
| ^^^^ value used here after move
81+
|
82+
help: consider mutably borrowing `iter`
83+
|
84+
LL | let _six: usize = aux::sum_three(&mut iter);
85+
| ++++
86+
help: consider cloning the value if the performance cost is acceptable
87+
|
88+
LL | let _six: usize = aux::sum_three(iter.clone());
89+
| ++++++++
90+
91+
error: aborting due to 5 previous errors
92+
93+
For more information about this error, try `rustc --explain E0382`.

tests/ui/not-copy-closure.stderr

-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ note: closure cannot be moved more than once as it is not `Copy` due to moving t
1111
|
1212
LL | a += 1;
1313
| ^
14-
help: consider mutably borrowing `hello`
15-
|
16-
LL | let b = &mut hello;
17-
| ++++
1814

1915
error: aborting due to 1 previous error
2016

0 commit comments

Comments
 (0)