discriminant reads: make semantics independent of module/crate
This commit is contained in:
parent
ee1a6f4e88
commit
af302a67fd
37 changed files with 455 additions and 116 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
25
src/tools/miri/tests/fail/match/all_variants_uninhabited.rs
Normal file
25
src/tools/miri/tests/fail/match/all_variants_uninhabited.rs
Normal 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");
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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 _ = || {
|
||||
| _____________^
|
||||
|
|
@ -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 _ = || {
|
||||
| _____________^
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
21
src/tools/miri/tests/fail/match/only_inhabited_variant.rs
Normal file
21
src/tools/miri/tests/fail/match/only_inhabited_variant.rs
Normal 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(_) => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
31
src/tools/miri/tests/fail/match/single_variant.rs
Normal file
31
src/tools/miri/tests/fail/match/single_variant.rs
Normal 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(_) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/tools/miri/tests/fail/match/single_variant.stderr
Normal file
13
src/tools/miri/tests/fail/match/single_variant.stderr
Normal 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
|
||||
|
||||
36
src/tools/miri/tests/fail/match/single_variant_uninit.rs
Normal file
36
src/tools/miri/tests/fail/match/single_variant_uninit.rs
Normal 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) => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/tools/miri/tests/fail/match/single_variant_uninit.stderr
Normal file
18
src/tools/miri/tests/fail/match/single_variant_uninit.stderr
Normal 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
|
||||
|
||||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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]]: {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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)) => { }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue