forked from OSchip/llvm-project
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 llvm-svn: 238382
This commit is contained in:
parent
caf09fe022
commit
95a192a3ab
|
@ -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 {
|
||||
|
|
|
@ -1315,6 +1315,28 @@ CXXDestructorDecl *CXXRecordDecl::getDestructor() const {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -1179,8 +1179,7 @@ void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B,
|
|||
}
|
||||
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::VisitCXXBindTemporaryExprForTemporaryDtors(
|
|||
|
||||
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
|
||||
|
|
|
@ -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 (*)());
|
||||
|
|
Loading…
Reference in New Issue