Skip to content

Commit cfebef6

Browse files
committed
Auto merge of #139449 - Nadrieril:peel-recursive, r=<try>
match ergonomics: replace `peel_off_references` with a recursive call This makes it imo quite a bit easier to follow how the binding mode gets calculated. cc `@dianne`
2 parents f5c5102 + fb0d9f5 commit cfebef6

File tree

1 file changed

+115
-145
lines changed
  • compiler/rustc_hir_typeck/src

1 file changed

+115
-145
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+115-145
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
163163
enum AdjustMode {
164164
/// Peel off all immediate reference types.
165165
Peel,
166-
/// Reset binding mode to the initial mode.
167-
/// Used for destructuring assignment, where we don't want any match ergonomics.
168-
Reset,
169166
/// Pass on the input binding mode and expected type.
170167
Pass,
171168
}
@@ -321,77 +318,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
321318
/// Conversely, inside this module, `check_pat_top` should never be used.
322319
#[instrument(level = "debug", skip(self, pat_info))]
323320
fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>) {
324-
let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info;
325-
326-
let path_res = match pat.kind {
321+
let opt_path_res = match pat.kind {
327322
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
328323
Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span))
329324
}
330325
_ => None,
331326
};
332-
let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
333-
let (expected, binding_mode, max_ref_mutbl) =
334-
self.calc_default_binding_mode(pat, expected, binding_mode, adjust_mode, max_ref_mutbl);
335-
let pat_info = PatInfo {
336-
binding_mode,
337-
max_ref_mutbl,
338-
top_info: ti,
339-
decl_origin: pat_info.decl_origin,
340-
current_depth: current_depth + 1,
341-
};
342-
343-
let ty = match pat.kind {
344-
PatKind::Wild | PatKind::Err(_) => expected,
345-
// We allow any type here; we ensure that the type is uninhabited during match checking.
346-
PatKind::Never => expected,
347-
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
348-
let ty = self.check_pat_path(
349-
*hir_id,
350-
pat.hir_id,
351-
*span,
352-
qpath,
353-
path_res.unwrap(),
354-
expected,
355-
&pat_info.top_info,
356-
);
357-
self.write_ty(*hir_id, ty);
358-
ty
359-
}
360-
PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, &pat_info.top_info),
361-
PatKind::Range(lhs, rhs, _) => {
362-
self.check_pat_range(pat.span, lhs, rhs, expected, &pat_info.top_info)
363-
}
364-
PatKind::Binding(ba, var_id, ident, sub) => {
365-
self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info)
366-
}
367-
PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
368-
self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info)
369-
}
370-
PatKind::Struct(ref qpath, fields, has_rest_pat) => {
371-
self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info)
372-
}
373-
PatKind::Guard(pat, cond) => {
374-
self.check_pat(pat, expected, pat_info);
375-
self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {});
376-
expected
377-
}
378-
PatKind::Or(pats) => {
379-
for pat in pats {
380-
self.check_pat(pat, expected, pat_info);
381-
}
382-
expected
383-
}
384-
PatKind::Tuple(elements, ddpos) => {
385-
self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info)
386-
}
387-
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
388-
PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
389-
PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
390-
PatKind::Slice(before, slice, after) => {
391-
self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
392-
}
393-
};
394-
327+
let adjust_mode = self.calc_adjust_mode(pat, opt_path_res.map(|(res, ..)| res));
328+
let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
395329
self.write_ty(pat.hir_id, ty);
396330

397331
// (note_1): In most of the cases where (note_1) is referenced
@@ -437,39 +371,133 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
437371
// `regions-relate-bound-regions-on-closures-to-inference-variables.rs`,
438372
}
439373

