@@ -162,12 +162,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
162
162
/// Mode for adjusting the expected type and binding mode.
163
163
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
164
164
enum AdjustMode {
165
- /// Peel off all immediate reference types.
166
- Peel ,
165
+ /// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this
166
+ /// also peels smart pointer ADTs.
167
+ Peel { kind : PeelKind } ,
167
168
/// Pass on the input binding mode and expected type.
168
169
Pass ,
169
170
}
170
171
172
+ /// Restrictions on what types to peel when adjusting the expected type and binding mode.
173
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
174
+ enum PeelKind {
175
+ /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference
176
+ /// any number of `&`/`&mut` references, plus a single smart pointer.
177
+ ExplicitDerefPat ,
178
+ /// Implicitly peel any number of references, and if `deref_patterns` is enabled, smart pointer
179
+ /// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt`
180
+ /// field contains the ADT def that the pattern is a constructor for, if applicable, so that we
181
+ /// don't peel it. See [`ResolvedPat`] for more information.
182
+ // TODO: add `ResolvedPat` and `until_adt`.
183
+ Implicit ,
184
+ }
185
+
171
186
/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
172
187
/// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics,
173
188
/// we track this when typing patterns for two purposes:
@@ -390,7 +405,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
390
405
}
391
406
392
407
// Resolve type if needed.
393
- let expected = if let AdjustMode :: Peel = adjust_mode
408
+ let expected = if let AdjustMode :: Peel { .. } = adjust_mode
394
409
&& pat. default_binding_modes
395
410
{
396
411
self . try_structurally_resolve_type ( pat. span , expected)
@@ -403,7 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
403
418
match pat. kind {
404
419
// Peel off a `&` or `&mut` from the scrutinee type. See the examples in
405
420
// `tests/ui/rfcs/rfc-2005-default-binding-mode`.
406
- _ if let AdjustMode :: Peel = adjust_mode
421
+ _ if let AdjustMode :: Peel { .. } = adjust_mode
407
422
&& pat. default_binding_modes
408
423
&& let ty:: Ref ( _, inner_ty, inner_mutability) = * expected. kind ( ) =>
409
424
{
@@ -443,6 +458,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
443
458
// Recurse with the new expected type.
444
459
self . check_pat_inner ( pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
445
460
}
461
+ // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
462
+ // examples in `tests/ui/pattern/deref_patterns/`.
463
+ _ if self . tcx . features ( ) . deref_patterns ( )
464
+ && let AdjustMode :: Peel { kind : PeelKind :: Implicit } = adjust_mode
465
+ && pat. default_binding_modes
466
+ // For simplicity, only apply overloaded derefs if `expected` is a known ADT.
467
+ // FIXME(deref_patterns): we'll get better diagnostics for users trying to
468
+ // implicitly deref generics if we allow them here, but primitives, tuples, and
469
+ // inference vars definitely should be stopped. Figure out what makes most sense.
470
+ // TODO: stop peeling if the pattern is a constructor for the scrutinee type
471
+ && expected. is_adt ( )
472
+ // At this point, the pattern isn't able to match `expected` without peeling. Check
473
+ // that it implements `Deref` before assuming it's a smart pointer, to get a normal
474
+ // type error instead of a missing impl error if not. This only checks for `Deref`,
475
+ // not `DerefPure`: we require that too, but we want a trait error if it's missing.
476
+ && let Some ( deref_trait) = self . tcx . lang_items ( ) . deref_trait ( )
477
+ && self
478
+ . type_implements_trait ( deref_trait, [ expected] , self . param_env )
479
+ . may_apply ( ) =>
480
+ {
481
+ debug ! ( "scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref" ) ;
482
+ // The scrutinee is a smart pointer; implicitly dereference it. This adds a
483
+ // requirement that `expected: DerefPure`.
484
+ let inner_ty = self . deref_pat_target ( pat. span , expected) ;
485
+ // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
486
+ // `ref mut` bindings. TODO: implement that, then reference here.
487
+
488
+ // Preserve the smart pointer type for THIR lowering and upvar analysis.
489
+ self . typeck_results
490
+ . borrow_mut ( )
491
+ . pat_adjustments_mut ( )
492
+ . entry ( pat. hir_id )
493
+ . or_default ( )
494
+ . push ( PatAdjustment { kind : PatAdjust :: OverloadedDeref , source : expected } ) ;
495
+
496
+ // Recurse, using the old pat info to keep `current_depth` to its old value.
497
+ // Peeling smart pointers does not update the default binding mode.
498
+ self . check_pat_inner ( pat, opt_path_res, adjust_mode, inner_ty, old_pat_info)
499
+ }
446
500
PatKind :: Missing | PatKind :: Wild | PatKind :: Err ( _) => expected,
447
501
// We allow any type here; we ensure that the type is uninhabited during match checking.
448
502
PatKind :: Never => expected,
@@ -505,12 +559,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
505
559
PatKind :: Struct ( ..)
506
560
| PatKind :: TupleStruct ( ..)
507
561
| PatKind :: Tuple ( ..)
508
- | PatKind :: Box ( _)
509
- | PatKind :: Deref ( _)
510
562
| PatKind :: Range ( ..)
511
- | PatKind :: Slice ( ..) => AdjustMode :: Peel ,
563
+ | PatKind :: Slice ( ..) => AdjustMode :: Peel { kind : PeelKind :: Implicit } ,
564
+ // When checking an explicit deref pattern, only peel reference types.
565
+ // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
566
+ // patterns may want `PeelKind::Implicit`, stopping on encountering a box.
567
+ | PatKind :: Box ( _)
568
+ | PatKind :: Deref ( _) => AdjustMode :: Peel { kind : PeelKind :: ExplicitDerefPat } ,
512
569
// A never pattern behaves somewhat like a literal or unit variant.
513
- PatKind :: Never => AdjustMode :: Peel ,
570
+ PatKind :: Never => AdjustMode :: Peel { kind : PeelKind :: Implicit } ,
514
571
PatKind :: Expr ( PatExpr { kind : PatExprKind :: Path ( _) , .. } ) => match opt_path_res. unwrap ( ) {
515
572
// These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
516
573
// Peeling the reference types too early will cause type checking failures.
@@ -520,7 +577,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
520
577
// could successfully compile. The former being `Self` requires a unit struct.
521
578
// In either case, and unlike constants, the pattern itself cannot be
522
579
// a reference type wherefore peeling doesn't give up any expressiveness.
523
- _ => AdjustMode :: Peel ,
580
+ _ => AdjustMode :: Peel { kind : PeelKind :: Implicit } ,
524
581
} ,
525
582
526
583
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
@@ -530,7 +587,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
530
587
// Call `resolve_vars_if_possible` here for inline const blocks.
531
588
PatKind :: Expr ( lt) => match self . resolve_vars_if_possible ( self . check_pat_expr_unadjusted ( lt) ) . kind ( ) {
532
589
ty:: Ref ( ..) => AdjustMode :: Pass ,
533
- _ => AdjustMode :: Peel ,
590
+ _ => AdjustMode :: Peel { kind : PeelKind :: Implicit } ,
534
591
} ,
535
592
536
593
// Ref patterns are complicated, we handle them in `check_pat_ref`.
@@ -2256,38 +2313,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2256
2313
expected : Ty < ' tcx > ,
2257
2314
pat_info : PatInfo < ' tcx > ,
2258
2315
) -> Ty < ' tcx > {
2259
- let tcx = self . tcx ;
2260
- // Register a `DerefPure` bound, which is required by all `deref!()` pats.
2261
- self . register_bound (
2262
- expected,
2263
- tcx. require_lang_item ( hir:: LangItem :: DerefPure , Some ( span) ) ,
2264
- self . misc ( span) ,
2265
- ) ;
2266
- // <expected as Deref>::Target
2267
- let ty = Ty :: new_projection (
2268
- tcx,
2269
- tcx. require_lang_item ( hir:: LangItem :: DerefTarget , Some ( span) ) ,
2270
- [ expected] ,
2271
- ) ;
2272
- let ty = self . normalize ( span, ty) ;
2273
- let ty = self . try_structurally_resolve_type ( span, ty) ;
2274
- self . check_pat ( inner, ty, pat_info) ;
2316
+ let target_ty = self . deref_pat_target ( span, expected) ;
2317
+ self . check_pat ( inner, target_ty, pat_info) ;
2275
2318
2276
2319
// Check if the pattern has any `ref mut` bindings, which would require
2277
2320
// `DerefMut` to be emitted in MIR building instead of just `Deref`.
2278
2321
// We do this *after* checking the inner pattern, since we want to make
2279
2322
// sure to apply any match-ergonomics adjustments.
2323
+ // TODO: move this to a separate definition to share it with implicit deref pats
2280
2324
if self . typeck_results . borrow ( ) . pat_has_ref_mut_binding ( inner) {
2281
2325
self . register_bound (
2282
2326
expected,
2283
- tcx. require_lang_item ( hir:: LangItem :: DerefMut , Some ( span) ) ,
2327
+ self . tcx . require_lang_item ( hir:: LangItem :: DerefMut , Some ( span) ) ,
2284
2328
self . misc ( span) ,
2285
2329
) ;
2286
2330
}
2287
2331
2288
2332
expected
2289
2333
}
2290
2334
2335
+ fn deref_pat_target ( & self , span : Span , source_ty : Ty < ' tcx > ) -> Ty < ' tcx > {
2336
+ // Register a `DerefPure` bound, which is required by all `deref!()` pats.
2337
+ let tcx = self . tcx ;
2338
+ self . register_bound (
2339
+ source_ty,
2340
+ tcx. require_lang_item ( hir:: LangItem :: DerefPure , Some ( span) ) ,
2341
+ self . misc ( span) ,
2342
+ ) ;
2343
+ // The expected type for the deref pat's inner pattern is `<expected as Deref>::Target`.
2344
+ let target_ty = Ty :: new_projection (
2345
+ tcx,
2346
+ tcx. require_lang_item ( hir:: LangItem :: DerefTarget , Some ( span) ) ,
2347
+ [ source_ty] ,
2348
+ ) ;
2349
+ let target_ty = self . normalize ( span, target_ty) ;
2350
+ self . try_structurally_resolve_type ( span, target_ty)
2351
+ }
2352
+
2291
2353
// Precondition: Pat is Ref(inner)
2292
2354
fn check_pat_ref (
2293
2355
& self ,
0 commit comments