Description
Right now, type variables fallback to !
if they are the result of a return
etc. The idea was that this represents a "dead control flow path", but it is not clear that this is the right rule, because those type variables often get 'caught up' in other paths.
In #40224, I outlined another possibility in a comment:
In talking with @pnkfelix, we had an alternative idea that yields a similar result, but does so in a more tailored way. Specifically, we could say that when you instantiate one enum variant (say,
Err
), any type parameters which do not appear in the variant in question get a diverging fallback. So the type ofErr(x)
isResult<?T, X>
, where?T
falls back to!
.This would allow
Err(x)?
to work as expected, while makingDeserialize::deserialize()?
fail as requiring a type annotation....
This result is not perfect. One can still write things like:
let mut x = Err(E); x = Ok(Deserialize::deserialize()?);
in particular, this would mean that Deserialize::deserialize()?;
would error out, whereas today it defaults to deserializing the !
type (and hence ... probably ... panics?). This feels like a case where I would expect an "unconstrained type variable" error -- but we don't get one today because one of the arms is dead, and hence generates a diverging fallback when coerced.
There is an obvious backwards compatibility concern here. It's not clear how much we can change these paths. But I suspect we have some leeway, if what we do is tailored enough.