From a0d9c8726678fdc6a43d51e388e1a92e388d5fc2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Apr 2025 14:34:47 +0200 Subject: [PATCH 1/6] Inline `calc_default_binding_mode` --- compiler/rustc_hir_typeck/src/pat.rs | 158 +++++++++++---------------- 1 file changed, 66 insertions(+), 92 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 8641348bffbbe..e6e262279cd9f 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, } @@ -322,6 +319,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[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; + #[cfg(debug_assertions)] + if binding_mode == ByRef::Yes(Mutability::Mut) + && max_ref_mutbl != MutblCap::Mut + && self.downgrade_mut_inside_shared() + { + span_bug!(pat.span, "Pattern mutability cap violated!"); + } let path_res = match pat.kind { PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { @@ -330,8 +334,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => 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 (expected, binding_mode, max_ref_mutbl) = match adjust_mode { + // When we perform destructuring assignment, we disable default match bindings, which + // are unintuitive in this context. + _ if !pat.default_binding_modes => (expected, ByRef::No, MutblCap::Mut), + AdjustMode::Pass => (expected, binding_mode, max_ref_mutbl), + // 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. + AdjustMode::Peel => { + let mut binding_mode = binding_mode; + let mut max_ref_mutbl = max_ref_mutbl; + 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); + binding_mode = ByRef::Yes(match 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, + }); + } + + 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; + } + + if !pat_adjustments.is_empty() { + debug!("default binding mode is now {:?}", binding_mode); + self.typeck_results + .borrow_mut() + .pat_adjustments_mut() + .insert(pat.hir_id, pat_adjustments); + } + + (expected, binding_mode, max_ref_mutbl) + } + }; let pat_info = PatInfo { binding_mode, max_ref_mutbl, @@ -437,39 +498,10 @@ 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( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - def_br: ByRef, - adjust_mode: AdjustMode, - max_ref_mutbl: MutblCap, - ) -> (Ty<'tcx>, ByRef, MutblCap) { - #[cfg(debug_assertions)] - if def_br == ByRef::Yes(Mutability::Mut) - && 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), - } - } - /// How should the binding mode and expected type be adjusted? /// /// 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) -> 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 +558,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 <.kind { rustc_hir::PatExprKind::Lit { lit, negated } => { From 9f57903e9cdafda3e32ce0e3472ad7fe2a9c1e3a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Apr 2025 14:38:16 +0200 Subject: [PATCH 2/6] Insert adjustments incrementally --- compiler/rustc_hir_typeck/src/pat.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index e6e262279cd9f..70eb21ca3560f 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -354,13 +354,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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); + self.typeck_results + .borrow_mut() + .pat_adjustments_mut() + .entry(pat.hir_id) + .or_default() + .push(expected); expected = self.try_structurally_resolve_type(pat.span, inner_ty); binding_mode = ByRef::Yes(match binding_mode { @@ -382,13 +386,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { max_ref_mutbl = MutblCap::Not; } - if !pat_adjustments.is_empty() { - debug!("default binding mode is now {:?}", binding_mode); - self.typeck_results - .borrow_mut() - .pat_adjustments_mut() - .insert(pat.hir_id, pat_adjustments); - } + debug!("default binding mode is now {:?}", binding_mode); (expected, binding_mode, max_ref_mutbl) } From 19950b52c70a5989c4937c5a91e1184439bdad5c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Apr 2025 14:52:52 +0200 Subject: [PATCH 3/6] Turn the peeling loop into a recursive call --- compiler/rustc_hir_typeck/src/pat.rs | 80 ++++++++++++++++------------ 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 70eb21ca3560f..ca51a28c4be85 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -318,6 +318,25 @@ 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 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, opt_path_res.map(|(res, ..)| res)); + self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info); + } + + // Helper to avoid resolving the same path pattern several times. + fn check_pat_inner( + &self, + pat: &'tcx Pat<'tcx>, + opt_path_res: Option<(Res, Option>, &'tcx [hir::PathSegment<'tcx>])>, + adjust_mode: AdjustMode, + expected: Ty<'tcx>, + pat_info: PatInfo<'tcx>, + ) { let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; #[cfg(debug_assertions)] if binding_mode == ByRef::Yes(Mutability::Mut) @@ -327,34 +346,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span_bug!(pat.span, "Pattern mutability cap violated!"); } - let 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) = match adjust_mode { // When we perform destructuring assignment, we disable default match bindings, which // are unintuitive in this context. _ if !pat.default_binding_modes => (expected, ByRef::No, MutblCap::Mut), AdjustMode::Pass => (expected, binding_mode, max_ref_mutbl), - // 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. + // Peel an immediately nested `& mut?` from the expected type if possible and return the + // new expected type and binding default binding mode. AdjustMode::Peel => { - let mut binding_mode = binding_mode; - let mut max_ref_mutbl = max_ref_mutbl; - 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. + let expected = self.try_structurally_resolve_type(pat.span, expected); + // Peel off a `&` or `&mut` from the scrutinee type. 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`. - while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() { + // See the examples in `tests/ui/rfcs/rfc-2005-default-binding-mode`. + if let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() { debug!("inspecting {:?}", expected); debug!("current discriminant is Ref, inserting implicit deref"); @@ -366,8 +371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .or_default() .push(expected); - expected = self.try_structurally_resolve_type(pat.span, inner_ty); - binding_mode = ByRef::Yes(match binding_mode { + let mut binding_mode = ByRef::Yes(match binding_mode { // If default binding mode is by value, make it `ref` or `ref mut` // (depending on whether we observe `&` or `&mut`). ByRef::No | @@ -377,18 +381,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This is because a `& &mut` cannot mutate the underlying value. ByRef::Yes(Mutability::Not) => Mutability::Not, }); - } - 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; + if self.downgrade_mut_inside_shared() { + binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); + } + let mut max_ref_mutbl = max_ref_mutbl; + if binding_mode == ByRef::Yes(Mutability::Not) { + max_ref_mutbl = MutblCap::Not; + } + debug!("default binding mode is now {:?}", binding_mode); + let pat_info = PatInfo { binding_mode, max_ref_mutbl, ..pat_info }; + return self.check_pat_inner( + pat, + opt_path_res, + adjust_mode, + inner_ty, + pat_info, + ); + } else { + (expected, binding_mode, max_ref_mutbl) } - - debug!("default binding mode is now {:?}", binding_mode); - - (expected, binding_mode, max_ref_mutbl) } }; let pat_info = PatInfo { @@ -409,7 +421,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat.hir_id, *span, qpath, - path_res.unwrap(), + opt_path_res.unwrap(), expected, &pat_info.top_info, ); From 38adb9931cd934fefe2a2f65ca0071612c9ec583 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Apr 2025 15:08:55 +0200 Subject: [PATCH 4/6] Reorganize `check_pat_inner` --- compiler/rustc_hir_typeck/src/pat.rs | 118 ++++++++++++++------------- 1 file changed, 61 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ca51a28c4be85..8f01bb1c297c6 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -337,7 +337,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx>, ) { - let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; + let PatInfo { mut binding_mode, mut max_ref_mutbl, current_depth, .. } = pat_info; #[cfg(debug_assertions)] if binding_mode == ByRef::Yes(Mutability::Mut) && max_ref_mutbl != MutblCap::Mut @@ -346,72 +346,76 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span_bug!(pat.span, "Pattern mutability cap violated!"); } - let (expected, binding_mode, max_ref_mutbl) = match adjust_mode { + if !pat.default_binding_modes { // When we perform destructuring assignment, we disable default match bindings, which // are unintuitive in this context. - _ if !pat.default_binding_modes => (expected, ByRef::No, MutblCap::Mut), - AdjustMode::Pass => (expected, binding_mode, max_ref_mutbl), - // Peel an immediately nested `& mut?` from the expected type if possible and return the - // new expected type and binding default binding mode. - AdjustMode::Peel => { - let expected = self.try_structurally_resolve_type(pat.span, expected); - // Peel off a `&` or `&mut` from the scrutinee type. For each ampersand peeled off, - // update the binding mode and push the original type into the adjustments vector. - // - // See the examples in `tests/ui/rfcs/rfc-2005-default-binding-mode`. - if 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 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, - }); - - if self.downgrade_mut_inside_shared() { - binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); - } - let mut max_ref_mutbl = max_ref_mutbl; - if binding_mode == ByRef::Yes(Mutability::Not) { - max_ref_mutbl = MutblCap::Not; - } - debug!("default binding mode is now {:?}", binding_mode); - let pat_info = PatInfo { binding_mode, max_ref_mutbl, ..pat_info }; - return self.check_pat_inner( - pat, - opt_path_res, - adjust_mode, - inner_ty, - pat_info, - ); - } else { - (expected, binding_mode, max_ref_mutbl) - } - } + binding_mode = ByRef::No; + max_ref_mutbl = MutblCap::Mut; + }; + // 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 { binding_mode, max_ref_mutbl, - top_info: ti, - decl_origin: pat_info.decl_origin, current_depth: current_depth + 1, + top_info: old_pat_info.top_info, + decl_origin: old_pat_info.decl_origin, }; let ty = 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); + + binding_mode = ByRef::Yes(match 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, + }); + + 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 }; + return 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, From 6588018fb413efe0476773982355ba772671bd85 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Apr 2025 15:10:47 +0200 Subject: [PATCH 5/6] Return a type from `check_pat_inner` --- compiler/rustc_hir_typeck/src/pat.rs | 104 +++++++++++++-------------- 1 file changed, 49 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 8f01bb1c297c6..c635c2c3f2d22 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -325,7 +325,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, opt_path_res.map(|(res, ..)| res)); - self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info); + 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 + // (literals and constants being the exception), we relate types + // using strict equality, even though subtyping would be sufficient. + // There are a few reasons for this, some of which are fairly subtle + // and which cost me (nmatsakis) an hour or two debugging to remember, + // so I thought I'd write them down this time. + // + // 1. There is no loss of expressiveness here, though it does + // cause some inconvenience. What we are saying is that the type + // of `x` becomes *exactly* what is expected. This can cause unnecessary + // errors in some cases, such as this one: + // + // ``` + // fn foo<'x>(x: &'x i32) { + // let a = 1; + // let mut z = x; + // z = &a; + // } + // ``` + // + // The reason we might get an error is that `z` might be + // assigned a type like `&'x i32`, and then we would have + // a problem when we try to assign `&a` to `z`, because + // the lifetime of `&a` (i.e., the enclosing block) is + // shorter than `'x`. + // + // HOWEVER, this code works fine. The reason is that the + // expected type here is whatever type the user wrote, not + // the initializer's type. In this case the user wrote + // nothing, so we are going to create a type variable `Z`. + // Then we will assign the type of the initializer (`&'x i32`) + // as a subtype of `Z`: `&'x i32 <: Z`. And hence we + // will instantiate `Z` as a type `&'0 i32` where `'0` is + // a fresh region variable, with the constraint that `'x : '0`. + // So basically we're all set. + // + // Note that there are two tests to check that this remains true + // (`regions-reassign-{match,let}-bound-pointer.rs`). + // + // 2. An outdated issue related to the old HIR borrowck. See the test + // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, } // Helper to avoid resolving the same path pattern several times. @@ -336,7 +379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { adjust_mode: AdjustMode, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>, - ) { + ) -> Ty<'tcx> { let PatInfo { mut binding_mode, mut max_ref_mutbl, current_depth, .. } = pat_info; #[cfg(debug_assertions)] if binding_mode == ByRef::Yes(Mutability::Mut) @@ -369,7 +412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl_origin: old_pat_info.decl_origin, }; - let ty = match pat.kind { + 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 @@ -408,13 +451,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 }; - return self.check_pat_inner( - pat, - opt_path_res, - adjust_mode, - inner_ty, - new_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. @@ -465,51 +503,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Slice(before, slice, after) => { self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) } - }; - - self.write_ty(pat.hir_id, ty); - - // (note_1): In most of the cases where (note_1) is referenced - // (literals and constants being the exception), we relate types - // using strict equality, even though subtyping would be sufficient. - // There are a few reasons for this, some of which are fairly subtle - // and which cost me (nmatsakis) an hour or two debugging to remember, - // so I thought I'd write them down this time. - // - // 1. There is no loss of expressiveness here, though it does - // cause some inconvenience. What we are saying is that the type - // of `x` becomes *exactly* what is expected. This can cause unnecessary - // errors in some cases, such as this one: - // - // ``` - // fn foo<'x>(x: &'x i32) { - // let a = 1; - // let mut z = x; - // z = &a; - // } - // ``` - // - // The reason we might get an error is that `z` might be - // assigned a type like `&'x i32`, and then we would have - // a problem when we try to assign `&a` to `z`, because - // the lifetime of `&a` (i.e., the enclosing block) is - // shorter than `'x`. - // - // HOWEVER, this code works fine. The reason is that the - // expected type here is whatever type the user wrote, not - // the initializer's type. In this case the user wrote - // nothing, so we are going to create a type variable `Z`. - // Then we will assign the type of the initializer (`&'x i32`) - // as a subtype of `Z`: `&'x i32 <: Z`. And hence we - // will instantiate `Z` as a type `&'0 i32` where `'0` is - // a fresh region variable, with the constraint that `'x : '0`. - // So basically we're all set. - // - // Note that there are two tests to check that this remains true - // (`regions-reassign-{match,let}-bound-pointer.rs`). - // - // 2. An outdated issue related to the old HIR borrowck. See the test - // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, + } } /// How should the binding mode and expected type be adjusted? From f458151b7aa773a46dfce8c58aa41de9ed697c5e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Apr 2025 15:48:07 +0200 Subject: [PATCH 6/6] Remove redundant assignment --- compiler/rustc_hir_typeck/src/pat.rs | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index c635c2c3f2d22..fbc783c050904 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -380,21 +380,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { - let PatInfo { mut binding_mode, mut max_ref_mutbl, current_depth, .. } = pat_info; #[cfg(debug_assertions)] - if binding_mode == 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!"); } - if !pat.default_binding_modes { - // When we perform destructuring assignment, we disable default match bindings, which - // are unintuitive in this context. - binding_mode = ByRef::No; - max_ref_mutbl = MutblCap::Mut; - }; // Resolve type if needed. let expected = if let AdjustMode::Peel = adjust_mode && pat.default_binding_modes @@ -404,13 +397,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected }; let old_pat_info = pat_info; - let pat_info = PatInfo { - binding_mode, - max_ref_mutbl, - current_depth: current_depth + 1, - top_info: old_pat_info.top_info, - decl_origin: old_pat_info.decl_origin, - }; + 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 @@ -430,7 +417,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .or_default() .push(expected); - binding_mode = ByRef::Yes(match binding_mode { + 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 | @@ -441,6 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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()); }