[clang] [clang] Introduce `SemaExceptionSpec` (PR #92653)

Vlad Serebrennikov via cfe-commits cfe-commits at lists.llvm.org
Sun May 19 03:15:45 PDT 2024


https://github.com/Endilll updated https://github.com/llvm/llvm-project/pull/92653

>From 06f2555364684dc7a6af10f4870523265235abe6 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 18 May 2024 17:49:06 +0300
Subject: [PATCH 1/2] [clang] Introduce `SemaExceptionSpec`

---
 clang/include/clang/Sema/Sema.h               | 187 ++-------
 clang/include/clang/Sema/SemaExceptionSpec.h  | 187 +++++++++
 clang/lib/Parse/ParseCXXInlineMethods.cpp     |   3 +-
 clang/lib/Parse/ParseDecl.cpp                 |   3 +-
 clang/lib/Parse/ParseDeclCXX.cpp              |   3 +-
 clang/lib/Sema/AnalysisBasedWarnings.cpp      |   3 +-
 clang/lib/Sema/Sema.cpp                       |  12 +-
 clang/lib/Sema/SemaCoroutine.cpp              |   3 +-
 clang/lib/Sema/SemaDecl.cpp                   |  13 +-
 clang/lib/Sema/SemaDeclCXX.cpp                | 215 +---------
 clang/lib/Sema/SemaExceptionSpec.cpp          | 392 ++++++++++++++----
 clang/lib/Sema/SemaExpr.cpp                   |   5 +-
 clang/lib/Sema/SemaExprCXX.cpp                |  25 +-
 clang/lib/Sema/SemaExprMember.cpp             |   3 +-
 clang/lib/Sema/SemaInit.cpp                   |   5 +-
 clang/lib/Sema/SemaOverload.cpp               |   7 +-
 clang/lib/Sema/SemaTemplate.cpp               |   5 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  30 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   9 +-
 clang/lib/Sema/SemaType.cpp                   |  11 +-
 clang/lib/Sema/TreeTransform.h                |   7 +-
 21 files changed, 638 insertions(+), 490 deletions(-)
 create mode 100644 clang/include/clang/Sema/SemaExceptionSpec.h

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d4d4a82525a02..95a54014cf672 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -169,6 +169,7 @@ class PseudoObjectExpr;
 class QualType;
 class SemaCodeCompletion;
 class SemaCUDA;
+class SemaExceptionSpec;
 class SemaHLSL;
 class SemaObjC;
 class SemaOpenACC;
@@ -461,29 +462,28 @@ class Sema final : public SemaBase {
   // 9. Declarations (SemaDecl.cpp)
   // 10. Declaration Attribute Handling (SemaDeclAttr.cpp)
   // 11. C++ Declarations (SemaDeclCXX.cpp)
-  // 12. C++ Exception Specifications (SemaExceptionSpec.cpp)
-  // 13. Expressions (SemaExpr.cpp)
-  // 14. C++ Expressions (SemaExprCXX.cpp)
-  // 15. Member Access Expressions (SemaExprMember.cpp)
-  // 16. Initializers (SemaInit.cpp)
-  // 17. C++ Lambda Expressions (SemaLambda.cpp)
-  // 18. Name Lookup (SemaLookup.cpp)
-  // 19. Modules (SemaModule.cpp)
-  // 20. C++ Overloading (SemaOverload.cpp)
-  // 21. Pseudo-Object (SemaPseudoObject.cpp)
-  // 22. Statements (SemaStmt.cpp)
-  // 23. `inline asm` Statement (SemaStmtAsm.cpp)
-  // 24. Statement Attribute Handling (SemaStmtAttr.cpp)
-  // 25. C++ Templates (SemaTemplate.cpp)
-  // 26. C++ Template Argument Deduction (SemaTemplateDeduction.cpp)
-  // 27. C++ Template Instantiation (SemaTemplateInstantiate.cpp)
-  // 28. C++ Template Declaration Instantiation
+  // 12. Expressions (SemaExpr.cpp)
+  // 13. C++ Expressions (SemaExprCXX.cpp)
+  // 14. Member Access Expressions (SemaExprMember.cpp)
+  // 15. Initializers (SemaInit.cpp)
+  // 16. C++ Lambda Expressions (SemaLambda.cpp)
+  // 17. Name Lookup (SemaLookup.cpp)
+  // 18. Modules (SemaModule.cpp)
+  // 19. C++ Overloading (SemaOverload.cpp)
+  // 20. Pseudo-Object (SemaPseudoObject.cpp)
+  // 21. Statements (SemaStmt.cpp)
+  // 22. `inline asm` Statement (SemaStmtAsm.cpp)
+  // 23. Statement Attribute Handling (SemaStmtAttr.cpp)
+  // 24. C++ Templates (SemaTemplate.cpp)
+  // 25. C++ Template Argument Deduction (SemaTemplateDeduction.cpp)
+  // 26. C++ Template Instantiation (SemaTemplateInstantiate.cpp)
+  // 27. C++ Template Declaration Instantiation
   //     (SemaTemplateInstantiateDecl.cpp)
-  // 29. C++ Variadic Templates (SemaTemplateVariadic.cpp)
-  // 30. Constraints and Concepts (SemaConcept.cpp)
-  // 31. Types (SemaType.cpp)
-  // 32. FixIt Helpers (SemaFixItUtils.cpp)
-  // 33. Name Lookup for RISC-V Vector Intrinsic (SemaRISCVVectorLookup.cpp)
+  // 28. C++ Variadic Templates (SemaTemplateVariadic.cpp)
+  // 29. Constraints and Concepts (SemaConcept.cpp)
+  // 30. Types (SemaType.cpp)
+  // 31. FixIt Helpers (SemaFixItUtils.cpp)
+  // 32. Name Lookup for RISC-V Vector Intrinsic (SemaRISCVVectorLookup.cpp)
 
   /// \name Semantic Analysis
   /// Implementations are in Sema.cpp
@@ -994,6 +994,11 @@ class Sema final : public SemaBase {
     return *CUDAPtr;
   }
 
+  SemaExceptionSpec &ExceptionSpec() {
+    assert(ExceptionSpecPtr);
+    return *ExceptionSpecPtr;
+  }
+
   SemaHLSL &HLSL() {
     assert(HLSLPtr);
     return *HLSLPtr;
@@ -1051,6 +1056,7 @@ class Sema final : public SemaBase {
 
   std::unique_ptr<SemaCodeCompletion> CodeCompletionPtr;
   std::unique_ptr<SemaCUDA> CUDAPtr;
+  std::unique_ptr<SemaExceptionSpec> ExceptionSpecPtr;
   std::unique_ptr<SemaHLSL> HLSLPtr;
   std::unique_ptr<SemaObjC> ObjCPtr;
   std::unique_ptr<SemaOpenACC> OpenACCPtr;
@@ -4048,25 +4054,6 @@ class Sema final : public SemaBase {
   /// Evaluate the implicit exception specification for a defaulted
   /// special member function.
   void EvaluateImplicitExceptionSpec(SourceLocation Loc, FunctionDecl *FD);
-
-  /// Check the given exception-specification and update the
-  /// exception specification information with the results.
-  void checkExceptionSpecification(bool IsTopLevel,
-                                   ExceptionSpecificationType EST,
-                                   ArrayRef<ParsedType> DynamicExceptions,
-                                   ArrayRef<SourceRange> DynamicExceptionRanges,
-                                   Expr *NoexceptExpr,
-                                   SmallVectorImpl<QualType> &Exceptions,
-                                   FunctionProtoType::ExceptionSpecInfo &ESI);
-
-  /// Add an exception-specification to the given member or friend function
-  /// (or function template). The exception-specification was parsed
-  /// after the function itself was declared.
-  void actOnDelayedExceptionSpecification(
-      Decl *D, ExceptionSpecificationType EST, SourceRange SpecificationRange,
-      ArrayRef<ParsedType> DynamicExceptions,
-      ArrayRef<SourceRange> DynamicExceptionRanges, Expr *NoexceptExpr);
-
   class InheritedConstructorInfo;
 
   /// Determine if a special member function should have a deleted
@@ -4105,12 +4092,6 @@ class Sema final : public SemaBase {
   void DefineImplicitDestructor(SourceLocation CurrentLocation,
                                 CXXDestructorDecl *Destructor);
 
-  /// Build an exception spec for destructors that don't have one.
-  ///
-  /// C++11 says that user-defined destructors with no exception spec get one
-  /// that looks as if the destructor was implicitly declared.
-  void AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor);
-
   /// Define the specified inheriting constructor.
   void DefineInheritingConstructor(SourceLocation UseLoc,
                                    CXXConstructorDecl *Constructor);
@@ -4346,11 +4327,6 @@ class Sema final : public SemaBase {
   void MarkVTableUsed(SourceLocation Loc, CXXRecordDecl *Class,
                       bool DefinitionRequired = false);
 
-  /// Mark the exception specifications of all virtual member functions
-  /// in the given class as needed.
-  void MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc,
-                                             const CXXRecordDecl *RD);
-
   /// MarkVirtualMembersReferenced - Will mark all members of the given
   /// CXXRecordDecl referenced.
   void MarkVirtualMembersReferenced(SourceLocation Loc, const CXXRecordDecl *RD,
@@ -4437,7 +4413,6 @@ class Sema final : public SemaBase {
   bool CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
                                              CXXSpecialMemberKind CSM,
                                              SourceLocation DefaultLoc);
-  void CheckDelayedMemberExceptionSpecs();
 
   /// Kinds of defaulted comparison operator functions.
   enum class DefaultedComparisonKind : unsigned char {
@@ -4588,7 +4563,6 @@ class Sema final : public SemaBase {
   SmallVector<CXXRecordDecl *, 4> DelayedDllExportClasses;
   SmallVector<CXXMethodDecl *, 4> DelayedDllExportMemberFunctions;
 
-  void MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old);
   bool MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old, Scope *S);
 
   /// Helpers for dealing with blocks and functions.
@@ -4836,84 +4810,6 @@ class Sema final : public SemaBase {
   //
   //
 
-  /// \name C++ Exception Specifications
-  /// Implementations are in SemaExceptionSpec.cpp
-  ///@{
-
-public:
-  /// All the overriding functions seen during a class definition
-  /// that had their exception spec checks delayed, plus the overridden
-  /// function.
-  SmallVector<std::pair<const CXXMethodDecl *, const CXXMethodDecl *>, 2>
-      DelayedOverridingExceptionSpecChecks;
-
-  /// All the function redeclarations seen during a class definition that had
-  /// their exception spec checks delayed, plus the prior declaration they
-  /// should be checked against. Except during error recovery, the new decl
-  /// should always be a friend declaration, as that's the only valid way to
-  /// redeclare a special member before its class is complete.
-  SmallVector<std::pair<FunctionDecl *, FunctionDecl *>, 2>
-      DelayedEquivalentExceptionSpecChecks;
-
-  /// Determine if we're in a case where we need to (incorrectly) eagerly
-  /// parse an exception specification to work around a libstdc++ bug.
-  bool isLibstdcxxEagerExceptionSpecHack(const Declarator &D);
-
-  /// Check the given noexcept-specifier, convert its expression, and compute
-  /// the appropriate ExceptionSpecificationType.
-  ExprResult ActOnNoexceptSpec(Expr *NoexceptExpr,
-                               ExceptionSpecificationType &EST);
-
-  CanThrowResult canThrow(const Stmt *E);
-  /// Determine whether the callee of a particular function call can throw.
-  /// E, D and Loc are all optional.
-  static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
-                                       SourceLocation Loc = SourceLocation());
-  const FunctionProtoType *ResolveExceptionSpec(SourceLocation Loc,
-                                                const FunctionProtoType *FPT);
-  void UpdateExceptionSpec(FunctionDecl *FD,
-                           const FunctionProtoType::ExceptionSpecInfo &ESI);
-  bool CheckSpecifiedExceptionType(QualType &T, SourceRange Range);
-  bool CheckDistantExceptionSpec(QualType T);
-  bool CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New);
-  bool CheckEquivalentExceptionSpec(const FunctionProtoType *Old,
-                                    SourceLocation OldLoc,
-                                    const FunctionProtoType *New,
-                                    SourceLocation NewLoc);
-  bool CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
-                                    const PartialDiagnostic &NoteID,
-                                    const FunctionProtoType *Old,
-                                    SourceLocation OldLoc,
-                                    const FunctionProtoType *New,
-                                    SourceLocation NewLoc);
-  bool handlerCanCatch(QualType HandlerType, QualType ExceptionType);
-  bool CheckExceptionSpecSubset(
-      const PartialDiagnostic &DiagID, const PartialDiagnostic &NestedDiagID,
-      const PartialDiagnostic &NoteID, const PartialDiagnostic &NoThrowDiagID,
-      const FunctionProtoType *Superset, bool SkipSupersetFirstParameter,
-      SourceLocation SuperLoc, const FunctionProtoType *Subset,
-      bool SkipSubsetFirstParameter, SourceLocation SubLoc);
-  bool CheckParamExceptionSpec(
-      const PartialDiagnostic &NestedDiagID, const PartialDiagnostic &NoteID,
-      const FunctionProtoType *Target, bool SkipTargetFirstParameter,
-      SourceLocation TargetLoc, const FunctionProtoType *Source,
-      bool SkipSourceFirstParameter, SourceLocation SourceLoc);
-
-  bool CheckExceptionSpecCompatibility(Expr *From, QualType ToType);
-
-  /// CheckOverridingFunctionExceptionSpec - Checks whether the exception
-  /// spec is a subset of base spec.
-  bool CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
-                                            const CXXMethodDecl *Old);
-
-  ///@}
-
-  //
-  //
-  // -------------------------------------------------------------------------
-  //
-  //
-
   /// \name Expressions
   /// Implementations are in SemaExpr.cpp
   ///@{
@@ -10654,33 +10550,6 @@ class Sema final : public SemaBase {
 
   int ParsingClassDepth = 0;
 
-  class SavePendingParsedClassStateRAII {
-  public:
-    SavePendingParsedClassStateRAII(Sema &S) : S(S) { swapSavedState(); }
-
-    ~SavePendingParsedClassStateRAII() {
-      assert(S.DelayedOverridingExceptionSpecChecks.empty() &&
-             "there shouldn't be any pending delayed exception spec checks");
-      assert(S.DelayedEquivalentExceptionSpecChecks.empty() &&
-             "there shouldn't be any pending delayed exception spec checks");
-      swapSavedState();
-    }
-
-  private:
-    Sema &S;
-    decltype(DelayedOverridingExceptionSpecChecks)
-        SavedOverridingExceptionSpecChecks;
-    decltype(DelayedEquivalentExceptionSpecChecks)
-        SavedEquivalentExceptionSpecChecks;
-
-    void swapSavedState() {
-      SavedOverridingExceptionSpecChecks.swap(
-          S.DelayedOverridingExceptionSpecChecks);
-      SavedEquivalentExceptionSpecChecks.swap(
-          S.DelayedEquivalentExceptionSpecChecks);
-    }
-  };
-
   ///@}
 
   //
diff --git a/clang/include/clang/Sema/SemaExceptionSpec.h b/clang/include/clang/Sema/SemaExceptionSpec.h
new file mode 100644
index 0000000000000..17d505e977889
--- /dev/null
+++ b/clang/include/clang/Sema/SemaExceptionSpec.h
@@ -0,0 +1,187 @@
+//===--- SemaExceptionSpec.h --- C++ exception specification ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file declares routines for C++ exception specification testing.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SEMA_SEMAEXCEPTIONSPEC_H
+#define LLVM_CLANG_SEMA_SEMAEXCEPTIONSPEC_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/ExceptionSpecificationType.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/PartialDiagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/Ownership.h"
+#include "clang/Sema/SemaBase.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <utility>
+
+namespace clang {
+class SemaExceptionSpec : public SemaBase {
+public:
+  SemaExceptionSpec(Sema &S);
+
+  /// All the overriding functions seen during a class definition
+  /// that had their exception spec checks delayed, plus the overridden
+  /// function.
+  SmallVector<std::pair<const CXXMethodDecl *, const CXXMethodDecl *>, 2>
+      DelayedOverridingExceptionSpecChecks;
+
+  /// All the function redeclarations seen during a class definition that had
+  /// their exception spec checks delayed, plus the prior declaration they
+  /// should be checked against. Except during error recovery, the new decl
+  /// should always be a friend declaration, as that's the only valid way to
+  /// redeclare a special member before its class is complete.
+  SmallVector<std::pair<FunctionDecl *, FunctionDecl *>, 2>
+      DelayedEquivalentExceptionSpecChecks;
+
+  /// Determine if we're in a case where we need to (incorrectly) eagerly
+  /// parse an exception specification to work around a libstdc++ bug.
+  bool isLibstdcxxEagerExceptionSpecHack(const Declarator &D);
+
+  /// Check the given noexcept-specifier, convert its expression, and compute
+  /// the appropriate ExceptionSpecificationType.
+  ExprResult ActOnNoexceptSpec(Expr *NoexceptExpr,
+                               ExceptionSpecificationType &EST);
+
+  CanThrowResult canThrow(const Stmt *E);
+  /// Determine whether the callee of a particular function call can throw.
+  /// E, D and Loc are all optional.
+  static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
+                                       SourceLocation Loc = SourceLocation());
+  const FunctionProtoType *ResolveExceptionSpec(SourceLocation Loc,
+                                                const FunctionProtoType *FPT);
+  void UpdateExceptionSpec(FunctionDecl *FD,
+                           const FunctionProtoType::ExceptionSpecInfo &ESI);
+  bool CheckSpecifiedExceptionType(QualType &T, SourceRange Range);
+  bool CheckDistantExceptionSpec(QualType T);
+  bool CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New);
+  bool CheckEquivalentExceptionSpec(const FunctionProtoType *Old,
+                                    SourceLocation OldLoc,
+                                    const FunctionProtoType *New,
+                                    SourceLocation NewLoc);
+  bool CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
+                                    const PartialDiagnostic &NoteID,
+                                    const FunctionProtoType *Old,
+                                    SourceLocation OldLoc,
+                                    const FunctionProtoType *New,
+                                    SourceLocation NewLoc);
+  bool handlerCanCatch(QualType HandlerType, QualType ExceptionType);
+  bool CheckExceptionSpecSubset(
+      const PartialDiagnostic &DiagID, const PartialDiagnostic &NestedDiagID,
+      const PartialDiagnostic &NoteID, const PartialDiagnostic &NoThrowDiagID,
+      const FunctionProtoType *Superset, bool SkipSupersetFirstParameter,
+      SourceLocation SuperLoc, const FunctionProtoType *Subset,
+      bool SkipSubsetFirstParameter, SourceLocation SubLoc);
+  bool CheckParamExceptionSpec(
+      const PartialDiagnostic &NestedDiagID, const PartialDiagnostic &NoteID,
+      const FunctionProtoType *Target, bool SkipTargetFirstParameter,
+      SourceLocation TargetLoc, const FunctionProtoType *Source,
+      bool SkipSourceFirstParameter, SourceLocation SourceLoc);
+
+  bool CheckExceptionSpecCompatibility(Expr *From, QualType ToType);
+
+  /// CheckOverridingFunctionExceptionSpec - Checks whether the exception
+  /// spec is a subset of base spec.
+  bool CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
+                                            const CXXMethodDecl *Old);
+
+  void CheckDelayedMemberExceptionSpecs();
+
+  /// Helper class that collects exception specifications for
+  /// implicitly-declared special member functions.
+  class ImplicitExceptionSpecification {
+    // Pointer to allow copying
+    Sema *Self;
+    // We order exception specifications thus:
+    // noexcept is the most restrictive, but is only used in C++11.
+    // throw() comes next.
+    // Then a throw(collected exceptions)
+    // Finally no specification, which is expressed as noexcept(false).
+    // throw(...) is used instead if any called function uses it.
+    ExceptionSpecificationType ComputedEST;
+    llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
+    SmallVector<QualType, 4> Exceptions;
+
+    void ClearExceptions() {
+      ExceptionsSeen.clear();
+      Exceptions.clear();
+    }
+
+  public:
+    explicit ImplicitExceptionSpecification(Sema &Self);
+
+    /// Get the computed exception specification type.
+    ExceptionSpecificationType getExceptionSpecType() const {
+      assert(!isComputedNoexcept(ComputedEST) &&
+             "noexcept(expr) should not be a possible result");
+      return ComputedEST;
+    }
+
+    /// The number of exceptions in the exception specification.
+    unsigned size() const { return Exceptions.size(); }
+
+    /// The set of exceptions in the exception specification.
+    const QualType *data() const { return Exceptions.data(); }
+
+    /// Integrate another called method into the collected data.
+    void CalledDecl(SourceLocation CallLoc, const CXXMethodDecl *Method);
+
+    /// Integrate an invoked expression into the collected data.
+    void CalledExpr(Expr *E) { CalledStmt(E); }
+
+    /// Integrate an invoked statement into the collected data.
+    void CalledStmt(Stmt *S);
+
+    /// Overwrite an EPI's exception specification with this
+    /// computed exception specification.
+    FunctionProtoType::ExceptionSpecInfo getExceptionSpec() const;
+  };
+
+  /// Check the given exception-specification and update the
+  /// exception specification information with the results.
+  void checkExceptionSpecification(bool IsTopLevel,
+                                   ExceptionSpecificationType EST,
+                                   ArrayRef<ParsedType> DynamicExceptions,
+                                   ArrayRef<SourceRange> DynamicExceptionRanges,
+                                   Expr *NoexceptExpr,
+                                   SmallVectorImpl<QualType> &Exceptions,
+                                   FunctionProtoType::ExceptionSpecInfo &ESI);
+
+  /// Add an exception-specification to the given member or friend function
+  /// (or function template). The exception-specification was parsed
+  /// after the function itself was declared.
+  void actOnDelayedExceptionSpecification(
+      Decl *D, ExceptionSpecificationType EST, SourceRange SpecificationRange,
+      ArrayRef<ParsedType> DynamicExceptions,
+      ArrayRef<SourceRange> DynamicExceptionRanges, Expr *NoexceptExpr);
+
+  /// Build an exception spec for destructors that don't have one.
+  ///
+  /// C++11 says that user-defined destructors with no exception spec get one
+  /// that looks as if the destructor was implicitly declared.
+  void AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor);
+
+  /// Mark the exception specifications of all virtual member functions
+  /// in the given class as needed.
+  void MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc,
+                                             const CXXRecordDecl *RD);
+
+  void MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old);
+};
+} // namespace clang
+
+#endif // LLVM_CLANG_SEMA_SEMAEXCEPTIONSPEC_H
\ No newline at end of file
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 943ce0fdde3a3..ccff9db1238a0 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -17,6 +17,7 @@
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Scope.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 
 using namespace clang;
 
