r238382 - Update -Winvalid-noreturn to handle destructors better.
Richard Trieu
rtrieu at google.com
Wed May 27 17:14:02 PDT 2015
Author: rtrieu
Date: Wed May 27 19:14:02 2015
New Revision: 238382
URL: http://llvm.org/viewvc/llvm-project?rev=238382&view=rev
Log:
Update -Winvalid-noreturn to handle destructors better.
When checking if a function is noreturn, consider a codepath to be noreturn if
the path destroys a class and the class destructor, base class destructors, or
member field destructors are marked noreturn.
Differential Revision: http://reviews.llvm.org/D9454
Modified:
cfe/trunk/include/clang/AST/DeclCXX.h
cfe/trunk/lib/AST/DeclCXX.cpp
cfe/trunk/lib/Analysis/CFG.cpp
cfe/trunk/test/SemaCXX/attr-noreturn.cpp
Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=238382&r1=238381&r2=238382&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Wed May 27 19:14:02 2015
@@ -1392,6 +1392,10 @@ public:
/// \brief Returns the destructor decl for this class.
CXXDestructorDecl *getDestructor() const;
+ /// \brief Returns true if the class destructor, or any implicitly invoked
+ /// destructors are marked noreturn.
+ bool isAnyDestructorNoReturn() const;
+
/// \brief If the class is a local class [class.local], returns
/// the enclosing function declaration.
const FunctionDecl *isLocalClass() const {
Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=238382&r1=238381&r2=238382&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Wed May 27 19:14:02 2015
@@ -1315,6 +1315,28 @@ CXXDestructorDecl *CXXRecordDecl::getDes
return Dtor;
}
+bool CXXRecordDecl::isAnyDestructorNoReturn() const {
+ // Destructor is noreturn.
+ if (const CXXDestructorDecl *Destructor = getDestructor())
+ if (Destructor->isNoReturn())
+ return true;
+
+ // Check base classes destructor for noreturn.
+ for (const auto &Base : bases())
+ if (Base.getType()->getAsCXXRecordDecl()->isAnyDestructorNoReturn())
+ return true;
+
+ // Check fields for noreturn.
+ for (const auto *Field : fields())
+ if (const CXXRecordDecl *RD =
+ Field->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl())
+ if (RD->isAnyDestructorNoReturn())
+ return true;
+
+ // All destructors are not noreturn.
+ return false;
+}
+
void CXXRecordDecl::completeDefinition() {
completeDefinition(nullptr);
}
Modified: cfe/trunk/lib/Analysis/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=238382&r1=238381&r2=238382&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFG.cpp (original)
+++ cfe/trunk/lib/Analysis/CFG.cpp Wed May 27 19:14:02 2015
@@ -1179,8 +1179,7 @@ void CFGBuilder::addAutomaticObjDtors(Lo
}
Ty = Context->getBaseElementType(Ty);
- const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor();
- if (Dtor->isNoReturn())
+ if (Ty->getAsCXXRecordDecl()->isAnyDestructorNoReturn())
Block = createNoReturnBlock();
else
autoCreateBlock();
@@ -3682,7 +3681,7 @@ CFGBlock *CFGBuilder::VisitCXXBindTempor
const CXXDestructorDecl *Dtor = E->getTemporary()->getDestructor();
- if (Dtor->isNoReturn()) {
+ if (Dtor->getParent()->isAnyDestructorNoReturn()) {
// If the destructor is marked as a no-return destructor, we need to
// create a new block for the destructor which does not have as a
// successor anything built thus far. Control won't flow out of this
Modified: cfe/trunk/test/SemaCXX/attr-noreturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/attr-noreturn.cpp?rev=238382&r1=238381&r2=238382&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/attr-noreturn.cpp (original)
+++ cfe/trunk/test/SemaCXX/attr-noreturn.cpp Wed May 27 19:14:02 2015
@@ -17,6 +17,154 @@ namespace test5 {
}
}
+namespace destructor_tests {
+ __attribute__((noreturn)) void fail();
+
+ struct A {
+ ~A() __attribute__((noreturn)) { fail(); }
+ };
+ struct B {
+ B() {}
+ ~B() __attribute__((noreturn)) { fail(); }
+ };
+ struct C : A {};
+ struct D : B {};
+ struct E : virtual A {};
+ struct F : A, virtual B {};
+ struct G : E {};
+ struct H : virtual D {};
+ struct I : A {};
+ struct J : I {};
+ struct K : virtual A {};
+ struct L : K {};
+ struct M : virtual C {};
+ struct N : M {};
+ struct O { N n; };
+
+ __attribute__((noreturn)) void test_1() { A a; }
+ __attribute__((noreturn)) void test_2() { B b; }
+ __attribute__((noreturn)) void test_3() { C c; }
+ __attribute__((noreturn)) void test_4() { D d; }
+ __attribute__((noreturn)) void test_5() { E e; }
+ __attribute__((noreturn)) void test_6() { F f; }
+ __attribute__((noreturn)) void test_7() { G g; }
+ __attribute__((noreturn)) void test_8() { H h; }
+ __attribute__((noreturn)) void test_9() { I i; }
+ __attribute__((noreturn)) void test_10() { J j; }
+ __attribute__((noreturn)) void test_11() { K k; }
+ __attribute__((noreturn)) void test_12() { L l; }
+ __attribute__((noreturn)) void test_13() { M m; }
+ __attribute__((noreturn)) void test_14() { N n; }
+ __attribute__((noreturn)) void test_15() { O o; }
+
+ __attribute__((noreturn)) void test_16() { const A& a = A(); }
+ __attribute__((noreturn)) void test_17() { const B& b = B(); }
+ __attribute__((noreturn)) void test_18() { const C& c = C(); }
+ __attribute__((noreturn)) void test_19() { const D& d = D(); }
+ __attribute__((noreturn)) void test_20() { const E& e = E(); }
+ __attribute__((noreturn)) void test_21() { const F& f = F(); }
+ __attribute__((noreturn)) void test_22() { const G& g = G(); }
+ __attribute__((noreturn)) void test_23() { const H& h = H(); }
+ __attribute__((noreturn)) void test_24() { const I& i = I(); }
+ __attribute__((noreturn)) void test_25() { const J& j = J(); }
+ __attribute__((noreturn)) void test_26() { const K& k = K(); }
+ __attribute__((noreturn)) void test_27() { const L& l = L(); }
+ __attribute__((noreturn)) void test_28() { const M& m = M(); }
+ __attribute__((noreturn)) void test_29() { const N& n = N(); }
+ __attribute__((noreturn)) void test_30() { const O& o = O(); }
+
+ struct AA {};
+ struct BB { BB() {} ~BB() {} };
+ struct CC : AA {};
+ struct DD : BB {};
+ struct EE : virtual AA {};
+ struct FF : AA, virtual BB {};
+ struct GG : EE {};
+ struct HH : virtual DD {};
+ struct II : AA {};
+ struct JJ : II {};
+ struct KK : virtual AA {};
+ struct LL : KK {};
+ struct MM : virtual CC {};
+ struct NN : MM {};
+ struct OO { NN n; };
+
+ __attribute__((noreturn)) void test_31() {
+ AA a;
+ BB b;
+ CC c;
+ DD d;
+ EE e;
+ FF f;
+ GG g;
+ HH h;
+ II i;
+ JJ j;
+ KK k;
+ LL l;
+ MM m;
+ NN n;
+ OO o;
+
+ const AA& aa = AA();
+ const BB& bb = BB();
+ const CC& cc = CC();
+ const DD& dd = DD();
+ const EE& ee = EE();
+ const FF& ff = FF();
+ const GG& gg = GG();
+ const HH& hh = HH();
+ const II& ii = II();
+ const JJ& jj = JJ();
+ const KK& kk = KK();
+ const LL& ll = LL();
+ const MM& mm = MM();
+ const NN& nn = NN();
+ const OO& oo = OO();
+ } // expected-warning {{function declared 'noreturn' should not return}}
+
+ struct P {
+ ~P() __attribute__((noreturn)) { fail(); }
+ void foo() {}
+ };
+ struct Q : P { };
+ __attribute__((noreturn)) void test31() {
+ P().foo();
+ }
+ __attribute__((noreturn)) void test32() {
+ Q().foo();
+ }
+
+ struct R {
+ A a[5];
+ };
+ __attribute__((noreturn)) void test33() {
+ R r;
+ }
+
+ // FIXME: Code flow analysis does not preserve information about non-null
+ // pointers, so it can't determine that this function is noreturn.
+ __attribute__((noreturn)) void test34() {
+ A *a = new A;
+ delete a;
+ } // expected-warning {{function declared 'noreturn' should not return}}
+
+ struct S {
+ virtual ~S();
+ };
+ struct T : S {
+ __attribute__((noreturn)) ~T();
+ };
+
+ // FIXME: Code flow analysis does not preserve information about non-null
+ // pointers or derived class pointers, so it can't determine that this
+ // function is noreturn.
+ __attribute__((noreturn)) void test35() {
+ S *s = new T;
+ delete s;
+ } // expected-warning {{function declared 'noreturn' should not return}}
+}
+
// PR5620
void f0() __attribute__((__noreturn__));
void f1(void (*)());
More information about the cfe-commits
mailing list