From fbf60b7dbeb0f66b45037925c384859f2f161504 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 13 Dec 2019 14:10:13 -0800 Subject: [PATCH] Properly compute whether statement expressions can throw, rather than conservatively assuming they always can. Also fix cases where we would not consider the computation of a VLA type when determining whether an expression can throw. We don't yet properly determine whether a VLA can throw, but no longer incorrectly claim it can never throw. --- clang/include/clang/AST/Stmt.h | 4 + clang/include/clang/Sema/Sema.h | 2 +- clang/lib/AST/Stmt.cpp | 6 + clang/lib/Sema/SemaExceptionSpec.cpp | 329 ++++++++++++++++++----- clang/test/SemaCXX/cxx0x-noexcept-expression.cpp | 60 ++++- 5 files changed, 339 insertions(+), 62 deletions(-) diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 7aebbf2..eaacb1a 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -1995,6 +1995,10 @@ public: bool isConstexpr() const { return IfStmtBits.IsConstexpr; } void setConstexpr(bool C) { IfStmtBits.IsConstexpr = C; } + /// If this is an 'if constexpr', determine which substatement will be taken. + /// Otherwise, or if the condition is value-dependent, returns None. + Optional getNondiscardedCase(const ASTContext &Ctx) const; + bool isObjCAvailabilityCheck() const; SourceLocation getBeginLoc() const { return getIfLoc(); } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6d0f79a..6b561dc 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1599,7 +1599,7 @@ public: DeclarationNameInfo GetNameFromUnqualifiedId(const UnqualifiedId &Name); static QualType GetTypeFromParser(ParsedType Ty, TypeSourceInfo **TInfo = nullptr); - CanThrowResult canThrow(const Expr *E); + CanThrowResult canThrow(const Stmt *E); const FunctionProtoType *ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT); void UpdateExceptionSpec(FunctionDecl *FD, diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index 80a1451..b6e4d8a 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -908,6 +908,12 @@ bool IfStmt::isObjCAvailabilityCheck() const { return isa(getCond()); } +Optional IfStmt::getNondiscardedCase(const ASTContext &Ctx) const { + if (!isConstexpr() || getCond()->isValueDependent()) + return None; + return !getCond()->EvaluateKnownConstInt(Ctx) ? getElse() : getThen(); +} + ForStmt::ForStmt(const ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, SourceLocation RP) diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index c1abf09..38b9ceb 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" @@ -970,17 +971,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(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(D) && D->hasAttr()) @@ -989,7 +995,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(E)) { + if (S.getLangOpts().CPlusPlus17 && E && isa(E)) { E = cast(E)->getCallee(); T = E->getType(); if (T->isSpecificPlaceholderType(BuiltinType::BoundMember)) { @@ -1026,13 +1032,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(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 +1101,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(E)->getSubExpr()); + return canThrow(cast(S)->getSubExpr()); case Expr::CXXThrowExprClass: // - a potentially evaluated throw-expression @@ -1082,16 +1116,20 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::CXXDynamicCastExprClass: { // - a potentially evaluated dynamic_cast expression dynamic_cast(v), // where T is a reference type, that requires a run-time check - CanThrowResult CT = canDynamicCastThrow(cast(E)); + auto *CE = cast(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(E)); + return canTypeidThrow(*this, cast(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 +1138,38 @@ CanThrowResult Sema::canThrow(const Expr *E) { case Expr::CXXMemberCallExprClass: case Expr::CXXOperatorCallExprClass: case Expr::UserDefinedLiteralClass: { - const CallExpr *CE = cast(E); + const CallExpr *CE = cast(S); CanThrowResult CT; - if (E->isTypeDependent()) + if (CE->isTypeDependent()) CT = CT_Dependent; else if (isa(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(E)->getConstructor()); + auto *CE = cast(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(E)->getConstructor()); + case Expr::CXXInheritedCtorInitExprClass: { + auto *ICIE = cast(S); + return canCalleeThrow(*this, ICIE, ICIE->getConstructor()); + } case Expr::LambdaExprClass: { - const LambdaExpr *Lambda = cast(E); + const LambdaExpr *Lambda = cast(S); CanThrowResult CT = CT_Cannot; for (LambdaExpr::const_capture_init_iterator Cap = Lambda->capture_init_begin(), @@ -1138,43 +1180,45 @@ CanThrowResult Sema::canThrow(const Expr *E) { } case Expr::CXXNewExprClass: { + auto *NE = cast(S); CanThrowResult CT; - if (E->isTypeDependent()) + if (NE->isTypeDependent()) CT = CT_Dependent; else - CT = canCalleeThrow(*this, E, cast(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(S); CanThrowResult CT; - QualType DTy = cast(E)->getDestroyedType(); + QualType DTy = DE->getDestroyedType(); if (DTy.isNull() || DTy->isDependentType()) { CT = CT_Dependent; } else { - CT = canCalleeThrow(*this, E, - cast(E)->getOperatorDelete()); + CT = canCalleeThrow(*this, DE, DE->getOperatorDelete()); if (const RecordType *RT = DTy->getAs()) { const CXXRecordDecl *RD = cast(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(S); // The bound temporary has to be destroyed again, which might throw. - CanThrowResult CT = canCalleeThrow(*this, E, - cast(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)); } // ObjC message sends are like function calls, but never have exception @@ -1196,12 +1240,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 +1255,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(S)->getType()->isVariablyModifiedType()) + return CT_Can; + return canSubStmtsThrow(*this, S); // Some might be dependent for other reasons. case Expr::ArraySubscriptExprClass: @@ -1231,29 +1281,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(S)) + if (CE->getType()->isVariablyModifiedType()) + return CT_Can; + CanThrowResult CT = + cast(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(E)->getExpr()); + return canThrow(cast(S)->getExpr()); case Expr::CXXDefaultInitExprClass: - return canThrow(cast(E)->getExpr()); + return canThrow(cast(S)->getExpr()); - case Expr::ChooseExprClass: - if (E->isTypeDependent() || E->isValueDependent()) + case Expr::ChooseExprClass: { + auto *CE = cast(S); + if (CE->isTypeDependent() || CE->isValueDependent()) return CT_Dependent; - return canThrow(cast(E)->getChosenSubExpr()); + return canThrow(CE->getChosenSubExpr()); + } case Expr::GenericSelectionExprClass: - if (cast(E)->isResultDependent()) + if (cast(S)->isResultDependent()) return CT_Dependent; - return canThrow(cast(E)->getResultExpr()); + return canThrow(cast(S)->getResultExpr()); // Some expressions are always dependent. case Expr::CXXDependentScopeMemberExprClass: @@ -1322,14 +1375,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(S)->decls()) { + if (auto *VD = dyn_cast(D)) + CT = mergeCanThrow(CT, canVarDeclThrow(*this, VD)); + + // FIXME: Properly determine whether a variably-modified type can throw. + if (auto *TND = dyn_cast(D)) + if (TND->getUnderlyingType()->isVariablyModifiedType()) + return CT_Can; + if (auto *VD = dyn_cast(D)) + if (VD->getType()->isVariablyModifiedType()) + return CT_Can; + } + return CT; + } + + case Stmt::IfStmtClass: { + auto *IS = cast(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 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(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(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"); } diff --git a/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp b/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp index f1fe01a..cbee300 100644 --- a/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp +++ b/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s -fexceptions -fcxx-exceptions -Wno-unevaluated-expression void f(); // expected-note {{possible target for call}} void f(int); // expected-note {{possible target for call}} @@ -17,3 +17,61 @@ struct S { bool b2 = noexcept(this->g(0)); } }; + +void stmt_expr() { + static_assert(noexcept(({ 0; }))); + + static_assert(!noexcept(({ throw 0; }))); + + static_assert(noexcept(({ + try { + throw 0; + } catch (...) { + } + 0; + }))); + + static_assert(!noexcept(({ + try { + throw 0; + } catch (...) { + throw; + } + 0; + }))); + + static_assert(!noexcept(({ + try { + throw 0; + } catch (int) { + } + 0; + }))); + + static_assert(!noexcept(({ + if (false) throw 0; + }))); + + static_assert(noexcept(({ + if constexpr (false) throw 0; + }))); + + static_assert(!noexcept(({ + if constexpr (false) throw 0; else throw 1; + }))); + + static_assert(noexcept(({ + if constexpr (true) 0; else throw 1; + }))); +} + +void vla(bool b) { + static_assert(noexcept(static_cast(0)), ""); + // FIXME: This can't actually throw, but we conservatively assume any VLA + // type can throw for now. + static_assert(!noexcept(static_cast(0)), ""); + static_assert(!noexcept(static_cast(0)), ""); + static_assert(!noexcept(reinterpret_cast(0)), ""); + static_assert(!noexcept((int(*)[b ? throw : 42])0), ""); + static_assert(!noexcept((int(*)[b ? throw : 42]){0}), ""); +} -- 2.7.4