Merge pull request #17916 from aschwaighofer/fix_getSpecializedConformance_4.2

[4.2] ASTContext: Recompute the insert position in getSpecializedConformance after the SpecializedProtocolConformance constructor
diff --git a/lib/Sema/TypeCheckSwitchStmt.cpp b/lib/Sema/TypeCheckSwitchStmt.cpp
index 2cd9203..38f40a0 100644
--- a/lib/Sema/TypeCheckSwitchStmt.cpp
+++ b/lib/Sema/TypeCheckSwitchStmt.cpp
@@ -1241,7 +1241,105 @@
         return false;
       }
     }
-    
+
+    /// Estimate how big is the search space that exhaustivity
+    /// checker needs to cover, based on the total space and information
+    /// from the `switch` statement itself. Some of the easy situations
+    /// like `case .foo(let bar)` don't really contribute to the complexity
+    /// of the search so their sub-space sizes could be excluded from
+    /// consideration.
+    ///
+    /// \param total The total space to check.
+    /// \param covered The space covered by the `case` statements in the switch.
+    ///
+    /// \returns The size of the search space exhastivity checker has to check.
+    size_t estimateSearchSpaceSize(const Space &total, const Space &covered) {
+      switch (PairSwitch(total.getKind(), covered.getKind())) {
+      PAIRCASE(SpaceKind::Type, SpaceKind::Type): {
+        return total.getType()->isEqual(covered.getType())
+                    ? 0
+                    : total.getSize(TC, DC);
+      }
+      PAIRCASE(SpaceKind::Type, SpaceKind::Disjunct):
+      PAIRCASE(SpaceKind::Type, SpaceKind::Constructor): {
+        if (!Space::canDecompose(total.getType(), DC))
+          break;
+
+        SmallVector<Space, 4> spaces;
+        Space::decompose(TC, DC, total.getType(), spaces);
+        return estimateSearchSpaceSize(Space::forDisjunct(spaces), covered);
+      }
+
+      PAIRCASE(SpaceKind::Disjunct, SpaceKind::Disjunct):
+      PAIRCASE(SpaceKind::Disjunct, SpaceKind::Constructor): {
+        auto &spaces = total.getSpaces();
+        return std::accumulate(spaces.begin(), spaces.end(), 0,
+                               [&](size_t totalSize, const Space &space) {
+                                 return totalSize + estimateSearchSpaceSize(
+                                                        space, covered);
+                               });
+      }
+
+      // Search space size computation is not commutative, because it
+      // tries to check if space on right-hand side is covering any
+      // portion of the "total" space on the left.
+      PAIRCASE(SpaceKind::Constructor, SpaceKind::Disjunct): {
+        for (const auto &space : covered.getSpaces()) {
+          // enum E { case foo }
+          // func bar(_ lhs: E, _ rhs: E) {
+          //   switch (lhs, rhs) {
+          //     case (_, _): break
+          // }
+          if (total == space)
+            return 0;
+
+          if (!space.isSubspace(total, TC, DC))
+            continue;
+
+          if (estimateSearchSpaceSize(total, space) == 0)
+            return 0;
+        }
+        break;
+      }
+
+      PAIRCASE(SpaceKind::Constructor, SpaceKind::Constructor): {
+        if (total.getHead() != covered.getHead())
+          break;
+
+        auto &lhs = total.getSpaces();
+        auto &rhs = covered.getSpaces();
+
+        if (std::distance(lhs.begin(), lhs.end()) !=
+            std::distance(rhs.begin(), rhs.end()))
+          return total.getSize(TC, DC);
+
+        auto i = lhs.begin();
+        auto j = rhs.begin();
+
+        size_t totalSize = 0;
+        for (; i != lhs.end() && j != rhs.end(); ++i, ++j) {
+          // The only light-weight checking we can do
+          // is when sub-spaces on both sides are types
+          // otherwise we'd have to decompose, which
+          // is too heavy, so let's just return total
+          // space size if such situation is detected.
+          if (i->getKind() != SpaceKind::Type ||
+              j->getKind() != SpaceKind::Type)
+            return total.getSize(TC, DC);
+
+          totalSize += estimateSearchSpaceSize(*i, *j);
+        }
+
+        return totalSize;
+      }
+
+      default:
+        break;
+      }
+
+      return total.getSize(TC, DC);
+    }
+
     void checkExhaustiveness(bool limitedChecking) {
       // If the type of the scrutinee is uninhabited, we're already dead.
       // Allow any well-typed patterns through.
@@ -1308,16 +1406,17 @@
       Space totalSpace = Space::forType(subjectType, Identifier());
       Space coveredSpace = Space::forDisjunct(spaces);
 
-      const size_t totalSpaceSize = totalSpace.getSize(TC, DC);
-      if (totalSpaceSize > Space::getMaximumSize()) {
-        diagnoseCannotCheck(sawRedundantPattern, totalSpaceSize, coveredSpace,
+      const size_t searchSpaceSizeEstimate =
+          estimateSearchSpaceSize(totalSpace, coveredSpace);
+      if (searchSpaceSizeEstimate > Space::getMaximumSize()) {
+        diagnoseCannotCheck(sawRedundantPattern, totalSpace, coveredSpace,
                             unknownCase);
         return;
       }
       unsigned minusCount = 0;
       auto diff = totalSpace.minus(coveredSpace, TC, DC, &minusCount);
       if (!diff) {
-        diagnoseCannotCheck(sawRedundantPattern, totalSpaceSize, coveredSpace,
+        diagnoseCannotCheck(sawRedundantPattern, totalSpace, coveredSpace,
                             unknownCase);
         return;
       }
@@ -1369,7 +1468,7 @@
     };
     
     void diagnoseCannotCheck(const bool sawRedundantPattern,
-                             const size_t totalSpaceSize,
+                             const Space &totalSpace,
                              const Space &coveredSpace,
                              const CaseStmt *unknownCase) {
       // Because the space is large or the check is too slow,
@@ -1381,7 +1480,7 @@
       // a 'default' to be inserted.
       // FIXME: Do something sensible for non-frozen enums.
       if (!sawRedundantPattern &&
-          coveredSpace.getSize(TC, DC) >= totalSpaceSize)
+          coveredSpace.getSize(TC, DC) >= totalSpace.getSize(TC, DC))
         return;
       diagnoseMissingCases(RequiresDefault::SpaceTooLarge, Space(),
                            unknownCase);
diff --git a/test/stmt/rdar40400251.swift b/test/stmt/rdar40400251.swift
new file mode 100644
index 0000000..d71fe39
--- /dev/null
+++ b/test/stmt/rdar40400251.swift
@@ -0,0 +1,37 @@
+// RUN: %target-typecheck-verify-swift
+
+enum E1 {
+  case v1
+  case v2
+  case v3
+  case v4
+  case v5
+  case v6
+  indirect case v7(E1)
+}
+
+enum E2 {
+  case foo((E1, E1, E1)) // total size of this case is 7 ^ 3 + 1
+  case bar(E1)
+  case baz
+}
+
+func foo(_ e: E2) {
+  switch e {
+    // expected-error@-1 {{switch must be exhaustive}}
+    // expected-note@-2 {{add missing case: '.bar(_)'}}
+
+    case .foo(let tuple): break // expected-warning {{immutable value 'tuple' was never used; consider replacing with '_' or removing it}}
+    case .baz: break
+  }
+}
+
+func bar(_ e: E2) {
+  switch e {
+    // expected-error@-1 {{switch must be exhaustive}}
+    // expected-note@-2 {{add missing case: '.bar(_)'}}
+    // expected-note@-3 {{add missing case: '.baz'}}
+
+    case .foo((_, _, _)): break
+  }
+}