jyu2 created this revision. Throwing in the destructor is not good (C++11 change try to not allow see below). But in reality, those codes are exist. C++11 [class.dtor]p3:
A declaration of a destructor that does not have an exception-specification is implicitly considered to have the same exception specification as an implicit declaration. With this change, the application worked before may now run into runtime termination. May gold here is to emit a warning to provide only possible info to where the code may need to be changed. First there is no way, in compile time to identify the “throw” really throw out of the function. Things like the call which throw out… To keep this simple, when “throw” is seen, checking its enclosing function(only destructor and dealloc functions) with noexcept(true) specifier emit warning. Here is implementation detail: A new member function CheckCXXThrowInNonThrowingFunc is added for class Sema in Sema.h. It is used in the call to both BuildCXXThrow and TransformCXXThrowExpr. The function basic check if the enclosing function with non-throwing noexcept specifer, if so emit warning for it. The example of warning message like: k1.cpp:18:3: warning: ''~dependent_warn'' has a (possible implicit) non-throwing noexcept specifier. Throwing exception may cause termination. [-Wthrow-in-dtor] throw 1; ^ k1.cpp:43:30: note: in instantiation of member function 'dependent_warn<noexcept_fun>::~dependent_warn' requested here dependent_warn<noexcept_fun> f; // cause warning Let me know if more information is needed. Thanks. Jennifer https://reviews.llvm.org/D33333 Files: include/clang/Basic/DiagnosticGroups.td include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/Sema.h lib/Sema/SemaExprCXX.cpp lib/Sema/TreeTransform.h test/SemaCXX/warn-throw-out-dtor.cpp
Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -687,6 +687,37 @@ return BuildCXXThrow(OpLoc, Ex, IsThrownVarInScope); } +static bool isNoexcept(const FunctionDecl * FD) +{ + if (const FunctionProtoType *FPT = FD->getType()->castAs<FunctionProtoType>()) + if (isNoexceptExceptionSpec(FPT->getExceptionSpecType()) && + FPT->getNoexceptSpec(FD->getASTContext()) == + FunctionProtoType::NR_Nothrow) + return true; + return false; +} + +static bool isNoexceptTrue(const FunctionDecl * FD) +{ + // Avoid emitting error twice. + if (const FunctionDecl * TempFD = FD->getTemplateInstantiationPattern()) + if (isNoexcept(TempFD)) + return false; + return isNoexcept(FD); +} + +void Sema::CheckCXXThrowInNonThrowingFunc(SourceLocation OpLoc) { + + if (const FunctionDecl *FD = getCurFunctionDecl()) + if (getLangOpts().CPlusPlus11 && + !getSourceManager().isInSystemHeader(OpLoc) && + (isa<CXXDestructorDecl>(FD) || + FD->getDeclName().getCXXOverloadedOperator() == OO_Delete || + FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete)) + if (isNoexceptTrue(FD)) + Diag(OpLoc, diag::warn_throw_in_dtor) << FD; +} + ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex, bool IsThrownVarInScope) { // Don't report an error if 'throw' is used in system headers. @@ -702,6 +733,8 @@ if (getCurScope() && getCurScope()->isOpenMPSimdDirectiveScope()) Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw"; + CheckCXXThrowInNonThrowingFunc(OpLoc); + if (Ex && !Ex->isTypeDependent()) { QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType()); if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex)) Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -9873,9 +9873,10 @@ if (SubExpr.isInvalid()) return ExprError(); - if (!getDerived().AlwaysRebuild() && - SubExpr.get() == E->getSubExpr()) + if (!getDerived().AlwaysRebuild() && SubExpr.get() == E->getSubExpr()) { + getSema().CheckCXXThrowInNonThrowingFunc(E->getThrowLoc()); return E; + } return getDerived().RebuildCXXThrowExpr(E->getThrowLoc(), SubExpr.get(), E->isThrownVariableInScope()); Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4967,6 +4967,9 @@ ExprResult BuildCXXThrow(SourceLocation OpLoc, Expr *Ex, bool IsThrownVarInScope); bool CheckCXXThrowOperand(SourceLocation ThrowLoc, QualType ThrowTy, Expr *E); + /// Check if throw is used in function with non-throwing noexcept + /// specifier. + void CheckCXXThrowInNonThrowingFunc(SourceLocation OpLoc); /// ActOnCXXTypeConstructExpr - Parse construction of a specified type. /// Can be interpreted either as function-style casting ("int(x)") Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6331,6 +6331,10 @@ "cannot use '%0' with exceptions disabled">; def err_objc_exceptions_disabled : Error< "cannot use '%0' with Objective-C exceptions disabled">; +def warn_throw_in_dtor + : Warning<"'%0' has a (possible implicit) non-throwing noexcept " + "specifier. Throwing exception may cause termination.">, + InGroup<ThrowInDtor>; def err_seh_try_outside_functions : Error< "cannot use SEH '__try' in blocks, captured regions, or Obj-C method decls">; def err_mixing_cxx_try_seh_try : Error< Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -137,6 +137,7 @@ def EmptyBody : DiagGroup<"empty-body">; def Exceptions : DiagGroup<"exceptions">; +def ThrowInDtor : DiagGroup<"throw-in-dtor">; def GNUEmptyInitializer : DiagGroup<"gnu-empty-initializer">; def GNUEmptyStruct : DiagGroup<"gnu-empty-struct">; Index: test/SemaCXX/warn-throw-out-dtor.cpp =================================================================== --- test/SemaCXX/warn-throw-out-dtor.cpp +++ test/SemaCXX/warn-throw-out-dtor.cpp @@ -0,0 +1,277 @@ +// RUN: %clang_cc1 %s -fdelayed-template-parsing -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 +struct A { + ~A(); +}; // implicitly noexcept(true) +A::~A() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} +} +struct B { + int i; + ~B() noexcept(true) {} +}; +struct R : A { + B b; + ~R() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; + +struct M : A { + B b; + ~M() noexcept(false); +}; + +M::~M() noexcept(false) { + throw 1; +} + +struct N : A { + B b; + ~N(); //implicitly noexcept(true) +}; + +N::~N() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} +} +struct X : A { + B b; + ~X() noexcept { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +struct Y : A { + B b; + ~Y() noexcept(true) { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +struct C { + int i; + ~C() noexcept(false) {} +}; +struct D : A { + C c; + ~D() { //implicitly noexcept(false) + throw 1; + } +}; +struct E : A { + C c; + ~E(); //implicitly noexcept(false) +}; +E::~E() //implicitly noexcept(false) +{ + throw 1; +} + +template <typename T> +class A1 { + T b; + +public: + ~A1() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template <typename T> +struct B1 { + T i; + ~B1() noexcept(true) {} +}; +template <typename T> +struct R1 : A1<T> //expected-note {{in instantiation of member function}} +{ + B1<T> b; + ~R1() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template <typename T> +struct S1 : A1<T> { + B1<T> b; + ~S1() noexcept { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +void operator delete(void *ptr) noexcept { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} +} +struct except_fun { + static const bool i = false; +}; +struct noexcept_fun { + static const bool i = true; +}; +template <typename T> +struct dependent_warn { + ~dependent_warn() noexcept(T::i) { + throw 1; + } +}; +template <typename T> +struct dependent_warn_noexcept { + ~dependent_warn_noexcept() noexcept(T::i) { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template <typename T> +struct dependent_warn_both { + ~dependent_warn_both() noexcept(T::i) { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +int main() { + R1<int> o; //expected-note {{in instantiation of member function}} + S1<int> b; + dependent_warn<except_fun> f; + dependent_warn_noexcept<noexcept_fun> f1; //expected-note {{in instantiation of member function}} + dependent_warn_both<except_fun> f2; + dependent_warn_both<noexcept_fun> f3; //expected-note {{in instantiation of member function}} +} +// RUN: %clang_cc1 %s -fdelayed-template-parsing -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 +struct A { ~A();}; // implicitly noexcept(true) +A::~A() +{ + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} +} +struct B +{ + int i; + ~B() noexcept(true) {} +}; +struct R : A +{ + B b; + ~R() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; + +struct M : A +{ + B b; + ~M() noexcept(false); +}; + +M::~M() noexcept(false) +{ + throw 1; +} + +struct N : A +{ + B b; + ~N(); //implicitly noexcept(true) +}; + +N::~N () +{ + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} +} +struct X : A +{ + B b; + ~X() noexcept { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +struct Y : A +{ + B b; + ~Y() noexcept(true) { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +struct C +{ + int i; + ~C() noexcept(false) {} +}; +struct D : A +{ + C c; + ~D() { //implicitly noexcept(false) + throw 1; + } +}; +struct E : A +{ + C c; + ~E(); //implicitly noexcept(false) +}; +E::~E() //implicitly noexcept(false) +{ + throw 1; +} + +template <typename T> +class A1{ + T b; +public: + ~A1() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template <typename T> +struct B1 +{ + T i; + ~B1() noexcept(true) {} +}; +template <typename T> +struct R1 : A1 <T> //expected-note {{in instantiation of member function}} +{ + B1<T> b; + ~R1() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template <typename T> +struct S1 : A1 <T> +{ + B1<T> b; + ~S1() noexcept { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +void operator delete(void* ptr) noexcept +{ + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} +} +struct except_fun { +static const bool i = false; +}; +struct noexcept_fun { +static const bool i = true; +}; +template<typename T> +struct dependent_warn { + ~dependent_warn() noexcept(T::i) + { + throw 1; + } +}; +template<typename T> +struct dependent_warn_noexcept { + ~dependent_warn_noexcept() noexcept(T::i) + { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template<typename T> +struct dependent_warn_both { + ~dependent_warn_both() noexcept(T::i) + { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +int main() +{ + R1 <int> o; //expected-note {{in instantiation of member function}} + S1 <int> b; + dependent_warn<except_fun> f; + dependent_warn_noexcept<noexcept_fun> f1; //expected-note {{in instantiation of member function}} + dependent_warn_both<except_fun> f2; + dependent_warn_both<noexcept_fun> f3; //expected-note {{in instantiation of member function}} +} +
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits