Skip to content

Commit 292dfdc

Browse files
Move all ref pat logic into check_pat_ref
1 parent f06d63a commit 292dfdc

File tree

1 file changed

+135
-156
lines changed
  • compiler/rustc_hir_typeck/src

1 file changed

+135
-156
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+135-156
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,6 @@ enum AdjustMode {
133133
/// Reset binding mode to the initial mode.
134134
/// Used for destructuring assignment, where we don't want any match ergonomics.
135135
Reset,
136-
/// Produced by ref patterns.
137-
/// Reset the binding mode to the initial mode,
138-
/// and if the old biding mode was by-reference
139-
/// with mutability matching the pattern,
140-
/// mark the pattern as having consumed this reference.
141-
///
142-
/// `Span` is that of the `&` or `&mut` itself.
143-
ResetAndConsumeRef(Mutability, Option<Span>),
144136
/// Pass on the input binding mode and expected type.
145137
Pass,
146138
}
@@ -172,13 +164,15 @@ enum MutblCap {
172164
}
173165

174166
impl MutblCap {
167+
#[must_use]
175168
fn cap_to_weakly_not(self, span: Option<Span>) -> Self {
176169
match self {
177170
MutblCap::Not => MutblCap::Not,
178171
_ => MutblCap::WeaklyNot(span),
179172
}
180173
}
181174

175+
#[must_use]
182176
fn as_mutbl(self) -> Mutability {
183177
match self {
184178
MutblCap::Not | MutblCap::WeaklyNot(_) => Mutability::Not,
@@ -216,14 +210,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
216210
}
217211

218212
/// Type check the given `pat` against the `expected` type
219-
/// with the provided `def_bm` (default binding mode).
213+
/// with the provided `binding_mode` (default binding mode).
220214
///
221215
/// Outside of this module, `check_pat_top` should always be used.
222216
/// Conversely, inside this module, `check_pat_top` should never be used.
223217
#[instrument(level = "debug", skip(self, pat_info))]
224218
fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) {
225-
let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } =
226-
pat_info;
219+
let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
227220

228221
let path_res = match &pat.kind {
229222
PatKind::Path(qpath) => Some(
@@ -232,10 +225,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
232225
_ => None,
233226
};
234227
let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
235-
let (expected, def_bm, max_ref_mutbl, ref_pattern_already_consumed) =
236-
self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode, max_ref_mutbl);
228+
let (expected, binding_mode, max_ref_mutbl) =
229+
self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl);
237230
let pat_info = PatInfo {
238-
binding_mode: def_bm,
231+
binding_mode,
239232
max_ref_mutbl,
240233
top_info: ti,
241234
decl_origin: pat_info.decl_origin,
@@ -271,14 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
271264
}
272265
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
273266
PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
274-
PatKind::Ref(inner, mutbl) => self.check_pat_ref(
275-
pat,
276-
inner,
277-
mutbl,
278-
expected,
279-
pat_info,
280-
ref_pattern_already_consumed,
281-
),
267+
PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
282268
PatKind::Slice(before, slice, after) => {
283269
self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
284270
}
@@ -331,61 +317,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
331317

