Merge pull request #10011 from CodaFi/code-covfeferage

[4.0] Add a size heuristic to the Space Engine
diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def
index d96613b..22b0dbb 100644
--- a/include/swift/AST/Attr.def
+++ b/include/swift/AST/Attr.def
@@ -286,6 +286,11 @@
                  OnClass | NotSerialized | LongAttribute,
                  /*Not serialized */ 70)
 
+// HACK: Attribute needed to preserve source compatibility by downgrading errors
+// due to an SDK change in Dispatch
+SIMPLE_DECL_ATTR(_downgrade_exhaustivity_check, DowngradeExhaustivityCheck,
+                 OnEnumElement | LongAttribute | UserInaccessible, 71)
+
 #undef TYPE_ATTR
 #undef DECL_ATTR_ALIAS
 #undef SIMPLE_DECL_ATTR
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index dd0c6a1..37a0add 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -3701,6 +3701,10 @@
 WARNING(redundant_particular_case,none,
         "case is already handled by previous patterns; consider removing it",())
 
+// HACK: Downgrades the above to warnings if any of the cases is marked
+// @_downgrade_exhaustivity_check.
+WARNING(non_exhaustive_switch_warn_swift3,none, "switch must be exhaustive", ())
+
 #ifndef DIAG_NO_UNDEF
 # if defined(DIAG)
 #  undef DIAG
diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp
index 66a9dde..71e7d99 100644
--- a/lib/AST/ASTDumper.cpp
+++ b/lib/AST/ASTDumper.cpp
@@ -840,6 +840,8 @@
 
     void visitEnumElementDecl(EnumElementDecl *EED) {
       printCommon(EED, "enum_element_decl");
+      if (EED->getAttrs().hasAttribute<DowngradeExhaustivityCheckAttr>())
+        OS << "@_downgrade_exhaustivity_check";
       PrintWithColorRAII(OS, ParenthesisColor) << ')';
     }
 
diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp
index e782469..bb065bb 100644
--- a/lib/AST/Attr.cpp
+++ b/lib/AST/Attr.cpp
@@ -506,6 +506,10 @@
     Printer.printAttrName("@_staticInitializeObjCMetadata");
     break;
 
+  case DAK_DowngradeExhaustivityCheck:
+    Printer.printAttrName("@_downgrade_exhaustivity_check");
+    break;
+    
   case DAK_Count:
     llvm_unreachable("exceed declaration attribute kinds");
 
diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp
index 6c657b3..a43c95e 100644
--- a/lib/Sema/TypeCheckAttr.cpp
+++ b/lib/Sema/TypeCheckAttr.cpp
@@ -107,6 +107,7 @@
   IGNORED_ATTR(NSKeyedArchiverClassName)
   IGNORED_ATTR(StaticInitializeObjCMetadata)
   IGNORED_ATTR(NSKeyedArchiverEncodeNonGenericSubclassesOnly)
+  IGNORED_ATTR(DowngradeExhaustivityCheck)
 #undef IGNORED_ATTR
 
   // @noreturn has been replaced with a 'Never' return type.
@@ -784,6 +785,7 @@
     IGNORED_ATTR(ObjCMembers)
     IGNORED_ATTR(StaticInitializeObjCMetadata)
     IGNORED_ATTR(NSKeyedArchiverEncodeNonGenericSubclassesOnly)
+    IGNORED_ATTR(DowngradeExhaustivityCheck)
 #undef IGNORED_ATTR
 
   void visitAvailableAttr(AvailableAttr *attr);
diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp
index 57892c3..40f2da3 100644
--- a/lib/Sema/TypeCheckDecl.cpp
+++ b/lib/Sema/TypeCheckDecl.cpp
@@ -6103,6 +6103,7 @@
     UNINTERESTING_ATTR(NSKeyedArchiverClassName)
     UNINTERESTING_ATTR(StaticInitializeObjCMetadata)
     UNINTERESTING_ATTR(NSKeyedArchiverEncodeNonGenericSubclassesOnly)
