[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