in clang/lib/Sema/SemaExceptionSpec.cpp [1126:1594]
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::CXXAddrspaceCastExprClass:
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::MatrixSubscriptExprClass:
case Expr::OMPArraySectionExprClass:
case Expr::OMPArrayShapingExprClass:
case Expr::OMPIteratorExprClass:
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:
case Expr::RecoveryExprClass:
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::OMPScanDirectiveClass:
case Stmt::OMPForDirectiveClass:
case Stmt::OMPForSimdDirectiveClass:
case Stmt::OMPMasterDirectiveClass:
case Stmt::OMPMasterTaskLoopDirectiveClass:
case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
case Stmt::OMPOrderedDirectiveClass:
case Stmt::OMPCanonicalLoopClass:
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::OMPTileDirectiveClass:
case Stmt::OMPUnrollDirectiveClass:
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::OMPInteropDirectiveClass:
case Stmt::OMPDispatchDirectiveClass:
case Stmt::OMPMaskedDirectiveClass:
case Stmt::OMPMetaDirectiveClass:
case Stmt::OMPGenericLoopDirectiveClass:
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::SYCLUniqueStableNameExprClass:
return CT_Cannot;
case Stmt::NoStmtClass:
llvm_unreachable("Invalid class for statement");
}
llvm_unreachable("Bogus StmtClass");
}