Skip to content

Commit bd7196c

Browse files
committed
upvar inference for implicit deref patterns
There's a bit of a hack here, which I don't love, but the other ways I could think of doing this were substantially messier. I'll be saving that for a separate commit to reduce the noise in this one. I also haven't written that separate commit yet though, since I'm not convinced the alternatives I've thought of are better than this. Please let me know if one of these (or better yet, something else) sounds good: - The callback argument to `cat_pattern` could take a `PatOrAdjust<'hir>` enum instead of a `Pat<'hir>`, which would correspond either to a (potentially scrutinee-borrowing) pattern or a (currently definitely scrutinee-borrowing) implicit overloaded deref. Matching on `PatOrAdjust` is really noisy, unfortunately, and would require a separate `cat_pattern_unadjusted` helper that bypasses it to avoid reindenting `maybe_read_scrutinee`. - `cat_pattern` could invoke the delegate's `borrow` callback directly. This results in some repeated work between `walk_pat` and `maybe_read_scrutinee` for refutable patterns (but hopefully that wouldn't have correctness implications..?). `cat_pattern` would also need to take the `HirId` of the top-level scrutinee expression as an argument for use in diagnostics.
1 parent da43919 commit bd7196c

File tree

2 files changed

+69
-11
lines changed

2 files changed

+69
-11
lines changed

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

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

@@ -1782,14 +1802,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
17821802
self.cat_pattern(subplace, subpat, op)?;
17831803
}
17841804
PatKind::Deref(subpat) => {
1785-
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpat);
1786-
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1787-
let re_erased = self.cx.tcx().lifetimes.re_erased;
17881805
let ty = self.pat_ty_adjusted(subpat)?;
1789-
let ty = Ty::new_ref(self.cx.tcx(), re_erased, ty, mutability);
1790-
// A deref pattern generates a temporary.
1791-
let base = self.cat_rvalue(pat.hir_id, ty);
1792-
let place = self.cat_deref(pat.hir_id, base)?;
1806+
let place = self.pat_deref_temp(pat.hir_id, subpat, ty)?;
17931807
self.cat_pattern(place, subpat, op)?;
17941808
}
17951809

@@ -1841,6 +1855,23 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18411855
Ok(())
18421856
}
18431857

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

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

Lines changed: 27 additions & 0 deletions
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)