1
Fork 0

store the kind of pattern adjustments in pat_adjustments

This allows us to better distinguish builtin and overloaded implicit
dereferences.
This commit is contained in:
dianne 2025-03-14 18:56:15 -07:00
parent 0fe8f3454d
commit f35eae780b
8 changed files with 50 additions and 16 deletions

View file

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

View file

@ -29,6 +29,7 @@ use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
use tracing::{debug, instrument, trace}; use tracing::{debug, instrument, trace};
use ty::VariantDef; use ty::VariantDef;
use ty::adjustment::{PatAdjust, PatAdjustment};
use super::report_unexpected_variant_res; use super::report_unexpected_variant_res;
use crate::expectation::Expectation; use crate::expectation::Expectation;
@ -415,7 +416,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.pat_adjustments_mut() .pat_adjustments_mut()
.entry(pat.hir_id) .entry(pat.hir_id)
.or_default() .or_default()
.push(expected); .push(PatAdjustment { kind: PatAdjust::BuiltinDeref, source: expected });
let mut binding_mode = ByRef::Yes(match pat_info.binding_mode { let mut binding_mode = ByRef::Yes(match pat_info.binding_mode {
// If default binding mode is by value, make it `ref` or `ref mut` // If default binding mode is by value, make it `ref` or `ref mut`

View file

@ -214,3 +214,25 @@ pub enum CustomCoerceUnsized {
/// Records the index of the field being coerced. /// Records the index of the field being coerced.
Struct(FieldIdx), Struct(FieldIdx),
} }
/// Represents an implicit coercion applied to the scrutinee of a match before testing a pattern
/// against it. Currently, this is used only for implicit dereferences.
#[derive(Clone, Copy, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct PatAdjustment<'tcx> {
pub kind: PatAdjust,
/// The type of the scrutinee before the adjustment is applied, or the "adjusted type" of the
/// pattern.
pub source: Ty<'tcx>,
}
/// Represents implicit coercions of patterns' types, rather than values' types.
#[derive(Clone, Copy, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)]
#[derive(TypeFoldable, TypeVisitable)]
pub enum PatAdjust {
/// An implicit dereference before matching, such as when matching the pattern `0` against a
/// scrutinee of type `&u8` or `&mut u8`.
BuiltinDeref,
/// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the
/// pattern `[..]` against a scrutinee of type `Vec<T>`.
OverloadedDeref,
}

View file

@ -61,6 +61,12 @@ impl<'tcx> fmt::Debug for ty::adjustment::Adjustment<'tcx> {
} }
} }
impl<'tcx> fmt::Debug for ty::adjustment::PatAdjustment<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} -> {:?}", self.source, self.kind)
}
}
impl fmt::Debug for ty::BoundRegionKind { impl fmt::Debug for ty::BoundRegionKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match *self {

View file

@ -90,7 +90,7 @@ pub struct TypeckResults<'tcx> {
/// ///
/// See: /// See:
/// <https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions> /// <https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions>
pat_adjustments: ItemLocalMap<Vec<Ty<'tcx>>>, pat_adjustments: ItemLocalMap<Vec<ty::adjustment::PatAdjustment<'tcx>>>,
/// Set of reference patterns that match against a match-ergonomics inserted reference /// Set of reference patterns that match against a match-ergonomics inserted reference
/// (as opposed to against a reference in the scrutinee type). /// (as opposed to against a reference in the scrutinee type).
@ -403,11 +403,15 @@ impl<'tcx> TypeckResults<'tcx> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes } LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes }
} }
pub fn pat_adjustments(&self) -> LocalTableInContext<'_, Vec<Ty<'tcx>>> { pub fn pat_adjustments(
&self,
) -> LocalTableInContext<'_, Vec<ty::adjustment::PatAdjustment<'tcx>>> {
LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_adjustments } LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_adjustments }
} }
pub fn pat_adjustments_mut(&mut self) -> LocalTableInContextMut<'_, Vec<Ty<'tcx>>> { pub fn pat_adjustments_mut(
&mut self,
) -> LocalTableInContextMut<'_, Vec<ty::adjustment::PatAdjustment<'tcx>>> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
} }

View file

@ -5,7 +5,7 @@ use rustc_errors::MultiSpan;
use rustc_hir::{BindingMode, ByRef, HirId, Mutability}; use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
use rustc_lint as lint; use rustc_lint as lint;
use rustc_middle::span_bug; use rustc_middle::span_bug;
use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt}; use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt};
use rustc_span::{Ident, Span}; use rustc_span::{Ident, Span};
use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg}; use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
@ -93,10 +93,10 @@ impl<'a> PatMigration<'a> {
pub(super) fn visit_implicit_derefs<'tcx>( pub(super) fn visit_implicit_derefs<'tcx>(
&mut self, &mut self,
pat_span: Span, pat_span: Span,
adjustments: &[Ty<'tcx>], adjustments: &[ty::adjustment::PatAdjustment<'tcx>],
) -> Option<(Span, Mutability)> { ) -> Option<(Span, Mutability)> {
let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| { let implicit_deref_mutbls = adjustments.iter().map(|adjust| {
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else { let &ty::Ref(_, _, mutbl) = adjust.source.kind() else {
span_bug!(pat_span, "pattern implicitly dereferences a non-ref type"); span_bug!(pat_span, "pattern implicitly dereferences a non-ref type");
}; };
mutbl mutbl

View file

@ -18,6 +18,7 @@ use rustc_middle::mir::interpret::LitToConstInput;
use rustc_middle::thir::{ use rustc_middle::thir::{
Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
}; };
use rustc_middle::ty::adjustment::PatAdjustment;
use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypingMode}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypingMode};
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
@ -63,7 +64,7 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
impl<'a, 'tcx> PatCtxt<'a, 'tcx> { impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> { fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
let adjustments: &[Ty<'tcx>] = let adjustments: &[PatAdjustment<'tcx>] =
self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
// Track the default binding mode for the Rust 2024 migration suggestion. // Track the default binding mode for the Rust 2024 migration suggestion.
@ -102,11 +103,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
_ => self.lower_pattern_unadjusted(pat), _ => self.lower_pattern_unadjusted(pat),
}; };
let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| { let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, adjust| {
debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty); debug!("{:?}: wrapping pattern with type {:?}", thir_pat, adjust);
Box::new(Pat { Box::new(Pat {
span: thir_pat.span, span: thir_pat.span,
ty: *ref_ty, ty: adjust.source,
kind: PatKind::Deref { subpattern: thir_pat }, kind: PatKind::Deref { subpattern: thir_pat },
}) })
}); });

View file

@ -179,7 +179,7 @@ fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mut
}; };
if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) { if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) {
if let [first, ..] = **adjustments { if let [first, ..] = **adjustments {
if let ty::Ref(.., mutability) = *first.kind() { if let ty::Ref(.., mutability) = *first.source.kind() {
let level = if p.hir_id == pat.hir_id { let level = if p.hir_id == pat.hir_id {
Level::Top Level::Top
} else { } else {