-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Cannot infer closure type with higher-ranked lifetimes inside Box::new #20841
Comments
Convenience trumps performance: it currently is hard for the compiler to correctly infer the types of closure's arguments when the closure is constructed inside Box::new. The compiler issue: rust-lang/rust#20841
Triage: updated code fn do_async<F>(_cb: Box<F>) where F: FnOnce(&i32) {
}
fn do_async_unboxed<F>(cb: F) where F: FnOnce(&i32) {
do_async(Box::new(cb))
}
fn main() {
do_async_unboxed(|x| { println!("{}", *x); }); // Ok
do_async(Box::new(|x| { println!("{}", *x); })); // ERROR the type of this value must be known in this context
} |
From my investigation, the problem is that we don't relate the return type of |
Nominating as this is makes some API patterns horribly non-ergonomic. |
triage: P-medium |
I agree we could probably do better here. I'm not sure what precisely is going on or the current state of this coercion code though. |
triage: P-medium |
// Works:
fn foo<F: Fn(f32) -> f32>(_: F) {}
foo(|x| x.sin())
// Why it works: the inference variables look like this:
foo::<$F>((|x: $A| x.sin()): $F)
// Because $F: Fn(f32) -> f32, we can deduce $A: f32. // Doesn't work:
fn bar<F: Fn(f32) -> f32>(_: Option<F>) {}
bar(Some(|x| x.sin()))
// Why it doesn't work: the inference variables look like this:
bar::<$F>(Some::<$T>((|x: $A| x.sin()): $T): Option<$F>)
// Because Some: T -> Option<T>, we know that $T = $F.
// But they are both inference variables, so we don't replace
// one with the other, and $T: Fn(f32) -> f32 doesn't exist. If my analysis is correct, it should be possible to fix this bug by computing inference variable equivalence via union-find instead of using simple equality. |
Except that the variables are only related via a coercion. |
That looks like a case of things being so loosely coupled they fall apart. |
@arielb1 There's no coercion magic going on, this is the "expected" type being propagated down. Coercions are applied after the type is known. |
I mean, we don't equate |
@arielb1 We do to obtain the expected type for the arguments of a call. |
That would be a subtype relation, which is destroyed by the |
@arielb1 Let's try to be clearer using pseudosyntax: // Initial expression.
bar(Some((|x| x.sin())))
// Add type variables for bar.
bar::<$F>(Some((|x| x.sin())))
// Propagate expected type from signature of bar.
bar::<$F>(Some((|x| x.sin())) expects Option<$F>)
// Add type variables for Some.
bar::<$F>(Some::<$T>((|x| x.sin())))
// Propagate expected type from signature of Some.
bar::<$F>(Some::<$T>((|x| x.sin()) expects $T) expects Option<$F>) Well, that's embarrassing, turns out I was being unreasonable. A relationship between Presumably it might be possible to end up with: bar::<$F>(Some::<$T>((|x| x.sin()) expects $F) expects Option<$F>) What confused me was that the following snippet fn foo<F: Fn(f32) -> f32>(_: F) {}
fn id<T>(x: T) -> T {x}
foo(id(|x| x.sin())) Given that it doesn't actually work, it's a better testcase. foo::<$F>(id::<$T>((|x| x.sin()) expects $F) expects $F) Then that would work, but consider: fn foo<F>(_: F) {}
fn id<T: Fn(f32) -> f32>(x: T) -> T {x}
foo(id(|x| x.sin())) Where @arielb1 @nikomatsakis Would it make sense to have a set of inference variable "assignments" used for the expected type? |
That might fix this, but it may cause confusing issues if a coercion were actually to occur. Maybe have hints solely for closure inference? That looks like a hack. |
@arielb1 Nothing other than closure inference looks at bounds which refer to the expected type, AFAICT. |
Triage: not aware of any movement on this issue |
Triage: still reproduces on 2021 edition, tested using |
In the code below, type inference works in the first line of
main()
, but fails in the second one. Note also that the "kind" of the closure has to be given with explicit syntax when insideBox::new()
.The text was updated successfully, but these errors were encountered: