@@ -89,6 +89,18 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
8989 }
9090}
9191
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+
92104impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
93105 /// Type check the given top level pattern against the `expected` type.
94106 ///
@@ -105,8 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
105117 span : Option < Span > ,
106118 origin_expr : bool ,
107119 ) {
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 } ) ;
110121 }
111122
112123 /// Type check the given `pat` against the `expected` type
@@ -123,12 +134,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
123134 ) {
124135 debug ! ( "check_pat(pat={:?},expected={:?},def_bm={:?})" , pat, expected, def_bm) ;
125136
126- let path_resolution = match & pat. kind {
137+ let path_res = match & pat. kind {
127138 PatKind :: Path ( qpath) => Some ( self . resolve_ty_and_res_ufcs ( qpath, pat. hir_id , pat. span ) ) ,
128139 _ => None ,
129140 } ;
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 ) ;
132143
133144 let ty = match pat. kind {
134145 PatKind :: Wild => expected,
@@ -141,7 +152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
141152 self . check_pat_tuple_struct ( pat, qpath, subpats, ddpos, expected, def_bm, ti)
142153 }
143154 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)
145156 }
146157 PatKind :: Struct ( ref qpath, fields, etc) => {
147158 self . check_pat_struct ( pat, qpath, fields, etc, expected, def_bm, ti)
@@ -223,64 +234,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
223234 pat : & ' tcx Pat < ' tcx > ,
224235 expected : Ty < ' tcx > ,
225236 def_bm : BindingMode ,
226- is_non_ref_pat : bool ,
237+ adjust_mode : AdjustMode ,
227238 ) -> ( 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) ,
248243 }
249244 }
250245
251- /// Is the pattern a "non reference pattern"?
246+ /// How should the binding mode and expected type be adjusted?
247+ ///
252248 /// 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.
255253 PatKind :: Struct ( ..)
256254 | PatKind :: TupleStruct ( ..)
257255 | PatKind :: Tuple ( ..)
258256 | PatKind :: Box ( _)
259257 | 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+ } ,
268266 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 ,
271276 } ,
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+ //
275280 // ```
276- // match &1 {
277- // e @ &(1...2) | e @ &(3...4) => {}
278- // _ => {}
281+ // match &(&22, &44) {
282+ // (&x, &y) => ...
279283 // }
280284 // ```
281285 //
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 ,
284299 }
285300 }
286301
@@ -508,7 +523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
508523 let local_ty = self . local_ty ( pat. span , pat. hir_id ) . decl_ty ;
509524 let eq_ty = match bm {
510525 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`,
512527 // then `x` is assigned a value of type `&M T` where M is the
513528 // mutability and T is the expected type.
514529 //
0 commit comments