Propagate the new exception information to FunctionProtoType.
Change the interface to expose the new information and deal with the enormous fallout.
Introduce the new ExceptionSpecificationType value EST_DynamicNone to more easily deal with empty throw specifications.
Update the tests for noexcept and fix the various bugs uncovered, such as lack of tentative parsing support.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@127537 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c967864..3b2a062 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1552,8 +1552,8 @@
     mergeParamDeclAttributes(*ni, *oi, Context);    
 }
 
-/// MergeVarDecl - We parsed a variable 'New' which has the same name and scope
-/// as a previous declaration 'Old'.  Figure out how to merge their types,
+/// MergeVarDeclTypes - We parsed a variable 'New' which has the same name and
+/// scope as a previous declaration 'Old'.  Figure out how to merge their types,
 /// emitting diagnostics as appropriate.
 ///
 /// Declarations using the auto type specifier (C++ [decl.spec.auto]) call back
@@ -1570,8 +1570,10 @@
     if (AT && !AT->isDeduced()) {
       // We don't know what the new type is until the initializer is attached.
       return;
-    } else if (Context.hasSameType(New->getType(), Old->getType()))
-      return;
+    } else if (Context.hasSameType(New->getType(), Old->getType())) {
+      // These could still be something that needs exception specs checked.
+      return MergeVarDeclExceptionSpecs(New, Old);
+    }
     // C++ [basic.link]p10:
     //   [...] the types specified by all declarations referring to a given
     //   object or function shall be identical, except that declarations for an
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 2bce585..f655105 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -383,6 +383,48 @@
   return Invalid;
 }
 
+/// \brief Merge the exception specifications of two variable declarations.
+///
+/// This is called when there's a redeclaration of a VarDecl. The function
+/// checks if the redeclaration might have an exception specification and
+/// validates compatibility and merges the specs if necessary.
+void Sema::MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old) {
+  // Shortcut if exceptions are disabled.
+  if (!getLangOptions().CXXExceptions)
+    return;
+
+  assert(Context.hasSameType(New->getType(), Old->getType()) &&
+         "Should only be called if types are otherwise the same.");
+
+  QualType NewType = New->getType();
+  QualType OldType = Old->getType();
+
+  // We're only interested in pointers and references to functions, as well
+  // as pointers to member functions.
+  if (const ReferenceType *R = NewType->getAs<ReferenceType>()) {
+    NewType = R->getPointeeType();
+    OldType = OldType->getAs<ReferenceType>()->getPointeeType();
+  } else if (const PointerType *P = NewType->getAs<PointerType>()) {
+    NewType = P->getPointeeType();
+    OldType = OldType->getAs<PointerType>()->getPointeeType();
+  } else if (const MemberPointerType *M = NewType->getAs<MemberPointerType>()) {
+    NewType = M->getPointeeType();
+    OldType = OldType->getAs<MemberPointerType>()->getPointeeType();
+  }
+
+  if (!NewType->isFunctionProtoType())
+    return;
+
+  // There's lots of special cases for functions. For function pointers, system
+  // libraries are hopefully not as broken so that we don't need these
+  // workarounds.
+  if (CheckEquivalentExceptionSpec(
+        OldType->getAs<FunctionProtoType>(), Old->getLocation(),
+        NewType->getAs<FunctionProtoType>(), New->getLocation())) {
+    New->setInvalidDecl();
+  }
+}
+
 /// CheckCXXDefaultArguments - Verify that the default arguments for a
 /// function declaration are well-formed according to C++
 /// [dcl.fct.default].
