@@ -637,32 +637,56 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
637637 }
638638}
639639
640- /// In the matrix, tracks whether a given place (aka column) is known to contain a valid value or
641- /// not.
640+ /// Serves two purposes:
641+ /// - in a wildcard, tracks whether the wildcard matches only valid values (i.e. is a binding `_a`)
642+ /// or also invalid values (i.e. is a true `_` pattern).
643+ /// - in the matrix, track whether a given place (aka column) is known to contain a valid value or
644+ /// not.
642645#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
643646pub ( super ) enum ValidityConstraint {
644647 ValidOnly ,
645648 MaybeInvalid ,
649+ /// Option for backwards compatibility: the place is not known to be valid but we allow omitting
650+ /// `useful && !reachable` arms anyway.
651+ MaybeInvalidButAllowOmittingArms ,
646652}
647653
648654impl ValidityConstraint {
649655 pub ( super ) fn from_bool ( is_valid_only : bool ) -> Self {
650656 if is_valid_only { ValidOnly } else { MaybeInvalid }
651657 }
652658
659+ fn allow_omitting_side_effecting_arms ( self ) -> Self {
660+ match self {
661+ MaybeInvalid | MaybeInvalidButAllowOmittingArms => MaybeInvalidButAllowOmittingArms ,
662+ // There are no side-effecting empty arms here, nothing to do.
663+ ValidOnly => ValidOnly ,
664+ }
665+ }
666+
667+ pub ( super ) fn is_known_valid ( self ) -> bool {
668+ matches ! ( self , ValidOnly )
669+ }
670+ pub ( super ) fn allows_omitting_empty_arms ( self ) -> bool {
671+ matches ! ( self , ValidOnly | MaybeInvalidButAllowOmittingArms )
672+ }
673+
653674 /// If the place has validity given by `self` and we read that the value at the place has
654675 /// constructor `ctor`, this computes what we can assume about the validity of the constructor
655676 /// fields.
656677 ///
657678 /// Pending further opsem decisions, the current behavior is: validity is preserved, except
658- /// under `&` where validity is reset to `MaybeInvalid`.
679+ /// inside `&` and union fields where validity is reset to `MaybeInvalid`.
659680 pub ( super ) fn specialize < ' tcx > (
660681 self ,
661682 pcx : & PatCtxt < ' _ , ' _ , ' tcx > ,
662683 ctor : & Constructor < ' tcx > ,
663684 ) -> Self {
664- // We preserve validity except when we go under a reference.
665- if matches ! ( ctor, Constructor :: Single ) && matches ! ( pcx. ty. kind( ) , ty:: Ref ( ..) ) {
685+ // We preserve validity except when we go inside a reference or a union field.
686+ if matches ! ( ctor, Constructor :: Single )
687+ && ( matches ! ( pcx. ty. kind( ) , ty:: Ref ( ..) )
688+ || matches ! ( pcx. ty. kind( ) , ty:: Adt ( def, ..) if def. is_union( ) ) )
689+ {
666690 // Validity of `x: &T` does not imply validity of `*x: T`.
667691 MaybeInvalid
668692 } else {
@@ -675,7 +699,7 @@ impl fmt::Display for ValidityConstraint {
675699 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
676700 let s = match self {
677701 ValidOnly => "✓" ,
678- MaybeInvalid => "?" ,
702+ MaybeInvalid | MaybeInvalidButAllowOmittingArms => "?" ,
679703 } ;
680704 write ! ( f, "{s}" )
681705 }
@@ -1202,9 +1226,9 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
12021226 for row in matrix. rows_mut ( ) {
12031227 // All rows are useful until they're not.
12041228 row. useful = true ;
1229+ // When there's an unguarded row, the match is exhaustive and any subsequent row is not
1230+ // useful.
12051231 if !row. is_under_guard {
1206- // There's an unguarded row, so the match is exhaustive, and any subsequent row is
1207- // unreachable.
12081232 return WitnessMatrix :: empty ( ) ;
12091233 }
12101234 }
@@ -1215,26 +1239,37 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
12151239 debug ! ( "ty: {ty:?}" ) ;
12161240 let pcx = & PatCtxt { cx, ty, is_top_level } ;
12171241
1242+ // Whether the place/column we are inspecting is known to contain valid data.
1243+ let place_validity = matrix. place_validity [ 0 ] ;
1244+ // For backwards compability we allow omitting some empty arms that we ideally shouldn't.
1245+ let place_validity = place_validity. allow_omitting_side_effecting_arms ( ) ;
1246+
12181247 // Analyze the constructors present in this column.
12191248 let ctors = matrix. heads ( ) . map ( |p| p. ctor ( ) ) ;
1220- let split_set = ConstructorSet :: for_ty ( pcx. cx , pcx. ty ) . split ( pcx, ctors) ;
1221-
1249+ let split_set = ConstructorSet :: for_ty ( cx, ty) . split ( pcx, ctors) ;
12221250 let all_missing = split_set. present . is_empty ( ) ;
1223- let always_report_all = is_top_level && !IntRange :: is_integral ( pcx. ty ) ;
1224- // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing".
1225- let report_individual_missing_ctors = always_report_all || !all_missing;
12261251
1252+ // Build the set of constructors we will specialize with. It must cover the whole type.
12271253 let mut split_ctors = split_set. present ;
1228- let mut only_report_missing = false ;
12291254 if !split_set. missing . is_empty ( ) {
12301255 // We need to iterate over a full set of constructors, so we add `Missing` to represent the
12311256 // missing ones. This is explained under "Constructor Splitting" at the top of this file.
12321257 split_ctors. push ( Constructor :: Missing ) ;
1233- // For diagnostic purposes we choose to only report the constructors that are missing. Since
1234- // `Missing` matches only the wildcard rows, it matches fewer rows than any normal
1235- // constructor and is therefore guaranteed to result in more witnesses. So skipping the
1236- // other constructors does not jeopardize correctness.
1237- only_report_missing = true ;
1258+ } else if !split_set. missing_empty . is_empty ( ) && !place_validity. is_known_valid ( ) {
1259+ // The missing empty constructors are reachable if the place can contain invalid data.
1260+ split_ctors. push ( Constructor :: Missing ) ;
1261+ }
1262+
1263+ // Decide what constructors to report.
1264+ let always_report_all = is_top_level && !IntRange :: is_integral ( pcx. ty ) ;
1265+ // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing".
1266+ let report_individual_missing_ctors = always_report_all || !all_missing;
1267+ // Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() =>
1268+ // split_ctors.contains(Missing)`. The converse usually holds except in the
1269+ // `MaybeInvalidButAllowOmittingArms` backwards-compatibility case.
1270+ let mut missing_ctors = split_set. missing ;
1271+ if !place_validity. allows_omitting_empty_arms ( ) {
1272+ missing_ctors. extend ( split_set. missing_empty ) ;
12381273 }
12391274
12401275 let mut ret = WitnessMatrix :: empty ( ) ;
@@ -1246,11 +1281,19 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
12461281 compute_exhaustiveness_and_usefulness ( cx, & mut spec_matrix, false )
12471282 } ) ;
12481283
1249- if !only_report_missing || matches ! ( ctor, Constructor :: Missing ) {
1284+ let counts_for_exhaustiveness = match ctor {
1285+ Constructor :: Missing => !missing_ctors. is_empty ( ) ,
1286+ // If there are missing constructors we'll report those instead. Since `Missing` matches
1287+ // only the wildcard rows, it matches fewer rows than this constructor, and is therefore
1288+ // guaranteed to result in the same or more witnesses. So skipping this does not
1289+ // jeopardize correctness.
1290+ _ => missing_ctors. is_empty ( ) ,
1291+ } ;
1292+ if counts_for_exhaustiveness {
12501293 // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
12511294 witnesses. apply_constructor (
12521295 pcx,
1253- & split_set . missing ,
1296+ & missing_ctors ,
12541297 & ctor,
12551298 report_individual_missing_ctors,
12561299 ) ;
0 commit comments