[clang] fbf60b7 - Properly compute whether statement expressions can throw, rather than
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Sun Dec 15 22:10:58 PST 2019
Author: Richard Smith
Date: 2019-12-15T22:02:31-08:00
New Revision: fbf60b7dbeb0f66b45037925c384859f2f161504
URL: https://github.com/llvm/llvm-project/commit/fbf60b7dbeb0f66b45037925c384859f2f161504
DIFF: https://github.com/llvm/llvm-project/commit/fbf60b7dbeb0f66b45037925c384859f2f161504.diff
LOG: 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.
Added:
Modified:
clang/include/clang/AST/Stmt.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/Stmt.cpp
clang/lib/Sema/SemaExceptionSpec.cpp
clang/test/SemaCXX/cxx0x-noexcept-expression.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 7aebbf2cb6a3..eaacb1a5b252 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -1995,6 +1995,10 @@ class IfStmt final
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<const Stmt*> 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 6d0f79a2d2ce..6b561dce3e8d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1599,7 +1599,7 @@ class Sema final {
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 80a1451ac789..b6e4d8aff21e 100644
--- a/clang/lib/AST/Stmt.cpp
+++ b/clang/lib/AST/Stmt.cpp
@@ -908,6 +908,12 @@ bool IfStmt::isObjCAvailabilityCheck() const {
return isa<ObjCAvailabilityCheckExpr>(getCond());
}
+Optional<const Stmt*> 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 c1abf099e9f2..38b9cebb6186 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<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 +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<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 +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<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 +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<ConstantExpr>(E)->getSubExpr());
+ return canThrow(cast<ConstantExpr>(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<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 +1138,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 +1180,45 @@ 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));
}
// 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<Expr>(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<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:
@@ -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<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");
}
diff --git a/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp b/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp
index f1fe01a845fd..cbee30003b23 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<int(*)[true ? 41 : 42]>(0)), "");
+ // FIXME: This can't actually throw, but we conservatively assume any VLA
+ // type can throw for now.
+ static_assert(!noexcept(static_cast<int(*)[b ? 41 : 42]>(0)), "");
+ static_assert(!noexcept(static_cast<int(*)[b ? throw : 42]>(0)), "");
+ static_assert(!noexcept(reinterpret_cast<int(*)[b ? throw : 42]>(0)), "");
+ static_assert(!noexcept((int(*)[b ? throw : 42])0), "");
+ static_assert(!noexcept((int(*)[b ? throw : 42]){0}), "");
+}
More information about the cfe-commits
mailing list