+    UNINTERESTING_ATTR(DowngradeExhaustivityCheck)
 #undef UNINTERESTING_ATTR
 
     void visitAvailableAttr(AvailableAttr *attr) {
diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp
index 46cc777..f840484 100644
--- a/lib/Sema/TypeCheckProtocol.cpp
+++ b/lib/Sema/TypeCheckProtocol.cpp
@@ -5915,7 +5915,7 @@
 /// Infer the attribute tostatic-initialize the Objective-C metadata for the
 /// given class, if needed.
 static void inferStaticInitializeObjCMetadata(ClassDecl *classDecl,
-                                               bool requiresNSCodingAttr) {
+                                              bool requiresNSCodingAttr) {
   // If we already have the attribute, there's nothing to do.
   if (classDecl->getAttrs().hasAttribute<StaticInitializeObjCMetadataAttr>())
     return;
diff --git a/lib/Sema/TypeCheckSwitchStmt.cpp b/lib/Sema/TypeCheckSwitchStmt.cpp
index 78b67b2..bfb590e 100644
--- a/lib/Sema/TypeCheckSwitchStmt.cpp
+++ b/lib/Sema/TypeCheckSwitchStmt.cpp
@@ -71,7 +71,7 @@
     };
 
   #define PAIRCASE(XS, YS) case PairSwitch(XS, YS)
-
+    
     class Space final {
     private:
       SpaceKind Kind;
@@ -79,12 +79,66 @@
       Identifier Head;
       std::forward_list<Space> Spaces;
 
+      // NB: This constant is arbitrary.  Anecdotally, the Space Engine is
+      // capable of efficiently handling Spaces of around size 200, but it would
+      // potentially push an enormous fixit on the user.
+      static const size_t MAX_SPACE_SIZE = 128;
+
+      size_t computeSize(TypeChecker &TC,
+                         SmallPtrSetImpl<TypeBase *> &cache) const {
+        switch (getKind()) {
+        case SpaceKind::Empty:
+          return 0;
+        case SpaceKind::BooleanConstant:
+          return 1;
+        case SpaceKind::Type: {
+          if (!canDecompose(getType())) {
+            return 1;
+          }
+          cache.insert(getType().getPointer());
+
+          SmallVector<Space, 4> spaces;
+          decompose(TC, getType(), spaces);
+          size_t acc = 0;
+          for (auto &sp : spaces) {
+            // Decomposed pattern spaces grow with the sum of the subspaces.
+            acc += sp.computeSize(TC, cache);
+          }
+          
+          cache.erase(getType().getPointer());
+          return acc;
+        }
+        case SpaceKind::Constructor: {
+          size_t acc = 1;
+          for (auto &sp : getSpaces()) {
+            // Break self-recursive references among enum arguments.
+            if (sp.getKind() == SpaceKind::Type
+                  && cache.count(sp.getType().getPointer())) {
+              continue;
+            }
+            
+            // Constructor spaces grow with the product of their arguments.
+            acc *= sp.computeSize(TC, cache);
+          }
+          return acc;
+        }
+        case SpaceKind::Disjunct: {
+          size_t acc = 0;
+          for (auto &sp : getSpaces()) {
+            // Disjoint grow with the sum of the subspaces.
+            acc += sp.computeSize(TC, cache);
+          }
+          return acc;
+        }
+        }
+      }
+      
     public:
       explicit Space(Type T)
         : Kind(SpaceKind::Type), TypeAndVal(T, false), Head(Identifier()),
           Spaces({}){}
-      explicit Space(Type T, Identifier H, SmallVectorImpl<Space> &SP)
-        : Kind(SpaceKind::Constructor), TypeAndVal(T, false), Head(H),
+      explicit Space(Type T, Identifier H, bool downgrade, SmallVectorImpl<Space> &SP)
+        : Kind(SpaceKind::Constructor), TypeAndVal(T, downgrade), Head(H),
           Spaces(SP.begin(), SP.end()) {}
       explicit Space(SmallVectorImpl<Space> &SP)
         : Kind(SpaceKind::Disjunct), TypeAndVal(Type(), false),
@@ -100,7 +154,22 @@
 
       void dump() const LLVM_ATTRIBUTE_USED;
 
+      size_t getSize(TypeChecker &TC) const {
+        SmallPtrSet<TypeBase *, 4> cache;
+        return computeSize(TC, cache);
+      }
+
+      static size_t getMaximumSize() {
+        return MAX_SPACE_SIZE;
+      }
+
       bool isEmpty() const { return getKind() == SpaceKind::Empty; }
+      
+      bool canDowngrade() const {
+        assert(getKind() == SpaceKind::Constructor
+               && "Wrong kind of space tried to access downgrade");
+        return TypeAndVal.getInt();
+      }
 
       Type getType() const {
         assert((getKind() == SpaceKind::Type
@@ -585,7 +654,8 @@
             SmallVector<Space, 4> copyParams(this->getSpaces().begin(),
                                              this->getSpaces().end());
             copyParams[idx] = s1.minus(s2, TC);
-            Space CS(this->getType(), this->Head, copyParams);
+            Space CS(this->getType(), this->getHead(), this->canDowngrade(),
+                     copyParams);
             constrSpaces.push_back(CS);
           }
 
@@ -721,7 +791,7 @@
               return Space();
             }
           }
-          return Space(getType(), Head, simplifiedSpaces);
+          return Space(getType(), Head, canDowngrade(), simplifiedSpaces);
         }
         case SpaceKind::Type: {
           // If the decomposition of a space is empty, the space is empty.
@@ -813,7 +883,10 @@
                 constElemSpaces.push_back(Space(TTy->getUnderlyingType()));
               }
             }
