@@ -636,32 +636,56 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
636
636
}
637
637
}
638
638
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.
641
644
#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
642
645
pub ( super ) enum ValidityConstraint {
643
646
ValidOnly ,
644
647
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 ,
645
651
}
646
652
647
653
impl ValidityConstraint {
648
654
pub ( super ) fn from_bool ( is_valid_only : bool ) -> Self {
649
655
if is_valid_only { ValidOnly } else { MaybeInvalid }
650
656
}
651
657
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
+
652
673
/// If the place has validity given by `self` and we read that the value at the place has
653
674
/// constructor `ctor`, this computes what we can assume about the validity of the constructor
654
675
/// fields.
655
676
///
656
677
/// 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`.
658
679
pub ( super ) fn specialize < ' tcx > (
659
680
self ,
660
681
pcx : & PatCtxt < ' _ , ' _ , ' tcx > ,
661
682
ctor : & Constructor < ' tcx > ,
662
683
) -> 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
+ {
665
689
// Validity of `x: &T` does not imply validity of `*x: T`.
666
690
MaybeInvalid
667
691
} else {
@@ -674,7 +698,7 @@ impl fmt::Display for ValidityConstraint {
674
698
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
675
699
let s = match self {
676
700
ValidOnly => "✓" ,
677
- MaybeInvalid => "?" ,
701
+ MaybeInvalid | MaybeInvalidButAllowOmittingArms => "?" ,
678
702
} ;
679
703
write ! ( f, "{s}" )
680
704
}
@@ -1197,9 +1221,9 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
1197
1221
for row in matrix. rows_mut ( ) {
1198
1222
// All rows are useful until they're not.
1199
1223
row. useful = true ;
1224
+ // When there's an unguarded row, the match is exhaustive and any subsequent row is not
1225
+ // useful.
1200
1226
if !row. is_under_guard {
1201
- // There's an unguarded row, so the match is exhaustive, and any subsequent row is
1202
- // unreachable.
1203
1227
return WitnessMatrix :: empty ( ) ;
1204
1228
}
1205
1229
}
@@ -1210,26 +1234,37 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
1210
1234
debug ! ( "ty: {ty:?}" ) ;
1211
1235
let pcx = & PatCtxt { cx, ty, is_top_level } ;
1212
1236
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
+
1213
1242
// Analyze the constructors present in this column.
1214
1243
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) ;
1217
1245
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;
1221
1246
1247
+ // Build the set of constructors we will specialize with. It must cover the whole type.
1222
1248
let mut split_ctors = split_set. present ;
1223
- let mut only_report_missing = false ;
1224
1249
if !split_set. missing . is_empty ( ) {
1225
1250
// We need to iterate over a full set of constructors, so we add `Missing` to represent the
1226
1251
// missing ones. This is explained under "Constructor Splitting" at the top of this file.
1227
1252
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 ) ;
1233
1268
}
1234
1269
1235
1270
let mut ret = WitnessMatrix :: empty ( ) ;
@@ -1241,11 +1276,19 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
1241
1276
compute_exhaustiveness_and_usefulness ( cx, & mut spec_matrix, false )
1242
1277
} ) ;
1243
1278
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 {
1245
1288
// Transform witnesses for `spec_matrix` into witnesses for `matrix`.
1246
1289
witnesses. apply_constructor (
1247
1290
pcx,
1248
- & split_set . missing ,
1291
+ & missing_ctors ,
1249
1292
& ctor,
1250
1293
report_individual_missing_ctors,
1251
1294
) ;
0 commit comments