Skip to content

Commit 021a983

Browse files
authored
Rollup merge of #129392 - compiler-errors:raw-ref-op-doesnt-diverge-but-more, r=lcnr
Do not consider match/let/ref of place that evaluates to `!` to diverge, disallow coercions from them too 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](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf3a2246433fe43fdc07d1389c276c9), 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.
2 parents 69cceab + e00eae6 commit 021a983

File tree

1 file changed

+49
-2
lines changed

1 file changed

+49
-2
lines changed

tests/pass/underscore_pattern.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Various tests ensuring that underscore patterns really just construct the place, but don't check its contents.
22
#![feature(strict_provenance)]
3+
#![feature(never_type)]
4+
35
use std::ptr;
46

57
fn main() {
@@ -9,6 +11,7 @@ fn main() {
911
invalid_let();
1012
dangling_let_type_annotation();
1113
invalid_let_type_annotation();
14+
never();
1215
}
1316

1417
fn dangling_match() {
@@ -34,13 +37,25 @@ fn invalid_match() {
3437
_ => {}
3538
}
3639
}
40+
41+
unsafe {
42+
let x: Uninit<!> = Uninit { uninit: () };
43+
match x.value {
44+
_ => {}
45+
}
46+
}
3747
}
3848

3949
fn dangling_let() {
4050
unsafe {
4151
let ptr = ptr::without_provenance::<bool>(0x40);
4252
let _ = *ptr;
4353
}
54+
55+
unsafe {
56+
let ptr = ptr::without_provenance::<!>(0x40);
57+
let _ = *ptr;
58+
}
4459
}
4560

4661
fn invalid_let() {
@@ -49,6 +64,12 @@ fn invalid_let() {
4964
let ptr = ptr::addr_of!(val).cast::<bool>();
5065
let _ = *ptr;
5166
}
67+
68+
unsafe {
69+
let val = 3u8;
70+
let ptr = ptr::addr_of!(val).cast::<!>();
71+
let _ = *ptr;
72+
}
5273
}
5374

5475
// Adding a type annotation used to change how MIR is generated, make sure we cover both cases.
@@ -57,6 +78,11 @@ fn dangling_let_type_annotation() {
5778
let ptr = ptr::without_provenance::<bool>(0x40);
5879
let _: bool = *ptr;
5980
}
81+
82+
unsafe {
83+
let ptr = ptr::without_provenance::<!>(0x40);
84+
let _: ! = *ptr;
85+
}
6086
}
6187

6288
fn invalid_let_type_annotation() {
@@ -65,7 +91,28 @@ fn invalid_let_type_annotation() {
6591
let ptr = ptr::addr_of!(val).cast::<bool>();
6692
let _: bool = *ptr;
6793
}
94+
95+
unsafe {
96+
let val = 3u8;
97+
let ptr = ptr::addr_of!(val).cast::<!>();
98+
let _: ! = *ptr;
99+
}
68100
}
69101

70-
// FIXME: we should also test `!`, not just `bool` -- but that s currently buggy:
71-
// https://github.com/rust-lang/rust/issues/117288
102+
// Regression test from <https://github.com/rust-lang/rust/issues/117288>.
103+
fn never() {
104+
unsafe {
105+
let x = 3u8;
106+
let x: *const ! = &x as *const u8 as *const _;
107+
let _: ! = *x;
108+
}
109+
110+
// Without a type annotation, make sure we don't implicitly coerce `!` to `()`
111+
// when we do the noop `*x` (as that would require a `!` *value*, creating
112+
// which is UB).
113+
unsafe {
114+
let x = 3u8;
115+
let x: *const ! = &x as *const u8 as *const _;
116+
let _ = *x;
117+
}
118+
}

0 commit comments

Comments
 (0)