Skip to content

Commit af302a6

Browse files
committed
discriminant reads: make semantics independent of module/crate
1 parent ee1a6f4 commit af302a6

File tree

37 files changed

+450
-111
lines changed

37 files changed

+450
-111
lines changed

‎compiler/rustc_mir_build/src/builder/matches/match_pair.rs‎

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -295,22 +295,18 @@ impl<'tcx> MatchPairTree<'tcx> {
295295
}
296296
}
297297

298-
PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
298+
PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => {
299299
let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
300300
cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns);
301301

302-
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
303-
i == variant_index
304-
|| !v.inhabited_predicate(cx.tcx, adt_def).instantiate(cx.tcx, args).apply(
305-
cx.tcx,
306-
cx.infcx.typing_env(cx.param_env),
307-
cx.def_id.into(),
308-
)
309-
}) && !adt_def.variant_list_has_applicable_non_exhaustive();
310-
if irrefutable {
311-
None
312-
} else {
302+
// We treat non-exhaustive enums the same independent of the crate they are
303+
// defined in, to avoid differences in the operational semantics between crates.
304+
let refutable =
305+
adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive();
306+
if refutable {
313307
Some(TestableCase::Variant { adt_def, variant_index })
308+
} else {
309+
None
314310
}
315311
}
316312

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![allow(deref_nullptr)]
2+
3+
enum Never {}
4+
5+
fn main() {
6+
unsafe {
7+
match *std::ptr::null::<Result<Never, Never>>() {
8+
//~^ ERROR: read discriminant of an uninhabited enum variant
9+
Ok(_) => {
10+
lol();
11+
}
12+
Err(_) => {
13+
wut();
14+
}
15+
}
16+
}
17+
}
18+
19+
fn lol() {
20+
println!("lol");
21+
}
22+
23+
fn wut() {
24+
println!("wut");
25+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: Undefined Behavior: read discriminant of an uninhabited enum variant
2+
--> tests/fail/match/all_variants_uninhabited.rs:LL:CC
3+
|
4+
LL | match *std::ptr::null::<Result<Never, Never>>() {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
10+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
11+
12+
error: aborting due to 1 previous error
13+

src/tools/miri/tests/fail/closures/deref-in-pattern.rs renamed to src/tools/miri/tests/fail/match/closures/deref-in-pattern.rs

File renamed without changes.

src/tools/miri/tests/fail/closures/deref-in-pattern.stderr renamed to src/tools/miri/tests/fail/match/closures/deref-in-pattern.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free)
2-
--> tests/fail/closures/deref-in-pattern.rs:LL:CC
2+
--> tests/fail/match/closures/deref-in-pattern.rs:LL:CC
33
|
44
LL | let _ = || {
55
| _____________^

src/tools/miri/tests/fail/closures/partial-pattern.rs renamed to src/tools/miri/tests/fail/match/closures/partial-pattern.rs

File renamed without changes.

src/tools/miri/tests/fail/closures/partial-pattern.stderr renamed to src/tools/miri/tests/fail/match/closures/partial-pattern.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free)
2-
--> tests/fail/closures/partial-pattern.rs:LL:CC
2+
--> tests/fail/match/closures/partial-pattern.rs:LL:CC
33
|
44
LL | let _ = || {
55
| _____________^

src/tools/miri/tests/fail/closures/uninhabited-variant.rs renamed to src/tools/miri/tests/fail/match/closures/uninhabited-variant1.rs

File renamed without changes.

src/tools/miri/tests/fail/closures/uninhabited-variant.stderr renamed to src/tools/miri/tests/fail/match/closures/uninhabited-variant1.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Undefined Behavior: read discriminant of an uninhabited enum variant
2-
--> tests/fail/closures/uninhabited-variant.rs:LL:CC
2+
--> tests/fail/match/closures/uninhabited-variant1.rs:LL:CC
33
|
44
LL | match r {
55
| ^ Undefined Behavior occurred here
@@ -8,9 +8,9 @@ LL | match r {
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
99
= note: stack backtrace:
1010
0: main::{closure#0}
11-
at tests/fail/closures/uninhabited-variant.rs:LL:CC
11+
at tests/fail/match/closures/uninhabited-variant1.rs:LL:CC
1212
1: main
13-
at tests/fail/closures/uninhabited-variant.rs:LL:CC
13+
at tests/fail/match/closures/uninhabited-variant1.rs:LL:CC
1414

1515
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
1616

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Motivated by rust-lang/rust#138961, this shows how invalid discriminants interact with
2+
// closure captures.
3+
//
4+
// Test case with only one inhabited variant, for which rustc used to not emit
5+
// a discriminant read in the first place. See: rust-lang/miri#4778
6+
#![feature(never_type)]
7+
8+
#[repr(C)]
9+
#[allow(dead_code)]
10+
enum E {
11+
V0, // discriminant: 0
12+
V1(!), // 1
13+
}
14+
15+
fn main() {
16+
assert_eq!(std::mem::size_of::<E>(), 4);
17+
18+
let val = 1u32;
19+
let ptr = (&raw const val).cast::<E>();
20+
let r = unsafe { &*ptr };
21+
let f = || {
22+
// After rust-lang/rust#138961, constructing the closure performs a reborrow of r.
23+
// Nevertheless, the discriminant is only actually inspected when the closure
24+
// is called.
25+
match r { //~ ERROR: read discriminant of an uninhabited enum variant
26+
E::V0 => {}
27+
E::V1(_) => {}
28+
}
29+
};
30+
31+
f();
32+
}

0 commit comments

Comments
 (0)