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

“closure references itself” error when it’s not #97680

Open
GoldsteinE opened this issue Jun 3, 2022 · 2 comments
Open

“closure references itself” error when it’s not #97680

GoldsteinE opened this issue Jun 3, 2022 · 2 comments
Labels
A-closures Area: Closures (`|…| { … }`) A-inference Area: Type inference A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@GoldsteinE
Copy link
Contributor

GoldsteinE commented Jun 3, 2022

I tried this code (playground):

use std::future::Future;

fn hrc<
    R,
    F: for<'a> AsyncClosure<'a, (), R>
        + for<'a> Fn(&'a ()) -> <F as AsyncClosure<'a, (), R>>::Fut,
>(
    f: F,
) -> F {
    f
}

fn main() {
    hrc(|x| async { });
}

trait AsyncClosure<'a, I, R>: Fn(&'a I) -> Self::Fut
where
    I: 'a,
{
    type Fut: Future<Output = R> + Send + 'a;
}

impl<'a, I, R, Fut, F> AsyncClosure<'a, I, R> for F
where
    I: 'a,
    F: Fn(&'a I) -> Fut,
    Fut: Future<Output = R> + Send + 'a,
{
    type Fut = Fut;
}

Trying to compile it produces the following error:

error[[E0644]](https://doc.rust-lang.org/stable/error-index.html#E0644): closure/generator type that references itself
  --> src/main.rs:14:9
   |
14 |     hrc(|x| async { });
   |         ^^^^^^^^^^^^^ cyclic type of infinite size
   |
   = note: closures cannot capture themselves or take themselves as argument;
           this error may be the result of a recent compiler bug-fix,
           see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
           for more information

I’m not sure if it should compile, but the diagnostic is misleading either way: this closure doesn’t have infinite size (in fact, it’s zero-sized), doesn’t capture itself (it captures nothing) and doesn’t take itself as argument (it only takes ()).

I don’t see a reason why it shouldn’t compile though, since all the types are known without evaluating the recursive bound on hrc().

Meta

This bug is reproducible on playground on stable, beta and nightly (1.61.0, 1.62.0-beta3 and 1.63.0-nightly (2022-06-02 e714405) respectively)

Links

@kpreid
Copy link
Contributor

kpreid commented Feb 6, 2023

Here's a shorter repro (sort of) that involves closures without async:

// Trait blanket implemented for Fn
trait StrFn<'a>: Fn(&'a str) -> Self::SOutput {
    type SOutput;
}
impl<'a, O, F: Fn(&'a str) -> O> StrFn<'a> for F {
    type SOutput = O;
}

// Usage of the trait with HRTB
fn go<F: for<'a> StrFn<'a>>(f: F) {
    f("");
}

fn main() {
    go(str::chars); // no error
    go(|line: &str| line.chars()); // error
}

This fails with “closure/generator type that references itself” on 1.67.0 stable. However, on nightly (2023-02-05 75a0be9) it gives a new error:

error: lifetime may not live long enough
  --> src/main.rs:16:21
   |
16 |     go(|line: &str| line.chars()); // error
   |               -   - ^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |               |   |
   |               |   return type of closure is Chars<'2>
   |               let's call the lifetime of this reference `'1`

which suggests that PR #105409, where @compiler-errors wrote

This also doesn't fix #97680, but it makes it possible to compile with the standard "closure wasn't smart enough at inferring higher-ranked lifetime" trick

doesn't fix as much as might be hoped, because in this case I am giving a type to the closure parameter but it still isn't accepted. (Or maybe I'm missing something about the required lifetime bounds.)

@WaffleLapkin
Copy link
Member

in this case I am giving a type to the closure parameter but it still isn't accepted

I think the problem here is that the return type is inferred to some specific lifetime, while the argument is inferred to be higher ranked (so the code is basically equivalent to "for<'a> |line: &'a str| -> Chars<'?> { line.chars() }"

@fmease fmease added A-lifetimes Area: Lifetimes / regions A-closures Area: Closures (`|…| { … }`) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-inference Area: Type inference T-types Relevant to the types team, which will review and decide on the PR/issue. and removed needs-triage-legacy labels Jan 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) A-inference Area: Type inference A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants