discriminant reads: make semantics independent of module/crate

This commit is contained in:
Maja Kądziołka 2026-01-04 19:34:37 +01:00
parent ee1a6f4e88
commit af302a67fd
37 changed files with 455 additions and 116 deletions

View file

@ -295,22 +295,18 @@ impl<'tcx> MatchPairTree<'tcx> {
}
}
PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => {
let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns);
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
i == variant_index
|| !v.inhabited_predicate(cx.tcx, adt_def).instantiate(cx.tcx, args).apply(
cx.tcx,
cx.infcx.typing_env(cx.param_env),
cx.def_id.into(),
)
}) && !adt_def.variant_list_has_applicable_non_exhaustive();
if irrefutable {
None
} else {
// We treat non-exhaustive enums the same independent of the crate they are
// defined in, to avoid differences in the operational semantics between crates.
let refutable =
adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive();
if refutable {
Some(TestableCase::Variant { adt_def, variant_index })
} else {
None
}
}

View file

@ -0,0 +1,25 @@
#![allow(deref_nullptr)]
enum Never {}
fn main() {
unsafe {
match *std::ptr::null::<Result<Never, Never>>() {
//~^ ERROR: read discriminant of an uninhabited enum variant
Ok(_) => {
lol();
}
Err(_) => {
wut();
}
}
}
}
fn lol() {
println!("lol");
}
fn wut() {
println!("wut");
}

View file

@ -0,0 +1,13 @@
error: Undefined Behavior: read discriminant of an uninhabited enum variant
--> tests/fail/match/all_variants_uninhabited.rs:LL:CC
|
LL | match *std::ptr::null::<Result<Never, Never>>() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= 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: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -1,5 +1,5 @@
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free)
--> tests/fail/closures/deref-in-pattern.rs:LL:CC
--> tests/fail/match/closures/deref-in-pattern.rs:LL:CC
|
LL | let _ = || {
| _____________^

View file

@ -1,5 +1,5 @@
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free)
--> tests/fail/closures/partial-pattern.rs:LL:CC
--> tests/fail/match/closures/partial-pattern.rs:LL:CC
|
LL | let _ = || {
| _____________^

View file

@ -1,5 +1,5 @@
error: Undefined Behavior: read discriminant of an uninhabited enum variant
--> tests/fail/closures/uninhabited-variant.rs:LL:CC
--> tests/fail/match/closures/uninhabited-variant1.rs:LL:CC
|
LL | match r {
| ^ Undefined Behavior occurred here
@ -8,9 +8,9 @@ LL | match r {
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: stack backtrace:
0: main::{closure#0}
at tests/fail/closures/uninhabited-variant.rs:LL:CC
at tests/fail/match/closures/uninhabited-variant1.rs:LL:CC
1: main
at tests/fail/closures/uninhabited-variant.rs:LL:CC
at tests/fail/match/closures/uninhabited-variant1.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View file

@ -0,0 +1,32 @@
// Motivated by rust-lang/rust#138961, this shows how invalid discriminants interact with
// closure captures.
//
// Test case with only one inhabited variant, for which rustc used to not emit
// a discriminant read in the first place. See: rust-lang/miri#4778
#![feature(never_type)]
#[repr(C)]
#[allow(dead_code)]
enum E {
V0, // discriminant: 0
V1(!), // 1
}
fn main() {
assert_eq!(std::mem::size_of::<E>(), 4);
let val = 1u32;
let ptr = (&raw const val).cast::<E>();
let r = unsafe { &*ptr };
let f = || {
// After rust-lang/rust#138961, constructing the closure performs a reborrow of r.
// Nevertheless, the discriminant is only actually inspected when the closure
// is called.
match r { //~ ERROR: read discriminant of an uninhabited enum variant
E::V0 => {}
E::V1(_) => {}
}
};
f();
}

View file

@ -0,0 +1,18 @@
error: Undefined Behavior: read discriminant of an uninhabited enum variant
--> tests/fail/match/closures/uninhabited-variant2.rs:LL:CC
|
LL | match r {
| ^ Undefined Behavior occurred here
|
= 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: stack backtrace:
0: main::{closure#0}
at tests/fail/match/closures/uninhabited-variant2.rs:LL:CC
1: main
at tests/fail/match/closures/uninhabited-variant2.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,21 @@
// rust-lang/miri#4778
#![feature(never_type)]
#[repr(C)]
#[allow(dead_code)]
enum E {
V0, // discriminant: 0
V1(!), // 1
}
fn main() {
assert_eq!(std::mem::size_of::<E>(), 4);
let val = 1u32;
let ptr = (&raw const val).cast::<E>();
let r = unsafe { &*ptr };
match r { //~ ERROR: read discriminant of an uninhabited enum variant
E::V0 => {}
E::V1(_) => {}
}
}

View file

@ -0,0 +1,13 @@
error: Undefined Behavior: read discriminant of an uninhabited enum variant
--> tests/fail/match/only_inhabited_variant.rs:LL:CC
|
LL | match r {
| ^ Undefined Behavior occurred here
|
= 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: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,31 @@
// Ideally, this would be UB regardless of #[non_exhaustive]. For now,
// at least the semantics don't depend on the crate you're in.
//
// See: rust-lang/rust#147722
#![allow(dead_code)]
#[repr(u8)]
enum Exhaustive {
A(u8) = 42,
}
#[repr(u8)]
#[non_exhaustive]
enum NonExhaustive {
A(u8) = 42,
}
fn main() {
unsafe {
let x: &[u8; 2] = &[21, 37];
let y: &Exhaustive = std::mem::transmute(x);
match y {
Exhaustive::A(_) => {},
}
let y: &NonExhaustive = std::mem::transmute(x);
match y { //~ ERROR: enum value has invalid tag
NonExhaustive::A(_) => {},
}
}
}

View file

@ -0,0 +1,13 @@
error: Undefined Behavior: enum value has invalid tag: 0x15
--> tests/fail/match/single_variant.rs:LL:CC
|
LL | match y {
| ^ Undefined Behavior occurred here
|
= 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: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,36 @@
// Ideally, this would be UB regardless of #[non_exhaustive]. For now,
// at least the semantics don't depend on the crate you're in.
//
// See: rust-lang/rust#147722
#![allow(dead_code)]
#![allow(unreachable_patterns)]
#[repr(u8)]
enum Exhaustive {
A(u8) = 0,
}
#[repr(u8)]
#[non_exhaustive]
enum NonExhaustive {
A(u8) = 0,
}
use std::mem::MaybeUninit;
fn main() {
let buffer: [MaybeUninit<u8>; 2] = [MaybeUninit::uninit(), MaybeUninit::new(0u8)];
let exh: *const Exhaustive = (&raw const buffer).cast();
let nexh: *const NonExhaustive = (&raw const buffer).cast();
unsafe {
match *exh {
Exhaustive::A(ref _val) => {}
_ => {}
}
match *nexh { //~ ERROR: memory is uninitialized
NonExhaustive::A(ref _val) => {}
_ => {}
}
}
}

View file

@ -0,0 +1,18 @@
error: Undefined Behavior: reading memory at ALLOC[0x0..0x1], but memory is uninitialized at [0x0..0x1], and this operation requires initialized memory
--> tests/fail/match/single_variant_uninit.rs:LL:CC
|
LL | match *nexh {
| ^^^^^ Undefined Behavior occurred here
|
= 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
Uninitialized memory occurred at ALLOC[0x0..0x1], in this allocation:
ALLOC (stack variable, size: 2, align: 1) {
__ 00 │ ░.
}
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -11,6 +11,8 @@ pub enum Never {}
pub fn make_unmake_result_never(x: i32) -> i32 {
// CHECK-LABEL: define i32 @make_unmake_result_never(i32{{( signext)?}} %x)
// CHECK: start:
// CHECK-NEXT: br label %[[next:bb.*]]
// CHECK: [[next]]:
// CHECK-NEXT: ret i32 %x
let y: Result<i32, Never> = Ok(x);
@ -22,6 +24,8 @@ pub fn make_unmake_result_never(x: i32) -> i32 {
pub fn extract_control_flow_never(x: ControlFlow<&str, Never>) -> &str {
// CHECK-LABEL: define { ptr, i64 } @extract_control_flow_never(ptr align 1 %x.0, i64 %x.1)
// CHECK: start:
// CHECK-NEXT: br label %[[next:bb.*]]
// CHECK: [[next]]:
// CHECK-NEXT: %[[P0:.+]] = insertvalue { ptr, i64 } poison, ptr %x.0, 0
// CHECK-NEXT: %[[P1:.+]] = insertvalue { ptr, i64 } %[[P0]], i64 %x.1, 1
// CHECK-NEXT: ret { ptr, i64 } %[[P1]]

View file

@ -13,17 +13,17 @@ fn opt1(_1: &Result<u32, Void>) -> &u32 {
bb0: {
PlaceMention(_1);
falseEdge -> [real: bb4, imaginary: bb1];
_2 = discriminant((*_1));
switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
_2 = discriminant((*_1));
switchInt(move _2) -> [1: bb3, otherwise: bb2];
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb2: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
falseEdge -> [real: bb4, imaginary: bb3];
}
bb3: {

View file

@ -11,10 +11,25 @@ fn opt2(_1: &Result<u32, Void>) -> &u32 {
bb0: {
PlaceMention(_1);
_2 = discriminant((*_1));
switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1];
}
bb1: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb2: {
StorageLive(_3);
_3 = &(((*_1) as Ok).0: u32);
_0 = &(*_3);
StorageDead(_3);
return;
}
bb3: {
FakeRead(ForMatchedPlace(None), (((*_1) as Err).0: Void));
unreachable;
}
}

View file

@ -12,19 +12,24 @@ fn opt3(_1: &Result<u32, Void>) -> &u32 {
bb0: {
PlaceMention(_1);
_2 = discriminant((*_1));
switchInt(move _2) -> [1: bb2, otherwise: bb1];
switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
}
bb1: {
StorageLive(_3);
_3 = &(((*_1) as Ok).0: u32);
_0 = &(*_3);
StorageDead(_3);
return;
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}
bb2: {
FakeRead(ForMatchedPlace(None), (((*_1) as Err).0: Void));
unreachable;
}
bb3: {
StorageLive(_3);
_3 = &(((*_1) as Ok).0: u32);
_0 = &(*_3);
StorageDead(_3);
return;
}
}

View file

@ -16,8 +16,10 @@
debug residual => _6;
scope 2 {
scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) {
let _14: i32;
let mut _15: i32;
let mut _14: isize;
let _15: i32;
let mut _16: i32;
let mut _17: bool;
scope 9 {
scope 10 (inlined <i32 as From<i32>>::from) {
}
@ -74,10 +76,17 @@
StorageLive(_8);
_8 = copy _6;
StorageLive(_14);
_14 = move ((_8 as Err).0: i32);
StorageLive(_15);
_15 = move _14;
_0 = Result::<i32, i32>::Err(move _15);
StorageLive(_17);
_14 = discriminant(_8);
_17 = Eq(copy _14, const 1_isize);
assume(move _17);
_15 = move ((_8 as Err).0: i32);
StorageLive(_16);
_16 = move _15;
_0 = Result::<i32, i32>::Err(move _16);
StorageDead(_16);
StorageDead(_17);
StorageDead(_15);
StorageDead(_14);
StorageDead(_8);

View file

@ -16,8 +16,10 @@
debug residual => _6;
scope 2 {
scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) {
let _14: i32;
let mut _15: i32;
let mut _14: isize;
let _15: i32;
let mut _16: i32;
let mut _17: bool;
scope 9 {
scope 10 (inlined <i32 as From<i32>>::from) {
}
@ -74,10 +76,17 @@
StorageLive(_8);
_8 = copy _6;
StorageLive(_14);
_14 = move ((_8 as Err).0: i32);
StorageLive(_15);
_15 = move _14;
_0 = Result::<i32, i32>::Err(move _15);
StorageLive(_17);
_14 = discriminant(_8);
_17 = Eq(copy _14, const 1_isize);
assume(move _17);
_15 = move ((_8 as Err).0: i32);
StorageLive(_16);
_16 = move _15;
_0 = Result::<i32, i32>::Err(move _16);
StorageDead(_16);
StorageDead(_17);
StorageDead(_15);
StorageDead(_14);
StorageDead(_8);

View file

@ -3,56 +3,66 @@
fn map_via_question_mark(_1: Option<i32>) -> Option<i32> {
debug x => _1;
let mut _0: std::option::Option<i32>;
let mut _4: std::ops::ControlFlow<std::option::Option<std::convert::Infallible>, i32>;
let _5: i32;
let mut _6: i32;
let mut _4: std::option::Option<std::convert::Infallible>;
let mut _7: std::ops::ControlFlow<std::option::Option<std::convert::Infallible>, i32>;
let _8: i32;
let mut _9: i32;
scope 1 {
debug residual => const Option::<Infallible>::None;
scope 2 {
scope 7 (inlined <Option<i32> as FromResidual<Option<Infallible>>>::from_residual) {
let mut _3: isize;
let mut _5: bool;
}
}
}
scope 3 {
debug val => _5;
debug val => _8;
scope 4 {
}
}
scope 5 (inlined <Option<i32> as Try>::branch) {
let mut _2: isize;
let _3: i32;
let _6: i32;
scope 6 {
}
}
bb0: {
StorageLive(_6);
StorageLive(_4);
StorageLive(_9);
StorageLive(_7);
StorageLive(_2);
StorageLive(_3);
StorageLive(_6);
_2 = discriminant(_1);
switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4];
}
bb1: {
StorageDead(_3);
StorageDead(_2);
_0 = const Option::<i32>::None;
StorageDead(_6);
StorageDead(_4);
StorageDead(_2);
StorageLive(_3);
StorageLive(_5);
_3 = discriminant(_4);
_5 = Eq(copy _3, const 0_isize);
assume(move _5);
_0 = const Option::<i32>::None;
StorageDead(_5);
StorageDead(_3);
StorageDead(_9);
StorageDead(_7);
goto -> bb3;
}
bb2: {
_3 = copy ((_1 as Some).0: i32);
_4 = ControlFlow::<Option<Infallible>, i32>::Continue(copy _3);
StorageDead(_3);
StorageDead(_2);
_5 = copy ((_4 as Continue).0: i32);
_6 = Add(copy _5, const 1_i32);
_0 = Option::<i32>::Some(move _6);
_6 = copy ((_1 as Some).0: i32);
_7 = ControlFlow::<Option<Infallible>, i32>::Continue(copy _6);
StorageDead(_6);
StorageDead(_4);
StorageDead(_2);
_8 = copy ((_7 as Continue).0: i32);
_9 = Add(copy _8, const 1_i32);
_0 = Option::<i32>::Some(move _9);
StorageDead(_9);
StorageDead(_7);
goto -> bb3;
}

View file

@ -12,7 +12,9 @@
debug residual => _4;
scope 2 {
scope 8 (inlined #[track_caller] <Result<i32, i32> as FromResidual<Result<Infallible, i32>>>::from_residual) {
let _10: i32;
let mut _10: isize;
let _11: i32;
let mut _12: bool;
scope 9 {
scope 10 (inlined <i32 as From<i32>>::from) {
}
@ -58,8 +60,15 @@
bb3: {
_4 = copy ((_2 as Break).0: std::result::Result<std::convert::Infallible, i32>);
_10 = copy ((_4 as Err).0: i32);
_0 = Result::<i32, i32>::Err(copy _10);
StorageLive(_10);
StorageLive(_12);
_10 = discriminant(_4);
_12 = Eq(copy _10, const 1_isize);
assume(move _12);
_11 = copy ((_4 as Err).0: i32);
_0 = Result::<i32, i32>::Err(copy _11);
StorageDead(_12);
StorageDead(_10);
StorageDead(_2);
return;
}

View file

@ -19,19 +19,23 @@
bb1: {
_2 = discriminant(_1);
- switchInt(move _2) -> [1: bb3, otherwise: bb2];
+ _5 = Ne(copy _2, const 1_isize);
- switchInt(move _2) -> [0: bb3, 1: bb4, otherwise: bb2];
+ _5 = Eq(copy _2, const 0_isize);
+ assume(move _5);
+ goto -> bb2;
+ goto -> bb3;
}
bb2: {
unreachable;
}
bb3: {
_0 = const ();
StorageDead(_1);
return;
}
bb3: {
bb4: {
- StorageLive(_3);
- _3 = move ((_1 as Some).0: Empty);
- StorageLive(_4);

View file

@ -19,19 +19,23 @@
bb1: {
_2 = discriminant(_1);
- switchInt(move _2) -> [1: bb3, otherwise: bb2];
+ _5 = Ne(copy _2, const 1_isize);
- switchInt(move _2) -> [0: bb3, 1: bb4, otherwise: bb2];
+ _5 = Eq(copy _2, const 0_isize);
+ assume(move _5);
+ goto -> bb2;
+ goto -> bb3;
}
bb2: {
unreachable;
}
bb3: {
_0 = const ();
StorageDead(_1);
return;
}
bb3: {
bb4: {
- StorageLive(_3);
- _3 = move ((_1 as Some).0: Empty);
- StorageLive(_4);

View file

@ -45,7 +45,7 @@ fn as_match() {
// CHECK: bb0: {
// CHECK: {{_.*}} = empty()
// CHECK: bb1: {
// CHECK: [[eq:_.*]] = Ne({{.*}}, const 1_isize);
// CHECK: [[eq:_.*]] = Eq({{.*}}, const 0_isize);
// CHECK-NEXT: assume(move [[eq]]);
// CHECK-NEXT: goto -> [[return:bb.*]];
// CHECK: [[return]]: {

View file

@ -14,40 +14,40 @@
StorageLive(_2);
_2 = Test1::C;
_3 = discriminant(_2);
- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1];
+ switchInt(move _3) -> [0: bb5, 1: bb5, 2: bb1, otherwise: bb5];
- switchInt(move _3) -> [0: bb4, 1: bb3, 2: bb2, otherwise: bb1];
+ switchInt(move _3) -> [0: bb1, 1: bb1, 2: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_5);
_5 = const "C";
_1 = &(*_5);
StorageDead(_5);
goto -> bb4;
goto -> bb5;
}
bb2: {
bb3: {
StorageLive(_4);
_4 = const "B(Empty)";
_1 = &(*_4);
StorageDead(_4);
goto -> bb4;
}
bb3: {
_1 = const "A(Empty)";
goto -> bb4;
goto -> bb5;
}
bb4: {
_1 = const "A(Empty)";
goto -> bb5;
}
bb5: {
StorageDead(_2);
StorageDead(_1);
_0 = const ();
return;
+ }
+
+ bb5: {
+ unreachable;
}
}

View file

@ -14,40 +14,40 @@
StorageLive(_2);
_2 = Test1::C;
_3 = discriminant(_2);
- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1];
+ switchInt(move _3) -> [0: bb5, 1: bb5, 2: bb1, otherwise: bb5];
- switchInt(move _3) -> [0: bb4, 1: bb3, 2: bb2, otherwise: bb1];
+ switchInt(move _3) -> [0: bb1, 1: bb1, 2: bb2, otherwise: bb1];
}
bb1: {
unreachable;
}
bb2: {
StorageLive(_5);
_5 = const "C";
_1 = &(*_5);
StorageDead(_5);
goto -> bb4;
goto -> bb5;
}
bb2: {
bb3: {
StorageLive(_4);
_4 = const "B(Empty)";
_1 = &(*_4);
StorageDead(_4);
goto -> bb4;
}
bb3: {
_1 = const "A(Empty)";
goto -> bb4;
goto -> bb5;
}
bb4: {
_1 = const "A(Empty)";
goto -> bb5;
}
bb5: {
StorageDead(_2);
StorageDead(_1);
_0 = const ();
return;
+ }
+
+ bb5: {
+ unreachable;
}
}

View file

@ -17,9 +17,11 @@ fn both_inhabited(x: &mut Result<String, String>) {
};
}
// this used to be accepted, even though it shouldn't
fn ref_uninhabited(x: &mut Result<Never, String>) {
match x {
&mut Ok(ref mut y) => match x {
//~^ ERROR: cannot use `*x` because it was mutably borrowed
&mut Err(ref mut z) => {
let _y = y;
let _z = z;

View file

@ -9,6 +9,17 @@ LL | &mut Ok(ref mut y) => match x {
LL | let _y = y;
| - borrow later used here
error: aborting due to 1 previous error
error[E0503]: cannot use `*x` because it was mutably borrowed
--> $DIR/borrowck-uninhabited.rs:23:37
|
LL | &mut Ok(ref mut y) => match x {
| --------- ^ use of borrowed `x.0`
| |
| `x.0` is borrowed here
...
LL | let _y = y;
| - borrow later used here
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0503`.

View file

@ -17,7 +17,7 @@ fn test1(thefoo: Foo<(Box<u64>, Box<u64>)>) {
Foo::Bar((a, _)) => { }
}
match thefoo {
match thefoo { //~ ERROR: use of partially moved value: `thefoo`
Foo::Bar((_, a)) => { }
}
}
@ -38,7 +38,7 @@ fn test3(thefoo: Foo<(Box<u64>, Box<u64>)>) {
Foo::Bar((a, _)) => { }
Foo::Qux(_) => { }
}
match thefoo {
match thefoo { //~ ERROR: use of partially moved value: `thefoo`
Foo::Bar((_, a)) => { }
}
}

View file

@ -1,3 +1,18 @@
error[E0382]: use of partially moved value: `thefoo`
--> $DIR/uninhabited-granular-moves.rs:20:11
|
LL | Foo::Bar((a, _)) => { }
| - value partially moved here
...
LL | match thefoo {
| ^^^^^^ value used here after partial move
|
= note: partial move occurs because value has type `Box<u64>`, which does not implement the `Copy` trait
help: borrow this binding in the pattern to avoid moving the value
|
LL | Foo::Bar((ref a, _)) => { }
| +++
error[E0382]: use of partially moved value: `thefoo`
--> $DIR/uninhabited-granular-moves.rs:30:11
|
@ -13,6 +28,21 @@ help: borrow this binding in the pattern to avoid moving the value
LL | Foo::Bar((ref a, _)) => { }
| +++
error: aborting due to 1 previous error
error[E0382]: use of partially moved value: `thefoo`
--> $DIR/uninhabited-granular-moves.rs:41:11
|
LL | Foo::Bar((a, _)) => { }
| - value partially moved here
...
LL | match thefoo {
| ^^^^^^ value used here after partial move
|
= note: partial move occurs because value has type `Box<u64>`, which does not implement the `Copy` trait
help: borrow this binding in the pattern to avoid moving the value
|
LL | Foo::Bar((ref a, _)) => { }
| +++
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0382`.

View file

@ -14,11 +14,6 @@ enum Local {
Variant(u32),
}
#[non_exhaustive]
enum LocalNonExhaustive {
Variant(u32),
}
fn main() {
let mut x = ExhaustiveMonovariant::Variant(1);
let y = &mut x;
@ -34,11 +29,4 @@ fn main() {
_ => {},
}
drop(y);
let mut x = LocalNonExhaustive::Variant(1);
let y = &mut x;
match x {
LocalNonExhaustive::Variant(_) => {},
_ => {},
}
drop(y);
}

View file

@ -6,6 +6,11 @@ extern crate monovariants;
use monovariants::NonExhaustiveMonovariant;
#[non_exhaustive]
enum LocalNonExhaustive {
Variant(u32),
}
fn main() {
let mut x = NonExhaustiveMonovariant::Variant(1);
let y = &mut x;
@ -15,4 +20,11 @@ fn main() {
_ => {},
}
drop(y);
let mut x = LocalNonExhaustive::Variant(1);
let y = &mut x;
match x { //~ ERROR cannot use `x` because it was mutably borrowed
LocalNonExhaustive::Variant(_) => {},
_ => {},
}
drop(y);
}

View file

@ -1,5 +1,5 @@
error[E0503]: cannot use `x` because it was mutably borrowed
--> $DIR/borrowck-non-exhaustive.rs:12:11
--> $DIR/borrowck-non-exhaustive.rs:17:11
|
LL | let y = &mut x;
| ------ `x` is borrowed here
@ -9,6 +9,17 @@ LL | match x {
LL | drop(y);
| - borrow later used here
error: aborting due to 1 previous error
error[E0503]: cannot use `x` because it was mutably borrowed
--> $DIR/borrowck-non-exhaustive.rs:25:11
|
LL | let y = &mut x;
| ------ `x` is borrowed here
LL | match x {
| ^ use of borrowed `x`
...
LL | drop(y);
| - borrow later used here
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0503`.