diff options
Diffstat (limited to 'clang/lib/Sema/SemaExceptionSpec.cpp')
-rw-r--r-- | clang/lib/Sema/SemaExceptionSpec.cpp | 357 |
1 files changed, 292 insertions, 65 deletions
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index c1abf099e9f26..5aedbe7644e4b 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -15,6 +15,7 @@ #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" @@ -80,8 +81,16 @@ ExprResult Sema::ActOnNoexceptSpec(SourceLocation NoexceptLoc, ExceptionSpecificationType &EST) { // FIXME: This is bogus, a noexcept expression is not a condition. ExprResult Converted = CheckBooleanCondition(NoexceptLoc, NoexceptExpr); - if (Converted.isInvalid()) - return Converted; + 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; @@ -204,7 +213,7 @@ Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) { // Compute or instantiate the exception specification now. if (SourceFPT->getExceptionSpecType() == EST_Unevaluated) - EvaluateImplicitExceptionSpec(Loc, cast<CXXMethodDecl>(SourceDecl)); + EvaluateImplicitExceptionSpec(Loc, SourceDecl); else InstantiateExceptionSpec(Loc, SourceDecl); @@ -970,17 +979,22 @@ bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New, New->getLocation()); } -static CanThrowResult canSubExprsThrow(Sema &S, const Expr *E) { +static CanThrowResult canSubStmtsThrow(Sema &Self, const Stmt *S) { CanThrowResult R = CT_Cannot; - for (const Stmt *SubStmt : E->children()) { - R = mergeCanThrow(R, S.canThrow(cast<Expr>(SubStmt))); + for (const Stmt *SubStmt : S->children()) { + if (!SubStmt) + continue; + R = mergeCanThrow(R, Self.canThrow(SubStmt)); if (R == CT_Can) break; } return R; } -static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D) { +/// 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>()) @@ -989,7 +1003,7 @@ static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D) { QualType T; // In C++1z, just look at the function type of the callee. - if (S.getLangOpts().CPlusPlus17 && isa<CallExpr>(E)) { + if (S.getLangOpts().CPlusPlus17 && E && isa<CallExpr>(E)) { E = cast<CallExpr>(E)->getCallee(); T = E->getType(); if (T->isSpecificPlaceholderType(BuiltinType::BoundMember)) { @@ -1026,13 +1040,41 @@ static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D) { if (!FT) return CT_Can; - FT = S.ResolveExceptionSpec(E->getBeginLoc(), FT); + 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; @@ -1067,13 +1109,13 @@ static CanThrowResult canTypeidThrow(Sema &S, const CXXTypeidExpr *DC) { return CT_Can; } -CanThrowResult Sema::canThrow(const Expr *E) { +CanThrowResult Sema::canThrow(const Stmt *S) { // C++ [expr.unary.noexcept]p3: // [Can throw] if in a potentially-evaluated context the expression would // contain: - switch (E->getStmtClass()) { + switch (S->getStmtClass()) { case Expr::ConstantExprClass: - return canThrow(cast<ConstantExpr>(E)->getSubExpr()); + return canThrow(cast<ConstantExpr>(S)->getSubExpr()); case Expr::CXXThrowExprClass: // - a potentially evaluated throw-expression @@ -1082,16 +1124,20 @@ CanThrowResult Sema::canThrow(const Expr *E) { 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)); + 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, canSubExprsThrow(*this, E)); + 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>(E)); + 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 @@ -1100,34 +1146,38 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::CXXMemberCallExprClass: case Expr::CXXOperatorCallExprClass: case Expr::UserDefinedLiteralClass: { - const CallExpr *CE = cast<CallExpr>(E); + const CallExpr *CE = cast<CallExpr>(S); CanThrowResult CT; - if (E->isTypeDependent()) + if (CE->isTypeDependent()) CT = CT_Dependent; else if (isa<CXXPseudoDestructorExpr>(CE->getCallee()->IgnoreParens())) CT = CT_Cannot; else - CT = canCalleeThrow(*this, E, CE->getCalleeDecl()); + CT = canCalleeThrow(*this, CE, CE->getCalleeDecl()); if (CT == CT_Can) return CT; - return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + return mergeCanThrow(CT, canSubStmtsThrow(*this, CE)); } case Expr::CXXConstructExprClass: case Expr::CXXTemporaryObjectExprClass: { - CanThrowResult CT = canCalleeThrow(*this, E, - cast<CXXConstructExpr>(E)->getConstructor()); + 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, canSubExprsThrow(*this, E)); + return mergeCanThrow(CT, canSubStmtsThrow(*this, CE)); } - case Expr::CXXInheritedCtorInitExprClass: - return canCalleeThrow(*this, E, - cast<CXXInheritedCtorInitExpr>(E)->getConstructor()); + case Expr::CXXInheritedCtorInitExprClass: { + auto *ICIE = cast<CXXInheritedCtorInitExpr>(S); + return canCalleeThrow(*this, ICIE, ICIE->getConstructor()); + } case Expr::LambdaExprClass: { - const LambdaExpr *Lambda = cast<LambdaExpr>(E); + const LambdaExpr *Lambda = cast<LambdaExpr>(S); CanThrowResult CT = CT_Cannot; for (LambdaExpr::const_capture_init_iterator Cap = Lambda->capture_init_begin(), @@ -1138,43 +1188,56 @@ CanThrowResult Sema::canThrow(const Expr *E) { } case Expr::CXXNewExprClass: { + auto *NE = cast<CXXNewExpr>(S); CanThrowResult CT; - if (E->isTypeDependent()) + if (NE->isTypeDependent()) CT = CT_Dependent; else - CT = canCalleeThrow(*this, E, cast<CXXNewExpr>(E)->getOperatorNew()); + CT = canCalleeThrow(*this, NE, NE->getOperatorNew()); if (CT == CT_Can) return CT; - return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + return mergeCanThrow(CT, canSubStmtsThrow(*this, NE)); } case Expr::CXXDeleteExprClass: { + auto *DE = cast<CXXDeleteExpr>(S); CanThrowResult CT; - QualType DTy = cast<CXXDeleteExpr>(E)->getDestroyedType(); + QualType DTy = DE->getDestroyedType(); if (DTy.isNull() || DTy->isDependentType()) { CT = CT_Dependent; } else { - CT = canCalleeThrow(*this, E, - cast<CXXDeleteExpr>(E)->getOperatorDelete()); + 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, E, DD)); + CT = mergeCanThrow(CT, canCalleeThrow(*this, DE, DD)); } if (CT == CT_Can) return CT; } - return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + 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, E, - cast<CXXBindTemporaryExpr>(E)->getTemporary()->getDestructor()); + CanThrowResult CT = + canCalleeThrow(*this, BTE, BTE->getTemporary()->getDestructor()); if (CT == CT_Can) return CT; - return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + 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 @@ -1196,12 +1259,8 @@ CanThrowResult Sema::canThrow(const Expr *E) { // Some are simple: case Expr::CoawaitExprClass: case Expr::ConditionalOperatorClass: - case Expr::CompoundLiteralExprClass: case Expr::CoyieldExprClass: - case Expr::CXXConstCastExprClass: - case Expr::CXXReinterpretCastExprClass: case Expr::CXXRewrittenBinaryOperatorClass: - case Expr::BuiltinBitCastExprClass: case Expr::CXXStdInitializerListExprClass: case Expr::DesignatedInitExprClass: case Expr::DesignatedInitUpdateExprClass: @@ -1215,9 +1274,19 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::ParenExprClass: case Expr::ParenListExprClass: case Expr::ShuffleVectorExprClass: + case Expr::StmtExprClass: case Expr::ConvertVectorExprClass: case Expr::VAArgExprClass: - return canSubExprsThrow(*this, E); + 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: @@ -1231,29 +1300,32 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::ImplicitCastExprClass: case Expr::MaterializeTemporaryExprClass: case Expr::UnaryOperatorClass: { - CanThrowResult CT = E->isTypeDependent() ? CT_Dependent : CT_Cannot; - return mergeCanThrow(CT, canSubExprsThrow(*this, E)); + // 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)); } - // 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()); + return canThrow(cast<CXXDefaultArgExpr>(S)->getExpr()); case Expr::CXXDefaultInitExprClass: - return canThrow(cast<CXXDefaultInitExpr>(E)->getExpr()); + return canThrow(cast<CXXDefaultInitExpr>(S)->getExpr()); - case Expr::ChooseExprClass: - if (E->isTypeDependent() || E->isValueDependent()) + case Expr::ChooseExprClass: { + auto *CE = cast<ChooseExpr>(S); + if (CE->isTypeDependent() || CE->isValueDependent()) return CT_Dependent; - return canThrow(cast<ChooseExpr>(E)->getChosenSubExpr()); + return canThrow(CE->getChosenSubExpr()); + } case Expr::GenericSelectionExprClass: - if (cast<GenericSelectionExpr>(E)->isResultDependent()) + if (cast<GenericSelectionExpr>(S)->isResultDependent()) return CT_Dependent; - return canThrow(cast<GenericSelectionExpr>(E)->getResultExpr()); + return canThrow(cast<GenericSelectionExpr>(S)->getResultExpr()); // Some expressions are always dependent. case Expr::CXXDependentScopeMemberExprClass: @@ -1274,7 +1346,6 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::ObjCAvailabilityCheckExprClass: case Expr::OffsetOfExprClass: case Expr::PackExpansionExprClass: - case Expr::PseudoObjectExprClass: case Expr::SubstNonTypeTemplateParmExprClass: case Expr::SubstNonTypeTemplateParmPackExprClass: case Expr::FunctionParmPackExprClass: @@ -1282,7 +1353,7 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::UnresolvedLookupExprClass: case Expr::UnresolvedMemberExprClass: case Expr::TypoExprClass: - // FIXME: Can any of the above throw? If so, when? + // FIXME: Many of the above can throw. return CT_Cannot; case Expr::AddrLabelExprClass: @@ -1322,14 +1393,170 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::MSPropertySubscriptExprClass: 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"); + // Most statements can throw if any substatement can throw. + case Stmt::AttributedStmtClass: + case Stmt::BreakStmtClass: + case Stmt::CapturedStmtClass: + case Stmt::CaseStmtClass: + case Stmt::CompoundStmtClass: + case Stmt::ContinueStmtClass: + case Stmt::CoreturnStmtClass: + case Stmt::CoroutineBodyStmtClass: + case Stmt::CXXCatchStmtClass: + case Stmt::CXXForRangeStmtClass: + case Stmt::DefaultStmtClass: + case Stmt::DoStmtClass: + case Stmt::ForStmtClass: + case Stmt::GCCAsmStmtClass: + case Stmt::GotoStmtClass: + case Stmt::IndirectGotoStmtClass: + case Stmt::LabelStmtClass: + case Stmt::MSAsmStmtClass: + case Stmt::MSDependentExistsStmtClass: + case Stmt::NullStmtClass: + case Stmt::ObjCAtCatchStmtClass: + case Stmt::ObjCAtFinallyStmtClass: + case Stmt::ObjCAtSynchronizedStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: + case Stmt::ObjCForCollectionStmtClass: + case Stmt::OMPAtomicDirectiveClass: + case Stmt::OMPBarrierDirectiveClass: + case Stmt::OMPCancelDirectiveClass: + case Stmt::OMPCancellationPointDirectiveClass: + case Stmt::OMPCriticalDirectiveClass: + case Stmt::OMPDistributeDirectiveClass: + case Stmt::OMPDistributeParallelForDirectiveClass: + case Stmt::OMPDistributeParallelForSimdDirectiveClass: + case Stmt::OMPDistributeSimdDirectiveClass: + case Stmt::OMPFlushDirectiveClass: + case Stmt::OMPForDirectiveClass: + case Stmt::OMPForSimdDirectiveClass: + case Stmt::OMPMasterDirectiveClass: + case Stmt::OMPMasterTaskLoopDirectiveClass: + case Stmt::OMPMasterTaskLoopSimdDirectiveClass: + case Stmt::OMPOrderedDirectiveClass: + case Stmt::OMPParallelDirectiveClass: + case Stmt::OMPParallelForDirectiveClass: + case Stmt::OMPParallelForSimdDirectiveClass: + case Stmt::OMPParallelMasterDirectiveClass: + case Stmt::OMPParallelMasterTaskLoopDirectiveClass: + case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass: + case Stmt::OMPParallelSectionsDirectiveClass: + case Stmt::OMPSectionDirectiveClass: + case Stmt::OMPSectionsDirectiveClass: + case Stmt::OMPSimdDirectiveClass: + case Stmt::OMPSingleDirectiveClass: + case Stmt::OMPTargetDataDirectiveClass: + case Stmt::OMPTargetDirectiveClass: + case Stmt::OMPTargetEnterDataDirectiveClass: + case Stmt::OMPTargetExitDataDirectiveClass: + case Stmt::OMPTargetParallelDirectiveClass: + case Stmt::OMPTargetParallelForDirectiveClass: + case Stmt::OMPTargetParallelForSimdDirectiveClass: + case Stmt::OMPTargetSimdDirectiveClass: + case Stmt::OMPTargetTeamsDirectiveClass: + case Stmt::OMPTargetTeamsDistributeDirectiveClass: + case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: + case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: + case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: + case Stmt::OMPTargetUpdateDirectiveClass: + case Stmt::OMPTaskDirectiveClass: + case Stmt::OMPTaskgroupDirectiveClass: + case Stmt::OMPTaskLoopDirectiveClass: + case Stmt::OMPTaskLoopSimdDirectiveClass: + case Stmt::OMPTaskwaitDirectiveClass: + case Stmt::OMPTaskyieldDirectiveClass: + case Stmt::OMPTeamsDirectiveClass: + case Stmt::OMPTeamsDistributeDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: + case Stmt::OMPTeamsDistributeSimdDirectiveClass: + case Stmt::ReturnStmtClass: + case Stmt::SEHExceptStmtClass: + case Stmt::SEHFinallyStmtClass: + case Stmt::SEHLeaveStmtClass: + case Stmt::SEHTryStmtClass: + case Stmt::SwitchStmtClass: + case Stmt::WhileStmtClass: + return canSubStmtsThrow(*this, S); + + case Stmt::DeclStmtClass: { + CanThrowResult CT = CT_Cannot; + for (const Decl *D : cast<DeclStmt>(S)->decls()) { + if (auto *VD = dyn_cast<VarDecl>(D)) + CT = mergeCanThrow(CT, canVarDeclThrow(*this, VD)); + + // FIXME: Properly determine whether a variably-modified type can throw. + if (auto *TND = dyn_cast<TypedefNameDecl>(D)) + if (TND->getUnderlyingType()->isVariablyModifiedType()) + return CT_Can; + if (auto *VD = dyn_cast<ValueDecl>(D)) + if (VD->getType()->isVariablyModifiedType()) + return CT_Can; + } + return CT; + } + + case Stmt::IfStmtClass: { + auto *IS = cast<IfStmt>(S); + CanThrowResult CT = CT_Cannot; + if (const Stmt *Init = IS->getInit()) + CT = mergeCanThrow(CT, canThrow(Init)); + if (const Stmt *CondDS = IS->getConditionVariableDeclStmt()) + CT = mergeCanThrow(CT, canThrow(CondDS)); + CT = mergeCanThrow(CT, canThrow(IS->getCond())); + + // For 'if constexpr', consider only the non-discarded case. + // FIXME: We should add a DiscardedStmt marker to the AST. + if (Optional<const Stmt *> Case = IS->getNondiscardedCase(Context)) + return *Case ? mergeCanThrow(CT, canThrow(*Case)) : CT; + + CanThrowResult Then = canThrow(IS->getThen()); + CanThrowResult Else = IS->getElse() ? canThrow(IS->getElse()) : CT_Cannot; + if (Then == Else) + return mergeCanThrow(CT, Then); + + // For a dependent 'if constexpr', the result is dependent if it depends on + // the value of the condition. + return mergeCanThrow(CT, IS->isConstexpr() ? CT_Dependent + : mergeCanThrow(Then, Else)); + } + + case Stmt::CXXTryStmtClass: { + auto *TS = cast<CXXTryStmt>(S); + // try /*...*/ catch (...) { H } can throw only if H can throw. + // Any other try-catch can throw if any substatement can throw. + const CXXCatchStmt *FinalHandler = TS->getHandler(TS->getNumHandlers() - 1); + if (!FinalHandler->getExceptionDecl()) + return canThrow(FinalHandler->getHandlerBlock()); + return canSubStmtsThrow(*this, S); + } + + case Stmt::ObjCAtThrowStmtClass: + return CT_Can; + + case Stmt::ObjCAtTryStmtClass: { + auto *TS = cast<ObjCAtTryStmt>(S); + + // @catch(...) need not be last in Objective-C. Walk backwards until we + // see one or hit the @try. + CanThrowResult CT = CT_Cannot; + if (const Stmt *Finally = TS->getFinallyStmt()) + CT = mergeCanThrow(CT, canThrow(Finally)); + for (unsigned I = TS->getNumCatchStmts(); I != 0; --I) { + const ObjCAtCatchStmt *Catch = TS->getCatchStmt(I - 1); + CT = mergeCanThrow(CT, canThrow(Catch)); + // If we reach a @catch(...), no earlier exceptions can escape. + if (Catch->hasEllipsis()) + return CT; + } + + // Didn't find an @catch(...). Exceptions from the @try body can escape. + return mergeCanThrow(CT, canThrow(TS->getTryBody())); + } + + case Stmt::NoStmtClass: + llvm_unreachable("Invalid class for statement"); } llvm_unreachable("Bogus StmtClass"); } |