upvar inference for implicit deref patterns

This commit is contained in:
dianne 2025-03-13 22:34:26 -07:00
parent 977c9ab7a2
commit ff0d4bc743
2 changed files with 70 additions and 11 deletions

View file

@ -1000,6 +1000,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
// 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).
// HACK: this could be a fake pattern corresponding to a deref inserted by match
// ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern);
let mutability =
if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
@ -1675,12 +1677,31 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
// Then we see that to get the same result, we must start with
// `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
// and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
for _ in
0..self.cx.typeck_results().pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len())
{
let typeck_results = self.cx.typeck_results();
let adjustments: &[adjustment::PatAdjustment<'tcx>] =
typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
let mut adjusts = adjustments.iter().peekable();
while let Some(adjust) = adjusts.next() {
debug!("applying adjustment to place_with_id={:?}", place_with_id);
place_with_id = self.cat_deref(pat.hir_id, place_with_id)?;
place_with_id = match adjust.kind {
adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?,
adjustment::PatAdjust::OverloadedDeref => {
// This adjustment corresponds to an overloaded deref; it borrows the scrutinee to
// call `Deref::deref` or `DerefMut::deref_mut`. Invoke the callback before setting
// `place_with_id` to the temporary storing the result of the deref.
// HACK(dianne): giving the callback a fake deref pattern makes sure it behaves the
// same as it would if this were an explicit deref pattern.
op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?;
let target_ty = match adjusts.peek() {
Some(&&next_adjust) => next_adjust.source,
// At the end of the deref chain, we get `pat`'s scrutinee.
None => self.pat_ty_unadjusted(pat)?,
};
self.pat_deref_temp(pat.hir_id, pat, target_ty)?
}
};
}
drop(typeck_results); // explicitly release borrow of typeck results, just in case.
let place_with_id = place_with_id; // lose mutability
debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
@ -1783,14 +1804,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
self.cat_pattern(subplace, subpat, op)?;
}
PatKind::Deref(subpat) => {
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpat);
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
let re_erased = self.cx.tcx().lifetimes.re_erased;
let ty = self.pat_ty_adjusted(subpat)?;
let ty = Ty::new_ref(self.cx.tcx(), re_erased, ty, mutability);
// A deref pattern generates a temporary.
let base = self.cat_rvalue(pat.hir_id, ty);
let place = self.cat_deref(pat.hir_id, base)?;
let place = self.pat_deref_temp(pat.hir_id, subpat, ty)?;
self.cat_pattern(place, subpat, op)?;
}
@ -1843,6 +1858,23 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
Ok(())
}
/// Represents the place of the temp that stores the scrutinee of a deref pattern's interior.
fn pat_deref_temp(
&self,
hir_id: HirId,
inner: &hir::Pat<'_>,
target_ty: Ty<'tcx>,
) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(inner);
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
let re_erased = self.cx.tcx().lifetimes.re_erased;
let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
// A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
let base = self.cat_rvalue(hir_id, ty);
// ... and the inner pattern matches on the place behind that reference.
self.cat_deref(hir_id, base)
}
fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
if let ty::Adt(def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() {
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need