@@ -636,32 +636,56 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
636636 }
637637}
638638
639- /// In the matrix, tracks whether a given place (aka column) is known to contain a valid value or
640- /// not.
639+ /// Serves two purposes:
640+ /// - in a wildcard, tracks whether the wildcard matches only valid values (i.e. is a binding `_a`)
641+ /// or also invalid values (i.e. is a true `_` pattern).
642+ /// - in the matrix, track whether a given place (aka column) is known to contain a valid value or
643+ /// not.
641644#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
642645pub ( super ) enum ValidityConstraint {
643646 ValidOnly ,
644647 MaybeInvalid ,
648+ /// Option for backwards compatibility: the place is not known to be valid but we allow omitting
649+ /// `useful && !reachable` arms anyway.
650+ MaybeInvalidButAllowOmittingArms ,
645651}
646652
647653impl ValidityConstraint {
648654 pub ( super ) fn from_bool ( is_valid_only : bool ) -> Self {
649655 if is_valid_only { ValidOnly } else { MaybeInvalid }
650656 }
651657
658+ fn allow_omitting_side_effecting_arms ( self ) -> Self {
659+ match self {
660+ MaybeInvalid | MaybeInvalidButAllowOmittingArms => MaybeInvalidButAllowOmittingArms ,
661+ // There are no side-effecting empty arms here, nothing to do.
662+ ValidOnly => ValidOnly ,
663+ }
664+ }
665+
666+ pub ( super ) fn is_known_valid ( self ) -> bool {
667+ matches ! ( self , ValidOnly )
668+ }
669+ pub ( super ) fn allows_omitting_empty_arms ( self ) -> bool {
670+ matches ! ( self , ValidOnly | MaybeInvalidButAllowOmittingArms )
671+ }
672+
652673 /// If the place has validity given by `self` and we read that the value at the place has
653674 /// constructor `ctor`, this computes what we can assume about the validity of the constructor
654675 /// fields.
655676 ///
656677 /// Pending further opsem decisions, the current behavior is: validity is preserved, except
657- /// under `&` where validity is reset to `MaybeInvalid`.
678+ /// inside `&` and union fields where validity is reset to `MaybeInvalid`.
658679 pub ( super ) fn specialize < ' tcx > (
659680 self ,
660681 pcx : & PatCtxt < ' _ , ' _ , ' tcx > ,
661682 ctor : & Constructor < ' tcx > ,
662683 ) -> Self {
663- // We preserve validity except when we go under a reference.
664- if matches ! ( ctor, Constructor :: Single ) && matches ! ( pcx. ty. kind( ) , ty:: Ref ( ..) ) {
684+ // We preserve validity except when we go inside a reference or a union field.
685+ if matches ! ( ctor, Constructor :: Single )
686+ && ( matches ! ( pcx. ty. kind( ) , ty:: Ref ( ..) )
687+ || matches ! ( pcx. ty. kind( ) , ty:: Adt ( def, ..) if def. is_union( ) ) )
688+ {
665689 // Validity of `x: &T` does not imply validity of `*x: T`.
666690 MaybeInvalid
667691 } else {
@@ -674,7 +698,7 @@ impl fmt::Display for ValidityConstraint {
674698 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
675699 let s = match self {
676700 ValidOnly => "✓" ,
677- MaybeInvalid => "?" ,
701+ MaybeInvalid | MaybeInvalidButAllowOmittingArms => "?" ,
678702 } ;
679703 write ! ( f, "{s}" )
680704 }
@@ -1197,9 +1221,9 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
11971221 for row in matrix. rows_mut ( ) {
11981222 // All rows are useful until they're not.
11991223 row. useful = true ;
1224+ // When there's an unguarded row, the match is exhaustive and any subsequent row is not
1225+ // useful.
12001226 if !row. is_under_guard {
1201- // There's an unguarded row, so the match is exhaustive, and any subsequent row is
1202- // unreachable.
12031227 return WitnessMatrix :: empty ( ) ;
12041228 }
12051229 }
@@ -1210,26 +1234,37 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
12101234 debug ! ( "ty: {ty:?}" ) ;
12111235 let pcx = & PatCtxt { cx, ty, is_top_level } ;
12121236
1237+ // Whether the place/column we are inspecting is known to contain valid data.
1238+ let place_validity = matrix. place_validity [ 0 ] ;
1239+ // For backwards compability we allow omitting some empty arms that we ideally shouldn't.
1240+ let place_validity = place_validity. allow_omitting_side_effecting_arms ( ) ;
1241+
12131242 // Analyze the constructors present in this column.
12141243 let ctors = matrix. heads ( ) . map ( |p| p. ctor ( ) ) ;
1215- let split_set = ConstructorSet :: for_ty ( pcx. cx , pcx. ty ) . split ( pcx, ctors) ;
1216-
1244+ let split_set = ConstructorSet :: for_ty ( cx, ty) . split ( pcx, ctors) ;
12171245 let all_missing = split_set. present . is_empty ( ) ;
1218- let always_report_all = is_top_level && !IntRange :: is_integral ( pcx. ty ) ;
1219- // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing".
1220- let report_individual_missing_ctors = always_report_all || !all_missing;
12211246
1247+ // Build the set of constructors we will specialize with. It must cover the whole type.
12221248 let mut split_ctors = split_set. present ;
1223- let mut only_report_missing = false ;
12241249 if !split_set. missing . is_empty ( ) {
12251250 // We need to iterate over a full set of constructors, so we add `Missing` to represent the
12261251 // missing ones. This is explained under "Constructor Splitting" at the top of this file.
12271252 split_ctors. push ( Constructor :: Missing ) ;
1228- // For diagnostic purposes we choose to only report the constructors that are missing. Since
1229- // `Missing` matches only the wildcard rows, it matches fewer rows than any normal
1230- // constructor and is therefore guaranteed to result in more witnesses. So skipping the
1231- // other constructors does not jeopardize correctness.
1232- only_report_missing = true ;
1253+ } else if !split_set. missing_empty . is_empty ( ) && !place_validity. is_known_valid ( ) {
1254+ // The missing empty constructors are reachable if the place can contain invalid data.
1255+ split_ctors. push ( Constructor :: Missing ) ;
1256+ }
1257+
1258+ // Decide what constructors to report.
1259+ let always_report_all = is_top_level && !IntRange :: is_integral ( pcx. ty ) ;
1260+ // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing".
1261+ let report_individual_missing_ctors = always_report_all || !all_missing;
1262+ // Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() =>
1263+ // split_ctors.contains(Missing)`. The converse usually holds except in the
1264+ // `MaybeInvalidButAllowOmittingArms` backwards-compatibility case.
1265+ let mut missing_ctors = split_set. missing ;
1266+ if !place_validity. allows_omitting_empty_arms ( ) {
1267+ missing_ctors. extend ( split_set. missing_empty ) ;
12331268 }
12341269
12351270 let mut ret = WitnessMatrix :: empty ( ) ;
@@ -1241,11 +1276,19 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
12411276 compute_exhaustiveness_and_usefulness ( cx, & mut spec_matrix, false )
12421277 } ) ;
12431278
1244- if !only_report_missing || matches ! ( ctor, Constructor :: Missing ) {
1279+ let counts_for_exhaustiveness = match ctor {
1280+ Constructor :: Missing => !missing_ctors. is_empty ( ) ,
1281+ // If there are missing constructors we'll report those instead. Since `Missing` matches
1282+ // only the wildcard rows, it matches fewer rows than this constructor, and is therefore
1283+ // guaranteed to result in the same or more witnesses. So skipping this does not
1284+ // jeopardize correctness.
1285+ _ => missing_ctors. is_empty ( ) ,
1286+ } ;
1287+ if counts_for_exhaustiveness {
12451288 // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
12461289 witnesses. apply_constructor (
12471290 pcx,
1248- & split_set . missing ,
1291+ & missing_ctors ,
12491292 & ctor,
12501293 report_individual_missing_ctors,
12511294 ) ;
0 commit comments