CanThrowResult Sema::canThrow()

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");
}