Skip to content

Error with nested async fn and anonymous lifetimes #63225

Closed

Description

@udoprog

The following breaks on nightly (see playground)

#![feature(async_await)]

struct Foo<'a>(&'a ());

impl Foo<'_> {
    fn test() {
        async fn test() {}
    }
}

With:

error[E0261]: use of undeclared lifetime name `'_`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0261`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

CC: dtolnay/async-trait#18

Activity

  1. added
    C-bugCategory: This is a bug.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Aug 2, 2019
  2. Centril commented on Aug 2, 2019

    @Centril
    Contributor

    Reduced:

    #![feature(async_await)]
    
    struct Foo<'a>(&'a ());
    
    impl<'a> Foo<'a> {
        fn test() {
            async fn test() {}
        }
    }
  3. added
    AsyncAwait-PolishAsync-await issues that are part of the "polish" area
    and removed on Aug 2, 2019
  4. udoprog commented on Aug 2, 2019

    @udoprog
    ContributorAuthor

    Forgot to mention in case this helps, giving the inner fn a matching lifetime avoids this issue for some reason:

    #![feature(async_await)]
    
    struct Foo<'a>(&'a ());
    
    impl<'a> Foo<'a> {
        fn test() {
            async fn test<'a>() {}
        }
    }
  5. cramertj commented on Aug 6, 2019

    @cramertj
    Member

    The reason this is happening is that the desugared version of async fn applies all in-scope lifetimes to the returned existential type, which in this case is creating -> Fut<'_>, which is invalid. I'm not sure exactly what the best fix is for this, though, since elided lifetimes in the impl signature are intentionally unnameable. The closest idea I have would be to create some special new lifetime that is '_ but can refer to lifetimes originating from the impl header. cc @nikomatsakis who may have opinions.

  6. nikomatsakis commented on Aug 7, 2019

    @nikomatsakis
    Contributor

    Hmm @cramertj wouldn't you expect the nested async fn to not inherit anything from its enclosing scopes? Seems like a bug to me that it does? Am I confused about something?

  7. cramertj commented on Aug 7, 2019

    @cramertj
    Member

    @nikomatsakis The function in the middle is actually a bit of a source of confusion here, although it does indicate that there's a second bug since we shouldn't be inheriting generics since the nested async fn isn't part of the impl.

    The bug I was focusing on is better illustrated by this example:

    #![feature(async_await)]
    
    struct Foo<'a>(&'a u8);
    
    impl Foo<'_> {
        async fn bar() {}
    }

    which produces this error:

    error[E0261]: use of undeclared lifetime name `'_`
    
    error: aborting due to previous error
    

    This is because the code is transformed into

    impl Foo<'_> {
        fn bar() -> OpaqueTy<'_> {}
    }

    and in that case '_ does not refer back to the usage from the impl header.

  8. nikomatsakis commented on Aug 12, 2019

    @nikomatsakis
    Contributor

    @cramertj I see. Indeed, I would expect that, internally, we'd have some way to refer back to the '_ from the impl header. I believe this should be possible, no? In particular, the '_ in impls expands (internally) to LifetimeName::Param(ParamName::Fresh(N)) for some integer N, which I believe is basically treated like any other named lifetime.

  9. cramertj commented on Aug 12, 2019

    @cramertj
    Member

    @nikomatsakis Yep, I would've expected so as well-- I'm not sure why that isn't working, I'd have to take a look.

  10. nikomatsakis commented on Aug 12, 2019

    @nikomatsakis
    Contributor

    @cramertj I was looking and I think the bug lies here:

    let lifetime_params: Vec<(Span, ParamName)> =
    this.in_scope_lifetimes
    .iter().cloned()
    .map(|ident| (ident.span, ParamName::Plain(ident)))
    .chain(this.lifetimes_to_define.iter().cloned())
    .collect();

    Probably in_scope_lifetimes needs to be a Vec<ParamName> instead, or we need to use something else for this purpose.

  11. 2 remaining items

  12. nikomatsakis commented on Aug 12, 2019

    @nikomatsakis
    Contributor

    OK, I have a fix for the async fn method issue, but not the nested async fn item issue.

  13. nikomatsakis commented on Aug 12, 2019

    @nikomatsakis
    Contributor

    I forked off #63500 for the method issue

  14. nikomatsakis commented on Aug 12, 2019

    @nikomatsakis
    Contributor

    @cramertj I didn't prep a fix for this one yet. It seems obvious that some internal state is not being reset when we enter a new item.

  15. nikomatsakis commented on Aug 12, 2019

    @nikomatsakis
    Contributor

    but if you have a chance to investigate, would be appreciated

  16. nikomatsakis commented on Aug 12, 2019

    @nikomatsakis
    Contributor

    Presumably the problem is that visit_item doesn't "reset" the in_scope_lifetimes vector -- it only seems to extend it...

    self.lctx.with_parent_item_lifetime_defs(hir_id, |this| {

  17. nikomatsakis commented on Aug 12, 2019

    @nikomatsakis
    Contributor

    Indeed, in-band lifetimes are broken in the same way. This fails to compile (playground):

    #![feature(in_band_lifetimes)]
    
    struct Foo<'a> {
        x: &'a u32
        
    }
    
    impl Foo<'a> {
        fn method(&self) {
            fn blah(f: Foo<'a>) { }
        }
    }
  18. nikomatsakis commented on Aug 12, 2019

    @nikomatsakis
    Contributor

    Working on a possible fix

  19. nikomatsakis commented on Aug 13, 2019

    @nikomatsakis
    Contributor

    Yep, have a fix.

  20. Mark-Simulacrum commented on Aug 13, 2019

    @Mark-Simulacrum
    Member

    Inband lifetimes looks similar to #52532 fwiw

  21. nikomatsakis commented on Aug 13, 2019

    @nikomatsakis
    Contributor
  22. self-assigned this
    on Aug 13, 2019
  23. added 2 commits that reference this issue on Aug 14, 2019
    6651373
    5741e29
  24. added a commit that references this issue on Aug 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-async-awaitArea: Async & AwaitAsyncAwait-PolishAsync-await issues that are part of the "polish" areaC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions