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

Rust has trouble seeing higher ranked lifetimes through generics #36582

Open
Stebalien opened this issue Sep 19, 2016 · 10 comments
Open

Rust has trouble seeing higher ranked lifetimes through generics #36582

Stebalien opened this issue Sep 19, 2016 · 10 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 A-typesystem Area: The type system C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@Stebalien
Copy link
Contributor

Rust has trouble seeing that a generic function is "generic" over higher ranked lifetimes. This behavior is best described by example. Given:

fn id<X>(x: X) -> X { x }

fn id_wrapped<'a, T>(v: &'a T) -> &'a T {
    // Defined for<'a>. No coercion.
    id::<&'a T>(v)
}

fn has_hrl<F: Fn(&()) -> &()>(f: F) {}

fn main() {
    has_hrl(id);
    has_hrl(id_wrapped);
    has_hrl(|x| id(x));
}

Rust complains:

error[E0281]: type mismatch: the type `fn(_) -> _ {id::<_>}` implements the trait `std::ops::Fn<(_,)>`, but the trait `for<'r> std::ops::Fn<(&'r (),)>` is required (expected concrete lifetime, found bound lifetime parameter )
  --> tmp.rs:11:5
   |
11 |     has_hrl(id);
   |     ^^^^^^^
   |
   = note: required by `has_hrl`

error[E0271]: type mismatch resolving `for<'r> <fn(_) -> _ {id::<_>} as std::ops::FnOnce<(&'r (),)>>::Output == &'r ()`
  --> tmp.rs:11:5
   |
11 |     has_hrl(id);
   |     ^^^^^^^ expected bound lifetime parameter , found concrete lifetime
   |
   = note: concrete lifetime that was found is lifetime '_#0r
   = note: required by `has_hrl`

error: aborting due to 2 previous errors

However, the has_hrl(id_wrapped) and has_hrl(|x| id(x)) calls demonstrate that, at some level, the rust type checker infers that id implements for<'r> Fn(&'r ()) -> &'r (). That's why I filed this as a bug instead of an RFC issue. However, I can move this if you feel that it's too much of a feature request.


My motivation is functional programming. Not being able to infer that a generic function is defined for some HRL makes it impossible to use such functions with, e.g., Iterator::filter.

@Stebalien
Copy link
Contributor Author

/cc @bluss @iopq

@Stebalien Stebalien changed the title Rust has trouble seeing HRL through generics Rust has trouble seeing higher ranked lifetimes through generics Sep 19, 2016
@TimNN TimNN added the A-typesystem Area: The type system label Sep 19, 2016
@iopq
Copy link

iopq commented Sep 20, 2016

I'm currently not able to use the library version of the function because of this limitation and I have to "specialize" manually by adding my own definition. This defeats the point of having libraries for functional programming in Rust, since the code doesn't compile due to this limitation.

@arielb1
Copy link
Contributor

arielb1 commented Sep 20, 2016

@iopq

That's an inherent limitation - generic type parameters can only be instantiated monomorphically:∀T. fn(T) -> T can't be instantiated to ∀'a. fn(&'a u32) -> &'a u32.

@Stebalien
Copy link
Contributor Author

@arielb1,

Then how does id_wrapped work? To the best of my understanding, it's doing exactly what you're saying can't be done.

@arielb1
Copy link
Contributor

arielb1 commented Sep 24, 2016

@Stebalien

id_wrapped uses a lifetime parameter, not a type parameter. It starts as being ∀T ∀'a. fn(&'a T) -> &'a T.

@Stebalien
Copy link
Contributor Author

@arielb1 Sorry, I should have explained further. id_wrapped is instantiated exactly once, therefore, it's body must be defined (in the executable) exactly once. Therefore, the rust compiler must be able to somehow tell that id::<'a, T> is defined for all 'a because the caller, id_wrapped, is defined for all 'a and is instantiated exactly once.

Here, I'm asking that rust use this knowledge to infer that <typeof id>: for<'a> Fn(&'a T) -> &'a T.

@arielb1
Copy link
Contributor

arielb1 commented Sep 24, 2016

As a trait object? The problem is that <typeof id>, which is basically System F's ∀T. T -> T (except for irrelevant fn-item vs. fn-ptr issues), is not really a type within Rust's trait/type system. Rust types can not contain higher-rank type parameters, and this is not expected to change in the nearby future (see: impredicativity). So when you use a Rust type, it is always instantiated to a type with no higher-ranked type parameters.

id_wrapped works because it uses a skolemized type, rather than a higher-ranked one.

@withoutboats
Copy link
Contributor

withoutboats commented Oct 4, 2016

@arielb1 Types which are higher-rank over lifetimes only don't suffer from impredicativity issues, no? You can't self-reference because they aren't of the correct kind for the higher-rank parameter.

Not saying they wouldn't be complicated to implement, just that I don't think they necessarily introduce decidability problems.

@steveklabnik
Copy link
Member

Triage: this still fails to compile.

@jonas-schievink jonas-schievink added A-lifetimes Area: Lifetimes / regions T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Mar 28, 2020
@estebank
Copy link
Contributor

Triage: current output

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:11:5
   |
11 |     has_hrl(id);
   |     ^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: `fn(&'2 ()) -> &'2 () {id::<&'2 ()>}` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2`

@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 A-typesystem Area: The type system C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

10 participants