Skip to content

Improve diagnostics for impl Trait capturing lifetimes #78402

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

Open
djc opened this issue Oct 26, 2020 · 6 comments
Open

Improve diagnostics for impl Trait capturing lifetimes #78402

djc opened this issue Oct 26, 2020 · 6 comments
Labels
A-async-await Area: Async & Await A-borrow-checker Area: The borrow checker A-impl-trait Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch. A-lifetimes Area: Lifetimes / regions AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@djc
Copy link
Contributor

djc commented Oct 26, 2020

I tried this code:

use futures_util::stream::{once, Stream};
use std::io;

fn assert_static<T: 'static>(value: T) {}

async fn foo(a: &str) {
    assert_static(bar(a.split(',')));
}

fn bar<T>(_: T) -> impl Stream<Item = Result<Vec<u8>, io::Error>> {
    once(async { Ok(vec![]) })
}

I expected to see this happen: compiles correctly.

Instead, this happened:

error[E0759]: `a` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
 --> src/lib.rs:6:14
  |
6 | async fn foo(a: &str) {
  |              ^  ---- this data with an anonymous lifetime `'_`...
  |              |
  |              ...is captured here...
7 |     assert_static(bar(a.split(',')));
  |     ------------- ...and is required to live as long as `'static` here

Meta

rustc 1.47.0. This appears to be #42940, fixing which might depend on type-alias-impl-trait (although so far that's only a workaround, not really a fix?). It would be nice to have more specific diagnostics here.

@jyn514
Copy link
Member

jyn514 commented Oct 26, 2020

The error is correct, I think. You happen to call foo with a static value, but you don't put that in the type signature, so someone else is free to call it with a shorter lifetime. If you want to enforce only static lifetimes for foo, you can use a: &'static.

@jyn514
Copy link
Member

jyn514 commented Oct 26, 2020

Oh wait, I missed that the return type of bar doesn't (or at least, shouldn't) depends on a. Yeah this does seem like a bug then.

@estebank estebank added A-async-await Area: Async & Await A-borrow-checker Area: The borrow checker A-lifetimes Area: Lifetimes / regions T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 26, 2020
@Arnavion
Copy link

This appears to be #42940, fixing which might depend on type-alias-impl-trait (although so far that's only a workaround, not really a fix?).

As cramertj wrote there, the current behavior is by design of the RPIT RFC ("Assumption 2"), because it was expected that type-alias-impl-trait would resolve it ("Assumption 1") correctly.

@estebank estebank added the A-impl-trait Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch. label Oct 26, 2020
@tmandry tmandry added the A-diagnostics Area: Messages for errors, warnings, and lints label Nov 19, 2020
@tmandry
Copy link
Member

tmandry commented Nov 19, 2020

Based on the RFC I think it's correct that this wouldn't compile. However, I am curious why adding a 'static bound to the return type of bar doesn't fix it:

fn bar<T>(_: T) -> impl Stream<Item = Result<Vec<u8>, io::Error>> + 'static {

That ought to remove any doubt that the return type is tied to the lifetime of T.

Also, this still fails to compile when foo is not async, but prints a more concise error message:

error[E0621]: explicit lifetime required in the type of `a`
 --> src/lib.rs:7:5
  |
6 | fn foo(a: &str) {
  |           ---- help: add explicit lifetime `'static` to the type of `a`: `&'static str`
7 |     assert_static(bar(a.split(',')));
  |     ^^^^^^^^^^^^^ lifetime `'static` required

@tmandry tmandry added AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. and removed A-diagnostics Area: Messages for errors, warnings, and lints labels Nov 19, 2020
@masklinn
Copy link
Contributor

@tmandry from #42940 (comment)

From some experimentation, it seems to be because fn foo<T>(_: T) -> impl Bar compiles to something like fn foo<T>(_: T) -> impl Bar + 't, where 't is the lifetime of T. (I don't think this relationship can be expressed in regular Rust code, though Ralith on IRC suggested one could consider the anonymous type to contain PhantomData<T>)

Thus in the original code fn post<'a, B>(&'a self, _body: &B) -> impl Future + 'a effectively forces the return type to be bounded by B's lifetime in addition to 'a. This is why changing _body: &B to _body: B does not change anything, nor does annotating the parameter as _body: &'b B for an unconstrained 'b

(emphasis mine)

So when adding an explicit lifetime bound, it's in addition to the implicit one, not instead of. And since 'static is the widest lifetime it has no effect. I tried the same thing when I hit the issue, with the same results, before I found #42940 :/

@Dylan-DPC
Copy link
Member

Current output:

error[[E0521]](https://doc.rust-lang.org/stable/error_codes/E0521.html): borrowed data escapes outside of function
 --> src/lib.rs:7:5
  |
6 | async fn foo(a: &str) {
  |              -  - let's call the lifetime of this reference `'1`
  |              |
  |              `a` is a reference that is only valid in the function body
7 |     assert_static(bar(a.split(',')));
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |     |
  |     `a` escapes the function body here
  |     argument requires that `'1` must outlive `'static`

For more information about this error, try `rustc --explain E0521`.
error: could not compile `playground` due to previous error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await A-borrow-checker Area: The borrow checker A-impl-trait Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch. A-lifetimes Area: Lifetimes / regions AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

7 participants