-            return Space(tp, eed->getName(), constElemSpaces);
+            return Space(tp, eed->getName(),
+                         eed->getAttrs()
+                          .getAttribute<DowngradeExhaustivityCheckAttr>(),
+                         constElemSpaces);
           });
         } else if (auto *TTy = tp->castTo<TupleType>()) {
           // Decompose each of the elements into its component type space.
@@ -824,7 +897,8 @@
             return Space(ty.getType());
           });
           // Create an empty constructor head for the tuple space.
-          arr.push_back(Space(tp, Identifier(), constElemSpaces));
+          arr.push_back(Space(tp, Identifier(), /*canDowngrade*/false,
+                              constElemSpaces));
         } else {
           llvm_unreachable("Can't decompose type?");
         }
@@ -851,6 +925,8 @@
         return;
       }
 
+      bool sawDowngradablePattern = false;
+      bool sawRedundantPattern = false;
       SmallVector<Space, 4> spaces;
       for (unsigned i = 0, e = Switch->getCases().size(); i < e; ++i) {
         auto *caseBlock = Switch->getCases()[i];
@@ -864,9 +940,12 @@
           if (caseItem.isDefault())
             return;
 
-          auto projection = projectPattern(TC, caseItem.getPattern());
+          auto projection = projectPattern(TC, caseItem.getPattern(),
+                                           sawDowngradablePattern);
           if (projection.isUseful()
                 && projection.isSubspace(Space(spaces), TC)) {
+            sawRedundantPattern |= true;
+
             TC.diagnose(caseItem.getStartLoc(),
                           diag::redundant_particular_case)
               .highlight(caseItem.getSourceRange());
@@ -874,9 +953,26 @@
           spaces.push_back(projection);
         }
       }
-
+      
       Space totalSpace(Switch->getSubjectExpr()->getType());
       Space coveredSpace(spaces);
+      size_t totalSpaceSize = totalSpace.getSize(TC);
+      if (totalSpaceSize > Space::getMaximumSize()) {
+        // Because the space is large, we have to extend the size
+        // heuristic to compensate for actually exhaustively pattern matching
+        // over enormous spaces.  In this case, if the covered space covers
+        // as much as the total space, and there were no duplicates, then we
+        // can assume the user did the right thing and that they don't need
+        // a 'default' to be inserted.
+        if (!sawRedundantPattern
+            && coveredSpace.getSize(TC) >= totalSpaceSize) {
+          return;
+        }
+
+        diagnoseMissingCases(TC, Switch, /*justNeedsDefault*/true, Space());
+        return;
+      }
+      
       auto uncovered = totalSpace.minus(coveredSpace, TC).simplify(TC);
       if (uncovered.isEmpty()) {
         return;
@@ -904,43 +1000,81 @@
         uncovered = Space(spaces);
       }
 
-      diagnoseMissingCases(TC, Switch, /*justNeedsDefault*/ false, uncovered);
+      diagnoseMissingCases(TC, Switch, /*justNeedsDefault*/ false, uncovered,
+                           sawDowngradablePattern);
+    }
+    
+    // HACK: Search the space for any remaining cases that were labelled
+    // @_downgrade_exhaustivity_check.
+    static bool shouldDowngradeToWarning(const Space &masterSpace) {
+      switch (masterSpace.getKind()) {
+        case SpaceKind::Type:
+        case SpaceKind::BooleanConstant:
+        case SpaceKind::Empty:
+          return false;
+        // Traverse the constructor and its subspaces.
+        case SpaceKind::Constructor:
+          return masterSpace.canDowngrade()
+              || std::accumulate(masterSpace.getSpaces().begin(),
+                                 masterSpace.getSpaces().end(),
+                                false,
+                                [](bool acc, const Space &space) {
+            return acc || shouldDowngradeToWarning(space);
+          });
+        case SpaceKind::Disjunct:
+          // Traverse the disjunct's subspaces.
+          return std::accumulate(masterSpace.getSpaces().begin(),
+                                 masterSpace.getSpaces().end(),
+                                 false,
+                                 [](bool acc, const Space &space) {
+                                   return acc || shouldDowngradeToWarning(space);
+                                 });
+      }
     }
 
