jyu2 updated this revision to Diff 99517.
jyu2 added a comment.
Reid,
Thank you so much for your comments. I upload new patch to address your
suggestion.
1> Emit warning for throw exception in all noexcept function. And special
diagnostic note for destructor and delete operators.
2> Silence this warning when the throw inside try block.
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/CXX/concepts-ts/dcl.dcl/dcl.spec/dcl.spec.concept/p1.cpp
test/CXX/except/except.spec/p11.cpp
test/SemaCXX/warn-throw-out-dtor.cpp
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -687,6 +687,48 @@
return BuildCXXThrow(OpLoc, Ex, IsThrownVarInScope);
}
+static bool isNoexcept(const FunctionDecl * FD)
+{
+ if (const FunctionProtoType *FPT = FD->getType()->castAs<FunctionProtoType>())
+ 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 FunctionDecl * TempFD = FD->getTemplateInstantiationPattern())
+ if (isNoexcept(TempFD))
+ return false;
+ return isNoexcept(FD);
+}
+
+
+void Sema::CheckCXXThrowInNonThrowingFunc(SourceLocation OpLoc) {
+ bool isInCXXTryBlock = false;
+ for (auto *S = getCurScope(); S; S = S->getParent())
+ if (S->getFlags() & (Scope::TryScope)) {
+ isInCXXTryBlock = true;
+ break;
+ } else if (S->getFlags() & (Scope::FnScope))
+ 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<CXXDestructorDecl>(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 +744,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,16 @@
"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' function assumed not to throw an exception but does. "
+ "Throwing exception may cause termination.">,
+ InGroup<ThrowInNoexceptFunc>;
+def note_throw_in_dtor
+ : Note<"destructor or deallocator has a (possible implicit) non-throwing "
+ "excepton specification">;
+def note_throw_in_function
+ : Note<"__declspec(nothrow), throw(), noexcept(true), or noexcept was "
+ "specified on the function">;
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 ThrowInNoexceptFunc : DiagGroup<"throw-in-noexcept-function">;
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,139 @@
+// RUN: %clang_cc1 %s -fdelayed-template-parsing -fcxx-exceptions -fexceptions -fsyntax-only -Wthrow-in-noexcept-function -verify -std=c++11
+struct A {
+ ~A();
+}; // implicitly noexcept(true)
+A::~A() { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+}
+struct B {
+ int i;
+ ~B() noexcept(true) {}
+};
+struct R : A {
+ B b;
+ ~R() { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+ }
+};
+
+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 {{function assumed not to throw an}}
+}
+struct X : A {
+ B b;
+ ~X() noexcept { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+ }
+};
+struct Y : A {
+ B b;
+ ~Y() noexcept(true) { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+ }
+};
+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() { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+ }
+};
+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() { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+ }
+};
+template <typename T>
+struct S1 : A1<T> {
+ B1<T> b;
+ ~S1() noexcept { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+ }
+};
+void operator delete(void *ptr) noexcept { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+}
+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) { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+ }
+};
+template <typename T>
+struct dependent_warn_both {
+ ~dependent_warn_both() noexcept(T::i) { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+ }
+};
+void foo() noexcept { //expected-note {{__declspec(nothrow), throw(), noexcept(true), or}}
+ throw 1; // expected-warning {{function assumed not to throw an}}
+}
+void bar() noexcept {
+ try {
+ throw 1;
+ } catch (...) {
+ }
+}
+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}}
+}
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<typename T> concept bool C2 = true;
}
-template<typename T> concept bool C3() { return (throw 0, true); }
+template<typename T> concept bool C3() { // expected-note {{__declspec(nothrow), throw(), noexcept(true), or}}
+ return (throw 0, // expected-warning {{function assumed not to throw an}}
+ true); }
static_assert(noexcept(C3<int>()), "function concept should be treated as if noexcept(true) specified");
template<typename T> 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() {
throw 0; // no-error
}
void g() throw() {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits