@@ -89,6 +89,18 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
89
89
}
90
90
}
91
91
92
+ const INITIAL_BM : BindingMode = BindingMode :: BindByValue ( hir:: Mutability :: Not ) ;
93
+
94
+ /// Mode for adjusting the expected type and binding mode.
95
+ enum AdjustMode {
96
+ /// Peel off all immediate reference types.
97
+ Peel ,
98
+ /// Reset binding mode to the inital mode.
99
+ Reset ,
100
+ /// Pass on the input binding mode and expected type.
101
+ Pass ,
102
+ }
103
+
92
104
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
93
105
/// Type check the given top level pattern against the `expected` type.
94
106
///
@@ -105,8 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
105
117
span : Option < Span > ,
106
118
origin_expr : bool ,
107
119
) {
108
- let def_bm = BindingMode :: BindByValue ( hir:: Mutability :: Not ) ;
109
- self . check_pat ( pat, expected, def_bm, TopInfo { expected, origin_expr, span } ) ;
120
+ self . check_pat ( pat, expected, INITIAL_BM , TopInfo { expected, origin_expr, span } ) ;
110
121
}
111
122
112
123
/// Type check the given `pat` against the `expected` type
@@ -123,12 +134,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
123
134
) {
124
135
debug ! ( "check_pat(pat={:?},expected={:?},def_bm={:?})" , pat, expected, def_bm) ;
125
136
126
- let path_resolution = match & pat. kind {
137
+ let path_res = match & pat. kind {
127
138
PatKind :: Path ( qpath) => Some ( self . resolve_ty_and_res_ufcs ( qpath, pat. hir_id , pat. span ) ) ,
128
139
_ => None ,
129
140
} ;
130
- let is_nrp = self . is_non_ref_pat ( pat, path_resolution . map ( |( res, ..) | res) ) ;
131
- let ( expected, def_bm) = self . calc_default_binding_mode ( pat, expected, def_bm, is_nrp ) ;
141
+ let adjust_mode = self . calc_adjust_mode ( pat, path_res . map ( |( res, ..) | res) ) ;
142
+ let ( expected, def_bm) = self . calc_default_binding_mode ( pat, expected, def_bm, adjust_mode ) ;
132
143
133
144
let ty = match pat. kind {
134
145
PatKind :: Wild => expected,
@@ -141,7 +152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
141
152
self . check_pat_tuple_struct ( pat, qpath, subpats, ddpos, expected, def_bm, ti)
142
153
}
143
154
PatKind :: Path ( ref qpath) => {
144
- self . check_pat_path ( pat, path_resolution . unwrap ( ) , qpath, expected)
155
+ self . check_pat_path ( pat, path_res . unwrap ( ) , qpath, expected)
145
156
}
146
157
PatKind :: Struct ( ref qpath, fields, etc) => {
147
158
self . check_pat_struct ( pat, qpath, fields, etc, expected, def_bm, ti)
@@ -223,64 +234,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
223
234
pat : & ' tcx Pat < ' tcx > ,
224
235
expected : Ty < ' tcx > ,
225
236
def_bm : BindingMode ,
226
- is_non_ref_pat : bool ,
237
+ adjust_mode : AdjustMode ,
227
238
) -> ( Ty < ' tcx > , BindingMode ) {
228
- if is_non_ref_pat {
229
- debug ! ( "pattern is non reference pattern" ) ;
230
- self . peel_off_references ( pat, expected, def_bm)
231
- } else {
232
- // When you encounter a `&pat` pattern, reset to "by
233
- // value". This is so that `x` and `y` here are by value,
234
- // as they appear to be:
235
- //
236
- // ```
237
- // match &(&22, &44) {
238
- // (&x, &y) => ...
239
- // }
240
- // ```
241
- //
242
- // See issue #46688.
243
- let def_bm = match pat. kind {
244
- PatKind :: Ref ( ..) => ty:: BindByValue ( hir:: Mutability :: Not ) ,
245
- _ => def_bm,
246
- } ;
247
- ( expected, def_bm)
239
+ match adjust_mode {
240
+ AdjustMode :: Pass => ( expected, def_bm) ,
241
+ AdjustMode :: Reset => ( expected, INITIAL_BM ) ,
242
+ AdjustMode :: Peel => self . peel_off_references ( pat, expected, def_bm) ,
248
243
}
249
244
}
250
245
251
- /// Is the pattern a "non reference pattern"?
246
+ /// How should the binding mode and expected type be adjusted?
247
+ ///
252
248
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
253
- fn is_non_ref_pat ( & self , pat : & ' tcx Pat < ' tcx > , opt_path_res : Option < Res > ) -> bool {
254
- match pat. kind {
249
+ fn calc_adjust_mode ( & self , pat : & ' tcx Pat < ' tcx > , opt_path_res : Option < Res > ) -> AdjustMode {
250
+ match & pat. kind {
251
+ // Type checking these product-like types successfully always require
252
+ // that the expected type be of those types and not reference types.
255
253
PatKind :: Struct ( ..)
256
254
| PatKind :: TupleStruct ( ..)
257
255
| PatKind :: Tuple ( ..)
258
256
| PatKind :: Box ( _)
259
257
| PatKind :: Range ( ..)
260
- | PatKind :: Slice ( ..) => true ,
261
- PatKind :: Lit ( ref lt ) => {
262
- let ty = self . check_expr ( lt ) ;
263
- match ty . kind {
264
- ty :: Ref ( .. ) => false ,
265
- _ => true ,
266
- }
267
- }
258
+ | PatKind :: Slice ( ..) => AdjustMode :: Peel ,
259
+ // String and byte-string literals result in types `&str` and `&[u8]` respectively.
260
+ // All other literals result in non-reference types.
261
+ // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`.
262
+ PatKind :: Lit ( lt ) => match self . check_expr ( lt ) . kind {
263
+ ty :: Ref ( .. ) => AdjustMode :: Pass ,
264
+ _ => AdjustMode :: Peel ,
265
+ } ,
268
266
PatKind :: Path ( _) => match opt_path_res. unwrap ( ) {
269
- Res :: Def ( DefKind :: Const , _) | Res :: Def ( DefKind :: AssocConst , _) => false ,
270
- _ => true ,
267
+ // These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
268
+ // Peeling the reference types too early will cause type checking failures.
269
+ // Although it would be possible to *also* peel the types of the constants too.
270
+ Res :: Def ( DefKind :: Const , _) | Res :: Def ( DefKind :: AssocConst , _) => AdjustMode :: Pass ,
271
+ // In the `ValueNS`, we have `SelfCtor(..) | Ctor(_, Const), _)` remaining which
272
+ // could successfully compile. The former being `Self` requires a unit struct.
273
+ // In either case, and unlike constants, the pattern itself cannot be
274
+ // a reference type wherefore peeling doesn't give up any expressivity.
275
+ _ => AdjustMode :: Peel ,
271
276
} ,
272
- // FIXME(or_patterns; Centril | dlrobertson): To keep things compiling
273
- // for or-patterns at the top level, we need to make `p_0 | ... | p_n`
274
- // a "non reference pattern". For example the following currently compiles:
277
+ // When encountering a `& mut? pat` pattern, reset to "by value".
278
+ // This is so that `x` and `y` here are by value, as they appear to be:
279
+ //
275
280
// ```
276
- // match &1 {
277
- // e @ &(1...2) | e @ &(3...4) => {}
278
- // _ => {}
281
+ // match &(&22, &44) {
282
+ // (&x, &y) => ...
279
283
// }
280
284
// ```
281
285
//
282
- // We should consider whether we should do something special in nested or-patterns.
283
- PatKind :: Or ( _) | PatKind :: Wild | PatKind :: Binding ( ..) | PatKind :: Ref ( ..) => false ,
286
+ // See issue #46688.
287
+ PatKind :: Ref ( ..) => AdjustMode :: Reset ,
288
+ // A `_` pattern works with any expected type, so there's no need to do anything.
289
+ PatKind :: Wild
290
+ // Bindings also work with whatever the expected type is,
291
+ // and moreover if we peel references off, that will give us the wrong binding type.
292
+ // Also, we can have a subpattern `binding @ pat`.
293
+ // Each side of the `@` should be treated independently (like with OR-patterns).
294
+ | PatKind :: Binding ( ..)
295
+ // An OR-pattern just propagates to each individual alternative.
296
+ // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`.
297
+ // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`.
298
+ | PatKind :: Or ( _) => AdjustMode :: Pass ,
284
299
}
285
300
}
286
301
@@ -508,7 +523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
508
523
let local_ty = self . local_ty ( pat. span , pat. hir_id ) . decl_ty ;
509
524
let eq_ty = match bm {
510
525
ty:: BindByReference ( mutbl) => {
511
- // If the binding is like `ref x | ref const x | ref mut x`
526
+ // If the binding is like `ref x | ref mut x`,
512
527
// then `x` is assigned a value of type `&M T` where M is the
513
528
// mutability and T is the expected type.
514
529
//
0 commit comments