-    static void diagnoseMissingCases(TypeChecker &TC, const SwitchStmt *Switch,
-                                     bool JustNeedsDefault,
-                                     SpaceEngine::Space uncovered) {
-      bool Empty = Switch->getCases().empty();
-      SourceLoc StartLoc = Switch->getStartLoc();
-      SourceLoc EndLoc = Switch->getEndLoc();
-      StringRef Placeholder = getCodePlaceholder();
-      llvm::SmallString<128> Buffer;
-      llvm::raw_svector_ostream OS(Buffer);
+    static void diagnoseMissingCases(TypeChecker &TC, const SwitchStmt *SS,
+                                     bool justNeedsDefault,
+                                     Space uncovered,
+                                     bool sawDowngradablePattern = false) {
+      SourceLoc startLoc = SS->getStartLoc();
+      SourceLoc endLoc = SS->getEndLoc();
+      StringRef placeholder = getCodePlaceholder();
+      llvm::SmallString<128> buffer;
+      llvm::raw_svector_ostream OS(buffer);
 
       bool InEditor = TC.Context.LangOpts.DiagnosticsEditorMode;
 
-      if (JustNeedsDefault) {
-        OS << tok::kw_default << ":\n" << Placeholder << "\n";
-        if (Empty) {
-          TC.Context.Diags.diagnose(StartLoc, diag::empty_switch_stmt)
-             .fixItInsert(EndLoc, Buffer.str());
+      if (justNeedsDefault) {
+        OS << tok::kw_default << ":\n" << placeholder << "\n";
+        if (SS->getCases().empty()) {
+          TC.Context.Diags.diagnose(startLoc, diag::empty_switch_stmt)
+             .fixItInsert(endLoc, buffer.str());
         } else {
-          TC.Context.Diags.diagnose(StartLoc, diag::non_exhaustive_switch);
-          TC.Context.Diags.diagnose(StartLoc, diag::missing_several_cases,
-                                    uncovered.isEmpty()).fixItInsert(EndLoc,
-                                                              Buffer.str());
+          TC.Context.Diags.diagnose(startLoc, diag::non_exhaustive_switch);
+          TC.Context.Diags.diagnose(startLoc, diag::missing_several_cases,
+                                    uncovered.isEmpty()).fixItInsert(endLoc,
+                                                              buffer.str());
         }
         return;
       }
 
       // If there's nothing else to diagnose, bail.
       if (uncovered.isEmpty()) return;
-
+      
+      auto mainDiagType = diag::non_exhaustive_switch;
+      if (TC.Context.isSwiftVersion3()) {
+        if (!sawDowngradablePattern && shouldDowngradeToWarning(uncovered)) {
+          mainDiagType = diag::non_exhaustive_switch_warn_swift3;
+        }
+      }
+      
       // If editing is enabled, emit a formatted error of the form:
       //
       // switch must be exhaustive, do you want to add missing cases?
-      //     case (.none, .some(_)): <#code#>
-      //     case (.some(_), .none): <#code#>
+      //     case (.none, .some(_)):
+      //       <#code#>
+      //     case (.some(_), .none):
+      //       <#code#>
       //
       // else:
       //
@@ -949,7 +1083,7 @@
       // missing case '(.none, .some(_))'
       // missing case '(.some(_), .none)'
       if (InEditor) {
-        Buffer.clear();
+        buffer.clear();
         SmallVector<Space, 8> emittedSpaces;
         for (auto &uncoveredSpace : uncovered.getSpaces()) {
           SmallVector<Space, 4> flats;
@@ -961,17 +1095,18 @@
             
             OS << tok::kw_case << " ";
             flat.show(OS);
-            OS << ":\n" << Placeholder << "\n";
+            OS << ":\n" << placeholder << "\n";
             
             emittedSpaces.push_back(flat);
           }
         }
 
-        TC.diagnose(StartLoc, diag::non_exhaustive_switch);
-        TC.diagnose(StartLoc, diag::missing_several_cases, false)
-          .fixItInsert(EndLoc, Buffer.str());
+        TC.diagnose(startLoc, diag::non_exhaustive_switch);
+        TC.diagnose(startLoc, diag::missing_several_cases, false)
+          .fixItInsert(endLoc, buffer.str());
+
       } else {
-        TC.Context.Diags.diagnose(StartLoc, diag::non_exhaustive_switch);
+        TC.Context.Diags.diagnose(startLoc, mainDiagType);
 
         SmallVector<Space, 8> emittedSpaces;
         for (auto &uncoveredSpace : uncovered.getSpaces()) {
@@ -982,9 +1117,9 @@
               continue;
             }
             
-            Buffer.clear();
+            buffer.clear();
             flat.show(OS);
-            TC.diagnose(StartLoc, diag::missing_particular_case, Buffer.str());
+            TC.diagnose(startLoc, diag::missing_particular_case, buffer.str());
             
             emittedSpaces.push_back(flat);
           }
@@ -1006,7 +1141,7 @@
           flats.push_back(space);
           return;
         }
-
+        
         // To recursively recover a pattern matrix from a bunch of disjuncts:
         // 1) Unpack the arguments to the constructor under scrutiny.
         // 2) Traverse each argument in turn.
@@ -1022,17 +1157,13 @@
           SmallVector<Space, 4> columnVect;
           flatten(subspace, columnVect);
 
-          // Pattern matrices grow quasi-factorially in the size of the
-          // input space.
-          multiplier *= columnVect.size();
-
           size_t startSize = matrix.size();
           if (!matrix.empty() && columnVect.size() > 1) {
             size_t oldCount = matrix.size();
-            matrix.reserve(multiplier * oldCount);
+            matrix.reserve(oldCount * columnVect.size());
             // Indexing starts at 1, we already have 'startSize'-many elements
             // in the matrix; multiplies by 1 are no-ops.
-            for (size_t i = 1; i < multiplier; ++i) {
+            for (size_t i = 1; i < columnVect.size(); ++i) {
               std::copy_n(matrix.begin(), oldCount, std::back_inserter(matrix));
             }
           }
@@ -1067,11 +1198,16 @@
               matrix[rowIdx].push_back(columnVect[colIdx]);
             }
           }
+          
+          // Pattern matrices grow quasi-factorially in the size of the
+          // input space.
+          multiplier *= columnVect.size();
         }
 
         // Wrap the matrix rows into this constructor.
         for (auto &row : matrix) {
-          flats.push_back(Space(space.getType(), space.getHead(), row));
+          flats.push_back(Space(space.getType(), space.getHead(),
+                                space.canDowngrade(), row));
         }
       }
         break;