@@ -2978,53 +3020,101 @@
   /// implicitly-declared special member functions.
   class ImplicitExceptionSpecification {
     ASTContext &Context;
-    bool AllowsAllExceptions;
+    // We order exception specifications thus:
+    // noexcept is the most restrictive, but is only used in C++0x.
+    // throw() comes next.
+    // Then a throw(collected exceptions)
+    // Finally no specification.
+    // throw(...) is used instead if any called function uses it.
+    ExceptionSpecificationType ComputedEST;
     llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
     llvm::SmallVector<QualType, 4> Exceptions;
-    
+
+    void ClearExceptions() {
+      ExceptionsSeen.clear();
+      Exceptions.clear();
+    }
+
   public:
     explicit ImplicitExceptionSpecification(ASTContext &Context) 
-      : Context(Context), AllowsAllExceptions(false) { }
-    
-    /// \brief Whether the special member function should have any
-    /// exception specification at all.
-    bool hasExceptionSpecification() const {
-      return !AllowsAllExceptions;
+      : Context(Context), ComputedEST(EST_BasicNoexcept) {
+      if (!Context.getLangOptions().CPlusPlus0x)
+        ComputedEST = EST_DynamicNone;
     }
-    
-    /// \brief Whether the special member function should have a
-    /// throw(...) exception specification (a Microsoft extension).
-    bool hasAnyExceptionSpecification() const {
-      return false;
+
+    /// \brief Get the computed exception specification type.
+    ExceptionSpecificationType getExceptionSpecType() const {
+      assert(ComputedEST != EST_ComputedNoexcept &&
+             "noexcept(expr) should not be a possible result");
+      return ComputedEST;
     }
-    
+
     /// \brief The number of exceptions in the exception specification.
     unsigned size() const { return Exceptions.size(); }
-    
+
     /// \brief The set of exceptions in the exception specification.
     const QualType *data() const { return Exceptions.data(); }
-    
-    /// \brief Note that 
+
+    /// \brief Integrate another called method into the collected data.
     void CalledDecl(CXXMethodDecl *Method) {
-      // If we already know that we allow all exceptions, do nothing.
-      if (AllowsAllExceptions || !Method)
+      // If we have an MSAny spec already, don't bother.
+      if (!Method || ComputedEST == EST_MSAny)
         return;
-      
+
       const FunctionProtoType *Proto
         = Method->getType()->getAs<FunctionProtoType>();
-      
+
+      ExceptionSpecificationType EST = Proto->getExceptionSpecType();
+
       // If this function can throw any exceptions, make a note of that.
-      if (!Proto->hasExceptionSpec() || Proto->hasAnyExceptionSpec()) {
-        AllowsAllExceptions = true;
-        ExceptionsSeen.clear();
-        Exceptions.clear();
+      if (EST == EST_MSAny || EST == EST_None) {
+        ClearExceptions();
+        ComputedEST = EST;
         return;
       }
-        
+
+      // If this function has a basic noexcept, it doesn't affect the outcome.
+      if (EST == EST_BasicNoexcept)
+        return;
+
+      // If we have a throw-all spec at this point, ignore the function.
+      if (ComputedEST == EST_None)
+        return;
+
+      // If we're still at noexcept(true) and there's a nothrow() callee,
+      // change to that specification.
+      if (EST == EST_DynamicNone) {
+        if (ComputedEST == EST_BasicNoexcept)
+          ComputedEST = EST_DynamicNone;
+        return;
+      }
+
+      // Check out noexcept specs.
+      if (EST == EST_ComputedNoexcept) {
+        FunctionProtoType::NoexceptResult NR = Proto->getNoexceptSpec();
+        assert(NR != FunctionProtoType::NR_NoNoexcept &&
+               "Must have noexcept result for EST_ComputedNoexcept.");
+        assert(NR != FunctionProtoType::NR_Dependent &&
+               "Should not generate implicit declarations for dependent cases, "
+               "and don't know how to handle them anyway.");
+
+        // noexcept(false) -> no spec on the new function
+        if (NR == FunctionProtoType::NR_Throw) {
+          ClearExceptions();
+          ComputedEST = EST_None;
+        }
+        // noexcept(true) won't change anything either.
+        return;
+      }
+
+      assert(EST == EST_Dynamic && "EST case not considered earlier.");
+      assert(ComputedEST != EST_None &&
+             "Shouldn't collect exceptions when throw-all is guaranteed.");
+      ComputedEST = EST_Dynamic;
       // Record the exceptions in this function's exception specification.
       for (FunctionProtoType::exception_iterator E = Proto->exception_begin(),
                                               EEnd = Proto->exception_end();
-           E != EEnd; ++E) 
+           E != EEnd; ++E)
         if (ExceptionsSeen.insert(Context.getCanonicalType(*E)))
           Exceptions.push_back(*E);
     }
@@ -4672,7 +4762,7 @@
   //   exception-specification. [...]
   ImplicitExceptionSpecification ExceptSpec(Context);
 
-  // Direct base-class destructors.
+  // Direct base-class constructors.
   for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(),
                                        BEnd = ClassDecl->bases_end();
        B != BEnd; ++B) {
@@ -4688,8 +4778,8 @@
         ExceptSpec.CalledDecl(Constructor);
     }
   }
-  
-  // Virtual base-class destructors.
+
+  // Virtual base-class constructors.
   for (CXXRecordDecl::base_class_iterator B = ClassDecl->vbases_begin(),
                                        BEnd = ClassDecl->vbases_end();
        B != BEnd; ++B) {
@@ -4702,8 +4792,8 @@
         ExceptSpec.CalledDecl(Constructor);
     }
   }
