Skip to content

Commit 3b20cf1

Browse files
committed
lower implicit deref patterns to THIR
Since this uses `pat_adjustments`, I've also tweaked the documentation to mention implicit deref patterns and made sure the pattern migration diagnostic logic accounts for it. I'll adjust `ExprUseVisitor` in a later commit and add some tests there for closure capture inference.
1 parent 9a3f482 commit 3b20cf1

File tree

3 files changed

+36
-21
lines changed

3 files changed

+36
-21
lines changed

compiler/rustc_middle/src/ty/typeck_results.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,25 @@ pub struct TypeckResults<'tcx> {
7777
/// to a form valid in all Editions, either as a lint diagnostic or hard error.
7878
rust_2024_migration_desugared_pats: ItemLocalMap<Rust2024IncompatiblePatInfo>,
7979

80-
/// Stores the types which were implicitly dereferenced in pattern binding modes
81-
/// for later usage in THIR lowering. For example,
80+
/// Stores the types which were implicitly dereferenced in pattern binding modes or deref
81+
/// patterns for later usage in THIR lowering. For example,
8282
///
8383
/// ```
8484
/// match &&Some(5i32) {
8585
/// Some(n) => {},
8686
/// _ => {},
8787
/// }
8888
/// ```
89-
/// leads to a `vec![&&Option<i32>, &Option<i32>]`. Empty vectors are not stored.
89+
/// leads to a `vec![&&Option<i32>, &Option<i32>]` and
90+
///
91+
/// ```
92+
/// #![feature(deref_patterns)]
93+
/// match &Box::new(Some(5i32)) {
94+
/// Some(n) => {},
95+
/// _ => {},
96+
/// }
97+
/// ```
98+
/// leads to a `vec![&Box<Option<i32>>, Box<Option<i32>>]`. Empty vectors are not stored.
9099
///
91100
/// See:
92101
/// <https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions>

compiler/rustc_mir_build/src/thir/pattern/migration.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use rustc_data_structures::fx::FxIndexMap;
44
use rustc_errors::MultiSpan;
55
use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
66
use rustc_lint as lint;
7-
use rustc_middle::span_bug;
87
use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt};
98
use rustc_span::{Ident, Span};
109

@@ -87,19 +86,18 @@ impl<'a> PatMigration<'a> {
8786
}
8887

8988
/// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee.
90-
/// This should only be called when the pattern type adjustments list `adjustments` is
91-
/// non-empty. Returns the prior default binding mode; this should be followed by a call to
92-
/// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
89+
/// This should only be called when the pattern type adjustments list `adjustments` contains an
90+
/// implicit deref of a reference type. Returns the prior default binding mode; this should be
91+
/// followed by a call to [`PatMigration::leave_ref`] to restore it when we leave the pattern.
9392
pub(super) fn visit_implicit_derefs<'tcx>(
9493
&mut self,
9594
pat_span: Span,
9695
adjustments: &[ty::adjustment::PatAdjustment<'tcx>],
9796
) -> Option<(Span, Mutability)> {
98-
let implicit_deref_mutbls = adjustments.iter().map(|adjust| {
99-
let &ty::Ref(_, _, mutbl) = adjust.source.kind() else {
100-
span_bug!(pat_span, "pattern implicitly dereferences a non-ref type");
101-
};
102-
mutbl
97+
// Implicitly dereferencing references changes the default binding mode, but implicit derefs
98+
// of smart pointers do not. Thus, we only consider implicit derefs of reference types.
99+
let implicit_deref_mutbls = adjustments.iter().filter_map(|adjust| {
100+
if let &ty::Ref(_, _, mutbl) = adjust.source.kind() { Some(mutbl) } else { None }
103101
});
104102

105103
if !self.info.suggest_eliding_modes {

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::LitToConstInput;
1818
use rustc_middle::thir::{
1919
Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
2020
};
21-
use rustc_middle::ty::adjustment::PatAdjustment;
21+
use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment};
2222
use rustc_middle::ty::layout::IntegerExt;
2323
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypingMode};
2424
use rustc_middle::{bug, span_bug};
@@ -68,9 +68,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
6868
self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
6969

7070
// Track the default binding mode for the Rust 2024 migration suggestion.
71+
// Implicitly dereferencing references changes the default binding mode, but implicit deref
72+
// patterns do not. Only track binding mode changes if a ref type is in the adjustments.
7173
let mut opt_old_mode_span = None;
7274
if let Some(s) = &mut self.rust_2024_migration
73-
&& !adjustments.is_empty()
75+
&& adjustments.iter().any(|adjust| adjust.source.is_ref())
7476
{
7577
opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments);
7678
}
@@ -104,16 +106,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
104106
};
105107

106108
let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, adjust| {
107-
debug!("{:?}: wrapping pattern with type {:?}", thir_pat, adjust);
108-
Box::new(Pat {
109-
span: thir_pat.span,
110-
ty: adjust.source,
111-
kind: PatKind::Deref { subpattern: thir_pat },
112-
})
109+
debug!("{:?}: wrapping pattern with adjustment {:?}", thir_pat, adjust);
110+
let span = thir_pat.span;
111+
let kind = match adjust.kind {
112+
PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat },
113+
PatAdjust::OverloadedDeref => {
114+
let mutable = self.typeck_results.pat_has_ref_mut_binding(pat);
115+
let mutability =
116+
if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
117+
PatKind::DerefPattern { subpattern: thir_pat, mutability }
118+
}
119+
};
120+
Box::new(Pat { span, ty: adjust.source, kind })
113121
});
114122

115123
if let Some(s) = &mut self.rust_2024_migration
116-
&& !adjustments.is_empty()
124+
&& adjustments.iter().any(|adjust| adjust.source.is_ref())
117125
{
118126
s.leave_ref(opt_old_mode_span);
119127
}

0 commit comments

Comments
 (0)