about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-04-10 11:10:14 +0200
committerGitHub <noreply@github.com>2025-04-10 11:10:14 +0200
commit7f08e638c496a91410d78d58253108f46f1ea604 (patch)
tree844ba45486a4e61c762ceb4c921c25dcfefbeb9c
parentb131828f0dbda6e3c3e07b06a11af5c3a3cac120 (diff)
parentf458151b7aa773a46dfce8c58aa41de9ed697c5e (diff)
downloadrust-7f08e638c496a91410d78d58253108f46f1ea604.tar.gz
rust-7f08e638c496a91410d78d58253108f46f1ea604.zip
Rollup merge of #139449 - Nadrieril:peel-recursive, r=lcnr
match ergonomics: replace `peel_off_references` with a recursive call

This makes it imo quite a bit easier to follow how the binding mode gets calculated.

cc ```@dianne```
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs260
1 files changed, 115 insertions, 145 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 8641348bffb..fbc783c0509 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -163,9 +163,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 enum AdjustMode {
     /// Peel off all immediate reference types.
     Peel,
-    /// Reset binding mode to the initial mode.
-    /// Used for destructuring assignment, where we don't want any match ergonomics.
-    Reset,
     /// Pass on the input binding mode and expected type.
     Pass,
 }
@@ -321,77 +318,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Conversely, inside this module, `check_pat_top` should never be used.
     #[instrument(level = "debug", skip(self, pat_info))]
     fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>) {
-        let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
-
-        let path_res = match pat.kind {
+        let opt_path_res = match pat.kind {
             PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
                 Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span))
             }
             _ => None,
         };
-        let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
-        let (expected, binding_mode, max_ref_mutbl) =
-            self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl);
-        let pat_info = PatInfo {
-            binding_mode,
-            max_ref_mutbl,
-            top_info: ti,
-            decl_origin: pat_info.decl_origin,
-            current_depth: current_depth + 1,
-        };
-
-        let ty = match pat.kind {
-            PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
-            // We allow any type here; we ensure that the type is uninhabited during match checking.
-            PatKind::Never => expected,
-            PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
-                let ty = self.check_pat_path(
-                    *hir_id,
-                    pat.hir_id,
-                    *span,
-                    qpath,
-                    path_res.unwrap(),
-                    expected,
-                    &pat_info.top_info,
-                );
-                self.write_ty(*hir_id, ty);
-                ty
-            }
-            PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, &pat_info.top_info),
-            PatKind::Range(lhs, rhs, _) => {
-                self.check_pat_range(pat.span, lhs, rhs, expected, &pat_info.top_info)
-            }
-            PatKind::Binding(ba, var_id, ident, sub) => {
-                self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info)
-            }
-            PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
-                self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info)
-            }
-            PatKind::Struct(ref qpath, fields, has_rest_pat) => {
-                self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info)
-            }
-            PatKind::Guard(pat, cond) => {
-                self.check_pat(pat, expected, pat_info);
-                self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {});
-                expected
-            }
-            PatKind::Or(pats) => {
-                for pat in pats {
-                    self.check_pat(pat, expected, pat_info);
-                }
-                expected
-            }
-            PatKind::Tuple(elements, ddpos) => {
-                self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info)
-            }
-            PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
-            PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
-            PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
-            PatKind::Slice(before, slice, after) => {
-                self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
-            }
-        };
-
+        let adjust_mode = self.calc_adjust_mode(pat, opt_path_res.map(|(res, ..)| res));
+        let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
         self.write_ty(pat.hir_id, ty);
 
         // (note_1): In most of the cases where (note_1) is referenced
