From: Sebastian Redl Date: Fri, 10 Sep 2010 20:55:33 +0000 (+0000) Subject: Implement Expr::CanThrow, a function that applies the noexcept operator rules to... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=369e51fa400aeb5835bb9af4634ea516c11429a7;p=clang Implement Expr::CanThrow, a function that applies the noexcept operator rules to expressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@113621 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index debc637745..4cf2d17076 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -253,7 +253,7 @@ public: bool isRValue() const { return Kind >= CL_XValue; } bool isModifiable() const { return getModifiable() == CM_Modifiable; } }; - /// \brief classify - Classify this expression according to the C++0x + /// \brief Classify - Classify this expression according to the C++0x /// expression taxonomy. /// /// C++0x defines ([basic.lval]) a new taxonomy of expressions to replace the @@ -269,7 +269,7 @@ public: return ClassifyImpl(Ctx, 0); } - /// \brief classifyModifiable - Classify this expression according to the + /// \brief ClassifyModifiable - Classify this expression according to the /// C++0x expression taxonomy, and see if it is valid on the left side /// of an assignment. /// @@ -408,6 +408,16 @@ public: /// write barrier. bool isOBJCGCCandidate(ASTContext &Ctx) const; + /// \brief Result type of CanThrow(). + enum CanThrowResult { + CT_Cannot, + CT_Dependent, + CT_Can + }; + /// \brief Test if this expression, if evaluated, might throw, according to + /// the rules of C++ [expr.unary.noexcept]. + CanThrowResult CanThrow(ASTContext &C) const; + /// IgnoreParens - Ignore parentheses. If this Expr is a ParenExpr, return /// its subexpression. If that subexpression is also a ParenExpr, /// then this method recursively returns its subexpression, and so forth. diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index cf3eea959e..35456b38ef 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1319,6 +1319,207 @@ bool Expr::isOBJCGCCandidate(ASTContext &Ctx) const { return cast(this)->getBase()->isOBJCGCCandidate(Ctx); } } + +static Expr::CanThrowResult MergeCanThrow(Expr::CanThrowResult CT1, + Expr::CanThrowResult CT2) { + // CanThrowResult constants are ordered so that the maximum is the correct + // merge result. + return CT1 > CT2 ? CT1 : CT2; +} + +static Expr::CanThrowResult CanSubExprsThrow(ASTContext &C, const Expr *CE) { + Expr *E = const_cast(CE); + Expr::CanThrowResult R = Expr::CT_Cannot; + for (Expr::child_iterator I = E->child_begin(), IE = E->child_end(); + I != IE && R != Expr::CT_Can; ++I) { + R = MergeCanThrow(R, cast(*I)->CanThrow(C)); + } + return R; +} + +static Expr::CanThrowResult CanCalleeThrow(const Decl *D, + bool NullThrows = true) { + if (!D) + return NullThrows ? Expr::CT_Can : Expr::CT_Cannot; + + // See if we can get a function type from the decl somehow. + const ValueDecl *VD = dyn_cast(D); + if (!VD) // If we have no clue what we're calling, assume the worst. + return Expr::CT_Can; + + QualType T = VD->getType(); + const FunctionProtoType *FT; + if ((FT = T->getAs())) { + } else if (const PointerType *PT = T->getAs()) + FT = PT->getPointeeType()->getAs(); + else if (const ReferenceType *RT = T->getAs()) + FT = RT->getPointeeType()->getAs(); + else if (const MemberPointerType *MT = T->getAs()) + FT = MT->getPointeeType()->getAs(); + else if (const BlockPointerType *BT = T->getAs()) + FT = BT->getPointeeType()->getAs(); + + if (!FT) + return Expr::CT_Can; + + return FT->hasEmptyExceptionSpec() ? Expr::CT_Cannot : Expr::CT_Can; +} + +static Expr::CanThrowResult CanDynamicCastThrow(const CXXDynamicCastExpr *DC) { + if (DC->isTypeDependent()) + return Expr::CT_Dependent; + + return DC->getCastKind() == clang::CK_Dynamic? Expr::CT_Can : Expr::CT_Cannot; +} + +static Expr::CanThrowResult CanTypeidThrow(ASTContext &C, + const CXXTypeidExpr *DC) { + if (DC->isTypeOperand()) + return Expr::CT_Cannot; + + Expr *Op = DC->getExprOperand(); + if (Op->isTypeDependent()) + return Expr::CT_Dependent; + + const RecordType *RT = Op->getType()->getAs(); + if (!RT) + return Expr::CT_Cannot; + + if (!cast(RT->getDecl())->isPolymorphic()) + return Expr::CT_Cannot; + + if (Op->Classify(C).isPRValue()) + return Expr::CT_Cannot; + + return Expr::CT_Can; +} + +Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { + // C++ [expr.unary.noexcept]p3: + // [Can throw] if in a potentially-evaluated context the expression would + // contain: + switch (getStmtClass()) { + case CXXThrowExprClass: + // - a potentially evaluated throw-expression + return CT_Can; + + case 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(this)); + if (CT == CT_Can) + return CT; + return MergeCanThrow(CT, CanSubExprsThrow(C, this)); + } + + case CXXTypeidExprClass: + // - a potentially evaluated typeid expression applied to a glvalue + // expression whose type is a polymorphic class type + return CanTypeidThrow(C, cast(this)); + + // - 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 CallExprClass: + case CXXOperatorCallExprClass: + case CXXMemberCallExprClass: { + CanThrowResult CT = CanCalleeThrow(cast(this)->getCalleeDecl()); + if (CT == CT_Can) + return CT; + return MergeCanThrow(CT, CanSubExprsThrow(C, this)); + } + + case CXXConstructExprClass: { + CanThrowResult CT = CanCalleeThrow( + cast(this)->getConstructor()); + if (CT == CT_Can) + return CT; + return MergeCanThrow(CT, CanSubExprsThrow(C, this)); + } + + case CXXNewExprClass: { + CanThrowResult CT = MergeCanThrow( + CanCalleeThrow(cast(this)->getOperatorNew()), + CanCalleeThrow(cast(this)->getConstructor(), + /*NullThrows*/false)); + if (CT == CT_Can) + return CT; + return MergeCanThrow(CT, CanSubExprsThrow(C, this)); + } + + case CXXDeleteExprClass: { + // FIXME: check if destructor might throw + CanThrowResult CT = CanCalleeThrow( + cast(this)->getOperatorDelete()); + if (CT == CT_Can) + return CT; + return MergeCanThrow(CT, CanSubExprsThrow(C, this)); + } + + // ObjC message sends are like function calls, but never have exception + // specs. + case ObjCMessageExprClass: + case ObjCPropertyRefExprClass: + case ObjCImplicitSetterGetterRefExprClass: + return CT_Can; + + // Many other things have subexpressions, so we have to test those. + // Some are simple: + case ParenExprClass: + case MemberExprClass: + case CXXReinterpretCastExprClass: + case CXXConstCastExprClass: + case ConditionalOperatorClass: + case CompoundLiteralExprClass: + case ExtVectorElementExprClass: + case InitListExprClass: + case DesignatedInitExprClass: + case ParenListExprClass: + case VAArgExprClass: + case CXXDefaultArgExprClass: + case CXXBindTemporaryExprClass: + case CXXExprWithTemporariesClass: + case CXXTemporaryObjectExprClass: + case ObjCIvarRefExprClass: + case ObjCIsaExprClass: + case ShuffleVectorExprClass: + return CanSubExprsThrow(C, this); + + // Some might be dependent for other reasons. + case UnaryOperatorClass: + case ArraySubscriptExprClass: + case ImplicitCastExprClass: + case CStyleCastExprClass: + case CXXStaticCastExprClass: + case CXXFunctionalCastExprClass: + case BinaryOperatorClass: + case CompoundAssignOperatorClass: { + CanThrowResult CT = isTypeDependent() ? CT_Dependent : CT_Cannot; + return MergeCanThrow(CT, CanSubExprsThrow(C, this)); + } + + // FIXME: We should handle StmtExpr, but that opens a MASSIVE can of worms. + case StmtExprClass: + return CT_Can; + + case ChooseExprClass: + if (isTypeDependent() || isValueDependent()) + return CT_Dependent; + return cast(this)->getChosenSubExpr(C)->CanThrow(C); + + // Some expressions are always dependent. + case DependentScopeDeclRefExprClass: + case CXXUnresolvedConstructExprClass: + case CXXDependentScopeMemberExprClass: + return CT_Dependent; + + default: + // All other expressions don't have subexpressions, or else they are + // unevaluated. + return CT_Cannot; + } +} + Expr* Expr::IgnoreParens() { Expr* E = this; while (ParenExpr* P = dyn_cast(E))