@@ -1090,7 +1226,8 @@
     }
 
     // Recursively project a pattern into a Space.
-    static Space projectPattern(TypeChecker &TC, const Pattern *item) {
+    static Space projectPattern(TypeChecker &TC, const Pattern *item,
+                                bool &sawDowngradablePattern) {
       switch (item->getKind()) {
       case PatternKind::Any:
       case PatternKind::Named:
@@ -1122,28 +1259,39 @@
         return Space();
       case PatternKind::Var: {
         auto *VP = cast<VarPattern>(item);
-        return projectPattern(TC, VP->getSubPattern());
+        return projectPattern(TC, VP->getSubPattern(), sawDowngradablePattern);
       }
       case PatternKind::Paren: {
         auto *PP = cast<ParenPattern>(item);
-        return projectPattern(TC, PP->getSubPattern());
+        return projectPattern(TC, PP->getSubPattern(), sawDowngradablePattern);
       }
       case PatternKind::OptionalSome: {
         auto *OSP = cast<OptionalSomePattern>(item);
         SmallVector<Space, 1> payload = {
-          projectPattern(TC, OSP->getSubPattern())
+          projectPattern(TC, OSP->getSubPattern(), sawDowngradablePattern)
         };
-        return Space(item->getType(), TC.Context.getIdentifier("some"), payload);
+        return Space(item->getType(), TC.Context.getIdentifier("some"), /*canDowngrade*/false,
+                     payload);
       }
       case PatternKind::EnumElement: {
         auto *VP = cast<EnumElementPattern>(item);
         TC.validateDecl(item->getType()->getEnumOrBoundGenericEnum());
+        
+        bool canDowngrade = false;
+        if (auto *eed = VP->getElementDecl()) {
+          if (eed->getAttrs().getAttribute<DowngradeExhaustivityCheckAttr>()) {
+            canDowngrade |= true;
+            sawDowngradablePattern |= true;
+          }
+        }
+        
         SmallVector<Space, 4> conArgSpace;
         auto *SP = VP->getSubPattern();
         if (!SP) {
           // If there's no sub-pattern then there's no further recursive
           // structure here.  Yield the constructor space.
-          return Space(item->getType(), VP->getName(), conArgSpace);
+          return Space(item->getType(), VP->getName(), canDowngrade,
+                       conArgSpace);
         }
 
         switch (SP->getKind()) {
@@ -1152,9 +1300,11 @@
           std::transform(TP->getElements().begin(), TP->getElements().end(),
                          std::back_inserter(conArgSpace),
                          [&](TuplePatternElt pate) {
-                           return projectPattern(TC, pate.getPattern());
+                           return projectPattern(TC, pate.getPattern(),
+                                                 sawDowngradablePattern);
                          });
-          return Space(item->getType(), VP->getName(), conArgSpace);
+          return Space(item->getType(), VP->getName(), /*canDowngrade*/false,
+                       conArgSpace);
         }
         case PatternKind::Paren: {
           auto *PP = dyn_cast<ParenPattern>(SP);
@@ -1171,15 +1321,21 @@
                 conArgSpace.push_back(Space(ty.getType()));
               }
             } else {
-              conArgSpace.push_back(projectPattern(TC, SP));
+              conArgSpace.push_back(projectPattern(TC, SP,
+                                                   sawDowngradablePattern));
             }
           } else {
-            conArgSpace.push_back(projectPattern(TC, SP));
+            conArgSpace.push_back(projectPattern(TC, SP,
+                                                 sawDowngradablePattern));
           }
-          return Space(item->getType(), VP->getName(), conArgSpace);
+          // FIXME: This isn't *technically* correct, but we only use the
+          // downgradability of the master space, not the projected space,
+          // when reconstructing the missing pattern matrix.
+          return Space(item->getType(), VP->getName(), /*canDowngrade*/false,
+                       conArgSpace);
         }
         default:
-          return projectPattern(TC, SP);
+          return projectPattern(TC, SP, sawDowngradablePattern);
         }
       }
       case PatternKind::Tuple: {
@@ -1188,9 +1344,10 @@
         std::transform(TP->getElements().begin(), TP->getElements().end(),
                        std::back_inserter(conArgSpace),
                        [&](TuplePatternElt pate) {
-          return projectPattern(TC, pate.getPattern());
+          return projectPattern(TC, pate.getPattern(), sawDowngradablePattern);
         });
-        return Space(item->getType(), Identifier(), conArgSpace);
+        return Space(item->getType(), Identifier(), /*canDowngrade*/false,
+                     conArgSpace);
       }
       }
     }
