Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6a5b56a

Browse files
committedMar 15, 2025
use an enum to distinguish builtin/overloaded pat adjusts
This makes the existing code a little less fragile, but it's also future-proofing. In another PR, I plan on lowering deref patterns on boxes to THIR `PatKind::Deref` rather than `PatKind::DerefPattern`, which should allow moving out of them when matching (like how box patterns are implemented). Having an enum means we can change just `Ty::pat_adjust_kind` to handle this for implicit deref patterns, rather than checking for `ty.is_ref() || ty.is_box()` everywhere we were checking `ty.is_ref()`. This could maybe be made a bit more robust by introducing a `PatAdjustment` struct analogous to `ty::adjustment::Adjustment`, but I think that'd probably be too heavy-weight for how this is used. If there were more kinds of or subtleties to pattern adjustments, that might be helpful, though. I'm not totally sure this belongs in `rustc_middle::ty::adjustment`, but the only other place I could think of it was `rustc_middle::ty::util`. I think this placement makes enough sense to avoid putting more things in `ty::util`, even if it's not a perfect fit.
1 parent 4c1ac36 commit 6a5b56a

File tree

4 files changed

+47
-23
lines changed

4 files changed

+47
-23
lines changed
 

‎compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+16-15
Original file line numberDiff line numberDiff line change
@@ -1734,21 +1734,22 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
17341734
let mut ref_tys = adjustments.iter().peekable();
17351735
while let Some(ref_ty) = ref_tys.next() {
17361736
debug!("applying adjustment to place_with_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)?
1737+
place_with_id = match ref_ty.pat_adjust_kind() {
1738+
adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?,
1739+
adjustment::PatAdjust::OverloadedDeref => {
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+
}
17521753
};
17531754
}
17541755
drop(typeck_results); // explicitly release borrow of typeck results, just in case.

‎compiler/rustc_hir_typeck/src/pat.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use rustc_trait_selection::infer::InferCtxtExt;
3030
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
3131
use tracing::{debug, instrument, trace};
3232
use ty::VariantDef;
33+
use ty::adjustment::PatAdjust;
3334

3435
use super::report_unexpected_variant_res;
3536
use crate::expectation::Expectation;
@@ -439,7 +440,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
439440
// If we implicitly inserted overloaded dereferences before matching, check the pattern to
440441
// see if the dereferenced types need `DerefMut` bounds.
441442
if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id)
442-
&& derefed_tys.iter().any(|ty| !ty.is_ref())
443+
&& derefed_tys.iter().any(|ty| ty.pat_adjust_kind() == PatAdjust::OverloadedDeref)
443444
{
444445
self.register_deref_mut_bounds_if_needed(
445446
pat.span,

‎compiler/rustc_middle/src/ty/adjustment.rs

+20
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,23 @@ pub enum CustomCoerceUnsized {
214214
/// Records the index of the field being coerced.
215215
Struct(FieldIdx),
216216
}
217+
218+
/// Represents implicit coercions of patterns' types, rather than values' types.
219+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
220+
pub enum PatAdjust {
221+
/// An implicit dereference before matching, such as when matching the pattern `0` against a
222+
/// scrutinee of type `&u8` or `&mut u8`.
223+
// FIXME(deref_patterns): this can also be used for boxes to allow moving out of them
224+
BuiltinDeref,
225+
/// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the
226+
/// pattern `[..]` against a scrutinee of type `Vec<T>`.
227+
OverloadedDeref,
228+
}
229+
230+
impl<'tcx> Ty<'tcx> {
231+
/// Given an adjusted pattern type (see [`ty::typeck_results::TypeckResults::pat_adjustments`]),
232+
/// determine what operation dereferences it, e.g. for building the pattern's THIR.
233+
pub fn pat_adjust_kind(self) -> PatAdjust {
234+
if self.is_ref() { PatAdjust::BuiltinDeref } else { PatAdjust::OverloadedDeref }
235+
}
236+
}

‎compiler/rustc_mir_build/src/thir/pattern/mod.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_middle::mir::interpret::LitToConstInput;
1717
use rustc_middle::thir::{
1818
Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
1919
};
20+
use rustc_middle::ty::adjustment::PatAdjust;
2021
use rustc_middle::ty::layout::IntegerExt;
2122
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt};
2223
use rustc_middle::{bug, span_bug};
@@ -106,13 +107,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
106107
let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| {
107108
debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty);
108109
let span = thir_pat.span;
109-
// TODO: use an enum to distinguish between builtin and overloaded derefs
110-
let kind = if ref_ty.is_ref() {
111-
PatKind::Deref { subpattern: thir_pat }
112-
} else {
113-
let mutable = self.typeck_results.pat_has_ref_mut_binding(pat);
114-
let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
115-
PatKind::DerefPattern { subpattern: thir_pat, mutability }
110+
let kind = match ref_ty.pat_adjust_kind() {
111+
PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat },
112+
PatAdjust::OverloadedDeref => {
113+
let mutable = self.typeck_results.pat_has_ref_mut_binding(pat);
114+
let mutability =
115+
if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
116+
PatKind::DerefPattern { subpattern: thir_pat, mutability }
117+
}
116118
};
117119
Box::new(Pat { span, ty: *ref_ty, kind })
118120
});

0 commit comments

Comments
 (0)
Failed to load comments.