440-
/// Compute the new expected type and default binding mode from the old ones
441-
/// as well as the pattern form we are currently checking.
442-
fn calc_default_binding_mode(
374+
// Helper to avoid resolving the same path pattern several times.
375+
fn check_pat_inner(
443376
&self,
444377
pat: &'tcx Pat<'tcx>,
445-
expected: Ty<'tcx>,
446-
def_br: ByRef,
378+
opt_path_res: Option<(Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>])>,
447379
adjust_mode: AdjustMode,
448-
max_ref_mutbl: MutblCap,
449-
) -> (Ty<'tcx>, ByRef, MutblCap) {
380+
expected: Ty<'tcx>,
381+
pat_info: PatInfo<'tcx>,
382+
) -> Ty<'tcx> {
450383
#[cfg(debug_assertions)]
451-
if def_br == ByRef::Yes(Mutability::Mut)
452-
&& max_ref_mutbl != MutblCap::Mut
384+
if pat_info.binding_mode == ByRef::Yes(Mutability::Mut)
385+
&& pat_info.max_ref_mutbl != MutblCap::Mut
453386
&& self.downgrade_mut_inside_shared()
454387
{
455388
span_bug!(pat.span, "Pattern mutability cap violated!");
456389
}
457-
match adjust_mode {
458-
AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
459-
AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut),
460-
AdjustMode::Peel => self.peel_off_references(pat, expected, def_br, max_ref_mutbl),
390+
391+
// Resolve type if needed.
392+
let expected = if let AdjustMode::Peel = adjust_mode
393+
&& pat.default_binding_modes
394+
{
395+
self.try_structurally_resolve_type(pat.span, expected)
396+
} else {
397+
expected
398+
};
399+
let old_pat_info = pat_info;
400+
let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info };
401+
402+
match pat.kind {
403+
// Peel off a `&` or `&mut` from the scrutinee type. See the examples in
404+
// `tests/ui/rfcs/rfc-2005-default-binding-mode`.
405+
_ if let AdjustMode::Peel = adjust_mode
406+
&& pat.default_binding_modes
407+
&& let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() =>
408+
{
409+
debug!("inspecting {:?}", expected);
410+
411+
debug!("current discriminant is Ref, inserting implicit deref");
412+
// Preserve the reference type. We'll need it later during THIR lowering.
413+
self.typeck_results
414+
.borrow_mut()
415+
.pat_adjustments_mut()
416+
.entry(pat.hir_id)
417+
.or_default()
418+
.push(expected);
419+
420+
let mut binding_mode = ByRef::Yes(match pat_info.binding_mode {
421+
// If default binding mode is by value, make it `ref` or `ref mut`
422+
// (depending on whether we observe `&` or `&mut`).
423+
ByRef::No |
424+
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
425+
ByRef::Yes(Mutability::Mut) => inner_mutability,
426+
// Once a `ref`, always a `ref`.
427+
// This is because a `& &mut` cannot mutate the underlying value.
428+
ByRef::Yes(Mutability::Not) => Mutability::Not,
429+
});
430+
431+
let mut max_ref_mutbl = pat_info.max_ref_mutbl;
432+
if self.downgrade_mut_inside_shared() {
433+
binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
434+
}
435+
if binding_mode == ByRef::Yes(Mutability::Not) {
436+
max_ref_mutbl = MutblCap::Not;
437+
}
438+
debug!("default binding mode is now {:?}", binding_mode);
439+
440+
// Use the old pat info to keep `current_depth` to its old value.
441+
let new_pat_info = PatInfo { binding_mode, max_ref_mutbl, ..old_pat_info };
442+
// Recurse with the new expected type.
443+
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
444+
}
445+
PatKind::Wild | PatKind::Err(_) => expected,
446+
// We allow any type here; we ensure that the type is uninhabited during match checking.
447+
PatKind::Never => expected,
448+
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
449+
let ty = self.check_pat_path(
450+
*hir_id,
451+
pat.hir_id,
452+
*span,
453+
qpath,
454+
opt_path_res.unwrap(),
455+
expected,
456+
&pat_info.top_info,
457+
);
458+
self.write_ty(*hir_id, ty);
459+
ty
460+
}
461+
PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, &pat_info.top_info),
462+
PatKind::Range(lhs, rhs, _) => {
463+
self.check_pat_range(pat.span, lhs, rhs, expected, &pat_info.top_info)
464+
}
465+
PatKind::Binding(ba, var_id, ident, sub) => {
466+
self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info)
467+
}
468+
PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
469+
self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info)
470+
}
471+
PatKind::Struct(ref qpath, fields, has_rest_pat) => {
472+
self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info)
473+
}
474+
PatKind::Guard(pat, cond) => {
475+
self.check_pat(pat, expected, pat_info);
476+
self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {});
477+
expected
478+
}
479+
PatKind::Or(pats) => {
480+
for pat in pats {
481+
self.check_pat(pat, expected, pat_info);
482+
}
483+
expected
484+
}
485+
PatKind::Tuple(elements, ddpos) => {
486+
self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info)
487+
}
488+
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
489+
PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
490+
PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
491+
PatKind::Slice(before, slice, after) => {
492+
self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
493+
}
461494
}
462495
}
463496

