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

properly handle anonymous regions appearing in return type- named-anon conflicts #42703

Open
gaurikholkar-zz opened this issue Jun 16, 2017 · 10 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-diagnostics Working group: Diagnostics

Comments

@gaurikholkar-zz
Copy link

gaurikholkar-zz commented Jun 16, 2017

These cases, going by the way #42669 for one named and other anonymous lifetime parameter , the error message generated would suggest consider changing type of self to &'a i32. In the first case, this is fine but the second case, with self.field not being the return value, the message is wrong and should only suggest changing the return type. We need to differentiate both the cases and fix the error message for the second case.

fn foo<'a>(&self, x: &'a i32) -> &i32 {
 // Preferred message:
 //     fn foo<'a>(&self, x: &'a i32) -> &i32 {
 //                 -----                 ---- consider changing to `&'a i32`
 //                 |
 //                 consider changing to `&'a self`
    if true { &self.field } else { x }
  }
fn foo<'a>(&self, x: &'a i32) -> &i32 {
//       preferred error
//       fn foo<'a>(&self, x: &'a i32) -> &i32 {             
//                                         ---- consider changing to `&'a i32`
    x

// error[E0611]: explicit lifetime required in the type of `self`
//    |
// 33 |     fn foo<'a>(&self, x: &'a i32) -> &i32 {
//    |                 ^^^^^ consider changing the type of `self` to `&'a Foo`
// 34 |         //                             
// 35 |         x
//    |         - lifetime `'a` required
  }

cc @nikomatsakis

@nikomatsakis nikomatsakis changed the title Differentiate between the cases for trait impls properly handle anonymous regions appearing in self or return type Jun 16, 2017
@nikomatsakis nikomatsakis changed the title properly handle anonymous regions appearing in self or return type properly handle anonymous regions appearing in return type Jun 16, 2017
@Mark-Simulacrum Mark-Simulacrum added the A-diagnostics Area: Messages for errors, warnings, and lints label Jun 23, 2017
@nikomatsakis
Copy link
Contributor

So the problem here is that, without examining the region graph closely, we can't tell the difference between these two cases. This is non-trivial to solve: I have to say that when I took a look at the region inference graphs for these two cases, I failed to see any obvious rules we could use to distinguish them. We may have to add additional refinement into the "causes" and so forth to figure this out.

@nikomatsakis
Copy link
Contributor

I had a thought about possibly changing the error message to avoid doing complex analysis. Maybe instead of suggesting to the user what change they ought to make (which would require analysis), we might do a better job just telling them what is needed. So the two cases would look like:

fn foo<'a>(&self, x: &'a i32) -> &i32 {
    //               -------     ---- these references must have the same lifetime
    x
//  ^ data from `x` is returned here
}

fn foo<'a>(&self, x: &'a i32) -> &i32 {
    //               -------     ---- these references must have the same lifetime
    if true { &self.field } else { x }
    //                             ^ data from `x` is returned here
}

The second case is, I guess, non-ideal, in that it would be nicer if we could underline all three references.

@Mark-Simulacrum Mark-Simulacrum added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jul 26, 2017
@gaurikholkar-zz
Copy link
Author

gaurikholkar-zz commented Aug 21, 2017

working on this now

@gaurikholkar-zz gaurikholkar-zz changed the title properly handle anonymous regions appearing in return type properly handle anonymous regions appearing in return type- named-anon conflicts Aug 21, 2017
@nikomatsakis nikomatsakis added E-needs-mentor WG-diagnostics Working group: Diagnostics T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 22, 2017
@nikomatsakis
Copy link
Contributor

Seems like we could use some mentoring instructions here!

@nikomatsakis
Copy link
Contributor

@gaurikholkar I'm not sure, did we ever land code fixing this?

@gaurikholkar-zz
Copy link
Author

@nikomatsakis for now, #44124 handles them all

@estebank
Copy link
Contributor

estebank commented Nov 4, 2017

@gaurikholkar we can close this ticket now, right?

@gaurikholkar-zz
Copy link
Author

gaurikholkar-zz commented Nov 19, 2017

For Case 1,

5 | fn foo<'a>(&self, x: &'a i32) -> &i32 
  |                      -------     ----
  |                      |
  |                      this parameter and the return type are declared with different lifetimes...
6 |         x
  |         ^ ...but data from `x` is returned here

Case 2

5 | fn foo<'a>(&self, x: &'a i32) -> &i32 
  |                      -------     ----
  |                      |
  |                      this parameter and the return type are declared with different lifetimes...
6 |         if true {x} else {&self}
  |                  ^ ...but data from `x` is returned here

@nikomatsakis afaik, we were planning to further modify the error message. Should we open up a new issue for that?
cc @estebank

@estebank
Copy link
Contributor

Current output:

error[E0623]: lifetime mismatch
  --> src/lib.rs:12:36
   |
