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:
commit
59bb9505bc
64 changed files with 394 additions and 506 deletions
|
|
@ -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
src/tools/miri/tests/fail/never_match_never.rs
Normal file
10
src/tools/miri/tests/fail/never_match_never.rs
Normal 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
|
||||
}
|
||||
15
src/tools/miri/tests/fail/never_match_never.stderr
Normal file
15
src/tools/miri/tests/fail/never_match_never.stderr
Normal 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
|
||||
|
||||
|
|
@ -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 {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
hi from the void!
|
||||
Loading…
Add table
Add a link
Reference in a new issue