@@ -437,27 +371,126 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`,
     }
 
-    /// Compute the new expected type and default binding mode from the old ones
-    /// as well as the pattern form we are currently checking.
-    fn calc_default_binding_mode(
+    // Helper to avoid resolving the same path pattern several times.
+    fn check_pat_inner(
         &self,
         pat: &'tcx Pat<'tcx>,
-        expected: Ty<'tcx>,
-        def_br: ByRef,
+        opt_path_res: Option<(Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>])>,
         adjust_mode: AdjustMode,
-        max_ref_mutbl: MutblCap,
-    ) -> (Ty<'tcx>, ByRef, MutblCap) {
+        expected: Ty<'tcx>,
+        pat_info: PatInfo<'tcx>,
+    ) -> Ty<'tcx> {
         #[cfg(debug_assertions)]
-        if def_br == ByRef::Yes(Mutability::Mut)
-            && max_ref_mutbl != MutblCap::Mut
+        if pat_info.binding_mode == ByRef::Yes(Mutability::Mut)
+            && pat_info.max_ref_mutbl != MutblCap::Mut
             && self.downgrade_mut_inside_shared()
         {
             span_bug!(pat.span, "Pattern mutability cap violated!");
         }
-        match adjust_mode {
-            AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
-            AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut),
-            AdjustMode::Peel => self.peel_off_references(pat, expected, def_br, max_ref_mutbl),
+
+        // Resolve type if needed.
+        let expected = if let AdjustMode::Peel = adjust_mode
+            && pat.default_binding_modes
+        {
+            self.try_structurally_resolve_type(pat.span, expected)
+        } else {
+            expected
+        };
+        let old_pat_info = pat_info;
+        let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info };
+
+        match pat.kind {
+            // Peel off a `&` or `&mut` from the scrutinee type. See the examples in
+            // `tests/ui/rfcs/rfc-2005-default-binding-mode`.
+            _ if let AdjustMode::Peel = adjust_mode
+                && pat.default_binding_modes
+                && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() =>
+            {
+                debug!("inspecting {:?}", expected);
+
+                debug!("current discriminant is Ref, inserting implicit deref");
+                // Preserve the reference type. We'll need it later during THIR lowering.
+                self.typeck_results
+                    .borrow_mut()
+                    .pat_adjustments_mut()
+                    .entry(pat.hir_id)
+                    .or_default()
+                    .push(expected);
+
+                let mut binding_mode = ByRef::Yes(match pat_info.binding_mode {
+                    // If default binding mode is by value, make it `ref` or `ref mut`
+                    // (depending on whether we observe `&` or `&mut`).
+                    ByRef::No |
+                    // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
+                    ByRef::Yes(Mutability::Mut) => inner_mutability,
+                    // Once a `ref`, always a `ref`.
+                    // This is because a `& &mut` cannot mutate the underlying value.
+                    ByRef::Yes(Mutability::Not) => Mutability::Not,
+                });
+
+                let mut max_ref_mutbl = pat_info.max_ref_mutbl;
+                if self.downgrade_mut_inside_shared() {
+                    binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
+                }
+                if binding_mode == ByRef::Yes(Mutability::Not) {
+                    max_ref_mutbl = MutblCap::Not;
+                }
+                debug!("default binding mode is now {:?}", binding_mode);
+
+                // Use the old pat info to keep `current_depth` to its old value.
+                let new_pat_info = PatInfo { binding_mode, max_ref_mutbl, ..old_pat_info };
+                // Recurse with the new expected type.
+                self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
+            }
+            PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
+            // We allow any type here; we ensure that the type is uninhabited during match checking.
+            PatKind::Never => expected,
+            PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
+                let ty = self.check_pat_path(
+                    *hir_id,
+                    pat.hir_id,
+                    *span,
+                    qpath,
+                    opt_path_res.unwrap(),
+                    expected,
+                    &pat_info.top_info,
+                );
+                self.write_ty(*hir_id, ty);
+                ty
+            }
+            PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, &pat_info.top_info),
+            PatKind::Range(lhs, rhs, _) => {
+                self.check_pat_range(pat.span, lhs, rhs, expected, &pat_info.top_info)
+            }
+            PatKind::Binding(ba, var_id, ident, sub) => {
+                self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info)
+            }
+            PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
+                self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info)
+            }
+            PatKind::Struct(ref qpath, fields, has_rest_pat) => {
+                self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info)
+            }
+            PatKind::Guard(pat, cond) => {
+                self.check_pat(pat, expected, pat_info);
+                self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {});
+                expected
+            }
+            PatKind::Or(pats) => {
+                for pat in pats {
+                    self.check_pat(pat, expected, pat_info);
+                }
+                expected
+            }
+            PatKind::Tuple(elements, ddpos) => {
+                self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info)
+            }
+            PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
+            PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
+            PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
+            PatKind::Slice(before, slice, after) => {
+                self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
+            }
         }
     }
 
@@ -465,11 +498,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
     fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
-        // When we perform destructuring assignment, we disable default match bindings, which are
-        // unintuitive in this context.
-        if !pat.default_binding_modes {
-            return AdjustMode::Reset;
-        }
         match &pat.kind {
             // Type checking these product-like types successfully always require
             // that the expected type be of those types and not reference types.
@@ -526,64 +554,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    /// Peel off as many immediately nested `& mut?` from the expected type as possible
-    /// and return the new expected type and binding default binding mode.
-    /// The adjustments vector, if non-empty is stored in a table.
-    fn peel_off_references(
-        &self,
-        pat: &'tcx Pat<'tcx>,
-        expected: Ty<'tcx>,
-        mut def_br: ByRef,
-        mut max_ref_mutbl: MutblCap,
-    ) -> (Ty<'tcx>, ByRef, MutblCap) {
-        let mut expected = self.try_structurally_resolve_type(pat.span, expected);
-        // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
-        // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
-        // the `Some(5)` which is not of type Ref.
-        //
-        // For each ampersand peeled off, update the binding mode and push the original
-        // type into the adjustments vector.
-        //
-        // See the examples in `ui/match-defbm*.rs`.
-        let mut pat_adjustments = vec![];
-        while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
-            debug!("inspecting {:?}", expected);
-
-            debug!("current discriminant is Ref, inserting implicit deref");
-            // Preserve the reference type. We'll need it later during THIR lowering.
-            pat_adjustments.push(expected);
-
-            expected = self.try_structurally_resolve_type(pat.span, inner_ty);
-            def_br = ByRef::Yes(match def_br {
-                // If default binding mode is by value, make it `ref` or `ref mut`
-                // (depending on whether we observe `&` or `&mut`).
-                ByRef::No |
-                // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
-                ByRef::Yes(Mutability::Mut) => inner_mutability,
-                // Once a `ref`, always a `ref`.
-                // This is because a `& &mut` cannot mutate the underlying value.
-                ByRef::Yes(Mutability::Not) => Mutability::Not,
-            });
-        }
-
-        if self.downgrade_mut_inside_shared() {
-            def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
-        }
-        if def_br == ByRef::Yes(Mutability::Not) {
-            max_ref_mutbl = MutblCap::Not;
-        }
-
-        if !pat_adjustments.is_empty() {
-            debug!("default binding mode is now {:?}", def_br);
-            self.typeck_results
-                .borrow_mut()
-                .pat_adjustments_mut()
-                .insert(pat.hir_id, pat_adjustments);
-        }
-
-        (expected, def_br, max_ref_mutbl)
-    }
-
     fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> {
         let ty = match &lt.kind {
             rustc_hir::PatExprKind::Lit { lit, negated } => {