464497
/// How should the binding mode and expected type be adjusted?
465498
///
466499
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
467500
fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
468-
// When we perform destructuring assignment, we disable default match bindings, which are
469-
// unintuitive in this context.
470-
if !pat.default_binding_modes {
471-
return AdjustMode::Reset;
472-
}
473501
match &pat.kind {
474502
// Type checking these product-like types successfully always require
475503
// that the expected type be of those types and not reference types.
@@ -524,64 +552,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
524552
}
525553
}
526554

527-
/// Peel off as many immediately nested `& mut?` from the expected type as possible
528-
/// and return the new expected type and binding default binding mode.
529-
/// The adjustments vector, if non-empty is stored in a table.
530-
fn peel_off_references(
531-
&self,
532-
pat: &'tcx Pat<'tcx>,
533-
expected: Ty<'tcx>,
534-
mut def_br: ByRef,
535-
mut max_ref_mutbl: MutblCap,
536-
) -> (Ty<'tcx>, ByRef, MutblCap) {
537-
let mut expected = self.try_structurally_resolve_type(pat.span, expected);
538-
// Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
539-
// for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
540-
// the `Some(5)` which is not of type Ref.
541-
//
542-
// For each ampersand peeled off, update the binding mode and push the original
543-
// type into the adjustments vector.
544-
//
545-
// See the examples in `ui/match-defbm*.rs`.
546-
let mut pat_adjustments = vec![];
547-
while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() {
548-
debug!("inspecting {:?}", expected);
549-
550-
debug!("current discriminant is Ref, inserting implicit deref");
551-
// Preserve the reference type. We'll need it later during THIR lowering.
552-
pat_adjustments.push(expected);
553-
554-
expected = self.try_structurally_resolve_type(pat.span, inner_ty);
555-
def_br = ByRef::Yes(match def_br {
556-
// If default binding mode is by value, make it `ref` or `ref mut`
557-
// (depending on whether we observe `&` or `&mut`).
558-
ByRef::No |
559-
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
560-
ByRef::Yes(Mutability::Mut) => inner_mutability,
561-
// Once a `ref`, always a `ref`.
562-
// This is because a `& &mut` cannot mutate the underlying value.
563-
ByRef::Yes(Mutability::Not) => Mutability::Not,
564-
});
565-
}
566-
567-
if self.downgrade_mut_inside_shared() {
568-
def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
569-
}
570-
if def_br == ByRef::Yes(Mutability::Not) {
571-
max_ref_mutbl = MutblCap::Not;
572-
}
573-
574-
if !pat_adjustments.is_empty() {
575-
debug!("default binding mode is now {:?}", def_br);
576-
self.typeck_results
577-
.borrow_mut()
578-
.pat_adjustments_mut()
579-
.insert(pat.hir_id, pat_adjustments);
580-
}
581-
582-
(expected, def_br, max_ref_mutbl)
583-
}
584-
585555
fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> {
586556
let ty = match &lt.kind {
587557
rustc_hir::PatExprKind::Lit { lit, negated } => {

0 commit comments

Comments
 (0)