-  
-  // Field destructors.
+
+  // Field constructors.
   for (RecordDecl::field_iterator F = ClassDecl->field_begin(),
                                FEnd = ClassDecl->field_end();
        F != FEnd; ++F) {
@@ -4720,9 +4810,7 @@
   }
 
   FunctionProtoType::ExtProtoInfo EPI;
-  EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
-    (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
-    EST_None;
+  EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
   EPI.NumExceptions = ExceptSpec.size();
   EPI.Exceptions = ExceptSpec.data();
 
@@ -4997,16 +5085,14 @@
       ExceptSpec.CalledDecl(
                     LookupDestructor(cast<CXXRecordDecl>(RecordTy->getDecl())));
   }
-  
+
   // Create the actual destructor declaration.
   FunctionProtoType::ExtProtoInfo EPI;
-  EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
-    (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
-    EST_None;
+  EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
   EPI.NumExceptions = ExceptSpec.size();
   EPI.Exceptions = ExceptSpec.data();
   QualType Ty = Context.getFunctionType(Context.VoidTy, 0, 0, EPI);
-  
+
   CanQualType ClassType
     = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl));
   SourceLocation ClassLoc = ClassDecl->getLocation();
@@ -5014,9 +5100,9 @@
     = Context.DeclarationNames.getCXXDestructorName(ClassType);
   DeclarationNameInfo NameInfo(Name, ClassLoc);
   CXXDestructorDecl *Destructor
-    = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, Ty, 0,
-                                /*isInline=*/true,
-                                /*isImplicitlyDeclared=*/true);
+      = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, Ty, 0,
+                                  /*isInline=*/true,
+                                  /*isImplicitlyDeclared=*/true);
   Destructor->setAccess(AS_public);
   Destructor->setImplicit();
   Destructor->setTrivial(ClassDecl->hasTrivialDestructor());
@@ -5396,13 +5482,11 @@
         ExceptSpec.CalledDecl(CopyAssign);      
     }      
   }
-  
+
   //   An implicitly-declared copy assignment operator is an inline public
   //   member of its class.
   FunctionProtoType::ExtProtoInfo EPI;
-  EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
-    (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
-    EST_None;
+  EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
   EPI.NumExceptions = ExceptSpec.size();
   EPI.Exceptions = ExceptSpec.data();
   DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
@@ -5860,13 +5944,11 @@
         ExceptSpec.CalledDecl(CopyConstructor);
     }
   }
-  
+
   //   An implicitly-declared copy constructor is an inline public
   //   member of its class.
   FunctionProtoType::ExtProtoInfo EPI;
-  EPI.ExceptionSpecType = ExceptSpec.hasExceptionSpecification() ?
-    (ExceptSpec.hasAnyExceptionSpecification() ? EST_DynamicAny : EST_Dynamic) :
-    EST_None;
+  EPI.ExceptionSpecType = ExceptSpec.getExceptionSpecType();
   EPI.NumExceptions = ExceptSpec.size();
   EPI.Exceptions = ExceptSpec.data();
   DeclarationName Name
diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp
index dce4c6e..01ee712 100644
--- a/lib/Sema/SemaExceptionSpec.cpp
+++ b/lib/Sema/SemaExceptionSpec.cpp
@@ -105,7 +105,8 @@
                                     New->getType()->getAs<FunctionProtoType>(),
                                     New->getLocation(),
                                     &MissingExceptionSpecification,
-                                    &MissingEmptyExceptionSpecification))
+                                    &MissingEmptyExceptionSpecification,
+                                    /*AllowNoexceptAllMatchWithNoSpec=*/true))
     return false;
 
   // The failure was something other than an empty exception
@@ -129,8 +130,7 @@
        Context.getSourceManager().isInSystemHeader(Old->getLocation())) &&
       Old->isExternC()) {
     FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
-    EPI.ExceptionSpecType = EST_Dynamic;
-    EPI.NumExceptions = 0;
+    EPI.ExceptionSpecType = EST_DynamicNone;
     QualType NewType = Context.getFunctionType(NewProto->getResultType(),
                                                NewProto->arg_type_begin(),
                                                NewProto->getNumArgs(),
@@ -144,11 +144,14 @@
       = Old->getType()->getAs<FunctionProtoType>();
 
     FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
-    EPI.ExceptionSpecType = OldProto->hasExceptionSpec() ?
-      (OldProto->hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) :
-      EST_None;
-    EPI.NumExceptions = OldProto->getNumExceptions();
-    EPI.Exceptions = OldProto->exception_begin();
+    EPI.ExceptionSpecType = OldProto->getExceptionSpecType();
+    if (EPI.ExceptionSpecType == EST_Dynamic) {
+      EPI.NumExceptions = OldProto->getNumExceptions();
+      EPI.Exceptions = OldProto->exception_begin();
+    } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
+      // FIXME: We can't just take the expression from the old prototype. It
+      // likely contains references to the old prototype's parameters.
+    }
 
     // Update the type of the function with the appropriate exception
     // specification.
@@ -178,20 +181,43 @@
     // Warn about the lack of exception specification.
     llvm::SmallString<128> ExceptionSpecString;
     llvm::raw_svector_ostream OS(ExceptionSpecString);
-    OS << "throw(";
-    bool OnFirstException = true;
-    for (FunctionProtoType::exception_iterator E = OldProto->exception_begin(),
-                                            EEnd = OldProto->exception_end();
-         E != EEnd;
-         ++E) {
-      if (OnFirstException)
-        OnFirstException = false;
-      else
-        OS << ", ";
-      
-      OS << E->getAsString(Context.PrintingPolicy);
+    switch (OldProto->getExceptionSpecType()) {
+    case EST_DynamicNone:
+      OS << "throw()";
+      break;
+
+    case EST_Dynamic: {
+      OS << "throw(";
+      bool OnFirstException = true;
+      for (FunctionProtoType::exception_iterator E = OldProto->exception_begin(),
+                                              EEnd = OldProto->exception_end();
+           E != EEnd;
+           ++E) {
+        if (OnFirstException)
+          OnFirstException = false;
+        else
+          OS << ", ";
+        
+        OS << E->getAsString(Context.PrintingPolicy);
+      }
+      OS << ")";
+      break;
     }
-    OS << ")";
+
+    case EST_BasicNoexcept:
+      OS << "noexcept";
+      break;
+
+    case EST_ComputedNoexcept:
+      OS << "noexcept(";
+      OldProto->getNoexceptExpr()->printPretty(OS, Context, 0,
+                                               Context.PrintingPolicy);
+      OS << ")";
+      break;
+
+    default:
+      assert(false && "This spec type is compatible with none.");
+    }
     OS.flush();
 
     SourceLocation FixItLoc;
@@ -236,18 +262,17 @@
                                       Old, OldLoc, New, NewLoc);
 }
 
