@@ -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
@@ -1236,9 +1264,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1236
1264
qpath : & hir:: QPath < ' tcx > ,
1237
1265
fields : & ' tcx [ hir:: PatField < ' tcx > ] ,
1238
1266
has_rest_pat : bool ,
1239
- ) -> ResolvedPat < impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1267
+ ) -> ResolvedPat < ' tcx , impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1240
1268
// Resolve the path and check the definition for errors.
1241
1269
let variant_and_pat_ty = self . check_struct_path ( qpath, pat. hir_id ) ;
1270
+ let pat_adt_def = variant_and_pat_ty. ok ( ) . and_then ( |( _, pat_ty) | pat_ty. ty_adt_def ( ) ) ;
1242
1271
1243
1272
let check = move |expected : Ty < ' tcx > , pat_info : PatInfo < ' tcx > | -> Ty < ' tcx > {
1244
1273
let ( variant, pat_ty) = match variant_and_pat_ty {
@@ -1263,7 +1292,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1263
1292
}
1264
1293
} ;
1265
1294
1266
- ResolvedPat { path_res : None , check }
1295
+ ResolvedPat { path_res : None , pat_adt_def , check }
1267
1296
}
1268
1297
1269
1298
fn check_pat_path (
@@ -1273,7 +1302,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1273
1302
span : Span ,
1274
1303
qpath : & ' tcx hir:: QPath < ' tcx > ,
1275
1304
ti : & TopInfo < ' tcx > ,
1276
- ) -> ResolvedPat < impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1305
+ ) -> ResolvedPat < ' tcx , impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1277
1306
let tcx = self . tcx ;
1278
1307
1279
1308
let ( res, opt_ty, segments) =
@@ -1322,6 +1351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1322
1351
// Type-check the path.
1323
1352
let pat_ty_and_res =
1324
1353
res_ok. map ( |_| self . instantiate_value_path ( segments, opt_ty, res, span, span, path_id) ) ;
1354
+ let pat_adt_def = pat_ty_and_res. ok ( ) . and_then ( |( pat_ty, _) | pat_ty. ty_adt_def ( ) ) ;
1325
1355
1326
1356
let check = move |expected, _pat_info| -> Ty < ' tcx > {
1327
1357
let ( pat_ty, pat_res) = match pat_ty_and_res {
@@ -1336,7 +1366,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1336
1366
}
1337
1367
pat_ty
1338
1368
} ;
1339
- ResolvedPat { path_res : Some ( res) , check }
1369
+ ResolvedPat { path_res : Some ( res) , pat_adt_def , check }
1340
1370
}
1341
1371
1342
1372
fn maybe_suggest_range_literal (
@@ -1452,14 +1482,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1452
1482
qpath : & ' tcx hir:: QPath < ' tcx > ,
1453
1483
subpats : & ' tcx [ Pat < ' tcx > ] ,
1454
1484
ddpos : hir:: DotDotPos ,
1455
- ) -> ResolvedPat < impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1485
+ ) -> ResolvedPat < ' tcx , impl Fn ( Ty < ' tcx > , PatInfo < ' tcx > ) -> Ty < ' tcx > > {
1456
1486
let tcx = self . tcx ;
1457
1487
let report_unexpected_res = |res : Res | {
1458
1488
let expected = "tuple struct or tuple variant" ;
1459
1489
report_unexpected_variant_res ( tcx, res, None , qpath, pat. span , E0164 , expected)
1460
1490
} ;
1461
1491
1462
- let pat_ty_and_res_and_variant = try {
1492
+ let pat_ty_and_res_and_variant: Result < ( Ty < ' _ > , Res , & VariantDef ) , ErrorGuaranteed > = try {
1463
1493
// Resolve the path and check the definition for errors.
1464
1494
let ( res, opt_ty, segments) =
1465
1495
self . resolve_ty_and_res_fully_qualified_call ( qpath, pat. hir_id , pat. span ) ;
@@ -1493,6 +1523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1493
1523
1494
1524
( pat_ty, res, variant)
1495
1525
} ;
1526
+ let pat_adt_def = pat_ty_and_res_and_variant. ok ( ) . and_then ( |( ty, ..) | ty. ty_adt_def ( ) ) ;
1496
1527
1497
1528
let check = move |expected : Ty < ' tcx > , pat_info : PatInfo < ' tcx > | -> Ty < ' tcx > {
1498
1529
let on_error = |e| {
@@ -1552,7 +1583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1552
1583
pat_ty
1553
1584
} ;
1554
1585
1555
- ResolvedPat { path_res : None , check }
1586
+ ResolvedPat { path_res : None , pat_adt_def , check }
1556
1587
}
1557
1588
1558
1589
fn emit_err_pat_wrong_number_of_fields (
0 commit comments