Skip to content

Commit df2e334

Browse files
authored
Rollup merge of rust-lang#138528 - dianne:implicit-deref-patterns, r=Nadrieril
deref patterns: implement implicit deref patterns This implements implicit deref patterns (per https://hackmd.io/4qDDMcvyQ-GDB089IPcHGg#Implicit-deref-patterns) and adds tests and an unstable book chapter. Best reviewed commit-by-commit. Overall there's a lot of additions, but a lot of that is tests, documentation, and simple(?) refactoring. Tracking issue: rust-lang#87121 r? `@Nadrieril`
2 parents 15c4cce + 3b91b7a commit df2e334

33 files changed

+944
-170
lines changed

compiler/rustc_hir_analysis/src/autoderef.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use rustc_infer::infer::InferCtxt;
22
use rustc_infer::traits::PredicateObligations;
33
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
44
use rustc_session::Limit;
5-
use rustc_span::Span;
65
use rustc_span::def_id::{LOCAL_CRATE, LocalDefId};
6+
use rustc_span::{ErrorGuaranteed, Span};
77
use rustc_trait_selection::traits::ObligationCtxt;
88
use tracing::{debug, instrument};
99

@@ -259,7 +259,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
259259
}
260260
}
261261

262-
pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
262+
pub fn report_autoderef_recursion_limit_error<'tcx>(
263+
tcx: TyCtxt<'tcx>,
264+
span: Span,
265+
ty: Ty<'tcx>,
266+
) -> ErrorGuaranteed {
263267
// We've reached the recursion limit, error gracefully.
264268
let suggested_limit = match tcx.recursion_limit() {
265269
Limit(0) => Limit(2),
@@ -270,5 +274,5 @@ pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Spa
270274
ty,
271275
suggested_limit,
272276
crate_name: tcx.crate_name(LOCAL_CRATE),
273-
});
277+
})
274278
}

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+46-14
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 };
@@ -1227,9 +1229,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
12271229
// actually this is somewhat "disjoint" from the code below
12281230
// that aims to account for `ref x`.
12291231
if let Some(vec) = self.cx.typeck_results().pat_adjustments().get(pat.hir_id) {
1230-
if let Some(first_ty) = vec.first() {
1231-
debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty);
1232-
return Ok(*first_ty);
1232+
if let Some(first_adjust) = vec.first() {
1233+
debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust);
1234+
return Ok(first_adjust.source);
12331235
}
12341236
} else if let PatKind::Ref(subpat, _) = pat.kind
12351237
&& self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id)
@@ -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

0 commit comments

Comments
 (0)