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

Suggest borrowing if a trait implementation is found for &/&mut <type> #85369

Merged
merged 2 commits into from
May 18, 2021

Conversation

FabianWolff
Copy link
Contributor

This pull request fixes #84973 by suggesting to borrow if a trait is not implemented for some type T, but it is for &T or &mut T. For instance:

trait Ti {}
impl<T> Ti for &T {}
fn foo<T: Ti>(_: T) {}

trait Tm {}
impl<T> Tm for &mut T {}
fn bar<T: Tm>(_: T) {}

fn main() {
    let a: i32 = 5;
    foo(a);

    let b: Box<i32> = Box::new(42);
    bar(b);
}

gives, on current nightly:

error[E0277]: the trait bound `i32: Ti` is not satisfied
  --> t2.rs:11:9
   |
3  | fn foo<T: Ti>(_: T) {}
   |           -- required by this bound in `foo`
...
11 |     foo(a);
   |         ^ the trait `Ti` is not implemented for `i32`

error[E0277]: the trait bound `Box<i32>: Tm` is not satisfied
  --> t2.rs:14:9
   |
7  | fn bar<T: Tm>(_: T) {}
   |           -- required by this bound in `bar`
...
14 |     bar(b);
   |         ^ the trait `Tm` is not implemented for `Box<i32>`

error: aborting due to 2 previous errors

whereas with my changes, I get:

error[E0277]: the trait bound `i32: Ti` is not satisfied
  --> t2.rs:11:9
   |
3  | fn foo<T: Ti>(_: T) {}
   |           -- required by this bound in `foo`
...
11 |     foo(a);
   |         ^
   |         |
   |         expected an implementor of trait `Ti`
   |         help: consider borrowing here: `&a`

error[E0277]: the trait bound `Box<i32>: Tm` is not satisfied
  --> t2.rs:14:9
   |
7  | fn bar<T: Tm>(_: T) {}
   |           -- required by this bound in `bar`
...
14 |     bar(b);
   |         ^
   |         |
   |         expected an implementor of trait `Tm`
   |         help: consider borrowing mutably here: `&mut b`

error: aborting due to 2 previous errors

In my implementation, I have added a "blacklist" to make these suggestions flexible. In particular, suggesting to borrow can interfere with other suggestions, such as to add another trait bound to a generic argument. I have tried to configure this blacklist to cause the least amount of test case failures, i.e. to model the current behavior as closely as possible (I only had to change one existing test case, and this change was quite clearly an improvement).

@rust-highfive
Copy link
Collaborator

r? @jackh726

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 16, 2021
Copy link
Member

@jackh726 jackh726 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how I feel about the blacklist. Can you provide some examples of errors with/without it? (And add some tests for those (with))

Also, these suggestions are only shown if &T: Trait actually holds right? Not just if T: Trait doesn't?

| ^
| |
| expected an implementor of trait `Trait`
| help: consider borrowing mutably here: `&mut s`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to say "mutably borrowing"

@FabianWolff
Copy link
Contributor Author

I'm not sure how I feel about the blacklist. Can you provide some examples of errors with/without it? (And add some tests for those (with))

The blacklist doesn't endanger soundness or anything — it just controls whether we issue a suggestion in this particular function or not. Currently, no suggestions are issued at all for the obligation causes I have addressed in this PR, so we don't lose anything by blacklisting a few traits. Without the blacklist, there are many test case failures in src/test/ui/, i.e. the blacklist makes this PR a much less intrusive change.

As an example,

fn foo<T: Copy>(t: T) {}

fn main() {
    foo("".to_string());
}

gives

error[E0277]: the trait bound `String: Copy` is not satisfied
 --> test.rs:4:9
  |
1 | fn foo<T: Copy>(t: T) {}
  |           ---- required by this bound in `foo`
...
4 |     foo("".to_string());
  |         ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`

error: aborting due to previous error

with the blacklist, and

error[E0277]: the trait bound `String: Copy` is not satisfied
 --> test.rs:4:9
  |
1 | fn foo<T: Copy>(t: T) {}
  |           ---- required by this bound in `foo`
...
4 |     foo("".to_string());
  |         ^^^^^^^^^^^^^^
  |         |
  |         expected an implementor of trait `Copy`
  |         help: consider borrowing here: `&"".to_string()`

error: aborting due to previous error

without it.

Also, these suggestions are only shown if &T: Trait actually holds right? Not just if T: Trait doesn't?

Yes, of course. I have added another test case to emphasize this, along with one test case for the blacklisted traits. I have also adjusted the wording of the suggestion.

@rust-log-analyzer

This comment has been minimized.

@jackh726
Copy link
Member

Thank you ♥

@bors r+ rollup

@bors
Copy link
Contributor

bors commented May 17, 2021

📌 Commit 572bb13 has been approved by jackh726

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 17, 2021
@FabianWolff
Copy link
Contributor Author

@jackh726 Thanks for taking the time to review this PR!

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request May 18, 2021
Suggest borrowing if a trait implementation is found for &/&mut <type>

This pull request fixes rust-lang#84973 by suggesting to borrow if a trait is not implemented for some type `T`, but it is for `&T` or `&mut T`. For instance:
```rust
trait Ti {}
impl<T> Ti for &T {}
fn foo<T: Ti>(_: T) {}

