Skip to content

Commit d6f91e0

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 3564b31 commit d6f91e0

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

+42-11
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
10011001
// determines whether to borrow *at the level of the deref pattern* rather than
10021002
// borrowing the bound place (since that inner place is inside the temporary that
10031003
// stores the result of calling `deref()`/`deref_mut()` so can't be captured).
1004+
// HACK: this could be a fake pattern corresponding to a deref inserted by match
1005+
// ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
10041006
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern);
10051007
let mutability =
10061008
if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
@@ -1726,12 +1728,30 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
17261728
// Then we see that to get the same result, we must start with
17271729
// `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
17281730
// and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
1729-
for _ in
1730-
0..self.cx.typeck_results().pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len())
1731-
{
1731+
let typeck_results = self.cx.typeck_results();
1732+
let adjustments: &[Ty<'tcx>] =
1733+
typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
1734+
let mut ref_tys = adjustments.iter().peekable();
1735+
while let Some(ref_ty) = ref_tys.next() {
17321736
debug!("applying adjustment to place_with_id={:?}", place_with_id);
1733-
place_with_id = self.cat_deref(pat.hir_id, place_with_id)?;
1737+
place_with_id = if ref_ty.is_ref() {
1738+
self.cat_deref(pat.hir_id, place_with_id)?
1739+
} else {
1740+
// This adjustment corresponds to an overloaded deref; it borrows the scrutinee to
1741+
// call `Deref::deref` or `DerefMut::deref_mut`. Invoke the callback before setting
1742+
// `place_with_id` to the temporary storing the result of the deref.
1743+
// HACK(dianne): giving the callback a fake deref pattern makes sure it behaves the
1744+
// same as it would if this were an explicit deref pattern.
1745+
op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?;
1746+
let target_ty = match ref_tys.peek() {
1747+
Some(&&target_ty) => target_ty,
1748+
// At the end of the deref chain, we get `pat`'s scrutinee.
1749+
None => self.pat_ty_unadjusted(pat)?,
1750+
};
1751+
self.pat_deref_temp(pat.hir_id, pat, target_ty)?
1752+
};
17341753
}
1754+
drop(typeck_results); // explicitly release borrow of typeck results, just in case.
17351755
let place_with_id = place_with_id; // lose mutability
17361756
debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
17371757

@@ -1834,14 +1854,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18341854
self.cat_pattern(subplace, subpat, op)?;
18351855
}
18361856
PatKind::Deref(subpat) => {
1837-
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpat);
1838-
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1839-
let re_erased = self.cx.tcx().lifetimes.re_erased;
18401857
let ty = self.pat_ty_adjusted(subpat)?;
1841-
let ty = Ty::new_ref(self.cx.tcx(), re_erased, ty, mutability);
1842-
// A deref pattern generates a temporary.
1843-
let base = self.cat_rvalue(pat.hir_id, ty);
1844-
let place = self.cat_deref(pat.hir_id, base)?;
1858+
let place = self.pat_deref_temp(pat.hir_id, subpat, ty)?;
18451859
self.cat_pattern(place, subpat, op)?;
18461860
}
18471861

@@ -1893,6 +1907,23 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18931907
Ok(())
18941908
}
18951909

1910+
/// Represents the place of the temp that stores the scrutinee of a deref pattern's interior.
1911+
fn pat_deref_temp(
1912+
&self,
1913+
hir_id: HirId,
1914+
inner: &hir::Pat<'_>,
1915+
target_ty: Ty<'tcx>,
1916+
) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1917+
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(inner);
1918+
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1919+
let re_erased = self.cx.tcx().lifetimes.re_erased;
1920+
let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
1921+
// A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
1922+
let base = self.cat_rvalue(hir_id, ty);
1923+
// ... and the inner pattern matches on the place behind that reference.
1924+
self.cat_deref(hir_id, base)
1925+
}
1926+
18961927
fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
18971928
if let ty::Adt(def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() {
18981929
// 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)