r197103 - PR18217: Rewrite JumpDiagnostics' handling of temporaries, to correctly handle
Richard Smith
richard-llvm at metafoo.co.uk
Wed Dec 11 17:27:02 PST 2013
Author: rsmith
Date: Wed Dec 11 19:27:02 2013
New Revision: 197103
URL: http://llvm.org/viewvc/llvm-project?rev=197103&view=rev
Log:
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.
Modified:
cfe/trunk/include/clang/AST/Expr.h
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/AST/Expr.cpp
cfe/trunk/lib/Sema/JumpDiagnostics.cpp
cfe/trunk/test/SemaCXX/scope-check.cpp
Modified: cfe/trunk/include/clang/AST/Expr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Expr.h (original)
+++ cfe/trunk/include/clang/AST/Expr.h Wed Dec 11 19:27:02 2013
@@ -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;
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Dec 11 19:27:02 2013
@@ -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<
Modified: cfe/trunk/lib/AST/Expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Expr.cpp?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Expr.cpp (original)
+++ cfe/trunk/lib/AST/Expr.cpp Wed Dec 11 19:27:02 2013
@@ -105,37 +105,6 @@ const Expr *Expr::skipRValueSubobjectAdj
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
Modified: cfe/trunk/lib/Sema/JumpDiagnostics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/JumpDiagnostics.cpp?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/JumpDiagnostics.cpp (original)
+++ cfe/trunk/lib/Sema/JumpDiagnostics.cpp Wed Dec 11 19:27:02 2013
@@ -121,9 +121,11 @@ typedef std::pair<unsigned,unsigned> Sco
/// 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
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
// 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);
-
- 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;
+ InDiag = diag::note_protected_by_variable_init;
- 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);
- }
-
- if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
- if (TD->getUnderlyingType()->isVariablyModifiedType())
- return ScopePair(diag::note_protected_by_vla_typedef, 0);
+ return ScopePair(InDiag, OutDiag);
}
- if (const TypeAliasDecl *TD = dyn_cast<TypeAliasDecl>(D)) {
+ if (const TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(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
/// \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::BuildScopeInforma
}
}
}
-
+
+ // 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);
}
Modified: cfe/trunk/test/SemaCXX/scope-check.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/scope-check.cpp?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/scope-check.cpp (original)
+++ cfe/trunk/test/SemaCXX/scope-check.cpp Wed Dec 11 19:27:02 2013
@@ -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}}
More information about the cfe-commits
mailing list