@@ -539,7 +540,7 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
       Diag(Tok.getLocation(), diag::err_except_spec_unparsed);
 
     // Attach the exception-specification to the method.
-    Actions.actOnDelayedExceptionSpecification(LM.Method, EST,
+    Actions.ExceptionSpec().actOnDelayedExceptionSpecification(LM.Method, EST,
                                                SpecificationRange,
                                                DynamicExceptions,
                                                DynamicExceptionRanges,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 8405b44685ae4..b1c4f68ac4901 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -29,6 +29,7 @@
 #include "clang/Sema/SemaCUDA.h"
 #include "clang/Sema/SemaCodeCompletion.h"
 #include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/SemaOpenMP.h"
 #include "llvm/ADT/SmallSet.h"
@@ -7531,7 +7532,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
       // delayed (even if this is a friend declaration).
       bool Delayed = D.getContext() == DeclaratorContext::Member &&
                      D.isFunctionDeclaratorAFunctionDeclaration();
-      if (Delayed && Actions.isLibstdcxxEagerExceptionSpecHack(D) &&
+      if (Delayed && Actions.ExceptionSpec().isLibstdcxxEagerExceptionSpecHack(D) &&
           GetLookAheadToken(0).is(tok::kw_noexcept) &&
           GetLookAheadToken(1).is(tok::l_paren) &&
           GetLookAheadToken(2).is(tok::kw_noexcept) &&
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 5eaec2b621e6f..e51252cdeddb3 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -28,6 +28,7 @@
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/SemaCodeCompletion.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/TimeProfiler.h"
 #include <optional>
@@ -4139,7 +4140,7 @@ ExceptionSpecificationType Parser::tryParseExceptionSpecification(
     T.consumeClose();
     if (!NoexceptExpr.isInvalid()) {
       NoexceptExpr =
-          Actions.ActOnNoexceptSpec(NoexceptExpr.get(), NoexceptType);
+          Actions.ExceptionSpec().ActOnNoexceptSpec(NoexceptExpr.get(), NoexceptType);
       NoexceptRange = SourceRange(KeywordLoc, T.getCloseLocation());
     } else {
       NoexceptType = EST_BasicNoexcept;
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index b9d0b59ef1db7..3e1ec7a378acd 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -43,6 +43,7 @@
 #include "clang/Basic/SourceManager.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/BitVector.h"
@@ -331,7 +332,7 @@ static bool throwEscapes(Sema &S, const CXXThrowExpr *E, CFGBlock &ThrowBlock,
         QualType Caught = Catch->getCaughtType();
         if (Caught.isNull() || // catch (...) catches everything
             !E->getSubExpr() || // throw; is considered cuaght by any handler
-            S.handlerCanCatch(Caught, E->getSubExpr()->getType()))
+            S.ExceptionSpec().handlerCanCatch(Caught, E->getSubExpr()->getType()))
           // Exception doesn't escape via this path.
           break;
       } else {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index f847c49920cf3..dc8547d36a641 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -44,6 +44,7 @@
 #include "clang/Sema/SemaCUDA.h"
 #include "clang/Sema/SemaCodeCompletion.h"
 #include "clang/Sema/SemaConsumer.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaHLSL.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
@@ -206,6 +207,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
       CodeCompletionPtr(
           std::make_unique<SemaCodeCompletion>(*this, CodeCompleter)),
       CUDAPtr(std::make_unique<SemaCUDA>(*this)),
+      ExceptionSpecPtr(std::make_unique<SemaExceptionSpec>(*this)),
       HLSLPtr(std::make_unique<SemaHLSL>(*this)),
       ObjCPtr(std::make_unique<SemaObjC>(*this)),
       OpenACCPtr(std::make_unique<SemaOpenACC>(*this)),
@@ -1140,7 +1142,7 @@ void Sema::ActOnEndOfTranslationUnit() {
     if (LateTemplateParserCleanup)
       LateTemplateParserCleanup(OpaqueParser);
 
-    CheckDelayedMemberExceptionSpecs();
+    ExceptionSpec().CheckDelayedMemberExceptionSpecs();
   } else {
     // If we are building a TU prefix for serialization, it is safe to transfer
     // these over, even though they are not parsed. The end of the TU should be
@@ -1164,8 +1166,8 @@ void Sema::ActOnEndOfTranslationUnit() {
 
   // All delayed member exception specs should be checked or we end up accepting
   // incompatible declarations.
-  assert(DelayedOverridingExceptionSpecChecks.empty());
-  assert(DelayedEquivalentExceptionSpecChecks.empty());
+  assert(ExceptionSpec().DelayedOverridingExceptionSpecChecks.empty());
+  assert(ExceptionSpec().DelayedEquivalentExceptionSpecChecks.empty());
 
   // All dllexport classes should have been processed already.
   assert(DelayedDllExportClasses.empty());
@@ -2190,7 +2192,7 @@ static void checkEscapingByref(VarDecl *VD, Sema &S) {
   if (!Result.isInvalid()) {
     Result = S.MaybeCreateExprWithCleanups(Result);
     Expr *Init = Result.getAs<Expr>();
-    S.Context.setBlockVarCopyInit(VD, Init, S.canThrow(Init));
+    S.Context.setBlockVarCopyInit(VD, Init, S.ExceptionSpec().canThrow(Init));
   }
 
   // The destructor's exception specification is needed when IRGen generates
@@ -2198,7 +2200,7 @@ static void checkEscapingByref(VarDecl *VD, Sema &S) {
   if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
     if (CXXDestructorDecl *DD = RD->getDestructor()) {
       auto *FPT = DD->getType()->castAs<FunctionProtoType>();
-      S.ResolveExceptionSpec(Loc, FPT);
+      S.ExceptionSpec().ResolveExceptionSpec(Loc, FPT);
     }
 }
 
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 81334c817b2af..41eb97a391857 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -25,6 +25,7 @@
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Overload.h"
 #include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "llvm/ADT/SmallSet.h"
 
@@ -614,7 +615,7 @@ static void checkNoThrow(Sema &S, const Stmt *E,
   auto checkDeclNoexcept = [&](const Decl *D, bool IsDtor = false) {
     // In the case of dtor, the call to dtor is implicit and hence we should
     // pass nullptr to canCalleeThrow.
-    if (Sema::canCalleeThrow(S, IsDtor ? nullptr : cast<Expr>(E), D)) {
+    if (SemaExceptionSpec::canCalleeThrow(S, IsDtor ? nullptr : cast<Expr>(E), D)) {
       if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
         // co_await promise.final_suspend() could end up calling
         // __builtin_coro_resume for symmetric transfer if await_suspend()
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index f2b9202255cd4..2d9fd6a3a614d 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -46,6 +46,7 @@
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaHLSL.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
@@ -4036,7 +4037,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S,
     // types again after this. Because this updates the type, we do this before
     // any of the other checks below, which may update the "de facto" NewQType
     // but do not necessarily update the type of New.
-    if (CheckEquivalentExceptionSpec(Old, New))
+    if (ExceptionSpec().CheckEquivalentExceptionSpec(Old, New))
       return true;
 
     // C++11 [dcl.attr.noreturn]p1:
@@ -4415,7 +4416,7 @@ void Sema::MergeVarDeclTypes(VarDecl *New, VarDecl *Old,
       return;
     } else if (Context.hasSameType(New->getType(), Old->getType())) {
       // These could still be something that needs exception specs checked.
-      return MergeVarDeclExceptionSpecs(New, Old);
+      return ExceptionSpec().MergeVarDeclExceptionSpecs(New, Old);
     }
     // C++ [basic.link]p10:
     //   [...] the types specified by all declarations referring to a given
@@ -8995,7 +8996,7 @@ bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
         MD->addOverriddenMethod(BaseMD);
         CheckOverridingFunctionReturnType(MD, BaseMD);
         CheckOverridingFunctionAttributes(MD, BaseMD);
-        CheckOverridingFunctionExceptionSpec(MD, BaseMD);
+        ExceptionSpec().CheckOverridingFunctionExceptionSpec(MD, BaseMD);
         CheckIfOverriddenFunctionIsMarkedFinal(MD, BaseMD);
       }
 
@@ -9365,7 +9366,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
       // now. FIXME: It'd be nice to be able to create the right type to start
       // with, but the type needs to reference the destructor declaration.
       if (SemaRef.getLangOpts().CPlusPlus11)
-        SemaRef.AdjustDestructorExceptionSpec(NewDD);
+        SemaRef.ExceptionSpec().AdjustDestructorExceptionSpec(NewDD);
 
       IsVirtualOkay = true;
       return NewDD;
@@ -11331,7 +11332,7 @@ bool Sema::areMultiversionVariantFunctionsCompatible(
     if (!CLinkageMayDiffer && OldFD->isExternC() != NewFD->isExternC())
       return Diag(DiffDiagIDAt.first, DiffDiagIDAt.second) << LanguageLinkage;
 
-    if (CheckEquivalentExceptionSpec(
+    if (ExceptionSpec().CheckEquivalentExceptionSpec(
             OldFD->getType()->getAs<FunctionProtoType>(), OldFD->getLocation(),
             NewFD->getType()->getAs<FunctionProtoType>(), NewFD->getLocation()))
       return true;
@@ -15812,7 +15813,7 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
 
   // Ensure that the function's exception specification is instantiated.
   if (const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>())
-    ResolveExceptionSpec(D->getLocation(), FPT);
+    ExceptionSpec().ResolveExceptionSpec(D->getLocation(), FPT);
 
   // dllimport cannot be applied to non-inline function definitions.
   if (FD->hasAttr<DLLImportAttr>() && !FD->isInlined() &&
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 8225381985052..8e7f07d7efedf 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -43,6 +43,7 @@
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/SemaOpenMP.h"
@@ -191,7 +192,7 @@ Sema::ImplicitExceptionSpecification::CalledDecl(SourceLocation CallLoc,
 
   const FunctionProtoType *Proto
     = Method->getType()->getAs<FunctionProtoType>();
-  Proto = Self->ResolveExceptionSpec(CallLoc, Proto);
+  Proto = Self->ExceptionSpec().ResolveExceptionSpec(CallLoc, Proto);
   if (!Proto)
     return;
 
@@ -276,7 +277,7 @@ void Sema::ImplicitExceptionSpecification::CalledStmt(Stmt *S) {
   // implicit definition. For now, we assume that any non-nothrow expression can
   // throw any exception.
 
-  if (Self->canThrow(S))
+  if (Self->ExceptionSpec().canThrow(S))
     ComputedEST = EST_None;
 }
 
@@ -1607,48 +1608,6 @@ void Sema::CheckCompleteDecompositionDeclaration(DecompositionDecl *DD) {
     DD->setInvalidDecl();
 }
 
-/// 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 (!getLangOpts().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->castAs<ReferenceType>()->getPointeeType();
-  } else if (const PointerType *P = NewType->getAs<PointerType>()) {
-    NewType = P->getPointeeType();
-    OldType = OldType->castAs<PointerType>()->getPointeeType();
-  } else if (const MemberPointerType *M = NewType->getAs<MemberPointerType>()) {
-    NewType = M->getPointeeType();
-    OldType = OldType->castAs<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].
@@ -7681,7 +7640,7 @@ void Sema::EvaluateImplicitExceptionSpec(SourceLocation Loc, FunctionDecl *FD) {
   auto ESI = IES.getExceptionSpec();
 
   // Update the type of the special member to use it.
-  UpdateExceptionSpec(FD, ESI);
+  ExceptionSpec().UpdateExceptionSpec(FD, ESI);
 }
 
 void Sema::CheckExplicitlyDefaultedFunction(Scope *S, FunctionDecl *FD) {
@@ -9221,7 +9180,7 @@ void Sema::DefineDefaultedComparison(SourceLocation UseLoc, FunctionDecl *FD,
 
   // The exception specification is needed because we are defining the
   // function. Note that this will reuse the body we just built.
-  ResolveExceptionSpec(UseLoc, FD->getType()->castAs<FunctionProtoType>());
+  ExceptionSpec().ResolveExceptionSpec(UseLoc, FD->getType()->castAs<FunctionProtoType>());
 
   if (ASTMutationListener *L = getASTMutationListener())
     L->CompletedImplicitDefinition(FD);
@@ -9271,24 +9230,6 @@ ComputeDefaultedComparisonExceptionSpec(Sema &S, SourceLocation Loc,
   return ExceptSpec;
 }
 
-void Sema::CheckDelayedMemberExceptionSpecs() {
-  decltype(DelayedOverridingExceptionSpecChecks) Overriding;
-  decltype(DelayedEquivalentExceptionSpecChecks) Equivalent;
-
-  std::swap(Overriding, DelayedOverridingExceptionSpecChecks);
-  std::swap(Equivalent, DelayedEquivalentExceptionSpecChecks);
-
-  // Perform any deferred checking of exception specifications for virtual
-  // destructors.
-  for (auto &Check : Overriding)
-    CheckOverridingFunctionExceptionSpec(Check.first, Check.second);
-
-  // Perform any deferred checking of exception specifications for befriended
-  // special members.
-  for (auto &Check : Equivalent)
-    CheckEquivalentExceptionSpec(Check.second, Check.first);
-}
-
 namespace {
 /// CRTP base class for visiting operations performed by a special member
 /// function (or inherited constructor).
@@ -14112,7 +14053,7 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ResolveExceptionSpec(CurrentLocation,
+  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
                        Constructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
@@ -14139,7 +14080,7 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,
 
 void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) {
   // Perform any delayed checks on exception specifications.
-  CheckDelayedMemberExceptionSpecs();
+  ExceptionSpec().CheckDelayedMemberExceptionSpecs();
 }
 
 /// Find or create the fake constructor we synthesize to model constructing an
@@ -14252,7 +14193,7 @@ void Sema::DefineInheritingConstructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ResolveExceptionSpec(CurrentLocation,
+  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
                        Constructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
@@ -14401,7 +14342,7 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ResolveExceptionSpec(CurrentLocation,
+  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
                        Destructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
@@ -14452,8 +14393,8 @@ void Sema::ActOnFinishCXXMemberDecls() {
   // If the context is an invalid C++ class, just suppress these checks.
   if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(CurContext)) {
     if (Record->isInvalidDecl()) {
-      DelayedOverridingExceptionSpecChecks.clear();
-      DelayedEquivalentExceptionSpecChecks.clear();
+      ExceptionSpec().DelayedOverridingExceptionSpecChecks.clear();
+      ExceptionSpec().DelayedEquivalentExceptionSpecChecks.clear();
       return;
     }
     checkForMultipleExportedDefaultConstructors(*this, Record);
@@ -14490,37 +14431,6 @@ void Sema::referenceDLLExportedClassMethods() {
   }
 }
 
-void Sema::AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor) {
-  assert(getLangOpts().CPlusPlus11 &&
-         "adjusting dtor exception specs was introduced in c++11");
-
-  if (Destructor->isDependentContext())
-    return;
-
-  // C++11 [class.dtor]p3:
-  //   A declaration of a destructor that does not have an exception-
-  //   specification is implicitly considered to have the same exception-
-  //   specification as an implicit declaration.
-  const auto *DtorType = Destructor->getType()->castAs<FunctionProtoType>();
-  if (DtorType->hasExceptionSpec())
-    return;
-
-  // Replace the destructor's type, building off the existing one. Fortunately,
-  // the only thing of interest in the destructor type is its extended info.
-  // The return and arguments are fixed.
-  FunctionProtoType::ExtProtoInfo EPI = DtorType->getExtProtoInfo();
-  EPI.ExceptionSpec.Type = EST_Unevaluated;
-  EPI.ExceptionSpec.SourceDecl = Destructor;
-  Destructor->setType(
-      Context.getFunctionType(Context.VoidTy, std::nullopt, EPI));
-
-  // FIXME: If the destructor has a body that could throw, and the newly created
-  // spec doesn't allow exceptions, we should emit a warning, because this
-  // change in behavior can break conforming C++03 programs at runtime.
-  // However, we don't have a body or an exception specification yet, so it
-  // needs to be done somewhere else.
-}
-
 namespace {
 /// An abstract base class for all helper classes used in building the
 //  copy/move operators. These classes serve as factory functions and help us
@@ -15104,7 +15014,7 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ResolveExceptionSpec(CurrentLocation,
+  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
                        CopyAssignOperator->getType()->castAs<FunctionProtoType>());
 
   // Add a context note for diagnostics produced after this point.
@@ -15501,7 +15411,7 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ResolveExceptionSpec(CurrentLocation,
+  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
                        MoveAssignOperator->getType()->castAs<FunctionProtoType>());
 
   // Add a context note for diagnostics produced after this point.
@@ -15814,7 +15724,7 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ResolveExceptionSpec(CurrentLocation,
+  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
                        CopyConstructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
@@ -15953,7 +15863,7 @@ void Sema::DefineImplicitMoveConstructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ResolveExceptionSpec(CurrentLocation,
+  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
                        MoveConstructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
@@ -18781,7 +18691,7 @@ bool Sema::DefineUsedVTables() {
     // if we are not providing an authoritative form of the vtable in this TU.
     // We may choose to emit it available_externally anyway.
     if (!DefineVTable) {
-      MarkVirtualMemberExceptionSpecsNeeded(Loc, Class);
+      ExceptionSpec().MarkVirtualMemberExceptionSpecsNeeded(Loc, Class);
       continue;
     }
 
@@ -18811,13 +18721,6 @@ bool Sema::DefineUsedVTables() {
   return DefinedAnything;
 }
 
-void Sema::MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc,
-                                                 const CXXRecordDecl *RD) {
-  for (const auto *I : RD->methods())
-    if (I->isVirtual() && !I->isPureVirtual())
-      ResolveExceptionSpec(Loc, I->getType()->castAs<FunctionProtoType>());
-}
-
 void Sema::MarkVirtualMembersReferenced(SourceLocation Loc,
                                         const CXXRecordDecl *RD,
                                         bool ConstexprOnly) {
@@ -19074,92 +18977,6 @@ bool Sema::checkThisInStaticMemberFunctionAttributes(CXXMethodDecl *Method) {
   return false;
 }
 
-void Sema::checkExceptionSpecification(
-    bool IsTopLevel, ExceptionSpecificationType EST,
-    ArrayRef<ParsedType> DynamicExceptions,
-    ArrayRef<SourceRange> DynamicExceptionRanges, Expr *NoexceptExpr,
-    SmallVectorImpl<QualType> &Exceptions,
-    FunctionProtoType::ExceptionSpecInfo &ESI) {
-  Exceptions.clear();
-  ESI.Type = EST;
-  if (EST == EST_Dynamic) {
-    Exceptions.reserve(DynamicExceptions.size());
-    for (unsigned ei = 0, ee = DynamicExceptions.size(); ei != ee; ++ei) {
-      // FIXME: Preserve type source info.
-      QualType ET = GetTypeFromParser(DynamicExceptions[ei]);
-
-      if (IsTopLevel) {
-        SmallVector<UnexpandedParameterPack, 2> Unexpanded;
-        collectUnexpandedParameterPacks(ET, Unexpanded);
-        if (!Unexpanded.empty()) {
-          DiagnoseUnexpandedParameterPacks(
-              DynamicExceptionRanges[ei].getBegin(), UPPC_ExceptionType,
-              Unexpanded);
-          continue;
-        }
-      }
-
-      // Check that the type is valid for an exception spec, and
-      // drop it if not.
-      if (!CheckSpecifiedExceptionType(ET, DynamicExceptionRanges[ei]))
-        Exceptions.push_back(ET);
-    }
-    ESI.Exceptions = Exceptions;
-    return;
-  }
-
-  if (isComputedNoexcept(EST)) {
-    assert((NoexceptExpr->isTypeDependent() ||
-            NoexceptExpr->getType()->getCanonicalTypeUnqualified() ==
-            Context.BoolTy) &&
-           "Parser should have made sure that the expression is boolean");
-    if (IsTopLevel && DiagnoseUnexpandedParameterPack(NoexceptExpr)) {
-      ESI.Type = EST_BasicNoexcept;
-      return;
-    }
-
-    ESI.NoexceptExpr = NoexceptExpr;
-    return;
-  }
-}
-
-void Sema::actOnDelayedExceptionSpecification(
-    Decl *D, ExceptionSpecificationType EST, SourceRange SpecificationRange,
-    ArrayRef<ParsedType> DynamicExceptions,
-    ArrayRef<SourceRange> DynamicExceptionRanges, Expr *NoexceptExpr) {
-  if (!D)
-    return;
-
-  // Dig out the function we're referring to.
-  if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(D))
-    D = FTD->getTemplatedDecl();
-
-  FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
-  if (!FD)
-    return;
-
-  // Check the exception specification.
-  llvm::SmallVector<QualType, 4> Exceptions;
-  FunctionProtoType::ExceptionSpecInfo ESI;
-  checkExceptionSpecification(/*IsTopLevel=*/true, EST, DynamicExceptions,
-                              DynamicExceptionRanges, NoexceptExpr, Exceptions,
-                              ESI);
-
-  // Update the exception specification on the function type.
-  Context.adjustExceptionSpec(FD, ESI, /*AsWritten=*/true);
-
-  if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
-    if (MD->isStatic())
-      checkThisInStaticMemberFunctionExceptionSpec(MD);
-
-    if (MD->isVirtual()) {
-      // Check overrides, which we previously had to delay.
-      for (const CXXMethodDecl *O : MD->overridden_methods())
-        CheckOverridingFunctionExceptionSpec(MD, O);
-    }
-  }
-}
-
 /// HandleMSProperty - Analyze a __delcspec(property) field of a C++ class.
 ///
 MSPropertyDecl *Sema::HandleMSProperty(Scope *S, RecordDecl *Record,
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 41bf273d12f2f..d8548ba8cbc79 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -10,7 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/Sema/SemaInternal.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/Expr.h"
@@ -19,6 +19,9 @@
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaInternal.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallString.h"
 #include <optional>
@@ -42,8 +45,8 @@ static const FunctionProtoType *GetUnderlyingFunction(QualType T)
 /// we're in such a case and turns off delay-parsing of exception
 /// specifications. Libstdc++ 6.1 (released 2016-04-27) appears to have
 /// resolved it as side-effect of commit ddb63209a8d (2015-06-05).
-bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
-  auto *RD = dyn_cast<CXXRecordDecl>(CurContext);
+bool SemaExceptionSpec::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
+  auto *RD = dyn_cast<CXXRecordDecl>(SemaRef.CurContext);
 
   // All the problem cases are member functions named "swap" within class
   // templates declared directly within namespace std or std::__debug or
@@ -67,7 +70,7 @@ bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
   }
 
   // Only apply this hack within a system header.
-  if (!Context.getSourceManager().isInSystemHeader(D.getBeginLoc()))
+  if (!SemaRef.Context.getSourceManager().isInSystemHeader(D.getBeginLoc()))
     return false;
 
   return llvm::StringSwitch<bool>(RD->getIdentifier()->getName())
@@ -79,9 +82,9 @@ bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
       .Default(false);
 }
 
-ExprResult Sema::ActOnNoexceptSpec(Expr *NoexceptExpr,
+ExprResult SemaExceptionSpec::ActOnNoexceptSpec(Expr *NoexceptExpr,
                                    ExceptionSpecificationType &EST) {
-
+  ASTContext &Context = getASTContext();
   if (NoexceptExpr->isTypeDependent() ||
       NoexceptExpr->containsUnexpandedParameterPack()) {
     EST = EST_DependentNoexcept;
@@ -89,8 +92,8 @@ ExprResult Sema::ActOnNoexceptSpec(Expr *NoexceptExpr,
   }
 
   llvm::APSInt Result;
-  ExprResult Converted = CheckConvertedConstantExpression(
-      NoexceptExpr, Context.BoolTy, Result, CCEK_Noexcept);
+  ExprResult Converted = SemaRef.CheckConvertedConstantExpression(
+      NoexceptExpr, Context.BoolTy, Result, Sema::CCEK_Noexcept);
 
   if (Converted.isInvalid()) {
     EST = EST_NoexceptFalse;
@@ -118,7 +121,7 @@ ExprResult Sema::ActOnNoexceptSpec(Expr *NoexceptExpr,
 ///
 /// \param[in,out] T  The exception type. This will be decayed to a pointer type
 ///                   when the input is an array or a function type.
-bool Sema::CheckSpecifiedExceptionType(QualType &T, SourceRange Range) {
+bool SemaExceptionSpec::CheckSpecifiedExceptionType(QualType &T, SourceRange Range) {
   // C++11 [except.spec]p2:
   //   A type cv T, "array of T", or "function returning T" denoted
   //   in an exception-specification is adjusted to type T, "pointer to T", or
@@ -126,9 +129,9 @@ bool Sema::CheckSpecifiedExceptionType(QualType &T, SourceRange Range) {
   //
   // We also apply this rule in C++98.
   if (T->isArrayType())
-    T = Context.getArrayDecayedType(T);
+    T = SemaRef.Context.getArrayDecayedType(T);
   else if (T->isFunctionType())
-    T = Context.getPointerType(T);
+    T = SemaRef.Context.getPointerType(T);
 
   int Kind = 0;
   QualType PointeeT = T;
@@ -169,7 +172,7 @@ bool Sema::CheckSpecifiedExceptionType(QualType &T, SourceRange Range) {
   }
   if (!(PointeeT->isRecordType() &&
         PointeeT->castAs<RecordType>()->isBeingDefined()) &&
-      RequireCompleteType(Range.getBegin(), PointeeT, DiagID, Kind, Range))
+      SemaRef.RequireCompleteType(Range.getBegin(), PointeeT, DiagID, Kind, Range))
     return ReturnValueOnError;
 
   // WebAssembly reference types can't be used in exception specifications.
@@ -192,7 +195,7 @@ bool Sema::CheckSpecifiedExceptionType(QualType &T, SourceRange Range) {
 /// CheckDistantExceptionSpec - Check if the given type is a pointer or pointer
 /// to member to a function with an exception specification. This means that
 /// it is invalid to add another level of indirection.
-bool Sema::CheckDistantExceptionSpec(QualType T) {
+bool SemaExceptionSpec::CheckDistantExceptionSpec(QualType T) {
   // C++17 removes this rule in favor of putting exception specifications into
   // the type system.
   if (getLangOpts().CPlusPlus17)
@@ -213,7 +216,7 @@ bool Sema::CheckDistantExceptionSpec(QualType T) {
 }
 
 const FunctionProtoType *
-Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
+SemaExceptionSpec::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
   if (FPT->getExceptionSpecType() == EST_Unparsed) {
     Diag(Loc, diag::err_exception_spec_not_parsed);
     return nullptr;
@@ -232,9 +235,9 @@ Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
 
   // Compute or instantiate the exception specification now.
   if (SourceFPT->getExceptionSpecType() == EST_Unevaluated)
-    EvaluateImplicitExceptionSpec(Loc, SourceDecl);
+    SemaRef.EvaluateImplicitExceptionSpec(Loc, SourceDecl);
   else
-    InstantiateExceptionSpec(Loc, SourceDecl);
+    SemaRef.InstantiateExceptionSpec(Loc, SourceDecl);
 
   const FunctionProtoType *Proto =
     SourceDecl->getType()->castAs<FunctionProtoType>();
@@ -246,15 +249,15 @@ Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
 }
 
 void
-Sema::UpdateExceptionSpec(FunctionDecl *FD,
+SemaExceptionSpec::UpdateExceptionSpec(FunctionDecl *FD,
                           const FunctionProtoType::ExceptionSpecInfo &ESI) {
   // If we've fully resolved the exception specification, notify listeners.
   if (!isUnresolvedExceptionSpec(ESI.Type))
-    if (auto *Listener = getASTMutationListener())
+    if (auto *Listener = SemaRef.getASTMutationListener())
       Listener->ResolvedExceptionSpec(FD);
 
   for (FunctionDecl *Redecl : FD->redecls())
-    Context.adjustExceptionSpec(Redecl, ESI);
+    SemaRef.Context.adjustExceptionSpec(Redecl, ESI);
 }
 
 static bool exceptionSpecNotKnownYet(const FunctionDecl *FD) {
@@ -296,7 +299,7 @@ static bool hasImplicitExceptionSpec(FunctionDecl *Decl) {
   return !Ty->hasExceptionSpec();
 }
 
-bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
+bool SemaExceptionSpec::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   // Just completely ignore this under -fno-exceptions prior to C++17.
   // In C++17 onwards, the exception specification is part of the type and
   // we will diagnose mismatches anyway, so it's better to check for them here.
@@ -326,7 +329,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   // Check the types as written: they must match before any exception
   // specification adjustment is applied.
   if (!CheckEquivalentExceptionSpecImpl(
-        *this, PDiag(DiagID), PDiag(diag::note_previous_declaration),
+        SemaRef, SemaRef.PDiag(DiagID), SemaRef.PDiag(diag::note_previous_declaration),
         Old->getType()->getAs<FunctionProtoType>(), Old->getLocation(),
         New->getType()->getAs<FunctionProtoType>(), New->getLocation(),
         &MissingExceptionSpecification, &MissingEmptyExceptionSpecification,
@@ -362,10 +365,10 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   // Likewise if the old function is a builtin.
   if (MissingEmptyExceptionSpecification &&
       (Old->getLocation().isInvalid() ||
-       Context.getSourceManager().isInSystemHeader(Old->getLocation()) ||
+       SemaRef.Context.getSourceManager().isInSystemHeader(Old->getLocation()) ||
        Old->getBuiltinID()) &&
       Old->isExternC()) {
-    New->setType(Context.getFunctionType(
+    New->setType(SemaRef.Context.getFunctionType(
         NewProto->getReturnType(), NewProto->getParamTypes(),
         NewProto->getExtProtoInfo().withExceptionSpec(EST_DynamicNone)));
     return false;
@@ -392,7 +395,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   } else {
     // Update the type of the function with the appropriate exception
     // specification.
-    New->setType(Context.getFunctionType(
+    New->setType(SemaRef.Context.getFunctionType(
         NewProto->getReturnType(), NewProto->getParamTypes(),
         NewProto->getExtProtoInfo().withExceptionSpec(ESI)));
   }
@@ -437,7 +440,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
       else
         OS << ", ";
 
-      OS << E.getAsString(getPrintingPolicy());
+      OS << E.getAsString(SemaRef.getPrintingPolicy());
     }
     OS << ")";
     break;
@@ -452,7 +455,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
   case EST_NoexceptTrue:
     OS << "noexcept(";
     assert(OldProto->getNoexceptExpr() != nullptr && "Expected non-null Expr");
-    OldProto->getNoexceptExpr()->printPretty(OS, nullptr, getPrintingPolicy());
+    OldProto->getNoexceptExpr()->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
     OS << ")";
     break;
   case EST_NoThrow:
@@ -473,7 +476,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
     // location when there is a trailing return type.
     if (auto FTLoc = TL.getAs<FunctionProtoTypeLoc>())
       if (!FTLoc.getTypePtr()->hasTrailingReturn())
-        FixItLoc = getLocForEndOfToken(FTLoc.getLocalRangeEnd());
+        FixItLoc = SemaRef.getLocForEndOfToken(FTLoc.getLocalRangeEnd());
   }
 
   if (FixItLoc.isInvalid())
@@ -495,7 +498,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
 /// 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(
+bool SemaExceptionSpec::CheckEquivalentExceptionSpec(
     const FunctionProtoType *Old, SourceLocation OldLoc,
     const FunctionProtoType *New, SourceLocation NewLoc) {
   if (!getLangOpts().CXXExceptions)
@@ -505,7 +508,7 @@ bool Sema::CheckEquivalentExceptionSpec(
   if (getLangOpts().MSVCCompat)
     DiagID = diag::ext_mismatched_exception_spec;
   bool Result = CheckEquivalentExceptionSpecImpl(
-      *this, PDiag(DiagID), PDiag(diag::note_previous_declaration),
+      SemaRef, SemaRef.PDiag(DiagID), SemaRef.PDiag(diag::note_previous_declaration),
       Old, OldLoc, New, NewLoc);
 
   // In Microsoft mode, mismatching exception specifications just cause a warning.
@@ -533,10 +536,10 @@ static bool CheckEquivalentExceptionSpecImpl(
   if (MissingEmptyExceptionSpecification)
     *MissingEmptyExceptionSpecification = false;
 
-  Old = S.ResolveExceptionSpec(NewLoc, Old);
+  Old = S.ExceptionSpec().ResolveExceptionSpec(NewLoc, Old);
   if (!Old)
     return false;
-  New = S.ResolveExceptionSpec(NewLoc, New);
+  New = S.ExceptionSpec().ResolveExceptionSpec(NewLoc, New);
   if (!New)
     return false;
 
@@ -671,7 +674,7 @@ static bool CheckEquivalentExceptionSpecImpl(
   return true;
 }
 
-bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
+bool SemaExceptionSpec::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
                                         const PartialDiagnostic &NoteID,
                                         const FunctionProtoType *Old,
                                         SourceLocation OldLoc,
@@ -679,11 +682,12 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
                                         SourceLocation NewLoc) {
   if (!getLangOpts().CXXExceptions)
     return false;
-  return CheckEquivalentExceptionSpecImpl(*this, DiagID, NoteID, Old, OldLoc,
+  return CheckEquivalentExceptionSpecImpl(SemaRef, DiagID, NoteID, Old, OldLoc,
                                           New, NewLoc);
 }
 
-bool Sema::handlerCanCatch(QualType HandlerType, QualType ExceptionType) {
+bool SemaExceptionSpec::handlerCanCatch(QualType HandlerType, QualType ExceptionType) {
+  ASTContext &Context = getASTContext();
   // [except.handle]p3:
   //   A handler is a match for an exception object of type E if:
 
@@ -717,9 +721,9 @@ bool Sema::handlerCanCatch(QualType HandlerType, QualType ExceptionType) {
     QualType Result;
     // FIXME: Should we treat the exception as catchable if a lifetime
     // conversion is required?
-    if (IsQualificationConversion(ExceptionType, HandlerType, false,
+    if (SemaRef.IsQualificationConversion(ExceptionType, HandlerType, false,
                                   LifetimeConv) ||
-        IsFunctionConversion(ExceptionType, HandlerType, Result))
+        SemaRef.IsFunctionConversion(ExceptionType, HandlerType, Result))
       return true;
 
     //    -- a standard pointer conversion [...]
@@ -747,21 +751,21 @@ bool Sema::handlerCanCatch(QualType HandlerType, QualType ExceptionType) {
     return false;
   CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
                      /*DetectVirtual=*/false);
-  if (!IsDerivedFrom(SourceLocation(), ExceptionType, HandlerType, Paths) ||
+  if (!SemaRef.IsDerivedFrom(SourceLocation(), ExceptionType, HandlerType, Paths) ||
       Paths.isAmbiguous(Context.getCanonicalType(HandlerType)))
     return false;
 
   // Do this check from a context without privileges.
-  switch (CheckBaseClassAccess(SourceLocation(), HandlerType, ExceptionType,
+  switch (SemaRef.CheckBaseClassAccess(SourceLocation(), HandlerType, ExceptionType,
                                Paths.front(),
                                /*Diagnostic*/ 0,
                                /*ForceCheck*/ true,
                                /*ForceUnprivileged*/ true)) {
-  case AR_accessible: return true;
-  case AR_inaccessible: return false;
-  case AR_dependent:
+  case Sema::AR_accessible: return true;
+  case Sema::AR_inaccessible: return false;
+  case Sema::AR_dependent:
     llvm_unreachable("access check dependent for unprivileged context");
-  case AR_delayed:
+  case Sema::AR_delayed:
     llvm_unreachable("access check delayed in non-declaration");
   }
   llvm_unreachable("unexpected access check result");
@@ -770,7 +774,7 @@ bool Sema::handlerCanCatch(QualType HandlerType, QualType ExceptionType) {
 /// CheckExceptionSpecSubset - Check whether the second function type's
 /// exception specification is a subset (or equivalent) of the first function
 /// type. This is used by override and pointer assignment checks.
-bool Sema::CheckExceptionSpecSubset(
+bool SemaExceptionSpec::CheckExceptionSpecSubset(
     const PartialDiagnostic &DiagID, const PartialDiagnostic &NestedDiagID,
     const PartialDiagnostic &NoteID, const PartialDiagnostic &NoThrowDiagID,
     const FunctionProtoType *Superset, bool SkipSupersetFirstParameter,
@@ -886,7 +890,7 @@ CheckSpecForTypesEquivalent(Sema &S, const PartialDiagnostic &DiagID,
   if (!SFunc)
     return false;
 
-  return S.CheckEquivalentExceptionSpec(DiagID, NoteID, TFunc, TargetLoc,
+  return S.ExceptionSpec().CheckEquivalentExceptionSpec(DiagID, NoteID, TFunc, TargetLoc,
                                         SFunc, SourceLoc);
 }
 
@@ -895,7 +899,7 @@ CheckSpecForTypesEquivalent(Sema &S, const PartialDiagnostic &DiagID,
 /// assignment and override compatibility check. We do not check the parameters
 /// of parameter function pointers recursively, as no sane programmer would
 /// even be able to write such a function type.
-bool Sema::CheckParamExceptionSpec(
+bool SemaExceptionSpec::CheckParamExceptionSpec(
     const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID,
     const FunctionProtoType *Target, bool SkipTargetFirstParameter,
     SourceLocation TargetLoc, const FunctionProtoType *Source,
@@ -903,7 +907,7 @@ bool Sema::CheckParamExceptionSpec(
   auto RetDiag = DiagID;
   RetDiag << 0;
   if (CheckSpecForTypesEquivalent(
-          *this, RetDiag, PDiag(),
+          SemaRef, RetDiag, SemaRef.PDiag(),
           Target->getReturnType(), TargetLoc, Source->getReturnType(),
           SourceLoc))
     return true;
@@ -917,7 +921,7 @@ bool Sema::CheckParamExceptionSpec(
     auto ParamDiag = DiagID;
     ParamDiag << 1;
     if (CheckSpecForTypesEquivalent(
-            *this, ParamDiag, PDiag(),
+            SemaRef, ParamDiag, SemaRef.PDiag(),
             Target->getParamType(i + (SkipTargetFirstParameter ? 1 : 0)),
             TargetLoc, Source->getParamType(SkipSourceFirstParameter ? 1 : 0),
             SourceLoc))
@@ -926,7 +930,7 @@ bool Sema::CheckParamExceptionSpec(
   return false;
 }
 
-bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) {
+bool SemaExceptionSpec::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) {
   // First we check for applicability.
   // Target type must be a function, function pointer or function reference.
   const FunctionProtoType *ToFunc = GetUnderlyingFunction(ToType);
@@ -960,14 +964,14 @@ bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) {
   //     void (*q)(void (*) throw(int)) = p;
   //   }
   // ... because it might be instantiated with T=int.
-  return CheckExceptionSpecSubset(PDiag(DiagID), PDiag(NestedDiagID), PDiag(),
-                                  PDiag(), ToFunc, 0,
+  return CheckExceptionSpecSubset(SemaRef.PDiag(DiagID), SemaRef.PDiag(NestedDiagID), SemaRef.PDiag(),
+                                  SemaRef.PDiag(), ToFunc, 0,
                                   From->getSourceRange().getBegin(), FromFunc,
                                   0, SourceLocation()) &&
          !getLangOpts().CPlusPlus17;
 }
 
-bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
+bool SemaExceptionSpec::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
                                                 const CXXMethodDecl *Old) {
   // If the new exception specification hasn't been parsed yet, skip the check.
   // We'll get called again once it's been parsed.
@@ -993,9 +997,9 @@ bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
   if (getLangOpts().MSVCCompat)
     DiagID = diag::ext_override_exception_spec;
   return CheckExceptionSpecSubset(
-      PDiag(DiagID), PDiag(diag::err_deep_exception_specs_differ),
-      PDiag(diag::note_overridden_virtual_function),
-      PDiag(diag::ext_override_exception_spec),
+      SemaRef.PDiag(DiagID), SemaRef.PDiag(diag::err_deep_exception_specs_differ),
+      SemaRef.PDiag(diag::note_overridden_virtual_function),
+      SemaRef.PDiag(diag::ext_override_exception_spec),
       Old->getType()->castAs<FunctionProtoType>(),
       Old->hasCXXExplicitFunctionObjectParameter(), Old->getLocation(),
       New->getType()->castAs<FunctionProtoType>(),
@@ -1007,14 +1011,14 @@ static CanThrowResult canSubStmtsThrow(Sema &Self, const Stmt *S) {
   for (const Stmt *SubStmt : S->children()) {
     if (!SubStmt)
       continue;
-    R = mergeCanThrow(R, Self.canThrow(SubStmt));
+    R = mergeCanThrow(R, Self.ExceptionSpec().canThrow(SubStmt));
     if (R == CT_Can)
       break;
   }
   return R;
 }
 
-CanThrowResult Sema::canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
+CanThrowResult SemaExceptionSpec::canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
                                     SourceLocation Loc) {
   // As an extension, we assume that __attribute__((nothrow)) functions don't
   // throw.
@@ -1062,7 +1066,7 @@ CanThrowResult Sema::canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
     return CT_Can;
 
   if (Loc.isValid() || (Loc.isInvalid() && E))
-    FT = S.ResolveExceptionSpec(Loc.isInvalid() ? E->getBeginLoc() : Loc, FT);
+    FT = S.ExceptionSpec().ResolveExceptionSpec(Loc.isInvalid() ? E->getBeginLoc() : Loc, FT);
   if (!FT)
     return CT_Can;
 
@@ -1075,7 +1079,7 @@ static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) {
   // Initialization might throw.
   if (!VD->isUsableInConstantExpressions(Self.Context))
     if (const Expr *Init = VD->getInit())
-      CT = mergeCanThrow(CT, Self.canThrow(Init));
+      CT = mergeCanThrow(CT, Self.ExceptionSpec().canThrow(Init));
 
   // Destructor might throw.
   if (VD->needsDestruction(Self.Context) == QualType::DK_cxx_destructor) {
@@ -1083,7 +1087,7 @@ static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) {
             VD->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl()) {
       if (auto *Dtor = RD->getDestructor()) {
         CT = mergeCanThrow(
-            CT, Sema::canCalleeThrow(Self, nullptr, Dtor, VD->getLocation()));
+            CT, SemaExceptionSpec::canCalleeThrow(Self, nullptr, Dtor, VD->getLocation()));
       }
     }
   }
@@ -1131,7 +1135,7 @@ static CanThrowResult canTypeidThrow(Sema &S, const CXXTypeidExpr *DC) {
   return CT_Can;
 }
 
-CanThrowResult Sema::canThrow(const Stmt *S) {
+CanThrowResult SemaExceptionSpec::canThrow(const Stmt *S) {
   // C++ [expr.unary.noexcept]p3:
   //   [Can throw] if in a potentially-evaluated context the expression would
   //   contain:
@@ -1153,13 +1157,13 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
     CanThrowResult CT = canDynamicCastThrow(CE);
     if (CT == CT_Can)
       return CT;
-    return mergeCanThrow(CT, canSubStmtsThrow(*this, CE));
+    return mergeCanThrow(CT, canSubStmtsThrow(SemaRef, CE));
   }
 
   case Expr::CXXTypeidExprClass:
     //   - a potentially evaluated typeid expression applied to a glvalue
     //     expression whose type is a polymorphic class type
-    return canTypeidThrow(*this, cast<CXXTypeidExpr>(S));
+    return canTypeidThrow(SemaRef, cast<CXXTypeidExpr>(S));
 
     //   - a potentially evaluated call to a function, member function, function
     //     pointer, or member function pointer that does not have a non-throwing
@@ -1175,10 +1179,10 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
     else if (isa<CXXPseudoDestructorExpr>(CE->getCallee()->IgnoreParens()))
       CT = CT_Cannot;
     else
-      CT = canCalleeThrow(*this, CE, CE->getCalleeDecl());
+      CT = canCalleeThrow(SemaRef, CE, CE->getCalleeDecl());
     if (CT == CT_Can)
       return CT;
-    return mergeCanThrow(CT, canSubStmtsThrow(*this, CE));
+    return mergeCanThrow(CT, canSubStmtsThrow(SemaRef, CE));
   }
 
   case Expr::CXXConstructExprClass:
@@ -1187,15 +1191,15 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
     // FIXME: Properly determine whether a variably-modified type can throw.
     if (CE->getType()->isVariablyModifiedType())
       return CT_Can;
-    CanThrowResult CT = canCalleeThrow(*this, CE, CE->getConstructor());
+    CanThrowResult CT = canCalleeThrow(SemaRef, CE, CE->getConstructor());
     if (CT == CT_Can)
       return CT;
-    return mergeCanThrow(CT, canSubStmtsThrow(*this, CE));
+    return mergeCanThrow(CT, canSubStmtsThrow(SemaRef, CE));
   }
 
   case Expr::CXXInheritedCtorInitExprClass: {
     auto *ICIE = cast<CXXInheritedCtorInitExpr>(S);
-    return canCalleeThrow(*this, ICIE, ICIE->getConstructor());
+    return canCalleeThrow(SemaRef, ICIE, ICIE->getConstructor());
   }
 
   case Expr::LambdaExprClass: {
@@ -1215,10 +1219,10 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
     if (NE->isTypeDependent())
       CT = CT_Dependent;
     else
-      CT = canCalleeThrow(*this, NE, NE->getOperatorNew());
+      CT = canCalleeThrow(SemaRef, NE, NE->getOperatorNew());
     if (CT == CT_Can)
       return CT;
-    return mergeCanThrow(CT, canSubStmtsThrow(*this, NE));
+    return mergeCanThrow(CT, canSubStmtsThrow(SemaRef, NE));
   }
 
   case Expr::CXXDeleteExprClass: {
@@ -1228,27 +1232,27 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
     if (DTy.isNull() || DTy->isDependentType()) {
       CT = CT_Dependent;
     } else {
-      CT = canCalleeThrow(*this, DE, DE->getOperatorDelete());
+      CT = canCalleeThrow(SemaRef, DE, DE->getOperatorDelete());
       if (const RecordType *RT = DTy->getAs<RecordType>()) {
         const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
         const CXXDestructorDecl *DD = RD->getDestructor();
         if (DD)
-          CT = mergeCanThrow(CT, canCalleeThrow(*this, DE, DD));
+          CT = mergeCanThrow(CT, canCalleeThrow(SemaRef, DE, DD));
       }
       if (CT == CT_Can)
         return CT;
     }
-    return mergeCanThrow(CT, canSubStmtsThrow(*this, DE));
+    return mergeCanThrow(CT, canSubStmtsThrow(SemaRef, DE));
   }
 
   case Expr::CXXBindTemporaryExprClass: {
     auto *BTE = cast<CXXBindTemporaryExpr>(S);
     // The bound temporary has to be destroyed again, which might throw.
     CanThrowResult CT =
-        canCalleeThrow(*this, BTE, BTE->getTemporary()->getDestructor());
+        canCalleeThrow(SemaRef, BTE, BTE->getTemporary()->getDestructor());
     if (CT == CT_Can)
       return CT;
-    return mergeCanThrow(CT, canSubStmtsThrow(*this, BTE));
+    return mergeCanThrow(CT, canSubStmtsThrow(SemaRef, BTE));
   }
 
   case Expr::PseudoObjectExprClass: {
@@ -1300,7 +1304,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::ConvertVectorExprClass:
   case Expr::VAArgExprClass:
   case Expr::CXXParenListInitExprClass:
-    return canSubStmtsThrow(*this, S);
+    return canSubStmtsThrow(SemaRef, S);
 
   case Expr::CompoundLiteralExprClass:
   case Expr::CXXConstCastExprClass:
@@ -1310,7 +1314,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
       // FIXME: Properly determine whether a variably-modified type can throw.
     if (cast<Expr>(S)->getType()->isVariablyModifiedType())
       return CT_Can;
-    return canSubStmtsThrow(*this, S);
+    return canSubStmtsThrow(SemaRef, S);
 
     // Some might be dependent for other reasons.
   case Expr::ArraySubscriptExprClass:
@@ -1333,7 +1337,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
         return CT_Can;
     CanThrowResult CT =
         cast<Expr>(S)->isTypeDependent() ? CT_Dependent : CT_Cannot;
-    return mergeCanThrow(CT, canSubStmtsThrow(*this, S));
+    return mergeCanThrow(CT, canSubStmtsThrow(SemaRef, S));
   }
 
   case Expr::CXXDefaultArgExprClass:
@@ -1530,13 +1534,13 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Stmt::SEHTryStmtClass:
   case Stmt::SwitchStmtClass:
   case Stmt::WhileStmtClass:
-    return canSubStmtsThrow(*this, S);
+    return canSubStmtsThrow(SemaRef, S);
 
   case Stmt::DeclStmtClass: {
     CanThrowResult CT = CT_Cannot;
     for (const Decl *D : cast<DeclStmt>(S)->decls()) {
       if (auto *VD = dyn_cast<VarDecl>(D))
-        CT = mergeCanThrow(CT, canVarDeclThrow(*this, VD));
+        CT = mergeCanThrow(CT, canVarDeclThrow(SemaRef, VD));
 
       // FIXME: Properly determine whether a variably-modified type can throw.
       if (auto *TND = dyn_cast<TypedefNameDecl>(D))
@@ -1560,7 +1564,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
 
     // For 'if constexpr', consider only the non-discarded case.
     // FIXME: We should add a DiscardedStmt marker to the AST.
-    if (std::optional<const Stmt *> Case = IS->getNondiscardedCase(Context))
+    if (std::optional<const Stmt *> Case = IS->getNondiscardedCase(SemaRef.Context))
       return *Case ? mergeCanThrow(CT, canThrow(*Case)) : CT;
 
     CanThrowResult Then = canThrow(IS->getThen());
@@ -1581,7 +1585,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
     const CXXCatchStmt *FinalHandler = TS->getHandler(TS->getNumHandlers() - 1);
     if (!FinalHandler->getExceptionDecl())
       return canThrow(FinalHandler->getHandlerBlock());
-    return canSubStmtsThrow(*this, S);
+    return canSubStmtsThrow(SemaRef, S);
   }
 
   case Stmt::ObjCAtThrowStmtClass:
@@ -1615,4 +1619,230 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   llvm_unreachable("Bogus StmtClass");
 }
 
+SemaExceptionSpec::SemaExceptionSpec(Sema &S) : SemaBase(S) {}
+
+void SemaExceptionSpec::CheckDelayedMemberExceptionSpecs() {
+  decltype(SemaExceptionSpec::DelayedOverridingExceptionSpecChecks) Overriding;
+  decltype(SemaExceptionSpec::DelayedEquivalentExceptionSpecChecks) Equivalent;
+
+  std::swap(Overriding, DelayedOverridingExceptionSpecChecks);
+  std::swap(Equivalent, DelayedEquivalentExceptionSpecChecks);
+
+  // Perform any deferred checking of exception specifications for virtual
+  // destructors.
+  for (auto &Check : Overriding)
+    CheckOverridingFunctionExceptionSpec(Check.first, Check.second);
+
+  // Perform any deferred checking of exception specifications for befriended
+  // special members.
+  for (auto &Check : Equivalent)
+    CheckEquivalentExceptionSpec(Check.second, Check.first);
+}
+
+/// RAII object to register a defaulted function as having its exception
+/// specification computed.
+struct ComputingExceptionSpec {
+  Sema &S;
+
+  ComputingExceptionSpec(Sema &S, FunctionDecl *FD, SourceLocation Loc)
+      : S(S) {
+    Sema::CodeSynthesisContext Ctx;
+    Ctx.Kind = Sema::CodeSynthesisContext::ExceptionSpecEvaluation;
+    Ctx.PointOfInstantiation = Loc;
+    Ctx.Entity = FD;
+    S.pushCodeSynthesisContext(Ctx);
+  }
+  ~ComputingExceptionSpec() {
+    S.popCodeSynthesisContext();
+  }
+};
+
+void SemaExceptionSpec::checkExceptionSpecification(
+    bool IsTopLevel, ExceptionSpecificationType EST,
+    ArrayRef<ParsedType> DynamicExceptions,
+    ArrayRef<SourceRange> DynamicExceptionRanges, Expr *NoexceptExpr,
+    SmallVectorImpl<QualType> &Exceptions,
+    FunctionProtoType::ExceptionSpecInfo &ESI) {
+  Exceptions.clear();
+  ESI.Type = EST;
+  if (EST == EST_Dynamic) {
+    Exceptions.reserve(DynamicExceptions.size());
+    for (unsigned ei = 0, ee = DynamicExceptions.size(); ei != ee; ++ei) {
+      // FIXME: Preserve type source info.
+      QualType ET = SemaRef.GetTypeFromParser(DynamicExceptions[ei]);
+
+      if (IsTopLevel) {
+        SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+        SemaRef.collectUnexpandedParameterPacks(ET, Unexpanded);
+        if (!Unexpanded.empty()) {
+          SemaRef.DiagnoseUnexpandedParameterPacks(
+              DynamicExceptionRanges[ei].getBegin(), Sema::UPPC_ExceptionType,
+              Unexpanded);
+          continue;
+        }
+      }
+
+      // Check that the type is valid for an exception spec, and
+      // drop it if not.
+      if (!CheckSpecifiedExceptionType(ET, DynamicExceptionRanges[ei]))
+        Exceptions.push_back(ET);
+    }
+    ESI.Exceptions = Exceptions;
+    return;
+  }
+
+  if (isComputedNoexcept(EST)) {
+    assert((NoexceptExpr->isTypeDependent() ||
+            NoexceptExpr->getType()->getCanonicalTypeUnqualified() ==
+            SemaRef.Context.BoolTy) &&
+           "Parser should have made sure that the expression is boolean");
+    if (IsTopLevel && SemaRef.DiagnoseUnexpandedParameterPack(NoexceptExpr)) {
+      ESI.Type = EST_BasicNoexcept;
+      return;
+    }
+
+    ESI.NoexceptExpr = NoexceptExpr;
+    return;
+  }
+}
+
+void SemaExceptionSpec::actOnDelayedExceptionSpecification(
+    Decl *D, ExceptionSpecificationType EST, SourceRange SpecificationRange,
+    ArrayRef<ParsedType> DynamicExceptions,
+    ArrayRef<SourceRange> DynamicExceptionRanges, Expr *NoexceptExpr) {
+  if (!D)
+    return;
+
+  // Dig out the function we're referring to.
+  if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(D))
+    D = FTD->getTemplatedDecl();
+
+  FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
+  if (!FD)
+    return;
+
+  // Check the exception specification.
+  llvm::SmallVector<QualType, 4> Exceptions;
+  FunctionProtoType::ExceptionSpecInfo ESI;
+  checkExceptionSpecification(/*IsTopLevel=*/true, EST, DynamicExceptions,
+                              DynamicExceptionRanges, NoexceptExpr, Exceptions,
+                              ESI);
+
+  // Update the exception specification on the function type.
+  SemaRef.Context.adjustExceptionSpec(FD, ESI, /*AsWritten=*/true);
+
+  if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
+    if (MD->isStatic())
+      SemaRef.checkThisInStaticMemberFunctionExceptionSpec(MD);
+
+    if (MD->isVirtual()) {
+      // Check overrides, which we previously had to delay.
+      for (const CXXMethodDecl *O : MD->overridden_methods())
+        CheckOverridingFunctionExceptionSpec(MD, O);
+    }
+  }
+}
+
+void SemaExceptionSpec::AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor) {
+  assert(getLangOpts().CPlusPlus11 &&
+         "adjusting dtor exception specs was introduced in c++11");
+
+  if (Destructor->isDependentContext())
+    return;
+
+  // C++11 [class.dtor]p3:
+  //   A declaration of a destructor that does not have an exception-
+  //   specification is implicitly considered to have the same exception-
+  //   specification as an implicit declaration.
+  const auto *DtorType = Destructor->getType()->castAs<FunctionProtoType>();
+  if (DtorType->hasExceptionSpec())
+    return;
+
+  // Replace the destructor's type, building off the existing one. Fortunately,
+  // the only thing of interest in the destructor type is its extended info.
+  // The return and arguments are fixed.
+  FunctionProtoType::ExtProtoInfo EPI = DtorType->getExtProtoInfo();
+  EPI.ExceptionSpec.Type = EST_Unevaluated;
+  EPI.ExceptionSpec.SourceDecl = Destructor;
+  Destructor->setType(
+      SemaRef.Context.getFunctionType(SemaRef.Context.VoidTy, std::nullopt, EPI));
+
+  // FIXME: If the destructor has a body that could throw, and the newly created
+  // spec doesn't allow exceptions, we should emit a warning, because this
+  // change in behavior can break conforming C++03 programs at runtime.
+  // However, we don't have a body or an exception specification yet, so it
+  // needs to be done somewhere else.
+}
+
+void SemaExceptionSpec::MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc,
+                                                 const CXXRecordDecl *RD) {
+  for (const auto *I : RD->methods())
+    if (I->isVirtual() && !I->isPureVirtual())
+      ResolveExceptionSpec(Loc, I->getType()->castAs<FunctionProtoType>());
+}
+
+/// 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 SemaExceptionSpec::MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old) {
+  // Shortcut if exceptions are disabled.
+  if (!getLangOpts().CXXExceptions)
+    return;
+
+  assert(SemaRef.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->castAs<ReferenceType>()->getPointeeType();
+  } else if (const PointerType *P = NewType->getAs<PointerType>()) {
+    NewType = P->getPointeeType();
+    OldType = OldType->castAs<PointerType>()->getPointeeType();
+  } else if (const MemberPointerType *M = NewType->getAs<MemberPointerType>()) {
+    NewType = M->getPointeeType();
+    OldType = OldType->castAs<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();
+  }
+}
+
+SemaExceptionSpec::ImplicitExceptionSpecification::ImplicitExceptionSpecification(Sema &Self)
+        : Self(&Self), ComputedEST(EST_BasicNoexcept) {
+  if (!Self.getLangOpts().CPlusPlus11)
+    ComputedEST = EST_DynamicNone;
+}
+
+FunctionProtoType::ExceptionSpecInfo SemaExceptionSpec::ImplicitExceptionSpecification::getExceptionSpec() const {
+  FunctionProtoType::ExceptionSpecInfo ESI;
+  ESI.Type = getExceptionSpecType();
+  if (ESI.Type == EST_Dynamic) {
+    ESI.Exceptions = Exceptions;
+  } else if (ESI.Type == EST_None) {
+    /// C++11 [except.spec]p14:
+    ///   The exception-specification is noexcept(false) if the set of
+    ///   potential exceptions of the special member function contains "any"
+    ESI.Type = EST_NoexceptFalse;
+    ESI.NoexceptExpr =
+        Self->ActOnCXXBoolLiteral(SourceLocation(), tok::kw_false).get();
+  }
+  return ESI;
+}
+
 } // end namespace clang
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 5ecfdee21f09d..29e467802134e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -50,6 +50,7 @@
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaFixItUtils.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
@@ -2311,7 +2312,7 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK,
   //     computation rather than computing a new body.
   if (const auto *FPT = Ty->getAs<FunctionProtoType>()) {
     if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) {
-      if (const auto *NewFPT = ResolveExceptionSpec(NameInfo.getLoc(), FPT))
+      if (const auto *NewFPT = ExceptionSpec().ResolveExceptionSpec(NameInfo.getLoc(), FPT))
         E->setType(Context.getQualifiedType(NewFPT, Ty.getQualifiers()));
     }
   }
@@ -18198,7 +18199,7 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
   // exception specification for a different reason.
   const FunctionProtoType *FPT = Func->getType()->getAs<FunctionProtoType>();
   if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
-    ResolveExceptionSpec(Loc, FPT);
+    ExceptionSpec().ResolveExceptionSpec(Loc, FPT);
 
   // A callee could be called by a host function then by a device function.
   // If we only try recording once, we will miss recording the use on device
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index e4601f7d6c47d..0107e6f5a93fe 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -39,6 +39,7 @@
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaLambda.h"
 #include "clang/Sema/SemaObjC.h"
@@ -4500,7 +4501,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
     case AA_Returning:
     case AA_Sending:
     case AA_Passing_CFAudited:
-      if (CheckExceptionSpecCompatibility(From, ToType))
+      if (ExceptionSpec().CheckExceptionSpecCompatibility(From, ToType))
         return ExprError();
       break;
 
@@ -4673,7 +4674,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
     CXXCastPath BasePath;
     if (CheckMemberPointerConversion(From, ToType, Kind, BasePath, CStyle))
       return ExprError();
-    if (CheckExceptionSpecCompatibility(From, ToType))
+    if (ExceptionSpec().CheckExceptionSpecCompatibility(From, ToType))
       return ExprError();
 
     // We may not have been able to figure out what this member pointer resolved
@@ -4932,7 +4933,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
   case ICK_Function_Conversion:
     // If both sides are functions (or pointers/references to them), there could
     // be incompatible exception declarations.
-    if (CheckExceptionSpecCompatibility(From, ToType))
+    if (ExceptionSpec().CheckExceptionSpecCompatibility(From, ToType))
       return ExprError();
 
     From = ImpCastExprToType(From, ToType, CK_NoOp, VK_PRValue,
@@ -5189,7 +5190,7 @@ static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
       if((Operator->*IsDesiredOp)()) {
         FoundOperator = true;
         auto *CPT = Operator->getType()->castAs<FunctionProtoType>();
-        CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
+        CPT = Self.ExceptionSpec().ResolveExceptionSpec(KeyLoc, CPT);
         if (!CPT || !CPT->isNothrow())
           return false;
       }
@@ -5454,7 +5455,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
         return false;
       if (UTT == UTT_IsNothrowDestructible) {
         auto *CPT = Destructor->getType()->castAs<FunctionProtoType>();
-        CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
+        CPT = Self.ExceptionSpec().ResolveExceptionSpec(KeyLoc, CPT);
         if (!CPT || !CPT->isNothrow())
           return false;
       }
@@ -5542,7 +5543,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
         if (Constructor->isCopyConstructor(FoundTQs)) {
           FoundConstructor = true;
           auto *CPT = Constructor->getType()->castAs<FunctionProtoType>();
-          CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
+          CPT = Self.ExceptionSpec().ResolveExceptionSpec(KeyLoc, CPT);
           if (!CPT)
             return false;
           // TODO: check whether evaluating default arguments can throw.
@@ -5580,7 +5581,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
         if (Constructor->isDefaultConstructor()) {
           FoundConstructor = true;
           auto *CPT = Constructor->getType()->castAs<FunctionProtoType>();
-          CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
+          CPT = Self.ExceptionSpec().ResolveExceptionSpec(KeyLoc, CPT);
           if (!CPT)
             return false;
           // FIXME: check whether evaluating default arguments can throw.
@@ -5825,7 +5826,7 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
     }
 
     if (Kind == clang::TT_IsNothrowConstructible)
-      return S.canThrow(Result.get()) == CT_Cannot;
+      return S.ExceptionSpec().canThrow(Result.get()) == CT_Cannot;
 
     if (Kind == clang::TT_IsTriviallyConstructible) {
       // Under Objective-C ARC and Weak, if the destination has non-trivial
@@ -6038,7 +6039,7 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
     if (BTT != BTT_IsNothrowConvertible)
       return true;
 
-    return Self.canThrow(Result.get()) == CT_Cannot;
+    return Self.ExceptionSpec().canThrow(Result.get()) == CT_Cannot;
   }
 
   case BTT_IsAssignable:
@@ -6102,7 +6103,7 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
       return true;
 
     if (BTT == BTT_IsNothrowAssignable)
-      return Self.canThrow(Result.get()) == CT_Cannot;
+      return Self.ExceptionSpec().canThrow(Result.get()) == CT_Cannot;
 
     if (BTT == BTT_IsTriviallyAssignable) {
       // Under Objective-C ARC and Weak, if the destination has non-trivial
@@ -8394,7 +8395,7 @@ ExprResult Sema::BuildCXXNoexceptExpr(SourceLocation KeyLoc, Expr *Operand,
     Diag(Operand->getExprLoc(), diag::warn_side_effects_unevaluated_context);
   }
 
-  CanThrowResult CanThrow = canThrow(Operand);
+  CanThrowResult CanThrow = ExceptionSpec().canThrow(Operand);
   return new (Context)
       CXXNoexceptExpr(Context.BoolTy, Operand, CanThrow, KeyLoc, RParen);
 }
@@ -9317,7 +9318,7 @@ Sema::BuildExprRequirement(
   if (E->isInstantiationDependent() || E->getType()->isPlaceholderType() ||
       ReturnTypeRequirement.isDependent())
     Status = concepts::ExprRequirement::SS_Dependent;
-  else if (NoexceptLoc.isValid() && canThrow(E) == CanThrowResult::CT_Can)
+  else if (NoexceptLoc.isValid() && ExceptionSpec().canThrow(E) == CanThrowResult::CT_Can)
     Status = concepts::ExprRequirement::SS_NoexceptNotMet;
   else if (ReturnTypeRequirement.isSubstitutionFailure())
     Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure;
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 9aa60204bf29d..1843b28441c7c 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -20,6 +20,7 @@
 #include "clang/Sema/Overload.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/SemaOpenMP.h"
@@ -960,7 +961,7 @@ MemberExpr *Sema::BuildMemberExpr(
   //     selected member of a set of overloaded functions
   if (auto *FPT = Ty->getAs<FunctionProtoType>()) {
     if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) {
-      if (auto *NewFPT = ResolveExceptionSpec(MemberNameInfo.getLoc(), FPT))
+      if (auto *NewFPT = ExceptionSpec().ResolveExceptionSpec(MemberNameInfo.getLoc(), FPT))
         E->setType(Context.getQualifiedType(NewFPT, Ty.getQualifiers()));
     }
   }
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 353e911c5cc33..ce306021d505b 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -27,6 +27,7 @@
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Ownership.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
 #include "llvm/ADT/APInt.h"
@@ -8865,7 +8866,7 @@ ExprResult InitializationSequence::Perform(Sema &S,
       // Reference binding does not have any corresponding ASTs.
 
       // Check exception specifications
-      if (S.CheckExceptionSpecCompatibility(CurInit.get(), DestType))
+      if (S.ExceptionSpec().CheckExceptionSpecCompatibility(CurInit.get(), DestType))
         return ExprError();
 
       // We don't check for e.g. function pointers here, since address
@@ -8889,7 +8890,7 @@ ExprResult InitializationSequence::Perform(Sema &S,
       assert(CurInit.get()->isPRValue() && "not a temporary");
 
       // Check exception specifications
-      if (S.CheckExceptionSpecCompatibility(CurInit.get(), DestType))
+      if (S.ExceptionSpec().CheckExceptionSpecCompatibility(CurInit.get(), DestType))
         return ExprError();
 
       QualType MTETy = Step->Type;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 2eb25237a0de6..b2a43034866db 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -32,6 +32,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Overload.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/Template.h"
@@ -82,7 +83,7 @@ static ExprResult CreateFunctionRefExpr(
   S.MarkDeclRefReferenced(DRE, Base);
   if (auto *FPT = DRE->getType()->getAs<FunctionProtoType>()) {
     if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) {
-      S.ResolveExceptionSpec(Loc, FPT);
+      S.ExceptionSpec().ResolveExceptionSpec(Loc, FPT);
       DRE->setType(Fn->getType());
     }
   }
@@ -12813,7 +12814,7 @@ static bool completeFunctionType(Sema &S, FunctionDecl *FD, SourceLocation Loc,
   auto *FPT = FD->getType()->castAs<FunctionProtoType>();
   if (S.getLangOpts().CPlusPlus17 &&
       isUnresolvedExceptionSpec(FPT->getExceptionSpecType()) &&
-      !S.ResolveExceptionSpec(Loc, FPT))
+      !S.ExceptionSpec().ResolveExceptionSpec(Loc, FPT))
     return true;
 
   return false;
@@ -13267,7 +13268,7 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *AddressOfExpr,
     Fn = Resolver.getMatchingFunctionDecl();
     assert(Fn);
     if (auto *FPT = Fn->getType()->getAs<FunctionProtoType>())
-      ResolveExceptionSpec(AddressOfExpr->getExprLoc(), FPT);
+      ExceptionSpec().ResolveExceptionSpec(AddressOfExpr->getExprLoc(), FPT);
     FoundResult = *Resolver.getMatchingFunctionAccessPair();
     if (Complain) {
       if (Resolver.IsStaticMemberFunctionFromBoundPointer())
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 4937cce4621f0..9dd8fcd3c6ef4 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -34,6 +34,7 @@
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Template.h"
 #include "clang/Sema/TemplateDeduction.h"
@@ -10418,7 +10419,7 @@ bool Sema::CheckFunctionTemplateSpecialization(
   // we have selected the primary template so we can check whether it matches.
   if (getLangOpts().CPlusPlus17 &&
       isUnresolvedExceptionSpec(SpecializationFPT->getExceptionSpecType()) &&
-      !ResolveExceptionSpec(FD->getLocation(), SpecializationFPT))
+      !ExceptionSpec().ResolveExceptionSpec(FD->getLocation(), SpecializationFPT))
     return true;
 
   FunctionTemplateSpecializationInfo *SpecInfo
@@ -11615,7 +11616,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
           diag::err_mismatched_exception_spec_explicit_instantiation;
       if (getLangOpts().MicrosoftExt)
         DiagID = diag::ext_mismatched_exception_spec_explicit_instantiation;
-      bool Result = CheckEquivalentExceptionSpec(
+      bool Result = ExceptionSpec().CheckEquivalentExceptionSpec(
           PDiag(DiagID) << Specialization->getType(),
           PDiag(diag::note_explicit_instantiation_here),
           Specialization->getType()->getAs<FunctionProtoType>(),
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 07626058c7977..3803bb272d1a7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -32,6 +32,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaConcept.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Template.h"
 #include "clang/Sema/TemplateDeduction.h"
@@ -3018,7 +3019,7 @@ void Sema::SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
     // On error, recover by dropping the exception specification.
     ESI.Type = EST_None;
 
-  UpdateExceptionSpec(New, ESI);
+  ExceptionSpec().UpdateExceptionSpec(New, ESI);
 }
 
 namespace {
@@ -3494,6 +3495,33 @@ namespace clang {
   }
 }
 
+class SavePendingParsedClassStateRAII {
+public:
+  SavePendingParsedClassStateRAII(Sema &S) : S(S) { swapSavedState(); }
+
+  ~SavePendingParsedClassStateRAII() {
+    assert(S.ExceptionSpec().DelayedOverridingExceptionSpecChecks.empty() &&
+            "there shouldn't be any pending delayed exception spec checks");
+    assert(S.ExceptionSpec().DelayedEquivalentExceptionSpecChecks.empty() &&
+            "there shouldn't be any pending delayed exception spec checsks");
+    swapSavedState();
+  }
+
+private:
+  Sema &S;
+  decltype(SemaExceptionSpec::DelayedOverridingExceptionSpecChecks)
+      SavedOverridingExceptionSpecChecks;
+  decltype(SemaExceptionSpec::DelayedEquivalentExceptionSpecChecks)
+      SavedEquivalentExceptionSpecChecks;
+
+  void swapSavedState() {
+    SavedOverridingExceptionSpecChecks.swap(
+        S.ExceptionSpec().DelayedOverridingExceptionSpecChecks);
+    SavedEquivalentExceptionSpecChecks.swap(
+        S.ExceptionSpec().DelayedEquivalentExceptionSpecChecks);
+  }
+};
+
 /// Instantiate the definition of a class from a given pattern.
 ///
 /// \param PointOfInstantiation The point of instantiation within the
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 381d79b2fcd46..6eed03a48a8c6 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -27,6 +27,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/SemaOpenMP.h"
@@ -4685,14 +4686,14 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
   if (Inst.isInvalid()) {
     // We hit the instantiation depth limit. Clear the exception specification
     // so that our callers don't have to cope with EST_Uninstantiated.
-    UpdateExceptionSpec(Decl, EST_None);
+    ExceptionSpec().UpdateExceptionSpec(Decl, EST_None);
     return;
   }
   if (Inst.isAlreadyInstantiating()) {
     // This exception specification indirectly depends on itself. Reject.
     // FIXME: Corresponding rule in the standard?
     Diag(PointOfInstantiation, diag::err_exception_spec_cycle) << Decl;
-    UpdateExceptionSpec(Decl, EST_None);
+    ExceptionSpec().UpdateExceptionSpec(Decl, EST_None);
     return;
   }
 
@@ -4712,7 +4713,7 @@ void Sema::InstantiateExceptionSpec(SourceLocation PointOfInstantiation,
   // the template.
   FunctionDecl *Template = Proto->getExceptionSpecTemplate();
   if (addInstantiatedParametersToScope(Decl, Template, Scope, TemplateArgs)) {
-    UpdateExceptionSpec(Decl, EST_None);
+    ExceptionSpec().UpdateExceptionSpec(Decl, EST_None);
     return;
   }
 
@@ -4817,7 +4818,7 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New,
     return true;
 
   if (isa<CXXDestructorDecl>(New) && SemaRef.getLangOpts().CPlusPlus11)
-    SemaRef.AdjustDestructorExceptionSpec(cast<CXXDestructorDecl>(New));
+    SemaRef.ExceptionSpec().AdjustDestructorExceptionSpec(cast<CXXDestructorDecl>(New));
 
   New->setAccess(Tmpl->getAccess());
   if (Tmpl->isVirtualAsWritten())
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index ef0b6b701a52c..9d1abaed09651 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -34,6 +34,7 @@
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/SemaOpenMP.h"
@@ -2727,7 +2728,7 @@ QualType Sema::BuildMemberPointerType(QualType T, QualType Class,
                                       DeclarationName Entity) {
   // Verify that we're not building a pointer to pointer to function with
   // exception specification.
-  if (CheckDistantExceptionSpec(T)) {
+  if (ExceptionSpec().CheckDistantExceptionSpec(T)) {
     Diag(Loc, diag::err_distant_exception_spec);
     return QualType();
   }
@@ -4706,7 +4707,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
     case DeclaratorChunk::Pointer:
       // Verify that we're not building a pointer to pointer to function with
       // exception specification.
-      if (LangOpts.CPlusPlus && S.CheckDistantExceptionSpec(T)) {
+      if (LangOpts.CPlusPlus && S.ExceptionSpec().CheckDistantExceptionSpec(T)) {
         S.Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec);
         D.setInvalidType(true);
         // Build the type anyway.
@@ -4742,7 +4743,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
     case DeclaratorChunk::Reference: {
       // Verify that we're not building a reference to pointer to function with
       // exception specification.
-      if (LangOpts.CPlusPlus && S.CheckDistantExceptionSpec(T)) {
+      if (LangOpts.CPlusPlus && S.ExceptionSpec().CheckDistantExceptionSpec(T)) {
         S.Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec);
         D.setInvalidType(true);
         // Build the type anyway.
@@ -4756,7 +4757,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
     case DeclaratorChunk::Array: {
       // Verify that we're not building an array of pointers to function with
       // exception specification.
-      if (LangOpts.CPlusPlus && S.CheckDistantExceptionSpec(T)) {
+      if (LangOpts.CPlusPlus && S.ExceptionSpec().CheckDistantExceptionSpec(T)) {
         S.Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec);
         D.setInvalidType(true);
         // Build the type anyway.
@@ -5256,7 +5257,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
           NoexceptExpr = FTI.NoexceptExpr;
         }
 
-        S.checkExceptionSpecification(D.isFunctionDeclarationContext(),
+        S.ExceptionSpec().checkExceptionSpecification(D.isFunctionDeclarationContext(),
                                       FTI.getExceptionSpecType(),
                                       DynamicExceptions,
                                       DynamicExceptionRanges,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 29444f0edc2ae..85ee495ea9fc0 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -38,6 +38,7 @@
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Sema/SemaExceptionSpec.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/SemaOpenACC.h"
@@ -6309,7 +6310,7 @@ bool TreeTransform<Derived>::TransformExceptionSpec(
 
     ExceptionSpecificationType EST = ESI.Type;
     NoexceptExpr =
-        getSema().ActOnNoexceptSpec(NoexceptExpr.get(), EST);
+        getSema().ExceptionSpec().ActOnNoexceptSpec(NoexceptExpr.get(), EST);
     if (NoexceptExpr.isInvalid())
       return true;
 
@@ -6367,14 +6368,14 @@ bool TreeTransform<Derived>::TransformExceptionSpec(
         Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), ArgIdx);
 
         QualType U = getDerived().TransformType(PackExpansion->getPattern());
-        if (U.isNull() || SemaRef.CheckSpecifiedExceptionType(U, Loc))
+        if (U.isNull() || SemaRef.ExceptionSpec().CheckSpecifiedExceptionType(U, Loc))
           return true;
 
         Exceptions.push_back(U);
       }
     } else {
       QualType U = getDerived().TransformType(T);
-      if (U.isNull() || SemaRef.CheckSpecifiedExceptionType(U, Loc))
+      if (U.isNull() || SemaRef.ExceptionSpec().CheckSpecifiedExceptionType(U, Loc))
         return true;
       if (T != U)
         Changed = true;

>From 21d5282848b54ca55e8e9672c1f2007616cde702 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 18 May 2024 17:50:33 +0300
Subject: [PATCH 2/2] Run clang-format

---
 clang/lib/Parse/ParseCXXInlineMethods.cpp     |  10 +-
 clang/lib/Parse/ParseDecl.cpp                 |   3 +-
 clang/lib/Parse/ParseDeclCXX.cpp              |   4 +-
 clang/lib/Sema/AnalysisBasedWarnings.cpp      |   5 +-
 clang/lib/Sema/SemaCoroutine.cpp              |   3 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |  33 ++--
 clang/lib/Sema/SemaExceptionSpec.cpp          | 148 ++++++++++--------
 clang/lib/Sema/SemaExpr.cpp                   |   3 +-
 clang/lib/Sema/SemaExprCXX.cpp                |   3 +-
 clang/lib/Sema/SemaExprMember.cpp             |   3 +-
 clang/lib/Sema/SemaInit.cpp                   |   6 +-
 clang/lib/Sema/SemaTemplate.cpp               |   3 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |   4 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   3 +-
 clang/lib/Sema/SemaType.cpp                   |  20 +--
 clang/lib/Sema/TreeTransform.h                |   6 +-
 16 files changed, 142 insertions(+), 115 deletions(-)

diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index ccff9db1238a0..7623ea1d1492e 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -540,12 +540,10 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
       Diag(Tok.getLocation(), diag::err_except_spec_unparsed);
 
     // Attach the exception-specification to the method.
-    Actions.ExceptionSpec().actOnDelayedExceptionSpecification(LM.Method, EST,
-                                               SpecificationRange,
-                                               DynamicExceptions,
-                                               DynamicExceptionRanges,
-                                               NoexceptExpr.isUsable()?
-                                                 NoexceptExpr.get() : nullptr);
+    Actions.ExceptionSpec().actOnDelayedExceptionSpecification(
+        LM.Method, EST, SpecificationRange, DynamicExceptions,
+        DynamicExceptionRanges,
+        NoexceptExpr.isUsable() ? NoexceptExpr.get() : nullptr);
 
     // There could be leftover tokens (e.g. because of an error).
     // Skip through until we reach the original token position.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index b1c4f68ac4901..9870e43f5b1fe 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7532,7 +7532,8 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
       // delayed (even if this is a friend declaration).
       bool Delayed = D.getContext() == DeclaratorContext::Member &&
                      D.isFunctionDeclaratorAFunctionDeclaration();
-      if (Delayed && Actions.ExceptionSpec().isLibstdcxxEagerExceptionSpecHack(D) &&
+      if (Delayed &&
+          Actions.ExceptionSpec().isLibstdcxxEagerExceptionSpecHack(D) &&
           GetLookAheadToken(0).is(tok::kw_noexcept) &&
           GetLookAheadToken(1).is(tok::l_paren) &&
           GetLookAheadToken(2).is(tok::kw_noexcept) &&
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index e51252cdeddb3..8a44ec41cd07e 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -4139,8 +4139,8 @@ ExceptionSpecificationType Parser::tryParseExceptionSpecification(
 
     T.consumeClose();
     if (!NoexceptExpr.isInvalid()) {
-      NoexceptExpr =
-          Actions.ExceptionSpec().ActOnNoexceptSpec(NoexceptExpr.get(), NoexceptType);
+      NoexceptExpr = Actions.ExceptionSpec().ActOnNoexceptSpec(
+          NoexceptExpr.get(), NoexceptType);
       NoexceptRange = SourceRange(KeywordLoc, T.getCloseLocation());
     } else {
       NoexceptType = EST_BasicNoexcept;
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 3e1ec7a378acd..1226cfe7ed10d 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -330,9 +330,10 @@ static bool throwEscapes(Sema &S, const CXXThrowExpr *E, CFGBlock &ThrowBlock,
       if (auto *Catch =
               dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) {
         QualType Caught = Catch->getCaughtType();
-        if (Caught.isNull() || // catch (...) catches everything
+        if (Caught.isNull() ||  // catch (...) catches everything
             !E->getSubExpr() || // throw; is considered cuaght by any handler
-            S.ExceptionSpec().handlerCanCatch(Caught, E->getSubExpr()->getType()))
+            S.ExceptionSpec().handlerCanCatch(Caught,
+                                              E->getSubExpr()->getType()))
           // Exception doesn't escape via this path.
           break;
       } else {
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 41eb97a391857..2113ac7681090 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -615,7 +615,8 @@ static void checkNoThrow(Sema &S, const Stmt *E,
   auto checkDeclNoexcept = [&](const Decl *D, bool IsDtor = false) {
     // In the case of dtor, the call to dtor is implicit and hence we should
     // pass nullptr to canCalleeThrow.
-    if (SemaExceptionSpec::canCalleeThrow(S, IsDtor ? nullptr : cast<Expr>(E), D)) {
+    if (SemaExceptionSpec::canCalleeThrow(S, IsDtor ? nullptr : cast<Expr>(E),
+                                          D)) {
       if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
         // co_await promise.final_suspend() could end up calling
         // __builtin_coro_resume for symmetric transfer if await_suspend()
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 8e7f07d7efedf..aead70268d470 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9180,7 +9180,8 @@ void Sema::DefineDefaultedComparison(SourceLocation UseLoc, FunctionDecl *FD,
 
   // The exception specification is needed because we are defining the
   // function. Note that this will reuse the body we just built.
-  ExceptionSpec().ResolveExceptionSpec(UseLoc, FD->getType()->castAs<FunctionProtoType>());
+  ExceptionSpec().ResolveExceptionSpec(
+      UseLoc, FD->getType()->castAs<FunctionProtoType>());
 
   if (ASTMutationListener *L = getASTMutationListener())
     L->CompletedImplicitDefinition(FD);
@@ -14053,8 +14054,8 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
-                       Constructor->getType()->castAs<FunctionProtoType>());
+  ExceptionSpec().ResolveExceptionSpec(
+      CurrentLocation, Constructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
   // Add a context note for diagnostics produced after this point.
@@ -14193,8 +14194,8 @@ void Sema::DefineInheritingConstructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
-                       Constructor->getType()->castAs<FunctionProtoType>());
+  ExceptionSpec().ResolveExceptionSpec(
+      CurrentLocation, Constructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
   // Add a context note for diagnostics produced after this point.
@@ -14342,8 +14343,8 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
-                       Destructor->getType()->castAs<FunctionProtoType>());
+  ExceptionSpec().ResolveExceptionSpec(
+      CurrentLocation, Destructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
   // Add a context note for diagnostics produced after this point.
@@ -15014,8 +15015,9 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
-                       CopyAssignOperator->getType()->castAs<FunctionProtoType>());
+  ExceptionSpec().ResolveExceptionSpec(
+      CurrentLocation,
+      CopyAssignOperator->getType()->castAs<FunctionProtoType>());
 
   // Add a context note for diagnostics produced after this point.
   Scope.addContextNote(CurrentLocation);
@@ -15411,8 +15413,9 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
-                       MoveAssignOperator->getType()->castAs<FunctionProtoType>());
+  ExceptionSpec().ResolveExceptionSpec(
+      CurrentLocation,
+      MoveAssignOperator->getType()->castAs<FunctionProtoType>());
 
   // Add a context note for diagnostics produced after this point.
   Scope.addContextNote(CurrentLocation);
@@ -15724,8 +15727,8 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
-                       CopyConstructor->getType()->castAs<FunctionProtoType>());
+  ExceptionSpec().ResolveExceptionSpec(
+      CurrentLocation, CopyConstructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
   // Add a context note for diagnostics produced after this point.
@@ -15863,8 +15866,8 @@ void Sema::DefineImplicitMoveConstructor(SourceLocation CurrentLocation,
 
   // The exception specification is needed because we are defining the
   // function.
-  ExceptionSpec().ResolveExceptionSpec(CurrentLocation,
-                       MoveConstructor->getType()->castAs<FunctionProtoType>());
+  ExceptionSpec().ResolveExceptionSpec(
+      CurrentLocation, MoveConstructor->getType()->castAs<FunctionProtoType>());
   MarkVTableUsed(CurrentLocation, ClassDecl);
 
   // Add a context note for diagnostics produced after this point.
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index d8548ba8cbc79..a276b9ad3917f 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -82,8 +82,9 @@ bool SemaExceptionSpec::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
       .Default(false);
 }
 
-ExprResult SemaExceptionSpec::ActOnNoexceptSpec(Expr *NoexceptExpr,
-                                   ExceptionSpecificationType &EST) {
+ExprResult
+SemaExceptionSpec::ActOnNoexceptSpec(Expr *NoexceptExpr,
+                                     ExceptionSpecificationType &EST) {
   ASTContext &Context = getASTContext();
   if (NoexceptExpr->isTypeDependent() ||
       NoexceptExpr->containsUnexpandedParameterPack()) {
@@ -121,7 +122,8 @@ ExprResult SemaExceptionSpec::ActOnNoexceptSpec(Expr *NoexceptExpr,
 ///
 /// \param[in,out] T  The exception type. This will be decayed to a pointer type
 ///                   when the input is an array or a function type.
-bool SemaExceptionSpec::CheckSpecifiedExceptionType(QualType &T, SourceRange Range) {
+bool SemaExceptionSpec::CheckSpecifiedExceptionType(QualType &T,
+                                                    SourceRange Range) {
   // C++11 [except.spec]p2:
   //   A type cv T, "array of T", or "function returning T" denoted
   //   in an exception-specification is adjusted to type T, "pointer to T", or
@@ -172,7 +174,8 @@ bool SemaExceptionSpec::CheckSpecifiedExceptionType(QualType &T, SourceRange Ran
   }
   if (!(PointeeT->isRecordType() &&
         PointeeT->castAs<RecordType>()->isBeingDefined()) &&
-      SemaRef.RequireCompleteType(Range.getBegin(), PointeeT, DiagID, Kind, Range))
+      SemaRef.RequireCompleteType(Range.getBegin(), PointeeT, DiagID, Kind,
+                                  Range))
     return ReturnValueOnError;
 
   // WebAssembly reference types can't be used in exception specifications.
@@ -216,7 +219,8 @@ bool SemaExceptionSpec::CheckDistantExceptionSpec(QualType T) {
 }
 
 const FunctionProtoType *
-SemaExceptionSpec::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
+SemaExceptionSpec::ResolveExceptionSpec(SourceLocation Loc,
+                                        const FunctionProtoType *FPT) {
   if (FPT->getExceptionSpecType() == EST_Unparsed) {
     Diag(Loc, diag::err_exception_spec_not_parsed);
     return nullptr;
@@ -248,9 +252,8 @@ SemaExceptionSpec::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoT
   return Proto;
 }
 
-void
-SemaExceptionSpec::UpdateExceptionSpec(FunctionDecl *FD,
-                          const FunctionProtoType::ExceptionSpecInfo &ESI) {
+void SemaExceptionSpec::UpdateExceptionSpec(
+    FunctionDecl *FD, const FunctionProtoType::ExceptionSpecInfo &ESI) {
   // If we've fully resolved the exception specification, notify listeners.
   if (!isUnresolvedExceptionSpec(ESI.Type))
     if (auto *Listener = SemaRef.getASTMutationListener())
@@ -299,7 +302,8 @@ static bool hasImplicitExceptionSpec(FunctionDecl *Decl) {
   return !Ty->hasExceptionSpec();
 }
 
-bool SemaExceptionSpec::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
+bool SemaExceptionSpec::CheckEquivalentExceptionSpec(FunctionDecl *Old,
+                                                     FunctionDecl *New) {
   // Just completely ignore this under -fno-exceptions prior to C++17.
   // In C++17 onwards, the exception specification is part of the type and
   // we will diagnose mismatches anyway, so it's better to check for them here.
@@ -329,11 +333,12 @@ bool SemaExceptionSpec::CheckEquivalentExceptionSpec(FunctionDecl *Old, Function
   // Check the types as written: they must match before any exception
   // specification adjustment is applied.
   if (!CheckEquivalentExceptionSpecImpl(
-        SemaRef, SemaRef.PDiag(DiagID), SemaRef.PDiag(diag::note_previous_declaration),
-        Old->getType()->getAs<FunctionProtoType>(), Old->getLocation(),
-        New->getType()->getAs<FunctionProtoType>(), New->getLocation(),
-        &MissingExceptionSpecification, &MissingEmptyExceptionSpecification,
-        /*AllowNoexceptAllMatchWithNoSpec=*/true, IsOperatorNew)) {
+          SemaRef, SemaRef.PDiag(DiagID),
+          SemaRef.PDiag(diag::note_previous_declaration),
+          Old->getType()->getAs<FunctionProtoType>(), Old->getLocation(),
+          New->getType()->getAs<FunctionProtoType>(), New->getLocation(),
+          &MissingExceptionSpecification, &MissingEmptyExceptionSpecification,
+          /*AllowNoexceptAllMatchWithNoSpec=*/true, IsOperatorNew)) {
     // C++11 [except.spec]p4 [DR1492]:
     //   If a declaration of a function has an implicit
     //   exception-specification, other declarations of the function shall
@@ -365,7 +370,8 @@ bool SemaExceptionSpec::CheckEquivalentExceptionSpec(FunctionDecl *Old, Function
   // Likewise if the old function is a builtin.
   if (MissingEmptyExceptionSpecification &&
       (Old->getLocation().isInvalid() ||
-       SemaRef.Context.getSourceManager().isInSystemHeader(Old->getLocation()) ||
+       SemaRef.Context.getSourceManager().isInSystemHeader(
+           Old->getLocation()) ||
        Old->getBuiltinID()) &&
       Old->isExternC()) {
     New->setType(SemaRef.Context.getFunctionType(
@@ -455,7 +461,8 @@ bool SemaExceptionSpec::CheckEquivalentExceptionSpec(FunctionDecl *Old, Function
   case EST_NoexceptTrue:
     OS << "noexcept(";
     assert(OldProto->getNoexceptExpr() != nullptr && "Expected non-null Expr");
-    OldProto->getNoexceptExpr()->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
+    OldProto->getNoexceptExpr()->printPretty(OS, nullptr,
+                                             SemaRef.getPrintingPolicy());
     OS << ")";
     break;
   case EST_NoThrow:
@@ -508,8 +515,8 @@ bool SemaExceptionSpec::CheckEquivalentExceptionSpec(
   if (getLangOpts().MSVCCompat)
     DiagID = diag::ext_mismatched_exception_spec;
   bool Result = CheckEquivalentExceptionSpecImpl(
-      SemaRef, SemaRef.PDiag(DiagID), SemaRef.PDiag(diag::note_previous_declaration),
-      Old, OldLoc, New, NewLoc);
+      SemaRef, SemaRef.PDiag(DiagID),
+      SemaRef.PDiag(diag::note_previous_declaration), Old, OldLoc, New, NewLoc);
 
   // In Microsoft mode, mismatching exception specifications just cause a warning.
   if (getLangOpts().MSVCCompat)
@@ -674,19 +681,18 @@ static bool CheckEquivalentExceptionSpecImpl(
   return true;
 }
 
-bool SemaExceptionSpec::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
-                                        const PartialDiagnostic &NoteID,
-                                        const FunctionProtoType *Old,
-                                        SourceLocation OldLoc,
-                                        const FunctionProtoType *New,
-                                        SourceLocation NewLoc) {
+bool SemaExceptionSpec::CheckEquivalentExceptionSpec(
+    const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID,
+    const FunctionProtoType *Old, SourceLocation OldLoc,
+    const FunctionProtoType *New, SourceLocation NewLoc) {
   if (!getLangOpts().CXXExceptions)
     return false;
   return CheckEquivalentExceptionSpecImpl(SemaRef, DiagID, NoteID, Old, OldLoc,
                                           New, NewLoc);
 }
 
-bool SemaExceptionSpec::handlerCanCatch(QualType HandlerType, QualType ExceptionType) {
+bool SemaExceptionSpec::handlerCanCatch(QualType HandlerType,
+                                        QualType ExceptionType) {
   ASTContext &Context = getASTContext();
   // [except.handle]p3:
   //   A handler is a match for an exception object of type E if:
@@ -722,7 +728,7 @@ bool SemaExceptionSpec::handlerCanCatch(QualType HandlerType, QualType Exception
     // FIXME: Should we treat the exception as catchable if a lifetime
     // conversion is required?
     if (SemaRef.IsQualificationConversion(ExceptionType, HandlerType, false,
-                                  LifetimeConv) ||
+                                          LifetimeConv) ||
         SemaRef.IsFunctionConversion(ExceptionType, HandlerType, Result))
       return true;
 
@@ -751,18 +757,21 @@ bool SemaExceptionSpec::handlerCanCatch(QualType HandlerType, QualType Exception
     return false;
   CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
                      /*DetectVirtual=*/false);
-  if (!SemaRef.IsDerivedFrom(SourceLocation(), ExceptionType, HandlerType, Paths) ||
+  if (!SemaRef.IsDerivedFrom(SourceLocation(), ExceptionType, HandlerType,
+                             Paths) ||
       Paths.isAmbiguous(Context.getCanonicalType(HandlerType)))
     return false;
 
   // Do this check from a context without privileges.
-  switch (SemaRef.CheckBaseClassAccess(SourceLocation(), HandlerType, ExceptionType,
-                               Paths.front(),
-                               /*Diagnostic*/ 0,
-                               /*ForceCheck*/ true,
-                               /*ForceUnprivileged*/ true)) {
-  case Sema::AR_accessible: return true;
-  case Sema::AR_inaccessible: return false;
+  switch (SemaRef.CheckBaseClassAccess(SourceLocation(), HandlerType,
+                                       ExceptionType, Paths.front(),
+                                       /*Diagnostic*/ 0,
+                                       /*ForceCheck*/ true,
+                                       /*ForceUnprivileged*/ true)) {
+  case Sema::AR_accessible:
+    return true;
+  case Sema::AR_inaccessible:
+    return false;
   case Sema::AR_dependent:
     llvm_unreachable("access check dependent for unprivileged context");
   case Sema::AR_delayed:
@@ -890,8 +899,8 @@ CheckSpecForTypesEquivalent(Sema &S, const PartialDiagnostic &DiagID,
   if (!SFunc)
     return false;
 
-  return S.ExceptionSpec().CheckEquivalentExceptionSpec(DiagID, NoteID, TFunc, TargetLoc,
-                                        SFunc, SourceLoc);
+  return S.ExceptionSpec().CheckEquivalentExceptionSpec(
+      DiagID, NoteID, TFunc, TargetLoc, SFunc, SourceLoc);
 }
 
 /// CheckParamExceptionSpec - Check if the parameter and return types of the
@@ -906,10 +915,9 @@ bool SemaExceptionSpec::CheckParamExceptionSpec(
     bool SkipSourceFirstParameter, SourceLocation SourceLoc) {
   auto RetDiag = DiagID;
   RetDiag << 0;
-  if (CheckSpecForTypesEquivalent(
-          SemaRef, RetDiag, SemaRef.PDiag(),
-          Target->getReturnType(), TargetLoc, Source->getReturnType(),
-          SourceLoc))
+  if (CheckSpecForTypesEquivalent(SemaRef, RetDiag, SemaRef.PDiag(),
+                                  Target->getReturnType(), TargetLoc,
+                                  Source->getReturnType(), SourceLoc))
     return true;
 
   // We shouldn't even be testing this unless the arguments are otherwise
@@ -930,7 +938,8 @@ bool SemaExceptionSpec::CheckParamExceptionSpec(
   return false;
 }
 
-bool SemaExceptionSpec::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) {
+bool SemaExceptionSpec::CheckExceptionSpecCompatibility(Expr *From,
+                                                        QualType ToType) {
   // First we check for applicability.
   // Target type must be a function, function pointer or function reference.
   const FunctionProtoType *ToFunc = GetUnderlyingFunction(ToType);
@@ -964,15 +973,16 @@ bool SemaExceptionSpec::CheckExceptionSpecCompatibility(Expr *From, QualType ToT
   //     void (*q)(void (*) throw(int)) = p;
   //   }
   // ... because it might be instantiated with T=int.
-  return CheckExceptionSpecSubset(SemaRef.PDiag(DiagID), SemaRef.PDiag(NestedDiagID), SemaRef.PDiag(),
+  return CheckExceptionSpecSubset(SemaRef.PDiag(DiagID),
+                                  SemaRef.PDiag(NestedDiagID), SemaRef.PDiag(),
                                   SemaRef.PDiag(), ToFunc, 0,
                                   From->getSourceRange().getBegin(), FromFunc,
                                   0, SourceLocation()) &&
          !getLangOpts().CPlusPlus17;
 }
 
-bool SemaExceptionSpec::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
-                                                const CXXMethodDecl *Old) {
+bool SemaExceptionSpec::CheckOverridingFunctionExceptionSpec(
+    const CXXMethodDecl *New, const CXXMethodDecl *Old) {
   // If the new exception specification hasn't been parsed yet, skip the check.
   // We'll get called again once it's been parsed.
   if (New->getType()->castAs<FunctionProtoType>()->getExceptionSpecType() ==
@@ -997,7 +1007,8 @@ bool SemaExceptionSpec::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl
   if (getLangOpts().MSVCCompat)
     DiagID = diag::ext_override_exception_spec;
   return CheckExceptionSpecSubset(
-      SemaRef.PDiag(DiagID), SemaRef.PDiag(diag::err_deep_exception_specs_differ),
+      SemaRef.PDiag(DiagID),
+      SemaRef.PDiag(diag::err_deep_exception_specs_differ),
       SemaRef.PDiag(diag::note_overridden_virtual_function),
       SemaRef.PDiag(diag::ext_override_exception_spec),
       Old->getType()->castAs<FunctionProtoType>(),
@@ -1018,8 +1029,9 @@ static CanThrowResult canSubStmtsThrow(Sema &Self, const Stmt *S) {
   return R;
 }
 
-CanThrowResult SemaExceptionSpec::canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
-                                    SourceLocation Loc) {
+CanThrowResult SemaExceptionSpec::canCalleeThrow(Sema &S, const Expr *E,
+                                                 const Decl *D,
+                                                 SourceLocation Loc) {
   // As an extension, we assume that __attribute__((nothrow)) functions don't
   // throw.
   if (isa_and_nonnull<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>())
@@ -1066,7 +1078,8 @@ CanThrowResult SemaExceptionSpec::canCalleeThrow(Sema &S, const Expr *E, const D
     return CT_Can;
 
   if (Loc.isValid() || (Loc.isInvalid() && E))
-    FT = S.ExceptionSpec().ResolveExceptionSpec(Loc.isInvalid() ? E->getBeginLoc() : Loc, FT);
+    FT = S.ExceptionSpec().ResolveExceptionSpec(
+        Loc.isInvalid() ? E->getBeginLoc() : Loc, FT);
   if (!FT)
     return CT_Can;
 
@@ -1086,8 +1099,8 @@ static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) {
     if (auto *RD =
             VD->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl()) {
       if (auto *Dtor = RD->getDestructor()) {
-        CT = mergeCanThrow(
-            CT, SemaExceptionSpec::canCalleeThrow(Self, nullptr, Dtor, VD->getLocation()));
+        CT = mergeCanThrow(CT, SemaExceptionSpec::canCalleeThrow(
+                                   Self, nullptr, Dtor, VD->getLocation()));
       }
     }
   }
@@ -1564,7 +1577,8 @@ CanThrowResult SemaExceptionSpec::canThrow(const Stmt *S) {
 
     // For 'if constexpr', consider only the non-discarded case.
     // FIXME: We should add a DiscardedStmt marker to the AST.
-    if (std::optional<const Stmt *> Case = IS->getNondiscardedCase(SemaRef.Context))
+    if (std::optional<const Stmt *> Case =
+            IS->getNondiscardedCase(SemaRef.Context))
       return *Case ? mergeCanThrow(CT, canThrow(*Case)) : CT;
 
     CanThrowResult Then = canThrow(IS->getThen());
@@ -1644,17 +1658,14 @@ void SemaExceptionSpec::CheckDelayedMemberExceptionSpecs() {
 struct ComputingExceptionSpec {
   Sema &S;
 
-  ComputingExceptionSpec(Sema &S, FunctionDecl *FD, SourceLocation Loc)
-      : S(S) {
+  ComputingExceptionSpec(Sema &S, FunctionDecl *FD, SourceLocation Loc) : S(S) {
     Sema::CodeSynthesisContext Ctx;
     Ctx.Kind = Sema::CodeSynthesisContext::ExceptionSpecEvaluation;
     Ctx.PointOfInstantiation = Loc;
     Ctx.Entity = FD;
     S.pushCodeSynthesisContext(Ctx);
   }
-  ~ComputingExceptionSpec() {
-    S.popCodeSynthesisContext();
-  }
+  ~ComputingExceptionSpec() { S.popCodeSynthesisContext(); }
 };
 
 void SemaExceptionSpec::checkExceptionSpecification(
@@ -1694,7 +1705,7 @@ void SemaExceptionSpec::checkExceptionSpecification(
   if (isComputedNoexcept(EST)) {
     assert((NoexceptExpr->isTypeDependent() ||
             NoexceptExpr->getType()->getCanonicalTypeUnqualified() ==
-            SemaRef.Context.BoolTy) &&
+                SemaRef.Context.BoolTy) &&
            "Parser should have made sure that the expression is boolean");
     if (IsTopLevel && SemaRef.DiagnoseUnexpandedParameterPack(NoexceptExpr)) {
       ESI.Type = EST_BasicNoexcept;
@@ -1743,7 +1754,8 @@ void SemaExceptionSpec::actOnDelayedExceptionSpecification(
   }
 }
 
-void SemaExceptionSpec::AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor) {
+void SemaExceptionSpec::AdjustDestructorExceptionSpec(
+    CXXDestructorDecl *Destructor) {
   assert(getLangOpts().CPlusPlus11 &&
          "adjusting dtor exception specs was introduced in c++11");
 
@@ -1764,8 +1776,8 @@ void SemaExceptionSpec::AdjustDestructorExceptionSpec(CXXDestructorDecl *Destruc
   FunctionProtoType::ExtProtoInfo EPI = DtorType->getExtProtoInfo();
   EPI.ExceptionSpec.Type = EST_Unevaluated;
   EPI.ExceptionSpec.SourceDecl = Destructor;
-  Destructor->setType(
-      SemaRef.Context.getFunctionType(SemaRef.Context.VoidTy, std::nullopt, EPI));
+  Destructor->setType(SemaRef.Context.getFunctionType(SemaRef.Context.VoidTy,
+                                                      std::nullopt, EPI));
 
   // FIXME: If the destructor has a body that could throw, and the newly created
   // spec doesn't allow exceptions, we should emit a warning, because this
@@ -1774,8 +1786,8 @@ void SemaExceptionSpec::AdjustDestructorExceptionSpec(CXXDestructorDecl *Destruc
   // needs to be done somewhere else.
 }
 
-void SemaExceptionSpec::MarkVirtualMemberExceptionSpecsNeeded(SourceLocation Loc,
-                                                 const CXXRecordDecl *RD) {
+void SemaExceptionSpec::MarkVirtualMemberExceptionSpecsNeeded(
+    SourceLocation Loc, const CXXRecordDecl *RD) {
   for (const auto *I : RD->methods())
     if (I->isVirtual() && !I->isPureVirtual())
       ResolveExceptionSpec(Loc, I->getType()->castAs<FunctionProtoType>());
@@ -1817,19 +1829,21 @@ void SemaExceptionSpec::MergeVarDeclExceptionSpecs(VarDecl *New, VarDecl *Old) {
   // 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())) {
+          OldType->getAs<FunctionProtoType>(), Old->getLocation(),
+          NewType->getAs<FunctionProtoType>(), New->getLocation())) {
     New->setInvalidDecl();
   }
 }
 
-SemaExceptionSpec::ImplicitExceptionSpecification::ImplicitExceptionSpecification(Sema &Self)
-        : Self(&Self), ComputedEST(EST_BasicNoexcept) {
+SemaExceptionSpec::ImplicitExceptionSpecification::
+    ImplicitExceptionSpecification(Sema &Self)
+    : Self(&Self), ComputedEST(EST_BasicNoexcept) {
   if (!Self.getLangOpts().CPlusPlus11)
     ComputedEST = EST_DynamicNone;
 }
 
-FunctionProtoType::ExceptionSpecInfo SemaExceptionSpec::ImplicitExceptionSpecification::getExceptionSpec() const {
+FunctionProtoType::ExceptionSpecInfo
+SemaExceptionSpec::ImplicitExceptionSpecification::getExceptionSpec() const {
   FunctionProtoType::ExceptionSpecInfo ESI;
   ESI.Type = getExceptionSpecType();
   if (ESI.Type == EST_Dynamic) {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 29e467802134e..b6cfc4106fe06 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2312,7 +2312,8 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK,
   //     computation rather than computing a new body.
   if (const auto *FPT = Ty->getAs<FunctionProtoType>()) {
     if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) {
-      if (const auto *NewFPT = ExceptionSpec().ResolveExceptionSpec(NameInfo.getLoc(), FPT))
+      if (const auto *NewFPT =
+              ExceptionSpec().ResolveExceptionSpec(NameInfo.getLoc(), FPT))
         E->setType(Context.getQualifiedType(NewFPT, Ty.getQualifiers()));
     }
   }
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 0107e6f5a93fe..8ef346b4c3d63 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -9318,7 +9318,8 @@ Sema::BuildExprRequirement(
   if (E->isInstantiationDependent() || E->getType()->isPlaceholderType() ||
       ReturnTypeRequirement.isDependent())
     Status = concepts::ExprRequirement::SS_Dependent;
-  else if (NoexceptLoc.isValid() && ExceptionSpec().canThrow(E) == CanThrowResult::CT_Can)
+  else if (NoexceptLoc.isValid() &&
+           ExceptionSpec().canThrow(E) == CanThrowResult::CT_Can)
     Status = concepts::ExprRequirement::SS_NoexceptNotMet;
   else if (ReturnTypeRequirement.isSubstitutionFailure())
     Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure;
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 1843b28441c7c..e97a7007af53f 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -961,7 +961,8 @@ MemberExpr *Sema::BuildMemberExpr(
   //     selected member of a set of overloaded functions
   if (auto *FPT = Ty->getAs<FunctionProtoType>()) {
     if (isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) {
-      if (auto *NewFPT = ExceptionSpec().ResolveExceptionSpec(MemberNameInfo.getLoc(), FPT))
+      if (auto *NewFPT = ExceptionSpec().ResolveExceptionSpec(
+              MemberNameInfo.getLoc(), FPT))
         E->setType(Context.getQualifiedType(NewFPT, Ty.getQualifiers()));
     }
   }
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index ce306021d505b..a4bd5ca1981b7 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8866,7 +8866,8 @@ ExprResult InitializationSequence::Perform(Sema &S,
       // Reference binding does not have any corresponding ASTs.
 
       // Check exception specifications
-      if (S.ExceptionSpec().CheckExceptionSpecCompatibility(CurInit.get(), DestType))
+      if (S.ExceptionSpec().CheckExceptionSpecCompatibility(CurInit.get(),
+                                                            DestType))
         return ExprError();
 
       // We don't check for e.g. function pointers here, since address
@@ -8890,7 +8891,8 @@ ExprResult InitializationSequence::Perform(Sema &S,
       assert(CurInit.get()->isPRValue() && "not a temporary");
 
       // Check exception specifications
-      if (S.ExceptionSpec().CheckExceptionSpecCompatibility(CurInit.get(), DestType))
+      if (S.ExceptionSpec().CheckExceptionSpecCompatibility(CurInit.get(),
+                                                            DestType))
         return ExprError();
 
       QualType MTETy = Step->Type;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 9dd8fcd3c6ef4..db22530177fa7 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -10419,7 +10419,8 @@ bool Sema::CheckFunctionTemplateSpecialization(
   // we have selected the primary template so we can check whether it matches.
   if (getLangOpts().CPlusPlus17 &&
       isUnresolvedExceptionSpec(SpecializationFPT->getExceptionSpecType()) &&
-      !ExceptionSpec().ResolveExceptionSpec(FD->getLocation(), SpecializationFPT))
+      !ExceptionSpec().ResolveExceptionSpec(FD->getLocation(),
+                                            SpecializationFPT))
     return true;
 
   FunctionTemplateSpecializationInfo *SpecInfo
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 3803bb272d1a7..9af79d25e4266 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3501,9 +3501,9 @@ class SavePendingParsedClassStateRAII {
 
   ~SavePendingParsedClassStateRAII() {
     assert(S.ExceptionSpec().DelayedOverridingExceptionSpecChecks.empty() &&
-            "there shouldn't be any pending delayed exception spec checks");
+           "there shouldn't be any pending delayed exception spec checks");
     assert(S.ExceptionSpec().DelayedEquivalentExceptionSpecChecks.empty() &&
-            "there shouldn't be any pending delayed exception spec checsks");
+           "there shouldn't be any pending delayed exception spec checsks");
     swapSavedState();
   }
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 6eed03a48a8c6..53df6964d4b37 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4818,7 +4818,8 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New,
     return true;
 
   if (isa<CXXDestructorDecl>(New) && SemaRef.getLangOpts().CPlusPlus11)
-    SemaRef.ExceptionSpec().AdjustDestructorExceptionSpec(cast<CXXDestructorDecl>(New));
+    SemaRef.ExceptionSpec().AdjustDestructorExceptionSpec(
+        cast<CXXDestructorDecl>(New));
 
   New->setAccess(Tmpl->getAccess());
   if (Tmpl->isVirtualAsWritten())
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 9d1abaed09651..55a5f90acf59e 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -4707,7 +4707,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
     case DeclaratorChunk::Pointer:
       // Verify that we're not building a pointer to pointer to function with
       // exception specification.
-      if (LangOpts.CPlusPlus && S.ExceptionSpec().CheckDistantExceptionSpec(T)) {
+      if (LangOpts.CPlusPlus &&
+          S.ExceptionSpec().CheckDistantExceptionSpec(T)) {
         S.Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec);
         D.setInvalidType(true);
         // Build the type anyway.
@@ -4743,7 +4744,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
     case DeclaratorChunk::Reference: {
       // Verify that we're not building a reference to pointer to function with
       // exception specification.
-      if (LangOpts.CPlusPlus && S.ExceptionSpec().CheckDistantExceptionSpec(T)) {
+      if (LangOpts.CPlusPlus &&
+          S.ExceptionSpec().CheckDistantExceptionSpec(T)) {
         S.Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec);
         D.setInvalidType(true);
         // Build the type anyway.
@@ -4757,7 +4759,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
     case DeclaratorChunk::Array: {
       // Verify that we're not building an array of pointers to function with
       // exception specification.
-      if (LangOpts.CPlusPlus && S.ExceptionSpec().CheckDistantExceptionSpec(T)) {
+      if (LangOpts.CPlusPlus &&
+          S.ExceptionSpec().CheckDistantExceptionSpec(T)) {
         S.Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec);
         D.setInvalidType(true);
         // Build the type anyway.
@@ -5257,13 +5260,10 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
           NoexceptExpr = FTI.NoexceptExpr;
         }
 
-        S.ExceptionSpec().checkExceptionSpecification(D.isFunctionDeclarationContext(),
-                                      FTI.getExceptionSpecType(),
-                                      DynamicExceptions,
-                                      DynamicExceptionRanges,
-                                      NoexceptExpr,
-                                      Exceptions,
-                                      EPI.ExceptionSpec);
+        S.ExceptionSpec().checkExceptionSpecification(
+            D.isFunctionDeclarationContext(), FTI.getExceptionSpecType(),
+            DynamicExceptions, DynamicExceptionRanges, NoexceptExpr, Exceptions,
+            EPI.ExceptionSpec);
 
         // FIXME: Set address space from attrs for C++ mode here.
         // OpenCLCPlusPlus: A class member function has an address space.
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 85ee495ea9fc0..e86c77c9b7330 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -6368,14 +6368,16 @@ bool TreeTransform<Derived>::TransformExceptionSpec(
         Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), ArgIdx);
 
         QualType U = getDerived().TransformType(PackExpansion->getPattern());
-        if (U.isNull() || SemaRef.ExceptionSpec().CheckSpecifiedExceptionType(U, Loc))
+        if (U.isNull() ||
+            SemaRef.ExceptionSpec().CheckSpecifiedExceptionType(U, Loc))
           return true;
 
         Exceptions.push_back(U);
       }
     } else {
       QualType U = getDerived().TransformType(T);
-      if (U.isNull() || SemaRef.ExceptionSpec().CheckSpecifiedExceptionType(U, Loc))
+      if (U.isNull() ||
+          SemaRef.ExceptionSpec().CheckSpecifiedExceptionType(U, Loc))
         return true;
       if (T != U)
         Changed = true;



More information about the cfe-commits mailing list