11use super :: deconstruct_pat:: { Constructor , DeconstructedPat , WitnessPat } ;
22use super :: usefulness:: {
3- compute_match_usefulness, MatchArm , MatchCheckCtxt , Reachability , UsefulnessReport ,
3+ compute_match_usefulness, MatchArm , MatchCheckCtxt , Usefulness , UsefulnessReport ,
44} ;
55
66use crate :: errors:: * ;
@@ -42,7 +42,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
4242
4343 for param in thir. params . iter ( ) {
4444 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 ) ;
4646 }
4747 }
4848 visitor. error
@@ -254,10 +254,11 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
254254 self . with_lint_level ( lint_level, |this| this. visit_land_rhs ( & this. thir [ value] ) )
255255 }
256256 ExprKind :: Let { box ref pat, expr } => {
257+ let expr = & self . thir ( ) [ expr] ;
257258 self . with_let_source ( LetSource :: None , |this| {
258- this. visit_expr ( & this . thir ( ) [ expr] ) ;
259+ this. visit_expr ( expr) ;
259260 } ) ;
260- Ok ( Some ( ( ex. span , self . is_let_irrefutable ( pat) ?) ) )
261+ Ok ( Some ( ( ex. span , self . is_let_irrefutable ( pat, Some ( expr ) ) ?) ) )
261262 }
262263 _ => {
263264 self . with_let_source ( LetSource :: None , |this| {
@@ -287,35 +288,114 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
287288 }
288289 }
289290
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+
290363 fn new_cx (
291364 & self ,
292365 refutability : RefutableFlag ,
293- match_span : Option < Span > ,
366+ whole_match_span : Option < Span > ,
367+ scrutinee : Option < & Expr < ' tcx > > ,
294368 scrut_span : Span ,
295369 ) -> MatchCheckCtxt < ' p , ' tcx > {
296370 let refutable = match refutability {
297371 Irrefutable => false ,
298372 Refutable => true ,
299373 } ;
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 ) ;
300378 MatchCheckCtxt {
301379 tcx : self . tcx ,
302380 param_env : self . param_env ,
303381 module : self . tcx . parent_module ( self . lint_level ) . to_def_id ( ) ,
304382 pattern_arena : self . pattern_arena ,
305383 match_lint_level : self . lint_level ,
306- match_span ,
384+ whole_match_span ,
307385 scrut_span,
308386 refutable,
387+ known_valid_scrutinee,
309388 }
310389 }
311390
312391 #[ instrument( level = "trace" , skip( self ) ) ]
313392 fn check_let ( & mut self , pat : & Pat < ' tcx > , scrutinee : Option < ExprId > , span : Span ) {
314393 assert ! ( self . let_source != LetSource :: None ) ;
394+ let scrut = scrutinee. map ( |id| & self . thir [ id] ) ;
315395 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) )
317397 } else {
318- let Ok ( refutability) = self . is_let_irrefutable ( pat) else { return } ;
398+ let Ok ( refutability) = self . is_let_irrefutable ( pat, scrut ) else { return } ;
319399 if matches ! ( refutability, Irrefutable ) {
320400 report_irrefutable_let_patterns (
321401 self . tcx ,
@@ -336,7 +416,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
336416 expr_span : Span ,
337417 ) {
338418 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 ) ;
340420
341421 let mut tarms = Vec :: with_capacity ( arms. len ( ) ) ;
342422 for & arm in arms {
@@ -377,7 +457,12 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
377457 debug_assert_eq ! ( pat. span. desugaring_kind( ) , Some ( DesugaringKind :: ForLoop ) ) ;
378458 let PatKind :: Variant { ref subpatterns, .. } = pat. kind else { bug ! ( ) } ;
379459 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+ ) ;
381466 } else {
382467 self . error = Err ( report_non_exhaustive_match (
383468 & cx, self . thir , scrut_ty, scrut. span , witnesses, arms, expr_span,
@@ -457,16 +542,21 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
457542 & mut self ,
458543 pat : & Pat < ' tcx > ,
459544 refutability : RefutableFlag ,
545+ scrut : Option < & Expr < ' tcx > > ,
460546 ) -> 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 ) ;
462548 let pat = self . lower_pattern ( & cx, pat) ?;
463549 let arms = [ MatchArm { pat, hir_id : self . lint_level , has_guard : false } ] ;
464550 let report = compute_match_usefulness ( & cx, & arms, pat. ty ( ) ) ;
465551 Ok ( ( cx, report) )
466552 }
467553
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) ?;
470560 // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
471561 // This also reports unreachable sub-patterns.
472562 report_arm_reachability ( & cx, & report) ;
@@ -476,10 +566,16 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
476566 }
477567
478568 #[ 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+ ) {
480576 let pattern_ty = pat. ty ;
481577
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 } ;
483579 let witnesses = report. non_exhaustiveness_witnesses ;
484580 if witnesses. is_empty ( ) {
485581 // The pattern is irrefutable.
@@ -749,18 +845,18 @@ fn report_arm_reachability<'p, 'tcx>(
749845 ) ;
750846 } ;
751847
752- use Reachability :: * ;
848+ use Usefulness :: * ;
753849 let mut catchall = None ;
754850 for ( arm, is_useful) in report. arm_usefulness . iter ( ) {
755851 match is_useful {
756- Unreachable => report_unreachable_pattern ( arm. pat . span ( ) , arm. hir_id , catchall) ,
757- Reachable ( unreachables ) if unreachables . is_empty ( ) => { }
758- // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
759- Reachable ( unreachables ) => {
760- let mut unreachables = unreachables . clone ( ) ;
852+ Redundant => report_unreachable_pattern ( arm. pat . span ( ) , arm. hir_id , catchall) ,
853+ Useful ( redundant_spans ) if redundant_spans . is_empty ( ) => { }
854+ // The arm is reachable, but contains redundant subpatterns (from or-patterns).
855+ Useful ( redundant_spans ) => {
856+ let mut redundant_spans = redundant_spans . clone ( ) ;
761857 // Emit lints in the order in which they occur in the file.
762- unreachables . sort_unstable ( ) ;
763- for span in unreachables {
858+ redundant_spans . sort_unstable ( ) ;
859+ for span in redundant_spans {
764860 report_unreachable_pattern ( span, arm. hir_id , None ) ;
765861 }
766862 }
0 commit comments