Closure return type not deduced from bounds if the type is wrapped.

4c9ece1
Opened by Eduard-Mihai Burtescu at 2020-09-15 17:12:49

In a tuple, for example:

fn call<R, F: FnOnce() -> R>(f: F) -> R {
    f()
}

fn main() {
    let _: (&[i32],) = call(|| (&[],));
}

AFAICT, we don't deduce that call::<$R, _>(|| (&[],)) expects (&[i32],) implies &[] expects &[i32], only (&[],) expects $R.

  1. I don't think the wrapper is involved here (that's it, unlike the somewhat-similar #20841).

    In the first example, you are actually invoking call::<&String, _> and the coercion is done after the call.

    Ariel Ben-Yehuda at 2016-02-22 11:44:12

  2. @arielb1 Oops, I reduced the testcase incorrectly (I've edited the description now). I'm not sure how to get this to cause issues without some sort of wrapper. Maybe the MIR can show where the coercion happens?

    Eduard-Mihai Burtescu at 2016-02-22 11:57:01

  3. @eddyb

    I think I understand this. The type parameters passed to call are still type-variables when the closure is checked, which means that the fn obligation is FnOnce() -> _. As deduce_expectations_from_expected_type is keyed on the obligation, it only finds the FnOnce() -> _.

    The reason the coercion works without a closure is that we provide the "expected argument types". However, the expected argument types aren't related to the FnOnce obligations.

    So this is actually pretty similar to #20841.

    Ariel Ben-Yehuda at 2016-02-22 12:00:33

  4. It looks like you can resolve this by changing it to this:

    fn call<R, F: FnOnce() -> R>(f: F) -> R {
        f()
    }
    
    fn main() {
    -     let _: (&[i32],) = call(|| (&[],));
    +     let _: (&[i32; 0],) = call(|| (&[],));
    }
    

    Although I think it's still a bug.

    Here's a playground with the change applied: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d29a9da04efaf4845b9bcd93ab426f13

    Noah Lev at 2020-09-15 17:11:43