@@ -42,7 +42,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
42
42
43
43
for param in thir. params . iter ( ) {
44
44
if let Some ( box ref pattern) = param. pat {
45
- visitor. check_binding_is_irrefutable ( pattern, "function argument" , None ) ;
45
+ visitor. check_binding_is_irrefutable ( pattern, "function argument" , None , None ) ;
46
46
}
47
47
}
48
48
visitor. error
@@ -254,10 +254,11 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
254
254
self . with_lint_level ( lint_level, |this| this. visit_land_rhs ( & this. thir [ value] ) )
255
255
}
256
256
ExprKind :: Let { box ref pat, expr } => {
257
+ let expr = & self . thir ( ) [ expr] ;
257
258
self . with_let_source ( LetSource :: None , |this| {
258
- this. visit_expr ( & this . thir ( ) [ expr] ) ;
259
+ this. visit_expr ( expr) ;
259
260
} ) ;
260
- Ok ( Some ( ( ex. span , self . is_let_irrefutable ( pat) ?) ) )
261
+ Ok ( Some ( ( ex. span , self . is_let_irrefutable ( pat, Some ( expr ) ) ?) ) )
261
262
}
262
263
_ => {
263
264
self . with_let_source ( LetSource :: None , |this| {
@@ -287,35 +288,114 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
287
288
}
288
289
}
289
290
291
+ /// Inspects the match scrutinee expression to determine whether the place it evaluates to may
292
+ /// hold invalid data.
293
+ fn is_known_valid_scrutinee ( & self , scrutinee : & Expr < ' tcx > ) -> bool {
294
+ use ExprKind :: * ;
295
+ match & scrutinee. kind {
296
+ // Both pointers and references can validly point to a place with invalid data.
297
+ Deref { .. } => false ,
298
+ // Inherit validity of the parent place, unless the parent is an union.
299
+ Field { lhs, .. } => {
300
+ let lhs = & self . thir ( ) [ * lhs] ;
301
+ match lhs. ty . kind ( ) {
302
+ ty:: Adt ( def, _) if def. is_union ( ) => false ,
303
+ _ => self . is_known_valid_scrutinee ( lhs) ,
304
+ }
305
+ }
306
+ // Essentially a field access.
307
+ Index { lhs, .. } => {
308
+ let lhs = & self . thir ( ) [ * lhs] ;
309
+ self . is_known_valid_scrutinee ( lhs)
310
+ }
311
+
312
+ // No-op.
313
+ Scope { value, .. } => self . is_known_valid_scrutinee ( & self . thir ( ) [ * value] ) ,
314
+
315
+ // Casts don't cause a load.
316
+ NeverToAny { source }
317
+ | Cast { source }
318
+ | Use { source }
319
+ | PointerCoercion { source, .. }
320
+ | PlaceTypeAscription { source, .. }
321
+ | ValueTypeAscription { source, .. } => {
322
+ self . is_known_valid_scrutinee ( & self . thir ( ) [ * source] )
323
+ }
324
+
325
+ // These diverge.
326
+ Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true ,
327
+
328
+ // These are statements that evaluate to `()`.
329
+ Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true ,
330
+
331
+ // These evaluate to a value.
332
+ AddressOf { .. }
333
+ | Adt { .. }
334
+ | Array { .. }
335
+ | Binary { .. }
336
+ | Block { .. }
337
+ | Borrow { .. }
338
+ | Box { .. }
339
+ | Call { .. }
340
+ | Closure { .. }
341
+ | ConstBlock { .. }
342
+ | ConstParam { .. }
343
+ | If { .. }
344
+ | Literal { .. }
345
+ | LogicalOp { .. }
346
+ | Loop { .. }
347
+ | Match { .. }
348
+ | NamedConst { .. }
349
+ | NonHirLiteral { .. }
350
+ | OffsetOf { .. }
351
+ | Repeat { .. }
352
+ | StaticRef { .. }
353
+ | ThreadLocalRef { .. }
354
+ | Tuple { .. }
355
+ | Unary { .. }
356
+ | UpvarRef { .. }
357
+ | VarRef { .. }
358
+ | ZstLiteral { .. }
359
+ | Yield { .. } => true ,
360
+ }
361
+ }
362
+
290
363
fn new_cx (
291
364
& self ,
292
365
refutability : RefutableFlag ,
293
- match_span : Option < Span > ,
366
+ whole_match_span : Option < Span > ,
367
+ scrutinee : Option < & Expr < ' tcx > > ,
294
368
scrut_span : Span ,
295
369
) -> MatchCheckCtxt < ' p , ' tcx > {
296
370
let refutable = match refutability {
297
371
Irrefutable => false ,
298
372
Refutable => true ,
299
373
} ;
374
+ // If we don't have a scrutinee we're either a function parameter or a `let x;`. Both cases
375
+ // require validity.
376
+ let known_valid_scrutinee =
377
+ scrutinee. map ( |scrut| self . is_known_valid_scrutinee ( scrut) ) . unwrap_or ( true ) ;
300
378
MatchCheckCtxt {
301
379
tcx : self . tcx ,
302
380
param_env : self . param_env ,
303
381
module : self . tcx . parent_module ( self . lint_level ) . to_def_id ( ) ,
304
382
pattern_arena : self . pattern_arena ,
305
383
match_lint_level : self . lint_level ,
306
- match_span ,
384
+ whole_match_span ,
307
385
scrut_span,
308
386
refutable,
387
+ known_valid_scrutinee,
309
388
}
310
389
}
311
390
312
391
#[ instrument( level = "trace" , skip( self ) ) ]
313
392
fn check_let ( & mut self , pat : & Pat < ' tcx > , scrutinee : Option < ExprId > , span : Span ) {
314
393
assert ! ( self . let_source != LetSource :: None ) ;
394
+ let scrut = scrutinee. map ( |id| & self . thir [ id] ) ;
315
395
if let LetSource :: PlainLet = self . let_source {
316
- self . check_binding_is_irrefutable ( pat, "local binding" , Some ( span) )
396
+ self . check_binding_is_irrefutable ( pat, "local binding" , scrut , Some ( span) )
317
397
} else {
318
- let Ok ( refutability) = self . is_let_irrefutable ( pat) else { return } ;
398
+ let Ok ( refutability) = self . is_let_irrefutable ( pat, scrut ) else { return } ;
319
399
if matches ! ( refutability, Irrefutable ) {
320
400
report_irrefutable_let_patterns (
321
401
self . tcx ,
@@ -336,7 +416,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
336
416
expr_span : Span ,
337
417
) {
338
418
let scrut = & self . thir [ scrut] ;
339
- let cx = self . new_cx ( Refutable , Some ( expr_span) , scrut. span ) ;
419
+ let cx = self . new_cx ( Refutable , Some ( expr_span) , Some ( scrut ) , scrut. span ) ;
340
420
341
421
let mut tarms = Vec :: with_capacity ( arms. len ( ) ) ;
342
422
for & arm in arms {
@@ -377,7 +457,12 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
377
457
debug_assert_eq ! ( pat. span. desugaring_kind( ) , Some ( DesugaringKind :: ForLoop ) ) ;
378
458
let PatKind :: Variant { ref subpatterns, .. } = pat. kind else { bug ! ( ) } ;
379
459
let [ pat_field] = & subpatterns[ ..] else { bug ! ( ) } ;
380
- self . check_binding_is_irrefutable ( & pat_field. pattern , "`for` loop binding" , None ) ;
460
+ self . check_binding_is_irrefutable (
461
+ & pat_field. pattern ,
462
+ "`for` loop binding" ,
463
+ None ,
464
+ None ,
465
+ ) ;
381
466
} else {
382
467
self . error = Err ( report_non_exhaustive_match (
383
468
& cx, self . thir , scrut_ty, scrut. span , witnesses, arms, expr_span,
@@ -457,16 +542,21 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
457
542
& mut self ,
458
543
pat : & Pat < ' tcx > ,
459
544
refutability : RefutableFlag ,
545
+ scrut : Option < & Expr < ' tcx > > ,
460
546
) -> Result < ( MatchCheckCtxt < ' p , ' tcx > , UsefulnessReport < ' p , ' tcx > ) , ErrorGuaranteed > {
461
- let cx = self . new_cx ( refutability, None , pat. span ) ;
547
+ let cx = self . new_cx ( refutability, None , scrut , pat. span ) ;
462
548
let pat = self . lower_pattern ( & cx, pat) ?;
463
549
let arms = [ MatchArm { pat, hir_id : self . lint_level , has_guard : false } ] ;
464
550
let report = compute_match_usefulness ( & cx, & arms, pat. ty ( ) ) ;
465
551
Ok ( ( cx, report) )
466
552
}
467
553
468
- fn is_let_irrefutable ( & mut self , pat : & Pat < ' tcx > ) -> Result < RefutableFlag , ErrorGuaranteed > {
469
- let ( cx, report) = self . analyze_binding ( pat, Refutable ) ?;
554
+ fn is_let_irrefutable (
555
+ & mut self ,
556
+ pat : & Pat < ' tcx > ,
557
+ scrut : Option < & Expr < ' tcx > > ,
558
+ ) -> Result < RefutableFlag , ErrorGuaranteed > {
559
+ let ( cx, report) = self . analyze_binding ( pat, Refutable , scrut) ?;
470
560
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
471
561
// This also reports unreachable sub-patterns.
472
562
report_arm_reachability ( & cx, & report) ;
@@ -476,10 +566,16 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
476
566
}
477
567
478
568
#[ instrument( level = "trace" , skip( self ) ) ]
479
- fn check_binding_is_irrefutable ( & mut self , pat : & Pat < ' tcx > , origin : & str , sp : Option < Span > ) {
569
+ fn check_binding_is_irrefutable (
570
+ & mut self ,
571
+ pat : & Pat < ' tcx > ,
572
+ origin : & str ,
573
+ scrut : Option < & Expr < ' tcx > > ,
574
+ sp : Option < Span > ,
575
+ ) {
480
576
let pattern_ty = pat. ty ;
481
577
482
- let Ok ( ( cx, report) ) = self . analyze_binding ( pat, Irrefutable ) else { return } ;
578
+ let Ok ( ( cx, report) ) = self . analyze_binding ( pat, Irrefutable , scrut ) else { return } ;
483
579
let witnesses = report. non_exhaustiveness_witnesses ;
484
580
if witnesses. is_empty ( ) {
485
581
// The pattern is irrefutable.
0 commit comments