summaryrefslogtreecommitdiffstats
path: root/lib/Sema/JumpDiagnostics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Sema/JumpDiagnostics.cpp')
-rw-r--r--lib/Sema/JumpDiagnostics.cpp224
1 files changed, 160 insertions, 64 deletions
diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp
index ae154aa..59bc263 100644
--- a/lib/Sema/JumpDiagnostics.cpp
+++ b/lib/Sema/JumpDiagnostics.cpp
@@ -68,7 +68,10 @@ public:
JumpScopeChecker(Stmt *Body, Sema &S);
private:
void BuildScopeInformation(Decl *D, unsigned &ParentScope);
- void BuildScopeInformation(Stmt *S, unsigned ParentScope);
+ void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl,
+ unsigned &ParentScope);
+ void BuildScopeInformation(Stmt *S, unsigned &origParentScope);
+
void VerifyJumps();
void VerifyIndirectJumps();
void DiagnoseIndirectJump(IndirectGotoStmt *IG, unsigned IGScope,
@@ -87,7 +90,8 @@ JumpScopeChecker::JumpScopeChecker(Stmt *Body, Sema &s) : S(s) {
// Build information for the top level compound statement, so that we have a
// defined scope record for every "goto" and label.
- BuildScopeInformation(Body, 0);
+ unsigned BodyParentScope = 0;
+ BuildScopeInformation(Body, BodyParentScope);
// Check that all jumps we saw are kosher.
VerifyJumps();
@@ -111,87 +115,110 @@ unsigned JumpScopeChecker::GetDeepestCommonScope(unsigned A, unsigned B) {
return A;
}
+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 std::pair<unsigned,unsigned>
- GetDiagForGotoScopeDecl(const Decl *D, bool isCPlusPlus) {
+static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
unsigned InDiag = 0, OutDiag = 0;
if (VD->getType()->isVariablyModifiedType())
InDiag = diag::note_protected_by_vla;
- if (VD->hasAttr<BlocksAttr>()) {
- InDiag = diag::note_protected_by___block;
- OutDiag = diag::note_exits___block;
- } else if (VD->hasAttr<CleanupAttr>()) {
- InDiag = diag::note_protected_by_cleanup;
- OutDiag = diag::note_exits_cleanup;
- } else if (isCPlusPlus) {
- if (!VD->hasLocalStorage())
- return std::make_pair(InDiag, OutDiag);
-
- ASTContext &Context = D->getASTContext();
- QualType T = Context.getBaseElementType(VD->getType());
- if (!T->isDependentType()) {
- // C++0x [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
- // is ill-formed unless the variable has scalar type, class type with
- // a trivial default constructor and a trivial destructor, a
- // cv-qualified version of one of these types, or an array of one of
- // the preceding types and is declared without an initializer (8.5).
- // Check whether this is a C++ class.
- CXXRecordDecl *Record = T->getAsCXXRecordDecl();
-
- if (const Expr *Init = VD->getInit()) {
- bool CallsTrivialConstructor = false;
- if (Record) {
- // FIXME: With generalized initializer lists, this may
- // classify "X x{};" as having no initializer.
- if (const CXXConstructExpr *Construct
- = dyn_cast<CXXConstructExpr>(Init))
- if (const CXXConstructorDecl *Constructor
- = Construct->getConstructor())
- if ((Context.getLangOptions().CPlusPlus0x
- ? Record->hasTrivialDefaultConstructor()
- : Record->isPOD()) &&
- Constructor->isDefaultConstructor())
- CallsTrivialConstructor = true;
+ if (VD->hasAttr<BlocksAttr>())
+ return ScopePair(diag::note_protected_by___block,
+ diag::note_exits___block);
+
+ if (VD->hasAttr<CleanupAttr>())
+ return ScopePair(diag::note_protected_by_cleanup,
+ diag::note_exits_cleanup);
+
+ if (Context.getLangOptions().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:
+ return ScopePair(diag::note_protected_by_objc_ownership,
+ diag::note_exits_objc_ownership);
+ }
+ }
+
+ if (Context.getLangOptions().CPlusPlus && VD->hasLocalStorage()) {
+ // C++0x [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
+ // is ill-formed unless the variable has scalar type, class type with
+ // a trivial default constructor and a trivial destructor, a
+ // cv-qualified version of one of these types, or an array of one of
+ // the preceding types and is declared without an initializer.
+
+ // C++03 [stmt.dcl.p3:
+ // A program that jumps from a point where a local variable
+ // with automatic storage duration is not in scope to a point
+ // where it is in scope is ill-formed unless the variable has
+ // POD type and is declared without an initializer.
+
+ if (const Expr *init = VD->getInit()) {
+ // We actually give variables of record type (or array thereof)
+ // an initializer even if that initializer only calls a trivial
+ // ctor. Detect that case.
+ // FIXME: With generalized initializer lists, this may
+ // classify "X x{};" as having no initializer.
+ unsigned inDiagToUse = diag::note_protected_by_variable_init;
+
+ const CXXRecordDecl *record = 0;
+
+ if (const CXXConstructExpr *cce = dyn_cast<CXXConstructExpr>(init)) {
+ const CXXConstructorDecl *ctor = cce->getConstructor();
+ record = ctor->getParent();
+
+ if (ctor->isTrivial() && ctor->isDefaultConstructor()) {
+ if (Context.getLangOptions().CPlusPlus0x) {
+ inDiagToUse = (record->hasTrivialDestructor() ? 0 :
+ diag::note_protected_by_variable_nontriv_destructor);
+ } else {
+ if (record->isPOD())
+ inDiagToUse = 0;
+ }
}
-
- if (!CallsTrivialConstructor)
- InDiag = diag::note_protected_by_variable_init;
+ } else if (VD->getType()->isArrayType()) {
+ record = VD->getType()->getBaseElementTypeUnsafe()
+ ->getAsCXXRecordDecl();
}
-
- // Note whether we have a class with a non-trivial destructor.
- if (Record && !Record->hasTrivialDestructor())
+
+ if (inDiagToUse)
+ InDiag = inDiagToUse;
+
+ // Also object to indirect jumps which leave scopes with dtors.
+ if (record && !record->hasTrivialDestructor())
OutDiag = diag::note_exits_dtor;
}
}
- return std::make_pair(InDiag, OutDiag);
+ return ScopePair(InDiag, OutDiag);
}
if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
if (TD->getUnderlyingType()->isVariablyModifiedType())
- return std::make_pair((unsigned) diag::note_protected_by_vla_typedef, 0);
+ return ScopePair(diag::note_protected_by_vla_typedef, 0);
}
if (const TypeAliasDecl *TD = dyn_cast<TypeAliasDecl>(D)) {
if (TD->getUnderlyingType()->isVariablyModifiedType())
- return std::make_pair((unsigned) diag::note_protected_by_vla_type_alias, 0);
+ return ScopePair(diag::note_protected_by_vla_type_alias, 0);
}
- return std::make_pair(0U, 0U);
+ return ScopePair(0U, 0U);
}
/// \brief Build scope information for a declaration that is part of a DeclStmt.
void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) {
- bool isCPlusPlus = this->S.getLangOptions().CPlusPlus;
-
// If this decl causes a new scope, push and switch to it.
- std::pair<unsigned,unsigned> Diags
- = GetDiagForGotoScopeDecl(D, isCPlusPlus);
+ std::pair<unsigned,unsigned> Diags = GetDiagForGotoScopeDecl(S.Context, D);
if (Diags.first || Diags.second) {
Scopes.push_back(GotoScope(ParentScope, Diags.first, Diags.second,
D->getLocation()));
@@ -205,11 +232,55 @@ void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) {
BuildScopeInformation(Init, ParentScope);
}
+/// \brief Build scope information for a captured block literal variables.
+void JumpScopeChecker::BuildScopeInformation(VarDecl *D,
+ const BlockDecl *BDecl,
+ unsigned &ParentScope) {
+ // exclude captured __block variables; there's no destructor
+ // associated with the block literal for them.
+ if (D->hasAttr<BlocksAttr>())
+ return;
+ QualType T = D->getType();
+ QualType::DestructionKind destructKind = T.isDestructedType();
+ if (destructKind != QualType::DK_none) {
+ std::pair<unsigned,unsigned> Diags;
+ switch (destructKind) {
+ case QualType::DK_cxx_destructor:
+ Diags = ScopePair(diag::note_enters_block_captures_cxx_obj,
+ diag::note_exits_block_captures_cxx_obj);
+ break;
+ case QualType::DK_objc_strong_lifetime:
+ Diags = ScopePair(diag::note_enters_block_captures_strong,
+ diag::note_exits_block_captures_strong);
+ break;
+ case QualType::DK_objc_weak_lifetime:
+ Diags = ScopePair(diag::note_enters_block_captures_weak,
+ diag::note_exits_block_captures_weak);
+ break;
+ case QualType::DK_none:
+ llvm_unreachable("no-liftime captured variable");
+ }
+ SourceLocation Loc = D->getLocation();
+ if (Loc.isInvalid())
+ Loc = BDecl->getLocation();
+ Scopes.push_back(GotoScope(ParentScope,
+ Diags.first, Diags.second, Loc));
+ ParentScope = Scopes.size()-1;
+ }
+}
+
/// BuildScopeInformation - The statements from CI to CE are known to form a
/// coherent VLA scope with a specified parent node. Walk through the
/// statements, adding any labels or gotos to LabelAndGotoScopes and recursively
/// walking the AST as needed.
-void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
+void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope) {
+ // If this is a statement, rather than an expression, scopes within it don't
+ // propagate out into the enclosing scope. Otherwise we have to worry
+ // about block literals, which have the lifetime of their enclosing statement.
+ unsigned independentParentScope = origParentScope;
+ unsigned &ParentScope = ((isa<Expr>(S) && !isa<StmtExpr>(S))
+ ? origParentScope : independentParentScope);
+
bool SkipFirstSubStmt = false;
// If we found a label, remember that it is in ParentScope scope.
@@ -291,17 +362,17 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
BuildScopeInformation(*I, ParentScope);
continue;
}
-
// Disallow jumps into any part of an @try statement by pushing a scope and
// walking all sub-stmts in that scope.
if (ObjCAtTryStmt *AT = dyn_cast<ObjCAtTryStmt>(SubStmt)) {
+ unsigned newParentScope;
// Recursively walk the AST for the @try part.
Scopes.push_back(GotoScope(ParentScope,
diag::note_protected_by_objc_try,
diag::note_exits_objc_try,
AT->getAtTryLoc()));
if (Stmt *TryPart = AT->getTryBody())
- BuildScopeInformation(TryPart, Scopes.size()-1);
+ BuildScopeInformation(TryPart, (newParentScope = Scopes.size()-1));
// Jump from the catch to the finally or try is not valid.
for (unsigned I = 0, N = AT->getNumCatchStmts(); I != N; ++I) {
@@ -311,7 +382,8 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_exits_objc_catch,
AC->getAtCatchLoc()));
// @catches are nested and it isn't
- BuildScopeInformation(AC->getCatchBody(), Scopes.size()-1);
+ BuildScopeInformation(AC->getCatchBody(),
+ (newParentScope = Scopes.size()-1));
}
// Jump from the finally to the try or catch is not valid.
@@ -320,12 +392,13 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_protected_by_objc_finally,
diag::note_exits_objc_finally,
AF->getAtFinallyLoc()));
- BuildScopeInformation(AF, Scopes.size()-1);
+ BuildScopeInformation(AF, (newParentScope = Scopes.size()-1));
}
continue;
}
-
+
+ unsigned newParentScope;
// Disallow jumps into the protected statement of an @synchronized, but
// allow jumps into the object expression it protects.
if (ObjCAtSynchronizedStmt *AS = dyn_cast<ObjCAtSynchronizedStmt>(SubStmt)){
@@ -339,7 +412,8 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_protected_by_objc_synchronized,
diag::note_exits_objc_synchronized,
AS->getAtSynchronizedLoc()));
- BuildScopeInformation(AS->getSynchBody(), Scopes.size()-1);
+ BuildScopeInformation(AS->getSynchBody(),
+ (newParentScope = Scopes.size()-1));
continue;
}
@@ -351,7 +425,7 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_exits_cxx_try,
TS->getSourceRange().getBegin()));
if (Stmt *TryBlock = TS->getTryBlock())
- BuildScopeInformation(TryBlock, Scopes.size()-1);
+ BuildScopeInformation(TryBlock, (newParentScope = Scopes.size()-1));
// Jump from the catch into the try is not allowed either.
for (unsigned I = 0, E = TS->getNumHandlers(); I != E; ++I) {
@@ -360,12 +434,34 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) {
diag::note_protected_by_cxx_catch,
diag::note_exits_cxx_catch,
CS->getSourceRange().getBegin()));
- BuildScopeInformation(CS->getHandlerBlock(), Scopes.size()-1);
+ BuildScopeInformation(CS->getHandlerBlock(),
+ (newParentScope = Scopes.size()-1));
}
continue;
}
+ // Disallow jumps into the protected statement of an @autoreleasepool.
+ if (ObjCAutoreleasePoolStmt *AS = dyn_cast<ObjCAutoreleasePoolStmt>(SubStmt)){
+ // Recursively walk the AST for the @autoreleasepool part, protected by a new
+ // scope.
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_objc_autoreleasepool,
+ diag::note_exits_objc_autoreleasepool,
+ AS->getAtLoc()));
+ BuildScopeInformation(AS->getSubStmt(), (newParentScope = Scopes.size()-1));
+ continue;
+ }
+
+ if (const BlockExpr *BE = dyn_cast<BlockExpr>(SubStmt)) {
+ const BlockDecl *BDecl = BE->getBlockDecl();
+ for (BlockDecl::capture_const_iterator ci = BDecl->capture_begin(),
+ ce = BDecl->capture_end(); ci != ce; ++ci) {
+ VarDecl *variable = ci->getVariable();
+ BuildScopeInformation(variable, BDecl, ParentScope);
+ }
+ }
+
// Recursively walk the AST.
BuildScopeInformation(SubStmt, ParentScope);
}
OpenPOWER on IntegriCloud