Skip to content

Commit c8a9095

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 1f76d21 + 3b91b7a commit c8a9095

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)
@@ -1680,12 +1682,31 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
16801682
// Then we see that to get the same result, we must start with
16811683
// `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
16821684
// and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
1683-
for _ in
1684-
0..self.cx.typeck_results().pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len())
1685-
{
1685+
let typeck_results = self.cx.typeck_results();
1686+
let adjustments: &[adjustment::PatAdjustment<'tcx>] =
1687+
typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
1688+
let mut adjusts = adjustments.iter().peekable();
1689+
while let Some(adjust) = adjusts.next() {
16861690
debug!("applying adjustment to place_with_id={:?}", place_with_id);
1687-
place_with_id = self.cat_deref(pat.hir_id, place_with_id)?;
1691+
place_with_id = match adjust.kind {
1692+
adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?,
1693+
adjustment::PatAdjust::OverloadedDeref => {
1694+
// This adjustment corresponds to an overloaded deref; it borrows the scrutinee to
1695+
// call `Deref::deref` or `DerefMut::deref_mut`. Invoke the callback before setting
1696+
// `place_with_id` to the temporary storing the result of the deref.
1697+
// HACK(dianne): giving the callback a fake deref pattern makes sure it behaves the
1698+
// same as it would if this were an explicit deref pattern.
1699+
op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?;
1700+
let target_ty = match adjusts.peek() {
1701+
Some(&&next_adjust) => next_adjust.source,
1702+
// At the end of the deref chain, we get `pat`'s scrutinee.
1703+
None => self.pat_ty_unadjusted(pat)?,
1704+
};
1705+
self.pat_deref_temp(pat.hir_id, pat, target_ty)?
1706+
}
1707+
};
16881708
}
1709+
drop(typeck_results); // explicitly release borrow of typeck results, just in case.
16891710
let place_with_id = place_with_id; // lose mutability
16901711
debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
16911712

@@ -1788,14 +1809,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
17881809
self.cat_pattern(subplace, subpat, op)?;
17891810
}
17901811
PatKind::Deref(subpat) => {
1791-
let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpat);
1792-
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1793-
let re_erased = self.cx.tcx().lifetimes.re_erased;
17941812
let ty = self.pat_ty_adjusted(subpat)?;
1795-
let ty = Ty::new_ref(self.cx.tcx(), re_erased, ty, mutability);
1796-
// A deref pattern generates a temporary.
1797-
let base = self.cat_rvalue(pat.hir_id, ty);
1798-
let place = self.cat_deref(pat.hir_id, base)?;
1813+
let place = self.pat_deref_temp(pat.hir_id, subpat, ty)?;
17991814
self.cat_pattern(place, subpat, op)?;
18001815
}
18011816

@@ -1848,6 +1863,23 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18481863
Ok(())
18491864
}
18501865

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

0 commit comments

Comments
 (0)