diff --git a/test/SILGen/downgrade_exhaustivity_swift3.swift b/test/SILGen/downgrade_exhaustivity_swift3.swift
new file mode 100644
index 0000000..f0be6c1
--- /dev/null
+++ b/test/SILGen/downgrade_exhaustivity_swift3.swift
@@ -0,0 +1,48 @@
+// RUN: %target-swift-frontend -swift-version 3 -emit-silgen %s | %FileCheck %s
+
+enum Downgradable {
+  case spoon
+  case hat
+  @_downgrade_exhaustivity_check
+  case fork
+}
+
+// CHECK-LABEL: sil hidden @_T029downgrade_exhaustivity_swift343testDowngradableOmittedPatternIsUnreachableyAA0E0OSg3pat_tF
+func testDowngradableOmittedPatternIsUnreachable(pat : Downgradable?) {
+  // CHECK: switch_enum {{%.*}} : $Downgradable, case #Downgradable.spoon!enumelt: [[CASE1:bb[0-9]+]], case #Downgradable.hat!enumelt: [[CASE2:bb[0-9]+]], default [[DEFAULT_CASE:bb[0-9]+]]
+  switch pat! {
+  // CHECK: [[CASE1]]:
+  case .spoon:
+    break
+  // CHECK: [[CASE2]]:
+  case .hat:
+    break
+  // CHECK: [[DEFAULT_CASE]]:
+  // CHECK-NEXT:   unreachable
+  }
+  
+  // CHECK: switch_enum {{%[0-9]+}} : $Downgradable, case #Downgradable.spoon!enumelt: {{bb[0-9]+}}, case #Downgradable.hat!enumelt: {{bb[0-9]+}}, default [[TUPLE_DEFAULT_CASE_1:bb[0-9]+]]
+  // CHECK: switch_enum [[Y:%[0-9]+]] : $Downgradable, case #Downgradable.spoon!enumelt: {{bb[0-9]+}}, case #Downgradable.hat!enumelt: {{bb[0-9]+}}, default [[TUPLE_DEFAULT_CASE_2:bb[0-9]+]]
+  switch (pat!, pat!) {
+  case (.spoon, .spoon):
+    break
+  case (.spoon, .hat):
+    break
+  case (.hat, .spoon):
+    break
+  case (.hat, .hat):
+    break
+  // CHECK: [[TUPLE_DEFAULT_CASE_2]]:
+  // CHECK-NEXT:   unreachable
+    
+  // CHECK: switch_enum [[Y]] : $Downgradable, case #Downgradable.spoon!enumelt: {{bb[0-9]+}}, case #Downgradable.hat!enumelt: {{bb[0-9]+}}, default [[TUPLE_DEFAULT_CASE_3:bb[0-9]+]]
+    
+  // CHECK: [[TUPLE_DEFAULT_CASE_3]]:
+  // CHECK-NEXT:   unreachable
+    
+  // CHECK: [[TUPLE_DEFAULT_CASE_1]]:
+  // CHECK-NEXT:   unreachable
+  }
+  
+}
+
diff --git a/test/Sema/exhaustive_switch.swift b/test/Sema/exhaustive_switch.swift
index 0ea73f2..24c0200 100644
--- a/test/Sema/exhaustive_switch.swift
+++ b/test/Sema/exhaustive_switch.swift
@@ -329,7 +329,7 @@
   case (.hat, .spoon):
     break
   }
-  
+
   switch (x!, x!) { // expected-error {{switch must be exhaustive}}
   // expected-note@-1 {{add missing case: '(.fork, _)'}}
   // expected-note@-2 {{add missing case: '(.hat, .spoon)'}}
@@ -343,3 +343,158 @@
     break
   }
 }
