about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_mir_build/src/build/matches/simplify.rs63
1 files changed, 41 insertions, 22 deletions
diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs
index c2e44a06321..8563c2d6dfb 100644
--- a/compiler/rustc_mir_build/src/build/matches/simplify.rs
+++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs
@@ -38,16 +38,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         &mut self,
         candidate: &mut Candidate<'pat, 'tcx>,
     ) -> bool {
-        // repeatedly simplify match pairs until fixed point is reached
         debug!("{candidate:#?}");
-
-        // existing_bindings and new_bindings exists to keep the semantics in order.
+        // `original_bindings` and `new_bindings` exist to keep the semantics in order.
         // Reversing the binding order for bindings after `@` changes the binding order in places
-        // it shouldn't be changed, for example `let (Some(a), Some(b)) = (x, y)`
+        // where it shouldn't be changed, for example `let (Some(a), Some(b)) = (x, y)`.
         //
         // To avoid this, the binding occurs in the following manner:
-        // * the bindings for one iteration of the following loop occurs in order (i.e. left to
-        // right)
+        // * the bindings for one iteration of the loop occurs in order (i.e. left to right)
         // * the bindings from the previous iteration of the loop is prepended to the bindings from
         // the current iteration (in the implementation this is done by mem::swap and extend)
         // * after all iterations, these new bindings are then appended to the bindings that were
@@ -59,8 +56,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // binding in iter 2: [6, 7]
         //
         // final binding: [1, 2, 3, 6, 7, 4, 5]
-        let mut existing_bindings = mem::take(&mut candidate.bindings);
+        //
+        // This is because we treat refutable and irrefutable bindings differently. The binding
+        // order should be right-to-left if there are more _irrefutable_ bindings after `@` to
+        // please the borrow checker (#69971)
+        // Ex
+        // struct NonCopyStruct {
+        //     copy_field: u32,
+        // }
+        //
+        // fn foo1(x: NonCopyStruct) {
+        //     let y @ NonCopyStruct { copy_field: z } = x;
+        //     // the above should turn into
+        //     let z = x.copy_field;
+        //     let y = x;
+        // }
+        //
+        // If however the bindings are refutable, i.e. under a test, then we keep the bindings
+        // left-to-right.
+        // Ex
+        // enum NonCopyEnum {
+        //     Variant { copy_field: u32 },
+        //     None,
+        // }
+        //
+        // fn foo2(x: NonCopyEnum) {
+        //     let y @ NonCopyEnum::Variant { copy_field: z } = x else { return };
+        //     // turns into
+        //     let y = x;
+        //     let z = (x as Variant).copy_field;
+        //     // and raises an error
+        // }
+        let mut original_bindings = mem::take(&mut candidate.bindings);
         let mut new_bindings = Vec::new();
+        // Repeatedly simplify match pairs until fixed point is reached
         loop {
             let mut changed = false;
             for match_pair in mem::take(&mut candidate.match_pairs) {
@@ -74,19 +103,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 }
             }
 
-            // Avoid issue #69971: the binding order should be right to left if there are more
-            // bindings after `@` to please the borrow checker
-            // Ex
-            // struct NonCopyStruct {
-            //     copy_field: u32,
-            // }
-            //
-            // fn foo1(x: NonCopyStruct) {
-            //     let y @ NonCopyStruct { copy_field: z } = x;
-            //     // the above should turn into
-            //     let z = x.copy_field;
-            //     let y = x;
-            // }
+            // This does: new_bindings = candidate.bindings.take() ++ new_bindings
             candidate.bindings.extend_from_slice(&new_bindings);
             mem::swap(&mut candidate.bindings, &mut new_bindings);
             candidate.bindings.clear();
@@ -97,8 +114,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
         }
 
-        existing_bindings.extend_from_slice(&new_bindings);
-        mem::swap(&mut candidate.bindings, &mut existing_bindings);
+        // Restore original bindings and append the new ones.
+        // This does: candidate.bindings = original_bindings ++ new_bindings
+        mem::swap(&mut candidate.bindings, &mut original_bindings);
+        candidate.bindings.extend_from_slice(&new_bindings);
 
         let did_expand_or =
             if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place }] =