Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6331,6 +6331,15 @@ "cannot use '%0' with exceptions disabled">; def err_objc_exceptions_disabled : Error< "cannot use '%0' with Objective-C exceptions disabled">; +def warn_throw_in_noexcept_func + : Warning<"%0 has a non-throwing exception specification but can still " + "throw, may result in unexpected program termination.">, + InGroup; +def note_throw_in_dtor + : Note<"destructor or deallocator has a (possible implicit) non-throwing " + "excepton specification">; +def note_throw_in_function + : Note<"nonthrowing function declare here">; 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 a 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,47 @@ return BuildCXXThrow(OpLoc, Ex, IsThrownVarInScope); } +static bool isNoexcept(const FunctionDecl * FD) +{ + if (const FunctionProtoType *FPT = FD->getType()->castAs()) + if (FPT->getExceptionSpecType() != EST_None && + FPT->getNoexceptSpec(FD->getASTContext()) == + FunctionProtoType::NR_Nothrow) + return true; + return false; +} + +static bool isNoexceptTrue(const FunctionDecl * FD) { + // Avoid emitting error twice. + if (const auto * TempFD = FD->getTemplateInstantiationPattern()) + if (isNoexcept(TempFD)) + return false; + return isNoexcept(FD); +} + + +void Sema::CheckCXXThrowInNonThrowingFunc(SourceLocation OpLoc) { + bool isInCXXTryBlock = false; + for (Scope *S = getCurScope(); S; S = S->getParent()) + if (S->isTryScope()) { + isInCXXTryBlock = true; + break; + } else if (S->isFunctionScope()) + break; + if (const FunctionDecl *FD = getCurFunctionDecl()) + if (!isInCXXTryBlock && !getSourceManager().isInSystemHeader(OpLoc)) + if (isNoexceptTrue(FD)) { + Diag(OpLoc, diag::warn_throw_in_noexcept_func) << FD; + if (getLangOpts().CPlusPlus11 && + (isa(FD) || + FD->getDeclName().getCXXOverloadedOperator() == OO_Delete || + FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete)) + Diag(FD->getLocation(), diag::note_throw_in_dtor); + else + Diag(FD->getLocation(), diag::note_throw_in_function); + } +} + ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex, bool IsThrownVarInScope) { // Don't report an error if 'throw' is used in system headers. @@ -702,6 +743,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/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp =================================================================== --- test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp +++ test/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp @@ -6,7 +6,9 @@ template concept bool C2 = true; } -template concept bool C3() { return (throw 0, true); } +template concept bool C3() { // expected-note {{nonthrowing function declare here}} + return (throw 0, // expected-warning {{has a non-throwing exception specification but}} + true); } static_assert(noexcept(C3()), "function concept should be treated as if noexcept(true) specified"); template concept bool D1(); // expected-error {{function concept declaration must be a definition}} Index: test/CXX/except/except.spec/p11.cpp =================================================================== --- test/CXX/except/except.spec/p11.cpp +++ test/CXX/except/except.spec/p11.cpp @@ -2,7 +2,7 @@ // expected-no-diagnostics // This is the "let the user shoot themselves in the foot" clause. -void f() noexcept { +void f() noexcept(false) { throw 0; // no-error } void g() throw() { Index: test/SemaCXX/warn-throw-out_noexcept_func.cpp =================================================================== --- test/SemaCXX/warn-throw-out_noexcept_func.cpp +++ test/SemaCXX/warn-throw-out_noexcept_func.cpp @@ -0,0 +1,149 @@ +// RUN: %clang_cc1 %s -fdelayed-template-parsing -fcxx-exceptions -fexceptions -fsyntax-only -Wexceptions -verify -std=c++11 +struct A { + ~A(); +}; // implicitly noexcept(true) +A::~A() { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} +} +struct B { + int i; + ~B() noexcept(true) {} +}; +struct R : A { + B b; + ~R() { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } +}; + +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() { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} +} +struct X : A { + B b; + ~X() noexcept { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } +}; +struct Y : A { + B b; + ~Y() noexcept(true) { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } +}; +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() { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } +}; +template +struct B1 { + T i; + ~B1() noexcept(true) {} +}; +template +struct R1 : A1 //expected-note {{in instantiation of member function}} +{ + B1 b; + ~R1() { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } +}; +template +struct S1 : A1 { + B1 b; + ~S1() noexcept { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } +}; +void operator delete(void *ptr) noexcept { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} +} +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) { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } +}; +template +struct dependent_warn_both { + ~dependent_warn_both() noexcept(T::i) { // expected-note {{destructor or deallocator has a}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} + } +}; +void foo() noexcept { //expected-note {{nonthrowing function declare here}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} +} +void bar() noexcept { + try { + throw 1; + } catch (...) { + } +} +#define NOEXCEPT noexcept +void with_macro() NOEXCEPT { //expected-note {{nonthrowing function declare here}} + throw 1; // expected-warning {{has a non-throwing exception specification but}} +} + +void with_try_block() try { + throw 2; +} catch(...) { +} + +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}} +}