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 type mismatch on higher-ranked bounds #51004

Open
cramertj opened this issue May 23, 2018 · 6 comments
Open

Closure type mismatch on higher-ranked bounds #51004

cramertj opened this issue May 23, 2018 · 6 comments
Labels
A-closures Area: Closures (`|…| { … }`) A-higher-ranked Area: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs) 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.

Comments

@cramertj
Copy link
Member

cramertj commented May 23, 2018

In the following code, identical impls for a concrete type, a fn type, and a closure exist yet the only the closure fails to meet the higher-ranked bound:

trait FnLt<'a> {
    fn apply(self, input: &'a u8) -> &'a u8;
}

// Struct impl
struct Foo;
impl<'a> FnLt<'a> for Foo {
    fn apply(self, input: &'a u8) -> &'a u8 {
        input
    }
}

// Closure impl
impl<'a, T> FnLt<'a> for T
where
    T: FnOnce(&'a u8) -> &'a u8,
{
    fn apply(self, input: &'a u8) -> &'a u8 {
        (self)(input)
    }
}

fn take_fn_lt(_: impl for<'a> FnLt<'a>) {}

fn main() {
    take_fn_lt(Foo); // Works
    
    fn foo(x: &u8) -> &u8 { x }
    take_fn_lt(foo); // Works
    
    take_fn_lt(|x: &u8| -> &u8 { x }); // Doesn't work
}

The error is this:

error[E0271]: type mismatch resolving `for<'a> <[closure@src/main.rs:31:16: 31:37] as std::ops::FnOnce<(&'a u8,)>>::Output == &'a u8`
  --> src/main.rs:31:5
   |
31 |     take_fn_lt(|x: &u8| -> &u8 { x }); // Doesn't work
   |     ^^^^^^^^^^ expected bound lifetime parameter 'a, found concrete lifetime
   |
   = note: required because of the requirements on the impl of `for<'a> FnLt<'a>` for `[closure@src/main.rs:31:16: 31:37]`
note: required by `take_fn_lt`
  --> src/main.rs:23:1
   |
23 | fn take_fn_lt(_: impl for<'a> FnLt<'a>) {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Edit: the closure can be made to work through coercion to a fn pointer (which is unsurprising, since it's then the same as foo). take_fn_lt({ |x: &u8| -> &u8 { x } } as fn(&u8) -> &u8); compiles.

@Mark-Simulacrum
Copy link
Member

I believe this is the same issue as #41078.

@cramertj
Copy link
Member Author

cramertj commented May 24, 2018

@Mark-Simulacrum I'm not sure this is a proper duplicate-- when I've hit that other issue, I've always been able to get it to go away by explicitly writing out the signature of the closure types. I can't do that here-- you'll notice that the type of the closure is specified manually, yet it still fails.

cc @nikomatsakis

@cramertj
Copy link
Member Author

cramertj commented May 30, 2018

I'm going to reopen this, as I think it at least needs further investigation in order to determine if it is the same as #41076 (which I doubt).

@aturon
Copy link
Member

aturon commented Jul 24, 2018

cc @eddyb @nikomatsakis, this is a pretty persistent and important issue for async/await work. It might be most expedient to address it by adding AsyncFn/AsyncFnMut/AsyncFnOnce unstable traits -- does that seem plausible?

@eddyb
Copy link
Member

eddyb commented Aug 1, 2018

@nikomatsakis Looks like this works fine if the bound in take_fn_lt is replaced with FnOnce(&u8) -> &u8, which means that either the "closure signature deduction" hack is kicking in there and making it work (unlike FnLt, which doesn't get the deduction) or going through trait selection imposes additional constraints.

@aturon Not entirely opposed to AsyncFn* - if unstable, they're easy to add, but I worry that blanket impls won't cut it (as they don't in this issue). And trait aliases aren't implemented yet AFAIK.

@XAMPPRocky XAMPPRocky added A-trait-system Area: Trait system T-lang Relevant to the language team, which will review and decide on the PR/issue. C-bug Category: This is a bug. labels Oct 2, 2018
@Enselic Enselic added A-lifetimes Area: Lifetimes / regions T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. and removed T-lang Relevant to the language team, which will review and decide on the PR/issue. A-trait-system Area: Trait system labels Nov 20, 2023
@Enselic
Copy link
Member

Enselic commented Nov 20, 2023

Triage: Rust 1.74 gives more details on why it rejects this code. Playground link. It assigns different lifetimes to the closure input and output, contrary to usual elision rules:

error: lifetime may not live long enough
  --> src/main.rs:31:34
   |
31 |     take_fn_lt(|x: &u8| -> &u8 { x }); // Doesn't work
   |                    -       -     ^ returning this value requires that `'1` must outlive `'2`
   |                    |       |
   |                    |       let's call the lifetime of this reference `'2`
   |                    let's call the lifetime of this reference `'1`

@fmease fmease added A-closures Area: Closures (`|…| { … }`) A-higher-ranked Area: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs) labels Sep 24, 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-higher-ranked Area: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs) 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.
Projects
None yet
Development

No branches or pull requests

7 participants