-/// CheckEquivalentExceptionSpec - Check if the two types have equivalent
-/// exception specifications. Exception specifications are equivalent if
-/// they allow exactly the same set of exception types. It does not matter how
-/// that is achieved. See C++ [except.spec]p2.
-bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, 
+/// CheckEquivalentExceptionSpec - Check if the two types have compatible
+/// exception specifications. See C++ [except.spec]p3.
+bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
                                         const PartialDiagnostic & NoteID,
-                                        const FunctionProtoType *Old, 
+                                        const FunctionProtoType *Old,
                                         SourceLocation OldLoc,
-                                        const FunctionProtoType *New, 
+                                        const FunctionProtoType *New,
                                         SourceLocation NewLoc,
                                         bool *MissingExceptionSpecification,
-                                     bool *MissingEmptyExceptionSpecification)  {
+                                        bool*MissingEmptyExceptionSpecification,
+                                        bool AllowNoexceptAllMatchWithNoSpec) {
   // Just completely ignore this under -fno-exceptions.
   if (!getLangOptions().CXXExceptions)
     return false;
@@ -258,29 +283,109 @@
   if (MissingEmptyExceptionSpecification)
     *MissingEmptyExceptionSpecification = false;
 
-  bool OldAny = !Old->hasExceptionSpec() || Old->hasAnyExceptionSpec();
-  bool NewAny = !New->hasExceptionSpec() || New->hasAnyExceptionSpec();
-  if (getLangOptions().Microsoft) {
-    // Treat throw(whatever) as throw(...) to be compatible with MS headers.
-    if (New->hasExceptionSpec() && New->getNumExceptions() > 0)
-      NewAny = true;
-    if (Old->hasExceptionSpec() && Old->getNumExceptions() > 0)
-      OldAny = true;
+  // C++0x [except.spec]p3: Two exception-specifications are compatible if:
+  //   - both are non-throwing, regardless of their form,
+  //   - both have the form noexcept(constant-expression) and the constant-
+  //     expressions are equivalent,
+  //   - one exception-specification is a noexcept-specification allowing all
+  //     exceptions and the other is of the form throw(type-id-list), or
+  //   - both are dynamic-exception-specifications that have the same set of
+  //     adjusted types.
+  //
+  // C++0x [except.spec]p12: An exception-specifcation is non-throwing if it is
+  //   of the form throw(), noexcept, or noexcept(constant-expression) where the
+  //   constant-expression yields true.
+  //
+  // CWG 1073 Proposed resolution: Strike the third bullet above.
+  //
+  // C++0x [except.spec]p4: If any declaration of a function has an exception-
+  //   specifier that is not a noexcept-specification allowing all exceptions,
+  //   all declarations [...] of that function shall have a compatible
+  //   exception-specification.
+  //
+  // That last point basically means that noexcept(false) matches no spec.
+  // It's considered when AllowNoexceptAllMatchWithNoSpec is true.
+
+  ExceptionSpecificationType OldEST = Old->getExceptionSpecType();
+  ExceptionSpecificationType NewEST = New->getExceptionSpecType();
+
+  // Shortcut the case where both have no spec.
+  if (OldEST == EST_None && NewEST == EST_None)
+    return false;
+
+  FunctionProtoType::NoexceptResult OldNR = Old->getNoexceptSpec();
+  FunctionProtoType::NoexceptResult NewNR = New->getNoexceptSpec();
+  if (OldNR == FunctionProtoType::NR_BadNoexcept ||
+      NewNR == FunctionProtoType::NR_BadNoexcept)
+    return false;
+
+  // Dependent noexcept specifiers are compatible with each other, but nothing
+  // else.
+  // One noexcept is compatible with another if the argument is the same
+  if (OldNR == NewNR &&
+      OldNR != FunctionProtoType::NR_NoNoexcept &&
+      NewNR != FunctionProtoType::NR_NoNoexcept)
+    return false;
+  if (OldNR != NewNR &&
+      OldNR != FunctionProtoType::NR_NoNoexcept &&
+      NewNR != FunctionProtoType::NR_NoNoexcept) {
+    Diag(NewLoc, DiagID);
+    if (NoteID.getDiagID() != 0)
+      Diag(OldLoc, NoteID);
+    return true;
   }
 
-  if (OldAny && NewAny)
+  if (getLangOptions().Microsoft) {
+    // Treat throw(whatever) as throw(...) to be compatible with MS headers.
+    if (OldEST == EST_Dynamic)
+      OldEST = EST_MSAny;
+    if (NewEST == EST_Dynamic)
+      NewEST = EST_MSAny;
+  }
+
+  // The MS extension throw(...) is compatible with itself.
+  if (OldEST == EST_MSAny && NewEST == EST_MSAny)
     return false;
-  if (OldAny || NewAny) {
+
+  // It's also compatible with no spec.
+  if ((OldEST == EST_None && NewEST == EST_MSAny) ||
+      (OldEST == EST_MSAny && NewEST == EST_None))
+    return false;
+
+  // It's also compatible with noexcept(false).
+  if (OldEST == EST_MSAny && NewNR == FunctionProtoType::NR_Throw)
+    return false;
+  if (NewEST == EST_MSAny && OldNR == FunctionProtoType::NR_Throw)
+    return false;
+
+  // As described above, noexcept(false) matches no spec only for functions.
+  if (AllowNoexceptAllMatchWithNoSpec) {
+    if (OldEST == EST_None && NewNR == FunctionProtoType::NR_Throw)
+      return false;
+    if (NewEST == EST_None && OldNR == FunctionProtoType::NR_Throw)
+      return false;
+  }
+
+  // Any non-throwing specifications are compatible.
+  bool OldNonThrowing = OldNR == FunctionProtoType::NR_Nothrow ||
+                        OldEST == EST_DynamicNone;
+  bool NewNonThrowing = NewNR == FunctionProtoType::NR_Nothrow ||
+                        NewEST == EST_DynamicNone;
+  if (OldNonThrowing && NewNonThrowing)
+    return false;
+
+  // At this point, the only remaining valid case is two matching dynamic
+  // specifications. We return here unless both specifications are dynamic.
+  if (OldEST != EST_Dynamic || NewEST != EST_Dynamic) {
     if (MissingExceptionSpecification && Old->hasExceptionSpec() &&
         !New->hasExceptionSpec()) {
       // The old type has an exception specification of some sort, but
       // the new type does not.
       *MissingExceptionSpecification = true;
 
-      if (MissingEmptyExceptionSpecification && 
-          !Old->hasAnyExceptionSpec() && Old->getNumExceptions() == 0) {
-        // The old type has a throw() exception specification and the
-        // new type has no exception specification, and the caller asked
+      if (MissingEmptyExceptionSpecification && OldNonThrowing) {
+        // The old type has a throw() or noexcept(true) exception specification
+        // and the new type has no exception specification, and the caller asked
         // to handle this itself.
         *MissingEmptyExceptionSpecification = true;
       }
@@ -294,8 +399,11 @@
     return true;
   }
 
+  assert(OldEST == EST_Dynamic && NewEST == EST_Dynamic &&
+      "Exception compatibility logic error: non-dynamic spec slipped through.");
+
   bool Success = true;
-  // Both have a definite exception spec. Collect the first set, then compare
+  // Both have a dynamic exception spec. Collect the first set, then compare
   // to the second.
   llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes;
   for (FunctionProtoType::exception_iterator I = Old->exception_begin(),
@@ -340,19 +448,66 @@
   if (!SubLoc.isValid())
     SubLoc = SuperLoc;
 
+  ExceptionSpecificationType SuperEST = Superset->getExceptionSpecType();
+
   // If superset contains everything, we're done.
-  if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec())
+  if (SuperEST == EST_None || SuperEST == EST_MSAny)
     return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
 
+  // If there are dependent noexcept specs, assume everything is fine. Unlike
+  // with the equivalency check, this is safe in this case, because we don't
+  // want to merge declarations. Checks after instantiation will catch any
+  // omissions we make here.
+  // We also shortcut checking if a noexcept expression was bad.
+
+  FunctionProtoType::NoexceptResult SuperNR =Superset->getNoexceptSpec();
+  if (SuperNR == FunctionProtoType::NR_BadNoexcept ||
+      SuperNR == FunctionProtoType::NR_Dependent)
+    return false;
+
+  // Another case of the superset containing everything.
+  if (SuperNR == FunctionProtoType::NR_Throw)
+    return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
+
+  ExceptionSpecificationType SubEST = Subset->getExceptionSpecType();
+
   // It does not. If the subset contains everything, we've failed.
-  if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) {
+  if (SubEST == EST_None || SubEST == EST_MSAny) {
     Diag(SubLoc, DiagID);
     if (NoteID.getDiagID() != 0)
       Diag(SuperLoc, NoteID);
     return true;
   }
 
-  // Neither contains everything. Do a proper comparison.
+  FunctionProtoType::NoexceptResult SubNR = Subset->getNoexceptSpec();
+  if (SubNR == FunctionProtoType::NR_BadNoexcept ||
+      SubNR == FunctionProtoType::NR_Dependent)
+    return false;
+
+  // Another case of the subset containing everything.
+  if (SubNR == FunctionProtoType::NR_Throw) {
+    Diag(SubLoc, DiagID);
+    if (NoteID.getDiagID() != 0)
+      Diag(SuperLoc, NoteID);
+    return true;
+  }
+
+  // If the subset contains nothing, we're done.
+  if (SubEST == EST_DynamicNone || SubNR == FunctionProtoType::NR_Nothrow)
+    return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
+
+  // Otherwise, if the superset contains nothing, we've failed.
+  if (SuperEST == EST_DynamicNone || SuperNR == FunctionProtoType::NR_Nothrow) {
+    Diag(SubLoc, DiagID);
+    if (NoteID.getDiagID() != 0)
+      Diag(SuperLoc, NoteID);
+    return true;
+  }
+
+  assert(SuperEST == EST_Dynamic && SubEST == EST_Dynamic &&
+         "Exception spec subset: non-dynamic case slipped through.");
+
+  // Neither contains everything or nothing. Do a proper comparison.
   for (FunctionProtoType::exception_iterator SubI = Subset->exception_begin(),
        SubE = Subset->exception_end(); SubI != SubE; ++SubI) {
     // Take one type from the subset.
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 25f042c..a1cd43a 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -1501,10 +1501,12 @@
   }
 
   FunctionProtoType::ExtProtoInfo EPI;
-  EPI.ExceptionSpecType = EST_Dynamic;
   if (HasBadAllocExceptionSpec) {
+    EPI.ExceptionSpecType = EST_Dynamic;
     EPI.NumExceptions = 1;
     EPI.Exceptions = &BadAllocType;
+  } else {
+    EPI.ExceptionSpecType = EST_DynamicNone;
   }
 
   QualType FnType = Context.getFunctionType(Return, &Argument, 1, EPI);
@@ -2421,7 +2423,7 @@
             FoundAssign = true;
             const FunctionProtoType *CPT
                 = Operator->getType()->getAs<FunctionProtoType>();
-            if (!CPT->hasEmptyExceptionSpec()) {
+            if (!CPT->isNothrow()) {
               AllNoThrow = false;
               break;
             }
@@ -2461,9 +2463,9 @@
           FoundConstructor = true;
           const FunctionProtoType *CPT
               = Constructor->getType()->getAs<FunctionProtoType>();
-          // TODO: check whether evaluating default arguments can throw.
+          // FIXME: check whether evaluating default arguments can throw.
           // For now, we'll be conservative and assume that they can throw.
-          if (!CPT->hasEmptyExceptionSpec() || CPT->getNumArgs() > 1) {
+          if (!CPT->isNothrow() || CPT->getNumArgs() > 1) {
             AllNoThrow = false;
             break;
           }
@@ -2498,7 +2500,7 @@
               = Constructor->getType()->getAs<FunctionProtoType>();
           // TODO: check whether evaluating default arguments can throw.
           // For now, we'll be conservative and assume that they can throw.
-          return CPT->hasEmptyExceptionSpec() && CPT->getNumArgs() == 0;
+          return CPT->isNothrow() && CPT->getNumArgs() == 0;
         }
       }
     }
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e60882d..441149f 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2085,8 +2085,7 @@
   const FunctionProtoType *Proto = Tmpl->getType()->getAs<FunctionProtoType>();
   assert(Proto && "Function template without prototype?");
 
-  if (Proto->hasExceptionSpec() || Proto->hasAnyExceptionSpec() ||
-      Proto->getNoReturnAttr()) {
+  if (Proto->hasExceptionSpec() || Proto->getNoReturnAttr()) {
     // The function has an exception specification or a "noreturn"
     // attribute. Substitute into each of the exception types.
     llvm::SmallVector<QualType, 4> Exceptions;
@@ -2100,7 +2099,7 @@
                                                 Unexpanded);
         assert(!Unexpanded.empty() && 
                "Pack expansion without parameter packs?");
-        
+
         bool Expand = false;
         bool RetainExpansion = false;
         llvm::Optional<unsigned> NumExpansions
@@ -2114,7 +2113,7 @@
                                                     RetainExpansion,
                                                     NumExpansions))
           break;
-                      
+
         if (!Expand) {
           // We can't expand this pack expansion into separate arguments yet;
           // just substitute into the pattern and create a new pack expansion 
@@ -2130,7 +2129,7 @@
           Exceptions.push_back(T);
           continue;
         }
-        
+
         // Substitute into the pack expansion pattern for each template
         bool Invalid = false;
         for (unsigned ArgIdx = 0; ArgIdx != *NumExpansions; ++ArgIdx) {
@@ -2143,13 +2142,13 @@
             Invalid = true;
             break;
           }
-          
+
           Exceptions.push_back(T);
         }
-        
+
         if (Invalid)
           break;
-        
+
         continue;
       }
       
@@ -2166,9 +2165,8 @@
     // Rebuild the function type 
 
     FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo();
-    EPI.ExceptionSpecType = Proto->hasExceptionSpec() ?
-      (Proto->hasAnyExceptionSpec() ? EST_DynamicAny : EST_Dynamic) :
-      EST_None;
+    // FIXME: Handle noexcept
+    EPI.ExceptionSpecType = Proto->getExceptionSpecType();
     EPI.NumExceptions = Exceptions.size();
     EPI.Exceptions = Exceptions.data();
     EPI.ExtInfo = Proto->getExtInfo();
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 62bf8e6..f49a49c 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -1901,7 +1901,22 @@
           EPI.NumExceptions = Exceptions.size();
           EPI.Exceptions = Exceptions.data();
         } else if (FTI.getExceptionSpecType() == EST_ComputedNoexcept) {
-          EPI.NoexceptExpr = FTI.NoexceptExpr;
+          // If an error occurred, there's no expression here.
+          if (Expr *NoexceptExpr = FTI.NoexceptExpr) {
+            assert((NoexceptExpr->isTypeDependent() ||
+                    NoexceptExpr->getType()->getCanonicalTypeUnqualified() ==
+                        Context.BoolTy) &&
+                 "Parser should have made sure that the expression is boolean");
+            SourceLocation ErrLoc;
+            llvm::APSInt Dummy;
+            if (!NoexceptExpr->isValueDependent() &&
+                !NoexceptExpr->isIntegerConstantExpr(Dummy, Context, &ErrLoc,
+                                                     /*evaluated*/false))
+              Diag(ErrLoc, diag::err_noexcept_needs_constant_expression)
+                  << NoexceptExpr->getSourceRange();
+            else
+              EPI.NoexceptExpr = NoexceptExpr;
+          }
         }
 
         T = Context.getFunctionType(T, ArgTys.data(), ArgTys.size(), EPI);