trait Tm {}
impl<T> Tm for &mut T {}
fn bar<T: Tm>(_: T) {}

fn main() {
    let a: i32 = 5;
    foo(a);

    let b: Box<i32> = Box::new(42);
    bar(b);
}
```
gives, on current nightly:
```
error[E0277]: the trait bound `i32: Ti` is not satisfied
  --> t2.rs:11:9
   |
3  | fn foo<T: Ti>(_: T) {}
   |           -- required by this bound in `foo`
...
11 |     foo(a);
   |         ^ the trait `Ti` is not implemented for `i32`

error[E0277]: the trait bound `Box<i32>: Tm` is not satisfied
  --> t2.rs:14:9
   |
7  | fn bar<T: Tm>(_: T) {}
   |           -- required by this bound in `bar`
...
14 |     bar(b);
   |         ^ the trait `Tm` is not implemented for `Box<i32>`

error: aborting due to 2 previous errors
```
whereas with my changes, I get:
```
error[E0277]: the trait bound `i32: Ti` is not satisfied
  --> t2.rs:11:9
   |
3  | fn foo<T: Ti>(_: T) {}
   |           -- required by this bound in `foo`
...
11 |     foo(a);
   |         ^
   |         |
   |         expected an implementor of trait `Ti`
   |         help: consider borrowing here: `&a`

error[E0277]: the trait bound `Box<i32>: Tm` is not satisfied
  --> t2.rs:14:9
   |
7  | fn bar<T: Tm>(_: T) {}
   |           -- required by this bound in `bar`
...
14 |     bar(b);
   |         ^
   |         |
   |         expected an implementor of trait `Tm`
   |         help: consider borrowing mutably here: `&mut b`

error: aborting due to 2 previous errors
```
In my implementation, I have added a "blacklist" to make these suggestions flexible. In particular, suggesting to borrow can interfere with other suggestions, such as to add another trait bound to a generic argument. I have tried to configure this blacklist to cause the least amount of test case failures, i.e. to model the current behavior as closely as possible (I only had to change one existing test case, and this change was quite clearly an improvement).
bors added a commit to rust-lang-ci/rust that referenced this pull request May 18, 2021
…laumeGomez

Rollup of 7 pull requests

Successful merges:

 - rust-lang#84587 (rustdoc: Make "rust code block is empty" and "could not parse code block" warnings a lint (`INVALID_RUST_CODEBLOCKS`))
 - rust-lang#85280 (Toggle-wrap items differently than top-doc.)
 - rust-lang#85338 (Implement more Iterator methods on core::iter::Repeat)
 - rust-lang#85339 (Report an error if a lang item has the wrong number of generic arguments)
 - rust-lang#85369 (Suggest borrowing if a trait implementation is found for &/&mut <type>)
 - rust-lang#85393 (Suppress spurious errors inside `async fn`)
 - rust-lang#85415 (Clean up remnants of BorrowOfPackedField)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit fad04ff into rust-lang:master May 18, 2021
@rustbot rustbot added this to the 1.54.0 milestone May 18, 2021
@pickfire
Copy link
Contributor

Can you add a test where both the & and &mut is there? What will be the output?

@FabianWolff
Copy link
Contributor Author

Can you add a test where both the & and &mut is there? What will be the output?

No, because this pull request has already been merged. The output will be a suggestion to immutably borrow; you can see for yourself on nightly:

trait Tr {}
struct S {}

impl Tr for &S {}
impl Tr for &mut S {}

fn foo<T: Tr>(t: T) {}

fn main() {
    let s = S {};
    foo(s);
}
error[E0277]: the trait bound `S: Tr` is not satisfied
  --> src/main.rs:11:9
   |
7  | fn foo<T: Tr>(t: T) {}
   |           -- required by this bound in `foo`
...
11 |     foo(s);
   |         ^
   |         |
   |         expected an implementor of trait `Tr`
   |         help: consider borrowing here: `&s`

error: aborting due to previous error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Suggest borrowing parameter into function with generic argument where a matching impl is found
7 participants