fallback for diverging expressions leaks into the ? operator · Issue #39297 · rust-lang/rust (original) (raw)

This curious example was found by @canndrew:

trait Deserialize: Sized { fn deserialize() -> Result<Self, String>; }

impl Deserialize for i32 { fn deserialize() -> Result<i32, String> { Ok(22) } }

fn doit() -> Result<(), String> { let _ = <_ as Deserialize>::deserialize()?; Ok(()) }

fn main() { let _ = doit(); }

This fails to compile. In particular, the _ is inferred to () (at present) rather than i32. This is because of the interaction of two things:

Since there are no other constraints on the type of _, this winds up defaulting to (). Once the never-type work completes, it will default to !.

It's not entirely clear that this is a bug -- each part sort of makes sense -- but the result is pretty confounding. Some of the work on improving the trait system I've been doing would lead to this example compiling, because the _ would be inferred to i32.

Note that there are variants of this which do compile (because of the fallback to ()) -- i.e., if you changed the impl to be implemented for (). In this case, changing the fallback (to !) without improving the trait system's inference leads to a regression, since we fail to infer that () was the right answer all along.

I think that improving the trait system's inference does not lead to any breakage (since the default never kicks in). The basic reasoning is that, if the code compiled with a defualt before, but now compiles with improved inference, then the trait system must infer the same thing as the default, since otherwise there'd be ambiguity and it should not have done any inference (put another way, if it found another answer, then the default should have led to a compilation error).

That said, I think we should stop desugaring ? when we lower to HIR, and instead do it when we lower to MIR. This would be helpful for implementing catch, and would also give us more control over how the typing works. I think it's quite surprising the way the "divergence" is hidden in this example.

cc @eddyb @aturon, with whom I've discussed related issues