summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/clang/lib/Sema/SemaExceptionSpec.cpp
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
committerpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
commite5dd70708596ae51455a0ffa086a00c5b29f8583 (patch)
tree5d676f27b570bacf71e786c3b5cff3e6f6679b59 /gnu/llvm/clang/lib/Sema/SemaExceptionSpec.cpp
parentImport LLVM 10.0.0 release including clang, lld and lldb. (diff)
downloadwireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.tar.xz
wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.zip
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom tested by plenty
Diffstat (limited to 'gnu/llvm/clang/lib/Sema/SemaExceptionSpec.cpp')
-rw-r--r--gnu/llvm/clang/lib/Sema/SemaExceptionSpec.cpp1565
1 files changed, 1565 insertions, 0 deletions
diff --git a/gnu/llvm/clang/lib/Sema/SemaExceptionSpec.cpp b/gnu/llvm/clang/lib/Sema/SemaExceptionSpec.cpp
new file mode 100644
index 00000000000..193eaa3e01f
--- /dev/null
+++ b/gnu/llvm/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -0,0 +1,1565 @@
+//===--- SemaExceptionSpec.cpp - C++ Exception Specifications ---*- 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 provides Sema routines for C++ exception specification testing.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Sema/SemaInternal.h"
+#include "clang/AST/ASTMutationListener.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallString.h"
+
+namespace clang {
+
+static const FunctionProtoType *GetUnderlyingFunction(QualType T)
+{
+ if (const PointerType *PtrTy = T->getAs<PointerType>())
+ T = PtrTy->getPointeeType();
+ else if (const ReferenceType *RefTy = T->getAs<ReferenceType>())
+ T = RefTy->getPointeeType();
+ else if (const MemberPointerType *MPTy = T->getAs<MemberPointerType>())
+ T = MPTy->getPointeeType();
+ return T->getAs<FunctionProtoType>();
+}
+
+/// HACK: libstdc++ has a bug where it shadows std::swap with a member
+/// swap function then tries to call std::swap unqualified from the exception
+/// specification of that function. This function detects whether we're in
+/// such a case and turns off delay-parsing of exception specifications.
+bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
+ auto *RD = dyn_cast<CXXRecordDecl>(CurContext);
+
+ // All the problem cases are member functions named "swap" within class
+ // templates declared directly within namespace std or std::__debug or
+ // std::__profile.
+ if (!RD || !RD->getIdentifier() || !RD->getDescribedClassTemplate() ||
+ !D.getIdentifier() || !D.getIdentifier()->isStr("swap"))
+ return false;
+
+ auto *ND = dyn_cast<NamespaceDecl>(RD->getDeclContext());
+ if (!ND)
+ return false;
+
+ bool IsInStd = ND->isStdNamespace();
+ if (!IsInStd) {
+ // This isn't a direct member of namespace std, but it might still be
+ // libstdc++'s std::__debug::array or std::__profile::array.
+ IdentifierInfo *II = ND->getIdentifier();
+ if (!II || !(II->isStr("__debug") || II->isStr("__profile")) ||
+ !ND->isInStdNamespace())
+ return false;
+ }
+
+ // Only apply this hack within a system header.
+ if (!Context.getSourceManager().isInSystemHeader(D.getBeginLoc()))
+ return false;
+
+ return llvm::StringSwitch<bool>(RD->getIdentifier()->getName())
+ .Case("array", true)
+ .Case("pair", IsInStd)
+ .Case("priority_queue", IsInStd)
+ .Case("stack", IsInStd)
+ .Case("queue", IsInStd)
+ .Default(false);
+}
+
+ExprResult Sema::ActOnNoexceptSpec(SourceLocation NoexceptLoc,
+ Expr *NoexceptExpr,
+ ExceptionSpecificationType &EST) {
+ // FIXME: This is bogus, a noexcept expression is not a condition.
+ ExprResult Converted = CheckBooleanCondition(NoexceptLoc, NoexceptExpr);
+ if (Converted.isInvalid()) {
+ EST = EST_NoexceptFalse;
+
+ // Fill in an expression of 'false' as a fixup.
+ auto *BoolExpr = new (Context)
+ CXXBoolLiteralExpr(false, Context.BoolTy, NoexceptExpr->getBeginLoc());
+ llvm::APSInt Value{1};
+ Value = 0;
+ return ConstantExpr::Create(Context, BoolExpr, APValue{Value});
+ }
+
+ if (Converted.get()->isValueDependent()) {
+ EST = EST_DependentNoexcept;
+ return Converted;
+ }
+
+ llvm::APSInt Result;
+ Converted = VerifyIntegerConstantExpression(
+ Converted.get(), &Result,
+ diag::err_noexcept_needs_constant_expression,
+ /*AllowFold*/ false);
+ if (!Converted.isInvalid())
+ EST = !Result ? EST_NoexceptFalse : EST_NoexceptTrue;
+ return Converted;
+}
+
+/// CheckSpecifiedExceptionType - Check if the given type is valid in an
+/// exception specification. Incomplete types, or pointers to incomplete types
+/// other than void are not allowed.
+///
+/// \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) {
+ // 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
+ // "pointer to function returning T", respectively.
+ //
+ // We also apply this rule in C++98.
+ if (T->isArrayType())
+ T = Context.getArrayDecayedType(T);
+ else if (T->isFunctionType())
+ T = Context.getPointerType(T);
+
+ int Kind = 0;
+ QualType PointeeT = T;
+ if (const PointerType *PT = T->getAs<PointerType>()) {
+ PointeeT = PT->getPointeeType();
+ Kind = 1;
+
+ // cv void* is explicitly permitted, despite being a pointer to an
+ // incomplete type.
+ if (PointeeT->isVoidType())
+ return false;
+ } else if (const ReferenceType *RT = T->getAs<ReferenceType>()) {
+ PointeeT = RT->getPointeeType();
+ Kind = 2;
+
+ if (RT->isRValueReferenceType()) {
+ // C++11 [except.spec]p2:
+ // A type denoted in an exception-specification shall not denote [...]
+ // an rvalue reference type.
+ Diag(Range.getBegin(), diag::err_rref_in_exception_spec)
+ << T << Range;
+ return true;
+ }
+ }
+
+ // C++11 [except.spec]p2:
+ // A type denoted in an exception-specification shall not denote an
+ // incomplete type other than a class currently being defined [...].
+ // A type denoted in an exception-specification shall not denote a
+ // pointer or reference to an incomplete type, other than (cv) void* or a
+ // pointer or reference to a class currently being defined.
+ // In Microsoft mode, downgrade this to a warning.
+ unsigned DiagID = diag::err_incomplete_in_exception_spec;
+ bool ReturnValueOnError = true;
+ if (getLangOpts().MSVCCompat) {
+ DiagID = diag::ext_incomplete_in_exception_spec;
+ ReturnValueOnError = false;
+ }
+ if (!(PointeeT->isRecordType() &&
+ PointeeT->castAs<RecordType>()->isBeingDefined()) &&
+ RequireCompleteType(Range.getBegin(), PointeeT, DiagID, Kind, Range))
+ return ReturnValueOnError;
+
+ return false;
+}
+
+/// 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) {
+ // C++17 removes this rule in favor of putting exception specifications into
+ // the type system.
+ if (getLangOpts().CPlusPlus17)
+ return false;
+
+ if (const PointerType *PT = T->getAs<PointerType>())
+ T = PT->getPointeeType();
+ else if (const MemberPointerType *PT = T->getAs<MemberPointerType>())
+ T = PT->getPointeeType();
+ else
+ return false;
+
+ const FunctionProtoType *FnT = T->getAs<FunctionProtoType>();
+ if (!FnT)
+ return false;
+
+ return FnT->hasExceptionSpec();
+}
+
+const FunctionProtoType *
+Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) {
+ if (FPT->getExceptionSpecType() == EST_Unparsed) {
+ Diag(Loc, diag::err_exception_spec_not_parsed);
+ return nullptr;
+ }
+
+ if (!isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
+ return FPT;
+
+ FunctionDecl *SourceDecl = FPT->getExceptionSpecDecl();
+ const FunctionProtoType *SourceFPT =
+ SourceDecl->getType()->castAs<FunctionProtoType>();
+
+ // If the exception specification has already been resolved, just return it.
+ if (!isUnresolvedExceptionSpec(SourceFPT->getExceptionSpecType()))
+ return SourceFPT;
+
+ // Compute or instantiate the exception specification now.
+ if (SourceFPT->getExceptionSpecType() == EST_Unevaluated)
+ EvaluateImplicitExceptionSpec(Loc, SourceDecl);
+ else
+ InstantiateExceptionSpec(Loc, SourceDecl);
+
+ const FunctionProtoType *Proto =
+ SourceDecl->getType()->castAs<FunctionProtoType>();
+ if (Proto->getExceptionSpecType() == clang::EST_Unparsed) {
+ Diag(Loc, diag::err_exception_spec_not_parsed);
+ Proto = nullptr;
+ }
+ return Proto;
+}
+
+void
+Sema::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())
+ Listener->ResolvedExceptionSpec(FD);
+
+ for (FunctionDecl *Redecl : FD->redecls())
+ Context.adjustExceptionSpec(Redecl, ESI);
+}
+
+static bool exceptionSpecNotKnownYet(const FunctionDecl *FD) {
+ auto *MD = dyn_cast<CXXMethodDecl>(FD);
+ if (!MD)
+ return false;
+
+ auto EST = MD->getType()->castAs<FunctionProtoType>()->getExceptionSpecType();
+ return EST == EST_Unparsed ||
+ (EST == EST_Unevaluated && MD->getParent()->isBeingDefined());
+}
+
+static bool CheckEquivalentExceptionSpecImpl(
+ Sema &S, const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID,
+ const FunctionProtoType *Old, SourceLocation OldLoc,
+ const FunctionProtoType *New, SourceLocation NewLoc,
+ bool *MissingExceptionSpecification = nullptr,
+ bool *MissingEmptyExceptionSpecification = nullptr,
+ bool AllowNoexceptAllMatchWithNoSpec = false, bool IsOperatorNew = false);
+
+/// Determine whether a function has an implicitly-generated exception
+/// specification.
+static bool hasImplicitExceptionSpec(FunctionDecl *Decl) {
+ if (!isa<CXXDestructorDecl>(Decl) &&
+ Decl->getDeclName().getCXXOverloadedOperator() != OO_Delete &&
+ Decl->getDeclName().getCXXOverloadedOperator() != OO_Array_Delete)
+ return false;
+
+ // For a function that the user didn't declare:
+ // - if this is a destructor, its exception specification is implicit.
+ // - if this is 'operator delete' or 'operator delete[]', the exception
+ // specification is as-if an explicit exception specification was given
+ // (per [basic.stc.dynamic]p2).
+ if (!Decl->getTypeSourceInfo())
+ return isa<CXXDestructorDecl>(Decl);
+
+ auto *Ty = Decl->getTypeSourceInfo()->getType()->castAs<FunctionProtoType>();
+ return !Ty->hasExceptionSpec();
+}
+
+bool Sema::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.
+ if (!getLangOpts().CXXExceptions && !getLangOpts().CPlusPlus17)
+ return false;
+
+ OverloadedOperatorKind OO = New->getDeclName().getCXXOverloadedOperator();
+ bool IsOperatorNew = OO == OO_New || OO == OO_Array_New;
+ bool MissingExceptionSpecification = false;
+ bool MissingEmptyExceptionSpecification = false;
+
+ unsigned DiagID = diag::err_mismatched_exception_spec;
+ bool ReturnValueOnError = true;
+ if (getLangOpts().MSVCCompat) {
+ DiagID = diag::ext_mismatched_exception_spec;
+ ReturnValueOnError = false;
+ }
+
+ // If we're befriending a member function of a class that's currently being
+ // defined, we might not be able to work out its exception specification yet.
+ // If not, defer the check until later.
+ if (exceptionSpecNotKnownYet(Old) || exceptionSpecNotKnownYet(New)) {
+ DelayedEquivalentExceptionSpecChecks.push_back({New, Old});
+ return false;
+ }
+
+ // 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),
+ 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
+ // not specify an exception-specification.
+ if (getLangOpts().CPlusPlus11 && getLangOpts().CXXExceptions &&
+ hasImplicitExceptionSpec(Old) != hasImplicitExceptionSpec(New)) {
+ Diag(New->getLocation(), diag::ext_implicit_exception_spec_mismatch)
+ << hasImplicitExceptionSpec(Old);
+ if (Old->getLocation().isValid())
+ Diag(Old->getLocation(), diag::note_previous_declaration);
+ }
+ return false;
+ }
+
+ // The failure was something other than an missing exception
+ // specification; return an error, except in MS mode where this is a warning.
+ if (!MissingExceptionSpecification)
+ return ReturnValueOnError;
+
+ const FunctionProtoType *NewProto =
+ New->getType()->castAs<FunctionProtoType>();
+
+ // The new function declaration is only missing an empty exception
+ // specification "throw()". If the throw() specification came from a
+ // function in a system header that has C linkage, just add an empty
+ // exception specification to the "new" declaration. Note that C library
+ // implementations are permitted to add these nothrow exception
+ // specifications.
+ //
+ // Likewise if the old function is a builtin.
+ if (MissingEmptyExceptionSpecification && NewProto &&
+ (Old->getLocation().isInvalid() ||
+ Context.getSourceManager().isInSystemHeader(Old->getLocation()) ||
+ Old->getBuiltinID()) &&
+ Old->isExternC()) {
+ New->setType(Context.getFunctionType(
+ NewProto->getReturnType(), NewProto->getParamTypes(),
+ NewProto->getExtProtoInfo().withExceptionSpec(EST_DynamicNone)));
+ return false;
+ }
+
+ const FunctionProtoType *OldProto =
+ Old->getType()->castAs<FunctionProtoType>();
+
+ FunctionProtoType::ExceptionSpecInfo ESI = OldProto->getExceptionSpecType();
+ if (ESI.Type == EST_Dynamic) {
+ // FIXME: What if the exceptions are described in terms of the old
+ // prototype's parameters?
+ ESI.Exceptions = OldProto->exceptions();
+ }
+
+ if (ESI.Type == EST_NoexceptFalse)
+ ESI.Type = EST_None;
+ if (ESI.Type == EST_NoexceptTrue)
+ ESI.Type = EST_BasicNoexcept;
+
+ // For dependent noexcept, we can't just take the expression from the old
+ // prototype. It likely contains references to the old prototype's parameters.
+ if (ESI.Type == EST_DependentNoexcept) {
+ New->setInvalidDecl();
+ } else {
+ // Update the type of the function with the appropriate exception
+ // specification.
+ New->setType(Context.getFunctionType(
+ NewProto->getReturnType(), NewProto->getParamTypes(),
+ NewProto->getExtProtoInfo().withExceptionSpec(ESI)));
+ }
+
+ if (getLangOpts().MSVCCompat && ESI.Type != EST_DependentNoexcept) {
+ // Allow missing exception specifications in redeclarations as an extension.
+ DiagID = diag::ext_ms_missing_exception_specification;
+ ReturnValueOnError = false;
+ } else if (New->isReplaceableGlobalAllocationFunction() &&
+ ESI.Type != EST_DependentNoexcept) {
+ // Allow missing exception specifications in redeclarations as an extension,
+ // when declaring a replaceable global allocation function.
+ DiagID = diag::ext_missing_exception_specification;
+ ReturnValueOnError = false;
+ } else if (ESI.Type == EST_NoThrow) {
+ // Allow missing attribute 'nothrow' in redeclarations, since this is a very
+ // common omission.
+ DiagID = diag::ext_missing_exception_specification;
+ ReturnValueOnError = false;
+ } else {
+ DiagID = diag::err_missing_exception_specification;
+ ReturnValueOnError = true;
+ }
+
+ // Warn about the lack of exception specification.
+ SmallString<128> ExceptionSpecString;
+ llvm::raw_svector_ostream OS(ExceptionSpecString);
+ switch (OldProto->getExceptionSpecType()) {
+ case EST_DynamicNone:
+ OS << "throw()";
+ break;
+
+ case EST_Dynamic: {
+ OS << "throw(";
+ bool OnFirstException = true;
+ for (const auto &E : OldProto->exceptions()) {
+ if (OnFirstException)
+ OnFirstException = false;
+ else
+ OS << ", ";
+
+ OS << E.getAsString(getPrintingPolicy());
+ }
+ OS << ")";
+ break;
+ }
+
+ case EST_BasicNoexcept:
+ OS << "noexcept";
+ break;
+
+ case EST_DependentNoexcept:
+ case EST_NoexceptFalse:
+ case EST_NoexceptTrue:
+ OS << "noexcept(";
+ assert(OldProto->getNoexceptExpr() != nullptr && "Expected non-null Expr");
+ OldProto->getNoexceptExpr()->printPretty(OS, nullptr, getPrintingPolicy());
+ OS << ")";
+ break;
+ case EST_NoThrow:
+ OS <<"__attribute__((nothrow))";
+ break;
+ case EST_None:
+ case EST_MSAny:
+ case EST_Unevaluated:
+ case EST_Uninstantiated:
+ case EST_Unparsed:
+ llvm_unreachable("This spec type is compatible with none.");
+ }
+
+ SourceLocation FixItLoc;
+ if (TypeSourceInfo *TSInfo = New->getTypeSourceInfo()) {
+ TypeLoc TL = TSInfo->getTypeLoc().IgnoreParens();
+ // FIXME: Preserve enough information so that we can produce a correct fixit
+ // location when there is a trailing return type.
+ if (auto FTLoc = TL.getAs<FunctionProtoTypeLoc>())
+ if (!FTLoc.getTypePtr()->hasTrailingReturn())
+ FixItLoc = getLocForEndOfToken(FTLoc.getLocalRangeEnd());
+ }
+
+ if (FixItLoc.isInvalid())
+ Diag(New->getLocation(), DiagID)
+ << New << OS.str();
+ else {
+ Diag(New->getLocation(), DiagID)
+ << New << OS.str()
+ << FixItHint::CreateInsertion(FixItLoc, " " + OS.str().str());
+ }
+
+ if (Old->getLocation().isValid())
+ Diag(Old->getLocation(), diag::note_previous_declaration);
+
+ return ReturnValueOnError;
+}
+
+/// CheckEquivalentExceptionSpec - Check if the two types have equivalent
+/// exception specifications. Exception specifications are equivalent if
+/// they allow exactly the same set of exception types. It does not matter how
+/// that is achieved. See C++ [except.spec]p2.
+bool Sema::CheckEquivalentExceptionSpec(
+ const FunctionProtoType *Old, SourceLocation OldLoc,
+ const FunctionProtoType *New, SourceLocation NewLoc) {
+ if (!getLangOpts().CXXExceptions)
+ return false;
+
+ unsigned DiagID = diag::err_mismatched_exception_spec;
+ if (getLangOpts().MSVCCompat)
+ DiagID = diag::ext_mismatched_exception_spec;
+ bool Result = CheckEquivalentExceptionSpecImpl(
+ *this, PDiag(DiagID), PDiag(diag::note_previous_declaration),
+ Old, OldLoc, New, NewLoc);
+
+ // In Microsoft mode, mismatching exception specifications just cause a warning.
+ if (getLangOpts().MSVCCompat)
+ return false;
+ return Result;
+}
+
+/// CheckEquivalentExceptionSpec - Check if the two types have compatible
+/// exception specifications. See C++ [except.spec]p3.
+///
+/// \return \c false if the exception specifications match, \c true if there is
+/// a problem. If \c true is returned, either a diagnostic has already been
+/// produced or \c *MissingExceptionSpecification is set to \c true.
+static bool CheckEquivalentExceptionSpecImpl(
+ Sema &S, const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID,
+ const FunctionProtoType *Old, SourceLocation OldLoc,
+ const FunctionProtoType *New, SourceLocation NewLoc,
+ bool *MissingExceptionSpecification,
+ bool *MissingEmptyExceptionSpecification,
+ bool AllowNoexceptAllMatchWithNoSpec, bool IsOperatorNew) {
+ if (MissingExceptionSpecification)
+ *MissingExceptionSpecification = false;
+
+ if (MissingEmptyExceptionSpecification)
+ *MissingEmptyExceptionSpecification = false;
+
+ Old = S.ResolveExceptionSpec(NewLoc, Old);
+ if (!Old)
+ return false;
+ New = S.ResolveExceptionSpec(NewLoc, New);
+ if (!New)
+ return false;
+
+ // C++0x [except.spec]p3: Two exception-specifications are compatible if:
+ // - both are non-throwing, regardless of their form,
+ // - both have the form noexcept(constant-expression) and the constant-
+ // expressions are equivalent,
+ // - both are dynamic-exception-specifications that have the same set of
+ // adjusted types.
+ //
+ // C++0x [except.spec]p12: An exception-specification is non-throwing if it is
+ // of the form throw(), noexcept, or noexcept(constant-expression) where the
+ // constant-expression yields true.
+ //
+ // C++0x [except.spec]p4: If any declaration of a function has an exception-
+ // specifier that is not a noexcept-specification allowing all exceptions,
+ // all declarations [...] of that function shall have a compatible
+ // exception-specification.
+ //
+ // That last point basically means that noexcept(false) matches no spec.
+ // It's considered when AllowNoexceptAllMatchWithNoSpec is true.
+
+ ExceptionSpecificationType OldEST = Old->getExceptionSpecType();
+ ExceptionSpecificationType NewEST = New->getExceptionSpecType();
+
+ assert(!isUnresolvedExceptionSpec(OldEST) &&
+ !isUnresolvedExceptionSpec(NewEST) &&
+ "Shouldn't see unknown exception specifications here");
+
+ CanThrowResult OldCanThrow = Old->canThrow();
+ CanThrowResult NewCanThrow = New->canThrow();
+
+ // Any non-throwing specifications are compatible.
+ if (OldCanThrow == CT_Cannot && NewCanThrow == CT_Cannot)
+ return false;
+
+ // Any throws-anything specifications are usually compatible.
+ if (OldCanThrow == CT_Can && OldEST != EST_Dynamic &&
+ NewCanThrow == CT_Can && NewEST != EST_Dynamic) {
+ // The exception is that the absence of an exception specification only
+ // matches noexcept(false) for functions, as described above.
+ if (!AllowNoexceptAllMatchWithNoSpec &&
+ ((OldEST == EST_None && NewEST == EST_NoexceptFalse) ||
+ (OldEST == EST_NoexceptFalse && NewEST == EST_None))) {
+ // This is the disallowed case.
+ } else {
+ return false;
+ }
+ }
+
+ // C++14 [except.spec]p3:
+ // Two exception-specifications are compatible if [...] both have the form
+ // noexcept(constant-expression) and the constant-expressions are equivalent
+ if (OldEST == EST_DependentNoexcept && NewEST == EST_DependentNoexcept) {
+ llvm::FoldingSetNodeID OldFSN, NewFSN;
+ Old->getNoexceptExpr()->Profile(OldFSN, S.Context, true);
+ New->getNoexceptExpr()->Profile(NewFSN, S.Context, true);
+ if (OldFSN == NewFSN)
+ return false;
+ }
+
+ // Dynamic exception specifications with the same set of adjusted types
+ // are compatible.
+ if (OldEST == EST_Dynamic && NewEST == EST_Dynamic) {
+ bool Success = true;
+ // Both have a dynamic exception spec. Collect the first set, then compare
+ // to the second.
+ llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes;
+ for (const auto &I : Old->exceptions())
+ OldTypes.insert(S.Context.getCanonicalType(I).getUnqualifiedType());
+
+ for (const auto &I : New->exceptions()) {
+ CanQualType TypePtr = S.Context.getCanonicalType(I).getUnqualifiedType();
+ if (OldTypes.count(TypePtr))
+ NewTypes.insert(TypePtr);
+ else {
+ Success = false;
+ break;
+ }
+ }
+
+ if (Success && OldTypes.size() == NewTypes.size())
+ return false;
+ }
+
+ // As a special compatibility feature, under C++0x we accept no spec and
+ // throw(std::bad_alloc) as equivalent for operator new and operator new[].
+ // This is because the implicit declaration changed, but old code would break.
+ if (S.getLangOpts().CPlusPlus11 && IsOperatorNew) {
+ const FunctionProtoType *WithExceptions = nullptr;
+ if (OldEST == EST_None && NewEST == EST_Dynamic)
+ WithExceptions = New;
+ else if (OldEST == EST_Dynamic && NewEST == EST_None)
+ WithExceptions = Old;
+ if (WithExceptions && WithExceptions->getNumExceptions() == 1) {
+ // One has no spec, the other throw(something). If that something is
+ // std::bad_alloc, all conditions are met.
+ QualType Exception = *WithExceptions->exception_begin();
+ if (CXXRecordDecl *ExRecord = Exception->getAsCXXRecordDecl()) {
+ IdentifierInfo* Name = ExRecord->getIdentifier();
+ if (Name && Name->getName() == "bad_alloc") {
+ // It's called bad_alloc, but is it in std?
+ if (ExRecord->isInStdNamespace()) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ // If the caller wants to handle the case that the new function is
+ // incompatible due to a missing exception specification, let it.
+ if (MissingExceptionSpecification && OldEST != EST_None &&
+ NewEST == EST_None) {
+ // The old type has an exception specification of some sort, but
+ // the new type does not.
+ *MissingExceptionSpecification = true;
+
+ if (MissingEmptyExceptionSpecification && OldCanThrow == CT_Cannot) {
+ // The old type has a throw() or noexcept(true) exception specification
+ // and the new type has no exception specification, and the caller asked
+ // to handle this itself.
+ *MissingEmptyExceptionSpecification = true;
+ }
+
+ return true;
+ }
+
+ S.Diag(NewLoc, DiagID);
+ if (NoteID.getDiagID() != 0 && OldLoc.isValid())
+ S.Diag(OldLoc, NoteID);
+ return true;
+}
+
+bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
+ const PartialDiagnostic &NoteID,
+ const FunctionProtoType *Old,
+ SourceLocation OldLoc,
+ const FunctionProtoType *New,
+ SourceLocation NewLoc) {
+ if (!getLangOpts().CXXExceptions)
+ return false;
+ return CheckEquivalentExceptionSpecImpl(*this, DiagID, NoteID, Old, OldLoc,
+ New, NewLoc);
+}
+
+bool Sema::handlerCanCatch(QualType HandlerType, QualType ExceptionType) {
+ // [except.handle]p3:
+ // A handler is a match for an exception object of type E if:
+
+ // HandlerType must be ExceptionType or derived from it, or pointer or
+ // reference to such types.
+ const ReferenceType *RefTy = HandlerType->getAs<ReferenceType>();
+ if (RefTy)
+ HandlerType = RefTy->getPointeeType();
+
+ // -- the handler is of type cv T or cv T& and E and T are the same type
+ if (Context.hasSameUnqualifiedType(ExceptionType, HandlerType))
+ return true;
+
+ // FIXME: ObjC pointer types?
+ if (HandlerType->isPointerType() || HandlerType->isMemberPointerType()) {
+ if (RefTy && (!HandlerType.isConstQualified() ||
+ HandlerType.isVolatileQualified()))
+ return false;
+
+ // -- the handler is of type cv T or const T& where T is a pointer or
+ // pointer to member type and E is std::nullptr_t
+ if (ExceptionType->isNullPtrType())
+ return true;
+
+ // -- the handler is of type cv T or const T& where T is a pointer or
+ // pointer to member type and E is a pointer or pointer to member type
+ // that can be converted to T by one or more of
+ // -- a qualification conversion
+ // -- a function pointer conversion
+ bool LifetimeConv;
+ QualType Result;
+ // FIXME: Should we treat the exception as catchable if a lifetime
+ // conversion is required?
+ if (IsQualificationConversion(ExceptionType, HandlerType, false,
+ LifetimeConv) ||
+ IsFunctionConversion(ExceptionType, HandlerType, Result))
+ return true;
+
+ // -- a standard pointer conversion [...]
+ if (!ExceptionType->isPointerType() || !HandlerType->isPointerType())
+ return false;
+
+ // Handle the "qualification conversion" portion.
+ Qualifiers EQuals, HQuals;
+ ExceptionType = Context.getUnqualifiedArrayType(
+ ExceptionType->getPointeeType(), EQuals);
+ HandlerType = Context.getUnqualifiedArrayType(
+ HandlerType->getPointeeType(), HQuals);
+ if (!HQuals.compatiblyIncludes(EQuals))
+ return false;
+
+ if (HandlerType->isVoidType() && ExceptionType->isObjectType())
+ return true;
+
+ // The only remaining case is a derived-to-base conversion.
+ }
+
+ // -- the handler is of type cg T or cv T& and T is an unambiguous public
+ // base class of E
+ if (!ExceptionType->isRecordType() || !HandlerType->isRecordType())
+ return false;
+ CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+ if (!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,
+ Paths.front(),
+ /*Diagnostic*/ 0,
+ /*ForceCheck*/ true,
+ /*ForceUnprivileged*/ true)) {
+ case AR_accessible: return true;
+ case AR_inaccessible: return false;
+ case AR_dependent:
+ llvm_unreachable("access check dependent for unprivileged context");
+ case AR_delayed:
+ llvm_unreachable("access check delayed in non-declaration");
+ }
+ llvm_unreachable("unexpected access check result");
+}
+
+/// 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(const PartialDiagnostic &DiagID,
+ const PartialDiagnostic &NestedDiagID,
+ const PartialDiagnostic &NoteID,
+ const PartialDiagnostic &NoThrowDiagID,
+ const FunctionProtoType *Superset,
+ SourceLocation SuperLoc,
+ const FunctionProtoType *Subset,
+ SourceLocation SubLoc) {
+
+ // Just auto-succeed under -fno-exceptions.
+ if (!getLangOpts().CXXExceptions)
+ return false;
+
+ // FIXME: As usual, we could be more specific in our error messages, but
+ // that better waits until we've got types with source locations.
+
+ if (!SubLoc.isValid())
+ SubLoc = SuperLoc;
+
+ // Resolve the exception specifications, if needed.
+ Superset = ResolveExceptionSpec(SuperLoc, Superset);
+ if (!Superset)
+ return false;
+ Subset = ResolveExceptionSpec(SubLoc, Subset);
+ if (!Subset)
+ return false;
+
+ ExceptionSpecificationType SuperEST = Superset->getExceptionSpecType();
+ ExceptionSpecificationType SubEST = Subset->getExceptionSpecType();
+ assert(!isUnresolvedExceptionSpec(SuperEST) &&
+ !isUnresolvedExceptionSpec(SubEST) &&
+ "Shouldn't see unknown exception specifications here");
+
+ // If there are dependent noexcept specs, assume everything is fine. Unlike
+ // with the equivalency check, this is safe in this case, because we don't
+ // want to merge declarations. Checks after instantiation will catch any
+ // omissions we make here.
+ if (SuperEST == EST_DependentNoexcept || SubEST == EST_DependentNoexcept)
+ return false;
+
+ CanThrowResult SuperCanThrow = Superset->canThrow();
+ CanThrowResult SubCanThrow = Subset->canThrow();
+
+ // If the superset contains everything or the subset contains nothing, we're
+ // done.
+ if ((SuperCanThrow == CT_Can && SuperEST != EST_Dynamic) ||
+ SubCanThrow == CT_Cannot)
+ return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc,
+ Subset, SubLoc);
+
+ // Allow __declspec(nothrow) to be missing on redeclaration as an extension in
+ // some cases.
+ if (NoThrowDiagID.getDiagID() != 0 && SubCanThrow == CT_Can &&
+ SuperCanThrow == CT_Cannot && SuperEST == EST_NoThrow) {
+ Diag(SubLoc, NoThrowDiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(SuperLoc, NoteID);
+ return true;
+ }
+
+ // If the subset contains everything or the superset contains nothing, we've
+ // failed.
+ if ((SubCanThrow == CT_Can && SubEST != EST_Dynamic) ||
+ SuperCanThrow == CT_Cannot) {
+ Diag(SubLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(SuperLoc, NoteID);
+ return true;
+ }
+
+ assert(SuperEST == EST_Dynamic && SubEST == EST_Dynamic &&
+ "Exception spec subset: non-dynamic case slipped through.");
+
+ // Neither contains everything or nothing. Do a proper comparison.
+ for (QualType SubI : Subset->exceptions()) {
+ if (const ReferenceType *RefTy = SubI->getAs<ReferenceType>())
+ SubI = RefTy->getPointeeType();
+
+ // Make sure it's in the superset.
+ bool Contained = false;
+ for (QualType SuperI : Superset->exceptions()) {
+ // [except.spec]p5:
+ // the target entity shall allow at least the exceptions allowed by the
+ // source
+ //
+ // We interpret this as meaning that a handler for some target type would
+ // catch an exception of each source type.
+ if (handlerCanCatch(SuperI, SubI)) {
+ Contained = true;
+ break;
+ }
+ }
+ if (!Contained) {
+ Diag(SubLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(SuperLoc, NoteID);
+ return true;
+ }
+ }
+ // We've run half the gauntlet.
+ return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc,
+ Subset, SubLoc);
+}
+
+static bool
+CheckSpecForTypesEquivalent(Sema &S, const PartialDiagnostic &DiagID,
+ const PartialDiagnostic &NoteID, QualType Target,
+ SourceLocation TargetLoc, QualType Source,
+ SourceLocation SourceLoc) {
+ const FunctionProtoType *TFunc = GetUnderlyingFunction(Target);
+ if (!TFunc)
+ return false;
+ const FunctionProtoType *SFunc = GetUnderlyingFunction(Source);
+ if (!SFunc)
+ return false;
+
+ return S.CheckEquivalentExceptionSpec(DiagID, NoteID, TFunc, TargetLoc,
+ SFunc, SourceLoc);
+}
+
+/// CheckParamExceptionSpec - Check if the parameter and return types of the
+/// two functions have equivalent exception specs. This is part of the
+/// 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(const PartialDiagnostic &DiagID,
+ const PartialDiagnostic &NoteID,
+ const FunctionProtoType *Target,
+ SourceLocation TargetLoc,
+ const FunctionProtoType *Source,
+ SourceLocation SourceLoc) {
+ auto RetDiag = DiagID;
+ RetDiag << 0;
+ if (CheckSpecForTypesEquivalent(
+ *this, RetDiag, PDiag(),
+ Target->getReturnType(), TargetLoc, Source->getReturnType(),
+ SourceLoc))
+ return true;
+
+ // We shouldn't even be testing this unless the arguments are otherwise
+ // compatible.
+ assert(Target->getNumParams() == Source->getNumParams() &&
+ "Functions have different argument counts.");
+ for (unsigned i = 0, E = Target->getNumParams(); i != E; ++i) {
+ auto ParamDiag = DiagID;
+ ParamDiag << 1;
+ if (CheckSpecForTypesEquivalent(
+ *this, ParamDiag, PDiag(),
+ Target->getParamType(i), TargetLoc, Source->getParamType(i),
+ SourceLoc))
+ return true;
+ }
+ return false;
+}
+
+bool Sema::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);
+ if (!ToFunc || ToFunc->hasDependentExceptionSpec())
+ return false;
+
+ // SourceType must be a function or function pointer.
+ const FunctionProtoType *FromFunc = GetUnderlyingFunction(From->getType());
+ if (!FromFunc || FromFunc->hasDependentExceptionSpec())
+ return false;
+
+ unsigned DiagID = diag::err_incompatible_exception_specs;
+ unsigned NestedDiagID = diag::err_deep_exception_specs_differ;
+ // This is not an error in C++17 onwards, unless the noexceptness doesn't
+ // match, but in that case we have a full-on type mismatch, not just a
+ // type sugar mismatch.
+ if (getLangOpts().CPlusPlus17) {
+ DiagID = diag::warn_incompatible_exception_specs;
+ NestedDiagID = diag::warn_deep_exception_specs_differ;
+ }
+
+ // Now we've got the correct types on both sides, check their compatibility.
+ // This means that the source of the conversion can only throw a subset of
+ // the exceptions of the target, and any exception specs on arguments or
+ // return types must be equivalent.
+ //
+ // FIXME: If there is a nested dependent exception specification, we should
+ // not be checking it here. This is fine:
+ // template<typename T> void f() {
+ // void (*p)(void (*) throw(T));
+ // void (*q)(void (*) throw(int)) = p;
+ // }
+ // ... because it might be instantiated with T=int.
+ return CheckExceptionSpecSubset(
+ PDiag(DiagID), PDiag(NestedDiagID), PDiag(), PDiag(), ToFunc,
+ From->getSourceRange().getBegin(), FromFunc, SourceLocation()) &&
+ !getLangOpts().CPlusPlus17;
+}
+
+bool Sema::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() ==
+ EST_Unparsed)
+ return false;
+
+ // Don't check uninstantiated template destructors at all. We can only
+ // synthesize correct specs after the template is instantiated.
+ if (isa<CXXDestructorDecl>(New) && New->getParent()->isDependentType())
+ return false;
+
+ // If the old exception specification hasn't been parsed yet, or the new
+ // exception specification can't be computed yet, remember that we need to
+ // perform this check when we get to the end of the outermost
+ // lexically-surrounding class.
+ if (exceptionSpecNotKnownYet(Old) || exceptionSpecNotKnownYet(New)) {
+ DelayedOverridingExceptionSpecChecks.push_back({New, Old});
+ return false;
+ }
+
+ unsigned DiagID = diag::err_override_exception_spec;
+ 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),
+ Old->getType()->castAs<FunctionProtoType>(),
+ Old->getLocation(),
+ New->getType()->castAs<FunctionProtoType>(),
+ New->getLocation());
+}
+
+static CanThrowResult canSubStmtsThrow(Sema &Self, const Stmt *S) {
+ CanThrowResult R = CT_Cannot;
+ for (const Stmt *SubStmt : S->children()) {
+ if (!SubStmt)
+ continue;
+ R = mergeCanThrow(R, Self.canThrow(SubStmt));
+ if (R == CT_Can)
+ break;
+ }
+ return R;
+}
+
+/// Determine whether the callee of a particular function call can throw.
+/// E and D are both optional, but at least one of E and Loc must be specified.
+static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D,
+ SourceLocation Loc = SourceLocation()) {
+ // As an extension, we assume that __attribute__((nothrow)) functions don't
+ // throw.
+ if (D && isa<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>())
+ return CT_Cannot;
+
+ QualType T;
+
+ // In C++1z, just look at the function type of the callee.
+ if (S.getLangOpts().CPlusPlus17 && E && isa<CallExpr>(E)) {
+ E = cast<CallExpr>(E)->getCallee();
+ T = E->getType();
+ if (T->isSpecificPlaceholderType(BuiltinType::BoundMember)) {
+ // Sadly we don't preserve the actual type as part of the "bound member"
+ // placeholder, so we need to reconstruct it.
+ E = E->IgnoreParenImpCasts();
+
+ // Could be a call to a pointer-to-member or a plain member access.
+ if (auto *Op = dyn_cast<BinaryOperator>(E)) {
+ assert(Op->getOpcode() == BO_PtrMemD || Op->getOpcode() == BO_PtrMemI);
+ T = Op->getRHS()->getType()
+ ->castAs<MemberPointerType>()->getPointeeType();
+ } else {
+ T = cast<MemberExpr>(E)->getMemberDecl()->getType();
+ }
+ }
+ } else if (const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(D))
+ T = VD->getType();
+ else
+ // If we have no clue what we're calling, assume the worst.
+ return CT_Can;
+
+ const FunctionProtoType *FT;
+ if ((FT = T->getAs<FunctionProtoType>())) {
+ } else if (const PointerType *PT = T->getAs<PointerType>())
+ FT = PT->getPointeeType()->getAs<FunctionProtoType>();
+ else if (const ReferenceType *RT = T->getAs<ReferenceType>())
+ FT = RT->getPointeeType()->getAs<FunctionProtoType>();
+ else if (const MemberPointerType *MT = T->getAs<MemberPointerType>())
+ FT = MT->getPointeeType()->getAs<FunctionProtoType>();
+ else if (const BlockPointerType *BT = T->getAs<BlockPointerType>())
+ FT = BT->getPointeeType()->getAs<FunctionProtoType>();
+
+ if (!FT)
+ return CT_Can;
+
+ FT = S.ResolveExceptionSpec(Loc.isInvalid() ? E->getBeginLoc() : Loc, FT);
+ if (!FT)
+ return CT_Can;
+
+ return FT->canThrow();
+}
+
+static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) {
+ CanThrowResult CT = CT_Cannot;
+
+ // Initialization might throw.
+ if (!VD->isUsableInConstantExpressions(Self.Context))
+ if (const Expr *Init = VD->getInit())
+ CT = mergeCanThrow(CT, Self.canThrow(Init));
+
+ // Destructor might throw.
+ if (VD->needsDestruction(Self.Context) == QualType::DK_cxx_destructor) {
+ if (auto *RD =
+ VD->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl()) {
+ if (auto *Dtor = RD->getDestructor()) {
+ CT = mergeCanThrow(
+ CT, canCalleeThrow(Self, nullptr, Dtor, VD->getLocation()));
+ }
+ }
+ }
+
+ // If this is a decomposition declaration, bindings might throw.
+ if (auto *DD = dyn_cast<DecompositionDecl>(VD))
+ for (auto *B : DD->bindings())
+ if (auto *HD = B->getHoldingVar())
+ CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD));
+
+ return CT;
+}
+
+static CanThrowResult canDynamicCastThrow(const CXXDynamicCastExpr *DC) {
+ if (DC->isTypeDependent())
+ return CT_Dependent;
+
+ if (!DC->getTypeAsWritten()->isReferenceType())
+ return CT_Cannot;
+
+ if (DC->getSubExpr()->isTypeDependent())
+ return CT_Dependent;
+
+ return DC->getCastKind() == clang::CK_Dynamic? CT_Can : CT_Cannot;
+}
+
+static CanThrowResult canTypeidThrow(Sema &S, const CXXTypeidExpr *DC) {
+ if (DC->isTypeOperand())
+ return CT_Cannot;
+
+ Expr *Op = DC->getExprOperand();
+ if (Op->isTypeDependent())
+ return CT_Dependent;
+
+ const RecordType *RT = Op->getType()->getAs<RecordType>();
+ if (!RT)
+ return CT_Cannot;
+
+ if (!cast<CXXRecordDecl>(RT->getDecl())->isPolymorphic())
+ return CT_Cannot;
+
+ if (Op->Classify(S.Context).isPRValue())
+ return CT_Cannot;
+
+ return CT_Can;
+}
+
+CanThrowResult Sema::canThrow(const Stmt *S) {
+ // C++ [expr.unary.noexcept]p3:
+ // [Can throw] if in a potentially-evaluated context the expression would
+ // contain:
+ switch (S->getStmtClass()) {
+ case Expr::ConstantExprClass:
+ return canThrow(cast<ConstantExpr>(S)->getSubExpr());
+
+ case Expr::CXXThrowExprClass:
+ // - a potentially evaluated throw-expression
+ return CT_Can;
+
+ case Expr::CXXDynamicCastExprClass: {
+ // - a potentially evaluated dynamic_cast expression dynamic_cast<T>(v),
+ // where T is a reference type, that requires a run-time check
+ auto *CE = cast<CXXDynamicCastExpr>(S);
+ // FIXME: Properly determine whether a variably-modified type can throw.
+ if (CE->getType()->isVariablyModifiedType())
+ return CT_Can;
+ CanThrowResult CT = canDynamicCastThrow(CE);
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubStmtsThrow(*this, 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));
+
+ // - a potentially evaluated call to a function, member function, function
+ // pointer, or member function pointer that does not have a non-throwing
+ // exception-specification
+ case Expr::CallExprClass:
+ case Expr::CXXMemberCallExprClass:
+ case Expr::CXXOperatorCallExprClass:
+ case Expr::UserDefinedLiteralClass: {
+ const CallExpr *CE = cast<CallExpr>(S);
+ CanThrowResult CT;
+ if (CE->isTypeDependent())
+ CT = CT_Dependent;
+ else if (isa<CXXPseudoDestructorExpr>(CE->getCallee()->IgnoreParens()))
+ CT = CT_Cannot;
+ else
+ CT = canCalleeThrow(*this, CE, CE->getCalleeDecl());
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubStmtsThrow(*this, CE));
+ }
+
+ case Expr::CXXConstructExprClass:
+ case Expr::CXXTemporaryObjectExprClass: {
+ auto *CE = cast<CXXConstructExpr>(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());
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubStmtsThrow(*this, CE));
+ }
+
+ case Expr::CXXInheritedCtorInitExprClass: {
+ auto *ICIE = cast<CXXInheritedCtorInitExpr>(S);
+ return canCalleeThrow(*this, ICIE, ICIE->getConstructor());
+ }
+
+ case Expr::LambdaExprClass: {
+ const LambdaExpr *Lambda = cast<LambdaExpr>(S);
+ CanThrowResult CT = CT_Cannot;
+ for (LambdaExpr::const_capture_init_iterator
+ Cap = Lambda->capture_init_begin(),
+ CapEnd = Lambda->capture_init_end();
+ Cap != CapEnd; ++Cap)
+ CT = mergeCanThrow(CT, canThrow(*Cap));
+ return CT;
+ }
+
+ case Expr::CXXNewExprClass: {
+ auto *NE = cast<CXXNewExpr>(S);
+ CanThrowResult CT;
+ if (NE->isTypeDependent())
+ CT = CT_Dependent;
+ else
+ CT = canCalleeThrow(*this, NE, NE->getOperatorNew());
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubStmtsThrow(*this, NE));
+ }
+
+ case Expr::CXXDeleteExprClass: {
+ auto *DE = cast<CXXDeleteExpr>(S);
+ CanThrowResult CT;
+ QualType DTy = DE->getDestroyedType();
+ if (DTy.isNull() || DTy->isDependentType()) {
+ CT = CT_Dependent;
+ } else {
+ CT = canCalleeThrow(*this, 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));
+ }
+ if (CT == CT_Can)
+ return CT;
+ }
+ return mergeCanThrow(CT, canSubStmtsThrow(*this, 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());
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubStmtsThrow(*this, BTE));
+ }
+
+ case Expr::PseudoObjectExprClass: {
+ auto *POE = cast<PseudoObjectExpr>(S);
+ CanThrowResult CT = CT_Cannot;
+ for (const Expr *E : POE->semantics()) {
+ CT = mergeCanThrow(CT, canThrow(E));
+ if (CT == CT_Can)
+ break;
+ }
+ return CT;
+ }
+
+ // ObjC message sends are like function calls, but never have exception
+ // specs.
+ case Expr::ObjCMessageExprClass:
+ case Expr::ObjCPropertyRefExprClass:
+ case Expr::ObjCSubscriptRefExprClass:
+ return CT_Can;
+
+ // All the ObjC literals that are implemented as calls are
+ // potentially throwing unless we decide to close off that
+ // possibility.
+ case Expr::ObjCArrayLiteralClass:
+ case Expr::ObjCDictionaryLiteralClass:
+ case Expr::ObjCBoxedExprClass:
+ return CT_Can;
+
+ // Many other things have subexpressions, so we have to test those.
+ // Some are simple:
+ case Expr::CoawaitExprClass:
+ case Expr::ConditionalOperatorClass:
+ case Expr::CoyieldExprClass:
+ case Expr::CXXRewrittenBinaryOperatorClass:
+ case Expr::CXXStdInitializerListExprClass:
+ case Expr::DesignatedInitExprClass:
+ case Expr::DesignatedInitUpdateExprClass:
+ case Expr::ExprWithCleanupsClass:
+ case Expr::ExtVectorElementExprClass:
+ case Expr::InitListExprClass:
+ case Expr::ArrayInitLoopExprClass:
+ case Expr::MemberExprClass:
+ case Expr::ObjCIsaExprClass:
+ case Expr::ObjCIvarRefExprClass:
+ case Expr::ParenExprClass:
+ case Expr::ParenListExprClass:
+ case Expr::ShuffleVectorExprClass:
+ case Expr::StmtExprClass:
+ case Expr::ConvertVectorExprClass:
+ case Expr::VAArgExprClass:
+ return canSubStmtsThrow(*this, S);
+
+ case Expr::CompoundLiteralExprClass:
+ case Expr::CXXConstCastExprClass:
+ case Expr::CXXReinterpretCastExprClass:
+ case Expr::BuiltinBitCastExprClass:
+ // FIXME: Properly determine whether a variably-modified type can throw.
+ if (cast<Expr>(S)->getType()->isVariablyModifiedType())
+ return CT_Can;
+ return canSubStmtsThrow(*this, S);
+
+ // Some might be dependent for other reasons.
+ case Expr::ArraySubscriptExprClass:
+ case Expr::OMPArraySectionExprClass:
+ case Expr::BinaryOperatorClass:
+ case Expr::DependentCoawaitExprClass:
+ case Expr::CompoundAssignOperatorClass:
+ case Expr::CStyleCastExprClass:
+ case Expr::CXXStaticCastExprClass:
+ case Expr::CXXFunctionalCastExprClass:
+ case Expr::ImplicitCastExprClass:
+ case Expr::MaterializeTemporaryExprClass:
+ case Expr::UnaryOperatorClass: {
+ // FIXME: Properly determine whether a variably-modified type can throw.
+ if (auto *CE = dyn_cast<CastExpr>(S))
+ if (CE->getType()->isVariablyModifiedType())
+ return CT_Can;
+ CanThrowResult CT =
+ cast<Expr>(S)->isTypeDependent() ? CT_Dependent : CT_Cannot;
+ return mergeCanThrow(CT, canSubStmtsThrow(*this, S));
+ }
+
+ case Expr::CXXDefaultArgExprClass:
+ return canThrow(cast<CXXDefaultArgExpr>(S)->getExpr());
+
+ case Expr::CXXDefaultInitExprClass:
+ return canThrow(cast<CXXDefaultInitExpr>(S)->getExpr());
+
+ case Expr::ChooseExprClass: {
+ auto *CE = cast<ChooseExpr>(S);
+ if (CE->isTypeDependent() || CE->isValueDependent())
+ return CT_Dependent;
+ return canThrow(CE->getChosenSubExpr());
+ }
+
+ case Expr::GenericSelectionExprClass:
+ if (cast<GenericSelectionExpr>(S)->isResultDependent())
+ return CT_Dependent;
+ return canThrow(cast<GenericSelectionExpr>(S)->getResultExpr());
+
+ // Some expressions are always dependent.
+ case Expr::CXXDependentScopeMemberExprClass:
+ case Expr::CXXUnresolvedConstructExprClass:
+ case Expr::DependentScopeDeclRefExprClass:
+ case Expr::CXXFoldExprClass:
+ return CT_Dependent;
+
+ case Expr::AsTypeExprClass:
+ case Expr::BinaryConditionalOperatorClass:
+ case Expr::BlockExprClass:
+ case Expr::CUDAKernelCallExprClass:
+ case Expr::DeclRefExprClass:
+ case Expr::ObjCBridgedCastExprClass:
+ case Expr::ObjCIndirectCopyRestoreExprClass:
+ case Expr::ObjCProtocolExprClass:
+ case Expr::ObjCSelectorExprClass:
+ case Expr::ObjCAvailabilityCheckExprClass:
+ case Expr::OffsetOfExprClass:
+ case Expr::PackExpansionExprClass:
+ case Expr::SubstNonTypeTemplateParmExprClass:
+ case Expr::SubstNonTypeTemplateParmPackExprClass:
+ case Expr::FunctionParmPackExprClass:
+ case Expr::UnaryExprOrTypeTraitExprClass:
+ case Expr::UnresolvedLookupExprClass:
+ case Expr::UnresolvedMemberExprClass:
+ case Expr::TypoExprClass:
+ // FIXME: Many of the above can throw.
+ return CT_Cannot;
+
+ case Expr::AddrLabelExprClass:
+ case Expr::ArrayTypeTraitExprClass:
+ case Expr::AtomicExprClass:
+ case Expr::TypeTraitExprClass:
+ case Expr::CXXBoolLiteralExprClass:
+ case Expr::CXXNoexceptExprClass:
+ case Expr::CXXNullPtrLiteralExprClass:
+ case Expr::CXXPseudoDestructorExprClass:
+ case Expr::CXXScalarValueInitExprClass:
+ case Expr::CXXThisExprClass:
+ case Expr::CXXUuidofExprClass:
+ case Expr::CharacterLiteralClass:
+ case Expr::ExpressionTraitExprClass:
+ case Expr::FloatingLiteralClass:
+ case Expr::GNUNullExprClass:
+ case Expr::ImaginaryLiteralClass:
+ case Expr::ImplicitValueInitExprClass:
+ case Expr::IntegerLiteralClass:
+ case Expr::FixedPointLiteralClass:
+ case Expr::ArrayInitIndexExprClass:
+ case Expr::NoInitExprClass:
+ case Expr::ObjCEncodeExprClass:
+ case Expr::ObjCStringLiteralClass:
+ case Expr::ObjCBoolLiteralExprClass:
+ case Expr::OpaqueValueExprClass:
+ case Expr::PredefinedExprClass:
+ case Expr::SizeOfPackExprClass:
+ case Expr::StringLiteralClass:
+ case Expr::SourceLocExprClass:
+ case Expr::ConceptSpecializationExprClass:
+ case Expr::RequiresExprClass:
+ // These expressions can never throw.
+ return CT_Cannot;
+
+ case Expr::MSPropertyRefExprClass:
+ case Expr::MSPropertySubscriptExprClass:
+ llvm_unreachable("Invalid class for expression");
+
+ // Most statements can throw if any substatement can throw.
+ case Stmt::AttributedStmtClass:
+ case Stmt::BreakStmtClass:
+ case Stmt::CapturedStmtClass:
+ case Stmt::CaseStmtClass:
+ case Stmt::CompoundStmtClass:
+ case Stmt::ContinueStmtClass:
+ case Stmt::CoreturnStmtClass:
+ case Stmt::CoroutineBodyStmtClass:
+ case Stmt::CXXCatchStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::DefaultStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::GCCAsmStmtClass:
+ case Stmt::GotoStmtClass:
+ case Stmt::IndirectGotoStmtClass:
+ case Stmt::LabelStmtClass:
+ case Stmt::MSAsmStmtClass:
+ case Stmt::MSDependentExistsStmtClass:
+ case Stmt::NullStmtClass:
+ case Stmt::ObjCAtCatchStmtClass:
+ case Stmt::ObjCAtFinallyStmtClass:
+ case Stmt::ObjCAtSynchronizedStmtClass:
+ case Stmt::ObjCAutoreleasePoolStmtClass:
+ case Stmt::ObjCForCollectionStmtClass:
+ case Stmt::OMPAtomicDirectiveClass:
+ case Stmt::OMPBarrierDirectiveClass:
+ case Stmt::OMPCancelDirectiveClass:
+ case Stmt::OMPCancellationPointDirectiveClass:
+ case Stmt::OMPCriticalDirectiveClass:
+ case Stmt::OMPDistributeDirectiveClass:
+ case Stmt::OMPDistributeParallelForDirectiveClass:
+ case Stmt::OMPDistributeParallelForSimdDirectiveClass:
+ case Stmt::OMPDistributeSimdDirectiveClass:
+ case Stmt::OMPFlushDirectiveClass:
+ case Stmt::OMPForDirectiveClass:
+ case Stmt::OMPForSimdDirectiveClass:
+ case Stmt::OMPMasterDirectiveClass:
+ case Stmt::OMPMasterTaskLoopDirectiveClass:
+ case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
+ case Stmt::OMPOrderedDirectiveClass:
+ case Stmt::OMPParallelDirectiveClass:
+ case Stmt::OMPParallelForDirectiveClass:
+ case Stmt::OMPParallelForSimdDirectiveClass:
+ case Stmt::OMPParallelMasterDirectiveClass:
+ case Stmt::OMPParallelMasterTaskLoopDirectiveClass:
+ case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass:
+ case Stmt::OMPParallelSectionsDirectiveClass:
+ case Stmt::OMPSectionDirectiveClass:
+ case Stmt::OMPSectionsDirectiveClass:
+ case Stmt::OMPSimdDirectiveClass:
+ case Stmt::OMPSingleDirectiveClass:
+ case Stmt::OMPTargetDataDirectiveClass:
+ case Stmt::OMPTargetDirectiveClass:
+ case Stmt::OMPTargetEnterDataDirectiveClass:
+ case Stmt::OMPTargetExitDataDirectiveClass:
+ case Stmt::OMPTargetParallelDirectiveClass:
+ case Stmt::OMPTargetParallelForDirectiveClass:
+ case Stmt::OMPTargetParallelForSimdDirectiveClass:
+ case Stmt::OMPTargetSimdDirectiveClass:
+ case Stmt::OMPTargetTeamsDirectiveClass:
+ case Stmt::OMPTargetTeamsDistributeDirectiveClass:
+ case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
+ case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
+ case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
+ case Stmt::OMPTargetUpdateDirectiveClass:
+ case Stmt::OMPTaskDirectiveClass:
+ case Stmt::OMPTaskgroupDirectiveClass:
+ case Stmt::OMPTaskLoopDirectiveClass:
+ case Stmt::OMPTaskLoopSimdDirectiveClass:
+ case Stmt::OMPTaskwaitDirectiveClass:
+ case Stmt::OMPTaskyieldDirectiveClass:
+ case Stmt::OMPTeamsDirectiveClass:
+ case Stmt::OMPTeamsDistributeDirectiveClass:
+ case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
+ case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
+ case Stmt::OMPTeamsDistributeSimdDirectiveClass:
+ case Stmt::ReturnStmtClass:
+ case Stmt::SEHExceptStmtClass:
+ case Stmt::SEHFinallyStmtClass:
+ case Stmt::SEHLeaveStmtClass:
+ case Stmt::SEHTryStmtClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::WhileStmtClass:
+ return canSubStmtsThrow(*this, 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));
+
+ // FIXME: Properly determine whether a variably-modified type can throw.
+ if (auto *TND = dyn_cast<TypedefNameDecl>(D))
+ if (TND->getUnderlyingType()->isVariablyModifiedType())
+ return CT_Can;
+ if (auto *VD = dyn_cast<ValueDecl>(D))
+ if (VD->getType()->isVariablyModifiedType())
+ return CT_Can;
+ }
+ return CT;
+ }
+
+ case Stmt::IfStmtClass: {
+ auto *IS = cast<IfStmt>(S);
+ CanThrowResult CT = CT_Cannot;
+ if (const Stmt *Init = IS->getInit())
+ CT = mergeCanThrow(CT, canThrow(Init));
+ if (const Stmt *CondDS = IS->getConditionVariableDeclStmt())
+ CT = mergeCanThrow(CT, canThrow(CondDS));
+ CT = mergeCanThrow(CT, canThrow(IS->getCond()));
+
+ // For 'if constexpr', consider only the non-discarded case.
+ // FIXME: We should add a DiscardedStmt marker to the AST.
+ if (Optional<const Stmt *> Case = IS->getNondiscardedCase(Context))
+ return *Case ? mergeCanThrow(CT, canThrow(*Case)) : CT;
+
+ CanThrowResult Then = canThrow(IS->getThen());
+ CanThrowResult Else = IS->getElse() ? canThrow(IS->getElse()) : CT_Cannot;
+ if (Then == Else)
+ return mergeCanThrow(CT, Then);
+
+ // For a dependent 'if constexpr', the result is dependent if it depends on
+ // the value of the condition.
+ return mergeCanThrow(CT, IS->isConstexpr() ? CT_Dependent
+ : mergeCanThrow(Then, Else));
+ }
+
+ case Stmt::CXXTryStmtClass: {
+ auto *TS = cast<CXXTryStmt>(S);
+ // try /*...*/ catch (...) { H } can throw only if H can throw.
+ // Any other try-catch can throw if any substatement can throw.
+ const CXXCatchStmt *FinalHandler = TS->getHandler(TS->getNumHandlers() - 1);
+ if (!FinalHandler->getExceptionDecl())
+ return canThrow(FinalHandler->getHandlerBlock());
+ return canSubStmtsThrow(*this, S);
+ }
+
+ case Stmt::ObjCAtThrowStmtClass:
+ return CT_Can;
+
+ case Stmt::ObjCAtTryStmtClass: {
+ auto *TS = cast<ObjCAtTryStmt>(S);
+
+ // @catch(...) need not be last in Objective-C. Walk backwards until we
+ // see one or hit the @try.
+ CanThrowResult CT = CT_Cannot;
+ if (const Stmt *Finally = TS->getFinallyStmt())
+ CT = mergeCanThrow(CT, canThrow(Finally));
+ for (unsigned I = TS->getNumCatchStmts(); I != 0; --I) {
+ const ObjCAtCatchStmt *Catch = TS->getCatchStmt(I - 1);
+ CT = mergeCanThrow(CT, canThrow(Catch));
+ // If we reach a @catch(...), no earlier exceptions can escape.
+ if (Catch->hasEllipsis())
+ return CT;
+ }
+
+ // Didn't find an @catch(...). Exceptions from the @try body can escape.
+ return mergeCanThrow(CT, canThrow(TS->getTryBody()));
+ }
+
+ case Stmt::NoStmtClass:
+ llvm_unreachable("Invalid class for statement");
+ }
+ llvm_unreachable("Bogus StmtClass");
+}
+
+} // end namespace clang