3
3
use rustc_data_structures:: fx:: FxIndexMap ;
4
4
use rustc_errors:: MultiSpan ;
5
5
use rustc_hir:: { BindingMode , ByRef , HirId , Mutability } ;
6
+ use rustc_index:: IndexVec ;
6
7
use rustc_lint as lint;
7
8
use rustc_middle:: span_bug;
8
9
use rustc_middle:: ty:: { self , Rust2024IncompatiblePatInfo , Ty , TyCtxt } ;
@@ -17,9 +18,11 @@ pub(super) struct PatMigration<'a> {
17
18
suggestion : Vec < ( Span , String ) > ,
18
19
ref_pattern_count : usize ,
19
20
binding_mode_count : usize ,
20
- /// Internal state: the ref-mutability of the default binding mode at the subpattern being
21
- /// lowered, with the span where it was introduced. `None` for a by-value default mode.
22
- default_mode_span : Option < ( Span , ty:: Mutability ) > ,
21
+ /// All the dereferences encountered in lowering the pattern, along with how their corresponding
22
+ /// patterns affect the default binding mode.
23
+ derefs : IndexVec < PatDerefIdx , PatDeref > ,
24
+ /// Internal state: the innermost deref above the pattern currently being lowered.
25
+ innermost_deref : Option < PatDerefIdx > ,
23
26
/// Labels for where incompatibility-causing by-ref default binding modes were introduced.
24
27
// FIXME(ref_pat_eat_one_layer_2024_structural): To track the default binding mode, we duplicate
25
28
// logic from HIR typeck (in order to avoid needing to store all changes to the dbm in
@@ -30,13 +33,31 @@ pub(super) struct PatMigration<'a> {
30
33
info : & ' a Rust2024IncompatiblePatInfo ,
31
34
}
32
35
36
+ rustc_index:: newtype_index! {
37
+ struct PatDerefIdx { }
38
+ }
39
+
40
+ struct PatDeref {
41
+ /// The default binding mode for variables under this deref.
42
+ real_default_mode : ByRef ,
43
+ /// The span that introduced the current default binding mode, or `None` for the top-level pat.
44
+ default_mode_origin : Option < Span > ,
45
+ /// The next deref above this. Since we can't suggest using `&` or `&mut` on a by-ref default
46
+ /// binding mode, a suggested deref's ancestors must also all be suggested.
47
+ // FIXME(ref_pat_eat_one_layer_2024): By suggesting `&` and `&mut` patterns that can eat the
48
+ // default binding mode, we'll be able to make more local suggestions. That may make this forest
49
+ // structure unnecessary.
50
+ parent : Option < PatDerefIdx > ,
51
+ }
52
+
33
53
impl < ' a > PatMigration < ' a > {
34
54
pub ( super ) fn new ( info : & ' a Rust2024IncompatiblePatInfo ) -> Self {
35
55
PatMigration {
36
56
suggestion : Vec :: new ( ) ,
37
57
ref_pattern_count : 0 ,
38
58
binding_mode_count : 0 ,
39
- default_mode_span : None ,
59
+ derefs : IndexVec :: new ( ) ,
60
+ innermost_deref : None ,
40
61
default_mode_labels : Default :: default ( ) ,
41
62
info,
42
63
}
@@ -86,15 +107,39 @@ impl<'a> PatMigration<'a> {
86
107
}
87
108
}
88
109
110
+ /// When lowering a reference pattern or a binding with a modifier, this checks if the default
111
+ /// binding mode is by-ref, and if so, adds a labeled note to the diagnostic with the origin of
112
+ /// the current default binding mode.
113
+ fn add_default_mode_label_if_needed ( & mut self ) {
114
+ if let ByRef :: Yes ( ref_mutbl) = self . real_default_mode ( ) {
115
+ // The by-ref default binding mode must have come from an implicit deref. If there was a
116
+ // problem in tracking that for the diagnostic, try to avoid ICE on release builds.
117
+ debug_assert ! (
118
+ self . innermost_deref
119
+ . is_some_and( |ix| self . derefs[ ix] . default_mode_origin. is_some( ) )
120
+ ) ;
121
+ if let Some ( ix) = self . innermost_deref
122
+ && let Some ( span) = self . derefs [ ix] . default_mode_origin
123
+ {
124
+ self . default_mode_labels . insert ( span, ref_mutbl) ;
125
+ }
126
+ }
127
+ }
128
+
129
+ /// The default binding mode at the current pattern.
130
+ fn real_default_mode ( & self ) -> ByRef {
131
+ if let Some ( current_ix) = self . innermost_deref {
132
+ self . derefs [ current_ix] . real_default_mode
133
+ } else {
134
+ ByRef :: No
135
+ }
136
+ }
137
+
89
138
/// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee.
90
139
/// 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.
93
- pub ( super ) fn visit_implicit_derefs < ' tcx > (
94
- & mut self ,
95
- pat_span : Span ,
96
- adjustments : & [ Ty < ' tcx > ] ,
97
- ) -> Option < ( Span , Mutability ) > {
140
+ /// non-empty.
141
+ /// This should be followed by a call to [`PatMigration::leave_ref`] when we leave the pattern.
142
+ pub ( super ) fn visit_implicit_derefs < ' tcx > ( & mut self , pat_span : Span , adjustments : & [ Ty < ' tcx > ] ) {
98
143
let implicit_deref_mutbls = adjustments. iter ( ) . map ( |ref_ty| {
99
144
let & ty:: Ref ( _, _, mutbl) = ref_ty. kind ( ) else {
100
145
span_bug ! ( pat_span, "pattern implicitly dereferences a non-ref type" ) ;
@@ -112,35 +157,49 @@ impl<'a> PatMigration<'a> {
112
157
}
113
158
114
159
// Remember if this changed the default binding mode, in case we want to label it.
115
- let min_mutbl = implicit_deref_mutbls. min ( ) . unwrap ( ) ;
116
- if self . default_mode_span . is_none_or ( |( _, old_mutbl) | min_mutbl < old_mutbl) {
117
- // This changes the default binding mode to `ref` or `ref mut`. Return the old mode so
118
- // it can be reinstated when we leave the pattern.
119
- self . default_mode_span . replace ( ( pat_span, min_mutbl) )
120
- } else {
121
- // This does not change the default binding mode; it was already `ref` or `ref mut`.
122
- self . default_mode_span
123
- }
160
+ let new_real_ref_mutbl = match self . real_default_mode ( ) {
161
+ ByRef :: Yes ( Mutability :: Not ) => Mutability :: Not ,
162
+ _ => implicit_deref_mutbls. min ( ) . unwrap ( ) ,
163
+ } ;
164
+ self . push_deref ( pat_span, ByRef :: Yes ( new_real_ref_mutbl) ) ;
124
165
}
125
166
126
167
/// Tracks the default binding mode when we're lowering a `&` or `&mut` pattern.
127
- /// Returns the prior default binding mode; this should be followed by a call to
128
- /// [`PatMigration::leave_ref`] to restore it when we leave the pattern.
129
- pub ( super ) fn visit_explicit_deref ( & mut self ) -> Option < ( Span , Mutability ) > {
130
- if let Some ( ( default_mode_span, default_ref_mutbl) ) = self . default_mode_span {
131
- // If this eats a by-ref default binding mode, label the binding mode.
132
- self . default_mode_labels . insert ( default_mode_span, default_ref_mutbl) ;
133
- }
134
- // Set the default binding mode to by-value and return the old default binding mode so it
135
- // can be reinstated when we leave the pattern.
136
- self . default_mode_span . take ( )
168
+ /// This should be followed by a call to [`PatMigration::leave_ref`] when we leave the pattern.
169
+ // FIXME(ref_pat_eat_one_layer_2024): This assumes reference patterns correspond to real
170
+ // dereferences. If reference patterns can match the default binding mode alone, we may need to
171
+ // check `TypeckResults::skipped_ref_pats` to tell if this pattern corresponds to an implicit
172
+ // dereference we've already visited.
173
+ pub ( super ) fn visit_explicit_deref ( & mut self , pat_span : Span ) {
174
+ // If this eats a by-ref default binding mode, label the binding mode.
175
+ self . add_default_mode_label_if_needed ( ) ;
176
+ // Set the default binding mode to by-value.
177
+ self . push_deref ( pat_span, ByRef :: No ) ;
178
+ }
179
+
180
+ /// Adds a deref to our deref-forest, so that we can track the default binding mode.
181
+ // TODO: this is also for propagating binding mode changes when we suggest adding patterns
182
+ fn push_deref ( & mut self , span : Span , real_default_mode : ByRef ) {
183
+ let parent = self . innermost_deref ;
184
+ // If this keeps the default binding mode the same, it shares a mode origin with its
185
+ // parent. If it changes the default binding mode, its mode origin is itself.
186
+ let default_mode_origin = if real_default_mode == self . real_default_mode ( ) {
187
+ parent. and_then ( |p| self . derefs [ p] . default_mode_origin )
188
+ } else {
189
+ Some ( span)
190
+ } ;
191
+ let my_ix = self . derefs . push ( PatDeref { real_default_mode, default_mode_origin, parent } ) ;
192
+ self . innermost_deref = Some ( my_ix) ;
137
193
}
138
194
139
195
/// Restores the default binding mode after lowering a pattern that could change it.
140
196
/// This should follow a call to either [`PatMigration::visit_explicit_deref`] or
141
197
/// [`PatMigration::visit_implicit_derefs`].
142
- pub ( super ) fn leave_ref ( & mut self , old_mode_span : Option < ( Span , Mutability ) > ) {
143
- self . default_mode_span = old_mode_span
198
+ pub ( super ) fn leave_ref ( & mut self ) {
199
+ debug_assert ! ( self . innermost_deref. is_some( ) , "entering/leaving refs should be paired" ) ;
200
+ if let Some ( child_ix) = self . innermost_deref {
201
+ self . innermost_deref = self . derefs [ child_ix] . parent ;
202
+ }
144
203
}
145
204
146
205
/// Determines if a binding is relevant to the diagnostic and adjusts the notes/suggestion if
@@ -153,13 +212,11 @@ impl<'a> PatMigration<'a> {
153
212
explicit_ba : BindingMode ,
154
213
ident : Ident ,
155
214
) {
156
- if explicit_ba != BindingMode :: NONE
157
- && let Some ( ( default_mode_span, default_ref_mutbl) ) = self . default_mode_span
158
- {
215
+ if explicit_ba != BindingMode :: NONE {
159
216
// If this overrides a by-ref default binding mode, label the binding mode.
160
- self . default_mode_labels . insert ( default_mode_span , default_ref_mutbl ) ;
161
- // If our suggestion is to elide redundnt modes, this will be one of them.
162
- if self . info . suggest_eliding_modes {
217
+ self . add_default_mode_label_if_needed ( ) ;
218
+ if self . info . suggest_eliding_modes && matches ! ( mode . 0 , ByRef :: Yes ( _ ) ) {
219
+ // If our suggestion is to elide redundant modes, this will be one of them.
163
220
self . suggestion . push ( ( pat_span. with_hi ( ident. span . lo ( ) ) , String :: new ( ) ) ) ;
164
221
self . binding_mode_count += 1 ;
165
222
}
0 commit comments