Auto merge of #103208 - cjgillot:match-fake-read, r=oli-obk,RalfJung

Allow partially moved values in match

This PR attempts to unify the behaviour between `let _ = PLACE`, `let _: TY = PLACE;` and `match PLACE { _ => {} }`.
The logical conclusion is that the `match` version should not check for uninitialised places nor check that borrows are still live.

The `match PLACE {}` case is handled by keeping a `FakeRead` in the unreachable fallback case to verify that `PLACE` has a legal value.

Schematically, `match PLACE { arms }` in surface rust becomes in MIR:
```rust
PlaceMention(PLACE)
match PLACE {
  // Decision tree for the explicit arms
  arms,
  // An extra fallback arm
  _ => {
    FakeRead(ForMatchedPlace, PLACE);
    unreachable
  }
}
```

`match *borrow { _ => {} }` continues to check that `*borrow` is live, but does not read the value.
`match *borrow {}` both checks that `*borrow` is live, and fake-reads the value.

Continuation of ~https://github.com/rust-lang/rust/pull/102256~ ~https://github.com/rust-lang/rust/pull/104844~

Fixes https://github.com/rust-lang/rust/issues/99180 https://github.com/rust-lang/rust/issues/53114
This commit is contained in:
bors 2023-10-27 18:51:43 +00:00
commit 59bb9505bc
64 changed files with 394 additions and 506 deletions

View file

@ -0,0 +1,17 @@
// Make sure we find these even with many checks disabled.
//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
#![allow(unreachable_code)]
#![feature(never_type)]
fn main() {
let p = {
let b = Box::new(42);
&*b as *const i32 as *const !
};
unsafe {
match *p {} //~ ERROR: entering unreachable code
}
panic!("this should never print");
}

View file

@ -0,0 +1,15 @@
error: Undefined Behavior: entering unreachable code
--> $DIR/dangling_pointer_deref_match_never.rs:LL:CC
|
LL | match *p {}
| ^^ entering unreachable code
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/dangling_pointer_deref_match_never.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View file

@ -0,0 +1,10 @@
// This should fail even without validation
//@compile-flags: -Zmiri-disable-validation
#![feature(never_type)]
#![allow(unreachable_code)]
fn main() {
let ptr: *const (i32, !) = &0i32 as *const i32 as *const _;
unsafe { match (*ptr).1 {} } //~ ERROR: entering unreachable code
}

View file

@ -0,0 +1,15 @@
error: Undefined Behavior: entering unreachable code
--> $DIR/never_match_never.rs:LL:CC
|
LL | unsafe { match (*ptr).1 {} }
| ^^^^^^^^ entering unreachable code
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/never_match_never.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error

View file

@ -0,0 +1,14 @@
// A `_` binding in a match is a nop, so we do not detect that the pointer is dangling.
//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
fn main() {
let p = {
let b = Box::new(42);
&*b as *const i32
};
unsafe {
match *p {
_ => {}
}
}
}

View file

@ -0,0 +1,17 @@
fn main() {
#[derive(Copy, Clone)]
enum Void {}
union Uninit<T: Copy> {
value: T,
uninit: (),
}
unsafe {
let x: Uninit<Void> = Uninit { uninit: () };
match x.value {
// rustc warns about un unreachable pattern,
// but is wrong in unsafe code.
#[allow(unreachable_patterns)]
_ => println!("hi from the void!"),
}
}
}

View file

@ -0,0 +1 @@
hi from the void!