6  | fn foo<'a>(&self, x: &'a i32) -> &i32 {
   |                      -------     ----
   |                      |
   |                      this parameter and the return type are declared with different lifetimes...
...
12 |     if true { &self.field } else { x }
   |                                    ^ ...but data from `x` is returned here

error[E0623]: lifetime mismatch
  --> src/lib.rs:19:5
   |
15 |   fn bar<'a>(&self, x: &'a i32) -> &i32 {
   |                        -------     ----
   |                        |
   |                        this parameter and the return type are declared with different lifetimes...
...
19 |     x
   |     ^ ...but data from `x` is returned here

Only missing thing would be to suggest changing the lifetimes.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jan 15, 2022
Point at correct argument when async fn output type lifetime disagrees with signature

Fixes most of rust-lang#74256.

## Problems fixed

This PR fixes a couple of related problems in the error reporting code.

### Highlighting the wrong argument

First, the error reporting code was looking at the desugared return type of an `async fn` to decide which parameter to highlight. For example, a function like

```rust
async fn async_fn(self: &Struct, f: &u32) -> &u32
{ f }
```

desugars to

```rust
async fn async_fn<'a, 'b>(self: &'a Struct, f: &'b u32)
-> impl Future<Output = &'a u32> + 'a + 'b
{ f }
```

Since `f: &'b u32` is returned but the output type is `&'a u32`, the error would occur when checking that `'a: 'b`.

The reporting code would look to see if the "offending" lifetime `'b` was included in the return type, and because the code was looking at the desugared future type, it was included. So it defaulted to reporting that the source of the other lifetime `'a` (the `self` type) was the problem, when it was really the type of `f`. (Note that if it had chosen instead to look at `'a` first, it too would have been included in the output type, and it would have arbitrarily reported the error (correctly this time) on the type of `f`.)

Looking at the actual future type isn't useful for this reason; it captures all input lifetimes. Using the written return type for `async fn` solves this problem and results in less confusing error messages for the user.

This isn't a perfect fix, unfortunately; writing the "manually desugared" form of the above function still results in the wrong parameter being highlighted. Looking at the output type of every `impl Future` return type doesn't feel like a very principled approach, though it might work. The problem would remain for function signatures that look like the desugared one above but use different traits. There may be deeper changes required to pinpoint which part of each type is conflicting.

### Lying about await point capture causing lifetime conflicts

The second issue fixed by this PR is the unnecessary complexity in `try_report_anon_anon_conflict`. It turns out that the root cause I suggested in rust-lang#76547 (comment) wasn't really the root cause. Adding special handling to report that a variable was captured over an await point only made the error messages less correct and pointed to a problem other than the one that actually occurred.

Given the above discussion, it's easy to see why: `async fn`s capture all input lifetimes in their return type, so holding an argument across an await point should never cause a lifetime conflict! Removing the special handling simplified the code and improved the error messages (though they still aren't very good!)

## Future work

* Fix error reporting on the "desugared" form of this code
* Get the `suggest_adding_lifetime_params` suggestion firing on these examples
  * cc rust-lang#42703, I think

r? `@estebank`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jan 20, 2022
Point at correct argument when async fn output type lifetime disagrees with signature

Fixes most of rust-lang#74256.

## Problems fixed

This PR fixes a couple of related problems in the error reporting code.

### Highlighting the wrong argument

First, the error reporting code was looking at the desugared return type of an `async fn` to decide which parameter to highlight. For example, a function like

```rust
async fn async_fn(self: &Struct, f: &u32) -> &u32
{ f }
```

desugars to

```rust
async fn async_fn<'a, 'b>(self: &'a Struct, f: &'b u32)
-> impl Future<Output = &'a u32> + 'a + 'b
{ f }
```

Since `f: &'b u32` is returned but the output type is `&'a u32`, the error would occur when checking that `'a: 'b`.

The reporting code would look to see if the "offending" lifetime `'b` was included in the return type, and because the code was looking at the desugared future type, it was included. So it defaulted to reporting that the source of the other lifetime `'a` (the `self` type) was the problem, when it was really the type of `f`. (Note that if it had chosen instead to look at `'a` first, it too would have been included in the output type, and it would have arbitrarily reported the error (correctly this time) on the type of `f`.)

Looking at the actual future type isn't useful for this reason; it captures all input lifetimes. Using the written return type for `async fn` solves this problem and results in less confusing error messages for the user.

This isn't a perfect fix, unfortunately; writing the "manually desugared" form of the above function still results in the wrong parameter being highlighted. Looking at the output type of every `impl Future` return type doesn't feel like a very principled approach, though it might work. The problem would remain for function signatures that look like the desugared one above but use different traits. There may be deeper changes required to pinpoint which part of each type is conflicting.

### Lying about await point capture causing lifetime conflicts

The second issue fixed by this PR is the unnecessary complexity in `try_report_anon_anon_conflict`. It turns out that the root cause I suggested in rust-lang#76547 (comment) wasn't really the root cause. Adding special handling to report that a variable was captured over an await point only made the error messages less correct and pointed to a problem other than the one that actually occurred.

Given the above discussion, it's easy to see why: `async fn`s capture all input lifetimes in their return type, so holding an argument across an await point should never cause a lifetime conflict! Removing the special handling simplified the code and improved the error messages (though they still aren't very good!)

## Future work

* Fix error reporting on the "desugared" form of this code
* Get the `suggest_adding_lifetime_params` suggestion firing on these examples
  * cc rust-lang#42703, I think

r? `@estebank`
@estebank
Copy link
Contributor

Current output:

error: lifetime may not live long enough
  --> src/lib.rs:12:36
   |
6  | fn foo<'a>(&self, x: &'a i32) -> &i32 {
   |        --  - let's call the lifetime of this reference `'1`
   |        |
   |        lifetime `'a` defined here
...
12 |     if true { &self.field } else { x }
   |                                    ^ associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`

error: lifetime may not live long enough
  --> src/lib.rs:19:5
   |
15 |   fn bar<'a>(&self, x: &'a i32) -> &i32 {
   |          --  - let's call the lifetime of this reference `'1`
   |          |
   |          lifetime `'a` defined here
...
19 |     x
   |     ^ associated function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-diagnostics Working group: Diagnostics
Projects
None yet
Development

No branches or pull requests

5 participants