+
+enum LargeSpaceEnum {
+  case case0
+  case case1
+  case case2
+  case case3
+  case case4
+  case case5
+  case case6
+  case case7
+  case case8
+  case case9
+  case case10
+}
+
+func notQuiteBigEnough() -> Bool {
+  switch (LargeSpaceEnum.case1, LargeSpaceEnum.case2) { // expected-error {{switch must be exhaustive}}
+  // expected-note@-1 110 {{add missing case:}}
+  case (.case0, .case0): return true
+  case (.case1, .case1): return true
+  case (.case2, .case2): return true
+  case (.case3, .case3): return true
+  case (.case4, .case4): return true
+  case (.case5, .case5): return true
+  case (.case6, .case6): return true
+  case (.case7, .case7): return true
+  case (.case8, .case8): return true
+  case (.case9, .case9): return true
+  case (.case10, .case10): return true
+  }
+}
+
+enum OverlyLargeSpaceEnum {
+  case case0
+  case case1
+  case case2
+  case case3
+  case case4
+  case case5
+  case case6
+  case case7
+  case case8
+  case case9
+  case case10
+  case case11
+}
+
+func quiteBigEnough() -> Bool {
+  switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{switch must be exhaustive}}
+  // expected-note@-1 {{do you want to add a default clause?}}
+  case (.case0, .case0): return true
+  case (.case1, .case1): return true
+  case (.case2, .case2): return true
+  case (.case3, .case3): return true
+  case (.case4, .case4): return true
+  case (.case5, .case5): return true
+  case (.case6, .case6): return true
+  case (.case7, .case7): return true
+  case (.case8, .case8): return true
+  case (.case9, .case9): return true
+  case (.case10, .case10): return true
+  case (.case11, .case11): return true
+  }
+
+  // No diagnostic
+  switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{switch must be exhaustive}}
+  // expected-note@-1 {{do you want to add a default clause?}}
+  case (.case0, _): return true
+  case (.case1, _): return true
+  case (.case2, _): return true
+  case (.case3, _): return true
+  case (.case4, _): return true
+  case (.case5, _): return true
+  case (.case6, _): return true
+  case (.case7, _): return true
+  case (.case8, _): return true
+  case (.case9, _): return true
+  case (.case10, _): return true
+  }
+
+
+  // No diagnostic
+  switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) {
+  case (.case0, _): return true
+  case (.case1, _): return true
+  case (.case2, _): return true
+  case (.case3, _): return true
+  case (.case4, _): return true
+  case (.case5, _): return true
+  case (.case6, _): return true
+  case (.case7, _): return true
+  case (.case8, _): return true
+  case (.case9, _): return true
+  case (.case10, _): return true
+  case (.case11, _): return true
+  }
+
+  // No diagnostic
+  switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) {
+  case (_, .case0): return true
+  case (_, .case1): return true
+  case (_, .case2): return true
+  case (_, .case3): return true
+  case (_, .case4): return true
+  case (_, .case5): return true
+  case (_, .case6): return true
+  case (_, .case7): return true
+  case (_, .case8): return true
+  case (_, .case9): return true
+  case (_, .case10): return true
+  case (_, .case11): return true
+  }
+
+  // No diagnostic
+  switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) {
+  case (_, _): return true
+  }
+
+  // No diagnostic
+  switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) {
+  case (.case0, .case0): return true
+  case (.case1, .case1): return true
+  case (.case2, .case2): return true
+  case (.case3, .case3): return true
+  case _: return true
+  }
+}
+
+indirect enum InfinitelySized {
+  case one
+  case two
+  case recur(InfinitelySized)
+  case mutualRecur(MutuallyRecursive, InfinitelySized)
+}
+
+indirect enum MutuallyRecursive {
+  case one
+  case two
+  case recur(MutuallyRecursive)
+  case mutualRecur(InfinitelySized, MutuallyRecursive)
+}
+
+func infinitelySized() -> Bool {
+  switch (InfinitelySized.one, InfinitelySized.one) { // expected-error {{switch must be exhaustive}}
+  // expected-note@-1 10 {{add missing case:}}
+  case (.one, .one): return true
+  case (.two, .two): return true
+  }
+  
+  switch (MutuallyRecursive.one, MutuallyRecursive.one) { // expected-error {{switch must be exhaustive}}
+  // expected-note@-1 10 {{add missing case:}}
+  case (.one, .one): return true
+  case (.two, .two): return true
+  }
+}
diff --git a/test/attr/attr_downgrade_exhaustivity.swift b/test/attr/attr_downgrade_exhaustivity.swift
new file mode 100644
index 0000000..c357209
--- /dev/null
+++ b/test/attr/attr_downgrade_exhaustivity.swift
@@ -0,0 +1,150 @@
+// RUN: %target-typecheck-verify-swift -swift-version 3 %s
+
+enum Runcible {
+  case spoon
+  case hat
+  @_downgrade_exhaustivity_check
+  case fork
+}
+
+enum Trioptional {
+  case some(Runcible)
+  case just(Runcible)
+  case none
+}
+
+enum Fungible {
+  case cash
+  case giftCard
+}
+
+func missingCases(x: Runcible?, y: Runcible?, z: Trioptional) {
+  // Should warn in S3 mode that `fork` isn't used
+  switch x! { // expected-warning {{switch must be exhaustive}}
+  // expected-note@-1 {{add missing case: '.fork'}}
+  case .spoon:
+    break
+  case .hat:
+    break
+  }
+
+  // Should warn in S3 mode that `fork` isn't used
+  switch (x!, y!) { // expected-warning {{switch must be exhaustive}}
+  // expected-note@-1 3 {{add missing case:}}
+  case (.spoon, .spoon):
+    break
+  case (.spoon, .hat):
+    break
+  case (.hat, .spoon):
+    break
+  case (.hat, .hat):
+    break
+  }
+  
+  // Should error, since `fork` is used but not totally covered
+  switch (x!, y!) { // expected-error {{switch must be exhaustive}}
+  // expected-note@-1 {{add missing case: '(.fork, .hat)'}}
+  // expected-note@-2 {{add missing case: '(.fork, .fork)'}}
+  // expected-note@-3 {{add missing case: '(.hat, .fork)'}}
+  // expected-note@-4 {{add missing case: '(_, .fork)'}}
+  case (.spoon, .spoon):
+    break
+  case (.spoon, .hat):
+    break
+  case (.hat, .spoon):
+    break
+  case (.hat, .hat):
+    break
+  case (.fork, .spoon):
+    break
+  }
+  
+  // Should error, since `fork` is used but not totally covered
+  switch (x!, y!) { // expected-error {{switch must be exhaustive}}
+  // expected-note@-1 {{add missing case: '(.fork, _)'}}
+  // expected-note@-2 {{add missing case: '(.spoon, .fork)'}}
+  case (.spoon, .spoon):
+    break
+  case (.spoon, .hat):
+    break
+  case (.hat, .spoon):
+    break
+  case (.hat, .hat):
+    break
+  case (.hat, .fork):
+    break
+  }
+  
+  // Should warn in S3 mode that `fork` isn't used
+  switch x { // expected-warning {{switch must be exhaustive}}
+  // expected-note@-1 {{add missing case: '.some(.fork)'}}
+  case .some(.spoon):
+    break
+  case .some(.hat):
+    break
+  case .none:
+    break
+  }
+  
+  // Should warn in S3 mode that `fork` isn't used
+  switch (x, y!) { // expected-warning {{switch must be exhaustive}}
+  // expected-note@-1 {{add missing case: '(.some(.fork), _)'}}
+  // expected-note@-2 {{add missing case: '(.none, .fork)'}}
+  // expected-note@-3 {{add missing case: '(.some(.hat), .fork)'}}
+  // expected-note@-4 {{add missing case: '(_, .fork)'}}
+  case (.some(.spoon), .spoon):
+    break
+  case (.some(.spoon), .hat):
+    break
+  case (.some(.hat), .spoon):
+    break
+  case (.some(.hat), .hat):
+    break
+  case (.none, .spoon):
+    break
+  case (.none, .hat):
+    break
+  }
+
+  // Should warn in S3 mode that `fork` isn't used
+  switch (x, y) { // expected-warning {{switch must be exhaustive}}
+  // expected-note@-1 {{add missing case: '(.some(.fork), _)'}}
+  // expected-note@-2 {{add missing case: '(_, .some(.fork))'}}
+  // expected-note@-3 {{add missing case: '(.some(.hat), .some(.fork))'}}
+  // expected-note@-4 {{add missing case: '(.none, _)'}}
+  case (.some(.spoon), .some(.spoon)):
+    break
+  case (.some(.spoon), .some(.hat)):
+    break
+  case (.some(.spoon), .none):
+    break
+  case (.some(.hat), .some(.spoon)):
+    break
+  case (.some(.hat), .some(.hat)):
+    break
+  case (.some(.hat), .none):
+    break
+  case (.some(.hat), .some(.spoon)): // expected-warning {{case is already handled by previous patterns; consider removing it}}
+    break
+  case (.some(.hat), .some(.hat)): // expected-warning {{case is already handled by previous patterns; consider removing it}}
+    break
+  case (.some(.hat), .none): // expected-warning {{case is already handled by previous patterns; consider removing it}}
+    break
+  }
+  
+  // Should warn in S3 mode that `fork` isn't used
+  switch z { // expected-warning {{switch must be exhaustive}}
+  // expected-note@-1 {{add missing case: '.some(.fork)'}}
+  // expected-note@-2 {{add missing case: '.just(.fork)'}}
+  case .some(.spoon):
+    break
+  case .some(.hat):
+    break
+  case .just(.spoon):
+    break
+  case .just(.hat):
+    break
+  case .none:
+    break
+  }
+}
diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
index f7b1d8a..d2712da 100644
--- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
+++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.cpp
@@ -729,6 +729,7 @@
     // Ignore these.
     case DAK_ShowInInterface:
     case DAK_RawDocComment:
+    case DAK_DowngradeExhaustivityCheck:
       continue;
     default:
       break;