diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index a0a5a75d382..cc42e69f538 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -750,6 +750,15 @@ fn walk_pat( } } } + } else if let PatKind::Deref(subpattern) = pat.kind { + // A deref pattern is a bit special: the binding mode of its inner bindings + // determines whether to borrow *at the level of the deref pattern* rather than + // borrowing the bound place (since that inner place is inside the temporary that + // stores the result of calling `deref()`/`deref_mut()` so can't be captured). + let mutable = mc.typeck_results.pat_has_ref_mut_binding(subpattern); + let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; + let bk = ty::BorrowKind::from_mutbl(mutability); + delegate.borrow(place, discr_place.hir_id, bk); } })); } diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 15f802560fd..b44c2345933 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -719,7 +719,11 @@ fn cat_pattern_( self.cat_pattern_(subplace, subpat, op)?; } PatKind::Deref(subpat) => { + let mutable = self.typeck_results.pat_has_ref_mut_binding(subpat); + let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; + let re_erased = self.tcx().lifetimes.re_erased; let ty = self.pat_ty_adjusted(subpat)?; + let ty = Ty::new_ref(self.tcx(), re_erased, ty, mutability); // A deref pattern generates a temporary. let place = self.cat_rvalue(pat.hir_id, ty); self.cat_pattern_(place, subpat, op)?; diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index a28afcc4fb8..25bca70f102 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -451,7 +451,7 @@ pub fn skipped_ref_pats_mut(&mut self) -> LocalSetInContextMut<'_> { /// This is computed from the typeck results since we want to make /// sure to apply any match-ergonomics adjustments, which we cannot /// determine from the HIR alone. - pub fn pat_has_ref_mut_binding(&self, pat: &'tcx hir::Pat<'tcx>) -> bool { + pub fn pat_has_ref_mut_binding(&self, pat: &hir::Pat<'_>) -> bool { let mut has_ref_mut = false; pat.walk(|pat| { if let hir::PatKind::Binding(_, id, _, _) = pat.kind diff --git a/tests/ui/pattern/deref-patterns/closure_capture.rs b/tests/ui/pattern/deref-patterns/closure_capture.rs new file mode 100644 index 00000000000..fc0ddedac2b --- /dev/null +++ b/tests/ui/pattern/deref-patterns/closure_capture.rs @@ -0,0 +1,21 @@ +//@ run-pass +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +fn main() { + let b = Box::new("aaa".to_string()); + let f = || { + let deref!(ref s) = b else { unreachable!() }; + assert_eq!(s.len(), 3); + }; + assert_eq!(b.len(), 3); + f(); + + let mut b = Box::new("aaa".to_string()); + let mut f = || { + let deref!(ref mut s) = b else { unreachable!() }; + s.push_str("aa"); + }; + f(); + assert_eq!(b.len(), 5); +}