Skip to content

Commit

Permalink
Auto merge of #103208 - cjgillot:match-fake-read, r=oli-obk,RalfJung
Browse files Browse the repository at this point in the history
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 ~rust-lang/rust#102256 ~rust-lang/rust#104844

Fixes rust-lang/rust#99180 rust-lang/rust#53114
  • Loading branch information
bors committed Oct 27, 2023
2 parents d457cfc + 479374f commit 72dbdaf
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 0 deletions.
17 changes: 17 additions & 0 deletions tests/fail/dangling_pointers/dangling_pointer_deref_match_never.rs
@@ -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");
}

@@ -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

10 changes: 10 additions & 0 deletions tests/fail/never_match_never.rs
@@ -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
}
15 changes: 15 additions & 0 deletions tests/fail/never_match_never.stderr
@@ -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

14 changes: 14 additions & 0 deletions tests/pass/dangling_pointer_deref_match_underscore.rs
@@ -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 {
_ => {}
}
}
}
17 changes: 17 additions & 0 deletions tests/pass/union-uninhabited-match-underscore.rs
@@ -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!"),
}
}
}
1 change: 1 addition & 0 deletions tests/pass/union-uninhabited-match-underscore.stdout
@@ -0,0 +1 @@
hi from the void!

0 comments on commit 72dbdaf

Please sign in to comment.