PR18217: Rewrite JumpDiagnostics' handling of temporaries, to correctly handle

declarations that might lifetime-extend multiple temporaries. In passing, fix a
crasher (PR18217) if an initializer was dependent and exactly the wrong shape,
and remove a bogus function (Expr::findMaterializedTemporary) now its last use
is gone.

llvm-svn: 197103
This commit is contained in:
Richard Smith 2013-12-12 01:27:02 +00:00
parent 49e69ee557
commit c934e4fd2c
5 changed files with 173 additions and 102 deletions

View File

@ -764,11 +764,6 @@ public:
SmallVectorImpl<const Expr *> &CommaLHS,
SmallVectorImpl<SubobjectAdjustment> &Adjustments) const;
/// Skip irrelevant expressions to find what should be materialize for
/// binding with a reference.
const Expr *
findMaterializedTemporary(const MaterializeTemporaryExpr *&MTE) const;
static bool classof(const Stmt *T) {
return T->getStmtClass() >= firstExprConstant &&
T->getStmtClass() <= lastExprConstant;

View File

@ -3941,6 +3941,9 @@ def note_exits_cleanup : Note<
"jump exits scope of variable with __attribute__((cleanup))">;
def note_exits_dtor : Note<
"jump exits scope of variable with non-trivial destructor">;
def note_exits_temporary_dtor : Note<
"jump exits scope of lifetime-extended temporary with non-trivial "
"destructor">;
def note_exits___block : Note<
"jump exits scope of __block variable">;
def note_exits_objc_try : Note<

View File

@ -105,37 +105,6 @@ const Expr *Expr::skipRValueSubobjectAdjustments(
return E;
}
const Expr *
Expr::findMaterializedTemporary(const MaterializeTemporaryExpr *&MTE) const {
const Expr *E = this;
// This might be a default initializer for a reference member. Walk over the
// wrapper node for that.
if (const CXXDefaultInitExpr *DAE = dyn_cast<CXXDefaultInitExpr>(E))
E = DAE->getExpr();
// Look through single-element init lists that claim to be lvalues. They're
// just syntactic wrappers in this case.
if (const InitListExpr *ILE = dyn_cast<InitListExpr>(E)) {
if (ILE->getNumInits() == 1 && ILE->isGLValue()) {
E = ILE->getInit(0);
if (const CXXDefaultInitExpr *DAE = dyn_cast<CXXDefaultInitExpr>(E))
E = DAE->getExpr();
}
}
// Look through expressions for materialized temporaries (for now).
if (const MaterializeTemporaryExpr *M
= dyn_cast<MaterializeTemporaryExpr>(E)) {
MTE = M;
E = M->GetTemporaryExpr();
}
if (const CXXDefaultArgExpr *DAE = dyn_cast<CXXDefaultArgExpr>(E))
E = DAE->getExpr();
return E;
}
/// isKnownToHaveBooleanValue - Return true if this is an integer expression
/// that is known to return 0 or 1. This happens for _Bool/bool expressions
/// but also int expressions which are produced by things like comparisons in

View File

@ -121,9 +121,11 @@ typedef std::pair<unsigned,unsigned> ScopePair;
/// GetDiagForGotoScopeDecl - If this decl induces a new goto scope, return a
/// diagnostic that should be emitted if control goes over it. If not, return 0.
static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) {
static ScopePair GetDiagForGotoScopeDecl(Sema &S, const Decl *D) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
unsigned InDiag = 0;
unsigned OutDiag = 0;
if (VD->getType()->isVariablyModifiedType())
InDiag = diag::note_protected_by_vla;
@ -135,21 +137,24 @@ static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) {
return ScopePair(diag::note_protected_by_cleanup,
diag::note_exits_cleanup);
if (Context.getLangOpts().ObjCAutoRefCount && VD->hasLocalStorage()) {
switch (VD->getType().getObjCLifetime()) {
case Qualifiers::OCL_None:
case Qualifiers::OCL_ExplicitNone:
case Qualifiers::OCL_Autoreleasing:
break;
case Qualifiers::OCL_Strong:
case Qualifiers::OCL_Weak:
if (VD->hasLocalStorage()) {
switch (VD->getType().isDestructedType()) {
case QualType::DK_objc_strong_lifetime:
case QualType::DK_objc_weak_lifetime:
return ScopePair(diag::note_protected_by_objc_ownership,
diag::note_exits_objc_ownership);
case QualType::DK_cxx_destructor:
OutDiag = diag::note_exits_dtor;
break;
case QualType::DK_none:
break;
}
}
if (Context.getLangOpts().CPlusPlus && VD->hasLocalStorage()) {
const Expr *Init = VD->getInit();
if (S.Context.getLangOpts().CPlusPlus && VD->hasLocalStorage() && Init) {
// C++11 [stmt.dcl]p3:
// A program that jumps from a point where a variable with automatic
// storage duration is not in scope to a point where it is in scope
@ -164,68 +169,34 @@ static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) {
// where it is in scope is ill-formed unless the variable has
// POD type and is declared without an initializer.
const Expr *Init = VD->getInit();
if (!Init)
return ScopePair(InDiag, 0);
InDiag = diag::note_protected_by_variable_init;
const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init);
if (EWC)
Init = EWC->getSubExpr();
const MaterializeTemporaryExpr *M = NULL;
Init = Init->findMaterializedTemporary(M);
SmallVector<const Expr *, 2> CommaLHSs;
SmallVector<SubobjectAdjustment, 2> Adjustments;
Init = Init->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments);
QualType QT = Init->getType();
if (QT.isNull())
return ScopePair(diag::note_protected_by_variable_init, 0);
const Type *T = QT.getTypePtr();
if (T->isArrayType())
T = T->getBaseElementTypeUnsafe();
const CXXRecordDecl *Record = T->getAsCXXRecordDecl();
if (!Record)
return ScopePair(diag::note_protected_by_variable_init, 0);
// If we need to call a non-trivial destructor for this variable,
// record an out diagnostic.
unsigned OutDiag = 0;
if (!Init->isGLValue() && !Record->hasTrivialDestructor())
OutDiag = diag::note_exits_dtor;
if (const CXXConstructExpr *cce = dyn_cast<CXXConstructExpr>(Init)) {
const CXXConstructorDecl *ctor = cce->getConstructor();
// For a variable declared without an initializer, we will have
// call-style initialization and the initializer will be the
// CXXConstructExpr with no intervening nodes.
if (ctor->isTrivial() && ctor->isDefaultConstructor() &&
VD->getInit() == Init && VD->getInitStyle() == VarDecl::CallInit) {
// For a variable of (array of) class type declared without an
// initializer, we will have call-style initialization and the initializer
// will be the CXXConstructExpr with no intervening nodes.
if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(Init)) {
const CXXConstructorDecl *Ctor = CCE->getConstructor();
if (Ctor->isTrivial() && Ctor->isDefaultConstructor() &&
VD->getInitStyle() == VarDecl::CallInit) {
if (OutDiag)
InDiag = diag::note_protected_by_variable_nontriv_destructor;
else if (!Record->isPOD())
else if (!Ctor->getParent()->isPOD())
InDiag = diag::note_protected_by_variable_non_pod;
return ScopePair(InDiag, OutDiag);
else
InDiag = 0;
}
}
return ScopePair(diag::note_protected_by_variable_init, OutDiag);
}
return ScopePair(InDiag, 0);
return ScopePair(InDiag, OutDiag);
}
if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
if (const TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) {
if (TD->getUnderlyingType()->isVariablyModifiedType())
return ScopePair(diag::note_protected_by_vla_typedef, 0);
}
if (const TypeAliasDecl *TD = dyn_cast<TypeAliasDecl>(D)) {
if (TD->getUnderlyingType()->isVariablyModifiedType())
return ScopePair(diag::note_protected_by_vla_type_alias, 0);
return ScopePair(isa<TypedefDecl>(TD)
? diag::note_protected_by_vla_typedef
: diag::note_protected_by_vla_type_alias,
0);
}
return ScopePair(0U, 0U);
@ -234,7 +205,7 @@ static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) {
/// \brief Build scope information for a declaration that is part of a DeclStmt.
void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) {
// If this decl causes a new scope, push and switch to it.
std::pair<unsigned,unsigned> Diags = GetDiagForGotoScopeDecl(S.Context, D);
std::pair<unsigned,unsigned> Diags = GetDiagForGotoScopeDecl(S, D);
if (Diags.first || Diags.second) {
Scopes.push_back(GotoScope(ParentScope, Diags.first, Diags.second,
D->getLocation()));
@ -481,7 +452,26 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope)
}
}
}
// Disallow jumps out of scopes containing temporaries lifetime-extended to
// automatic storage duration.
if (MaterializeTemporaryExpr *MTE =
dyn_cast<MaterializeTemporaryExpr>(SubStmt)) {
if (MTE->getStorageDuration() == SD_Automatic) {
SmallVector<const Expr *, 4> CommaLHS;
SmallVector<SubobjectAdjustment, 4> Adjustments;
const Expr *ExtendedObject =
MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(
CommaLHS, Adjustments);
if (ExtendedObject->getType().isDestructedType()) {
Scopes.push_back(GotoScope(ParentScope, 0,
diag::note_exits_temporary_dtor,
ExtendedObject->getExprLoc()));
ParentScope = Scopes.size()-1;
}
}
}
// Recursively walk the AST.
BuildScopeInformation(SubStmt, ParentScope);
}

View File

@ -226,7 +226,7 @@ namespace test12 {
static void *ips[] = { &&l0 };
const C c0 = 17;
l0: // expected-note {{possible target of indirect goto}}
const C &c1 = 42; // expected-note {{jump exits scope of variable with non-trivial destructor}}
const C &c1 = 42; // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
const C &c2 = c0;
goto *ip; // expected-error {{indirect goto might cross protected scopes}}
}
@ -241,7 +241,7 @@ namespace test13 {
void f(void **ip) {
static void *ips[] = { &&l0 };
l0: // expected-note {{possible target of indirect goto}}
const int &c1 = C(1).i; // expected-note {{jump exits scope of variable with non-trivial destructor}}
const int &c1 = C(1).i; // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
goto *ip; // expected-error {{indirect goto might cross protected scopes}}
}
}
@ -295,6 +295,120 @@ x: return s.get();
}
#endif
namespace test18 {
struct A { ~A(); };
struct B { const int &r; const A &a; };
int f() {
void *p = &&x;
const A a = A();
x:
B b = { 0, a }; // ok
goto *p;
}
int g() {
void *p = &&x;
x: // expected-note {{possible target of indirect goto}}
B b = { 0, A() }; // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
goto *p; // expected-error {{indirect goto might cross protected scopes}}
}
}
#if __cplusplus >= 201103L
namespace std {
typedef decltype(sizeof(int)) size_t;
template<typename T> struct initializer_list {
const T *begin;
size_t size;
initializer_list(const T *, size_t);
};
}
namespace test19 {
struct A { ~A(); };
int f() {
void *p = &&x;
A a;
x: // expected-note {{possible target of indirect goto}}
std::initializer_list<A> il = { a }; // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
goto *p; // expected-error {{indirect goto might cross protected scopes}}
}
}
namespace test20 {
struct A { ~A(); };
struct B {
const A &a;
};
int f() {
void *p = &&x;
A a;
x:
std::initializer_list<B> il = {
a,
a
};
goto *p;
}
int g() {
void *p = &&x;
A a;
x: // expected-note {{possible target of indirect goto}}
std::initializer_list<B> il = {
a,
{ A() } // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
};
goto *p; // expected-error {{indirect goto might cross protected scopes}}
}
}
#endif
namespace test21 {
template<typename T> void f() {
goto x; // expected-error {{protected scope}}
T t; // expected-note {{bypasses}}
x: return;
}
template void f<int>();
struct X { ~X(); };
template void f<X>(); // expected-note {{instantiation of}}
}
namespace PR18217 {
typedef int *X;
template <typename T>
class MyCl {
T mem;
};
class Source {
MyCl<X> m;
public:
int getKind() const;
};
bool b;
template<typename TT>
static void foo(const Source &SF, MyCl<TT *> Source::*m) {
switch (SF.getKind()) {
case 1: return;
case 2: break;
case 3:
case 4: return;
};
if (b) {
auto &y = const_cast<MyCl<TT *> &>(SF.*m); // expected-warning 0-1{{extension}}
}
}
int Source::getKind() const {
foo(*this, &Source::m);
return 0;
}
}
// This test must be last, because the error prohibits further jump diagnostics.
namespace testInvalid {
Invalid inv; // expected-error {{unknown type name}}