diff options
Diffstat (limited to 'lib/Sema/JumpDiagnostics.cpp')
-rw-r--r-- | lib/Sema/JumpDiagnostics.cpp | 224 |
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); } |