Move all ref pat logic into check_pat_ref

This commit is contained in:
Jules Bertholet 2024-05-05 23:57:52 -04:00
parent bff287b4a5
commit 7951311c1b
No known key found for this signature in database
GPG Key ID: 32034DAFC38C1BFC

View File

@ -131,14 +131,6 @@ enum AdjustMode {
/// Reset binding mode to the initial mode. /// Reset binding mode to the initial mode.
/// Used for destructuring assignment, where we don't want any match ergonomics. /// Used for destructuring assignment, where we don't want any match ergonomics.
Reset, Reset,
/// Produced by ref patterns.
/// Reset the binding mode to the initial mode,
/// and if the old biding mode was by-reference
/// with mutability matching the pattern,
/// mark the pattern as having consumed this reference.
///
/// `Span` is that of the `&` or `&mut` itself.
ResetAndConsumeRef(Mutability, Option<Span>),
/// Pass on the input binding mode and expected type. /// Pass on the input binding mode and expected type.
Pass, Pass,
} }
@ -170,6 +162,7 @@ enum MutblCap {
} }
impl MutblCap { impl MutblCap {
#[must_use]
fn cap_to_weakly_not(self, span: Option<Span>) -> Self { fn cap_to_weakly_not(self, span: Option<Span>) -> Self {
match self { match self {
MutblCap::Not => MutblCap::Not, MutblCap::Not => MutblCap::Not,
@ -177,6 +170,7 @@ impl MutblCap {
} }
} }
#[must_use]
fn as_mutbl(self) -> Mutability { fn as_mutbl(self) -> Mutability {
match self { match self {
MutblCap::Not | MutblCap::WeaklyNot(_) => Mutability::Not, MutblCap::Not | MutblCap::WeaklyNot(_) => Mutability::Not,
@ -214,14 +208,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
/// Type check the given `pat` against the `expected` type /// Type check the given `pat` against the `expected` type
/// with the provided `def_bm` (default binding mode). /// with the provided `binding_mode` (default binding mode).
/// ///
/// Outside of this module, `check_pat_top` should always be used. /// Outside of this module, `check_pat_top` should always be used.
/// Conversely, inside this module, `check_pat_top` should never be used. /// Conversely, inside this module, `check_pat_top` should never be used.
#[instrument(level = "debug", skip(self, pat_info))] #[instrument(level = "debug", skip(self, pat_info))]
fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) {
let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } = let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
pat_info;
let path_res = match &pat.kind { let path_res = match &pat.kind {
PatKind::Path(qpath) => Some( PatKind::Path(qpath) => Some(
@ -230,10 +223,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => None, _ => None,
}; };
let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
let (expected, def_bm, max_ref_mutbl, ref_pattern_already_consumed) = let (expected, binding_mode, max_ref_mutbl) =
self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode, max_ref_mutbl); self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl);
let pat_info = PatInfo { let pat_info = PatInfo {
binding_mode: def_bm, binding_mode,
max_ref_mutbl, max_ref_mutbl,
top_info: ti, top_info: ti,
decl_origin: pat_info.decl_origin, decl_origin: pat_info.decl_origin,
@ -269,14 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info), PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info), PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
PatKind::Ref(inner, mutbl) => self.check_pat_ref( PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
pat,
inner,
mutbl,
expected,
pat_info,
ref_pattern_already_consumed,
),
PatKind::Slice(before, slice, after) => { PatKind::Slice(before, slice, after) => {
self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
} }
@ -329,10 +315,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Compute the new expected type and default binding mode from the old ones /// Compute the new expected type and default binding mode from the old ones
/// as well as the pattern form we are currently checking. /// as well as the pattern form we are currently checking.
///
/// Last entry is only relevant for ref patterns (`&` and `&mut`);
/// if `true`, then the ref pattern consumed a match ergonomics inserted reference
/// and so does no need to match against a reference in the scrutinee type.
fn calc_default_binding_mode( fn calc_default_binding_mode(
&self, &self,
pat: &'tcx Pat<'tcx>, pat: &'tcx Pat<'tcx>,
@ -340,50 +322,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
def_br: ByRef, def_br: ByRef,
adjust_mode: AdjustMode, adjust_mode: AdjustMode,
max_ref_mutbl: MutblCap, max_ref_mutbl: MutblCap,
) -> (Ty<'tcx>, ByRef, MutblCap, bool) { ) -> (Ty<'tcx>, ByRef, MutblCap) {
if let ByRef::Yes(Mutability::Mut) = def_br { if let ByRef::Yes(Mutability::Mut) = def_br {
debug_assert!(max_ref_mutbl == MutblCap::Mut); debug_assert!(max_ref_mutbl == MutblCap::Mut);
} }
match adjust_mode { match adjust_mode {
AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false), AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut, false), AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut),
AdjustMode::ResetAndConsumeRef(ref_pat_mutbl, ref_span) => {
// `&` pattern eats `&mut`
let mutbls_match =
if let ByRef::Yes(def_mut) = def_br { ref_pat_mutbl <= def_mut } else { false };
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
let max_ref_mutbl = if ref_pat_mutbl == Mutability::Not {
max_ref_mutbl.cap_to_weakly_not(ref_span)
} else {
max_ref_mutbl
};
if mutbls_match {
debug!("consuming inherited reference");
(expected, ByRef::No, max_ref_mutbl, true)
} else {
let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut {
self.peel_off_references(
pat,
expected,
def_br,
Mutability::Not,
max_ref_mutbl,
)
} else {
(expected, def_br.cap_ref_mutability(Mutability::Not), max_ref_mutbl)
};
(new_ty, new_bm, max_ref_mutbl, false)
}
} else {
(expected, ByRef::No, max_ref_mutbl, mutbls_match)
}
}
AdjustMode::Peel => { AdjustMode::Peel => {
let peeled = self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl)
self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl);
(peeled.0, peeled.1, peeled.2, false)
} }
} }
} }
@ -429,17 +376,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// a reference type wherefore peeling doesn't give up any expressiveness. // a reference type wherefore peeling doesn't give up any expressiveness.
_ => AdjustMode::Peel, _ => AdjustMode::Peel,
}, },
// When encountering a `& mut? pat` pattern, reset to "by value". // Ref patterns are complicated, we handle them in `check_pat_ref`.
// This is so that `x` and `y` here are by value, as they appear to be: PatKind::Ref(..) => AdjustMode::Pass,
//
// ```
// match &(&22, &44) {
// (&x, &y) => ...
// }
// ```
//
// See issue #46688.
PatKind::Ref(inner, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl, inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end))),
// A `_` pattern works with any expected type, so there's no need to do anything. // A `_` pattern works with any expected type, so there's no need to do anything.
PatKind::Wild PatKind::Wild
// A malformed pattern doesn't have an expected type, so let's just accept any type. // A malformed pattern doesn't have an expected type, so let's just accept any type.
@ -2179,24 +2117,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self, &self,
pat: &'tcx Pat<'tcx>, pat: &'tcx Pat<'tcx>,
inner: &'tcx Pat<'tcx>, inner: &'tcx Pat<'tcx>,
mutbl: Mutability, pat_mutbl: Mutability,
expected: Ty<'tcx>, mut expected: Ty<'tcx>,
pat_info: PatInfo<'tcx, '_>, mut pat_info: PatInfo<'tcx, '_>,
consumed_inherited_ref: bool,
) -> Ty<'tcx> { ) -> Ty<'tcx> {
if consumed_inherited_ref // FIXME: repace with `bool` once final decision on 1 vs 2 layers is made
&& pat.span.at_least_rust_2024() #[derive(Clone, Copy, Debug, PartialEq, Eq)]
&& self.tcx.features().ref_pat_eat_one_layer_2024 enum MatchErgonomicsMode {
{ EatOneLayer,
EatTwoLayers,
Legacy,
}
let match_ergonomics_mode =
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
MatchErgonomicsMode::EatOneLayer
} else if self.tcx.features().ref_pat_everywhere {
MatchErgonomicsMode::EatTwoLayers
} else {
MatchErgonomicsMode::Legacy
};
let mut inherited_ref_mutbl_match = false;
if match_ergonomics_mode != MatchErgonomicsMode::Legacy {
if pat_mutbl == Mutability::Not {
pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(
inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end)),
);
}
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
inherited_ref_mutbl_match = pat_mutbl <= inh_mut;
}
if inherited_ref_mutbl_match {
pat_info.binding_mode = ByRef::No;
if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer {
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
self.check_pat(inner, expected, pat_info); self.check_pat(inner, expected, pat_info);
expected return expected;
} else { }
let tcx = self.tcx; } else if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer
let expected = self.shallow_resolve(expected); && pat_mutbl == Mutability::Mut
let (ref_ty, inner_ty, pat_info) = match self
.check_dereferenceable(pat.span, expected, inner)
{ {
// `&mut` patterns pell off `&` references
let (new_expected, new_bm, max_ref_mutbl) = self.peel_off_references(
pat,
expected,
pat_info.binding_mode,
Mutability::Not,
pat_info.max_ref_mutbl,
);
expected = new_expected;
pat_info.binding_mode = new_bm;
pat_info.max_ref_mutbl = max_ref_mutbl;
}
} else {
// Reset binding mode on old editions
pat_info.binding_mode = ByRef::No;
pat_info.max_ref_mutbl = MutblCap::Mut
}
let tcx = self.tcx;
expected = self.try_structurally_resolve_type(pat.span, expected);
let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) {
Ok(()) => { Ok(()) => {
// `demand::subtype` would be good enough, but using `eqtype` turns // `demand::subtype` would be good enough, but using `eqtype` turns
// out to be equally general. See (note_1) for details. // out to be equally general. See (note_1) for details.
@ -2206,43 +2190,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the bad interactions of the given hack detailed in (note_1). // the bad interactions of the given hack detailed in (note_1).
debug!("check_pat_ref: expected={:?}", expected); debug!("check_pat_ref: expected={:?}", expected);
match *expected.kind() { match *expected.kind() {
ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => { ty::Ref(_, r_ty, r_mutbl) if r_mutbl == pat_mutbl => {
let pat_info = if r_mutbl == Mutability::Not if r_mutbl == Mutability::Not
&& ((pat.span.at_least_rust_2024() && match_ergonomics_mode != MatchErgonomicsMode::Legacy
&& self.tcx.features().ref_pat_eat_one_layer_2024)
|| self.tcx.features().ref_pat_everywhere)
{ {
PatInfo { max_ref_mutbl: MutblCap::Not, ..pat_info } pat_info.max_ref_mutbl = MutblCap::Not;
} else { }
pat_info
}; (expected, r_ty)
(expected, r_ty, pat_info)
} }
// `&` pattern eats `&mut` reference // `&` pattern eats `&mut` reference
ty::Ref(_, r_ty, Mutability::Mut) ty::Ref(_, r_ty, Mutability::Mut)
if mutbl == Mutability::Not if pat_mutbl == Mutability::Not
&& ((pat.span.at_least_rust_2024() && match_ergonomics_mode != MatchErgonomicsMode::Legacy =>
&& self.tcx.features().ref_pat_eat_one_layer_2024)
|| self.tcx.features().ref_pat_everywhere) =>
{ {
(expected, r_ty, pat_info) (expected, r_ty)
} }
_ if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere => { _ if inherited_ref_mutbl_match
&& match_ergonomics_mode == MatchErgonomicsMode::EatTwoLayers =>
{
// We already matched against a match-ergonmics inserted reference, // We already matched against a match-ergonmics inserted reference,
// so we don't need to match against a reference from the original type. // so we don't need to match against a reference from the original type.
// Save this infor for use in lowering later // Save this info for use in lowering later
self.typeck_results self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
.borrow_mut() (expected, expected)
.skipped_ref_pats_mut()
.insert(pat.hir_id);
(expected, expected, pat_info)
} }
_ => { _ => {
let inner_ty = self.next_ty_var(inner.span); let inner_ty = self.next_ty_var(inner.span);
let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); let ref_ty = self.new_ref_ty(pat.span, pat_mutbl, inner_ty);
debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
let err = self.demand_eqtype_pat_diag( let err = self.demand_eqtype_pat_diag(
pat.span, pat.span,
@ -2257,19 +2235,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.borrow_pat_suggestion(&mut err, pat); self.borrow_pat_suggestion(&mut err, pat);
err.emit(); err.emit();
} }
(ref_ty, inner_ty, pat_info) (ref_ty, inner_ty)
} }
} }
} }
Err(guar) => { Err(guar) => {
let err = Ty::new_error(tcx, guar); let err = Ty::new_error(tcx, guar);
(err, err, pat_info) (err, err)
} }
}; };
self.check_pat(inner, inner_ty, pat_info); self.check_pat(inner, inner_ty, pat_info);
ref_ty ref_ty
} }
}
/// Create a reference type with a fresh region variable. /// Create a reference type with a fresh region variable.
fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'tcx>) -> Ty<'tcx> {