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: 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; 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/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: 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()) + 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(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: 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 +class A1 { + T b; + +public: + ~A1() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template +struct B1 { + T i; + ~B1() noexcept(true) {} +}; +template +struct R1 : A1 //expected-note {{in instantiation of member function}} +{ + B1 b; + ~R1() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template +struct S1 : A1 { + B1 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 +struct dependent_warn { + ~dependent_warn() noexcept(T::i) { + throw 1; + } +}; +template +struct dependent_warn_noexcept { + ~dependent_warn_noexcept() noexcept(T::i) { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template +struct dependent_warn_both { + ~dependent_warn_both() noexcept(T::i) { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +int main() { + R1 o; //expected-note {{in instantiation of member function}} + S1 b; + dependent_warn f; + dependent_warn_noexcept f1; //expected-note {{in instantiation of member function}} + dependent_warn_both f2; + dependent_warn_both 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 +class A1{ + T b; +public: + ~A1() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template +struct B1 +{ + T i; + ~B1() noexcept(true) {} +}; +template +struct R1 : A1 //expected-note {{in instantiation of member function}} +{ + B1 b; + ~R1() { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template +struct S1 : A1 +{ + B1 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 +struct dependent_warn { + ~dependent_warn() noexcept(T::i) + { + throw 1; + } +}; +template +struct dependent_warn_noexcept { + ~dependent_warn_noexcept() noexcept(T::i) + { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +template +struct dependent_warn_both { + ~dependent_warn_both() noexcept(T::i) + { + throw 1; // expected-warning {{has a (possible implicit) non-throwing noexcept}} + } +}; +int main() +{ + R1 o; //expected-note {{in instantiation of member function}} + S1 b; + dependent_warn f; + dependent_warn_noexcept f1; //expected-note {{in instantiation of member function}} + dependent_warn_both f2; + dependent_warn_both f3; //expected-note {{in instantiation of member function}} +} +