ExprUseVisitor: properly report discriminant reads

This solves the "can't find the upvar" ICEs that resulted from
`maybe_read_scrutinee` being unfit for purpose.
This commit is contained in:
Maja Kądziołka 2025-03-26 03:26:09 +01:00
parent 68f11a11b6
commit 9cb47c6e8e
No known key found for this signature in database
14 changed files with 426 additions and 92 deletions

View file

@ -943,6 +943,19 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
}
/// The core driver for walking a pattern
///
/// This should mirror how pattern-matching gets lowered to MIR, as
/// otherwise lowering will ICE when trying to resolve the upvars.
///
/// However, it is okay to approximate it here by doing *more* accesses than
/// the actual MIR builder will, which is useful when some checks are too
/// cumbersome to perform here. For example, if after typeck it becomes
/// clear that only one variant of an enum is inhabited, and therefore a
/// read of the discriminant is not necessary, `walk_pat` will have
/// over-approximated the necessary upvar capture granularity.
///
/// Do note that discrepancies like these do still create obscure corners
/// in the semantics of the language, and should be avoided if possible.
#[instrument(skip(self), level = "debug")]
fn walk_pat(
&self,
@ -952,6 +965,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
) -> Result<(), Cx::Error> {
let tcx = self.cx.tcx();
self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
debug!("walk_pat: pat.kind={:?}", pat.kind);
let read_discriminant = || {
self.delegate.borrow_mut().borrow(place, discr_place.hir_id, BorrowKind::Immutable);
};
match pat.kind {
PatKind::Binding(_, canonical_id, ..) => {
debug!("walk_pat: binding place={:?} pat={:?}", place, pat);
@ -974,11 +992,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
// binding when lowering pattern guards to ensure that the guard does not
// modify the scrutinee.
if has_guard {
self.delegate.borrow_mut().borrow(
place,
discr_place.hir_id,
BorrowKind::Immutable,
);
read_discriminant();
}
// It is also a borrow or copy/move of the value being matched.
@ -1014,13 +1028,71 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
PatKind::Never => {
// A `!` pattern always counts as an immutable read of the discriminant,
// even in an irrefutable pattern.
self.delegate.borrow_mut().borrow(
place,
discr_place.hir_id,
BorrowKind::Immutable,
);
read_discriminant();
}
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
// A `Path` pattern is just a name like `Foo`. This is either a
// named constant or else it refers to an ADT variant
let res = self.cx.typeck_results().qpath_res(qpath, *hir_id);
match res {
Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {
// Named constants have to be equated with the value
// being matched, so that's a read of the value being matched.
//
// FIXME: Does the MIR code skip this read when matching on a ZST?
// If so, we can also skip it here.
read_discriminant();
}
_ => {
// 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) {
read_discriminant();
}
}
}
}
PatKind::Expr(_) | PatKind::Range(..) => {
// When matching against a literal or range, we need to
// borrow the place to compare it against the pattern.
//
// FIXME: What if the type being matched only has one
// possible value?
// FIXME: What if the range is the full range of the type
// and doesn't actually require a discriminant read?
read_discriminant();
}
PatKind::Struct(..) | PatKind::TupleStruct(..) => {
if self.is_multivariant_adt(place.place.ty(), pat.span) {
read_discriminant();
}
}
PatKind::Slice(lhs, wild, rhs) => {
// We don't need to test the length if the pattern is `[..]`
if matches!((lhs, wild, rhs), (&[], Some(_), &[]))
// Arrays have a statically known size, so
// there is no need to read their length
|| place.place.ty().peel_refs().is_array()
{
// No read necessary
} else {
read_discriminant();
}
}
PatKind::Or(_)
| PatKind::Box(_)
| PatKind::Ref(..)
| PatKind::Guard(..)
| PatKind::Tuple(..)
| PatKind::Wild
| PatKind::Missing
| PatKind::Err(_) => {
// If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses
// are made later as these patterns contains subpatterns.
// If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing
// the other patterns that are part of the match
}
_ => {}
}
Ok(())
@ -1904,6 +1976,14 @@ 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. Note that the actual MIR
/// builder code does a more specific check, filtering out variants that
/// happen to be uninhabited.
///
/// Here, we cannot perform such an accurate checks, because querying
/// whether a type is inhabited requires that it has been fully inferred,
/// which cannot be guaranteed at this point.
fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
if let ty::Adt(def, _) = self.cx.structurally_resolve_type(span, ty).kind() {
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need

View file

@ -1,17 +0,0 @@
//@ known-bug: #137467
//@ edition: 2021
enum Camera {
Normal { base_transform: i32 },
Volume { transform: i32 },
}
fn draw_ui(camera: &mut Camera) {
|| {
let (Camera::Normal {
base_transform: _transform,
}
| Camera::Volume {
transform: _transform,
}) = camera;
};
}

View file

@ -1,18 +0,0 @@
//@ known-bug: #137467
//@ edition: 2021
enum Camera {
Normal { base_transform: i32 },
Volume { transform: i32 },
}
fn draw_ui(camera: &mut Camera) {
|| {
let (Camera::Normal {
base_transform: _,
}
| Camera::Volume {
transform: _,
}) = camera;
};
}

View file

@ -1,8 +0,0 @@
//@ known-bug: #137467
//@ edition: 2021
fn meow(x: (u32, u32, u32)) {
let f = || {
let ((0, a, _) | (_, _, a)) = x;
};
}

View file

@ -22,6 +22,7 @@ fn multi_variant_enum() {
//~| ERROR Min Capture analysis includes:
if let Info::Point(_, _, str) = point {
//~^ NOTE: Capturing point[] -> Immutable
//~| NOTE: Capturing point[] -> Immutable
//~| NOTE: Capturing point[(2, 0)] -> ByValue
//~| NOTE: Min Capture point[] -> ByValue
println!("{}", str);
@ -29,6 +30,7 @@ fn multi_variant_enum() {
if let Info::Meta(_, v) = meta {
//~^ NOTE: Capturing meta[] -> Immutable
//~| NOTE: Capturing meta[] -> Immutable
//~| NOTE: Capturing meta[(1, 1)] -> ByValue
//~| NOTE: Min Capture meta[] -> ByValue
println!("{:?}", v);

View file

@ -9,7 +9,7 @@ LL | let c = #[rustc_capture_analysis]
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: attributes on expressions are experimental
--> $DIR/capture-enums.rs:48:13
--> $DIR/capture-enums.rs:50:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -34,18 +34,28 @@ note: Capturing point[] -> Immutable
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing point[] -> Immutable
--> $DIR/capture-enums.rs:23:41
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:23:41
|
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Capturing meta[] -> Immutable
--> $DIR/capture-enums.rs:30:35
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
note: Capturing meta[] -> Immutable
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
note: Capturing meta[(1, 1)] -> ByValue
--> $DIR/capture-enums.rs:30:35
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
@ -67,13 +77,13 @@ note: Min Capture point[] -> ByValue
LL | if let Info::Point(_, _, str) = point {
| ^^^^^
note: Min Capture meta[] -> ByValue
--> $DIR/capture-enums.rs:30:35
--> $DIR/capture-enums.rs:31:35
|
LL | if let Info::Meta(_, v) = meta {
| ^^^^
error: First Pass analysis includes:
--> $DIR/capture-enums.rs:52:5
--> $DIR/capture-enums.rs:54:5
|
LL | / || {
LL | |
@ -85,13 +95,13 @@ LL | | };
| |_____^
|
note: Capturing point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:55:47
--> $DIR/capture-enums.rs:57:47
|
LL | let SingleVariant::Point(_, _, str) = point;
| ^^^^^
error: Min Capture analysis includes:
--> $DIR/capture-enums.rs:52:5
--> $DIR/capture-enums.rs:54:5
|
LL | / || {
LL | |
@ -103,7 +113,7 @@ LL | | };
| |_____^
|
note: Min Capture point[(2, 0)] -> ByValue
--> $DIR/capture-enums.rs:55:47
--> $DIR/capture-enums.rs:57:47
|
LL | let SingleVariant::Point(_, _, str) = point;
| ^^^^^

View file

@ -14,6 +14,7 @@ fn test_1_should_capture() {
//~| ERROR Min Capture analysis includes:
match variant {
//~^ NOTE: Capturing variant[] -> Immutable
//~| NOTE: Capturing variant[] -> Immutable
//~| NOTE: Min Capture variant[] -> Immutable
Some(_) => {}
_ => {}
@ -132,6 +133,7 @@ fn test_5_should_capture_multi_variant() {
//~| ERROR Min Capture analysis includes:
match variant {
//~^ NOTE: Capturing variant[] -> Immutable
//~| NOTE: Capturing variant[] -> Immutable
//~| NOTE: Min Capture variant[] -> Immutable
MVariant::A => {}
_ => {}
@ -150,6 +152,7 @@ fn test_7_should_capture_slice_len() {
//~| ERROR Min Capture analysis includes:
match slice {
//~^ NOTE: Capturing slice[] -> Immutable
//~| NOTE: Capturing slice[Deref] -> Immutable
//~| NOTE: Min Capture slice[] -> Immutable
[_,_,_] => {},
_ => {}
@ -162,6 +165,7 @@ fn test_7_should_capture_slice_len() {
//~| ERROR Min Capture analysis includes:
match slice {
//~^ NOTE: Capturing slice[] -> Immutable
//~| NOTE: Capturing slice[Deref] -> Immutable
//~| NOTE: Min Capture slice[] -> Immutable
[] => {},
_ => {}
@ -174,6 +178,7 @@ fn test_7_should_capture_slice_len() {
//~| ERROR Min Capture analysis includes:
match slice {
//~^ NOTE: Capturing slice[] -> Immutable
//~| NOTE: Capturing slice[Deref] -> Immutable
//~| NOTE: Min Capture slice[] -> Immutable
[_, .. ,_] => {},
_ => {}

View file

@ -14,6 +14,11 @@ note: Capturing variant[] -> Immutable
|
LL | match variant {
| ^^^^^^^
note: Capturing variant[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:15:15
|
LL | match variant {
| ^^^^^^^
error: Min Capture analysis includes:
--> $DIR/patterns-capture-analysis.rs:12:5
@ -33,7 +38,7 @@ LL | match variant {
| ^^^^^^^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:30:5
--> $DIR/patterns-capture-analysis.rs:31:5
|
LL | / || {
LL | |
@ -44,7 +49,7 @@ LL | | };
| |_____^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:49:5
--> $DIR/patterns-capture-analysis.rs:50:5
|
LL | / || {
LL | |
@ -55,7 +60,7 @@ LL | | };
| |_____^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:63:5
--> $DIR/patterns-capture-analysis.rs:64:5
|
LL | / || {
LL | |
@ -66,18 +71,18 @@ LL | | };
| |_____^
|
note: Capturing variant[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:66:15
--> $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 | |
@ -88,13 +93,13 @@ LL | | };
| |_____^
|
note: Min Capture variant[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:66:15
--> $DIR/patterns-capture-analysis.rs:67:15
|
LL | match variant {
| ^^^^^^^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:83:5
--> $DIR/patterns-capture-analysis.rs:84:5
|
LL | / || {
LL | |
@ -105,7 +110,7 @@ LL | | };
| |_____^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:95:5
--> $DIR/patterns-capture-analysis.rs:96:5
|
LL | / || {
LL | |
@ -116,7 +121,7 @@ LL | | };
| |_____^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:108:5
--> $DIR/patterns-capture-analysis.rs:109:5
|
LL | / || {
LL | |
@ -127,7 +132,7 @@ LL | | };
| |_____^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:130:5
--> $DIR/patterns-capture-analysis.rs:131:5
|
LL | / || {
LL | |
@ -138,13 +143,18 @@ LL | | };
| |_____^
|
note: Capturing variant[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:133:15
--> $DIR/patterns-capture-analysis.rs:134:15
|
LL | match variant {
| ^^^^^^^
note: Capturing variant[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:134:15
|
LL | match variant {
| ^^^^^^^
error: Min Capture analysis includes:
--> $DIR/patterns-capture-analysis.rs:130:5
--> $DIR/patterns-capture-analysis.rs:131:5
|
LL | / || {
LL | |
@ -155,13 +165,13 @@ LL | | };
| |_____^
|
note: Min Capture variant[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:133:15
--> $DIR/patterns-capture-analysis.rs:134:15
|
LL | match variant {
| ^^^^^^^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:148:5
--> $DIR/patterns-capture-analysis.rs:150:5
|
LL | / || {
LL | |
@ -172,13 +182,18 @@ LL | | };
| |_____^
|
note: Capturing slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:151:15
--> $DIR/patterns-capture-analysis.rs:153:15
|
LL | match slice {
| ^^^^^
note: Capturing slice[Deref] -> Immutable
--> $DIR/patterns-capture-analysis.rs:153:15
|
LL | match slice {
| ^^^^^
error: Min Capture analysis includes:
--> $DIR/patterns-capture-analysis.rs:148:5
--> $DIR/patterns-capture-analysis.rs:150:5
|
LL | / || {
LL | |
@ -189,13 +204,13 @@ LL | | };
| |_____^
|
note: Min Capture slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:151:15
--> $DIR/patterns-capture-analysis.rs:153:15
|
LL | match slice {
| ^^^^^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:160:5
--> $DIR/patterns-capture-analysis.rs:163:5
|
LL | / || {
LL | |
@ -206,13 +221,18 @@ LL | | };
| |_____^
|
note: Capturing slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:163:15
--> $DIR/patterns-capture-analysis.rs:166:15
|
LL | match slice {
| ^^^^^
note: Capturing slice[Deref] -> Immutable
--> $DIR/patterns-capture-analysis.rs:166:15
|
LL | match slice {
| ^^^^^
error: Min Capture analysis includes:
--> $DIR/patterns-capture-analysis.rs:160:5
--> $DIR/patterns-capture-analysis.rs:163:5
|
LL | / || {
LL | |
@ -223,13 +243,13 @@ LL | | };
| |_____^
|
note: Min Capture slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:163:15
--> $DIR/patterns-capture-analysis.rs:166:15
|
LL | match slice {
| ^^^^^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:172:5
--> $DIR/patterns-capture-analysis.rs:176:5
|
LL | / || {
LL | |
@ -240,13 +260,18 @@ LL | | };
| |_____^
|
note: Capturing slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:175:15
--> $DIR/patterns-capture-analysis.rs:179:15
|
LL | match slice {
| ^^^^^
note: Capturing slice[Deref] -> Immutable
--> $DIR/patterns-capture-analysis.rs:179:15
|
LL | match slice {
| ^^^^^
error: Min Capture analysis includes:
--> $DIR/patterns-capture-analysis.rs:172:5
--> $DIR/patterns-capture-analysis.rs:176:5
|
LL | / || {
LL | |
@ -257,13 +282,13 @@ LL | | };
| |_____^
|
note: Min Capture slice[] -> Immutable
--> $DIR/patterns-capture-analysis.rs:175:15
--> $DIR/patterns-capture-analysis.rs:179:15
|
LL | match slice {
| ^^^^^
error: First Pass analysis includes:
--> $DIR/patterns-capture-analysis.rs:189:5
--> $DIR/patterns-capture-analysis.rs:194:5
|
LL | / || {
LL | |

View file

@ -0,0 +1,23 @@
// This example used to compile, but the fact that it should was never properly
// discussed. With further experience, we concluded that capture precision
// depending on whether some types are inhabited goes too far, introducing a
// bunch of headaches without much benefit.
//@ edition:2021
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Void {}
pub fn main() {
let mut r = Result::<Void, (u32, u32)>::Err((0, 0));
let mut f = || {
let Err((ref mut a, _)) = r;
*a = 1;
};
let mut g = || {
//~^ ERROR: cannot borrow `r` as mutable more than once at a time
let Err((_, ref mut b)) = r;
*b = 2;
};
f();
g();
assert_eq!(r, Err((1, 2)));
}

View file

@ -0,0 +1,20 @@
error[E0499]: cannot borrow `r` as mutable more than once at a time
--> $DIR/only-inhabited-variant-stable.rs:15:17
|
LL | let mut f = || {
| -- first mutable borrow occurs here
LL | let Err((ref mut a, _)) = r;
| - first borrow occurs due to use of `r` in closure
...
LL | let mut g = || {
| ^^ second mutable borrow occurs here
LL |
LL | let Err((_, ref mut b)) = r;
| - second borrow occurs due to use of `r` in closure
...
LL | f();
| - first borrow later used here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0499`.

View file

@ -0,0 +1,20 @@
error[E0499]: cannot borrow `r` as mutable more than once at a time
--> $DIR/only-inhabited-variant.rs:16:17
|
LL | let mut f = || {
| -- first mutable borrow occurs here
LL | let Err((ref mut a, _)) = r;
| - first borrow occurs due to use of `r` in closure
...
LL | let mut g = || {
| ^^ second mutable borrow occurs here
LL |
LL | let Err((_, ref mut b)) = r;
| - second borrow occurs due to use of `r` in closure
...
LL | f();
| - first borrow later used here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0499`.

View file

@ -0,0 +1,20 @@
error[E0499]: cannot borrow `r` as mutable more than once at a time
--> $DIR/only-inhabited-variant.rs:16:17
|
LL | let mut f = || {
| -- first mutable borrow occurs here
LL | let Err((ref mut a, _)) = r;
| - first borrow occurs due to use of `r` in closure
...
LL | let mut g = || {
| ^^ second mutable borrow occurs here
LL |
LL | let Err((_, ref mut b)) = r;
| - second borrow occurs due to use of `r` in closure
...
LL | f();
| - first borrow later used here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0499`.

View file

@ -1,8 +1,9 @@
// Test precise capture of a multi-variant enum (when remaining variants are
// visibly uninhabited).
// This example used to compile, but the fact that it should was never properly
// discussed. With further experience, we concluded that capture precision
// depending on whether some types are inhabited goes too far, introducing a
// bunch of headaches without much benefit.
//@ revisions: normal exhaustive_patterns
//@ edition:2021
//@ run-pass
#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
#![feature(never_type)]
@ -13,6 +14,7 @@ pub fn main() {
*a = 1;
};
let mut g = || {
//~^ ERROR: cannot borrow `r` as mutable more than once at a time
let Err((_, ref mut b)) = r;
*b = 2;
};

View file

@ -0,0 +1,170 @@
//@ edition:2024
//@ check-pass
const X: u32 = 0;
fn match_literal(x: (u32, u32, u32)) {
let _ = || {
let ((0, a, _) | (_, _, a)) = x;
a
};
}
fn match_range(x: (u32, u32, u32)) {
let _ = || {
let ((0..5, a, _) | (_, _, a)) = x;
a
};
}
fn match_const(x: (u32, u32, u32)) {
let _ = || {
let ((X, a, _) | (_, _, a)) = x;
a
};
}
enum Choice { A, B }
fn match_unit_variant(x: (Choice, u32, u32)) {
let _ = || {
let ((Choice::A, a, _) | (Choice::B, _, a)) = x;
a
};
}
struct Unit;
fn match_unit_struct(mut x: (Unit, u32)) {
let r = &mut x.0;
let _ = || {
let (Unit, a) = x;
a
};
let _ = *r;
}
enum Also { Unit }
fn match_unit_enum(mut x: (Also, u32)) {
let r = &mut x.0;
let _ = || {
let (Also::Unit, a) = x;
a
};
let _ = *r;
}
enum TEnum {
A(u32),
B(u32),
}
enum SEnum {
A { a: u32 },
B { a: u32 },
}
fn match_tuple_enum(x: TEnum) {
let _ = || {
let (TEnum::A(a) | TEnum::B(a)) = x;
a
};
}
fn match_struct_enum(x: SEnum) {
let _ = || {
let (SEnum::A { a } | SEnum::B { a }) = x;
a
};
}
enum TSingle {
A(u32, u32),
}
enum SSingle {
A { a: u32, b: u32 },
}
struct TStruct(u32, u32);
struct SStruct { a: u32, b: u32 }
fn match_struct(mut x: SStruct) {
let r = &mut x.a;
let _ = || {
let SStruct { b, .. } = x;
b
};
let _ = *r;
}
fn match_tuple_struct(mut x: TStruct) {
let r = &mut x.0;
let _ = || {
let TStruct(_, a) = x;
a
};
let _ = *r;
}
fn match_singleton(mut x: SSingle) {
let SSingle::A { a: ref mut r, .. } = x;
let _ = || {
let SSingle::A { b, .. } = x;
b
};
let _ = *r;
}
fn match_tuple_singleton(mut x: TSingle) {
let TSingle::A(ref mut r, _) = x;
let _ = || {
let TSingle::A(_, a) = x;
a
};
let _ = *r;
}
fn match_slice(x: (&[u32], u32, u32)) {
let _ = || {
let (([], a, _) | ([_, ..], _, a)) = x;
a
};
}
// Original testcase, for completeness
enum Camera {
Normal { base_transform: i32 },
Volume { transform: i32 },
}
fn draw_ui(camera: &mut Camera) {
|| {
let (Camera::Normal {
base_transform: _transform,
}
| Camera::Volume {
transform: _transform,
}) = camera;
};
}
fn draw_ui2(camera: &mut Camera) {
|| {
let (Camera::Normal {
base_transform: _,
}
| Camera::Volume {
transform: _,
}) = camera;
};
}
fn main() {}