forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			1567 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1567 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- 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::OMPDepobjDirectiveClass:
 | |
|   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
 |