Description
In #40224, I adopted the rule that one can coerce into the type !
if your expression is considered to diverge (in short, returns, breaks, or must evaluate some expression of type !
).
However, the code doens't quite implement that rule. As described in this comment, it actually uses the self.diverges
flag of the FnCtxt
, which today tracks a property that is different in a subtle way. The property we are checking is: if the coercion of the expression E happens in the context of an expression F, it is permitted to coerce to !
if the expression F has diverged by the time that E finishes evaluating.
So in particular, under the rules I originally envisioned, this would not be allowed (see src/test/compile-fail/coerce-to-bang.rs
for more examples):
fn foo(x: usize, y: !) { }
foo(return, 22); // the expression `22` does not diverge
But under the rules as implemented, it is allowed. It's not clear which rules we would prefer. I think I lean towards my original rules: they mean that whether an expression E
can be coerced to a type T
is purely a function of E
(modulo free variables of course). But implementing that will require refactoring the compiler.
In my diverging-types-and-reachability
branch, I changed how things work so that divergence is propagated upward, and "reachability" is passed downward. Divergence is thus a property purely of the expression at hand, whereas reachability is computed based on context. We use the combination of the two to drive unreachable warnings -- in particular, when you have reachable code which evaluates a diverging expression, the next thing to be evaluated triggered a warning. The precise setup I had in that branch doesn't work, sadly, because it only computed divergence after all types were known. Some refactoring needed.
Moreover, there are some other places I cut corners in the existing code, and I'm using this same FIXME to track them: in particular, in cast expressions, I am ignoring whether they diverge for the purposes of this check. That's just laziness.