@@ -1105,7 +1105,104 @@ namespace {
1105
1105
return false ;
1106
1106
}
1107
1107
}
1108
-
1108
+
1109
+ // / Estimate how big is the search space that exhaustivity
1110
+ // / checker needs to cover, based on the total space and information
1111
+ // / from the `switch` statement itself. Some of the easy situations
1112
+ // / like `case .foo(let bar)` don't really contribute to the complexity
1113
+ // / of the search so their sub-space sizes could be excluded from
1114
+ // / consideration.
1115
+ // /
1116
+ // / \param total The total space to check.
1117
+ // / \param covered The space covered by the `case` statements in the switch.
1118
+ // /
1119
+ // / \returns The size of the search space exhastivity checker has to check.
1120
+ size_t estimateSearchSpaceSize (const Space &total, const Space &covered) {
1121
+ switch (PairSwitch (total.getKind (), covered.getKind ())) {
1122
+ PAIRCASE (SpaceKind::Type, SpaceKind::Type): {
1123
+ return total.getType ()->isEqual (covered.getType ())
1124
+ ? 0
1125
+ : total.getSize (TC, DC);
1126
+ }
1127
+ PAIRCASE (SpaceKind::Type, SpaceKind::Disjunct):
1128
+ PAIRCASE (SpaceKind::Type, SpaceKind::Constructor): {
1129
+ if (!Space::canDecompose (total.getType (), DC))
1130
+ break ;
1131
+
1132
+ auto decomposition = Space::decompose (TC, DC, total.getType ());
1133
+ return estimateSearchSpaceSize (decomposition, covered);
1134
+ }
1135
+
1136
+ PAIRCASE (SpaceKind::Disjunct, SpaceKind::Disjunct):
1137
+ PAIRCASE (SpaceKind::Disjunct, SpaceKind::Constructor): {
1138
+ auto &spaces = total.getSpaces ();
1139
+ return std::accumulate (spaces.begin (), spaces.end (), 0 ,
1140
+ [&](size_t totalSize, const Space &space) {
1141
+ return totalSize + estimateSearchSpaceSize (
1142
+ space, covered);
1143
+ });
1144
+ }
1145
+
1146
+ // Search space size computation is not commutative, because it
1147
+ // tries to check if space on right-hand side is covering any
1148
+ // portion of the "total" space on the left.
1149
+ PAIRCASE (SpaceKind::Constructor, SpaceKind::Disjunct): {
1150
+ for (const auto &space : covered.getSpaces ()) {
1151
+ // enum E { case foo }
1152
+ // func bar(_ lhs: E, _ rhs: E) {
1153
+ // switch (lhs, rhs) {
1154
+ // case (_, _): break
1155
+ // }
1156
+ if (total == space)
1157
+ return 0 ;
1158
+
1159
+ if (!space.isSubspace (total, TC, DC))
1160
+ continue ;
1161
+
1162
+ if (estimateSearchSpaceSize (total, space) == 0 )
1163
+ return 0 ;
1164
+ }
1165
+ break ;
1166
+ }
1167
+
1168
+ PAIRCASE (SpaceKind::Constructor, SpaceKind::Constructor): {
1169
+ if (total.getHead () != covered.getHead ())
1170
+ break ;
1171
+
1172
+ auto &lhs = total.getSpaces ();
1173
+ auto &rhs = covered.getSpaces ();
1174
+
1175
+ if (std::distance (lhs.begin (), lhs.end ()) !=
1176
+ std::distance (rhs.begin (), rhs.end ()))
1177
+ return total.getSize (TC, DC);
1178
+
1179
+ auto i = lhs.begin ();
1180
+ auto j = rhs.begin ();
1181
+
1182
+ size_t totalSize = 0 ;
1183
+ for (; i != lhs.end () && j != rhs.end (); ++i, ++j) {
1184
+ // The only light-weight checking we can do
1185
+ // is when sub-spaces on both sides are types
1186
+ // otherwise we'd have to decompose, which
1187
+ // is too heavy, so let's just return total
1188
+ // space size if such situation is detected.
1189
+ if (i->getKind () != SpaceKind::Type ||
1190
+ j->getKind () != SpaceKind::Type)
1191
+ return total.getSize (TC, DC);
1192
+
1193
+ totalSize += estimateSearchSpaceSize (*i, *j);
1194
+ }
1195
+
1196
+ return totalSize;
1197
+ }
1198
+
1199
+ default :
1200
+ break ;
1201
+ }
1202
+
1203
+ return total.getSize (TC, DC);
1204
+ }
1205
+
1109
1206
void checkExhaustiveness (bool limitedChecking) {
1110
1207
// If the type of the scrutinee is uninhabited, we're already dead.
1111
1208
// Allow any well-typed patterns through.
@@ -1174,16 +1271,17 @@ namespace {
1174
1271
Space totalSpace = Space::forType (subjectType, Identifier ());
1175
1272
Space coveredSpace = Space::forDisjunct (spaces);
1176
1273
1177
- const size_t totalSpaceSize = totalSpace.getSize (TC, DC);
1178
- if (totalSpaceSize > Space::getMaximumSize ()) {
1179
- diagnoseCannotCheck (sawRedundantPattern, totalSpaceSize, coveredSpace,
1274
+ const size_t searchSpaceSizeEstimate =
1275
+ estimateSearchSpaceSize (totalSpace, coveredSpace);
1276
+ if (searchSpaceSizeEstimate > Space::getMaximumSize ()) {
1277
+ diagnoseCannotCheck (sawRedundantPattern, totalSpace, coveredSpace,
1180
1278
unknownCase);
1181
1279
return ;
1182
1280
}
1183
1281
unsigned minusCount = 0 ;
1184
1282
auto diff = totalSpace.minus (coveredSpace, TC, DC, &minusCount);
1185
1283
if (!diff) {
1186
- diagnoseCannotCheck (sawRedundantPattern, totalSpaceSize , coveredSpace,
1284
+ diagnoseCannotCheck (sawRedundantPattern, totalSpace , coveredSpace,
1187
1285
unknownCase);
1188
1286
return ;
1189
1287
}
@@ -1234,7 +1332,7 @@ namespace {
1234
1332
};
1235
1333
1236
1334
void diagnoseCannotCheck (const bool sawRedundantPattern,
1237
- const size_t totalSpaceSize ,
1335
+ const Space &totalSpace ,
1238
1336
const Space &coveredSpace,
1239
1337
const CaseStmt *unknownCase) {
1240
1338
// Because the space is large or the check is too slow,
@@ -1246,7 +1344,7 @@ namespace {
1246
1344
// a 'default' to be inserted.
1247
1345
// FIXME: Do something sensible for non-frozen enums.
1248
1346
if (!sawRedundantPattern &&
1249
- coveredSpace.getSize (TC, DC) >= totalSpaceSize )
1347
+ coveredSpace.getSize (TC, DC) >= totalSpace. getSize (TC, DC) )
1250
1348
return ;
1251
1349
diagnoseMissingCases (RequiresDefault::SpaceTooLarge, Space (),
1252
1350
unknownCase);
0 commit comments