Do not consider match/let/ref of place that evaluates to !
to diverge, disallow coercions from them too by compiler-errors · Pull Request #129392 · rust-lang/rust (original) (raw)
Fixes #117288.
This PR implements a heuristic which disables two things that are currently being performed on the HIR when we have expressions that involve place-like expressions that point to !
. Specifically, it will (in certain cases explained below):
(1.) Disable the NeverToAny
coercion we implicitly insert for !
.
Which fixes this inadvertent, sneaky unsoundness:
unsafe {
let x: *const ! = &0 as *const u8 as *const !;
let _: () = *x;
}
which is UB because currently rust emits an implicit NeverToAny coercion even though we really shouldn't be, since there's no read of the value pointed by x
.
(2.) Disable the logic which considers expression which evaluate to !
to diverge, which affects the type returned by the containing block.
Which fixes this unsoundness:
fn make_up_a_value<T>() -> T {
unsafe {
let x: *const ! = &0 as *const u8 as *const !;
let _ = *x;
}
}
We disable these two operations if the expression is a place-like expression (locals, statics, field projections, index operations, and deref operations), and if the parent expression is either:
(1.) the LHS of an assignment
(2.) AddrOf
(3.) A match or let unless all of the patterns consitute a read, which is explained below:
And finally, a pattern currently is considered to constitute a read unless it is a wildcard, or an OR pattern. An OR pattern is considered to constitute a read if all of its subpatterns constitute a read, to remain as conservative as possible in cases like _ | subpat
or subpat | _
.
All other patterns are considered currently to constitute a read. Specifically, because NeverToAny
is a coercion performed on a value and not a place, Struct { .. }
on a !
type must be a coercion currently, and we currently rely on this behavior to allow us to perform coercions like let _: i32 = x;
where x: !
.
This is already considered UB by miri, but also means it does not affect the preexisting UB in this case:
let Struct { .. } = *never_ptr;
Even though it's likely up for debate since we're not actually reading any data out of the struct, it almost certainly causes inference changes which I do NOT want to fix in this PR.