@@ -1241,7 +1241,105 @@ namespace {
1241
1241
return false ;
1242
1242
}
1243
1243
}
1244
-
1244
+
1245
+ // / Estimate how big is the search space that exhaustivity
1246
+ // / checker needs to cover, based on the total space and information
1247
+ // / from the `switch` statement itself. Some of the easy situations
1248
+ // / like `case .foo(let bar)` don't really contribute to the complexity
1249
+ // / of the search so their sub-space sizes could be excluded from
1250
+ // / consideration.
1251
+ // /
1252
+ // / \param total The total space to check.
1253
+ // / \param covered The space covered by the `case` statements in the switch.
1254
+ // /
1255
+ // / \returns The size of the search space exhastivity checker has to check.
1256
+ size_t estimateSearchSpaceSize (const Space &total, const Space &covered) {
1257
+ switch (PairSwitch (total.getKind (), covered.getKind ())) {
1258
+ PAIRCASE (SpaceKind::Type, SpaceKind::Type): {
1259
+ return total.getType ()->isEqual (covered.getType ())
1260
+ ? 0
1261
+ : total.getSize (TC, DC);
1262
+ }
1263
+ PAIRCASE (SpaceKind::Type, SpaceKind::Disjunct):
1264
+ PAIRCASE (SpaceKind::Type, SpaceKind::Constructor): {
1265
+ if (!Space::canDecompose (total.getType (), DC))
1266
+ break ;
1267
+
1268
+ SmallVector<Space, 4 > spaces;
1269
+ Space::decompose (TC, DC, total.getType (), spaces);
1270
+ return estimateSearchSpaceSize (Space::forDisjunct (spaces), covered);
1271
+ }
1272
+
1273
+ PAIRCASE (SpaceKind::Disjunct, SpaceKind::Disjunct):
1274
+ PAIRCASE (SpaceKind::Disjunct, SpaceKind::Constructor): {
1275
+ auto &spaces = total.getSpaces ();
1276
+ return std::accumulate (spaces.begin (), spaces.end (), 0 ,
1277
+ [&](size_t totalSize, const Space &space) {
1278
+ return totalSize + estimateSearchSpaceSize (
1279
+ space, covered);
1280
+ });
1281
+ }
1282
+
1283
+ // Search space size computation is not commutative, because it
1284
+ // tries to check if space on right-hand side is covering any
1285
+ // portion of the "total" space on the left.
1286
+ PAIRCASE (SpaceKind::Constructor, SpaceKind::Disjunct): {
1287
+ for (const auto &space : covered.getSpaces ()) {
1288
+ // enum E { case foo }
1289
+ // func bar(_ lhs: E, _ rhs: E) {
1290
+ // switch (lhs, rhs) {
1291
+ // case (_, _): break
1292
+ // }
1293
+ if (total == space)
1294
+ return 0 ;
1295
+
1296
+ if (!space.isSubspace (total, TC, DC))
1297
+ continue ;
1298
+
1299
+ if (estimateSearchSpaceSize (total, space) == 0 )
1300
+ return 0 ;
1301
+ }
1302
+ break ;
1303
+ }
1304
+
1305
+ PAIRCASE (SpaceKind::Constructor, SpaceKind::Constructor): {
1306
+ if (total.getHead () != covered.getHead ())
1307
+ break ;
1308
+
1309
+ auto &lhs = total.getSpaces ();
1310
+ auto &rhs = covered.getSpaces ();
1311
+
1312
+ if (std::distance (lhs.begin (), lhs.end ()) !=
1313
+ std::distance (rhs.begin (), rhs.end ()))
1314
+ return total.getSize (TC, DC);
1315
+
1316
+ auto i = lhs.begin ();
1317
+ auto j = rhs.begin ();
1318
+
1319
+ size_t totalSize = 0 ;
1320
+ for (; i != lhs.end () && j != rhs.end (); ++i, ++j) {
1321
+ // The only light-weight checking we can do
1322
+ // is when sub-spaces on both sides are types
1323
+ // otherwise we'd have to decompose, which
1324
+ // is too heavy, so let's just return total
1325
+ // space size if such situation is detected.
1326
+ if (i->getKind () != SpaceKind::Type ||
1327
+ j->getKind () != SpaceKind::Type)
1328
+ return total.getSize (TC, DC);
1329
+
1330
+ totalSize += estimateSearchSpaceSize (*i, *j);
1331
+ }
1332
+
1333
+ return totalSize;
1334
+ }
1335
+
1336
+ default :
1337
+ break ;
1338
+ }
1339
+
1340
+ return total.getSize (TC, DC);
1341
+ }
1342
+
1245
1343
void checkExhaustiveness (bool limitedChecking) {
1246
1344
// If the type of the scrutinee is uninhabited, we're already dead.
1247
1345
// Allow any well-typed patterns through.
@@ -1308,16 +1406,17 @@ namespace {
1308
1406
Space totalSpace = Space::forType (subjectType, Identifier ());
1309
1407
Space coveredSpace = Space::forDisjunct (spaces);
1310
1408
1311
- const size_t totalSpaceSize = totalSpace.getSize (TC, DC);
1312
- if (totalSpaceSize > Space::getMaximumSize ()) {
1313
- diagnoseCannotCheck (sawRedundantPattern, totalSpaceSize, coveredSpace,
1409
+ const size_t searchSpaceSizeEstimate =
1410
+ estimateSearchSpaceSize (totalSpace, coveredSpace);
1411
+ if (searchSpaceSizeEstimate > Space::getMaximumSize ()) {
1412
+ diagnoseCannotCheck (sawRedundantPattern, totalSpace, coveredSpace,
1314
1413
unknownCase);
1315
1414
return ;
1316
1415
}
1317
1416
unsigned minusCount = 0 ;
1318
1417
auto diff = totalSpace.minus (coveredSpace, TC, DC, &minusCount);
1319
1418
if (!diff) {
1320
- diagnoseCannotCheck (sawRedundantPattern, totalSpaceSize , coveredSpace,
1419
+ diagnoseCannotCheck (sawRedundantPattern, totalSpace , coveredSpace,
1321
1420
unknownCase);
1322
1421
return ;
1323
1422
}
@@ -1369,7 +1468,7 @@ namespace {
1369
1468
};
1370
1469
1371
1470
void diagnoseCannotCheck (const bool sawRedundantPattern,
1372
- const size_t totalSpaceSize ,
1471
+ const Space &totalSpace ,
1373
1472
const Space &coveredSpace,
1374
1473
const CaseStmt *unknownCase) {
1375
1474
// Because the space is large or the check is too slow,
@@ -1381,7 +1480,7 @@ namespace {
1381
1480
// a 'default' to be inserted.
1382
1481
// FIXME: Do something sensible for non-frozen enums.
1383
1482
if (!sawRedundantPattern &&
1384
- coveredSpace.getSize (TC, DC) >= totalSpaceSize )
1483
+ coveredSpace.getSize (TC, DC) >= totalSpace. getSize (TC, DC) )
1385
1484
return ;
1386
1485
diagnoseMissingCases (RequiresDefault::SpaceTooLarge, Space (),
1387
1486
unknownCase);
0 commit comments