summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/clang/lib/Sema/SemaExceptionSpec.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Sema/SemaExceptionSpec.cpp')
-rw-r--r--contrib/llvm/tools/clang/lib/Sema/SemaExceptionSpec.cpp1120
1 files changed, 1120 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/Sema/SemaExceptionSpec.cpp b/contrib/llvm/tools/clang/lib/Sema/SemaExceptionSpec.cpp
new file mode 100644
index 0000000..b92fcbd
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -0,0 +1,1120 @@
+//===--- SemaExceptionSpec.cpp - C++ Exception Specifications ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// 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/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>();
+}
+
+/// 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, const 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.
+ if (!(PointeeT->isRecordType() &&
+ PointeeT->getAs<RecordType>()->isBeingDefined()) &&
+ RequireCompleteType(Range.getBegin(), PointeeT,
+ diag::err_incomplete_in_exception_spec, Kind, Range))
+ return true;
+
+ 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) {
+ 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 (!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, cast<CXXMethodDecl>(SourceDecl));
+ else
+ InstantiateExceptionSpec(Loc, SourceDecl);
+
+ return SourceDecl->getType()->castAs<FunctionProtoType>();
+}
+
+void Sema::UpdateExceptionSpec(FunctionDecl *FD,
+ const FunctionProtoType::ExtProtoInfo &EPI) {
+ const FunctionProtoType *Proto = FD->getType()->castAs<FunctionProtoType>();
+
+ // Overwrite the exception spec and rebuild the function type.
+ FunctionProtoType::ExtProtoInfo NewEPI = Proto->getExtProtoInfo();
+ NewEPI.ExceptionSpecType = EPI.ExceptionSpecType;
+ NewEPI.NumExceptions = EPI.NumExceptions;
+ NewEPI.Exceptions = EPI.Exceptions;
+ NewEPI.NoexceptExpr = EPI.NoexceptExpr;
+ FD->setType(Context.getFunctionType(Proto->getReturnType(),
+ Proto->getParamTypes(), NewEPI));
+
+ // If we've fully resolved the exception specification, notify listeners.
+ if (!isUnresolvedExceptionSpec(EPI.ExceptionSpecType))
+ if (auto *Listener = getASTMutationListener())
+ Listener->ResolvedExceptionSpec(FD);
+}
+
+/// 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);
+
+ const FunctionProtoType *Ty =
+ Decl->getTypeSourceInfo()->getType()->getAs<FunctionProtoType>();
+ return !Ty->hasExceptionSpec();
+}
+
+bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
+ 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().MicrosoftExt) {
+ DiagID = diag::ext_mismatched_exception_spec;
+ ReturnValueOnError = false;
+ }
+
+ // Check the types as written: they must match before any exception
+ // specification adjustment is applied.
+ if (!CheckEquivalentExceptionSpec(
+ 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 &&
+ hasImplicitExceptionSpec(Old) != hasImplicitExceptionSpec(New)) {
+ Diag(New->getLocation(), diag::ext_implicit_exception_spec_mismatch)
+ << hasImplicitExceptionSpec(Old);
+ if (!Old->getLocation().isInvalid())
+ 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. This is an
+ // egregious workaround for glibc, which adds throw() specifications
+ // to many libc functions as an optimization. Unfortunately, that
+ // optimization isn't permitted by the C++ standard, so we're forced
+ // to work around it here.
+ if (MissingEmptyExceptionSpecification && NewProto &&
+ (Old->getLocation().isInvalid() ||
+ Context.getSourceManager().isInSystemHeader(Old->getLocation())) &&
+ Old->isExternC()) {
+ FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
+ EPI.ExceptionSpecType = EST_DynamicNone;
+ QualType NewType = Context.getFunctionType(NewProto->getReturnType(),
+ NewProto->getParamTypes(), EPI);
+ New->setType(NewType);
+ return false;
+ }
+
+ const FunctionProtoType *OldProto =
+ Old->getType()->castAs<FunctionProtoType>();
+
+ FunctionProtoType::ExtProtoInfo EPI = NewProto->getExtProtoInfo();
+ EPI.ExceptionSpecType = OldProto->getExceptionSpecType();
+ if (EPI.ExceptionSpecType == EST_Dynamic) {
+ EPI.NumExceptions = OldProto->getNumExceptions();
+ EPI.Exceptions = OldProto->exception_begin();
+ } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) {
+ // FIXME: We can't just take the expression from the old prototype. It
+ // likely contains references to the old prototype's parameters.
+ }
+
+ // Update the type of the function with the appropriate exception
+ // specification.
+ QualType NewType = Context.getFunctionType(NewProto->getReturnType(),
+ NewProto->getParamTypes(), EPI);
+ New->setType(NewType);
+
+ // 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_ComputedNoexcept:
+ OS << "noexcept(";
+ assert(OldProto->getNoexceptExpr() != nullptr && "Expected non-null Expr");
+ OldProto->getNoexceptExpr()->printPretty(OS, nullptr, getPrintingPolicy());
+ OS << ")";
+ break;
+
+ default:
+ llvm_unreachable("This spec type is compatible with none.");
+ }
+ OS.flush();
+
+ SourceLocation FixItLoc;
+ if (TypeSourceInfo *TSInfo = New->getTypeSourceInfo()) {
+ TypeLoc TL = TSInfo->getTypeLoc().IgnoreParens();
+ if (FunctionTypeLoc FTLoc = TL.getAs<FunctionTypeLoc>())
+ FixItLoc = getLocForEndOfToken(FTLoc.getLocalRangeEnd());
+ }
+
+ if (FixItLoc.isInvalid())
+ Diag(New->getLocation(), diag::warn_missing_exception_specification)
+ << New << OS.str();
+ else {
+ // FIXME: This will get more complicated with C++0x
+ // late-specified return types.
+ Diag(New->getLocation(), diag::warn_missing_exception_specification)
+ << New << OS.str()
+ << FixItHint::CreateInsertion(FixItLoc, " " + OS.str().str());
+ }
+
+ if (!Old->getLocation().isInvalid())
+ Diag(Old->getLocation(), diag::note_previous_declaration);
+
+ return false;
+}
+
+/// 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) {
+ unsigned DiagID = diag::err_mismatched_exception_spec;
+ if (getLangOpts().MicrosoftExt)
+ DiagID = diag::ext_mismatched_exception_spec;
+ bool Result = CheckEquivalentExceptionSpec(PDiag(DiagID),
+ PDiag(diag::note_previous_declaration), Old, OldLoc, New, NewLoc);
+
+ // In Microsoft mode, mismatching exception specifications just cause a warning.
+ if (getLangOpts().MicrosoftExt)
+ 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.
+bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
+ const PartialDiagnostic & NoteID,
+ const FunctionProtoType *Old,
+ SourceLocation OldLoc,
+ const FunctionProtoType *New,
+ SourceLocation NewLoc,
+ bool *MissingExceptionSpecification,
+ bool*MissingEmptyExceptionSpecification,
+ bool AllowNoexceptAllMatchWithNoSpec,
+ bool IsOperatorNew) {
+ // Just completely ignore this under -fno-exceptions.
+ if (!getLangOpts().CXXExceptions)
+ return false;
+
+ if (MissingExceptionSpecification)
+ *MissingExceptionSpecification = false;
+
+ if (MissingEmptyExceptionSpecification)
+ *MissingEmptyExceptionSpecification = false;
+
+ Old = ResolveExceptionSpec(NewLoc, Old);
+ if (!Old)
+ return false;
+ New = 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-specifcation 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");
+
+ // Shortcut the case where both have no spec.
+ if (OldEST == EST_None && NewEST == EST_None)
+ return false;
+
+ FunctionProtoType::NoexceptResult OldNR = Old->getNoexceptSpec(Context);
+ FunctionProtoType::NoexceptResult NewNR = New->getNoexceptSpec(Context);
+ if (OldNR == FunctionProtoType::NR_BadNoexcept ||
+ NewNR == FunctionProtoType::NR_BadNoexcept)
+ return false;
+
+ // Dependent noexcept specifiers are compatible with each other, but nothing
+ // else.
+ // One noexcept is compatible with another if the argument is the same
+ if (OldNR == NewNR &&
+ OldNR != FunctionProtoType::NR_NoNoexcept &&
+ NewNR != FunctionProtoType::NR_NoNoexcept)
+ return false;
+ if (OldNR != NewNR &&
+ OldNR != FunctionProtoType::NR_NoNoexcept &&
+ NewNR != FunctionProtoType::NR_NoNoexcept) {
+ Diag(NewLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(OldLoc, NoteID);
+ return true;
+ }
+
+ // The MS extension throw(...) is compatible with itself.
+ if (OldEST == EST_MSAny && NewEST == EST_MSAny)
+ return false;
+
+ // It's also compatible with no spec.
+ if ((OldEST == EST_None && NewEST == EST_MSAny) ||
+ (OldEST == EST_MSAny && NewEST == EST_None))
+ return false;
+
+ // It's also compatible with noexcept(false).
+ if (OldEST == EST_MSAny && NewNR == FunctionProtoType::NR_Throw)
+ return false;
+ if (NewEST == EST_MSAny && OldNR == FunctionProtoType::NR_Throw)
+ return false;
+
+ // As described above, noexcept(false) matches no spec only for functions.
+ if (AllowNoexceptAllMatchWithNoSpec) {
+ if (OldEST == EST_None && NewNR == FunctionProtoType::NR_Throw)
+ return false;
+ if (NewEST == EST_None && OldNR == FunctionProtoType::NR_Throw)
+ return false;
+ }
+
+ // Any non-throwing specifications are compatible.
+ bool OldNonThrowing = OldNR == FunctionProtoType::NR_Nothrow ||
+ OldEST == EST_DynamicNone;
+ bool NewNonThrowing = NewNR == FunctionProtoType::NR_Nothrow ||
+ NewEST == EST_DynamicNone;
+ if (OldNonThrowing && NewNonThrowing)
+ return false;
+
+ // 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 (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;
+ }
+ }
+ }
+ }
+ }
+
+ // At this point, the only remaining valid case is two matching dynamic
+ // specifications. We return here unless both specifications are dynamic.
+ if (OldEST != EST_Dynamic || NewEST != EST_Dynamic) {
+ if (MissingExceptionSpecification && Old->hasExceptionSpec() &&
+ !New->hasExceptionSpec()) {
+ // The old type has an exception specification of some sort, but
+ // the new type does not.
+ *MissingExceptionSpecification = true;
+
+ if (MissingEmptyExceptionSpecification && OldNonThrowing) {
+ // The old type has a throw() or noexcept(true) exception specification
+ // and the new type has no exception specification, and the caller asked
+ // to handle this itself.
+ *MissingEmptyExceptionSpecification = true;
+ }
+
+ return true;
+ }
+
+ Diag(NewLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(OldLoc, NoteID);
+ return true;
+ }
+
+ assert(OldEST == EST_Dynamic && NewEST == EST_Dynamic &&
+ "Exception compatibility logic error: non-dynamic spec slipped through.");
+
+ 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(Context.getCanonicalType(I).getUnqualifiedType());
+
+ for (const auto &I : New->exceptions()) {
+ CanQualType TypePtr = Context.getCanonicalType(I).getUnqualifiedType();
+ if(OldTypes.count(TypePtr))
+ NewTypes.insert(TypePtr);
+ else
+ Success = false;
+ }
+
+ Success = Success && OldTypes.size() == NewTypes.size();
+
+ if (Success) {
+ return false;
+ }
+ Diag(NewLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(OldLoc, NoteID);
+ return true;
+}
+
+/// 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 & NoteID,
+ 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();
+
+ // If superset contains everything, we're done.
+ if (SuperEST == EST_None || SuperEST == EST_MSAny)
+ return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
+
+ // If there are dependent noexcept specs, assume everything is fine. Unlike
+ // with the equivalency check, this is safe in this case, because we don't
+ // want to merge declarations. Checks after instantiation will catch any
+ // omissions we make here.
+ // We also shortcut checking if a noexcept expression was bad.
+
+ FunctionProtoType::NoexceptResult SuperNR =Superset->getNoexceptSpec(Context);
+ if (SuperNR == FunctionProtoType::NR_BadNoexcept ||
+ SuperNR == FunctionProtoType::NR_Dependent)
+ return false;
+
+ // Another case of the superset containing everything.
+ if (SuperNR == FunctionProtoType::NR_Throw)
+ return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
+
+ ExceptionSpecificationType SubEST = Subset->getExceptionSpecType();
+
+ assert(!isUnresolvedExceptionSpec(SuperEST) &&
+ !isUnresolvedExceptionSpec(SubEST) &&
+ "Shouldn't see unknown exception specifications here");
+
+ // It does not. If the subset contains everything, we've failed.
+ if (SubEST == EST_None || SubEST == EST_MSAny) {
+ Diag(SubLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(SuperLoc, NoteID);
+ return true;
+ }
+
+ FunctionProtoType::NoexceptResult SubNR = Subset->getNoexceptSpec(Context);
+ if (SubNR == FunctionProtoType::NR_BadNoexcept ||
+ SubNR == FunctionProtoType::NR_Dependent)
+ return false;
+
+ // Another case of the subset containing everything.
+ if (SubNR == FunctionProtoType::NR_Throw) {
+ Diag(SubLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(SuperLoc, NoteID);
+ return true;
+ }
+
+ // If the subset contains nothing, we're done.
+ if (SubEST == EST_DynamicNone || SubNR == FunctionProtoType::NR_Nothrow)
+ return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
+
+ // Otherwise, if the superset contains nothing, we've failed.
+ if (SuperEST == EST_DynamicNone || SuperNR == FunctionProtoType::NR_Nothrow) {
+ Diag(SubLoc, DiagID);
+ if (NoteID.getDiagID() != 0)
+ Diag(SuperLoc, NoteID);
+ return true;
+ }
+
+ assert(SuperEST == EST_Dynamic && SubEST == EST_Dynamic &&
+ "Exception spec subset: non-dynamic case slipped through.");
+
+ // Neither contains everything or nothing. Do a proper comparison.
+ for (const auto &SubI : Subset->exceptions()) {
+ // Take one type from the subset.
+ QualType CanonicalSubT = Context.getCanonicalType(SubI);
+ // Unwrap pointers and references so that we can do checks within a class
+ // hierarchy. Don't unwrap member pointers; they don't have hierarchy
+ // conversions on the pointee.
+ bool SubIsPointer = false;
+ if (const ReferenceType *RefTy = CanonicalSubT->getAs<ReferenceType>())
+ CanonicalSubT = RefTy->getPointeeType();
+ if (const PointerType *PtrTy = CanonicalSubT->getAs<PointerType>()) {
+ CanonicalSubT = PtrTy->getPointeeType();
+ SubIsPointer = true;
+ }
+ bool SubIsClass = CanonicalSubT->isRecordType();
+ CanonicalSubT = CanonicalSubT.getLocalUnqualifiedType();
+
+ CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+
+ bool Contained = false;
+ // Make sure it's in the superset.
+ for (const auto &SuperI : Superset->exceptions()) {
+ QualType CanonicalSuperT = Context.getCanonicalType(SuperI);
+ // SubT must be SuperT or derived from it, or pointer or reference to
+ // such types.
+ if (const ReferenceType *RefTy = CanonicalSuperT->getAs<ReferenceType>())
+ CanonicalSuperT = RefTy->getPointeeType();
+ if (SubIsPointer) {
+ if (const PointerType *PtrTy = CanonicalSuperT->getAs<PointerType>())
+ CanonicalSuperT = PtrTy->getPointeeType();
+ else {
+ continue;
+ }
+ }
+ CanonicalSuperT = CanonicalSuperT.getLocalUnqualifiedType();
+ // If the types are the same, move on to the next type in the subset.
+ if (CanonicalSubT == CanonicalSuperT) {
+ Contained = true;
+ break;
+ }
+
+ // Otherwise we need to check the inheritance.
+ if (!SubIsClass || !CanonicalSuperT->isRecordType())
+ continue;
+
+ Paths.clear();
+ if (!IsDerivedFrom(CanonicalSubT, CanonicalSuperT, Paths))
+ continue;
+
+ if (Paths.isAmbiguous(Context.getCanonicalType(CanonicalSuperT)))
+ continue;
+
+ // Do this check from a context without privileges.
+ switch (CheckBaseClassAccess(SourceLocation(),
+ CanonicalSuperT, CanonicalSubT,
+ Paths.front(),
+ /*Diagnostic*/ 0,
+ /*ForceCheck*/ true,
+ /*ForceUnprivileged*/ true)) {
+ case AR_accessible: break;
+ case AR_inaccessible: continue;
+ case AR_dependent:
+ llvm_unreachable("access check dependent for unprivileged context");
+ case AR_delayed:
+ llvm_unreachable("access check delayed in non-declaration");
+ }
+
+ 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(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 & NoteID,
+ const FunctionProtoType *Target, SourceLocation TargetLoc,
+ const FunctionProtoType *Source, SourceLocation SourceLoc)
+{
+ if (CheckSpecForTypesEquivalent(
+ *this, PDiag(diag::err_deep_exception_specs_differ) << 0, 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) {
+ if (CheckSpecForTypesEquivalent(
+ *this, PDiag(diag::err_deep_exception_specs_differ) << 1, 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)
+ return false;
+
+ // SourceType must be a function or function pointer.
+ const FunctionProtoType *FromFunc = GetUnderlyingFunction(From->getType());
+ if (!FromFunc)
+ return false;
+
+ // 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.
+ return CheckExceptionSpecSubset(PDiag(diag::err_incompatible_exception_specs),
+ PDiag(), ToFunc,
+ From->getSourceRange().getBegin(),
+ FromFunc, SourceLocation());
+}
+
+bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
+ const CXXMethodDecl *Old) {
+ if (getLangOpts().CPlusPlus11 && isa<CXXDestructorDecl>(New)) {
+ // Don't check uninstantiated template destructors at all. We can only
+ // synthesize correct specs after the template is instantiated.
+ if (New->getParent()->isDependentType())
+ return false;
+ if (New->getParent()->isBeingDefined()) {
+ // The destructor might be updated once the definition is finished. So
+ // remember it and check later.
+ DelayedDestructorExceptionSpecChecks.push_back(std::make_pair(
+ cast<CXXDestructorDecl>(New), cast<CXXDestructorDecl>(Old)));
+ return false;
+ }
+ }
+ unsigned DiagID = diag::err_override_exception_spec;
+ if (getLangOpts().MicrosoftExt)
+ DiagID = diag::ext_override_exception_spec;
+ return CheckExceptionSpecSubset(PDiag(DiagID),
+ PDiag(diag::note_overridden_virtual_function),
+ Old->getType()->getAs<FunctionProtoType>(),
+ Old->getLocation(),
+ New->getType()->getAs<FunctionProtoType>(),
+ New->getLocation());
+}
+
+static CanThrowResult canSubExprsThrow(Sema &S, const Expr *CE) {
+ Expr *E = const_cast<Expr*>(CE);
+ CanThrowResult R = CT_Cannot;
+ for (Expr::child_range I = E->children(); I && R != CT_Can; ++I)
+ R = mergeCanThrow(R, S.canThrow(cast<Expr>(*I)));
+ return R;
+}
+
+static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D) {
+ assert(D && "Expected decl");
+
+ // See if we can get a function type from the decl somehow.
+ const ValueDecl *VD = dyn_cast<ValueDecl>(D);
+ if (!VD) // If we have no clue what we're calling, assume the worst.
+ return CT_Can;
+
+ // As an extension, we assume that __attribute__((nothrow)) functions don't
+ // throw.
+ if (isa<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>())
+ return CT_Cannot;
+
+ QualType T = VD->getType();
+ 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(E->getLocStart(), FT);
+ if (!FT)
+ return CT_Can;
+
+ return FT->isNothrow(S.Context) ? CT_Cannot : CT_Can;
+}
+
+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 Expr *E) {
+ // C++ [expr.unary.noexcept]p3:
+ // [Can throw] if in a potentially-evaluated context the expression would
+ // contain:
+ switch (E->getStmtClass()) {
+ 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
+ CanThrowResult CT = canDynamicCastThrow(cast<CXXDynamicCastExpr>(E));
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubExprsThrow(*this, E));
+ }
+
+ 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>(E));
+
+ // - 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>(E);
+ CanThrowResult CT;
+ if (E->isTypeDependent())
+ CT = CT_Dependent;
+ else if (isa<CXXPseudoDestructorExpr>(CE->getCallee()->IgnoreParens()))
+ CT = CT_Cannot;
+ else if (CE->getCalleeDecl())
+ CT = canCalleeThrow(*this, E, CE->getCalleeDecl());
+ else
+ CT = CT_Can;
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubExprsThrow(*this, E));
+ }
+
+ case Expr::CXXConstructExprClass:
+ case Expr::CXXTemporaryObjectExprClass: {
+ CanThrowResult CT = canCalleeThrow(*this, E,
+ cast<CXXConstructExpr>(E)->getConstructor());
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubExprsThrow(*this, E));
+ }
+
+ case Expr::LambdaExprClass: {
+ const LambdaExpr *Lambda = cast<LambdaExpr>(E);
+ CanThrowResult CT = CT_Cannot;
+ for (LambdaExpr::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: {
+ CanThrowResult CT;
+ if (E->isTypeDependent())
+ CT = CT_Dependent;
+ else
+ CT = canCalleeThrow(*this, E, cast<CXXNewExpr>(E)->getOperatorNew());
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubExprsThrow(*this, E));
+ }
+
+ case Expr::CXXDeleteExprClass: {
+ CanThrowResult CT;
+ QualType DTy = cast<CXXDeleteExpr>(E)->getDestroyedType();
+ if (DTy.isNull() || DTy->isDependentType()) {
+ CT = CT_Dependent;
+ } else {
+ CT = canCalleeThrow(*this, E,
+ cast<CXXDeleteExpr>(E)->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, E, DD));
+ }
+ if (CT == CT_Can)
+ return CT;
+ }
+ return mergeCanThrow(CT, canSubExprsThrow(*this, E));
+ }
+
+ case Expr::CXXBindTemporaryExprClass: {
+ // The bound temporary has to be destroyed again, which might throw.
+ CanThrowResult CT = canCalleeThrow(*this, E,
+ cast<CXXBindTemporaryExpr>(E)->getTemporary()->getDestructor());
+ if (CT == CT_Can)
+ return CT;
+ return mergeCanThrow(CT, canSubExprsThrow(*this, E));
+ }
+
+ // 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::ConditionalOperatorClass:
+ case Expr::CompoundLiteralExprClass:
+ case Expr::CXXConstCastExprClass:
+ case Expr::CXXReinterpretCastExprClass:
+ case Expr::CXXStdInitializerListExprClass:
+ case Expr::DesignatedInitExprClass:
+ case Expr::ExprWithCleanupsClass:
+ case Expr::ExtVectorElementExprClass:
+ case Expr::InitListExprClass:
+ case Expr::MemberExprClass:
+ case Expr::ObjCIsaExprClass:
+ case Expr::ObjCIvarRefExprClass:
+ case Expr::ParenExprClass:
+ case Expr::ParenListExprClass:
+ case Expr::ShuffleVectorExprClass:
+ case Expr::ConvertVectorExprClass:
+ case Expr::VAArgExprClass:
+ return canSubExprsThrow(*this, E);
+
+ // Some might be dependent for other reasons.
+ case Expr::ArraySubscriptExprClass:
+ case Expr::BinaryOperatorClass:
+ case Expr::CompoundAssignOperatorClass:
+ case Expr::CStyleCastExprClass:
+ case Expr::CXXStaticCastExprClass:
+ case Expr::CXXFunctionalCastExprClass:
+ case Expr::ImplicitCastExprClass:
+ case Expr::MaterializeTemporaryExprClass:
+ case Expr::UnaryOperatorClass: {
+ CanThrowResult CT = E->isTypeDependent() ? CT_Dependent : CT_Cannot;
+ return mergeCanThrow(CT, canSubExprsThrow(*this, E));
+ }
+
+ // FIXME: We should handle StmtExpr, but that opens a MASSIVE can of worms.
+ case Expr::StmtExprClass:
+ return CT_Can;
+
+ case Expr::CXXDefaultArgExprClass:
+ return canThrow(cast<CXXDefaultArgExpr>(E)->getExpr());
+
+ case Expr::CXXDefaultInitExprClass:
+ return canThrow(cast<CXXDefaultInitExpr>(E)->getExpr());
+
+ case Expr::ChooseExprClass:
+ if (E->isTypeDependent() || E->isValueDependent())
+ return CT_Dependent;
+ return canThrow(cast<ChooseExpr>(E)->getChosenSubExpr());
+
+ case Expr::GenericSelectionExprClass:
+ if (cast<GenericSelectionExpr>(E)->isResultDependent())
+ return CT_Dependent;
+ return canThrow(cast<GenericSelectionExpr>(E)->getResultExpr());
+
+ // Some expressions are always dependent.
+ case Expr::CXXDependentScopeMemberExprClass:
+ case Expr::CXXUnresolvedConstructExprClass:
+ case Expr::DependentScopeDeclRefExprClass:
+ 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::OffsetOfExprClass:
+ case Expr::PackExpansionExprClass:
+ case Expr::PseudoObjectExprClass:
+ case Expr::SubstNonTypeTemplateParmExprClass:
+ case Expr::SubstNonTypeTemplateParmPackExprClass:
+ case Expr::FunctionParmPackExprClass:
+ case Expr::UnaryExprOrTypeTraitExprClass:
+ case Expr::UnresolvedLookupExprClass:
+ case Expr::UnresolvedMemberExprClass:
+ // FIXME: Can any of the above throw? If so, when?
+ 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::ObjCEncodeExprClass:
+ case Expr::ObjCStringLiteralClass:
+ case Expr::ObjCBoolLiteralExprClass:
+ case Expr::OpaqueValueExprClass:
+ case Expr::PredefinedExprClass:
+ case Expr::SizeOfPackExprClass:
+ case Expr::StringLiteralClass:
+ // These expressions can never throw.
+ return CT_Cannot;
+
+ case Expr::MSPropertyRefExprClass:
+ llvm_unreachable("Invalid class for expression");
+
+#define STMT(CLASS, PARENT) case Expr::CLASS##Class:
+#define STMT_RANGE(Base, First, Last)
+#define LAST_STMT_RANGE(BASE, FIRST, LAST)
+#define EXPR(CLASS, PARENT)
+#define ABSTRACT_STMT(STMT)
+#include "clang/AST/StmtNodes.inc"
+ case Expr::NoStmtClass:
+ llvm_unreachable("Invalid class for expression");
+ }
+ llvm_unreachable("Bogus StmtClass");
+}
+
+} // end namespace clang
OpenPOWER on IntegriCloud