From e69508e5cd1bf61be72714032a15db2ed8b6790e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Sun, 4 Jan 2026 04:22:55 +0100 Subject: [PATCH 1/3] Make matching on single-variant enums capture the entire enum --- .../rustc_hir_typeck/src/expr_use_visitor.rs | 12 +-- .../capture-enum-field.rs | 3 +- .../capture-enum-field.stderr | 37 ++++++++ .../2229_closure_analysis/capture-enums.rs | 5 +- .../capture-enums.stderr | 7 +- .../match/match-edge-cases_2.rs | 17 ++-- .../match/match-edge-cases_2.stderr | 63 +++++++++++++- .../match/non-exhaustive-match.rs | 11 ++- .../match/non-exhaustive-match.stderr | 19 +++- .../match/partial-move-drop-order.run.stdout | 2 +- .../match/partial-move.rs | 7 +- .../match/partial-move.stderr | 54 +----------- .../match/patterns-capture-analysis.rs | 18 ++-- .../match/patterns-capture-analysis.stderr | 86 ++++++++++++------- 14 files changed, 222 insertions(+), 119 deletions(-) create mode 100644 tests/ui/closures/2229_closure_analysis/capture-enum-field.stderr diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 9587b774c4ff2..9042ccde4e77f 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -912,7 +912,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx _ => { // Otherwise, this is a struct/enum variant, and so it's // only a read if we need to read the discriminant. - if self.is_multivariant_adt(place.place.ty(), *span) { + if self.is_enum(place.place.ty(), *span) { read_discriminant(); } } @@ -931,7 +931,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx read_discriminant(); } PatKind::Struct(..) | PatKind::TupleStruct(..) => { - if self.is_multivariant_adt(place.place.ty(), pat.span) { + if self.is_enum(place.place.ty(), pat.span) { read_discriminant(); } } @@ -1842,14 +1842,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } - /// Checks whether a type has multiple variants, and therefore, whether a - /// read of the discriminant might be necessary. #[instrument(skip(self, span), level = "debug")] - fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool { + fn is_enum(&self, ty: Ty<'tcx>, span: Span) -> bool { if let ty::Adt(def, _) = self.cx.structurally_resolve_type(span, ty).kind() { - // We treat non-exhaustive enums the same independent of the crate they are - // defined in, to avoid differences in the operational semantics between crates. - def.variants().len() > 1 || def.is_variant_list_non_exhaustive() + def.is_enum() } else { false } diff --git a/tests/ui/closures/2229_closure_analysis/capture-enum-field.rs b/tests/ui/closures/2229_closure_analysis/capture-enum-field.rs index 6e3da1236db6f..ba7096fd6aeb4 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-enum-field.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-enum-field.rs @@ -1,5 +1,4 @@ //@ edition:2021 -//@ run-pass #[derive(Debug, PartialEq, Eq)] pub enum Color { @@ -13,10 +12,12 @@ fn main() { *r = v; }; let mut green = |v| { + //~^ ERROR: cannot borrow `color` as mutable let Color::RGB(_, ref mut g, _) = color; *g = v; }; let mut blue = |v| { + //~^ ERROR: cannot borrow `color` as mutable let Color::RGB(_, _, ref mut b) = color; *b = v; }; diff --git a/tests/ui/closures/2229_closure_analysis/capture-enum-field.stderr b/tests/ui/closures/2229_closure_analysis/capture-enum-field.stderr new file mode 100644 index 0000000000000..1a6ccb2278800 --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/capture-enum-field.stderr @@ -0,0 +1,37 @@ +error[E0499]: cannot borrow `color` as mutable more than once at a time + --> $DIR/capture-enum-field.rs:14:21 + | +LL | let mut red = |v| { + | --- first mutable borrow occurs here +LL | let Color::RGB(ref mut r, _, _) = color; + | ----- first borrow occurs due to use of `color` in closure +... +LL | let mut green = |v| { + | ^^^ second mutable borrow occurs here +LL | +LL | let Color::RGB(_, ref mut g, _) = color; + | ----- second borrow occurs due to use of `color` in closure +... +LL | red(1); + | --- first borrow later used here + +error[E0499]: cannot borrow `color` as mutable more than once at a time + --> $DIR/capture-enum-field.rs:19:20 + | +LL | let mut red = |v| { + | --- first mutable borrow occurs here +LL | let Color::RGB(ref mut r, _, _) = color; + | ----- first borrow occurs due to use of `color` in closure +... +LL | let mut blue = |v| { + | ^^^ second mutable borrow occurs here +LL | +LL | let Color::RGB(_, _, ref mut b) = color; + | ----- second borrow occurs due to use of `color` in closure +... +LL | red(1); + | --- first borrow later used here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/closures/2229_closure_analysis/capture-enums.rs b/tests/ui/closures/2229_closure_analysis/capture-enums.rs index 36b98351854bf..e6ad2bc2bcd0e 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-enums.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-enums.rs @@ -50,8 +50,9 @@ fn single_variant_enum() { //~^ ERROR First Pass analysis includes: //~| ERROR Min Capture analysis includes: let SingleVariant::Point(_, _, str) = point; - //~^ NOTE: Capturing point[(2, 0)] -> ByValue - //~| NOTE: Min Capture point[(2, 0)] -> ByValue + //~^ NOTE: Capturing point[] -> Immutable + //~| NOTE: Capturing point[(2, 0)] -> ByValue + //~| NOTE: Min Capture point[] -> ByValue println!("{}", str); }; diff --git a/tests/ui/closures/2229_closure_analysis/capture-enums.stderr b/tests/ui/closures/2229_closure_analysis/capture-enums.stderr index 2f49c8668f85c..1716ebd46be94 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-enums.stderr +++ b/tests/ui/closures/2229_closure_analysis/capture-enums.stderr @@ -74,6 +74,11 @@ LL | | println!("{}", str); LL | | }; | |_____^ | +note: Capturing point[] -> Immutable + --> $DIR/capture-enums.rs:52:47 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ note: Capturing point[(2, 0)] -> ByValue --> $DIR/capture-enums.rs:52:47 | @@ -92,7 +97,7 @@ LL | | println!("{}", str); LL | | }; | |_____^ | -note: Min Capture point[(2, 0)] -> ByValue +note: Min Capture point[] -> ByValue --> $DIR/capture-enums.rs:52:47 | LL | let SingleVariant::Point(_, _, str) = point; diff --git a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs index 81013e5b2cd16..ab71bd35ce2c7 100644 --- a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs +++ b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs @@ -13,7 +13,7 @@ struct TestStruct { fn edge_case_if() { let sv = SingleVariant::A; let condition = true; - // sv should not be captured as it is a SingleVariant + // sv should be captured, regardless of being SingleVariant let _a = || { match sv { SingleVariant::A if condition => (), @@ -21,6 +21,7 @@ fn edge_case_if() { } }; let mut mut_sv = sv; + //~^ ERROR: cannot move out of `sv` because it is borrowed _a(); // ts should be captured @@ -48,7 +49,7 @@ struct TStruct(u32, u32); struct SStruct { a: u32, b: u32 } -// Destructuring a unit struct should not capture it +// Matching on a unit struct should not capture it fn match_unit_struct(mut x: (Unit, u32)) { let r = &mut x.0; let _ = || { @@ -59,10 +60,12 @@ fn match_unit_struct(mut x: (Unit, u32)) { let _ = *r; } -// The same is true for an equivalent enum +// However, in the equivalent test case with a single-variant unit enum, +// the unit enum *should* be captured. fn match_unit_enum(mut x: (SingleVariant, u32)) { let r = &mut x.0; let _ = || { + //~^ ERROR: cannot borrow `x.0` as immutable let (SingleVariant::A, a) = x; a }; @@ -70,7 +73,7 @@ fn match_unit_enum(mut x: (SingleVariant, u32)) { let _ = *r; } -// More generally, destructuring a struct should only capture the fields being touched +// Matching on a struct should only capture the fields being touched fn match_struct(mut x: SStruct) { let r = &mut x.a; let _ = || { @@ -91,10 +94,13 @@ fn match_tuple_struct(mut x: TStruct) { let _ = *r; } -// The same is true for an equivalent enum as well +// However, matching on a single-variant enum with fields inside +// does not behave like a struct: the entire enum is captured, as +// the (zero-size) discriminant must be read. fn match_singleton(mut x: SSingle) { let SSingle::A { a: ref mut r, .. } = x; let _ = || { + //~^ ERROR: cannot borrow `x` as immutable let SSingle::A { b, .. } = x; b }; @@ -105,6 +111,7 @@ fn match_singleton(mut x: SSingle) { fn match_tuple_singleton(mut x: TSingle) { let TSingle::A(ref mut r, _) = x; let _ = || { + //~^ ERROR: cannot borrow `x` as immutable let TSingle::A(_, a) = x; a }; diff --git a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr index 3f5fe9eda423f..d20b86bb13272 100644 --- a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr @@ -1,5 +1,19 @@ +error[E0505]: cannot move out of `sv` because it is borrowed + --> $DIR/match-edge-cases_2.rs:23:22 + | +LL | let _a = || { + | -- borrow of `sv` occurs here +LL | match sv { + | -- borrow occurs due to use in closure +... +LL | let mut mut_sv = sv; + | ^^ move out of `sv` occurs here +LL | +LL | _a(); + | -- borrow later used here + error[E0505]: cannot move out of `ts` because it is borrowed - --> $DIR/match-edge-cases_2.rs:32:22 + --> $DIR/match-edge-cases_2.rs:33:22 | LL | let _b = || { match ts { | -- -- borrow occurs due to use in closure @@ -12,6 +26,49 @@ LL | LL | _b(); | -- borrow later used here -error: aborting due to 1 previous error +error[E0502]: cannot borrow `x.0` as immutable because it is also borrowed as mutable + --> $DIR/match-edge-cases_2.rs:67:13 + | +LL | let r = &mut x.0; + | -------- mutable borrow occurs here +LL | let _ = || { + | ^^ immutable borrow occurs here +LL | +LL | let (SingleVariant::A, a) = x; + | - second borrow occurs due to use of `x.0` in closure +... +LL | let _ = *r; + | -- mutable borrow later used here + +error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable + --> $DIR/match-edge-cases_2.rs:102:13 + | +LL | let SSingle::A { a: ref mut r, .. } = x; + | --------- mutable borrow occurs here +LL | let _ = || { + | ^^ immutable borrow occurs here +LL | +LL | let SSingle::A { b, .. } = x; + | - second borrow occurs due to use of `x` in closure +... +LL | let _ = *r; + | -- mutable borrow later used here + +error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable + --> $DIR/match-edge-cases_2.rs:113:13 + | +LL | let TSingle::A(ref mut r, _) = x; + | --------- mutable borrow occurs here +LL | let _ = || { + | ^^ immutable borrow occurs here +LL | +LL | let TSingle::A(_, a) = x; + | - second borrow occurs due to use of `x` in closure +... +LL | let _ = *r; + | -- mutable borrow later used here + +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0505`. +Some errors have detailed explanations: E0502, E0505. +For more information about an error, try `rustc --explain E0502`. diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs index f47d70b52f200..9d73c23521a7e 100644 --- a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs +++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs @@ -36,21 +36,24 @@ fn main() { //~^ ERROR: non-exhaustive patterns: `_` not covered [E0004] let _f = || { match e2 { E2::A => (), E2::B => (), _ => () } }; - // non-exhaustive enums should always be captured, regardless if they - // are defined in the current crate: + // Matching on an enum should always cause the value to be captured, regardless of the enum + // being being non-exhaustive, single-variant, or defined in this crate. This used to vary! + + // non-exhaustive SingleVariant defined in this crate: let _c = || { match l2 { L2::C => (), _ => () } }; let mut mut_l2 = l2; //~^ ERROR: cannot move out of `l2` because it is borrowed _c(); - // ...or in another crate: + // non-exhaustive SingleVariant defined in another crate: let _g = || { match e3 { E3::C => (), _ => () } }; let mut mut_e3 = e3; //~^ ERROR: cannot move out of `e3` because it is borrowed _g(); - // e4 should not be captured as it is a SingleVariant + // exhaustive SingleVariant (defined in another crate) let _h = || { match e4 { E4::D => (), _ => () } }; let mut mut_e4 = e4; + //~^ ERROR: cannot move out of `e4` because it is borrowed _h(); } diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr index e34d1889803a9..4f7da14622772 100644 --- a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr @@ -53,7 +53,7 @@ LL | let _e = || { match e2 { E2::A => (), E2::B => (), _ => todo!() } }; | ++++++++++++++ error[E0505]: cannot move out of `l2` because it is borrowed - --> $DIR/non-exhaustive-match.rs:42:22 + --> $DIR/non-exhaustive-match.rs:44:22 | LL | let _c = || { match l2 { L2::C => (), _ => () } }; | -- -- borrow occurs due to use in closure @@ -66,7 +66,7 @@ LL | _c(); | -- borrow later used here error[E0505]: cannot move out of `e3` because it is borrowed - --> $DIR/non-exhaustive-match.rs:48:22 + --> $DIR/non-exhaustive-match.rs:50:22 | LL | let _g = || { match e3 { E3::C => (), _ => () } }; | -- -- borrow occurs due to use in closure @@ -78,7 +78,20 @@ LL | LL | _g(); | -- borrow later used here -error: aborting due to 5 previous errors +error[E0505]: cannot move out of `e4` because it is borrowed + --> $DIR/non-exhaustive-match.rs:56:22 + | +LL | let _h = || { match e4 { E4::D => (), _ => () } }; + | -- -- borrow occurs due to use in closure + | | + | borrow of `e4` occurs here +LL | let mut mut_e4 = e4; + | ^^ move out of `e4` occurs here +LL | +LL | _h(); + | -- borrow later used here + +error: aborting due to 6 previous errors Some errors have detailed explanations: E0004, E0505. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/closures/2229_closure_analysis/match/partial-move-drop-order.run.stdout b/tests/ui/closures/2229_closure_analysis/match/partial-move-drop-order.run.stdout index 3fa346d87b072..f80d451af688d 100644 --- a/tests/ui/closures/2229_closure_analysis/match/partial-move-drop-order.run.stdout +++ b/tests/ui/closures/2229_closure_analysis/match/partial-move-drop-order.run.stdout @@ -1,7 +1,7 @@ one variant: before assign -dropping a after assign +dropping a dropping b two variants: diff --git a/tests/ui/closures/2229_closure_analysis/match/partial-move.rs b/tests/ui/closures/2229_closure_analysis/match/partial-move.rs index 7bd435bd1856f..17fa2713e570a 100644 --- a/tests/ui/closures/2229_closure_analysis/match/partial-move.rs +++ b/tests/ui/closures/2229_closure_analysis/match/partial-move.rs @@ -33,7 +33,6 @@ use partial_move_lib::ExtNonExhaustive; // differences: pub fn test_enum1(x: Enum) -> impl FnOnce() { || { - //~^ ERROR: closure may outlive the current function, but it borrows `x.0` match x { Enum::A(a, b) => { drop((a, b)); @@ -43,9 +42,11 @@ pub fn test_enum1(x: Enum) -> impl FnOnce() { } } +// When matching on an enum, the entire enum gets captured, even if it only has a single variant. +// This is because the closure will need to read the discriminant (which in this case happens to be +// zero-sized). pub fn test_enum2(x: Enum) -> impl FnOnce() { || { - //~^ ERROR: closure may outlive the current function, but it borrows `x.0` match x { Enum::A(a, b) => { drop((a, b)); @@ -54,7 +55,7 @@ pub fn test_enum2(x: Enum) -> impl FnOnce() { } } -// The behavior for single-variant enums matches what happens for a struct +// In particular, this means that structs behave differently than single-variant enums: pub fn test_struct(x: Struct) -> impl FnOnce() { || { //~^ ERROR: closure may outlive the current function, but it borrows `x.0` diff --git a/tests/ui/closures/2229_closure_analysis/match/partial-move.stderr b/tests/ui/closures/2229_closure_analysis/match/partial-move.stderr index 09f9adf95d5bb..ffe35cbd8c961 100644 --- a/tests/ui/closures/2229_closure_analysis/match/partial-move.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/partial-move.stderr @@ -1,5 +1,5 @@ error[E0373]: closure may outlive the current function, but it borrows `x.0`, which is owned by the current function - --> $DIR/partial-move.rs:35:5 + --> $DIR/partial-move.rs:60:5 | LL | || { | ^^ may outlive borrowed value `x.0` @@ -8,55 +8,7 @@ LL | match x { | - `x.0` is borrowed here | note: closure is returned here - --> $DIR/partial-move.rs:35:5 - | -LL | / || { -LL | | -LL | | match x { -LL | | Enum::A(a, b) => { -... | -LL | | } - | |_____^ -help: to force the closure to take ownership of `x.0` (and any other referenced variables), use the `move` keyword - | -LL | move || { - | ++++ - -error[E0373]: closure may outlive the current function, but it borrows `x.0`, which is owned by the current function - --> $DIR/partial-move.rs:47:5 - | -LL | || { - | ^^ may outlive borrowed value `x.0` -LL | -LL | match x { - | - `x.0` is borrowed here - | -note: closure is returned here - --> $DIR/partial-move.rs:47:5 - | -LL | / || { -LL | | -LL | | match x { -LL | | Enum::A(a, b) => { -... | -LL | | } - | |_____^ -help: to force the closure to take ownership of `x.0` (and any other referenced variables), use the `move` keyword - | -LL | move || { - | ++++ - -error[E0373]: closure may outlive the current function, but it borrows `x.0`, which is owned by the current function - --> $DIR/partial-move.rs:59:5 - | -LL | || { - | ^^ may outlive borrowed value `x.0` -LL | -LL | match x { - | - `x.0` is borrowed here - | -note: closure is returned here - --> $DIR/partial-move.rs:59:5 + --> $DIR/partial-move.rs:60:5 | LL | / || { LL | | @@ -70,6 +22,6 @@ help: to force the closure to take ownership of `x.0` (and any other referenced LL | move || { | ++++ -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0373`. diff --git a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs index 16cb9d7355da5..d603678da9b96 100644 --- a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs +++ b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs @@ -41,22 +41,23 @@ enum SingleVariant { Points(u32) } -// Should not capture the discriminant since the single variant mentioned -// in the match arm does not trigger a binding -fn test_3_should_not_capture_single_variant() { +// Should capture the discriminant despite there only being one variant +fn test_3_should_capture_single_variant() { let variant = SingleVariant::Points(1); let c = #[rustc_capture_analysis] || { //~^ ERROR First Pass analysis includes: + //~| ERROR Min Capture analysis includes: match variant { + //~^ NOTE: Capturing variant[] -> Immutable + //~| NOTE: Min Capture variant[] -> Immutable SingleVariant::Points(_) => {} } }; c(); } -// Should not capture the discriminant since the single variant mentioned -// in the match arm does not trigger a binding +// Should capture the discriminant despite there only being one variant fn test_6_should_capture_single_variant() { let variant = SingleVariant::Points(1); let c = #[rustc_capture_analysis] @@ -64,8 +65,9 @@ fn test_6_should_capture_single_variant() { //~^ ERROR First Pass analysis includes: //~| ERROR Min Capture analysis includes: match variant { - //~^ NOTE: Capturing variant[(0, 0)] -> Immutable - //~| NOTE: Min Capture variant[(0, 0)] -> Immutable + //~^ NOTE: Capturing variant[] -> Immutable + //~| NOTE: Capturing variant[(0, 0)] -> Immutable + //~| NOTE: Min Capture variant[] -> Immutable SingleVariant::Points(a) => { println!("{:?}", a); } @@ -198,7 +200,7 @@ fn test_8_capture_slice_wild() { fn main() { test_1_should_capture(); test_2_should_not_capture(); - test_3_should_not_capture_single_variant(); + test_3_should_capture_single_variant(); test_6_should_capture_single_variant(); test_4_should_not_capture_array(); test_5_should_capture_multi_variant(); diff --git a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr index 73c685e152765..7928912d12245 100644 --- a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr @@ -44,18 +44,41 @@ LL | | }; | |_____^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:49:5 + --> $DIR/patterns-capture-analysis.rs:48:5 | LL | / || { LL | | +LL | | LL | | match variant { -LL | | SingleVariant::Points(_) => {} -LL | | } +... | +LL | | }; + | |_____^ + | +note: Capturing variant[] -> Immutable + --> $DIR/patterns-capture-analysis.rs:51:15 + | +LL | match variant { + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/patterns-capture-analysis.rs:48:5 + | +LL | / || { +LL | | +LL | | +LL | | match variant { +... | LL | | }; | |_____^ + | +note: Min Capture variant[] -> Immutable + --> $DIR/patterns-capture-analysis.rs:51:15 + | +LL | match variant { + | ^^^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:63:5 + --> $DIR/patterns-capture-analysis.rs:64:5 | LL | / || { LL | | @@ -65,14 +88,19 @@ LL | | match variant { LL | | }; | |_____^ | +note: Capturing variant[] -> Immutable + --> $DIR/patterns-capture-analysis.rs:67:15 + | +LL | match variant { + | ^^^^^^^ note: Capturing variant[(0, 0)] -> Immutable - --> $DIR/patterns-capture-analysis.rs:66:15 + --> $DIR/patterns-capture-analysis.rs:67:15 | LL | match variant { | ^^^^^^^ error: Min Capture analysis includes: - --> $DIR/patterns-capture-analysis.rs:63:5 + --> $DIR/patterns-capture-analysis.rs:64:5 | LL | / || { LL | | @@ -82,14 +110,14 @@ LL | | match variant { LL | | }; | |_____^ | -note: Min Capture variant[(0, 0)] -> Immutable - --> $DIR/patterns-capture-analysis.rs:66:15 +note: Min Capture variant[] -> Immutable + --> $DIR/patterns-capture-analysis.rs:67:15 | LL | match variant { | ^^^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:82:5 + --> $DIR/patterns-capture-analysis.rs:84:5 | LL | / || { LL | | @@ -100,7 +128,7 @@ LL | | }; | |_____^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:94:5 + --> $DIR/patterns-capture-analysis.rs:96:5 | LL | / || { LL | | @@ -111,7 +139,7 @@ LL | | }; | |_____^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:107:5 + --> $DIR/patterns-capture-analysis.rs:109:5 | LL | / || { LL | | @@ -122,7 +150,7 @@ LL | | }; | |_____^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:129:5 + --> $DIR/patterns-capture-analysis.rs:131:5 | LL | / || { LL | | @@ -133,13 +161,13 @@ LL | | }; | |_____^ | note: Capturing variant[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:132:15 + --> $DIR/patterns-capture-analysis.rs:134:15 | LL | match variant { | ^^^^^^^ error: Min Capture analysis includes: - --> $DIR/patterns-capture-analysis.rs:129:5 + --> $DIR/patterns-capture-analysis.rs:131:5 | LL | / || { LL | | @@ -150,13 +178,13 @@ LL | | }; | |_____^ | note: Min Capture variant[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:132:15 + --> $DIR/patterns-capture-analysis.rs:134:15 | LL | match variant { | ^^^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:147:5 + --> $DIR/patterns-capture-analysis.rs:149:5 | LL | / || { LL | | @@ -167,13 +195,13 @@ LL | | }; | |_____^ | note: Capturing slice[Deref] -> Immutable - --> $DIR/patterns-capture-analysis.rs:150:15 + --> $DIR/patterns-capture-analysis.rs:152:15 | LL | match slice { | ^^^^^ error: Min Capture analysis includes: - --> $DIR/patterns-capture-analysis.rs:147:5 + --> $DIR/patterns-capture-analysis.rs:149:5 | LL | / || { LL | | @@ -184,13 +212,13 @@ LL | | }; | |_____^ | note: Min Capture slice[Deref] -> Immutable - --> $DIR/patterns-capture-analysis.rs:150:15 + --> $DIR/patterns-capture-analysis.rs:152:15 | LL | match slice { | ^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:159:5 + --> $DIR/patterns-capture-analysis.rs:161:5 | LL | / || { LL | | @@ -201,13 +229,13 @@ LL | | }; | |_____^ | note: Capturing slice[Deref] -> Immutable - --> $DIR/patterns-capture-analysis.rs:162:15 + --> $DIR/patterns-capture-analysis.rs:164:15 | LL | match slice { | ^^^^^ error: Min Capture analysis includes: - --> $DIR/patterns-capture-analysis.rs:159:5 + --> $DIR/patterns-capture-analysis.rs:161:5 | LL | / || { LL | | @@ -218,13 +246,13 @@ LL | | }; | |_____^ | note: Min Capture slice[Deref] -> Immutable - --> $DIR/patterns-capture-analysis.rs:162:15 + --> $DIR/patterns-capture-analysis.rs:164:15 | LL | match slice { | ^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:171:5 + --> $DIR/patterns-capture-analysis.rs:173:5 | LL | / || { LL | | @@ -235,13 +263,13 @@ LL | | }; | |_____^ | note: Capturing slice[Deref] -> Immutable - --> $DIR/patterns-capture-analysis.rs:174:15 + --> $DIR/patterns-capture-analysis.rs:176:15 | LL | match slice { | ^^^^^ error: Min Capture analysis includes: - --> $DIR/patterns-capture-analysis.rs:171:5 + --> $DIR/patterns-capture-analysis.rs:173:5 | LL | / || { LL | | @@ -252,13 +280,13 @@ LL | | }; | |_____^ | note: Min Capture slice[Deref] -> Immutable - --> $DIR/patterns-capture-analysis.rs:174:15 + --> $DIR/patterns-capture-analysis.rs:176:15 | LL | match slice { | ^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:188:5 + --> $DIR/patterns-capture-analysis.rs:190:5 | LL | / || { LL | | @@ -268,5 +296,5 @@ LL | | [..] => {}, LL | | }; | |_____^ -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors From 11649b2eaeef525a5ef0eb79662cba2d54816eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Sun, 4 Jan 2026 19:34:37 +0100 Subject: [PATCH 2/3] Read discriminant of enum even with only one inhabited variant --- .../src/builder/matches/match_pair.rs | 6 +--- .../issue-120337-irrefutable-let-ice.rs | 1 + .../issue-120337-irrefutable-let-ice.stderr | 13 +++++++ .../fail/match/all_variants_uninhabited.rs | 2 ++ .../fail/match/only_inhabited_variant.rs | 3 ++ .../miri/tests/fail/match/single_variant.rs | 19 +++------- .../match/single_variant_non_exhaustive.rs | 25 +++++++++++++ .../single_variant_non_exhaustive.stderr | 13 +++++++ .../tests/fail/match/single_variant_uninit.rs | 20 +++-------- .../fail/match/single_variant_uninit.stderr | 4 +-- .../single_variant_uninit_non_exhaustive.rs | 29 +++++++++++++++ ...ingle_variant_uninit_non_exhaustive.stderr | 18 ++++++++++ tests/ui/borrowck/borrowck-describe-lvalue.rs | 4 +-- .../borrowck/borrowck-describe-lvalue.stderr | 24 ++++++++++++- .../let-irrefutable-pattern-ice-120337.rs | 5 ++- .../let-irrefutable-pattern-ice-120337.stderr | 9 +++++ tests/ui/match/borrowck-uninhabited.rs | 1 + tests/ui/match/borrowck-uninhabited.stderr | 13 ++++++- .../borrowck-exhaustive.rs | 22 ++++++++---- .../borrowck-exhaustive.stderr | 36 +++++++++++++++++++ 20 files changed, 216 insertions(+), 51 deletions(-) rename src/tools/miri/tests/{pass/issues => fail}/issue-120337-irrefutable-let-ice.rs (81%) create mode 100644 src/tools/miri/tests/fail/issue-120337-irrefutable-let-ice.stderr create mode 100644 src/tools/miri/tests/fail/match/single_variant_non_exhaustive.rs create mode 100644 src/tools/miri/tests/fail/match/single_variant_non_exhaustive.stderr create mode 100644 src/tools/miri/tests/fail/match/single_variant_uninit_non_exhaustive.rs create mode 100644 src/tools/miri/tests/fail/match/single_variant_uninit_non_exhaustive.stderr create mode 100644 tests/ui/consts/let-irrefutable-pattern-ice-120337.stderr create mode 100644 tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.stderr diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index cbe31fd7d1325..584317efe21fd 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -295,11 +295,7 @@ impl<'tcx> MatchPairTree<'tcx> { let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns); - // 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 { + if adt_def.is_enum() { Some(TestableCase::Variant { adt_def, variant_index }) } else { None diff --git a/src/tools/miri/tests/pass/issues/issue-120337-irrefutable-let-ice.rs b/src/tools/miri/tests/fail/issue-120337-irrefutable-let-ice.rs similarity index 81% rename from src/tools/miri/tests/pass/issues/issue-120337-irrefutable-let-ice.rs rename to src/tools/miri/tests/fail/issue-120337-irrefutable-let-ice.rs index 5af0d0e4bbd95..ff42c5ada1d10 100644 --- a/src/tools/miri/tests/pass/issues/issue-120337-irrefutable-let-ice.rs +++ b/src/tools/miri/tests/fail/issue-120337-irrefutable-let-ice.rs @@ -13,4 +13,5 @@ pub union U { fn main() { let E::A(ref _a) = unsafe { &(&U { u: () }).e }; + //~^ ERROR: read discriminant of an uninhabited enum variant } diff --git a/src/tools/miri/tests/fail/issue-120337-irrefutable-let-ice.stderr b/src/tools/miri/tests/fail/issue-120337-irrefutable-let-ice.stderr new file mode 100644 index 0000000000000..c8a3d7bacad39 --- /dev/null +++ b/src/tools/miri/tests/fail/issue-120337-irrefutable-let-ice.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: read discriminant of an uninhabited enum variant + --> tests/fail/issue-120337-irrefutable-let-ice.rs:LL:CC + | +LL | let E::A(ref _a) = unsafe { &(&U { u: () }).e }; + | ^^^^^^^^^^^^ 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 + diff --git a/src/tools/miri/tests/fail/match/all_variants_uninhabited.rs b/src/tools/miri/tests/fail/match/all_variants_uninhabited.rs index b073d339f10cd..425a784d35364 100644 --- a/src/tools/miri/tests/fail/match/all_variants_uninhabited.rs +++ b/src/tools/miri/tests/fail/match/all_variants_uninhabited.rs @@ -1,3 +1,5 @@ +// This UB should be detected even with validation disabled. +//@compile-flags: -Zmiri-disable-validation #![allow(deref_nullptr)] enum Never {} diff --git a/src/tools/miri/tests/fail/match/only_inhabited_variant.rs b/src/tools/miri/tests/fail/match/only_inhabited_variant.rs index 2be5e8083aa7f..18fe02d695594 100644 --- a/src/tools/miri/tests/fail/match/only_inhabited_variant.rs +++ b/src/tools/miri/tests/fail/match/only_inhabited_variant.rs @@ -1,4 +1,7 @@ // rust-lang/miri#4778 +// +// This UB should be detected even with validation disabled. +//@compile-flags: -Zmiri-disable-validation #![feature(never_type)] #[repr(C)] diff --git a/src/tools/miri/tests/fail/match/single_variant.rs b/src/tools/miri/tests/fail/match/single_variant.rs index 35bb63620ea23..bd3e47b9a6bf1 100644 --- a/src/tools/miri/tests/fail/match/single_variant.rs +++ b/src/tools/miri/tests/fail/match/single_variant.rs @@ -1,7 +1,7 @@ -// 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 +// +// This UB should be detected even with validation disabled. +//@compile-flags: -Zmiri-disable-validation #![allow(dead_code)] #[repr(u8)] @@ -9,24 +9,13 @@ 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(_) => {} + Exhaustive::A(_) => {} } } } diff --git a/src/tools/miri/tests/fail/match/single_variant_non_exhaustive.rs b/src/tools/miri/tests/fail/match/single_variant_non_exhaustive.rs new file mode 100644 index 0000000000000..2fdf4a7a6b0bd --- /dev/null +++ b/src/tools/miri/tests/fail/match/single_variant_non_exhaustive.rs @@ -0,0 +1,25 @@ +// Like single_variant.rs, but with a non_exhaustive enum, as the generated MIR used to differ +// between these cases. +// +// See: rust-lang/rust#147722 +// +// This UB should be detected even with validation disabled. +//@compile-flags: -Zmiri-disable-validation +#![allow(dead_code)] + +#[repr(u8)] +#[non_exhaustive] +enum NonExhaustive { + A(u8) = 42, +} + +fn main() { + unsafe { + let x: &[u8; 2] = &[21, 37]; + let y: &NonExhaustive = std::mem::transmute(x); + match y { + //~^ ERROR: enum value has invalid tag + NonExhaustive::A(_) => {} + } + } +} diff --git a/src/tools/miri/tests/fail/match/single_variant_non_exhaustive.stderr b/src/tools/miri/tests/fail/match/single_variant_non_exhaustive.stderr new file mode 100644 index 0000000000000..126f773247870 --- /dev/null +++ b/src/tools/miri/tests/fail/match/single_variant_non_exhaustive.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: enum value has invalid tag: 0x15 + --> tests/fail/match/single_variant_non_exhaustive.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 + diff --git a/src/tools/miri/tests/fail/match/single_variant_uninit.rs b/src/tools/miri/tests/fail/match/single_variant_uninit.rs index e04947f996288..4151984354c2a 100644 --- a/src/tools/miri/tests/fail/match/single_variant_uninit.rs +++ b/src/tools/miri/tests/fail/match/single_variant_uninit.rs @@ -1,7 +1,7 @@ -// 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 +// +// This UB should be detected even with validation disabled. +//@compile-flags: -Zmiri-disable-validation #![allow(dead_code)] #![allow(unreachable_patterns)] @@ -10,27 +10,15 @@ enum Exhaustive { A(u8) = 0, } -#[repr(u8)] -#[non_exhaustive] -enum NonExhaustive { - A(u8) = 0, -} - use std::mem::MaybeUninit; fn main() { let buffer: [MaybeUninit; 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) => {} + Exhaustive::A(ref _val) => {} _ => {} } } diff --git a/src/tools/miri/tests/fail/match/single_variant_uninit.stderr b/src/tools/miri/tests/fail/match/single_variant_uninit.stderr index fa8e9babae602..8c16c1a9bdc00 100644 --- a/src/tools/miri/tests/fail/match/single_variant_uninit.stderr +++ b/src/tools/miri/tests/fail/match/single_variant_uninit.stderr @@ -1,8 +1,8 @@ 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 +LL | match *exh { + | ^^^^ 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 diff --git a/src/tools/miri/tests/fail/match/single_variant_uninit_non_exhaustive.rs b/src/tools/miri/tests/fail/match/single_variant_uninit_non_exhaustive.rs new file mode 100644 index 0000000000000..ad3cad0d3ec07 --- /dev/null +++ b/src/tools/miri/tests/fail/match/single_variant_uninit_non_exhaustive.rs @@ -0,0 +1,29 @@ +// Like single_variant_uninit.rs, but with a non_exhaustive enum, as the generated MIR used to +// differ between these cases. +// +// See: rust-lang/rust#147722 +// +// This UB should be detected even with validation disabled. +//@compile-flags: -Zmiri-disable-validation +#![allow(dead_code)] +#![allow(unreachable_patterns)] + +#[repr(u8)] +#[non_exhaustive] +enum NonExhaustive { + A(u8) = 0, +} + +use std::mem::MaybeUninit; + +fn main() { + let buffer: [MaybeUninit; 2] = [MaybeUninit::uninit(), MaybeUninit::new(0u8)]; + let nexh: *const NonExhaustive = (&raw const buffer).cast(); + unsafe { + match *nexh { + //~^ ERROR: memory is uninitialized + NonExhaustive::A(ref _val) => {} + _ => {} + } + } +} diff --git a/src/tools/miri/tests/fail/match/single_variant_uninit_non_exhaustive.stderr b/src/tools/miri/tests/fail/match/single_variant_uninit_non_exhaustive.stderr new file mode 100644 index 0000000000000..5e88098091eac --- /dev/null +++ b/src/tools/miri/tests/fail/match/single_variant_uninit_non_exhaustive.stderr @@ -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_non_exhaustive.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 + diff --git a/tests/ui/borrowck/borrowck-describe-lvalue.rs b/tests/ui/borrowck/borrowck-describe-lvalue.rs index f3a4b382fa89f..55c8b7a7bec7e 100644 --- a/tests/ui/borrowck/borrowck-describe-lvalue.rs +++ b/tests/ui/borrowck/borrowck-describe-lvalue.rs @@ -55,7 +55,7 @@ fn main() { { let mut e = Baz::X(2); let x = e.x(); - match e { + match e { //~ ERROR cannot use `e` because it was mutably borrowed Baz::X(value) => value //~ ERROR cannot use `e.0` because it was mutably borrowed }; drop(x); @@ -92,7 +92,7 @@ fn main() { { let mut e = Box::new(Baz::X(3)); let x = e.x(); - match *e { + match *e { //~ ERROR cannot use `*e` because it was mutably borrowed Baz::X(value) => value //~^ ERROR cannot use `e.0` because it was mutably borrowed }; diff --git a/tests/ui/borrowck/borrowck-describe-lvalue.stderr b/tests/ui/borrowck/borrowck-describe-lvalue.stderr index 666a21808d80b..a49e2bc6a965b 100644 --- a/tests/ui/borrowck/borrowck-describe-lvalue.stderr +++ b/tests/ui/borrowck/borrowck-describe-lvalue.stderr @@ -71,6 +71,17 @@ LL | h.0; LL | drop(x); | - borrow later used here +error[E0503]: cannot use `e` because it was mutably borrowed + --> $DIR/borrowck-describe-lvalue.rs:58:15 + | +LL | let x = e.x(); + | - `e` is borrowed here +LL | match e { + | ^ use of borrowed `e` +... +LL | drop(x); + | - borrow later used here + error[E0503]: cannot use `e.0` because it was mutably borrowed --> $DIR/borrowck-describe-lvalue.rs:59:20 | @@ -123,6 +134,17 @@ LL | h.0; LL | drop(x); | - borrow later used here +error[E0503]: cannot use `*e` because it was mutably borrowed + --> $DIR/borrowck-describe-lvalue.rs:95:15 + | +LL | let x = e.x(); + | - `*e` is borrowed here +LL | match *e { + | ^^ use of borrowed `*e` +... +LL | drop(x); + | - borrow later used here + error[E0503]: cannot use `e.0` because it was mutably borrowed --> $DIR/borrowck-describe-lvalue.rs:96:20 | @@ -355,7 +377,7 @@ LL | drop(x); | = note: move occurs because `x` has type `Vec`, which does not implement the `Copy` trait -error: aborting due to 31 previous errors +error: aborting due to 33 previous errors Some errors have detailed explanations: E0382, E0499, E0502, E0503. For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/consts/let-irrefutable-pattern-ice-120337.rs b/tests/ui/consts/let-irrefutable-pattern-ice-120337.rs index 24ff0a1f0719c..c8cdd092004ae 100644 --- a/tests/ui/consts/let-irrefutable-pattern-ice-120337.rs +++ b/tests/ui/consts/let-irrefutable-pattern-ice-120337.rs @@ -1,9 +1,7 @@ // Regression test for . // // This checks that const eval doesn't cause an ICE when reading an uninhabited -// variant. (N.B. this is UB, but not currently detected by rustc) -// -//@ check-pass +// variant. #![feature(never_type)] #[derive(Copy, Clone)] @@ -13,6 +11,7 @@ pub union U { u: (), e: E, } pub const C: () = { let E::A(ref a) = unsafe { &(&U { u: () }).e }; + //~^ ERROR: read discriminant of an uninhabited enum variant }; fn main() {} diff --git a/tests/ui/consts/let-irrefutable-pattern-ice-120337.stderr b/tests/ui/consts/let-irrefutable-pattern-ice-120337.stderr new file mode 100644 index 0000000000000..ab8111f4edf9a --- /dev/null +++ b/tests/ui/consts/let-irrefutable-pattern-ice-120337.stderr @@ -0,0 +1,9 @@ +error[E0080]: read discriminant of an uninhabited enum variant + --> $DIR/let-irrefutable-pattern-ice-120337.rs:13:9 + | +LL | let E::A(ref a) = unsafe { &(&U { u: () }).e }; + | ^^^^^^^^^^^ evaluation of `C` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/match/borrowck-uninhabited.rs b/tests/ui/match/borrowck-uninhabited.rs index 34f5e323a91e9..29fe24029c054 100644 --- a/tests/ui/match/borrowck-uninhabited.rs +++ b/tests/ui/match/borrowck-uninhabited.rs @@ -41,6 +41,7 @@ fn single_variant(x: &mut Single) { match x { &mut Single::V(ref mut y, _) => { match x { + //~^ ERROR: cannot use `*x` because it was mutably borrowed &mut Single::V(_, ref mut z) => { let _y = y; let _z = z; diff --git a/tests/ui/match/borrowck-uninhabited.stderr b/tests/ui/match/borrowck-uninhabited.stderr index 4bbe6ecea16b9..dc9412a285ae7 100644 --- a/tests/ui/match/borrowck-uninhabited.stderr +++ b/tests/ui/match/borrowck-uninhabited.stderr @@ -20,6 +20,17 @@ LL | &mut Ok(ref mut y) => match x { LL | let _y = y; | - borrow later used here -error: aborting due to 2 previous errors +error[E0503]: cannot use `*x` because it was mutably borrowed + --> $DIR/borrowck-uninhabited.rs:43:19 + | +LL | &mut Single::V(ref mut y, _) => { + | --------- `x.0` is borrowed here +LL | match x { + | ^ use of borrowed `x.0` +... +LL | let _y = y; + | - borrow later used here + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0503`. diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.rs index 2e40819d69ad7..9e620150e1861 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.rs @@ -1,7 +1,5 @@ -// Test that the borrow checker doesn't consider checking an exhaustive pattern -// as an access. - -//@ check-pass +// Test that the borrow checker considers checking an exhaustive pattern +// to be an access. #![allow(dropping_references)] @@ -14,19 +12,31 @@ enum Local { Variant(u32), } +#[non_exhaustive] +enum LocalNonExhaustive { + Variant(u32), +} + fn main() { let mut x = ExhaustiveMonovariant::Variant(1); let y = &mut x; - match x { + match x { //~ ERROR cannot use `x` because it was mutably borrowed ExhaustiveMonovariant::Variant(_) => {}, _ => {}, } drop(y); let mut x = Local::Variant(1); let y = &mut x; - match x { + match x { //~ ERROR cannot use `x` because it was mutably borrowed Local::Variant(_) => {}, _ => {}, } 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); } diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.stderr new file mode 100644 index 0000000000000..f24f2ed2578a8 --- /dev/null +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.stderr @@ -0,0 +1,36 @@ +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/borrowck-exhaustive.rs:23: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[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/borrowck-exhaustive.rs:30: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[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/borrowck-exhaustive.rs:37: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 3 previous errors + +For more information about this error, try `rustc --explain E0503`. From 063ee651d9317ed152a5afbff57bdf966df67567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maja=20K=C4=85dzio=C5=82ka?= Date: Sun, 4 Jan 2026 21:45:21 +0100 Subject: [PATCH 3/3] bless mir-opt tests --- ...opy_aggregate.remove_storage_dead.GVN.diff | 46 ++++++++++++------- .../gvn_uninhabited.f.GVN.panic-unwind.diff | 16 +++++-- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/tests/mir-opt/gvn_copy_aggregate.remove_storage_dead.GVN.diff b/tests/mir-opt/gvn_copy_aggregate.remove_storage_dead.GVN.diff index c9cfc7efcd1a6..42454344d3fc6 100644 --- a/tests/mir-opt/gvn_copy_aggregate.remove_storage_dead.GVN.diff +++ b/tests/mir-opt/gvn_copy_aggregate.remove_storage_dead.GVN.diff @@ -7,15 +7,16 @@ let _2: T; let mut _3: AlwaysSome; let mut _4: fn() -> AlwaysSome; - let _5: T; - let mut _6: T; - let mut _7: isize; + let mut _5: isize; + let _6: T; + let mut _7: T; let mut _8: isize; + let mut _9: isize; scope 1 { debug v => _2; } scope 2 { - debug v => _5; + debug v => _6; } bb0: { @@ -30,23 +31,34 @@ bb1: { StorageDead(_4); -- StorageLive(_5); -- _5 = move ((_3 as Some).0: T); -- _2 = move _5; -- StorageDead(_5); + _5 = discriminant(_3); +- switchInt(move _5) -> [0: bb3, otherwise: bb2]; ++ switchInt(copy _5) -> [0: bb3, otherwise: bb2]; + } + + bb2: { + unreachable; + } + + bb3: { +- StorageLive(_6); +- _6 = move ((_3 as Some).0: T); +- _2 = move _6; +- StorageDead(_6); +- _8 = discriminant(_3); +- StorageDead(_3); + nop; -+ _5 = copy ((_3 as Some).0: T); -+ _2 = copy _5; ++ _6 = copy ((_3 as Some).0: T); ++ _2 = copy _6; + nop; - _7 = discriminant(_3); -- StorageDead(_3); ++ _8 = copy _5; + nop; - StorageLive(_6); -- _6 = move _2; -- _0 = AlwaysSome::::Some(move _6); -+ _6 = copy _5; + StorageLive(_7); +- _7 = move _2; +- _0 = AlwaysSome::::Some(move _7); ++ _7 = copy _6; + _0 = copy _3; - StorageDead(_6); + StorageDead(_7); StorageDead(_2); return; } diff --git a/tests/mir-opt/gvn_uninhabited.f.GVN.panic-unwind.diff b/tests/mir-opt/gvn_uninhabited.f.GVN.panic-unwind.diff index a3e4796d088e4..46b4dbb941033 100644 --- a/tests/mir-opt/gvn_uninhabited.f.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn_uninhabited.f.GVN.panic-unwind.diff @@ -7,7 +7,8 @@ let mut _2: E; let mut _3: &U; let _4: U; - let mut _5: &U; + let mut _5: isize; + let mut _6: &U; scope 1 { debug i => _1; } @@ -15,10 +16,19 @@ bb0: { StorageLive(_2); StorageLive(_3); - _5 = const f::promoted[0]; - _3 = &(*_5); + _6 = const f::promoted[0]; + _3 = &(*_6); - _2 = copy ((*_3).1: E); + _2 = const Scalar(0x00000000): E; + _5 = discriminant(_2); + switchInt(move _5) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { StorageLive(_1); - _1 = copy ((_2 as A).1: u32); + _1 = const 0_u32;