Skip to content

Commit ff0d4bc

Browse files
committed
upvar inference for implicit deref patterns
1 parent 977c9ab commit ff0d4bc

File tree

2 files changed

+70
-11
lines changed

2 files changed

+70
-11
lines changed

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+43-11
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
10001000
// determines whether to borrow *at the level of the deref pattern* rather than
10011001
// borrowing the bound place (since that inner place is inside the temporary that
10021002
// stores the result of calling `deref()`/`deref_mut()` so can't be captured).
1003+
// HACK: this could be a fake pattern corresponding to a deref inserted by match
1004+
// ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
10031005
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern);
10041006
let mutability =
10051007
if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
@@ -1675,12 +1677,31 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
16751677
// Then we see that to get the same result, we must start with
16761678
// `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
16771679
// and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
1678-
for _ in
1679-
0..self.cx.typeck_results().pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len())
1680-
{
1680+
let typeck_results = self.cx.typeck_results();
1681+
let adjustments: &[adjustment::PatAdjustment<'tcx>] =
1682+
typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
1683+
let mut adjusts = adjustments.iter().peekable();
1684+
while let Some(adjust) = adjusts.next() {
16811685
debug!("applying adjustment to place_with_id={:?}", place_with_id);
1682-
place_with_id = self.cat_deref(pat.hir_id, place_with_id)?;
1686+
place_with_id = match adjust.kind {
1687+
adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?,
1688+
adjustment::PatAdjust::OverloadedDeref => {
1689+
// This adjustment corresponds to an overloaded deref; it borrows the scrutinee to
1690+
// call `Deref::deref` or `DerefMut::deref_mut`. Invoke the callback before setting
1691+
// `place_with_id` to the temporary storing the result of the deref.
1692+
// HACK(dianne): giving the callback a fake deref pattern makes sure it behaves the
1693+
// same as it would if this were an explicit deref pattern.
1694+
op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?;
1695+
let target_ty = match adjusts.peek() {
1696+
Some(&&next_adjust) => next_adjust.source,
1697+
// At the end of the deref chain, we get `pat`'s scrutinee.
1698+
None => self.pat_ty_unadjusted(pat)?,
1699+
};
1700+
self.pat_deref_temp(pat.hir_id, pat, target_ty)?
1701+
}
1702+
};
16831703
}
1704+
drop(typeck_results); // explicitly release borrow of typeck results, just in case.
16841705
let place_with_id = place_with_id; // lose mutability
16851706
debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
16861707

@@ -1783,14 +1804,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
17831804
self.cat_pattern(subplace, subpat, op)?;
17841805
}
17851806
PatKind::Deref(subpat) => {
1786-
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpat);
1787-
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1788-
let re_erased = self.cx.tcx().lifetimes.re_erased;
17891807
let ty = self.pat_ty_adjusted(subpat)?;
1790-
let ty = Ty::new_ref(self.cx.tcx(), re_erased, ty, mutability);
1791-
// A deref pattern generates a temporary.
1792-
let base = self.cat_rvalue(pat.hir_id, ty);
1793-
let place = self.cat_deref(pat.hir_id, base)?;
1808+
let place = self.pat_deref_temp(pat.hir_id, subpat, ty)?;
17941809
self.cat_pattern(place, subpat, op)?;
17951810
}
17961811

@@ -1843,6 +1858,23 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18431858
Ok(())
18441859
}
18451860

1861+
/// Represents the place of the temp that stores the scrutinee of a deref pattern's interior.
1862+
fn pat_deref_temp(
1863+
&self,
1864+
hir_id: HirId,
1865+
inner: &hir::Pat<'_>,
1866+
target_ty: Ty<'tcx>,
1867+
) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1868+
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(inner);
1869+
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1870+
let re_erased = self.cx.tcx().lifetimes.re_erased;
1871+
let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
1872+
// A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
1873+
let base = self.cat_rvalue(hir_id, ty);
1874+
// ... and the inner pattern matches on the place behind that reference.
1875+
self.cat_deref(hir_id, base)
1876+
}
1877+
18461878
fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
18471879
if let ty::Adt(def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() {
18481880
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need

tests/ui/pattern/deref-patterns/closure_capture.rs

+27
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,38 @@ fn main() {
1111
assert_eq!(b.len(), 3);
1212
f();
1313

14+
let v = vec![1, 2, 3];
15+
let f = || {
16+
// this should count as a borrow of `v` as a whole
17+
let [.., x] = v else { unreachable!() };
18+
assert_eq!(x, 3);
19+
};
20+
assert_eq!(v, [1, 2, 3]);
21+
f();
22+
1423
let mut b = Box::new("aaa".to_string());
1524
let mut f = || {
1625
let deref!(ref mut s) = b else { unreachable!() };
1726
s.push_str("aa");
1827
};
1928
f();
2029
assert_eq!(b.len(), 5);
30+
31+
let mut v = vec![1, 2, 3];
32+
let mut f = || {
33+
// this should count as a mutable borrow of `v` as a whole
34+
let [.., ref mut x] = v else { unreachable!() };
35+
*x = 4;
36+
};
37+
f();
38+
assert_eq!(v, [1, 2, 4]);
39+
40+
let mut v = vec![1, 2, 3];
41+
let mut f = || {
42+
// here, `[.., x]` is adjusted by both an overloaded deref and a builtin deref
43+
let [.., x] = &mut v else { unreachable!() };
44+
*x = 4;
45+
};
46+
f();
47+
assert_eq!(v, [1, 2, 4]);
2148
}

0 commit comments

Comments
 (0)