@@ -1091,6 +1091,32 @@ pub(super) enum ConstructorSet {
1091
1091
Uninhabited ,
1092
1092
}
1093
1093
1094
+ /// Describes the result of analyzing the constructors in a column of a match.
1095
+ ///
1096
+ /// `present` is morally the set of constructors present in the column, and `missing` is the set of
1097
+ /// constructors that exist in the type but are not present in the column.
1098
+ ///
1099
+ /// More formally, they respect the following constraints:
1100
+ /// - the union of `present` and `missing` covers the whole type
1101
+ /// - `present` and `missing` are disjoint
1102
+ /// - neither contains wildcards
1103
+ /// - each constructor in `present` is covered by some non-wildcard constructor in the column
1104
+ /// - together, the constructors in `present` cover all the non-wildcard constructor in the column
1105
+ /// - non-wildcards in the column do no cover anything in `missing`
1106
+ /// - constructors in `present` and `missing` are split for the column; in other words, they are
1107
+ /// either fully included in or disjoint from each constructor in the column. This rules out
1108
+ /// non-trivial intersections like between `0..10` and `5..15`.
1109
+ ///
1110
+ /// FIXME(Nadrieril): examples?
1111
+ struct SplitConstructorSet < ' tcx > {
1112
+ present : SmallVec < [ Constructor < ' tcx > ; 1 ] > ,
1113
+ missing : Vec < Constructor < ' tcx > > ,
1114
+ /// Whether there were any non-wildcard constructors in the column.
1115
+ seen_any_non_wildcards : bool ,
1116
+ /// For the `non_exhaustive_omitted_patterns` lint.
1117
+ nonexhaustive_enum_missing_real_variants : bool ,
1118
+ }
1119
+
1094
1120
impl ConstructorSet {
1095
1121
pub ( super ) fn new < ' p , ' tcx > ( pcx : & PatCtxt < ' _ , ' p , ' tcx > ) -> Self {
1096
1122
debug ! ( "ConstructorSet::new({:?})" , pcx. ty) ;
@@ -1203,36 +1229,38 @@ impl ConstructorSet {
1203
1229
}
1204
1230
}
1205
1231
1206
- pub ( super ) fn compute_split_wildcard < ' a , ' tcx > (
1232
+ /// This is the core logical operation of exhaustiveness checking.
1233
+ /// This analyzes a column a constructors to 1/ determine which constructors of the type (if
1234
+ /// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges
1235
+ /// or slices.
1236
+ fn split < ' a , ' tcx > (
1207
1237
& self ,
1208
1238
pcx : & PatCtxt < ' _ , ' _ , ' tcx > ,
1209
1239
ctors : impl Iterator < Item = & ' a Constructor < ' tcx > > + Clone ,
1210
1240
// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
1211
1241
// subpattern.
1212
1242
is_top_level : bool ,
1213
- ) -> SmallVec < [ Constructor < ' tcx > ; 1 ] >
1243
+ ) -> SplitConstructorSet < ' tcx >
1214
1244
where
1215
1245
' tcx : ' a ,
1216
1246
{
1217
1247
let mut missing = Vec :: new ( ) ;
1218
- let mut any_missing = false ;
1219
1248
let mut present: SmallVec < [ _ ; 1 ] > = SmallVec :: new ( ) ;
1220
- // Constructors in `ctors`, except wildcards and opaques .
1249
+ // Constructors in `ctors`, except wildcards.
1221
1250
let mut seen = Vec :: new ( ) ;
1222
1251
for ctor in ctors. cloned ( ) {
1223
1252
match ctor {
1224
1253
// Wildcards in `ctors` are irrelevant to splitting
1225
- Wildcard => { }
1226
- Constructor :: Opaque => { }
1254
+ Opaque | Wildcard => { }
1227
1255
_ => {
1228
1256
seen. push ( ctor) ;
1229
1257
}
1230
1258
}
1231
1259
}
1260
+ let mut nonexhaustive_enum_missing_real_variants = false ;
1232
1261
match self {
1233
1262
ConstructorSet :: Single => {
1234
1263
if seen. is_empty ( ) {
1235
- any_missing = true ;
1236
1264
missing. push ( Single ) ;
1237
1265
} else {
1238
1266
present. push ( Single ) ;
@@ -1251,15 +1279,16 @@ impl ConstructorSet {
1251
1279
skipped_a_hidden_variant = true ;
1252
1280
} else {
1253
1281
missing. push ( ctor) ;
1254
- any_missing = true ;
1255
1282
}
1256
1283
}
1284
+
1257
1285
if * non_exhaustive {
1286
+ nonexhaustive_enum_missing_real_variants = !missing. is_empty ( ) ;
1258
1287
missing. push ( NonExhaustive ) ;
1259
- any_missing = true ;
1260
1288
} else if skipped_a_hidden_variant {
1289
+ // FIXME(Nadrieril): This represents the skipped variants, but isn't super
1290
+ // clean. Using `NonExhaustive` breaks things elsewhere.
1261
1291
missing. push ( Wildcard ) ;
1262
- any_missing = true ;
1263
1292
}
1264
1293
}
1265
1294
ConstructorSet :: Integers { range_1, range_2, non_exhaustive } => {
@@ -1271,27 +1300,21 @@ impl ConstructorSet {
1271
1300
for ( seen, splitted_range) in range. split ( seen_ranges. cloned ( ) ) {
1272
1301
let ctor = IntRange ( splitted_range) ;
1273
1302
match seen {
1274
- Presence :: Unseen => {
1275
- missing. push ( ctor) ;
1276
- any_missing = true ;
1277
- }
1303
+ Presence :: Unseen => missing. push ( ctor) ,
1278
1304
Presence :: Seen => present. push ( ctor) ,
1279
1305
}
1280
1306
}
1307
+
1281
1308
if * non_exhaustive {
1282
1309
missing. push ( NonExhaustive ) ;
1283
- any_missing = true ;
1284
1310
}
1285
1311
}
1286
1312
& ConstructorSet :: Slice ( array_len) => {
1287
1313
let seen_slices = seen. iter ( ) . map ( |c| c. as_slice ( ) . unwrap ( ) ) . map ( |s| s. kind ) ;
1288
1314
for ( seen, splitted_slice) in SplitVarLenSlice2 :: split ( array_len, seen_slices) {
1289
1315
let ctor = Slice ( splitted_slice) ;
1290
1316
match seen {
1291
- Presence :: Unseen => {
1292
- missing. push ( ctor) ;
1293
- any_missing = true ;
1294
- }
1317
+ Presence :: Unseen => missing. push ( ctor) ,
1295
1318
Presence :: Seen => present. push ( ctor) ,
1296
1319
}
1297
1320
}
@@ -1301,17 +1324,15 @@ impl ConstructorSet {
1301
1324
let slice = Slice ( Slice :: new ( None , FixedLen ( 0 ) ) ) ;
1302
1325
if seen. is_empty ( ) {
1303
1326
missing. push ( slice) ;
1304
- any_missing = true ;
1305
1327
} else {
1306
1328
present. push ( slice) ;
1307
1329
}
1308
1330
}
1309
1331
ConstructorSet :: Unlistable => {
1310
- // Since we can't list constructors, we take the ones in the matrix . This might list
1332
+ // Since we can't list constructors, we take the ones in the column . This might list
1311
1333
// some constructors several times but there's not much we can do.
1312
1334
present. extend ( seen. iter ( ) . cloned ( ) ) ;
1313
1335
missing. push ( NonExhaustive ) ;
1314
- any_missing = true ;
1315
1336
}
1316
1337
// If `exhaustive_patterns` is disabled and our scrutinee is an empty type, we cannot
1317
1338
// expose its emptiness. The exception is if the pattern is at the top level, because we
@@ -1320,14 +1341,33 @@ impl ConstructorSet {
1320
1341
if !pcx. cx . tcx . features ( ) . exhaustive_patterns && !is_top_level =>
1321
1342
{
1322
1343
missing. push ( NonExhaustive ) ;
1323
- any_missing = true ;
1324
1344
}
1325
1345
ConstructorSet :: Uninhabited => { }
1326
1346
}
1327
1347
1328
- if !missing. is_empty ( ) {
1348
+ SplitConstructorSet {
1349
+ present,
1350
+ missing,
1351
+ seen_any_non_wildcards : !seen. is_empty ( ) ,
1352
+ nonexhaustive_enum_missing_real_variants,
1353
+ }
1354
+ }
1355
+
1356
+ pub ( super ) fn compute_split_wildcard < ' a , ' tcx > (
1357
+ & self ,
1358
+ pcx : & PatCtxt < ' _ , ' _ , ' tcx > ,
1359
+ ctors : impl Iterator < Item = & ' a Constructor < ' tcx > > + Clone ,
1360
+ // Whether the current pattern is the whole pattern as found in a match arm, or if it's a
1361
+ // subpattern.
1362
+ is_top_level : bool ,
1363
+ ) -> SmallVec < [ Constructor < ' tcx > ; 1 ] >
1364
+ where
1365
+ ' tcx : ' a ,
1366
+ {
1367
+ let split_set = self . split ( pcx, ctors, is_top_level) ;
1368
+ if !split_set. missing . is_empty ( ) {
1329
1369
// Some constructors are missing, thus we can specialize with the special `Missing`
1330
- // constructor, which stands for those constructors that are not seen in the matrix ,
1370
+ // constructor, which stands for those constructors that are not seen in the column ,
1331
1371
// and matches the same rows as any of them (namely the wildcard rows). See the top of
1332
1372
// the file for details.
1333
1373
// However, when all constructors are missing we can also specialize with the full
@@ -1354,25 +1394,22 @@ impl ConstructorSet {
1354
1394
// The exception is: if we are at the top-level, for example in an empty match, we
1355
1395
// sometimes prefer reporting the list of constructors instead of just `_`.
1356
1396
let report_when_all_missing = is_top_level && !matches ! ( self , Self :: Integers { .. } ) ;
1357
- let ctor = if !seen. is_empty ( ) || report_when_all_missing {
1358
- if pcx. is_non_exhaustive {
1359
- Missing {
1360
- nonexhaustive_enum_missing_real_variants : missing
1361
- . iter ( )
1362
- . any ( |c| !( c. is_non_exhaustive ( ) || c. is_unstable_variant ( pcx) ) ) ,
1363
- }
1364
- } else {
1365
- Missing { nonexhaustive_enum_missing_real_variants : false }
1397
+ let ctor = if split_set. seen_any_non_wildcards || report_when_all_missing {
1398
+ Missing {
1399
+ nonexhaustive_enum_missing_real_variants : split_set
1400
+ . nonexhaustive_enum_missing_real_variants ,
1366
1401
}
1367
1402
} else {
1368
1403
Wildcard
1369
1404
} ;
1370
1405
smallvec ! [ ctor]
1371
1406
} else {
1372
- present
1407
+ split_set . present
1373
1408
}
1374
1409
}
1375
1410
1411
+ /// Compute the set of constructors missing from this column.
1412
+ /// This is only used for reporting to the user.
1376
1413
pub ( super ) fn compute_missing < ' a , ' tcx > (
1377
1414
& self ,
1378
1415
pcx : & PatCtxt < ' _ , ' _ , ' tcx > ,
@@ -1384,94 +1421,7 @@ impl ConstructorSet {
1384
1421
where
1385
1422
' tcx : ' a ,
1386
1423
{
1387
- let mut missing = Vec :: new ( ) ;
1388
- // Constructors in `ctors`, except wildcards and opaques.
1389
- let mut seen = Vec :: new ( ) ;
1390
- for ctor in ctors. cloned ( ) {
1391
- match ctor {
1392
- // Wildcards in `ctors` are irrelevant to splitting
1393
- Wildcard => { }
1394
- Constructor :: Opaque => { }
1395
- _ => {
1396
- seen. push ( ctor) ;
1397
- }
1398
- }
1399
- }
1400
- match self {
1401
- ConstructorSet :: Single => {
1402
- if seen. is_empty ( ) {
1403
- missing. push ( Single ) ;
1404
- }
1405
- }
1406
- ConstructorSet :: Variants { variants, non_exhaustive } => {
1407
- let seen_set: FxHashSet < _ > = seen. iter ( ) . map ( |c| c. as_variant ( ) . unwrap ( ) ) . collect ( ) ;
1408
- let mut skipped_a_hidden_variant = false ;
1409
- for variant in variants {
1410
- let ctor = Variant ( * variant) ;
1411
- if seen_set. contains ( & variant) {
1412
- } else if ctor. is_doc_hidden_variant ( pcx) || ctor. is_unstable_variant ( pcx) {
1413
- // We don't want to mention any variants that are `doc(hidden)` or behind an
1414
- // unstable feature gate if they aren't present in the match.
1415
- skipped_a_hidden_variant = true ;
1416
- } else {
1417
- missing. push ( ctor) ;
1418
- }
1419
- }
1420
- if * non_exhaustive {
1421
- missing. push ( NonExhaustive ) ;
1422
- } else if skipped_a_hidden_variant {
1423
- missing. push ( Wildcard ) ;
1424
- }
1425
- }
1426
- ConstructorSet :: Integers { range_1, range_2, non_exhaustive } => {
1427
- if * non_exhaustive {
1428
- missing. push ( NonExhaustive ) ;
1429
- } else {
1430
- let range = match range_2 {
1431
- None => SplitIntRange2 :: Single ( range_1. clone ( ) ) ,
1432
- Some ( range_2) => SplitIntRange2 :: Double ( [ range_1. clone ( ) , range_2. clone ( ) ] ) ,
1433
- } ;
1434
- let seen_ranges = seen. iter ( ) . map ( |ctor| ctor. as_int_range ( ) . unwrap ( ) ) ;
1435
- for ( seen, splitted_range) in range. split ( seen_ranges. cloned ( ) ) {
1436
- let ctor = IntRange ( splitted_range) ;
1437
- match seen {
1438
- Presence :: Unseen => missing. push ( ctor) ,
1439
- Presence :: Seen => { }
1440
- }
1441
- }
1442
- }
1443
- }
1444
- & ConstructorSet :: Slice ( array_len) => {
1445
- let seen_slices = seen. iter ( ) . map ( |c| c. as_slice ( ) . unwrap ( ) ) . map ( |s| s. kind ) ;
1446
- for ( seen, splitted_slice) in SplitVarLenSlice2 :: split ( array_len, seen_slices) {
1447
- let ctor = Slice ( splitted_slice) ;
1448
- match seen {
1449
- Presence :: Unseen => missing. push ( ctor) ,
1450
- Presence :: Seen => { }
1451
- }
1452
- }
1453
- }
1454
- ConstructorSet :: SliceOfEmpty => {
1455
- if seen. is_empty ( ) {
1456
- // Behaves essentially like `Single`.
1457
- let slice = Slice ( Slice :: new ( None , FixedLen ( 0 ) ) ) ;
1458
- missing. push ( slice) ;
1459
- }
1460
- }
1461
- ConstructorSet :: Unlistable => {
1462
- missing. push ( NonExhaustive ) ;
1463
- }
1464
- // If `exhaustive_patterns` is disabled and our scrutinee is an empty type, we cannot
1465
- // expose its emptiness. The exception is if the pattern is at the top level, because we
1466
- // want empty matches to be considered exhaustive.
1467
- ConstructorSet :: Uninhabited
1468
- if !pcx. cx . tcx . features ( ) . exhaustive_patterns && !is_top_level =>
1469
- {
1470
- missing. push ( NonExhaustive ) ;
1471
- }
1472
- ConstructorSet :: Uninhabited => { }
1473
- }
1474
- missing
1424
+ self . split ( pcx, ctors, is_top_level) . missing
1475
1425
}
1476
1426
}
1477
1427
0 commit comments