332318
/// Compute the new expected type and default binding mode from the old ones
333319
/// as well as the pattern form we are currently checking.
334-
///
335-
/// Last entry is only relevant for ref patterns (`&` and `&mut`);
336-
/// if `true`, then the ref pattern consumed a match ergonomics inserted reference
337-
/// and so does no need to match against a reference in the scrutinee type.
338320
fn calc_default_binding_mode(
339321
&self,
340322
pat: &'tcx Pat<'tcx>,
341323
expected: Ty<'tcx>,
342324
def_br: ByRef,
343325
adjust_mode: AdjustMode,
344326
max_ref_mutbl: MutblCap,
345-
) -> (Ty<'tcx>, ByRef, MutblCap, bool) {
327+
) -> (Ty<'tcx>, ByRef, MutblCap) {
346328
if let ByRef::Yes(Mutability::Mut) = def_br {
347329
debug_assert!(max_ref_mutbl == MutblCap::Mut);
348330
}
349331
match adjust_mode {
350-
AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false),
351-
AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut, false),
352-
AdjustMode::ResetAndConsumeRef(ref_pat_mutbl, ref_span) => {
353-
// `&` pattern eats `&mut`
354-
let mutbls_match =
355-
if let ByRef::Yes(def_mut) = def_br { ref_pat_mutbl <= def_mut } else { false };
356-
357-
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
358-
let max_ref_mutbl = if ref_pat_mutbl == Mutability::Not {
359-
max_ref_mutbl.cap_to_weakly_not(ref_span)
360-
} else {
361-
max_ref_mutbl
362-
};
363-
364-
if mutbls_match {
365-
debug!("consuming inherited reference");
366-
(expected, ByRef::No, max_ref_mutbl, true)
367-
} else {
368-
let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut {
369-
self.peel_off_references(
370-
pat,
371-
expected,
372-
def_br,
373-
Mutability::Not,
374-
max_ref_mutbl,
375-
)
376-
} else {
377-
(expected, def_br.cap_ref_mutability(Mutability::Not), max_ref_mutbl)
378-
};
379-
(new_ty, new_bm, max_ref_mutbl, false)
380-
}
381-
} else {
382-
(expected, ByRef::No, max_ref_mutbl, mutbls_match)
383-
}
384-
}
332+
AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
333+
AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut),
385334
AdjustMode::Peel => {
386335
let peeled =
387336
self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl);
388-
(peeled.0, peeled.1, peeled.2, false)
337+
(peeled.0, peeled.1, peeled.2)
389338
}
390339
}
391340
}
@@ -431,17 +380,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
431380
// a reference type wherefore peeling doesn't give up any expressiveness.
432381
_ => AdjustMode::Peel,
433382
},
434-
// When encountering a `& mut? pat` pattern, reset to "by value".
435-
// This is so that `x` and `y` here are by value, as they appear to be:
436-
//
437-
// ```
438-
// match &(&22, &44) {
439-
// (&x, &y) => ...
440-
// }
441-
// ```
442-
//
443-
// See issue #46688.
444-
PatKind::Ref(inner, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl, inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end))),
383+
// Ref patterns are complicated, we handle them in `check_pat_ref`.
384+
PatKind::Ref(..) => AdjustMode::Pass,
445385
// A `_` pattern works with any expected type, so there's no need to do anything.
446386
PatKind::Wild
447387
// A malformed pattern doesn't have an expected type, so let's just accept any type.
@@ -2177,99 +2117,138 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21772117
&self,
21782118
pat: &'tcx Pat<'tcx>,
21792119
inner: &'tcx Pat<'tcx>,
2180-
mutbl: Mutability,
2181-
expected: Ty<'tcx>,
2182-
pat_info: PatInfo<'tcx, '_>,
2183-
consumed_inherited_ref: bool,
2120+
pat_mutbl: Mutability,
2121+
mut expected: Ty<'tcx>,
2122+
mut pat_info: PatInfo<'tcx, '_>,
21842123
) -> Ty<'tcx> {
2185-
if consumed_inherited_ref
2186-
&& pat.span.at_least_rust_2024()
2187-
&& self.tcx.features().ref_pat_eat_one_layer_2024
2188-
{
2189-
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
2190-
self.check_pat(inner, expected, pat_info);
2191-
expected
2192-
} else {
2193-
let tcx = self.tcx;
2194-
let expected = self.shallow_resolve(expected);
2195-
let (ref_ty, inner_ty, pat_info) = match self
2196-
.check_dereferenceable(pat.span, expected, inner)
2124+
// FIXME: repace with `bool` once final decision on 1 vs 2 layers is made
2125+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2126+
enum MatchErgonomicsMode {
2127+
EatOneLayer,
2128+
EatTwoLayers,
2129+
Legacy,
2130+
}
2131+
2132+
let match_ergonomics_mode =
2133+
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
2134+
MatchErgonomicsMode::EatOneLayer
2135+
} else if self.tcx.features().ref_pat_everywhere {
2136+
MatchErgonomicsMode::EatTwoLayers
2137+
} else {
2138+
MatchErgonomicsMode::Legacy
2139+
};
2140+
2141+
let mut inherited_ref_mutbl_match = false;
2142+
if match_ergonomics_mode != MatchErgonomicsMode::Legacy {
2143+
if pat_mutbl == Mutability::Not {
2144+
pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(
2145+
inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end)),
2146+
);
2147+
}
2148+
2149+
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
2150+
inherited_ref_mutbl_match = pat_mutbl <= inh_mut;
2151+
}
2152+
2153+
if inherited_ref_mutbl_match {
2154+
pat_info.binding_mode = ByRef::No;
2155+
if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer {
2156+
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
2157+
self.check_pat(inner, expected, pat_info);
2158+
return expected;
2159+
}
2160+
} else if match_ergonomics_mode == MatchErgonomicsMode::EatOneLayer
2161+
&& pat_mutbl == Mutability::Mut
21972162
{
2198-
Ok(()) => {
2199-
// `demand::subtype` would be good enough, but using `eqtype` turns
2200-
// out to be equally general. See (note_1) for details.
2201-
2202-
// Take region, inner-type from expected type if we can,
2203-
// to avoid creating needless variables. This also helps with
2204-
// the bad interactions of the given hack detailed in (note_1).
2205-
debug!("check_pat_ref: expected={:?}", expected);
2206-
match *expected.kind() {
2207-
ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => {
2208-
let pat_info = if r_mutbl == Mutability::Not
2209-
&& ((pat.span.at_least_rust_2024()
2210-
&& self.tcx.features().ref_pat_eat_one_layer_2024)
2211-
|| self.tcx.features().ref_pat_everywhere)
2212-
{
2213-
PatInfo { max_ref_mutbl: MutblCap::Not, ..pat_info }
2214-
} else {
2215-
pat_info
2216-
};
2217-
(expected, r_ty, pat_info)
2218-
}
2163+
// `&mut` patterns pell off `&` references
2164+
let (new_expected, new_bm, max_ref_mutbl) = self.peel_off_references(
2165+
pat,
2166+
expected,
2167+
pat_info.binding_mode,
2168+
Mutability::Not,
2169+
pat_info.max_ref_mutbl,
2170+
);
2171+
expected = new_expected;
2172+
pat_info.binding_mode = new_bm;
2173+
pat_info.max_ref_mutbl = max_ref_mutbl;
2174+
}
2175+
} else {
2176+
// Reset binding mode on old editions
2177+
pat_info.binding_mode = ByRef::No;
2178+
pat_info.max_ref_mutbl = MutblCap::Mut
2179+
}
22192180

2220-
// `&` pattern eats `&mut` reference
2221-
ty::Ref(_, r_ty, Mutability::Mut)
2222-
if mutbl == Mutability::Not
2223-
&& ((pat.span.at_least_rust_2024()
2224-
&& self.tcx.features().ref_pat_eat_one_layer_2024)
2225-
|| self.tcx.features().ref_pat_everywhere) =>
2181+
let tcx = self.tcx;
2182+
expected = self.try_structurally_resolve_type(pat.span, expected);
2183+
let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) {
2184+
Ok(()) => {
2185+
// `demand::subtype` would be good enough, but using `eqtype` turns
2186+
// out to be equally general. See (note_1) for details.
2187+
2188+
// Take region, inner-type from expected type if we can,
2189+
// to avoid creating needless variables. This also helps with
2190+
// the bad interactions of the given hack detailed in (note_1).
2191+
debug!("check_pat_ref: expected={:?}", expected);
2192+
match *expected.kind() {
2193+
ty::Ref(_, r_ty, r_mutbl) if r_mutbl == pat_mutbl => {
2194+
if r_mutbl == Mutability::Not
2195+
&& match_ergonomics_mode != MatchErgonomicsMode::Legacy
22262196
{
2227-
(expected, r_ty, pat_info)
2197+
pat_info.max_ref_mutbl = MutblCap::Not;
22282198
}
22292199

2230-
_ if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere => {
2231-
// We already matched against a match-ergonmics inserted reference,
2232-
// so we don't need to match against a reference from the original type.
2233-
// Save this infor for use in lowering later
2234-
self.typeck_results
2235-
.borrow_mut()
2236-
.skipped_ref_pats_mut()
2237-
.insert(pat.hir_id);
2238-
(expected, expected, pat_info)
2239-
}
2200+
(expected, r_ty)
2201+
}
22402202

2241-
_ => {
2242-
let inner_ty = self.next_ty_var(TypeVariableOrigin {
2243-
param_def_id: None,
2244-
span: inner.span,
2245-
});
2246-
let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
2247-
debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
2248-
let err = self.demand_eqtype_pat_diag(
2249-
pat.span,
2250-
expected,
2251-
ref_ty,
2252-
pat_info.top_info,
2253-
);
2203+
// `&` pattern eats `&mut` reference
2204+
ty::Ref(_, r_ty, Mutability::Mut)
2205+
if pat_mutbl == Mutability::Not
2206+
&& match_ergonomics_mode != MatchErgonomicsMode::Legacy =>
2207+
{
2208+
(expected, r_ty)
2209+
}
22542210

2255-
// Look for a case like `fn foo(&foo: u32)` and suggest
2256-
// `fn foo(foo: &u32)`
2257-
if let Some(mut err) = err {
2258-
self.borrow_pat_suggestion(&mut err, pat);
2259-
err.emit();
2260-
}
2261-
(ref_ty, inner_ty, pat_info)
2211+
_ if inherited_ref_mutbl_match
2212+
&& match_ergonomics_mode == MatchErgonomicsMode::EatTwoLayers =>
2213+
{
2214+
// We already matched against a match-ergonmics inserted reference,
2215+
// so we don't need to match against a reference from the original type.
2216+
// Save this info for use in lowering later
2217+
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
2218+
(expected, expected)
2219+
}
2220+
2221+
_ => {
2222+
let inner_ty = self.next_ty_var(TypeVariableOrigin {
2223+
param_def_id: None,
2224+
span: inner.span,
2225+
});
2226+
let ref_ty = self.new_ref_ty(pat.span, pat_mutbl, inner_ty);
2227+
debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
2228+
let err = self.demand_eqtype_pat_diag(
2229+
pat.span,
2230+
expected,
2231+
ref_ty,
2232+
pat_info.top_info,
2233+
);
2234+
2235+
// Look for a case like `fn foo(&foo: u32)` and suggest
2236+
// `fn foo(foo: &u32)`
2237+
if let Some(mut err) = err {
2238+
self.borrow_pat_suggestion(&mut err, pat);
2239+
err.emit();
22622240
}
2241+
(ref_ty, inner_ty)
22632242
}
22642243
}
2265-
Err(guar) => {
2266-
let err = Ty::new_error(tcx, guar);
2267-
(err, err, pat_info)
2268-
}
2269-
};
2270-
self.check_pat(inner, inner_ty, pat_info);
2271-
ref_ty
2272-
}
2244+
}
2245+
Err(guar) => {
2246+
let err = Ty::new_error(tcx, guar);
2247+
(err, err)
2248+
}
2249+
};
2250+
self.check_pat(inner, inner_ty, pat_info);
2251+
ref_ty
22732252
}
22742253

22752254
/// Create a reference type with a fresh region variable.

0 commit comments

Comments
 (0)