@@ -16,7 +16,7 @@ use rustc_hir::{
16
16
} ;
17
17
use rustc_infer:: infer;
18
18
use rustc_middle:: traits:: PatternOriginExpr ;
19
- use rustc_middle:: ty:: { self , Ty , TypeVisitableExt } ;
19
+ use rustc_middle:: ty:: { self , AdtDef , Ty , TypeVisitableExt } ;
20
20
use rustc_middle:: { bug, span_bug} ;
21
21
use rustc_session:: lint:: builtin:: NON_EXHAUSTIVE_OMITTED_PATTERNS ;
22
22
use rustc_session:: parse:: feature_err;
@@ -160,10 +160,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
160
160
161
161
/// Mode for adjusting the expected type and binding mode.
162
162
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
163
- enum AdjustMode {
163
+ enum AdjustMode < ' tcx > {
164
164
/// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this
165
165
/// also peels library-defined smart pointer ADTs.
166
- Peel { kind : PeelKind } ,
166
+ Peel { kind : PeelKind < ' tcx > } ,
167
167
/// Reset binding mode to the initial mode.
168
168
/// Used for destructuring assignment, where we don't want any match ergonomics.
169
169
Reset ,
@@ -173,14 +173,22 @@ enum AdjustMode {
173
173
174
174
/// Restrictions on what types to peel when adjusting the expected type and binding mode.
175
175
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
176
- enum PeelKind {
176
+ enum PeelKind < ' tcx > {
177
177
/// Only peel reference types. This is used for explicit deref patterns.
178
178
RefOnly ,
179
179
/// If `deref_patterns` is enabled, this also peels library-defined smart pointer ADTs, except
180
180
/// for `until_adt` if the pattern is a constructor. Otherwise this is the same as `RefOnly`.
181
181
/// See [`ResolvedPat`] for more information.
182
- // TODO: add `ResolvedPat` and `until_adt`.
183
- Overloaded ,
182
+ Overloaded { until_adt : Option < AdtDef < ' tcx > > } ,
183
+ }
184
+
185
+ impl < ' tcx > AdjustMode < ' tcx > {
186
+ const fn peel_until_adt ( opt_adt_def : Option < AdtDef < ' tcx > > ) -> AdjustMode < ' tcx > {
187
+ AdjustMode :: Peel { kind : PeelKind :: Overloaded { until_adt : opt_adt_def } }
188
+ }
189
+ const fn peel_all ( ) -> AdjustMode < ' tcx > {
190
+ AdjustMode :: peel_until_adt ( None )
191
+ }
184
192
}
185
193
186
194
/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
@@ -266,11 +274,12 @@ enum InheritedRefMatchRule {
266
274
///
267
275
/// `ResolvedPat` contains the information from resolution needed to determine match ergonomics
268
276
/// adjustments, plus a callback to finish checking the pattern once we know its adjusted type.
269
- // TODO: add an `adt_def: Option<DefId>` field to handle the `Cow` case above.
270
- struct ResolvedPat < F : ?Sized > {
277
+ struct ResolvedPat < ' tcx , F : ?Sized > {
271
278
/// For path patterns, this must be `Some(res)`, where `res` is the resolution of the pattern's
272
279
/// `QPath`. Otherwise, this is `None`.
273
280
path_res : Option < Res > ,
281
+ /// If applicable, identifies the ADT that the pattern matches against.
282
+ pat_adt_def : Option < AdtDef < ' tcx > > ,
274
283
/// Given the expected type of the scrutinee and the [`PatInfo`] after applying match ergonomics
275
284
/// adjustments, finish checking the pattern.
276
285
check : F ,
@@ -356,7 +365,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
356
365
357
366
// For patterns containing paths, we need the path's resolution to determine whether to
358
367
// implicitly dereference the scrutinee before matching.
359
- let resolved_pat: Option < & ResolvedPat < dyn Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > > =
368
+ let resolved_pat: Option < & ResolvedPat < ' tcx , dyn Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > > =
360
369
match pat. kind {
361
370
PatKind :: Expr ( PatExpr { kind : PatExprKind :: Path ( qpath) , hir_id, span } ) => {
362
371
Some { 0 : & self . check_pat_path ( * hir_id, pat. hir_id , * span, qpath, & ti) }
@@ -369,7 +378,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
369
378
}
370
379
_ => None ,
371
380
} ;
372
- let adjust_mode = self . calc_adjust_mode ( pat, resolved_pat. and_then ( |r| r. path_res ) ) ;
381
+ let adjust_mode = self . calc_adjust_mode (
382
+ pat,
383
+ resolved_pat. and_then ( |r| r. path_res ) ,
384
+ resolved_pat. and_then ( |r| r. pat_adt_def ) ,
385
+ ) ;
373
386
let ( expected, binding_mode, max_ref_mutbl) =
374
387
self . calc_default_binding_mode ( pat, expected, binding_mode, adjust_mode, max_ref_mutbl) ;
375
388
let pat_info = PatInfo {
@@ -484,7 +497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
484
497
pat : & ' tcx Pat < ' tcx > ,
485
498
expected : Ty < ' tcx > ,
486
499
def_br : ByRef ,
487
- adjust_mode : AdjustMode ,
500
+ adjust_mode : AdjustMode < ' tcx > ,
488
501
max_ref_mutbl : MutblCap ,
489
502
) -> ( Ty < ' tcx > , ByRef , MutblCap ) {
490
503
#[ cfg( debug_assertions) ]
@@ -506,7 +519,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
506
519
/// How should the binding mode and expected type be adjusted?
507
520
///
508
521
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
509
- fn calc_adjust_mode ( & self , pat : & ' tcx Pat < ' tcx > , opt_path_res : Option < Res > ) -> AdjustMode {
522
+ /// When the pattern contains a path (such as a struct pattern or tuple struct pattern),
523
+ /// `pat_adt_def` should be `Some(adt)` if the path resolved to an ADT constructor.
524
+ fn calc_adjust_mode (
525
+ & self ,
526
+ pat : & ' tcx Pat < ' tcx > ,
527
+ opt_path_res : Option < Res > ,
528
+ pat_adt_def : Option < AdtDef < ' tcx > > ,
529
+ ) -> AdjustMode < ' tcx > {
510
530
// When we perform destructuring assignment, we disable default match bindings, which are
511
531
// unintuitive in this context.
512
532
if !pat. default_binding_modes {
@@ -519,14 +539,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
519
539
| PatKind :: TupleStruct ( ..)
520
540
| PatKind :: Tuple ( ..)
521
541
| PatKind :: Range ( ..)
522
- | PatKind :: Slice ( ..) => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
542
+ | PatKind :: Slice ( ..) => AdjustMode :: peel_until_adt ( pat_adt_def ) ,
523
543
// When checking an explicit deref pattern, only peel reference types.
524
544
// FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
525
545
// patterns may want `PeelKind::Overloaded`, stopping on encountering a box.
526
546
| PatKind :: Box ( _)
527
547
| PatKind :: Deref ( _) => AdjustMode :: Peel { kind : PeelKind :: RefOnly } ,
528
548
// A never pattern behaves somewhat like a literal or unit variant.
529
- PatKind :: Never => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
549
+ PatKind :: Never => AdjustMode :: peel_all ( ) ,
530
550
PatKind :: Expr ( PatExpr { kind : PatExprKind :: Path ( _) , .. } ) => match opt_path_res. unwrap ( ) {
531
551
// These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
532
552
// Peeling the reference types too early will cause type checking failures.
@@ -536,7 +556,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
536
556
// could successfully compile. The former being `Self` requires a unit struct.
537
557
// In either case, and unlike constants, the pattern itself cannot be
538
558
// a reference type wherefore peeling doesn't give up any expressiveness.
539
- _ => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
559
+ _ => AdjustMode :: peel_until_adt ( pat_adt_def ) ,
540
560
} ,
541
561
542
562
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
@@ -546,7 +566,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
546
566
// Call `resolve_vars_if_possible` here for inline const blocks.
547
567
PatKind :: Expr ( lt) => match self . resolve_vars_if_possible ( self . check_pat_expr_unadjusted ( lt) ) . kind ( ) {
548
568
ty:: Ref ( ..) => AdjustMode :: Pass ,
549
- _ => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
569
+ // If an inline const pattern has a library-defined pointer-like type and
570
+ // `deref_patterns` is enabled, don't implicitly add deref patterns for its ADT.
571
+ // Nothing in the library that implements `DerefPure` supports structural equality,
572
+ // but we don't check that until `const_to_pat` in THIR construction. By avoiding
573
+ // type errors here, we can get a more meaningful error there.
574
+ ty:: Adt ( adt, _) => AdjustMode :: peel_until_adt ( Some ( * adt) ) ,
575
+ _ => AdjustMode :: peel_all ( )
550
576
} ,
551
577
552
578
// Ref patterns are complicated, we handle them in `check_pat_ref`.
@@ -576,7 +602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
576
602
fn peel_off_references (
577
603
& self ,
578
604
pat : & ' tcx Pat < ' tcx > ,
579
- peel_kind : PeelKind ,
605
+ peel_kind : PeelKind < ' tcx > ,
580
606
expected : Ty < ' tcx > ,
581
607
mut def_br : ByRef ,
582
608
mut max_ref_mutbl : MutblCap ,
@@ -610,13 +636,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
610
636
} ) ;
611
637
inner_ty
612
638
} else if deref_patterns
613
- && peel_kind == PeelKind :: Overloaded
639
+ && let PeelKind :: Overloaded { until_adt } = peel_kind
614
640
// For simplicity, only apply overloaded derefs if `expected` is a known ADT.
615
641
// FIXME(deref_patterns): we'll get better diagnostics for users trying to
616
642
// implicitly deref generics if we allow them here, but primitives, tuples, and
617
643
// inference vars definitely should be stopped. Figure out what makes most sense.
618
- // TODO: stop peeling if the pattern is a constructor for the scrutinee type
619
- && expected. is_adt ( )
644
+ && let ty:: Adt ( scrutinee_adt, _) = * expected. kind ( )
645
+ // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
646
+ // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
647
+ && until_adt != Some ( scrutinee_adt)
620
648
{
621
649
// At this point, the pattern isn't able to match `expected` without peeling. Check
622
650
// that it implements `Deref` before assuming it's a smart pointer, to get a normal
@@ -1231,9 +1259,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1231
1259
qpath : & hir:: QPath < ' tcx > ,
1232
1260
fields : & ' tcx [ hir:: PatField < ' tcx > ] ,
1233
1261
has_rest_pat : bool ,
1234
- ) -> ResolvedPat < impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1262
+ ) -> ResolvedPat < ' tcx , impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1235
1263
// Resolve the path and check the definition for errors.
1236
1264
let variant_and_pat_ty = self . check_struct_path ( qpath, pat. hir_id ) ;
1265
+ let pat_adt_def = variant_and_pat_ty. ok ( ) . and_then ( |( _, pat_ty) | pat_ty. ty_adt_def ( ) ) ;
1237
1266
1238
1267
let check = move |expected : Ty < ' tcx > , pat_info : PatInfo < ' tcx > | -> Ty < ' tcx > {
1239
1268
let ( variant, pat_ty) = match variant_and_pat_ty {
@@ -1258,7 +1287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1258
1287
}
1259
1288
} ;
1260
1289
1261
- ResolvedPat { path_res : None , check }
1290
+ ResolvedPat { path_res : None , pat_adt_def , check }
1262
1291
}
1263
1292
1264
1293
fn check_pat_path (
@@ -1268,7 +1297,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1268
1297
span : Span ,
1269
1298
qpath : & ' tcx hir:: QPath < ' tcx > ,
1270
1299
ti : & TopInfo < ' tcx > ,
1271
- ) -> ResolvedPat < impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1300
+ ) -> ResolvedPat < ' tcx , impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1272
1301
let tcx = self . tcx ;
1273
1302
1274
1303
let ( res, opt_ty, segments) =
@@ -1317,6 +1346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1317
1346
// Type-check the path.
1318
1347
let pat_ty_and_res =
1319
1348
res_ok. map ( |_| self . instantiate_value_path ( segments, opt_ty, res, span, span, path_id) ) ;
1349
+ let pat_adt_def = pat_ty_and_res. ok ( ) . and_then ( |( pat_ty, _) | pat_ty. ty_adt_def ( ) ) ;
1320
1350
1321
1351
let check = move |expected, _pat_info| -> Ty < ' tcx > {
1322
1352
let ( pat_ty, pat_res) = match pat_ty_and_res {
@@ -1331,7 +1361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1331
1361
}
1332
1362
pat_ty
1333
1363
} ;
1334
- ResolvedPat { path_res : Some ( res) , check }
1364
+ ResolvedPat { path_res : Some ( res) , pat_adt_def , check }
1335
1365
}
1336
1366
1337
1367
fn maybe_suggest_range_literal (
@@ -1447,14 +1477,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1447
1477
qpath : & ' tcx hir:: QPath < ' tcx > ,
1448
1478
subpats : & ' tcx [ Pat < ' tcx > ] ,
1449
1479
ddpos : hir:: DotDotPos ,
1450
- ) -> ResolvedPat < impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1480
+ ) -> ResolvedPat < ' tcx , impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1451
1481
let tcx = self . tcx ;
1452
1482
let report_unexpected_res = |res : Res | {
1453
1483
let expected = "tuple struct or tuple variant" ;
1454
1484
report_unexpected_variant_res ( tcx, res, None , qpath, pat. span , E0164 , expected)
1455
1485
} ;
1456
1486
1457
- let pat_ty_and_res_and_variant = try {
1487
+ let pat_ty_and_res_and_variant: Result < ( Ty < ' _ > , Res , & VariantDef ) , ErrorGuaranteed > = try {
1458
1488
// Resolve the path and check the definition for errors.
1459
1489
let ( res, opt_ty, segments) =
1460
1490
self . resolve_ty_and_res_fully_qualified_call ( qpath, pat. hir_id , pat. span ) ;
@@ -1488,6 +1518,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1488
1518
1489
1519
( pat_ty, res, variant)
1490
1520
} ;
1521
+ let pat_adt_def = pat_ty_and_res_and_variant. ok ( ) . and_then ( |( ty, ..) | ty. ty_adt_def ( ) ) ;
1491
1522
1492
1523
let check = move |expected : Ty < ' tcx > , pat_info : PatInfo < ' tcx > | -> Ty < ' tcx > {
1493
1524
let on_error = |e| {
@@ -1547,7 +1578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1547
1578
pat_ty
1548
1579
} ;
1549
1580
1550
- ResolvedPat { path_res : None , check }
1581
+ ResolvedPat { path_res : None , pat_adt_def , check }
1551
1582
}
1552
1583
1553
1584
fn emit_err_pat_wrong_number_of_fields (
0 commit comments