diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Analysis')
22 files changed, 3102 insertions, 1285 deletions
diff --git a/contrib/llvm/tools/clang/lib/Analysis/AnalysisDeclContext.cpp b/contrib/llvm/tools/clang/lib/Analysis/AnalysisDeclContext.cpp index 465f0c3..90d4b13 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/AnalysisDeclContext.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/AnalysisDeclContext.cpp @@ -28,8 +28,8 @@ #include "clang/Analysis/Support/BumpVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/raw_ostream.h" using namespace clang; @@ -41,11 +41,11 @@ AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr, : Manager(Mgr), D(d), cfgBuildOptions(buildOptions), - forcedBlkExprs(0), + forcedBlkExprs(nullptr), builtCFG(false), builtCompleteCFG(false), - ReferencedBlockVars(0), - ManagedAnalyses(0) + ReferencedBlockVars(nullptr), + ManagedAnalyses(nullptr) { cfgBuildOptions.forcedBlkExprs = &forcedBlkExprs; } @@ -54,11 +54,11 @@ AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr, const Decl *d) : Manager(Mgr), D(d), - forcedBlkExprs(0), + forcedBlkExprs(nullptr), builtCFG(false), builtCompleteCFG(false), - ReferencedBlockVars(0), - ManagedAnalyses(0) + ReferencedBlockVars(nullptr), + ManagedAnalyses(nullptr) { cfgBuildOptions.forcedBlkExprs = &forcedBlkExprs; } @@ -68,7 +68,8 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(bool useUnoptimizedCFG, bool addInitializers, bool addTemporaryDtors, bool synthesizeBodies, - bool addStaticInitBranch) + bool addStaticInitBranch, + bool addCXXNewAllocator) : SynthesizeBodies(synthesizeBodies) { cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG; @@ -76,12 +77,11 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(bool useUnoptimizedCFG, cfgBuildOptions.AddInitializers = addInitializers; cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors; cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; + cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator; } void AnalysisDeclContextManager::clear() { - for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I) - delete I->second; - Contexts.clear(); + llvm::DeleteContainerSeconds(Contexts); } static BodyFarm &getBodyFarm(ASTContext &C) { @@ -94,14 +94,21 @@ Stmt *AnalysisDeclContext::getBody(bool &IsAutosynthesized) const { if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { Stmt *Body = FD->getBody(); if (!Body && Manager && Manager->synthesizeBodies()) { - IsAutosynthesized = true; - return getBodyFarm(getASTContext()).getBody(FD); + Body = getBodyFarm(getASTContext()).getBody(FD); + if (Body) + IsAutosynthesized = true; } return Body; } - else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) - return MD->getBody(); - else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) + else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + Stmt *Body = MD->getBody(); + if (!Body && Manager && Manager->synthesizeBodies()) { + Body = getBodyFarm(getASTContext()).getBody(MD); + if (Body) + IsAutosynthesized = true; + } + return Body; + } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) return BD->getBody(); else if (const FunctionTemplateDecl *FunTmpl = dyn_cast_or_null<FunctionTemplateDecl>(D)) @@ -126,15 +133,14 @@ const ImplicitParamDecl *AnalysisDeclContext::getSelfDecl() const { return MD->getSelfDecl(); if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { // See if 'self' was captured by the block. - for (BlockDecl::capture_const_iterator it = BD->capture_begin(), - et = BD->capture_end(); it != et; ++it) { - const VarDecl *VD = it->getVariable(); + for (const auto &I : BD->captures()) { + const VarDecl *VD = I.getVariable(); if (VD->getName() == "self") return dyn_cast<ImplicitParamDecl>(VD); } } - return NULL; + return nullptr; } void AnalysisDeclContext::registerForcedBlockExpression(const Stmt *stmt) { @@ -183,6 +189,9 @@ CFG *AnalysisDeclContext::getCFG() { if (PM) addParentsForSyntheticStmts(cfg.get(), *PM); + + // The Observer should only observe one build of the CFG. + getCFGBuildOptions().Observer = nullptr; } return cfg.get(); } @@ -199,6 +208,9 @@ CFG *AnalysisDeclContext::getUnoptimizedCFG() { if (PM) addParentsForSyntheticStmts(completeCFG.get(), *PM); + + // The Observer should only observe one build of the CFG. + getCFGBuildOptions().Observer = nullptr; } return completeCFG.get(); } @@ -211,8 +223,8 @@ CFGStmtMap *AnalysisDeclContext::getCFGStmtMap() { cfgStmtMap.reset(CFGStmtMap::Build(c, &getParentMap())); return cfgStmtMap.get(); } - - return 0; + + return nullptr; } CFGReverseBlockReachabilityAnalysis *AnalysisDeclContext::getCFGReachablityAnalysis() { @@ -223,8 +235,8 @@ CFGReverseBlockReachabilityAnalysis *AnalysisDeclContext::getCFGReachablityAnaly CFA.reset(new CFGReverseBlockReachabilityAnalysis(*c)); return CFA.get(); } - - return 0; + + return nullptr; } void AnalysisDeclContext::dumpCFG(bool ShowColors) { @@ -235,10 +247,8 @@ ParentMap &AnalysisDeclContext::getParentMap() { if (!PM) { PM.reset(new ParentMap(getBody())); if (const CXXConstructorDecl *C = dyn_cast<CXXConstructorDecl>(getDecl())) { - for (CXXConstructorDecl::init_const_iterator I = C->init_begin(), - E = C->init_end(); - I != E; ++I) { - PM->addStmt((*I)->getInit()); + for (const auto *I : C->inits()) { + PM->addStmt(I->getInit()); } } if (builtCFG) @@ -391,7 +401,7 @@ const StackFrameContext *LocationContext::getCurrentStackFrame() const { return SFC; LC = LC->getParent(); } - return NULL; + return nullptr; } bool LocationContext::inTopFrame() const { @@ -435,7 +445,7 @@ void LocationContext::dumpStack(raw_ostream &OS, StringRef Indent) const { } } -void LocationContext::dumpStack() const { +LLVM_DUMP_METHOD void LocationContext::dumpStack() const { dumpStack(llvm::errs()); } @@ -454,11 +464,6 @@ public: BumpVectorContext &bc) : BEVals(bevals), BC(bc) {} - bool IsTrackedDecl(const VarDecl *VD) { - const DeclContext *DC = VD->getDeclContext(); - return IgnoredContexts.count(DC) == 0; - } - void VisitStmt(Stmt *S) { for (Stmt::child_range I = S->children(); I; ++I) if (Stmt *child = *I) @@ -506,9 +511,8 @@ static DeclVec* LazyInitializeReferencedDecls(const BlockDecl *BD, new (BV) DeclVec(BC, 10); // Go through the capture list. - for (BlockDecl::capture_const_iterator CI = BD->capture_begin(), - CE = BD->capture_end(); CI != CE; ++CI) { - BV->push_back(CI->getVariable(), BC); + for (const auto &CI : BD->captures()) { + BV->push_back(CI.getVariable(), BC); } // Find the referenced global/static variables. @@ -548,15 +552,13 @@ AnalysisDeclContext::~AnalysisDeclContext() { // Release the managed analyses. if (ManagedAnalyses) { ManagedAnalysisMap *M = (ManagedAnalysisMap*) ManagedAnalyses; - for (ManagedAnalysisMap::iterator I = M->begin(), E = M->end(); I!=E; ++I) - delete I->second; + llvm::DeleteContainerSeconds(*M); delete M; } } AnalysisDeclContextManager::~AnalysisDeclContextManager() { - for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I) - delete I->second; + llvm::DeleteContainerSeconds(Contexts); } LocationContext::~LocationContext() {} diff --git a/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.cpp b/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.cpp index 4d5c2ee..316a18b 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.cpp @@ -35,8 +35,7 @@ static bool isDispatchBlock(QualType Ty) { // returns void. const FunctionProtoType *FT = BPT->getPointeeType()->getAs<FunctionProtoType>(); - if (!FT || !FT->getResultType()->isVoidType() || - FT->getNumArgs() != 0) + if (!FT || !FT->getReturnType()->isVoidType() || FT->getNumParams() != 0) return false; return true; @@ -74,6 +73,9 @@ public: /// Create an Objective-C bool literal. ObjCBoolLiteralExpr *makeObjCBool(bool Val); + + /// Create an Objective-C ivar reference. + ObjCIvarRefExpr *makeObjCIvarRef(const Expr *Base, const ObjCIvarDecl *IVar); /// Create a Return statement. ReturnStmt *makeReturn(const Expr *RetVal); @@ -126,20 +128,20 @@ UnaryOperator *ASTMaker::makeDereference(const Expr *Arg, QualType Ty) { ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) { return ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue, - const_cast<Expr*>(Arg), 0, VK_RValue); + const_cast<Expr*>(Arg), nullptr, VK_RValue); } Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) { if (Arg->getType() == Ty) return const_cast<Expr*>(Arg); - + return ImplicitCastExpr::Create(C, Ty, CK_IntegralCast, - const_cast<Expr*>(Arg), 0, VK_RValue); + const_cast<Expr*>(Arg), nullptr, VK_RValue); } ImplicitCastExpr *ASTMaker::makeIntegralCastToBoolean(const Expr *Arg) { return ImplicitCastExpr::Create(C, C.BoolTy, CK_IntegralToBoolean, - const_cast<Expr*>(Arg), 0, VK_RValue); + const_cast<Expr*>(Arg), nullptr, VK_RValue); } ObjCBoolLiteralExpr *ASTMaker::makeObjCBool(bool Val) { @@ -147,8 +149,18 @@ ObjCBoolLiteralExpr *ASTMaker::makeObjCBool(bool Val) { return new (C) ObjCBoolLiteralExpr(Val, Ty, SourceLocation()); } +ObjCIvarRefExpr *ASTMaker::makeObjCIvarRef(const Expr *Base, + const ObjCIvarDecl *IVar) { + return new (C) ObjCIvarRefExpr(const_cast<ObjCIvarDecl*>(IVar), + IVar->getType(), SourceLocation(), + SourceLocation(), const_cast<Expr*>(Base), + /*arrow=*/true, /*free=*/false); +} + + ReturnStmt *ASTMaker::makeReturn(const Expr *RetVal) { - return new (C) ReturnStmt(SourceLocation(), const_cast<Expr*>(RetVal), 0); + return new (C) ReturnStmt(SourceLocation(), const_cast<Expr*>(RetVal), + nullptr); } //===----------------------------------------------------------------------===// @@ -161,24 +173,24 @@ typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D); static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { // Check if we have at least two parameters. if (D->param_size() != 2) - return 0; + return nullptr; // Check if the first parameter is a pointer to integer type. const ParmVarDecl *Predicate = D->getParamDecl(0); QualType PredicateQPtrTy = Predicate->getType(); const PointerType *PredicatePtrTy = PredicateQPtrTy->getAs<PointerType>(); if (!PredicatePtrTy) - return 0; + return nullptr; QualType PredicateTy = PredicatePtrTy->getPointeeType(); if (!PredicateTy->isIntegerType()) - return 0; - + return nullptr; + // Check if the second parameter is the proper block type. const ParmVarDecl *Block = D->getParamDecl(1); QualType Ty = Block->getType(); if (!isDispatchBlock(Ty)) - return 0; - + return nullptr; + // Everything checks out. Create a fakse body that checks the predicate, // sets it, and calls the block. Basically, an AST dump of: // @@ -231,7 +243,7 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { SourceLocation()); // (5) Create the 'if' statement. - IfStmt *If = new (C) IfStmt(C, SourceLocation(), 0, UO, CS); + IfStmt *If = new (C) IfStmt(C, SourceLocation(), nullptr, UO, CS); return If; } @@ -239,14 +251,14 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) { // Check if we have at least two parameters. if (D->param_size() != 2) - return 0; - + return nullptr; + // Check if the second parameter is a block. const ParmVarDecl *PV = D->getParamDecl(1); QualType Ty = PV->getType(); if (!isDispatchBlock(Ty)) - return 0; - + return nullptr; + // Everything checks out. Create a fake body that just calls the block. // This is basically just an AST dump of: // @@ -266,8 +278,8 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D) { // There are exactly 3 arguments. if (D->param_size() != 3) - return 0; - + return nullptr; + // Signature: // _Bool OSAtomicCompareAndSwapPtr(void *__oldValue, // void *__newValue, @@ -278,12 +290,12 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D) // return YES; // } // else return NO; - - QualType ResultTy = D->getResultType(); + + QualType ResultTy = D->getReturnType(); bool isBoolean = ResultTy->isBooleanType(); if (!isBoolean && !ResultTy->isIntegralType(C)) - return 0; - + return nullptr; + const ParmVarDecl *OldValue = D->getParamDecl(0); QualType OldValueTy = OldValue->getType(); @@ -296,7 +308,7 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D) QualType TheValueTy = TheValue->getType(); const PointerType *PT = TheValueTy->getAs<PointerType>(); if (!PT) - return 0; + return nullptr; QualType PointeeTy = PT->getPointeeType(); ASTMaker M(C); @@ -335,9 +347,9 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D) /// Construct the If. Stmt *If = - new (C) IfStmt(C, SourceLocation(), 0, Comparison, Body, + new (C) IfStmt(C, SourceLocation(), nullptr, Comparison, Body, SourceLocation(), Else); - + return If; } @@ -347,15 +359,15 @@ Stmt *BodyFarm::getBody(const FunctionDecl *D) { Optional<Stmt *> &Val = Bodies[D]; if (Val.hasValue()) return Val.getValue(); - - Val = 0; - - if (D->getIdentifier() == 0) - return 0; + + Val = nullptr; + + if (D->getIdentifier() == nullptr) + return nullptr; StringRef Name = D->getName(); if (Name.empty()) - return 0; + return nullptr; FunctionFarmer FF; @@ -367,10 +379,94 @@ Stmt *BodyFarm::getBody(const FunctionDecl *D) { FF = llvm::StringSwitch<FunctionFarmer>(Name) .Case("dispatch_sync", create_dispatch_sync) .Case("dispatch_once", create_dispatch_once) - .Default(NULL); + .Default(nullptr); } if (FF) { Val = FF(C, D); } return Val.getValue(); } +static Stmt *createObjCPropertyGetter(ASTContext &Ctx, + const ObjCPropertyDecl *Prop) { + // First, find the backing ivar. + const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl(); + if (!IVar) + return nullptr; + + // Ignore weak variables, which have special behavior. + if (Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_weak) + return nullptr; + + // Look to see if Sema has synthesized a body for us. This happens in + // Objective-C++ because the return value may be a C++ class type with a + // non-trivial copy constructor. We can only do this if we can find the + // @synthesize for this property, though (or if we know it's been auto- + // synthesized). + const ObjCImplementationDecl *ImplDecl = + IVar->getContainingInterface()->getImplementation(); + if (ImplDecl) { + for (const auto *I : ImplDecl->property_impls()) { + if (I->getPropertyDecl() != Prop) + continue; + + if (I->getGetterCXXConstructor()) { + ASTMaker M(Ctx); + return M.makeReturn(I->getGetterCXXConstructor()); + } + } + } + + // Sanity check that the property is the same type as the ivar, or a + // reference to it, and that it is either an object pointer or trivially + // copyable. + if (!Ctx.hasSameUnqualifiedType(IVar->getType(), + Prop->getType().getNonReferenceType())) + return nullptr; + if (!IVar->getType()->isObjCLifetimeType() && + !IVar->getType().isTriviallyCopyableType(Ctx)) + return nullptr; + + // Generate our body: + // return self->_ivar; + ASTMaker M(Ctx); + + const VarDecl *selfVar = Prop->getGetterMethodDecl()->getSelfDecl(); + + Expr *loadedIVar = + M.makeObjCIvarRef( + M.makeLvalueToRvalue( + M.makeDeclRefExpr(selfVar), + selfVar->getType()), + IVar); + + if (!Prop->getType()->isReferenceType()) + loadedIVar = M.makeLvalueToRvalue(loadedIVar, IVar->getType()); + + return M.makeReturn(loadedIVar); +} + +Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) { + // We currently only know how to synthesize property accessors. + if (!D->isPropertyAccessor()) + return nullptr; + + D = D->getCanonicalDecl(); + + Optional<Stmt *> &Val = Bodies[D]; + if (Val.hasValue()) + return Val.getValue(); + Val = nullptr; + + const ObjCPropertyDecl *Prop = D->findPropertyDecl(); + if (!Prop) + return nullptr; + + // For now, we only synthesize getters. + if (D->param_size() != 0) + return nullptr; + + Val = createObjCPropertyGetter(C, Prop); + + return Val.getValue(); +} + diff --git a/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.h b/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.h index 96f61df..2d200fb 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.h +++ b/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.h @@ -24,6 +24,8 @@ namespace clang { class ASTContext; class Decl; class FunctionDecl; +class ObjCMethodDecl; +class ObjCPropertyDecl; class Stmt; class BodyFarm { @@ -32,7 +34,10 @@ public: /// Factory method for creating bodies for ordinary functions. Stmt *getBody(const FunctionDecl *D); - + + /// Factory method for creating bodies for Objective-C properties. + Stmt *getBody(const ObjCMethodDecl *D); + private: typedef llvm::DenseMap<const Decl *, Optional<Stmt *> > BodyMap; diff --git a/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp b/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp index 8b8c573..842a385 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp @@ -21,7 +21,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/OwningPtr.h" +#include <memory> #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Format.h" @@ -112,7 +112,7 @@ public: /// Incrementing invalid iterator is allowed and will result in invalid /// iterator. const_iterator() - : Scope(NULL), VarIter(0) {} + : Scope(nullptr), VarIter(0) {} /// Create valid iterator. In case when S.Prev is an invalid iterator and /// I is equal to 0, this will create invalid iterator. @@ -207,7 +207,7 @@ int LocalScope::const_iterator::distance(LocalScope::const_iterator L) { /// build process. It consists of CFGBlock that specifies position in CFG graph /// and LocalScope::const_iterator that specifies position in LocalScope graph. struct BlockScopePosPair { - BlockScopePosPair() : block(0) {} + BlockScopePosPair() : block(nullptr) {} BlockScopePosPair(CFGBlock *b, LocalScope::const_iterator scopePos) : block(b), scopePosition(scopePos) {} @@ -291,7 +291,7 @@ class CFGBuilder { typedef BlockScopePosPair JumpSource; ASTContext *Context; - OwningPtr<CFG> cfg; + std::unique_ptr<CFG> cfg; CFGBlock *Block; CFGBlock *Succ; @@ -336,11 +336,11 @@ public: explicit CFGBuilder(ASTContext *astContext, const CFG::BuildOptions &buildOpts) : Context(astContext), cfg(new CFG()), // crew a new CFG - Block(NULL), Succ(NULL), - SwitchTerminatedBlock(NULL), DefaultCaseBlock(NULL), - TryTerminatedBlock(NULL), badCFG(false), BuildOpts(buildOpts), - switchExclusivelyCovered(false), switchCond(0), - cachedEntry(0), lastLookup(0) {} + Block(nullptr), Succ(nullptr), + SwitchTerminatedBlock(nullptr), DefaultCaseBlock(nullptr), + TryTerminatedBlock(nullptr), badCFG(false), BuildOpts(buildOpts), + switchExclusivelyCovered(false), switchCond(nullptr), + cachedEntry(nullptr), lastLookup(nullptr) {} // buildCFG - Used by external clients to construct the CFG. CFG* buildCFG(const Decl *D, Stmt *Statement); @@ -363,6 +363,7 @@ private: AddStmtChoice asc); CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S); CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc); + CFGBlock *VisitCXXNewExpr(CXXNewExpr *DE, AddStmtChoice asc); CFGBlock *VisitCXXDeleteExpr(CXXDeleteExpr *DE, AddStmtChoice asc); CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S); CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E, @@ -442,8 +443,9 @@ private: LocalScope* createOrReuseLocalScope(LocalScope* Scope); void addLocalScopeForStmt(Stmt *S); - LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS, LocalScope* Scope = NULL); - LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = NULL); + LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS, + LocalScope* Scope = nullptr); + LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = nullptr); void addLocalScopeAndDtors(Stmt *S); @@ -459,6 +461,9 @@ private: void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) { B->appendInitializer(I, cfg->getBumpVectorContext()); } + void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) { + B->appendNewAllocator(NE, cfg->getBumpVectorContext()); + } void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) { B->appendBaseDtor(BS, cfg->getBumpVectorContext()); } @@ -479,8 +484,252 @@ private: void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk, LocalScope::const_iterator B, LocalScope::const_iterator E); - void addSuccessor(CFGBlock *B, CFGBlock *S) { - B->addSuccessor(S, cfg->getBumpVectorContext()); + void addSuccessor(CFGBlock *B, CFGBlock *S, bool IsReachable = true) { + B->addSuccessor(CFGBlock::AdjacentBlock(S, IsReachable), + cfg->getBumpVectorContext()); + } + + /// Add a reachable successor to a block, with the alternate variant that is + /// unreachable. + void addSuccessor(CFGBlock *B, CFGBlock *ReachableBlock, CFGBlock *AltBlock) { + B->addSuccessor(CFGBlock::AdjacentBlock(ReachableBlock, AltBlock), + cfg->getBumpVectorContext()); + } + + /// \brief Find a relational comparison with an expression evaluating to a + /// boolean and a constant other than 0 and 1. + /// e.g. if ((x < y) == 10) + TryResult checkIncorrectRelationalOperator(const BinaryOperator *B) { + const Expr *LHSExpr = B->getLHS()->IgnoreParens(); + const Expr *RHSExpr = B->getRHS()->IgnoreParens(); + + const IntegerLiteral *IntLiteral = dyn_cast<IntegerLiteral>(LHSExpr); + const Expr *BoolExpr = RHSExpr; + bool IntFirst = true; + if (!IntLiteral) { + IntLiteral = dyn_cast<IntegerLiteral>(RHSExpr); + BoolExpr = LHSExpr; + IntFirst = false; + } + + if (!IntLiteral || !BoolExpr->isKnownToHaveBooleanValue()) + return TryResult(); + + llvm::APInt IntValue = IntLiteral->getValue(); + if ((IntValue == 1) || (IntValue == 0)) + return TryResult(); + + bool IntLarger = IntLiteral->getType()->isUnsignedIntegerType() || + !IntValue.isNegative(); + + BinaryOperatorKind Bok = B->getOpcode(); + if (Bok == BO_GT || Bok == BO_GE) { + // Always true for 10 > bool and bool > -1 + // Always false for -1 > bool and bool > 10 + return TryResult(IntFirst == IntLarger); + } else { + // Always true for -1 < bool and bool < 10 + // Always false for 10 < bool and bool < -1 + return TryResult(IntFirst != IntLarger); + } + } + + /// Find an incorrect equality comparison. Either with an expression + /// evaluating to a boolean and a constant other than 0 and 1. + /// e.g. if (!x == 10) or a bitwise and/or operation that always evaluates to + /// true/false e.q. (x & 8) == 4. + TryResult checkIncorrectEqualityOperator(const BinaryOperator *B) { + const Expr *LHSExpr = B->getLHS()->IgnoreParens(); + const Expr *RHSExpr = B->getRHS()->IgnoreParens(); + + const IntegerLiteral *IntLiteral = dyn_cast<IntegerLiteral>(LHSExpr); + const Expr *BoolExpr = RHSExpr; + + if (!IntLiteral) { + IntLiteral = dyn_cast<IntegerLiteral>(RHSExpr); + BoolExpr = LHSExpr; + } + + if (!IntLiteral) + return TryResult(); + + const BinaryOperator *BitOp = dyn_cast<BinaryOperator>(BoolExpr); + if (BitOp && (BitOp->getOpcode() == BO_And || + BitOp->getOpcode() == BO_Or)) { + const Expr *LHSExpr2 = BitOp->getLHS()->IgnoreParens(); + const Expr *RHSExpr2 = BitOp->getRHS()->IgnoreParens(); + + const IntegerLiteral *IntLiteral2 = dyn_cast<IntegerLiteral>(LHSExpr2); + + if (!IntLiteral2) + IntLiteral2 = dyn_cast<IntegerLiteral>(RHSExpr2); + + if (!IntLiteral2) + return TryResult(); + + llvm::APInt L1 = IntLiteral->getValue(); + llvm::APInt L2 = IntLiteral2->getValue(); + if ((BitOp->getOpcode() == BO_And && (L2 & L1) != L1) || + (BitOp->getOpcode() == BO_Or && (L2 | L1) != L1)) { + if (BuildOpts.Observer) + BuildOpts.Observer->compareBitwiseEquality(B, + B->getOpcode() != BO_EQ); + TryResult(B->getOpcode() != BO_EQ); + } + } else if (BoolExpr->isKnownToHaveBooleanValue()) { + llvm::APInt IntValue = IntLiteral->getValue(); + if ((IntValue == 1) || (IntValue == 0)) { + return TryResult(); + } + return TryResult(B->getOpcode() != BO_EQ); + } + + return TryResult(); + } + + TryResult analyzeLogicOperatorCondition(BinaryOperatorKind Relation, + const llvm::APSInt &Value1, + const llvm::APSInt &Value2) { + assert(Value1.isSigned() == Value2.isSigned()); + switch (Relation) { + default: + return TryResult(); + case BO_EQ: + return TryResult(Value1 == Value2); + case BO_NE: + return TryResult(Value1 != Value2); + case BO_LT: + return TryResult(Value1 < Value2); + case BO_LE: + return TryResult(Value1 <= Value2); + case BO_GT: + return TryResult(Value1 > Value2); + case BO_GE: + return TryResult(Value1 >= Value2); + } + } + + /// \brief Find a pair of comparison expressions with or without parentheses + /// with a shared variable and constants and a logical operator between them + /// that always evaluates to either true or false. + /// e.g. if (x != 3 || x != 4) + TryResult checkIncorrectLogicOperator(const BinaryOperator *B) { + assert(B->isLogicalOp()); + const BinaryOperator *LHS = + dyn_cast<BinaryOperator>(B->getLHS()->IgnoreParens()); + const BinaryOperator *RHS = + dyn_cast<BinaryOperator>(B->getRHS()->IgnoreParens()); + if (!LHS || !RHS) + return TryResult(); + + if (!LHS->isComparisonOp() || !RHS->isComparisonOp()) + return TryResult(); + + BinaryOperatorKind BO1 = LHS->getOpcode(); + const DeclRefExpr *Decl1 = + dyn_cast<DeclRefExpr>(LHS->getLHS()->IgnoreParenImpCasts()); + const IntegerLiteral *Literal1 = + dyn_cast<IntegerLiteral>(LHS->getRHS()->IgnoreParens()); + if (!Decl1 && !Literal1) { + if (BO1 == BO_GT) + BO1 = BO_LT; + else if (BO1 == BO_GE) + BO1 = BO_LE; + else if (BO1 == BO_LT) + BO1 = BO_GT; + else if (BO1 == BO_LE) + BO1 = BO_GE; + Decl1 = dyn_cast<DeclRefExpr>(LHS->getRHS()->IgnoreParenImpCasts()); + Literal1 = dyn_cast<IntegerLiteral>(LHS->getLHS()->IgnoreParens()); + } + + if (!Decl1 || !Literal1) + return TryResult(); + + BinaryOperatorKind BO2 = RHS->getOpcode(); + const DeclRefExpr *Decl2 = + dyn_cast<DeclRefExpr>(RHS->getLHS()->IgnoreParenImpCasts()); + const IntegerLiteral *Literal2 = + dyn_cast<IntegerLiteral>(RHS->getRHS()->IgnoreParens()); + if (!Decl2 && !Literal2) { + if (BO2 == BO_GT) + BO2 = BO_LT; + else if (BO2 == BO_GE) + BO2 = BO_LE; + else if (BO2 == BO_LT) + BO2 = BO_GT; + else if (BO2 == BO_LE) + BO2 = BO_GE; + Decl2 = dyn_cast<DeclRefExpr>(RHS->getRHS()->IgnoreParenImpCasts()); + Literal2 = dyn_cast<IntegerLiteral>(RHS->getLHS()->IgnoreParens()); + } + + if (!Decl2 || !Literal2) + return TryResult(); + + // Check that it is the same variable on both sides. + if (Decl1->getDecl() != Decl2->getDecl()) + return TryResult(); + + llvm::APSInt L1, L2; + + if (!Literal1->EvaluateAsInt(L1, *Context) || + !Literal2->EvaluateAsInt(L2, *Context)) + return TryResult(); + + // Can't compare signed with unsigned or with different bit width. + if (L1.isSigned() != L2.isSigned() || L1.getBitWidth() != L2.getBitWidth()) + return TryResult(); + + // Values that will be used to determine if result of logical + // operator is always true/false + const llvm::APSInt Values[] = { + // Value less than both Value1 and Value2 + llvm::APSInt::getMinValue(L1.getBitWidth(), L1.isUnsigned()), + // L1 + L1, + // Value between Value1 and Value2 + ((L1 < L2) ? L1 : L2) + llvm::APSInt(llvm::APInt(L1.getBitWidth(), 1), + L1.isUnsigned()), + // L2 + L2, + // Value greater than both Value1 and Value2 + llvm::APSInt::getMaxValue(L1.getBitWidth(), L1.isUnsigned()), + }; + + // Check whether expression is always true/false by evaluating the following + // * variable x is less than the smallest literal. + // * variable x is equal to the smallest literal. + // * Variable x is between smallest and largest literal. + // * Variable x is equal to the largest literal. + // * Variable x is greater than largest literal. + bool AlwaysTrue = true, AlwaysFalse = true; + for (unsigned int ValueIndex = 0; + ValueIndex < sizeof(Values) / sizeof(Values[0]); + ++ValueIndex) { + llvm::APSInt Value = Values[ValueIndex]; + TryResult Res1, Res2; + Res1 = analyzeLogicOperatorCondition(BO1, Value, L1); + Res2 = analyzeLogicOperatorCondition(BO2, Value, L2); + + if (!Res1.isKnown() || !Res2.isKnown()) + return TryResult(); + + if (B->getOpcode() == BO_LAnd) { + AlwaysTrue &= (Res1.isTrue() && Res2.isTrue()); + AlwaysFalse &= !(Res1.isTrue() && Res2.isTrue()); + } else { + AlwaysTrue &= (Res1.isTrue() || Res2.isTrue()); + AlwaysFalse &= !(Res1.isTrue() || Res2.isTrue()); + } + } + + if (AlwaysTrue || AlwaysFalse) { + if (BuildOpts.Observer) + BuildOpts.Observer->compareAlwaysTrue(B, AlwaysTrue); + return TryResult(AlwaysTrue); + } + return TryResult(); } /// Try and evaluate an expression to an integer constant. @@ -565,10 +814,22 @@ private: // is determined by the RHS: X && 0 -> 0, X || 1 -> 1. if (RHS.isTrue() == (Bop->getOpcode() == BO_LOr)) return RHS.isTrue(); + } else { + TryResult BopRes = checkIncorrectLogicOperator(Bop); + if (BopRes.isKnown()) + return BopRes.isTrue(); } } return TryResult(); + } else if (Bop->isEqualityOp()) { + TryResult BopRes = checkIncorrectEqualityOperator(Bop); + if (BopRes.isKnown()) + return BopRes.isTrue(); + } else if (Bop->isRelationalOp()) { + TryResult BopRes = checkIncorrectRelationalOperator(Bop); + if (BopRes.isKnown()) + return BopRes.isTrue(); } } @@ -607,13 +868,13 @@ bool CFGBuilder::alwaysAdd(const Stmt *stmt) { if (!fb) { // No need to update 'cachedEntry', since it will always be null. - assert(cachedEntry == 0); + assert(!cachedEntry); return shouldAdd; } CFG::BuildOptions::ForcedBlkExprs::iterator itr = fb->find(stmt); if (itr == fb->end()) { - cachedEntry = 0; + cachedEntry = nullptr; return shouldAdd; } @@ -632,7 +893,7 @@ static const VariableArrayType *FindVA(const Type *t) { t = vt->getElementType().getTypePtr(); } - return 0; + return nullptr; } /// BuildCFG - Constructs a CFG from an AST (a Stmt*). The AST can represent an @@ -643,14 +904,14 @@ static const VariableArrayType *FindVA(const Type *t) { CFG* CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { assert(cfg.get()); if (!Statement) - return NULL; + return nullptr; // Create an empty block that will serve as the exit block for the CFG. Since // this is the first block added to the CFG, it will be implicitly registered // as the exit block. Succ = createBlock(); assert(Succ == &cfg->getExit()); - Block = NULL; // the EXIT block is empty. Create all other blocks lazily. + Block = nullptr; // the EXIT block is empty. Create all other blocks lazily. if (BuildOpts.AddImplicitDtors) if (const CXXDestructorDecl *DD = dyn_cast_or_null<CXXDestructorDecl>(D)) @@ -660,7 +921,7 @@ CFG* CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { CFGBlock *B = addStmt(Statement); if (badCFG) - return NULL; + return nullptr; // For C++ constructor add initializers to CFG. if (const CXXConstructorDecl *CD = dyn_cast_or_null<CXXConstructorDecl>(D)) { @@ -668,7 +929,7 @@ CFG* CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { E = CD->init_rend(); I != E; ++I) { B = addInitializer(*I); if (badCFG) - return NULL; + return nullptr; } } @@ -712,7 +973,7 @@ CFG* CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { // Create an empty entry block that has no predecessors. cfg->setEntry(createBlock()); - return cfg.take(); + return cfg.release(); } /// createBlock - Used to lazily create blocks that are connected @@ -730,7 +991,7 @@ CFGBlock *CFGBuilder::createBlock(bool add_successor) { CFGBlock *CFGBuilder::createNoReturnBlock() { CFGBlock *B = createBlock(false); B->setHasNoReturnElement(); - addSuccessor(B, &cfg->getExit()); + addSuccessor(B, &cfg->getExit(), Succ); return B; } @@ -868,30 +1129,27 @@ void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) { const CXXRecordDecl *RD = DD->getParent(); // At the end destroy virtual base objects. - for (CXXRecordDecl::base_class_const_iterator VI = RD->vbases_begin(), - VE = RD->vbases_end(); VI != VE; ++VI) { - const CXXRecordDecl *CD = VI->getType()->getAsCXXRecordDecl(); + for (const auto &VI : RD->vbases()) { + const CXXRecordDecl *CD = VI.getType()->getAsCXXRecordDecl(); if (!CD->hasTrivialDestructor()) { autoCreateBlock(); - appendBaseDtor(Block, VI); + appendBaseDtor(Block, &VI); } } // Before virtual bases destroy direct base objects. - for (CXXRecordDecl::base_class_const_iterator BI = RD->bases_begin(), - BE = RD->bases_end(); BI != BE; ++BI) { - if (!BI->isVirtual()) { - const CXXRecordDecl *CD = BI->getType()->getAsCXXRecordDecl(); + for (const auto &BI : RD->bases()) { + if (!BI.isVirtual()) { + const CXXRecordDecl *CD = BI.getType()->getAsCXXRecordDecl(); if (!CD->hasTrivialDestructor()) { autoCreateBlock(); - appendBaseDtor(Block, BI); + appendBaseDtor(Block, &BI); } } } // First destroy member objects. - for (CXXRecordDecl::field_iterator FI = RD->field_begin(), - FE = RD->field_end(); FI != FE; ++FI) { + for (auto *FI : RD->fields()) { // Check for constant size array. Set type to array element type. QualType QT = FI->getType(); if (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { @@ -903,7 +1161,7 @@ void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) { if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) if (!CD->hasTrivialDestructor()) { autoCreateBlock(); - appendMemberDtor(Block, *FI); + appendMemberDtor(Block, FI); } } } @@ -926,13 +1184,12 @@ void CFGBuilder::addLocalScopeForStmt(Stmt *S) { if (!BuildOpts.AddImplicitDtors) return; - LocalScope *Scope = 0; + LocalScope *Scope = nullptr; // For compound statement we will be creating explicit scope. if (CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) { - for (CompoundStmt::body_iterator BI = CS->body_begin(), BE = CS->body_end() - ; BI != BE; ++BI) { - Stmt *SI = (*BI)->stripLabelLikeStatements(); + for (auto *BI : CS->body()) { + Stmt *SI = BI->stripLabelLikeStatements(); if (DeclStmt *DS = dyn_cast<DeclStmt>(SI)) Scope = addLocalScopeForDeclStmt(DS, Scope); } @@ -952,11 +1209,9 @@ LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, if (!BuildOpts.AddImplicitDtors) return Scope; - for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end() - ; DI != DE; ++DI) { - if (VarDecl *VD = dyn_cast<VarDecl>(*DI)) + for (auto *DI : DS->decls()) + if (VarDecl *VD = dyn_cast<VarDecl>(DI)) Scope = addLocalScopeForVarDecl(VD, Scope); - } return Scope; } @@ -1051,7 +1306,7 @@ void CFGBuilder::prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk, CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { if (!S) { badCFG = true; - return 0; + return nullptr; } if (Expr *E = dyn_cast<Expr>(S)) @@ -1122,6 +1377,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { case Stmt::CXXConstructExprClass: return VisitCXXConstructExpr(cast<CXXConstructExpr>(S), asc); + case Stmt::CXXNewExprClass: + return VisitCXXNewExpr(cast<CXXNewExpr>(S), asc); + case Stmt::CXXDeleteExprClass: return VisitCXXDeleteExpr(cast<CXXDeleteExpr>(S), asc); @@ -1273,9 +1531,10 @@ CFGBlock *CFGBuilder::VisitLogicalOperator(BinaryOperator *B) { appendStmt(ConfluenceBlock, B); if (badCFG) - return 0; + return nullptr; - return VisitLogicalOperator(B, 0, ConfluenceBlock, ConfluenceBlock).first; + return VisitLogicalOperator(B, nullptr, ConfluenceBlock, + ConfluenceBlock).first; } std::pair<CFGBlock*, CFGBlock*> @@ -1293,7 +1552,7 @@ CFGBuilder::VisitLogicalOperator(BinaryOperator *B, do { if (BinaryOperator *B_RHS = dyn_cast<BinaryOperator>(RHS)) if (B_RHS->isLogicalOp()) { - llvm::tie(RHSBlock, ExitBlock) = + std::tie(RHSBlock, ExitBlock) = VisitLogicalOperator(B_RHS, Term, TrueBlock, FalseBlock); break; } @@ -1311,8 +1570,10 @@ CFGBuilder::VisitLogicalOperator(BinaryOperator *B, else { RHSBlock->setTerminator(Term); TryResult KnownVal = tryEvaluateBool(RHS); - addSuccessor(RHSBlock, KnownVal.isFalse() ? NULL : TrueBlock); - addSuccessor(RHSBlock, KnownVal.isTrue() ? NULL : FalseBlock); + if (!KnownVal.isKnown()) + KnownVal = tryEvaluateBool(B); + addSuccessor(RHSBlock, TrueBlock, !KnownVal.isFalse()); + addSuccessor(RHSBlock, FalseBlock, !KnownVal.isTrue()); } Block = RHSBlock; @@ -1321,7 +1582,7 @@ CFGBuilder::VisitLogicalOperator(BinaryOperator *B, while (false); if (badCFG) - return std::make_pair((CFGBlock*)0, (CFGBlock*)0); + return std::make_pair(nullptr, nullptr); // Generate the blocks for evaluating the LHS. Expr *LHS = B->getLHS()->IgnoreParens(); @@ -1348,19 +1609,19 @@ CFGBuilder::VisitLogicalOperator(BinaryOperator *B, CFGBlock *EntryLHSBlock = addStmt(LHS); if (badCFG) - return std::make_pair((CFGBlock*)0, (CFGBlock*)0); + return std::make_pair(nullptr, nullptr); // See if this is a known constant. TryResult KnownVal = tryEvaluateBool(LHS); // Now link the LHSBlock with RHSBlock. if (B->getOpcode() == BO_LOr) { - addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : TrueBlock); - addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : RHSBlock); + addSuccessor(LHSBlock, TrueBlock, !KnownVal.isFalse()); + addSuccessor(LHSBlock, RHSBlock, !KnownVal.isTrue()); } else { assert(B->getOpcode() == BO_LAnd); - addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); - addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : FalseBlock); + addSuccessor(LHSBlock, RHSBlock, !KnownVal.isFalse()); + addSuccessor(LHSBlock, FalseBlock, !KnownVal.isTrue()); } return std::make_pair(EntryLHSBlock, ExitBlock); @@ -1414,7 +1675,7 @@ CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) { // "break" is a control-flow statement. Thus we stop processing the current // block. if (badCFG) - return 0; + return nullptr; // Now create a new block that ends with the break statement. Block = createBlock(false); @@ -1502,7 +1763,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { if (Block) { Succ = Block; if (badCFG) - return 0; + return nullptr; } if (NoReturn) @@ -1528,26 +1789,26 @@ CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C, CFGBlock *ConfluenceBlock = Block ? Block : createBlock(); appendStmt(ConfluenceBlock, C); if (badCFG) - return 0; + return nullptr; AddStmtChoice alwaysAdd = asc.withAlwaysAdd(true); Succ = ConfluenceBlock; - Block = NULL; + Block = nullptr; CFGBlock *LHSBlock = Visit(C->getLHS(), alwaysAdd); if (badCFG) - return 0; + return nullptr; Succ = ConfluenceBlock; - Block = NULL; + Block = nullptr; CFGBlock *RHSBlock = Visit(C->getRHS(), alwaysAdd); if (badCFG) - return 0; + return nullptr; Block = createBlock(false); // See if this is a known constant. const TryResult& KnownVal = tryEvaluateBool(C->getCond()); - addSuccessor(Block, KnownVal.isFalse() ? NULL : LHSBlock); - addSuccessor(Block, KnownVal.isTrue() ? NULL : RHSBlock); + addSuccessor(Block, KnownVal.isFalse() ? nullptr : LHSBlock); + addSuccessor(Block, KnownVal.isTrue() ? nullptr : RHSBlock); Block->setTerminator(C); return addStmt(C->getCond()); } @@ -1565,7 +1826,7 @@ CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) { LastBlock = newBlock; if (badCFG) - return NULL; + return nullptr; } return LastBlock; @@ -1574,14 +1835,14 @@ CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) { CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C, AddStmtChoice asc) { const BinaryConditionalOperator *BCO = dyn_cast<BinaryConditionalOperator>(C); - const OpaqueValueExpr *opaqueValue = (BCO ? BCO->getOpaqueValue() : NULL); + const OpaqueValueExpr *opaqueValue = (BCO ? BCO->getOpaqueValue() : nullptr); // Create the confluence block that will "merge" the results of the ternary // expression. CFGBlock *ConfluenceBlock = Block ? Block : createBlock(); appendStmt(ConfluenceBlock, C); if (badCFG) - return 0; + return nullptr; AddStmtChoice alwaysAdd = asc.withAlwaysAdd(true); @@ -1590,14 +1851,14 @@ CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C, // value that is returned instead. // e.g: x ?: y is shorthand for: x ? x : y; Succ = ConfluenceBlock; - Block = NULL; - CFGBlock *LHSBlock = 0; + Block = nullptr; + CFGBlock *LHSBlock = nullptr; const Expr *trueExpr = C->getTrueExpr(); if (trueExpr != opaqueValue) { LHSBlock = Visit(C->getTrueExpr(), alwaysAdd); if (badCFG) - return 0; - Block = NULL; + return nullptr; + Block = nullptr; } else LHSBlock = ConfluenceBlock; @@ -1606,7 +1867,7 @@ CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C, Succ = ConfluenceBlock; CFGBlock *RHSBlock = Visit(C->getFalseExpr(), alwaysAdd); if (badCFG) - return 0; + return nullptr; // If the condition is a logical '&&' or '||', build a more accurate CFG. if (BinaryOperator *Cond = @@ -1619,8 +1880,8 @@ CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C, // See if this is a known constant. const TryResult& KnownVal = tryEvaluateBool(C->getCond()); - addSuccessor(Block, KnownVal.isFalse() ? NULL : LHSBlock); - addSuccessor(Block, KnownVal.isTrue() ? NULL : RHSBlock); + addSuccessor(Block, LHSBlock, !KnownVal.isFalse()); + addSuccessor(Block, RHSBlock, !KnownVal.isTrue()); Block->setTerminator(C); Expr *condExpr = C->getCond(); @@ -1648,7 +1909,7 @@ CFGBlock *CFGBuilder::VisitDeclStmt(DeclStmt *DS) { if (DS->isSingleDecl()) return VisitDeclSubExpr(DS); - CFGBlock *B = 0; + CFGBlock *B = nullptr; // Build an individual DeclStmt for each decl. for (DeclStmt::reverse_decl_iterator I = DS->decl_rbegin(), @@ -1689,16 +1950,16 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { bool HasTemporaries = false; // Guard static initializers under a branch. - CFGBlock *blockAfterStaticInit = 0; + CFGBlock *blockAfterStaticInit = nullptr; if (BuildOpts.AddStaticInitBranches && VD->isStaticLocal()) { // For static variables, we need to create a branch to track // whether or not they are initialized. if (Block) { Succ = Block; - Block = 0; + Block = nullptr; if (badCFG) - return 0; + return nullptr; } blockAfterStaticInit = Succ; } @@ -1741,7 +2002,7 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { // If the type of VD is a VLA, then we must process its size expressions. for (const VariableArrayType* VA = FindVA(VD->getType().getTypePtr()); - VA != 0; VA = FindVA(VA->getElementType().getTypePtr())) { + VA != nullptr; VA = FindVA(VA->getElementType().getTypePtr())) { if (CFGBlock *newBlock = addStmt(VA->getSizeExpr())) LastBlock = newBlock; } @@ -1788,7 +2049,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { if (Block) { Succ = Block; if (badCFG) - return 0; + return nullptr; } // Process the false branch. @@ -1799,7 +2060,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { // NULL out Block so that the recursive call to Visit will // create a new basic block. - Block = NULL; + Block = nullptr; // If branch is not a compound statement create implicit scope // and add destructors. @@ -1812,7 +2073,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { ElseBlock = sv.get(); else if (Block) { if (badCFG) - return 0; + return nullptr; } } @@ -1822,7 +2083,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { Stmt *Then = I->getThen(); assert(Then); SaveAndRestore<CFGBlock*> sv(Succ); - Block = NULL; + Block = nullptr; // If branch is not a compound statement create implicit scope // and add destructors. @@ -1839,7 +2100,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { addSuccessor(ThenBlock, sv.get()); } else if (Block) { if (badCFG) - return 0; + return nullptr; } } @@ -1865,23 +2126,21 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { // See if this is a known constant. const TryResult &KnownVal = tryEvaluateBool(I->getCond()); - // Now add the successors. - addSuccessor(Block, KnownVal.isFalse() ? NULL : ThenBlock); - addSuccessor(Block, KnownVal.isTrue()? NULL : ElseBlock); + // Add the successors. If we know that specific branches are + // unreachable, inform addSuccessor() of that knowledge. + addSuccessor(Block, ThenBlock, /* isReachable = */ !KnownVal.isFalse()); + addSuccessor(Block, ElseBlock, /* isReachable = */ !KnownVal.isTrue()); // Add the condition as the last statement in the new block. This may create // new blocks as the condition may contain control-flow. Any newly created // blocks will be pointed to be "Block". CFGBlock *LastBlock = addStmt(I->getCond()); - // Finally, if the IfStmt contains a condition variable, add both the IfStmt - // and the condition variable initialization to the CFG. - if (VarDecl *VD = I->getConditionVariable()) { - if (Expr *Init = VD->getInit()) { - autoCreateBlock(); - appendStmt(Block, I->getConditionVariableDeclStmt()); - LastBlock = addStmt(Init); - } + // Finally, if the IfStmt contains a condition variable, add it and its + // initializer to the CFG. + if (const DeclStmt* DS = I->getConditionVariableDeclStmt()) { + autoCreateBlock(); + LastBlock = addStmt(const_cast<DeclStmt *>(DS)); } return LastBlock; @@ -1929,10 +2188,10 @@ CFGBlock *CFGBuilder::VisitLabelStmt(LabelStmt *L) { // about. LabelBlock->setLabel(L); if (badCFG) - return 0; + return nullptr; // We set Block to NULL to allow lazy creation of a new block (if necessary); - Block = NULL; + Block = nullptr; // This block is now the implicit successor of other blocks. Succ = LabelBlock; @@ -1946,7 +2205,7 @@ CFGBlock *CFGBuilder::VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc) { et = E->capture_init_end(); it != et; ++it) { if (Expr *Init = *it) { CFGBlock *Tmp = Visit(Init); - if (Tmp != 0) + if (Tmp) LastBlock = Tmp; } } @@ -1976,7 +2235,7 @@ CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) { } CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { - CFGBlock *LoopSuccessor = NULL; + CFGBlock *LoopSuccessor = nullptr; // Save local scope position because in case of condition variable ScopePos // won't be restored when traversing AST. @@ -1999,7 +2258,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { // block. if (Block) { if (badCFG) - return 0; + return nullptr; LoopSuccessor = Block; } else LoopSuccessor = Succ; @@ -2009,7 +2268,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { SaveAndRestore<JumpTarget> save_break(BreakJumpTarget); BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); - CFGBlock *BodyBlock = 0, *TransitionBlock = 0; + CFGBlock *BodyBlock = nullptr, *TransitionBlock = nullptr; // Now create the loop body. { @@ -2035,8 +2294,8 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { if (Block) { assert(Block == Succ); if (badCFG) - return 0; - Block = 0; + return nullptr; + Block = nullptr; } // The starting block for the loop increment is the block that should @@ -2062,13 +2321,13 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { BodyBlock = ContinueJumpTarget.block; } else if (badCFG) - return 0; + return nullptr; } // Because of short-circuit evaluation, the condition of the loop can span // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that // evaluate the condition. - CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0; + CFGBlock *EntryConditionBlock = nullptr, *ExitConditionBlock = nullptr; do { Expr *C = F->getCond(); @@ -2076,9 +2335,9 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { // Specially handle logical operators, which have a slightly // more optimal CFG representation. if (BinaryOperator *Cond = - dyn_cast_or_null<BinaryOperator>(C ? C->IgnoreParens() : 0)) + dyn_cast_or_null<BinaryOperator>(C ? C->IgnoreParens() : nullptr)) if (Cond->isLogicalOp()) { - llvm::tie(EntryConditionBlock, ExitConditionBlock) = + std::tie(EntryConditionBlock, ExitConditionBlock) = VisitLogicalOperator(Cond, F, BodyBlock, LoopSuccessor); break; } @@ -2109,16 +2368,17 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { } if (Block && badCFG) - return 0; + return nullptr; KnownVal = tryEvaluateBool(C); } // Add the loop body entry as a successor to the condition. - addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock); + addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? nullptr : BodyBlock); // Link up the condition block with the code that follows the loop. (the // false branch). - addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); + addSuccessor(ExitConditionBlock, + KnownVal.isTrue() ? nullptr : LoopSuccessor); } while (false); @@ -2137,7 +2397,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { // There is no loop initialization. We are thus basically a while loop. // NULL out Block to force lazy block construction. - Block = NULL; + Block = nullptr; Succ = EntryConditionBlock; return EntryConditionBlock; } @@ -2183,13 +2443,13 @@ CFGBlock *CFGBuilder::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { // a DeclStmt and the other returns a DeclRefExpr. // - CFGBlock *LoopSuccessor = 0; + CFGBlock *LoopSuccessor = nullptr; if (Block) { if (badCFG) - return 0; + return nullptr; LoopSuccessor = Block; - Block = 0; + Block = nullptr; } else LoopSuccessor = Succ; @@ -2212,8 +2472,8 @@ CFGBlock *CFGBuilder::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { AddStmtChoice::NotAlwaysAdd); if (Block) { if (badCFG) - return 0; - Block = 0; + return nullptr; + Block = nullptr; } // The condition block is the implicit successor for the loop body as well as @@ -2230,7 +2490,7 @@ CFGBlock *CFGBuilder::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { // Add an intermediate block between the BodyBlock and the // EntryConditionBlock to represent the "loop back" transition, for looping // back to the head of the loop. - CFGBlock *LoopBackBlock = 0; + CFGBlock *LoopBackBlock = nullptr; Succ = LoopBackBlock = createBlock(); LoopBackBlock->setLoopTarget(S); @@ -2243,7 +2503,7 @@ CFGBlock *CFGBuilder::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { BodyBlock = ContinueJumpTarget.block; // can happen for "for (X in Y) ;" else if (Block) { if (badCFG) - return 0; + return nullptr; } // This new body block is a successor to our "exit" condition block. @@ -2275,9 +2535,9 @@ CFGBlock *CFGBuilder::VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S) { // for diagnostic clients. if (SyncBlock) { if (badCFG) - return 0; + return nullptr; - Block = 0; + Block = nullptr; Succ = SyncBlock; } @@ -2320,7 +2580,7 @@ CFGBlock *CFGBuilder::VisitPseudoObjectExpr(PseudoObjectExpr *E) { } CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { - CFGBlock *LoopSuccessor = NULL; + CFGBlock *LoopSuccessor = nullptr; // Save local scope position because in case of condition variable ScopePos // won't be restored when traversing AST. @@ -2338,14 +2598,14 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { // block. if (Block) { if (badCFG) - return 0; + return nullptr; LoopSuccessor = Block; - Block = 0; + Block = nullptr; } else { LoopSuccessor = Succ; } - CFGBlock *BodyBlock = 0, *TransitionBlock = 0; + CFGBlock *BodyBlock = nullptr, *TransitionBlock = nullptr; // Process the loop body. { @@ -2379,13 +2639,13 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { if (!BodyBlock) BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;" else if (Block && badCFG) - return 0; + return nullptr; } // Because of short-circuit evaluation, the condition of the loop can span // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that // evaluate the condition. - CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0; + CFGBlock *EntryConditionBlock = nullptr, *ExitConditionBlock = nullptr; do { Expr *C = W->getCond(); @@ -2394,9 +2654,8 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { // more optimal CFG representation. if (BinaryOperator *Cond = dyn_cast<BinaryOperator>(C->IgnoreParens())) if (Cond->isLogicalOp()) { - llvm::tie(EntryConditionBlock, ExitConditionBlock) = - VisitLogicalOperator(Cond, W, BodyBlock, - LoopSuccessor); + std::tie(EntryConditionBlock, ExitConditionBlock) = + VisitLogicalOperator(Cond, W, BodyBlock, LoopSuccessor); break; } @@ -2422,16 +2681,17 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { } if (Block && badCFG) - return 0; + return nullptr; // See if this is a known constant. const TryResult& KnownVal = tryEvaluateBool(C); // Add the loop body entry as a successor to the condition. - addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock); + addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? nullptr : BodyBlock); // Link up the condition block with the code that follows the loop. (the // false branch). - addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); + addSuccessor(ExitConditionBlock, + KnownVal.isTrue() ? nullptr : LoopSuccessor); } while(false); @@ -2440,7 +2700,7 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { // There can be no more statements in the condition block since we loop back // to this block. NULL out Block to force lazy creation of another block. - Block = NULL; + Block = nullptr; // Return the condition block, which is the dominating block for the loop. Succ = EntryConditionBlock; @@ -2460,7 +2720,7 @@ CFGBlock *CFGBuilder::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { // If we were in the middle of a block we stop processing that block. if (badCFG) - return 0; + return nullptr; // Create the new block. Block = createBlock(false); @@ -2476,7 +2736,7 @@ CFGBlock *CFGBuilder::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { CFGBlock *CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr *T) { // If we were in the middle of a block we stop processing that block. if (badCFG) - return 0; + return nullptr; // Create the new block. Block = createBlock(false); @@ -2494,13 +2754,13 @@ CFGBlock *CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr *T) { } CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { - CFGBlock *LoopSuccessor = NULL; + CFGBlock *LoopSuccessor = nullptr; // "do...while" is a control-flow statement. Thus we stop processing the // current block. if (Block) { if (badCFG) - return 0; + return nullptr; LoopSuccessor = Block; } else LoopSuccessor = Succ; @@ -2521,7 +2781,7 @@ CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { EntryConditionBlock = addStmt(C); if (Block) { if (badCFG) - return 0; + return nullptr; } } @@ -2532,7 +2792,7 @@ CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { const TryResult &KnownVal = tryEvaluateBool(D->getCond()); // Process the loop body. - CFGBlock *BodyBlock = NULL; + CFGBlock *BodyBlock = nullptr; { assert(D->getBody()); @@ -2548,7 +2808,7 @@ CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); // NULL out Block to force lazy instantiation of blocks for the body. - Block = NULL; + Block = nullptr; // If body is not a compound statement create implicit scope // and add destructors. @@ -2562,7 +2822,7 @@ CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { BodyBlock = EntryConditionBlock; // can happen for "do ; while(...)" else if (Block) { if (badCFG) - return 0; + return nullptr; } if (!KnownVal.isFalse()) { @@ -2571,7 +2831,7 @@ CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { // empty block to represent the transition block for looping back to the // head of the loop. // FIXME: Can we do this more efficiently without adding another block? - Block = NULL; + Block = nullptr; Succ = BodyBlock; CFGBlock *LoopBackBlock = createBlock(); LoopBackBlock->setLoopTarget(D); @@ -2580,16 +2840,16 @@ CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { addSuccessor(ExitConditionBlock, LoopBackBlock); } else - addSuccessor(ExitConditionBlock, NULL); + addSuccessor(ExitConditionBlock, nullptr); } // Link up the condition block with the code that follows the loop. // (the false branch). - addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); + addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? nullptr : LoopSuccessor); // There can be no more statements in the body block(s) since we loop back to // the body. NULL out Block to force lazy creation of another block. - Block = NULL; + Block = nullptr; // Return the loop body, which is the dominating block for the loop. Succ = BodyBlock; @@ -2600,7 +2860,7 @@ CFGBlock *CFGBuilder::VisitContinueStmt(ContinueStmt *C) { // "continue" is a control-flow statement. Thus we stop processing the // current block. if (badCFG) - return 0; + return nullptr; // Now create a new block that ends with the continue statement. Block = createBlock(false); @@ -2630,7 +2890,7 @@ CFGBlock *CFGBuilder::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, if (E->isArgumentType()) { for (const VariableArrayType *VA =FindVA(E->getArgumentType().getTypePtr()); - VA != 0; VA = FindVA(VA->getElementType().getTypePtr())) + VA != nullptr; VA = FindVA(VA->getElementType().getTypePtr())) lastBlock = addStmt(VA->getSizeExpr()); } return lastBlock; @@ -2649,7 +2909,7 @@ CFGBlock *CFGBuilder::VisitStmtExpr(StmtExpr *SE, AddStmtChoice asc) { CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { // "switch" is a control-flow statement. Thus we stop processing the current // block. - CFGBlock *SwitchSuccessor = NULL; + CFGBlock *SwitchSuccessor = nullptr; // Save local scope position because in case of condition variable ScopePos // won't be restored when traversing AST. @@ -2665,7 +2925,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { if (Block) { if (badCFG) - return 0; + return nullptr; SwitchSuccessor = Block; } else SwitchSuccessor = Succ; @@ -2691,7 +2951,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { // up to the switch. We also don't keep a pointer to the body, since all // control-flow from the switch goes to case/default statements. assert(Terminator->getBody() && "switch must contain a non-NULL body"); - Block = NULL; + Block = nullptr; // For pruning unreachable case statements, save the current state // for tracking the condition value. @@ -2703,7 +2963,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { Expr::EvalResult result; bool b = tryEvaluate(Terminator->getCond(), result); SaveAndRestore<Expr::EvalResult*> save_switchCond(switchCond, - b ? &result : 0); + b ? &result : nullptr); // If body is not a compound statement create implicit scope // and add destructors. @@ -2713,7 +2973,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { addStmt(Terminator->getBody()); if (Block) { if (badCFG) - return 0; + return nullptr; } // If we have no "default:" case, the default transition is to the code @@ -2726,8 +2986,8 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { SwitchAlwaysHasSuccessor |= switchExclusivelyCovered; SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() && Terminator->getSwitchCaseList(); - addSuccessor(SwitchTerminatedBlock, - SwitchAlwaysHasSuccessor ? 0 : DefaultCaseBlock); + addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock, + !SwitchAlwaysHasSuccessor); // Add the terminator and condition in the switch block. SwitchTerminatedBlock->setTerminator(Terminator); @@ -2786,7 +3046,7 @@ static bool shouldAddCase(bool &switchExclusivelyCovered, CFGBlock *CFGBuilder::VisitCaseStmt(CaseStmt *CS) { // CaseStmts are essentially labels, so they are the first statement in a // block. - CFGBlock *TopBlock = 0, *LastBlock = 0; + CFGBlock *TopBlock = nullptr, *LastBlock = nullptr; if (Stmt *Sub = CS->getSubStmt()) { // For deeply nested chains of CaseStmts, instead of doing a recursion @@ -2804,7 +3064,7 @@ CFGBlock *CFGBuilder::VisitCaseStmt(CaseStmt *CS) { addSuccessor(SwitchTerminatedBlock, shouldAddCase(switchExclusivelyCovered, switchCond, CS, *Context) - ? currentBlock : 0); + ? currentBlock : nullptr); LastBlock = currentBlock; CS = cast<CaseStmt>(Sub); @@ -2823,18 +3083,17 @@ CFGBlock *CFGBuilder::VisitCaseStmt(CaseStmt *CS) { CaseBlock->setLabel(CS); if (badCFG) - return 0; + return nullptr; // Add this block to the list of successors for the block with the switch // statement. assert(SwitchTerminatedBlock); - addSuccessor(SwitchTerminatedBlock, + addSuccessor(SwitchTerminatedBlock, CaseBlock, shouldAddCase(switchExclusivelyCovered, switchCond, - CS, *Context) - ? CaseBlock : 0); + CS, *Context)); // We set Block to NULL to allow lazy creation of a new block (if necessary) - Block = NULL; + Block = nullptr; if (TopBlock) { addSuccessor(LastBlock, CaseBlock); @@ -2861,7 +3120,7 @@ CFGBlock *CFGBuilder::VisitDefaultStmt(DefaultStmt *Terminator) { DefaultCaseBlock->setLabel(Terminator); if (badCFG) - return 0; + return nullptr; // Unlike case statements, we don't add the default block to the successors // for the switch statement immediately. This is done when we finish @@ -2870,7 +3129,7 @@ CFGBlock *CFGBuilder::VisitDefaultStmt(DefaultStmt *Terminator) { // be the last successor of a switch-terminated block. // We set Block to NULL to allow lazy creation of a new block (if necessary) - Block = NULL; + Block = nullptr; // This block is now the implicit successor of other blocks. Succ = DefaultCaseBlock; @@ -2881,11 +3140,11 @@ CFGBlock *CFGBuilder::VisitDefaultStmt(DefaultStmt *Terminator) { CFGBlock *CFGBuilder::VisitCXXTryStmt(CXXTryStmt *Terminator) { // "try"/"catch" is a control-flow statement. Thus we stop processing the // current block. - CFGBlock *TrySuccessor = NULL; + CFGBlock *TrySuccessor = nullptr; if (Block) { if (badCFG) - return 0; + return nullptr; TrySuccessor = Block; } else TrySuccessor = Succ; @@ -2901,13 +3160,13 @@ CFGBlock *CFGBuilder::VisitCXXTryStmt(CXXTryStmt *Terminator) { // The code after the try is the implicit successor. Succ = TrySuccessor; CXXCatchStmt *CS = Terminator->getHandler(h); - if (CS->getExceptionDecl() == 0) { + if (CS->getExceptionDecl() == nullptr) { HasCatchAll = true; } - Block = NULL; + Block = nullptr; CFGBlock *CatchBlock = VisitCXXCatchStmt(CS); - if (CatchBlock == 0) - return 0; + if (!CatchBlock) + return nullptr; // Add this block to the list of successors for the block with the try // statement. addSuccessor(NewTryTerminatedBlock, CatchBlock); @@ -2927,7 +3186,7 @@ CFGBlock *CFGBuilder::VisitCXXTryStmt(CXXTryStmt *Terminator) { cfg->addTryDispatchBlock(TryTerminatedBlock); assert(Terminator->getTryBlock() && "try must contain a non-NULL body"); - Block = NULL; + Block = nullptr; return addStmt(Terminator->getTryBlock()); } @@ -2966,10 +3225,10 @@ CFGBlock *CFGBuilder::VisitCXXCatchStmt(CXXCatchStmt *CS) { // Bail out if the CFG is bad. if (badCFG) - return 0; + return nullptr; // We set Block to NULL to allow lazy creation of a new block (if necessary) - Block = NULL; + Block = nullptr; return CatchBlock; } @@ -3002,10 +3261,10 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { // "for" is a control-flow statement. Thus we stop processing the current // block. - CFGBlock *LoopSuccessor = NULL; + CFGBlock *LoopSuccessor = nullptr; if (Block) { if (badCFG) - return 0; + return nullptr; LoopSuccessor = Block; } else LoopSuccessor = Succ; @@ -3024,7 +3283,7 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { Block = ConditionBlock; CFGBlock *BeginConditionBlock = addStmt(C); if (badCFG) - return 0; + return nullptr; assert(BeginConditionBlock == ConditionBlock && "condition block in for-range was unexpectedly complex"); (void)BeginConditionBlock; @@ -3050,7 +3309,7 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { // Generate increment code in its own basic block. This is the target of // continue statements. - Block = 0; + Block = nullptr; Succ = addStmt(S->getInc()); ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); @@ -3061,9 +3320,8 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { // Finish up the increment block and prepare to start the loop body. assert(Block); if (badCFG) - return 0; - Block = 0; - + return nullptr; + Block = nullptr; // Add implicit scope and dtors for loop variable. addLocalScopeAndDtors(S->getLoopVarStmt()); @@ -3071,18 +3329,19 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { // Populate a new block to contain the loop body and loop variable. addStmt(S->getBody()); if (badCFG) - return 0; + return nullptr; CFGBlock *LoopVarStmtBlock = addStmt(S->getLoopVarStmt()); if (badCFG) - return 0; - + return nullptr; + // This new body block is a successor to our condition block. - addSuccessor(ConditionBlock, KnownVal.isFalse() ? 0 : LoopVarStmtBlock); + addSuccessor(ConditionBlock, + KnownVal.isFalse() ? nullptr : LoopVarStmtBlock); } // Link up the condition block with the code that follows the loop (the // false branch). - addSuccessor(ConditionBlock, KnownVal.isTrue() ? 0 : LoopSuccessor); + addSuccessor(ConditionBlock, KnownVal.isTrue() ? nullptr : LoopSuccessor); // Add the initialization statements. Block = createBlock(); @@ -3124,6 +3383,23 @@ CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C, return VisitChildren(C); } +CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE, + AddStmtChoice asc) { + + autoCreateBlock(); + appendStmt(Block, NE); + + if (NE->getInitializer()) + Block = Visit(NE->getInitializer()); + if (BuildOpts.AddCXXNewAllocator) + appendNewAllocator(Block, NE); + if (NE->isArray()) + Block = Visit(NE->getArraySize()); + for (CXXNewExpr::arg_iterator I = NE->placement_arg_begin(), + E = NE->placement_arg_end(); I != E; ++I) + Block = Visit(*I); + return Block; +} CFGBlock *CFGBuilder::VisitCXXDeleteExpr(CXXDeleteExpr *DE, AddStmtChoice asc) { @@ -3179,7 +3455,7 @@ CFGBlock *CFGBuilder::VisitIndirectGotoStmt(IndirectGotoStmt *I) { // IndirectGoto is a control-flow statement. Thus we stop processing the // current block and create a new one. if (badCFG) - return 0; + return nullptr; Block = createBlock(false); Block->setTerminator(I); @@ -3193,7 +3469,7 @@ CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool BindToTemporary) { tryAgain: if (!E) { badCFG = true; - return NULL; + return nullptr; } switch (E->getStmtClass()) { default: @@ -3219,10 +3495,35 @@ tryAgain: case Stmt::ParenExprClass: E = cast<ParenExpr>(E)->getSubExpr(); goto tryAgain; - + case Stmt::MaterializeTemporaryExprClass: E = cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(); goto tryAgain; + + case Stmt::BlockExprClass: + // Don't recurse into blocks; their subexpressions don't get evaluated + // here. + return Block; + + case Stmt::LambdaExprClass: { + // For lambda expressions, only recurse into the capture initializers, + // and not the body. + auto *LE = cast<LambdaExpr>(E); + CFGBlock *B = Block; + for (Expr *Init : LE->capture_inits()) { + if (CFGBlock *R = VisitForTemporaryDtors(Init)) + B = R; + } + return B; + } + + case Stmt::CXXDefaultArgExprClass: + E = cast<CXXDefaultArgExpr>(E)->getExpr(); + goto tryAgain; + + case Stmt::CXXDefaultInitExprClass: + E = cast<CXXDefaultInitExpr>(E)->getExpr(); + goto tryAgain; } } @@ -3248,15 +3549,15 @@ CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E) { autoCreateBlock(); CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getLHS()); if (badCFG) - return NULL; + return nullptr; Succ = ConfluenceBlock; - Block = NULL; + Block = nullptr; CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS()); if (RHSBlock) { if (badCFG) - return NULL; + return nullptr; // If RHS expression did produce destructors we need to connect created // blocks to CFG in same manner as for binary operator itself. @@ -3276,12 +3577,12 @@ CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E) { // Link LHSBlock with RHSBlock exactly the same way as for binary operator // itself. if (E->getOpcode() == BO_LOr) { - addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock); - addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); + addSuccessor(LHSBlock, KnownVal.isTrue() ? nullptr : ConfluenceBlock); + addSuccessor(LHSBlock, KnownVal.isFalse() ? nullptr : RHSBlock); } else { assert (E->getOpcode() == BO_LAnd); - addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); - addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock); + addSuccessor(LHSBlock, KnownVal.isFalse() ? nullptr : RHSBlock); + addSuccessor(LHSBlock, KnownVal.isTrue() ? nullptr : ConfluenceBlock); } Block = LHSBlock; @@ -3320,10 +3621,12 @@ CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors( // a new block for the destructor which does not have as a successor // anything built thus far. Control won't flow out of this block. const CXXDestructorDecl *Dtor = E->getTemporary()->getDestructor(); - if (Dtor->isNoReturn()) + if (Dtor->isNoReturn()) { + Succ = B; Block = createNoReturnBlock(); - else + } else { autoCreateBlock(); + } appendTemporaryDtor(Block, E); B = Block; @@ -3339,29 +3642,29 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors( autoCreateBlock(); CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getCond()); if (badCFG) - return NULL; + return nullptr; if (BinaryConditionalOperator *BCO = dyn_cast<BinaryConditionalOperator>(E)) { ConfluenceBlock = VisitForTemporaryDtors(BCO->getCommon()); if (badCFG) - return NULL; + return nullptr; } // Try to add block with destructors for LHS expression. - CFGBlock *LHSBlock = NULL; + CFGBlock *LHSBlock = nullptr; Succ = ConfluenceBlock; - Block = NULL; + Block = nullptr; LHSBlock = VisitForTemporaryDtors(E->getTrueExpr(), BindToTemporary); if (badCFG) - return NULL; + return nullptr; // Try to add block with destructors for RHS expression; Succ = ConfluenceBlock; - Block = NULL; + Block = nullptr; CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getFalseExpr(), BindToTemporary); if (badCFG) - return NULL; + return nullptr; if (!RHSBlock && !LHSBlock) { // If neither LHS nor RHS expression had temporaries to destroy don't create @@ -3372,14 +3675,15 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors( Block = createBlock(false); Block->setTerminator(CFGTerminator(E, true)); + assert(Block->getTerminator().isTemporaryDtorsBranch()); // See if this is a known constant. const TryResult &KnownVal = tryEvaluateBool(E->getCond()); if (LHSBlock) { - addSuccessor(Block, KnownVal.isFalse() ? NULL : LHSBlock); + addSuccessor(Block, LHSBlock, !KnownVal.isFalse()); } else if (KnownVal.isFalse()) { - addSuccessor(Block, NULL); + addSuccessor(Block, nullptr); } else { addSuccessor(Block, ConfluenceBlock); std::reverse(ConfluenceBlock->pred_begin(), ConfluenceBlock->pred_end()); @@ -3387,7 +3691,8 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors( if (!RHSBlock) RHSBlock = ConfluenceBlock; - addSuccessor(Block, KnownVal.isTrue() ? NULL : RHSBlock); + + addSuccessor(Block, RHSBlock, !KnownVal.isTrue()); return Block; } @@ -3426,6 +3731,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { switch (getKind()) { case CFGElement::Statement: case CFGElement::Initializer: + case CFGElement::NewAllocator: llvm_unreachable("getDestructorDecl should only be used with " "ImplicitDtors"); case CFGElement::AutomaticObjectDtor: { @@ -3458,7 +3764,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { case CFGElement::MemberDtor: // Not yet supported. - return 0; + return nullptr; } llvm_unreachable("getKind() returned bogus value"); } @@ -3470,13 +3776,37 @@ bool CFGImplicitDtor::isNoReturn(ASTContext &astContext) const { } //===----------------------------------------------------------------------===// -// Filtered walking of the CFG. +// CFGBlock operations. //===----------------------------------------------------------------------===// +CFGBlock::AdjacentBlock::AdjacentBlock(CFGBlock *B, bool IsReachable) + : ReachableBlock(IsReachable ? B : nullptr), + UnreachableBlock(!IsReachable ? B : nullptr, + B && IsReachable ? AB_Normal : AB_Unreachable) {} + +CFGBlock::AdjacentBlock::AdjacentBlock(CFGBlock *B, CFGBlock *AlternateBlock) + : ReachableBlock(B), + UnreachableBlock(B == AlternateBlock ? nullptr : AlternateBlock, + B == AlternateBlock ? AB_Alternate : AB_Normal) {} + +void CFGBlock::addSuccessor(AdjacentBlock Succ, + BumpVectorContext &C) { + if (CFGBlock *B = Succ.getReachableBlock()) + B->Preds.push_back(AdjacentBlock(this, Succ.isReachable()), C); + + if (CFGBlock *UnreachableB = Succ.getPossiblyUnreachableBlock()) + UnreachableB->Preds.push_back(AdjacentBlock(this, false), C); + + Succs.push_back(Succ, C); +} + bool CFGBlock::FilterEdge(const CFGBlock::FilterOptions &F, const CFGBlock *From, const CFGBlock *To) { - if (To && F.IgnoreDefaultsWithCoveredEnums) { + if (F.IgnoreNullPredecessors && !From) + return true; + + if (To && From && F.IgnoreDefaultsWithCoveredEnums) { // If the 'To' has no label or is labeled but the label isn't a // CaseStmt then filter this edge. if (const SwitchStmt *S = @@ -3572,7 +3902,7 @@ public: void setBlockID(signed i) { currentBlock = i; } void setStmtID(unsigned i) { currStmt = i; } - virtual bool handledStmt(Stmt *S, raw_ostream &OS) { + bool handledStmt(Stmt *S, raw_ostream &OS) override { StmtMapTy::iterator I = StmtMap.find(S); if (I == StmtMap.end()) @@ -3615,11 +3945,14 @@ class CFGBlockTerminatorPrint public: CFGBlockTerminatorPrint(raw_ostream &os, StmtPrinterHelper* helper, const PrintingPolicy &Policy) - : OS(os), Helper(helper), Policy(Policy) {} + : OS(os), Helper(helper), Policy(Policy) { + this->Policy.IncludeNewlines = false; + } void VisitIfStmt(IfStmt *I) { OS << "if "; - I->getCond()->printPretty(OS,Helper,Policy); + if (Stmt *C = I->getCond()) + C->printPretty(OS, Helper, Policy); } // Default case. @@ -3667,19 +4000,22 @@ public: } void VisitAbstractConditionalOperator(AbstractConditionalOperator* C) { - C->getCond()->printPretty(OS, Helper, Policy); + if (Stmt *Cond = C->getCond()) + Cond->printPretty(OS, Helper, Policy); OS << " ? ... : ..."; } void VisitChooseExpr(ChooseExpr *C) { OS << "__builtin_choose_expr( "; - C->getCond()->printPretty(OS, Helper, Policy); + if (Stmt *Cond = C->getCond()) + Cond->printPretty(OS, Helper, Policy); OS << " )"; } void VisitIndirectGotoStmt(IndirectGotoStmt *I) { OS << "goto *"; - I->getTarget()->printPretty(OS, Helper, Policy); + if (Stmt *T = I->getTarget()) + T->printPretty(OS, Helper, Policy); } void VisitBinaryOperator(BinaryOperator* B) { @@ -3688,7 +4024,8 @@ public: return; } - B->getLHS()->printPretty(OS, Helper, Policy); + if (B->getLHS()) + B->getLHS()->printPretty(OS, Helper, Policy); switch (B->getOpcode()) { case BO_LOr: @@ -3705,6 +4042,13 @@ public: void VisitExpr(Expr *E) { E->printPretty(OS, Helper, Policy); } + +public: + void print(CFGTerminator T) { + if (T.isTemporaryDtorsBranch()) + OS << "(Temp Dtor) "; + Visit(T.getStmt()); + } }; } // end anonymous namespace @@ -3712,7 +4056,8 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, const CFGElement &E) { if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) { const Stmt *S = CS->getStmt(); - + assert(S != nullptr && "Expecting non-null Stmt"); + // special printing for statement-expressions. if (const StmtExpr *SE = dyn_cast<StmtExpr>(S)) { const CompoundStmt *Sub = SE->getSubStmt(); @@ -3787,6 +4132,11 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; OS << " (Implicit destructor)\n"; + } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) { + OS << "CFGNewAllocator("; + if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) + AllocExpr->getType().print(OS, PrintingPolicy(Helper.getLangOpts())); + OS << ")\n"; } else if (Optional<CFGDeleteDtor> DE = E.getAs<CFGDeleteDtor>()) { const CXXRecordDecl *RD = DE->getCXXRecordDecl(); if (!RD) @@ -3835,6 +4185,8 @@ static void print_block(raw_ostream &OS, const CFG* cfg, OS << " (EXIT)]\n"; else if (&B == cfg->getIndirectGotoBlock()) OS << " (INDIRECT GOTO DISPATCH)]\n"; + else if (B.hasNoReturnElement()) + OS << " (NORETURN)]\n"; else OS << "]\n"; @@ -3851,8 +4203,9 @@ static void print_block(raw_ostream &OS, const CFG* cfg, OS << L->getName(); else if (CaseStmt *C = dyn_cast<CaseStmt>(Label)) { OS << "case "; - C->getLHS()->printPretty(OS, &Helper, - PrintingPolicy(Helper.getLangOpts())); + if (C->getLHS()) + C->getLHS()->printPretty(OS, &Helper, + PrintingPolicy(Helper.getLangOpts())); if (C->getRHS()) { OS << " ... "; C->getRHS()->printPretty(OS, &Helper, @@ -3903,7 +4256,7 @@ static void print_block(raw_ostream &OS, const CFG* cfg, PrintingPolicy PP(Helper.getLangOpts()); CFGBlockTerminatorPrint TPrinter(OS, &Helper, PP); - TPrinter.Visit(const_cast<Stmt*>(B.getTerminator().getStmt())); + TPrinter.print(B.getTerminator()); OS << '\n'; if (ShowColors) @@ -3931,7 +4284,16 @@ static void print_block(raw_ostream &OS, const CFG* cfg, if (i % 10 == 8) OS << "\n "; - OS << " B" << (*I)->getBlockID(); + CFGBlock *B = *I; + bool Reachable = true; + if (!B) { + Reachable = false; + B = I->getPossiblyUnreachableBlock(); + } + + OS << " B" << B->getBlockID(); + if (!Reachable) + OS << "(Unreachable)"; } if (ShowColors) @@ -3960,12 +4322,24 @@ static void print_block(raw_ostream &OS, const CFG* cfg, if (i % 10 == 8) OS << "\n "; - if (*I) - OS << " B" << (*I)->getBlockID(); - else - OS << " NULL"; + CFGBlock *B = *I; + + bool Reachable = true; + if (!B) { + Reachable = false; + B = I->getPossiblyUnreachableBlock(); + } + + if (B) { + OS << " B" << B->getBlockID(); + if (!Reachable) + OS << "(Unreachable)"; + } + else { + OS << " NULL"; + } } - + if (ShowColors) OS.resetColor(); OS << '\n'; @@ -4007,6 +4381,10 @@ void CFGBlock::dump(const CFG* cfg, const LangOptions &LO, print(llvm::errs(), cfg, LO, ShowColors); } +void CFGBlock::dump() const { + dump(getParent(), LangOptions(), false); +} + /// print - A simple pretty printer of a CFGBlock that outputs to an ostream. /// Generally this will only be called from CFG::print. void CFGBlock::print(raw_ostream &OS, const CFG* cfg, @@ -4019,16 +4397,16 @@ void CFGBlock::print(raw_ostream &OS, const CFG* cfg, /// printTerminator - A simple pretty printer of the terminator of a CFGBlock. void CFGBlock::printTerminator(raw_ostream &OS, const LangOptions &LO) const { - CFGBlockTerminatorPrint TPrinter(OS, NULL, PrintingPolicy(LO)); - TPrinter.Visit(const_cast<Stmt*>(getTerminator().getStmt())); + CFGBlockTerminatorPrint TPrinter(OS, nullptr, PrintingPolicy(LO)); + TPrinter.print(getTerminator()); } -Stmt *CFGBlock::getTerminatorCondition() { +Stmt *CFGBlock::getTerminatorCondition(bool StripParens) { Stmt *Terminator = this->Terminator; if (!Terminator) - return NULL; + return nullptr; - Expr *E = NULL; + Expr *E = nullptr; switch (Terminator->getStmtClass()) { default: @@ -4082,7 +4460,10 @@ Stmt *CFGBlock::getTerminatorCondition() { return Terminator; } - return E ? E->IgnoreParens() : NULL; + if (!StripParens) + return E; + + return E ? E->IgnoreParens() : nullptr; } //===----------------------------------------------------------------------===// @@ -4099,7 +4480,7 @@ void CFG::viewCFG(const LangOptions &LO) const { StmtPrinterHelper H(this, LO); GraphHelper = &H; llvm::ViewGraph(this,"CFG"); - GraphHelper = NULL; + GraphHelper = nullptr; #endif } diff --git a/contrib/llvm/tools/clang/lib/Analysis/CFGReachabilityAnalysis.cpp b/contrib/llvm/tools/clang/lib/Analysis/CFGReachabilityAnalysis.cpp index 492e66f..4ae135f 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/CFGReachabilityAnalysis.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/CFGReachabilityAnalysis.cpp @@ -69,7 +69,8 @@ void CFGReverseBlockReachabilityAnalysis::mapReachability(const CFGBlock *Dst) { // Add the predecessors to the worklist. for (CFGBlock::const_pred_iterator i = block->pred_begin(), e = block->pred_end(); i != e; ++i) { - worklist.push_back(*i); + if (*i) + worklist.push_back(*i); } } } diff --git a/contrib/llvm/tools/clang/lib/Analysis/CFGStmtMap.cpp b/contrib/llvm/tools/clang/lib/Analysis/CFGStmtMap.cpp index 87c2f5b..19b8019 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/CFGStmtMap.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/CFGStmtMap.cpp @@ -42,8 +42,8 @@ CFGBlock *CFGStmtMap::getBlock(Stmt *S) { X = PM->getParentIgnoreParens(X); } - - return 0; + + return nullptr; } static void Accumulate(SMap &SM, CFGBlock *B) { @@ -77,7 +77,7 @@ static void Accumulate(SMap &SM, CFGBlock *B) { CFGStmtMap *CFGStmtMap::Build(CFG *C, ParentMap *PM) { if (!C || !PM) - return 0; + return nullptr; SMap *SM = new SMap(); diff --git a/contrib/llvm/tools/clang/lib/Analysis/CallGraph.cpp b/contrib/llvm/tools/clang/lib/Analysis/CallGraph.cpp index 3387015..f41a96d 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/CallGraph.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/CallGraph.cpp @@ -10,8 +10,6 @@ // This file defines the AST-based CallGraph. // //===----------------------------------------------------------------------===// -#define DEBUG_TYPE "CallGraph" - #include "clang/Analysis/CallGraph.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -22,6 +20,8 @@ using namespace clang; +#define DEBUG_TYPE "CallGraph" + STATISTIC(NumObjCCallEdges, "Number of Objective-C method call edges"); STATISTIC(NumBlockCallEdges, "Number of block call edges"); @@ -49,7 +49,7 @@ public: return Block->getBlockDecl(); } - return 0; + return nullptr; } void addCalledDecl(Decl *D) { @@ -70,7 +70,7 @@ public: Selector Sel = ME->getSelector(); // Find the callee definition within the same translation unit. - Decl *D = 0; + Decl *D = nullptr; if (ME->isInstanceMessage()) D = IDecl->lookupPrivateMethod(Sel); else @@ -95,23 +95,17 @@ void CallGraph::addNodesForBlocks(DeclContext *D) { if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) addNodeForDecl(BD, true); - for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); - I!=E; ++I) - if (DeclContext *DC = dyn_cast<DeclContext>(*I)) + for (auto *I : D->decls()) + if (auto *DC = dyn_cast<DeclContext>(I)) addNodesForBlocks(DC); } CallGraph::CallGraph() { - Root = getOrInsertNode(0); + Root = getOrInsertNode(nullptr); } CallGraph::~CallGraph() { - if (!FunctionMap.empty()) { - for (FunctionMapTy::iterator I = FunctionMap.begin(), E = FunctionMap.end(); - I != E; ++I) - delete I->second; - FunctionMap.clear(); - } + llvm::DeleteContainerSeconds(FunctionMap); } bool CallGraph::includeInGraph(const Decl *D) { @@ -153,7 +147,7 @@ void CallGraph::addNodeForDecl(Decl* D, bool IsGlobal) { CallGraphNode *CallGraph::getNode(const Decl *F) const { FunctionMapTy::const_iterator I = FunctionMap.find(F); - if (I == FunctionMap.end()) return 0; + if (I == FunctionMap.end()) return nullptr; return I->second; } @@ -164,7 +158,7 @@ CallGraphNode *CallGraph::getOrInsertNode(Decl *F) { Node = new CallGraphNode(F); // Make Root node a parent of all functions to make sure all are reachable. - if (F != 0) + if (F) Root->addCallee(Node, this); return Node; } diff --git a/contrib/llvm/tools/clang/lib/Analysis/Consumed.cpp b/contrib/llvm/tools/clang/lib/Analysis/Consumed.cpp index e5ec3e6..2b2da2c 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/Consumed.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/Consumed.cpp @@ -17,20 +17,20 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" -#include "clang/AST/StmtVisitor.h" #include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" +#include "clang/Analysis/Analyses/Consumed.h" #include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" -#include "clang/Analysis/Analyses/Consumed.h" #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" +#include <memory> // TODO: Adjust states of args to constructors in the same way that arguments to // function calls are handled. @@ -57,11 +57,9 @@ ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {} static SourceLocation getFirstStmtLoc(const CFGBlock *Block) { // Find the source location of the first statement in the block, if the block // is not empty. - for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end(); - BI != BE; ++BI) { - if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) + for (const auto &B : *Block) + if (Optional<CFGStmt> CS = B.getAs<CFGStmt>()) return CS->getStmt()->getLocStart(); - } // Block is empty. // If we have one successor, return the first statement in that block @@ -115,14 +113,10 @@ static ConsumedState invertConsumedUnconsumed(ConsumedState State) { static bool isCallableInState(const CallableWhenAttr *CWAttr, ConsumedState State) { - CallableWhenAttr::callableState_iterator I = CWAttr->callableState_begin(), - E = CWAttr->callableState_end(); - - for (; I != E; ++I) { - + for (const auto &S : CWAttr->callableStates()) { ConsumedState MappedAttrState = CS_None; - - switch (*I) { + + switch (S) { case CallableWhenAttr::Unknown: MappedAttrState = CS_Unknown; break; @@ -143,6 +137,7 @@ static bool isCallableInState(const CallableWhenAttr *CWAttr, return false; } + static bool isConsumableType(const QualType &QT) { if (QT->isPointerType() || QT->isReferenceType()) return false; @@ -153,6 +148,23 @@ static bool isConsumableType(const QualType &QT) { return false; } +static bool isAutoCastType(const QualType &QT) { + if (QT->isPointerType() || QT->isReferenceType()) + return false; + + if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl()) + return RD->hasAttr<ConsumableAutoCastAttr>(); + + return false; +} + +static bool isSetOnReadPtrType(const QualType &QT) { + if (const CXXRecordDecl *RD = QT->getPointeeCXXRecordDecl()) + return RD->hasAttr<ConsumableSetOnReadAttr>(); + return false; +} + + static bool isKnownState(ConsumedState State) { switch (State) { case CS_Unconsumed: @@ -165,19 +177,16 @@ static bool isKnownState(ConsumedState State) { llvm_unreachable("invalid enum"); } -static bool isRValueRefish(QualType ParamType) { - return ParamType->isRValueReferenceType() || - (ParamType->isLValueReferenceType() && - !cast<LValueReferenceType>( - ParamType.getCanonicalType())->isSpelledAsLValue()); +static bool isRValueRef(QualType ParamType) { + return ParamType->isRValueReferenceType(); } static bool isTestingFunction(const FunctionDecl *FunDecl) { return FunDecl->hasAttr<TestTypestateAttr>(); } -static bool isValueType(QualType ParamType) { - return !(ParamType->isPointerType() || ParamType->isReferenceType()); +static bool isPointerOrRef(QualType ParamType) { + return ParamType->isPointerType() || ParamType->isReferenceType(); } static ConsumedState mapConsumableAttrState(const QualType QT) { @@ -455,15 +464,29 @@ class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> { ConsumedAnalyzer &Analyzer; ConsumedStateMap *StateMap; MapType PropagationMap; - void forwardInfo(const Stmt *From, const Stmt *To); - bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl); - void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun, - QualType ReturnType); + + InfoEntry findInfo(const Expr *E) { + return PropagationMap.find(E->IgnoreParens()); + } + ConstInfoEntry findInfo(const Expr *E) const { + return PropagationMap.find(E->IgnoreParens()); + } + void insertInfo(const Expr *E, const PropagationInfo &PI) { + PropagationMap.insert(PairType(E->IgnoreParens(), PI)); + } + + void forwardInfo(const Expr *From, const Expr *To); + void copyInfo(const Expr *From, const Expr *To, ConsumedState CS); + ConsumedState getInfo(const Expr *From); + void setInfo(const Expr *To, ConsumedState NS); + void propagateReturnType(const Expr *Call, const FunctionDecl *Fun); public: void checkCallability(const PropagationInfo &PInfo, const FunctionDecl *FunDecl, SourceLocation BlameLoc); + bool handleCall(const CallExpr *Call, const Expr *ObjArg, + const FunctionDecl *FunD); void VisitBinaryOperator(const BinaryOperator *BinOp); void VisitCallExpr(const CallExpr *Call); @@ -485,8 +508,8 @@ public: ConsumedStateMap *StateMap) : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {} - PropagationInfo getInfo(const Stmt *StmtNode) const { - ConstInfoEntry Entry = PropagationMap.find(StmtNode); + PropagationInfo getInfo(const Expr *StmtNode) const { + ConstInfoEntry Entry = findInfo(StmtNode); if (Entry != PropagationMap.end()) return Entry->second; @@ -499,76 +522,187 @@ public: } }; + +void ConsumedStmtVisitor::forwardInfo(const Expr *From, const Expr *To) { + InfoEntry Entry = findInfo(From); + if (Entry != PropagationMap.end()) + insertInfo(To, Entry->second); +} + + +// Create a new state for To, which is initialized to the state of From. +// If NS is not CS_None, sets the state of From to NS. +void ConsumedStmtVisitor::copyInfo(const Expr *From, const Expr *To, + ConsumedState NS) { + InfoEntry Entry = findInfo(From); + if (Entry != PropagationMap.end()) { + PropagationInfo& PInfo = Entry->second; + ConsumedState CS = PInfo.getAsState(StateMap); + if (CS != CS_None) + insertInfo(To, PropagationInfo(CS)); + if (NS != CS_None && PInfo.isPointerToValue()) + setStateForVarOrTmp(StateMap, PInfo, NS); + } +} + + +// Get the ConsumedState for From +ConsumedState ConsumedStmtVisitor::getInfo(const Expr *From) { + InfoEntry Entry = findInfo(From); + if (Entry != PropagationMap.end()) { + PropagationInfo& PInfo = Entry->second; + return PInfo.getAsState(StateMap); + } + return CS_None; +} + + +// If we already have info for To then update it, otherwise create a new entry. +void ConsumedStmtVisitor::setInfo(const Expr *To, ConsumedState NS) { + InfoEntry Entry = findInfo(To); + if (Entry != PropagationMap.end()) { + PropagationInfo& PInfo = Entry->second; + if (PInfo.isPointerToValue()) + setStateForVarOrTmp(StateMap, PInfo, NS); + } else if (NS != CS_None) { + insertInfo(To, PropagationInfo(NS)); + } +} + + + void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo, const FunctionDecl *FunDecl, SourceLocation BlameLoc) { assert(!PInfo.isTest()); - - if (!FunDecl->hasAttr<CallableWhenAttr>()) - return; - + const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>(); - + if (!CWAttr) + return; + if (PInfo.isVar()) { ConsumedState VarState = StateMap->getState(PInfo.getVar()); - + if (VarState == CS_None || isCallableInState(CWAttr, VarState)) return; - + Analyzer.WarningsHandler.warnUseInInvalidState( FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(), stateToString(VarState), BlameLoc); - + } else { ConsumedState TmpState = PInfo.getAsState(StateMap); - + if (TmpState == CS_None || isCallableInState(CWAttr, TmpState)) return; - + Analyzer.WarningsHandler.warnUseOfTempInInvalidState( FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc); } } -void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) { - InfoEntry Entry = PropagationMap.find(From); - - if (Entry != PropagationMap.end()) - PropagationMap.insert(PairType(To, Entry->second)); -} -bool ConsumedStmtVisitor::isLikeMoveAssignment( - const CXXMethodDecl *MethodDecl) { - - return MethodDecl->isMoveAssignmentOperator() || - (MethodDecl->getOverloadedOperator() == OO_Equal && - MethodDecl->getNumParams() == 1 && - MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType()); +// Factors out common behavior for function, method, and operator calls. +// Check parameters and set parameter state if necessary. +// Returns true if the state of ObjArg is set, or false otherwise. +bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg, + const FunctionDecl *FunD) { + unsigned Offset = 0; + if (isa<CXXOperatorCallExpr>(Call) && isa<CXXMethodDecl>(FunD)) + Offset = 1; // first argument is 'this' + + // check explicit parameters + for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) { + // Skip variable argument lists. + if (Index - Offset >= FunD->getNumParams()) + break; + + const ParmVarDecl *Param = FunD->getParamDecl(Index - Offset); + QualType ParamType = Param->getType(); + + InfoEntry Entry = findInfo(Call->getArg(Index)); + + if (Entry == PropagationMap.end() || Entry->second.isTest()) + continue; + PropagationInfo PInfo = Entry->second; + + // Check that the parameter is in the correct state. + if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) { + ConsumedState ParamState = PInfo.getAsState(StateMap); + ConsumedState ExpectedState = mapParamTypestateAttrState(PTA); + + if (ParamState != ExpectedState) + Analyzer.WarningsHandler.warnParamTypestateMismatch( + Call->getArg(Index)->getExprLoc(), + stateToString(ExpectedState), stateToString(ParamState)); + } + + if (!(Entry->second.isVar() || Entry->second.isTmp())) + continue; + + // Adjust state on the caller side. + if (isRValueRef(ParamType)) + setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed); + else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>()) + setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT)); + else if (isPointerOrRef(ParamType) && + (!ParamType->getPointeeType().isConstQualified() || + isSetOnReadPtrType(ParamType))) + setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown); + } + + if (!ObjArg) + return false; + + // check implicit 'self' parameter, if present + InfoEntry Entry = findInfo(ObjArg); + if (Entry != PropagationMap.end()) { + PropagationInfo PInfo = Entry->second; + checkCallability(PInfo, FunD, Call->getExprLoc()); + + if (SetTypestateAttr *STA = FunD->getAttr<SetTypestateAttr>()) { + if (PInfo.isVar()) { + StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA)); + return true; + } + else if (PInfo.isTmp()) { + StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA)); + return true; + } + } + else if (isTestingFunction(FunD) && PInfo.isVar()) { + PropagationMap.insert(PairType(Call, + PropagationInfo(PInfo.getVar(), testsFor(FunD)))); + } + } + return false; } -void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call, - const FunctionDecl *Fun, - QualType ReturnType) { - if (isConsumableType(ReturnType)) { - + +void ConsumedStmtVisitor::propagateReturnType(const Expr *Call, + const FunctionDecl *Fun) { + QualType RetType = Fun->getCallResultType(); + if (RetType->isReferenceType()) + RetType = RetType->getPointeeType(); + + if (isConsumableType(RetType)) { ConsumedState ReturnState; - - if (Fun->hasAttr<ReturnTypestateAttr>()) - ReturnState = mapReturnTypestateAttrState( - Fun->getAttr<ReturnTypestateAttr>()); + if (ReturnTypestateAttr *RTA = Fun->getAttr<ReturnTypestateAttr>()) + ReturnState = mapReturnTypestateAttrState(RTA); else - ReturnState = mapConsumableAttrState(ReturnType); + ReturnState = mapConsumableAttrState(RetType); PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState))); } } + void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) { switch (BinOp->getOpcode()) { case BO_LAnd: case BO_LOr : { - InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()), - REntry = PropagationMap.find(BinOp->getRHS()); + InfoEntry LEntry = findInfo(BinOp->getLHS()), + REntry = findInfo(BinOp->getRHS()); VarTestResult LTest, RTest; @@ -576,7 +710,7 @@ void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) { LTest = LEntry->second.getVarTest(); } else { - LTest.Var = NULL; + LTest.Var = nullptr; LTest.TestsFor = CS_None; } @@ -584,11 +718,11 @@ void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) { RTest = REntry->second.getVarTest(); } else { - RTest.Var = NULL; + RTest.Var = nullptr; RTest.TestsFor = CS_None; } - - if (!(LTest.Var == NULL && RTest.Var == NULL)) + + if (!(LTest.Var == nullptr && RTest.Var == nullptr)) PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp, static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest))); @@ -605,81 +739,21 @@ void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) { } } -static bool isStdNamespace(const DeclContext *DC) { - if (!DC->isNamespace()) return false; - while (DC->getParent()->isNamespace()) - DC = DC->getParent(); - const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC); - - return ND && ND->getName() == "std" && - ND->getDeclContext()->isTranslationUnit(); -} - void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) { - if (const FunctionDecl *FunDecl = - dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) { - - // Special case for the std::move function. - // TODO: Make this more specific. (Deferred) - if (Call->getNumArgs() == 1 && - FunDecl->getNameAsString() == "move" && - isStdNamespace(FunDecl->getDeclContext())) { - forwardInfo(Call->getArg(0), Call); - return; - } - - unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams(); - - for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) { - const ParmVarDecl *Param = FunDecl->getParamDecl(Index - Offset); - QualType ParamType = Param->getType(); - - InfoEntry Entry = PropagationMap.find(Call->getArg(Index)); - - if (Entry == PropagationMap.end() || Entry->second.isTest()) - continue; - - PropagationInfo PInfo = Entry->second; - - // Check that the parameter is in the correct state. - - if (Param->hasAttr<ParamTypestateAttr>()) { - ConsumedState ParamState = PInfo.getAsState(StateMap); - - ConsumedState ExpectedState = - mapParamTypestateAttrState(Param->getAttr<ParamTypestateAttr>()); - - if (ParamState != ExpectedState) - Analyzer.WarningsHandler.warnParamTypestateMismatch( - Call->getArg(Index - Offset)->getExprLoc(), - stateToString(ExpectedState), stateToString(ParamState)); - } - - if (!(Entry->second.isVar() || Entry->second.isTmp())) - continue; - - // Adjust state on the caller side. - - if (isRValueRefish(ParamType)) { - setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed); - - } else if (Param->hasAttr<ReturnTypestateAttr>()) { - setStateForVarOrTmp(StateMap, PInfo, - mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>())); - - } else if (!isValueType(ParamType) && - !ParamType->getPointeeType().isConstQualified()) { - - setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown); - } - } - - QualType RetType = FunDecl->getCallResultType(); - if (RetType->isReferenceType()) - RetType = RetType->getPointeeType(); - - propagateReturnType(Call, FunDecl, RetType); + const FunctionDecl *FunDecl = Call->getDirectCallee(); + if (!FunDecl) + return; + + // Special case for the std::move function. + // TODO: Make this more specific. (Deferred) + if (Call->getNumArgs() == 1 && FunDecl->getNameAsString() == "move" && + FunDecl->isInStdNamespace()) { + copyInfo(Call->getArg(0), Call, CS_Consumed); + return; } + + handleCall(Call, nullptr, FunDecl); + propagateReturnType(Call, FunDecl); } void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) { @@ -689,7 +763,7 @@ void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) { void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr( const CXXBindTemporaryExpr *Temp) { - InfoEntry Entry = PropagationMap.find(Temp->getSubExpr()); + InfoEntry Entry = findInfo(Temp->getSubExpr()); if (Entry != PropagationMap.end() && !Entry->second.isTest()) { StateMap->setState(Temp, Entry->second.getAsState(StateMap)); @@ -707,168 +781,60 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { return; // FIXME: What should happen if someone annotates the move constructor? - if (Constructor->hasAttr<ReturnTypestateAttr>()) { - // TODO: Adjust state of args appropriately. - - ReturnTypestateAttr *RTAttr = Constructor->getAttr<ReturnTypestateAttr>(); - ConsumedState RetState = mapReturnTypestateAttrState(RTAttr); + if (ReturnTypestateAttr *RTA = Constructor->getAttr<ReturnTypestateAttr>()) { + // TODO: Adjust state of args appropriately. + ConsumedState RetState = mapReturnTypestateAttrState(RTA); PropagationMap.insert(PairType(Call, PropagationInfo(RetState))); - } else if (Constructor->isDefaultConstructor()) { - PropagationMap.insert(PairType(Call, PropagationInfo(consumed::CS_Consumed))); - } else if (Constructor->isMoveConstructor()) { - - InfoEntry Entry = PropagationMap.find(Call->getArg(0)); - - if (Entry != PropagationMap.end()) { - PropagationInfo PInfo = Entry->second; - - if (PInfo.isVar()) { - const VarDecl* Var = PInfo.getVar(); - - PropagationMap.insert(PairType(Call, - PropagationInfo(StateMap->getState(Var)))); - - StateMap->setState(Var, consumed::CS_Consumed); - - } else if (PInfo.isTmp()) { - const CXXBindTemporaryExpr *Tmp = PInfo.getTmp(); - - PropagationMap.insert(PairType(Call, - PropagationInfo(StateMap->getState(Tmp)))); - - StateMap->setState(Tmp, consumed::CS_Consumed); - - } else { - PropagationMap.insert(PairType(Call, PInfo)); - } - } + copyInfo(Call->getArg(0), Call, CS_Consumed); } else if (Constructor->isCopyConstructor()) { - forwardInfo(Call->getArg(0), Call); - + // Copy state from arg. If setStateOnRead then set arg to CS_Unknown. + ConsumedState NS = + isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ? + CS_Unknown : CS_None; + copyInfo(Call->getArg(0), Call, NS); } else { // TODO: Adjust state of args appropriately. - ConsumedState RetState = mapConsumableAttrState(ThisType); PropagationMap.insert(PairType(Call, PropagationInfo(RetState))); } } + void ConsumedStmtVisitor::VisitCXXMemberCallExpr( - const CXXMemberCallExpr *Call) { - - VisitCallExpr(Call); - - InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens()); - - if (Entry != PropagationMap.end()) { - PropagationInfo PInfo = Entry->second; - const CXXMethodDecl *MethodDecl = Call->getMethodDecl(); - - checkCallability(PInfo, MethodDecl, Call->getExprLoc()); - - if (PInfo.isVar()) { - if (isTestingFunction(MethodDecl)) - PropagationMap.insert(PairType(Call, - PropagationInfo(PInfo.getVar(), testsFor(MethodDecl)))); - else if (MethodDecl->hasAttr<SetTypestateAttr>()) - StateMap->setState(PInfo.getVar(), - mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>())); - } else if (PInfo.isTmp() && MethodDecl->hasAttr<SetTypestateAttr>()) { - StateMap->setState(PInfo.getTmp(), - mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>())); - } - } + const CXXMemberCallExpr *Call) { + CXXMethodDecl* MD = Call->getMethodDecl(); + if (!MD) + return; + + handleCall(Call, Call->getImplicitObjectArgument(), MD); + propagateReturnType(Call, MD); } + void ConsumedStmtVisitor::VisitCXXOperatorCallExpr( - const CXXOperatorCallExpr *Call) { - + const CXXOperatorCallExpr *Call) { + const FunctionDecl *FunDecl = dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee()); - if (!FunDecl) return; - - if (isa<CXXMethodDecl>(FunDecl) && - isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) { - - InfoEntry LEntry = PropagationMap.find(Call->getArg(0)); - InfoEntry REntry = PropagationMap.find(Call->getArg(1)); - - PropagationInfo LPInfo, RPInfo; - - if (LEntry != PropagationMap.end() && - REntry != PropagationMap.end()) { - - LPInfo = LEntry->second; - RPInfo = REntry->second; - - if (LPInfo.isPointerToValue() && RPInfo.isPointerToValue()) { - setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getAsState(StateMap)); - PropagationMap.insert(PairType(Call, LPInfo)); - setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed); - - } else if (RPInfo.isState()) { - setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getState()); - PropagationMap.insert(PairType(Call, LPInfo)); - - } else { - setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed); - } - - } else if (LEntry != PropagationMap.end() && - REntry == PropagationMap.end()) { - - LPInfo = LEntry->second; - - assert(!LPInfo.isTest()); - - if (LPInfo.isPointerToValue()) { - setStateForVarOrTmp(StateMap, LPInfo, consumed::CS_Unknown); - PropagationMap.insert(PairType(Call, LPInfo)); - - } else { - PropagationMap.insert(PairType(Call, - PropagationInfo(consumed::CS_Unknown))); - } - - } else if (LEntry == PropagationMap.end() && - REntry != PropagationMap.end()) { - - RPInfo = REntry->second; - - if (RPInfo.isPointerToValue()) - setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed); - } - - } else { - - VisitCallExpr(Call); - - InfoEntry Entry = PropagationMap.find(Call->getArg(0)); - - if (Entry != PropagationMap.end()) { - PropagationInfo PInfo = Entry->second; - - checkCallability(PInfo, FunDecl, Call->getExprLoc()); - - if (PInfo.isVar()) { - if (isTestingFunction(FunDecl)) - PropagationMap.insert(PairType(Call, - PropagationInfo(PInfo.getVar(), testsFor(FunDecl)))); - else if (FunDecl->hasAttr<SetTypestateAttr>()) - StateMap->setState(PInfo.getVar(), - mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>())); - - } else if (PInfo.isTmp() && FunDecl->hasAttr<SetTypestateAttr>()) { - StateMap->setState(PInfo.getTmp(), - mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>())); - } - } + + if (Call->getOperator() == OO_Equal) { + ConsumedState CS = getInfo(Call->getArg(1)); + if (!handleCall(Call, Call->getArg(0), FunDecl)) + setInfo(Call->getArg(0), CS); + return; } + + if (const CXXMemberCallExpr *MCall = dyn_cast<CXXMemberCallExpr>(Call)) + handleCall(MCall, MCall->getImplicitObjectArgument(), FunDecl); + else + handleCall(Call, Call->getArg(0), FunDecl); + + propagateReturnType(Call, FunDecl); } void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) { @@ -878,11 +844,9 @@ void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) { } void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) { - for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(), - DE = DeclS->decl_end(); DI != DE; ++DI) { - - if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI)); - } + for (const auto *DI : DeclS->decls()) + if (isa<VarDecl>(DI)) + VisitVarDecl(cast<VarDecl>(DI)); if (DeclS->isSingleDecl()) if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl())) @@ -904,22 +868,16 @@ void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) { QualType ParamType = Param->getType(); ConsumedState ParamState = consumed::CS_None; - if (Param->hasAttr<ParamTypestateAttr>()) { - const ParamTypestateAttr *PTAttr = Param->getAttr<ParamTypestateAttr>(); - ParamState = mapParamTypestateAttrState(PTAttr); - - } else if (isConsumableType(ParamType)) { - ParamState = mapConsumableAttrState(ParamType); - - } else if (isRValueRefish(ParamType) && - isConsumableType(ParamType->getPointeeType())) { - - ParamState = mapConsumableAttrState(ParamType->getPointeeType()); - - } else if (ParamType->isReferenceType() && - isConsumableType(ParamType->getPointeeType())) { + if (const ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) + ParamState = mapParamTypestateAttrState(PTA); + else if (isConsumableType(ParamType)) + ParamState = mapConsumableAttrState(ParamType); + else if (isRValueRef(ParamType) && + isConsumableType(ParamType->getPointeeType())) + ParamState = mapConsumableAttrState(ParamType->getPointeeType()); + else if (ParamType->isReferenceType() && + isConsumableType(ParamType->getPointeeType())) ParamState = consumed::CS_Unknown; - } if (ParamState != CS_None) StateMap->setState(Param, ParamState); @@ -929,7 +887,7 @@ void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) { ConsumedState ExpectedState = Analyzer.getExpectedReturnState(); if (ExpectedState != CS_None) { - InfoEntry Entry = PropagationMap.find(Ret->getRetValue()); + InfoEntry Entry = findInfo(Ret->getRetValue()); if (Entry != PropagationMap.end()) { ConsumedState RetState = Entry->second.getAsState(StateMap); @@ -946,7 +904,7 @@ void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) { } void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) { - InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens()); + InfoEntry Entry = findInfo(UOp->getSubExpr()); if (Entry == PropagationMap.end()) return; switch (UOp->getOpcode()) { @@ -968,8 +926,7 @@ void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) { void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) { if (isConsumableType(Var->getType())) { if (Var->hasInit()) { - MapType::iterator VIT = PropagationMap.find( - Var->getInit()->IgnoreImplicit()); + MapType::iterator VIT = findInfo(Var->getInit()->IgnoreImplicit()); if (VIT != PropagationMap.end()) { PropagationInfo PInfo = VIT->second; ConsumedState St = PInfo.getAsState(StateMap); @@ -1104,9 +1061,9 @@ void ConsumedBlockInfo::addInfo(const CFGBlock *Block, void ConsumedBlockInfo::addInfo(const CFGBlock *Block, ConsumedStateMap *StateMap) { - - assert(Block != NULL && "Block pointer must not be NULL"); - + + assert(Block && "Block pointer must not be NULL"); + ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()]; if (Entry) { @@ -1128,7 +1085,7 @@ ConsumedStateMap* ConsumedBlockInfo::borrowInfo(const CFGBlock *Block) { void ConsumedBlockInfo::discardInfo(const CFGBlock *Block) { unsigned int BlockID = Block->getBlockID(); delete StateMapsArray[BlockID]; - StateMapsArray[BlockID] = NULL; + StateMapsArray[BlockID] = nullptr; } ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) { @@ -1138,7 +1095,7 @@ ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) { if (isBackEdgeTarget(Block)) { return new ConsumedStateMap(*StateMap); } else { - StateMapsArray[Block->getBlockID()] = NULL; + StateMapsArray[Block->getBlockID()] = nullptr; return StateMap; } } @@ -1151,8 +1108,8 @@ bool ConsumedBlockInfo::isBackEdge(const CFGBlock *From, const CFGBlock *To) { } bool ConsumedBlockInfo::isBackEdgeTarget(const CFGBlock *Block) { - assert(Block != NULL && "Block pointer must not be NULL"); - + assert(Block && "Block pointer must not be NULL"); + // Anything with less than two predecessors can't be the target of a back // edge. if (Block->pred_size() < 2) @@ -1170,24 +1127,19 @@ bool ConsumedBlockInfo::isBackEdgeTarget(const CFGBlock *Block) { void ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc, ConsumedWarningsHandlerBase &WarningsHandler) const { - ConsumedState ExpectedState; - - for (VarMapType::const_iterator DMI = VarMap.begin(), DME = VarMap.end(); - DMI != DME; ++DMI) { - - if (isa<ParmVarDecl>(DMI->first)) { - const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first); + for (const auto &DM : VarMap) { + if (isa<ParmVarDecl>(DM.first)) { + const ParmVarDecl *Param = cast<ParmVarDecl>(DM.first); + const ReturnTypestateAttr *RTA = Param->getAttr<ReturnTypestateAttr>(); - if (!Param->hasAttr<ReturnTypestateAttr>()) continue; - - ExpectedState = - mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>()); + if (!RTA) + continue; - if (DMI->second != ExpectedState) { + ConsumedState ExpectedState = mapReturnTypestateAttrState(RTA); + if (DM.second != ExpectedState) WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc, Param->getNameAsString(), stateToString(ExpectedState), - stateToString(DMI->second)); - } + stateToString(DM.second)); } } } @@ -1223,16 +1175,14 @@ void ConsumedStateMap::intersect(const ConsumedStateMap *Other) { return; } - for (VarMapType::const_iterator DMI = Other->VarMap.begin(), - DME = Other->VarMap.end(); DMI != DME; ++DMI) { - - LocalState = this->getState(DMI->first); + for (const auto &DM : Other->VarMap) { + LocalState = this->getState(DM.first); if (LocalState == CS_None) continue; - if (LocalState != DMI->second) - VarMap[DMI->first] = CS_Unknown; + if (LocalState != DM.second) + VarMap[DM.first] = CS_Unknown; } } @@ -1243,18 +1193,16 @@ void ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead, ConsumedState LocalState; SourceLocation BlameLoc = getLastStmtLoc(LoopBack); - for (VarMapType::const_iterator DMI = LoopBackStates->VarMap.begin(), - DME = LoopBackStates->VarMap.end(); DMI != DME; ++DMI) { - - LocalState = this->getState(DMI->first); + for (const auto &DM : LoopBackStates->VarMap) { + LocalState = this->getState(DM.first); if (LocalState == CS_None) continue; - if (LocalState != DMI->second) { - VarMap[DMI->first] = CS_Unknown; - WarningsHandler.warnLoopStateMismatch( - BlameLoc, DMI->first->getNameAsString()); + if (LocalState != DM.second) { + VarMap[DM.first] = CS_Unknown; + WarningsHandler.warnLoopStateMismatch(BlameLoc, + DM.first->getNameAsString()); } } } @@ -1274,18 +1222,14 @@ void ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp, TmpMap[Tmp] = State; } -void ConsumedStateMap::remove(const VarDecl *Var) { - VarMap.erase(Var); +void ConsumedStateMap::remove(const CXXBindTemporaryExpr *Tmp) { + TmpMap.erase(Tmp); } bool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const { - for (VarMapType::const_iterator DMI = Other->VarMap.begin(), - DME = Other->VarMap.end(); DMI != DME; ++DMI) { - - if (this->getState(DMI->first) != DMI->second) - return true; - } - + for (const auto &DM : Other->VarMap) + if (this->getState(DM.first) != DM.second) + return true; return false; } @@ -1298,9 +1242,7 @@ void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC, } else ReturnType = D->getCallResultType(); - if (D->hasAttr<ReturnTypestateAttr>()) { - const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>(); - + if (const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>()) { const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); if (!RD || !RD->hasAttr<ConsumableAttr>()) { // FIXME: This should be removed when template instantiation propagates @@ -1312,22 +1254,27 @@ void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC, ExpectedReturnState = CS_None; } else ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr); - } else if (isConsumableType(ReturnType)) - ExpectedReturnState = mapConsumableAttrState(ReturnType); + } else if (isConsumableType(ReturnType)) { + if (isAutoCastType(ReturnType)) // We can auto-cast the state to the + ExpectedReturnState = CS_None; // expected state. + else + ExpectedReturnState = mapConsumableAttrState(ReturnType); + } else ExpectedReturnState = CS_None; } bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, const ConsumedStmtVisitor &Visitor) { - - OwningPtr<ConsumedStateMap> FalseStates(new ConsumedStateMap(*CurrStates)); + + std::unique_ptr<ConsumedStateMap> FalseStates( + new ConsumedStateMap(*CurrStates)); PropagationInfo PInfo; if (const IfStmt *IfNode = dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) { - const Stmt *Cond = IfNode->getCond(); + const Expr *Cond = IfNode->getCond(); PInfo = Visitor.getInfo(Cond); if (!PInfo.isValid() && isa<BinaryOperator>(Cond)) @@ -1396,9 +1343,9 @@ bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, delete CurrStates; if (*++SI) - BlockInfo.addInfo(*SI, FalseStates.take()); - - CurrStates = NULL; + BlockInfo.addInfo(*SI, FalseStates.release()); + + CurrStates = nullptr; return true; } @@ -1422,18 +1369,12 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { ConsumedStmtVisitor Visitor(AC, *this, CurrStates); // Add all trackable parameters to the state map. - for (FunctionDecl::param_const_iterator PI = D->param_begin(), - PE = D->param_end(); PI != PE; ++PI) { - Visitor.VisitParmVarDecl(*PI); - } + for (const auto *PI : D->params()) + Visitor.VisitParmVarDecl(PI); // Visit all of the function's basic blocks. - for (PostOrderCFGView::iterator I = SortedGraph->begin(), - E = SortedGraph->end(); I != E; ++I) { - - const CFGBlock *CurrBlock = *I; - - if (CurrStates == NULL) + for (const auto *CurrBlock : *SortedGraph) { + if (!CurrStates) CurrStates = BlockInfo.getInfo(CurrBlock); if (!CurrStates) { @@ -1441,33 +1382,32 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { } else if (!CurrStates->isReachable()) { delete CurrStates; - CurrStates = NULL; + CurrStates = nullptr; continue; } Visitor.reset(CurrStates); // Visit all of the basic block's statements. - for (CFGBlock::const_iterator BI = CurrBlock->begin(), - BE = CurrBlock->end(); BI != BE; ++BI) { - - switch (BI->getKind()) { + for (const auto &B : *CurrBlock) { + switch (B.getKind()) { case CFGElement::Statement: - Visitor.Visit(BI->castAs<CFGStmt>().getStmt()); + Visitor.Visit(B.castAs<CFGStmt>().getStmt()); break; case CFGElement::TemporaryDtor: { - const CFGTemporaryDtor DTor = BI->castAs<CFGTemporaryDtor>(); + const CFGTemporaryDtor &DTor = B.castAs<CFGTemporaryDtor>(); const CXXBindTemporaryExpr *BTE = DTor.getBindTemporaryExpr(); Visitor.checkCallability(PropagationInfo(BTE), DTor.getDestructorDecl(AC.getASTContext()), BTE->getExprLoc()); + CurrStates->remove(BTE); break; } case CFGElement::AutomaticObjectDtor: { - const CFGAutomaticObjDtor DTor = BI->castAs<CFGAutomaticObjDtor>(); + const CFGAutomaticObjDtor &DTor = B.castAs<CFGAutomaticObjDtor>(); SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd(); const VarDecl *Var = DTor.getVarDecl(); @@ -1482,13 +1422,11 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { } } - CurrStates->clearTemporaries(); - // TODO: Handle other forms of branching with precision, including while- // and for-loops. (Deferred) if (!splitState(CurrBlock, Visitor)) { - CurrStates->setSource(NULL); - + CurrStates->setSource(nullptr); + if (CurrBlock->succ_size() > 1 || (CurrBlock->succ_size() == 1 && (*CurrBlock->succ_begin())->pred_size() > 1)) { @@ -1497,9 +1435,9 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), SE = CurrBlock->succ_end(); SI != SE; ++SI) { - - if (*SI == NULL) continue; - + + if (*SI == nullptr) continue; + if (BlockInfo.isBackEdge(CurrBlock, *SI)) { BlockInfo.borrowInfo(*SI)->intersectAtLoopHead(*SI, CurrBlock, CurrStates, @@ -1514,8 +1452,8 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { if (!OwnershipTaken) delete CurrStates; - - CurrStates = NULL; + + CurrStates = nullptr; } } diff --git a/contrib/llvm/tools/clang/lib/Analysis/FormatString.cpp b/contrib/llvm/tools/clang/lib/Analysis/FormatString.cpp index 07431ac..1d6c7f7 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/FormatString.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/FormatString.cpp @@ -507,7 +507,7 @@ analyze_format_string::LengthModifier::toString() const { case None: return ""; } - return NULL; + return nullptr; } //===----------------------------------------------------------------------===// @@ -539,7 +539,7 @@ const char *ConversionSpecifier::toString() const { case nArg: return "n"; case PercentArg: return "%"; case ScanListArg: return "["; - case InvalidSpecifier: return NULL; + case InvalidSpecifier: return nullptr; // POSIX unicode extensions. case CArg: return "C"; @@ -556,7 +556,7 @@ const char *ConversionSpecifier::toString() const { // GlibC specific specifiers. case PrintErrno: return "m"; } - return NULL; + return nullptr; } Optional<ConversionSpecifier> diff --git a/contrib/llvm/tools/clang/lib/Analysis/FormatStringParsing.h b/contrib/llvm/tools/clang/lib/Analysis/FormatStringParsing.h index 6b25123..fba3180 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/FormatStringParsing.h +++ b/contrib/llvm/tools/clang/lib/Analysis/FormatStringParsing.h @@ -53,14 +53,14 @@ template <typename T> class SpecifierResult { bool Stop; public: SpecifierResult(bool stop = false) - : Start(0), Stop(stop) {} + : Start(nullptr), Stop(stop) {} SpecifierResult(const char *start, const T &fs) : FS(fs), Start(start), Stop(false) {} const char *getStart() const { return Start; } bool shouldStop() const { return Stop; } - bool hasValue() const { return Start != 0; } + bool hasValue() const { return Start != nullptr; } const T &getValue() const { assert(hasValue()); return FS; diff --git a/contrib/llvm/tools/clang/lib/Analysis/LiveVariables.cpp b/contrib/llvm/tools/clang/lib/Analysis/LiveVariables.cpp index 9e5ec55..3d6fc03 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/LiveVariables.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/LiveVariables.cpp @@ -37,7 +37,6 @@ public: POV(Ctx.getAnalysis<PostOrderCFGView>()) {} void enqueueBlock(const CFGBlock *block); - void enqueueSuccessors(const CFGBlock *block); void enqueuePredecessors(const CFGBlock *block); const CFGBlock *dequeue(); @@ -53,19 +52,6 @@ void DataflowWorklist::enqueueBlock(const clang::CFGBlock *block) { worklist.push_back(block); } } - -void DataflowWorklist::enqueueSuccessors(const clang::CFGBlock *block) { - const unsigned OldWorklistSize = worklist.size(); - for (CFGBlock::const_succ_iterator I = block->succ_begin(), - E = block->succ_end(); I != E; ++I) { - enqueueBlock(*I); - } - - if (OldWorklistSize == 0 || OldWorklistSize == worklist.size()) - return; - - sortWorklist(); -} void DataflowWorklist::enqueuePredecessors(const clang::CFGBlock *block) { const unsigned OldWorklistSize = worklist.size(); @@ -86,7 +72,7 @@ void DataflowWorklist::sortWorklist() { const CFGBlock *DataflowWorklist::dequeue() { if (worklist.empty()) - return 0; + return nullptr; const CFGBlock *b = worklist.pop_back_val(); enqueuedBlocks[b->getBlockID()] = false; return b; @@ -108,10 +94,10 @@ public: LiveVariables::LivenessValues merge(LiveVariables::LivenessValues valsA, LiveVariables::LivenessValues valsB); - - LiveVariables::LivenessValues runOnBlock(const CFGBlock *block, - LiveVariables::LivenessValues val, - LiveVariables::Observer *obs = 0); + + LiveVariables::LivenessValues + runOnBlock(const CFGBlock *block, LiveVariables::LivenessValues val, + LiveVariables::Observer *obs = nullptr); void dumpBlockLiveness(const SourceManager& M); @@ -238,8 +224,8 @@ static const VariableArrayType *FindVA(QualType Ty) { ty = VT->getElementType().getTypePtr(); } - - return 0; + + return nullptr; } static const Stmt *LookThroughStmt(const Stmt *S) { @@ -305,7 +291,7 @@ void TransferFunctions::Visit(Stmt *S) { const DeclStmt *DS = cast<DeclStmt>(S); if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) { for (const VariableArrayType* VA = FindVA(VD->getType()); - VA != 0; VA = FindVA(VA->getElementType())) { + VA != nullptr; VA = FindVA(VA->getElementType())) { AddLiveStmt(val.liveStmts, LV.SSetFact, VA->getSizeExpr()); } } @@ -372,7 +358,7 @@ void TransferFunctions::VisitBinaryOperator(BinaryOperator *B) { void TransferFunctions::VisitBlockExpr(BlockExpr *BE) { AnalysisDeclContext::referenced_decls_iterator I, E; - llvm::tie(I, E) = + std::tie(I, E) = LV.analysisContext.getReferencedBlockVars(BE->getBlockDecl()); for ( ; I != E ; ++I) { const VarDecl *VD = *I; @@ -389,9 +375,8 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *DR) { } void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { - for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE = DS->decl_end(); - DI != DE; ++DI) - if (VarDecl *VD = dyn_cast<VarDecl>(*DI)) { + for (const auto *DI : DS->decls()) + if (const auto *VD = dyn_cast<VarDecl>(DI)) { if (!isAlwaysAlive(VD)) val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); } @@ -399,8 +384,8 @@ void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *OS) { // Kill the iteration variable. - DeclRefExpr *DR = 0; - const VarDecl *VD = 0; + DeclRefExpr *DR = nullptr; + const VarDecl *VD = nullptr; Stmt *element = OS->getElement(); if (DeclStmt *DS = dyn_cast<DeclStmt>(element)) { @@ -508,12 +493,12 @@ LiveVariables::computeLiveness(AnalysisDeclContext &AC, // No CFG? Bail out. CFG *cfg = AC.getCFG(); if (!cfg) - return 0; + return nullptr; // The analysis currently has scalability issues for very large CFGs. // Bail out if it looks too large. if (cfg->getNumBlockIDs() > 300000) - return 0; + return nullptr; LiveVariablesImpl *LV = new LiveVariablesImpl(AC, killAtAssign); @@ -581,16 +566,6 @@ LiveVariables::computeLiveness(AnalysisDeclContext &AC, return new LiveVariables(LV); } -static bool compare_entries(const CFGBlock *A, const CFGBlock *B) { - return A->getBlockID() < B->getBlockID(); -} - -static bool compare_vd_entries(const Decl *A, const Decl *B) { - SourceLocation ALoc = A->getLocStart(); - SourceLocation BLoc = B->getLocStart(); - return ALoc.getRawEncoding() < BLoc.getRawEncoding(); -} - void LiveVariables::dumpBlockLiveness(const SourceManager &M) { getImpl(impl).dumpBlockLiveness(M); } @@ -602,7 +577,9 @@ void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) { it != ei; ++it) { vec.push_back(it->first); } - std::sort(vec.begin(), vec.end(), compare_entries); + std::sort(vec.begin(), vec.end(), [](const CFGBlock *A, const CFGBlock *B) { + return A->getBlockID() < B->getBlockID(); + }); std::vector<const VarDecl*> declVec; @@ -619,9 +596,11 @@ void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) { se = vals.liveDecls.end(); si != se; ++si) { declVec.push_back(*si); } - - std::sort(declVec.begin(), declVec.end(), compare_vd_entries); - + + std::sort(declVec.begin(), declVec.end(), [](const Decl *A, const Decl *B) { + return A->getLocStart() < B->getLocStart(); + }); + for (std::vector<const VarDecl*>::iterator di = declVec.begin(), de = declVec.end(); di != de; ++di) { llvm::errs() << " " << (*di)->getDeclName().getAsString() diff --git a/contrib/llvm/tools/clang/lib/Analysis/PostOrderCFGView.cpp b/contrib/llvm/tools/clang/lib/Analysis/PostOrderCFGView.cpp index cfd66f7..5a3c818 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/PostOrderCFGView.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/PostOrderCFGView.cpp @@ -31,7 +31,7 @@ PostOrderCFGView::PostOrderCFGView(const CFG *cfg) { PostOrderCFGView *PostOrderCFGView::create(AnalysisDeclContext &ctx) { const CFG *cfg = ctx.getCFG(); if (!cfg) - return 0; + return nullptr; return new PostOrderCFGView(cfg); } diff --git a/contrib/llvm/tools/clang/lib/Analysis/PrintfFormatString.cpp b/contrib/llvm/tools/clang/lib/Analysis/PrintfFormatString.cpp index 400bbf8..c68f99d 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/PrintfFormatString.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/PrintfFormatString.cpp @@ -60,7 +60,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, using namespace clang::analyze_printf; const char *I = Beg; - const char *Start = 0; + const char *Start = nullptr; UpdateOnReturn <const char*> UpdateBeg(Beg, I); // Look for a '%' character that indicates the start of a format specifier. @@ -124,7 +124,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, // Look for the field width (if any). if (ParseFieldWidth(H, FS, Start, I, E, - FS.usesPositionalArg() ? 0 : &argIndex)) + FS.usesPositionalArg() ? nullptr : &argIndex)) return true; if (I == E) { @@ -142,7 +142,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, } if (ParsePrecision(H, FS, Start, I, E, - FS.usesPositionalArg() ? 0 : &argIndex)) + FS.usesPositionalArg() ? nullptr : &argIndex)) return true; if (I == E) { diff --git a/contrib/llvm/tools/clang/lib/Analysis/ProgramPoint.cpp b/contrib/llvm/tools/clang/lib/Analysis/ProgramPoint.cpp index 7d67e8a..26b59bb 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/ProgramPoint.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/ProgramPoint.cpp @@ -43,9 +43,10 @@ ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K, } } -SimpleProgramPointTag::SimpleProgramPointTag(StringRef description) - : desc(description) {} +SimpleProgramPointTag::SimpleProgramPointTag(StringRef MsgProvider, + StringRef Msg) + : Desc((MsgProvider + " : " + Msg).str()) {} StringRef SimpleProgramPointTag::getTagDescription() const { - return desc; + return Desc; } diff --git a/contrib/llvm/tools/clang/lib/Analysis/PseudoConstantAnalysis.cpp b/contrib/llvm/tools/clang/lib/Analysis/PseudoConstantAnalysis.cpp index 5d659ce..3f96ca8 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/PseudoConstantAnalysis.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/PseudoConstantAnalysis.cpp @@ -70,7 +70,7 @@ const Decl *PseudoConstantAnalysis::getDecl(const Expr *E) { if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) return DR->getDecl(); else - return 0; + return nullptr; } void PseudoConstantAnalysis::RunAnalysis() { @@ -171,10 +171,9 @@ void PseudoConstantAnalysis::RunAnalysis() { case Stmt::DeclStmtClass: { const DeclStmt *DS = cast<DeclStmt>(Head); // Iterate over each decl and see if any of them contain reference decls - for (DeclStmt::const_decl_iterator I = DS->decl_begin(), - E = DS->decl_end(); I != E; ++I) { + for (const auto *I : DS->decls()) { // We only care about VarDecls - const VarDecl *VD = dyn_cast<VarDecl>(*I); + const VarDecl *VD = dyn_cast<VarDecl>(I); if (!VD) continue; diff --git a/contrib/llvm/tools/clang/lib/Analysis/ReachableCode.cpp b/contrib/llvm/tools/clang/lib/Analysis/ReachableCode.cpp index a2d19c0..b4a72a7 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/ReachableCode.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/ReachableCode.cpp @@ -13,10 +13,12 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/ReachableCode.h" +#include "clang/Lex/Preprocessor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/StmtCXX.h" +#include "clang/AST/ParentMap.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" #include "clang/Basic/SourceManager.h" @@ -25,36 +27,352 @@ using namespace clang; -namespace { -class DeadCodeScan { - llvm::BitVector Visited; - llvm::BitVector &Reachable; - SmallVector<const CFGBlock *, 10> WorkList; - - typedef SmallVector<std::pair<const CFGBlock *, const Stmt *>, 12> - DeferredLocsTy; - - DeferredLocsTy DeferredLocs; - -public: - DeadCodeScan(llvm::BitVector &reachable) - : Visited(reachable.size()), - Reachable(reachable) {} +//===----------------------------------------------------------------------===// +// Core Reachability Analysis routines. +//===----------------------------------------------------------------------===// + +static bool isEnumConstant(const Expr *Ex) { + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex); + if (!DR) + return false; + return isa<EnumConstantDecl>(DR->getDecl()); +} + +static bool isTrivialExpression(const Expr *Ex) { + Ex = Ex->IgnoreParenCasts(); + return isa<IntegerLiteral>(Ex) || isa<StringLiteral>(Ex) || + isa<CXXBoolLiteralExpr>(Ex) || isa<ObjCBoolLiteralExpr>(Ex) || + isa<CharacterLiteral>(Ex) || + isEnumConstant(Ex); +} + +static bool isTrivialDoWhile(const CFGBlock *B, const Stmt *S) { + // Check if the block ends with a do...while() and see if 'S' is the + // condition. + if (const Stmt *Term = B->getTerminator()) { + if (const DoStmt *DS = dyn_cast<DoStmt>(Term)) { + const Expr *Cond = DS->getCond()->IgnoreParenCasts(); + return Cond == S && isTrivialExpression(Cond); + } + } + return false; +} + +static bool isDeadReturn(const CFGBlock *B, const Stmt *S) { + // Look to see if the current control flow ends with a 'return', and see if + // 'S' is a substatement. The 'return' may not be the last element in the + // block, or may be in a subsequent block because of destructors. + const CFGBlock *Current = B; + while (true) { + for (CFGBlock::const_reverse_iterator I = Current->rbegin(), + E = Current->rend(); + I != E; ++I) { + if (Optional<CFGStmt> CS = I->getAs<CFGStmt>()) { + if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(CS->getStmt())) { + if (RS == S) + return true; + if (const Expr *RE = RS->getRetValue()) { + RE = RE->IgnoreParenCasts(); + if (RE == S) + return true; + ParentMap PM(const_cast<Expr *>(RE)); + // If 'S' is in the ParentMap, it is a subexpression of + // the return statement. + return PM.getParent(S); + } + } + break; + } + } + // Note also that we are restricting the search for the return statement + // to stop at control-flow; only part of a return statement may be dead, + // without the whole return statement being dead. + if (Current->getTerminator().isTemporaryDtorsBranch()) { + // Temporary destructors have a predictable control flow, thus we want to + // look into the next block for the return statement. + // We look into the false branch, as we know the true branch only contains + // the call to the destructor. + assert(Current->succ_size() == 2); + Current = *(Current->succ_begin() + 1); + } else if (!Current->getTerminator() && Current->succ_size() == 1) { + // If there is only one successor, we're not dealing with outgoing control + // flow. Thus, look into the next block. + Current = *Current->succ_begin(); + if (Current->pred_size() > 1) { + // If there is more than one predecessor, we're dealing with incoming + // control flow - if the return statement is in that block, it might + // well be reachable via a different control flow, thus it's not dead. + return false; + } + } else { + // We hit control flow or a dead end. Stop searching. + return false; + } + } + llvm_unreachable("Broke out of infinite loop."); +} + +static SourceLocation getTopMostMacro(SourceLocation Loc, SourceManager &SM) { + assert(Loc.isMacroID()); + SourceLocation Last; + while (Loc.isMacroID()) { + Last = Loc; + Loc = SM.getImmediateMacroCallerLoc(Loc); + } + return Last; +} + +/// Returns true if the statement is expanded from a configuration macro. +static bool isExpandedFromConfigurationMacro(const Stmt *S, + Preprocessor &PP, + bool IgnoreYES_NO = false) { + // FIXME: This is not very precise. Here we just check to see if the + // value comes from a macro, but we can do much better. This is likely + // to be over conservative. This logic is factored into a separate function + // so that we can refine it later. + SourceLocation L = S->getLocStart(); + if (L.isMacroID()) { + if (IgnoreYES_NO) { + // The Objective-C constant 'YES' and 'NO' + // are defined as macros. Do not treat them + // as configuration values. + SourceManager &SM = PP.getSourceManager(); + SourceLocation TopL = getTopMostMacro(L, SM); + StringRef MacroName = PP.getImmediateMacroName(TopL); + if (MacroName == "YES" || MacroName == "NO") + return false; + } + return true; + } + return false; +} + +static bool isConfigurationValue(const ValueDecl *D, Preprocessor &PP); + +/// Returns true if the statement represents a configuration value. +/// +/// A configuration value is something usually determined at compile-time +/// to conditionally always execute some branch. Such guards are for +/// "sometimes unreachable" code. Such code is usually not interesting +/// to report as unreachable, and may mask truly unreachable code within +/// those blocks. +static bool isConfigurationValue(const Stmt *S, + Preprocessor &PP, + SourceRange *SilenceableCondVal = nullptr, + bool IncludeIntegers = true, + bool WrappedInParens = false) { + if (!S) + return false; + + if (const Expr *Ex = dyn_cast<Expr>(S)) + S = Ex->IgnoreCasts(); + + // Special case looking for the sigil '()' around an integer literal. + if (const ParenExpr *PE = dyn_cast<ParenExpr>(S)) + if (!PE->getLocStart().isMacroID()) + return isConfigurationValue(PE->getSubExpr(), PP, SilenceableCondVal, + IncludeIntegers, true); + + if (const Expr *Ex = dyn_cast<Expr>(S)) + S = Ex->IgnoreCasts(); + + bool IgnoreYES_NO = false; + + switch (S->getStmtClass()) { + case Stmt::CallExprClass: { + const FunctionDecl *Callee = + dyn_cast_or_null<FunctionDecl>(cast<CallExpr>(S)->getCalleeDecl()); + return Callee ? Callee->isConstexpr() : false; + } + case Stmt::DeclRefExprClass: + return isConfigurationValue(cast<DeclRefExpr>(S)->getDecl(), PP); + case Stmt::ObjCBoolLiteralExprClass: + IgnoreYES_NO = true; + // Fallthrough. + case Stmt::CXXBoolLiteralExprClass: + case Stmt::IntegerLiteralClass: { + const Expr *E = cast<Expr>(S); + if (IncludeIntegers) { + if (SilenceableCondVal && !SilenceableCondVal->getBegin().isValid()) + *SilenceableCondVal = E->getSourceRange(); + return WrappedInParens || isExpandedFromConfigurationMacro(E, PP, IgnoreYES_NO); + } + return false; + } + case Stmt::MemberExprClass: + return isConfigurationValue(cast<MemberExpr>(S)->getMemberDecl(), PP); + case Stmt::UnaryExprOrTypeTraitExprClass: + return true; + case Stmt::BinaryOperatorClass: { + const BinaryOperator *B = cast<BinaryOperator>(S); + // Only include raw integers (not enums) as configuration + // values if they are used in a logical or comparison operator + // (not arithmetic). + IncludeIntegers &= (B->isLogicalOp() || B->isComparisonOp()); + return isConfigurationValue(B->getLHS(), PP, SilenceableCondVal, + IncludeIntegers) || + isConfigurationValue(B->getRHS(), PP, SilenceableCondVal, + IncludeIntegers); + } + case Stmt::UnaryOperatorClass: { + const UnaryOperator *UO = cast<UnaryOperator>(S); + if (SilenceableCondVal) + *SilenceableCondVal = UO->getSourceRange(); + return UO->getOpcode() == UO_LNot && + isConfigurationValue(UO->getSubExpr(), PP, SilenceableCondVal, + IncludeIntegers, WrappedInParens); + } + default: + return false; + } +} + +static bool isConfigurationValue(const ValueDecl *D, Preprocessor &PP) { + if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) + return isConfigurationValue(ED->getInitExpr(), PP); + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + // As a heuristic, treat globals as configuration values. Note + // that we only will get here if Sema evaluated this + // condition to a constant expression, which means the global + // had to be declared in a way to be a truly constant value. + // We could generalize this to local variables, but it isn't + // clear if those truly represent configuration values that + // gate unreachable code. + if (!VD->hasLocalStorage()) + return true; + + // As a heuristic, locals that have been marked 'const' explicitly + // can be treated as configuration values as well. + return VD->getType().isLocalConstQualified(); + } + return false; +} + +/// Returns true if we should always explore all successors of a block. +static bool shouldTreatSuccessorsAsReachable(const CFGBlock *B, + Preprocessor &PP) { + if (const Stmt *Term = B->getTerminator()) { + if (isa<SwitchStmt>(Term)) + return true; + // Specially handle '||' and '&&'. + if (isa<BinaryOperator>(Term)) { + return isConfigurationValue(Term, PP); + } + } + + const Stmt *Cond = B->getTerminatorCondition(/* stripParens */ false); + return isConfigurationValue(Cond, PP); +} + +static unsigned scanFromBlock(const CFGBlock *Start, + llvm::BitVector &Reachable, + Preprocessor *PP, + bool IncludeSometimesUnreachableEdges) { + unsigned count = 0; - void enqueue(const CFGBlock *block); - unsigned scanBackwards(const CFGBlock *Start, - clang::reachable_code::Callback &CB); + // Prep work queue + SmallVector<const CFGBlock*, 32> WL; - bool isDeadCodeRoot(const CFGBlock *Block); + // The entry block may have already been marked reachable + // by the caller. + if (!Reachable[Start->getBlockID()]) { + ++count; + Reachable[Start->getBlockID()] = true; + } - const Stmt *findDeadCode(const CFGBlock *Block); + WL.push_back(Start); - void reportDeadCode(const Stmt *S, - clang::reachable_code::Callback &CB); -}; + // Find the reachable blocks from 'Start'. + while (!WL.empty()) { + const CFGBlock *item = WL.pop_back_val(); + + // There are cases where we want to treat all successors as reachable. + // The idea is that some "sometimes unreachable" code is not interesting, + // and that we should forge ahead and explore those branches anyway. + // This allows us to potentially uncover some "always unreachable" code + // within the "sometimes unreachable" code. + // Look at the successors and mark then reachable. + Optional<bool> TreatAllSuccessorsAsReachable; + if (!IncludeSometimesUnreachableEdges) + TreatAllSuccessorsAsReachable = false; + + for (CFGBlock::const_succ_iterator I = item->succ_begin(), + E = item->succ_end(); I != E; ++I) { + const CFGBlock *B = *I; + if (!B) do { + const CFGBlock *UB = I->getPossiblyUnreachableBlock(); + if (!UB) + break; + + if (!TreatAllSuccessorsAsReachable.hasValue()) { + assert(PP); + TreatAllSuccessorsAsReachable = + shouldTreatSuccessorsAsReachable(item, *PP); + } + + if (TreatAllSuccessorsAsReachable.getValue()) { + B = UB; + break; + } + } + while (false); + + if (B) { + unsigned blockID = B->getBlockID(); + if (!Reachable[blockID]) { + Reachable.set(blockID); + WL.push_back(B); + ++count; + } + } + } + } + return count; } -void DeadCodeScan::enqueue(const CFGBlock *block) { +static unsigned scanMaybeReachableFromBlock(const CFGBlock *Start, + Preprocessor &PP, + llvm::BitVector &Reachable) { + return scanFromBlock(Start, Reachable, &PP, true); +} + +//===----------------------------------------------------------------------===// +// Dead Code Scanner. +//===----------------------------------------------------------------------===// + +namespace { + class DeadCodeScan { + llvm::BitVector Visited; + llvm::BitVector &Reachable; + SmallVector<const CFGBlock *, 10> WorkList; + Preprocessor &PP; + + typedef SmallVector<std::pair<const CFGBlock *, const Stmt *>, 12> + DeferredLocsTy; + + DeferredLocsTy DeferredLocs; + + public: + DeadCodeScan(llvm::BitVector &reachable, Preprocessor &PP) + : Visited(reachable.size()), + Reachable(reachable), + PP(PP) {} + + void enqueue(const CFGBlock *block); + unsigned scanBackwards(const CFGBlock *Start, + clang::reachable_code::Callback &CB); + + bool isDeadCodeRoot(const CFGBlock *Block); + + const Stmt *findDeadCode(const CFGBlock *Block); + + void reportDeadCode(const CFGBlock *B, + const Stmt *S, + clang::reachable_code::Callback &CB); + }; +} + +void DeadCodeScan::enqueue(const CFGBlock *block) { unsigned blockID = block->getBlockID(); if (Reachable[blockID] || Visited[blockID]) return; @@ -64,9 +382,9 @@ void DeadCodeScan::enqueue(const CFGBlock *block) { bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) { bool isDeadRoot = true; - + for (CFGBlock::const_pred_iterator I = Block->pred_begin(), - E = Block->pred_end(); I != E; ++I) { + E = Block->pred_end(); I != E; ++I) { if (const CFGBlock *PredBlock = *I) { unsigned blockID = PredBlock->getBlockID(); if (Visited[blockID]) { @@ -81,7 +399,7 @@ bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) { } } } - + return isDeadRoot; } @@ -100,14 +418,16 @@ const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) { if (isValidDeadStmt(S)) return S; } - + if (CFGTerminator T = Block->getTerminator()) { - const Stmt *S = T.getStmt(); - if (isValidDeadStmt(S)) - return S; + if (!T.isTemporaryDtorsBranch()) { + const Stmt *S = T.getStmt(); + if (isValidDeadStmt(S)) + return S; + } } - return 0; + return nullptr; } static int SrcCmp(const std::pair<const CFGBlock *, const Stmt *> *p1, @@ -124,7 +444,7 @@ unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start, unsigned count = 0; enqueue(Start); - + while (!WorkList.empty()) { const CFGBlock *Block = WorkList.pop_back_val(); @@ -135,7 +455,7 @@ unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start, // Look for any dead code within the block. const Stmt *S = findDeadCode(Block); - + if (!S) { // No dead code. Possibly an empty block. Look at dead predecessors. for (CFGBlock::const_pred_iterator I = Block->pred_begin(), @@ -145,16 +465,16 @@ unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start, } continue; } - + // Specially handle macro-expanded code. if (S->getLocStart().isMacroID()) { - count += clang::reachable_code::ScanReachableFromBlock(Block, Reachable); + count += scanMaybeReachableFromBlock(Block, PP, Reachable); continue; } if (isDeadCodeRoot(Block)) { - reportDeadCode(S, CB); - count += clang::reachable_code::ScanReachableFromBlock(Block, Reachable); + reportDeadCode(Block, S, CB); + count += scanMaybeReachableFromBlock(Block, PP, Reachable); } else { // Record this statement as the possibly best location in a @@ -169,15 +489,15 @@ unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start, if (!DeferredLocs.empty()) { llvm::array_pod_sort(DeferredLocs.begin(), DeferredLocs.end(), SrcCmp); for (DeferredLocsTy::iterator I = DeferredLocs.begin(), - E = DeferredLocs.end(); I != E; ++I) { - const CFGBlock *block = I->first; - if (Reachable[block->getBlockID()]) + E = DeferredLocs.end(); I != E; ++I) { + const CFGBlock *Block = I->first; + if (Reachable[Block->getBlockID()]) continue; - reportDeadCode(I->second, CB); - count += clang::reachable_code::ScanReachableFromBlock(block, Reachable); + reportDeadCode(Block, I->second, CB); + count += scanMaybeReachableFromBlock(Block, PP, Reachable); } } - + return count; } @@ -208,7 +528,7 @@ static SourceLocation GetUnreachableLoc(const Stmt *S, case Expr::BinaryConditionalOperatorClass: case Expr::ConditionalOperatorClass: { const AbstractConditionalOperator *CO = - cast<AbstractConditionalOperator>(S); + cast<AbstractConditionalOperator>(S); return CO->getQuestionLoc(); } case Expr::MemberExprClass: { @@ -246,61 +566,86 @@ static SourceLocation GetUnreachableLoc(const Stmt *S, return S->getLocStart(); } -void DeadCodeScan::reportDeadCode(const Stmt *S, +void DeadCodeScan::reportDeadCode(const CFGBlock *B, + const Stmt *S, clang::reachable_code::Callback &CB) { + // Classify the unreachable code found, or suppress it in some cases. + reachable_code::UnreachableKind UK = reachable_code::UK_Other; + + if (isa<BreakStmt>(S)) { + UK = reachable_code::UK_Break; + } + else if (isTrivialDoWhile(B, S)) { + return; + } + else if (isDeadReturn(B, S)) { + UK = reachable_code::UK_Return; + } + + SourceRange SilenceableCondVal; + + if (UK == reachable_code::UK_Other) { + // Check if the dead code is part of the "loop target" of + // a for/for-range loop. This is the block that contains + // the increment code. + if (const Stmt *LoopTarget = B->getLoopTarget()) { + SourceLocation Loc = LoopTarget->getLocStart(); + SourceRange R1(Loc, Loc), R2; + + if (const ForStmt *FS = dyn_cast<ForStmt>(LoopTarget)) { + const Expr *Inc = FS->getInc(); + Loc = Inc->getLocStart(); + R2 = Inc->getSourceRange(); + } + + CB.HandleUnreachable(reachable_code::UK_Loop_Increment, + Loc, SourceRange(), SourceRange(Loc, Loc), R2); + return; + } + + // Check if the dead block has a predecessor whose branch has + // a configuration value that *could* be modified to + // silence the warning. + CFGBlock::const_pred_iterator PI = B->pred_begin(); + if (PI != B->pred_end()) { + if (const CFGBlock *PredBlock = PI->getPossiblyUnreachableBlock()) { + const Stmt *TermCond = + PredBlock->getTerminatorCondition(/* strip parens */ false); + isConfigurationValue(TermCond, PP, &SilenceableCondVal); + } + } + } + SourceRange R1, R2; SourceLocation Loc = GetUnreachableLoc(S, R1, R2); - CB.HandleUnreachable(Loc, R1, R2); + CB.HandleUnreachable(UK, Loc, SilenceableCondVal, R1, R2); } +//===----------------------------------------------------------------------===// +// Reachability APIs. +//===----------------------------------------------------------------------===// + namespace clang { namespace reachable_code { -void Callback::anchor() { } +void Callback::anchor() { } unsigned ScanReachableFromBlock(const CFGBlock *Start, llvm::BitVector &Reachable) { - unsigned count = 0; - - // Prep work queue - SmallVector<const CFGBlock*, 32> WL; - - // The entry block may have already been marked reachable - // by the caller. - if (!Reachable[Start->getBlockID()]) { - ++count; - Reachable[Start->getBlockID()] = true; - } - - WL.push_back(Start); - - // Find the reachable blocks from 'Start'. - while (!WL.empty()) { - const CFGBlock *item = WL.pop_back_val(); - - // Look at the successors and mark then reachable. - for (CFGBlock::const_succ_iterator I = item->succ_begin(), - E = item->succ_end(); I != E; ++I) - if (const CFGBlock *B = *I) { - unsigned blockID = B->getBlockID(); - if (!Reachable[blockID]) { - Reachable.set(blockID); - WL.push_back(B); - ++count; - } - } - } - return count; + return scanFromBlock(Start, Reachable, /* SourceManager* */ nullptr, false); } - -void FindUnreachableCode(AnalysisDeclContext &AC, Callback &CB) { + +void FindUnreachableCode(AnalysisDeclContext &AC, Preprocessor &PP, + Callback &CB) { + CFG *cfg = AC.getCFG(); if (!cfg) return; - // Scan for reachable blocks from the entrance of the CFG. + // Scan for reachable blocks from the entrance of the CFG. // If there are no unreachable blocks, we're done. llvm::BitVector reachable(cfg->getNumBlockIDs()); - unsigned numReachable = ScanReachableFromBlock(&cfg->getEntry(), reachable); + unsigned numReachable = + scanMaybeReachableFromBlock(&cfg->getEntry(), PP, reachable); if (numReachable == cfg->getNumBlockIDs()) return; @@ -309,7 +654,7 @@ void FindUnreachableCode(AnalysisDeclContext &AC, Callback &CB) { if (!AC.getCFGBuildOptions().AddEHEdges) { for (CFG::try_block_iterator I = cfg->try_blocks_begin(), E = cfg->try_blocks_end() ; I != E; ++I) { - numReachable += ScanReachableFromBlock(*I, reachable); + numReachable += scanMaybeReachableFromBlock(*I, PP, reachable); } if (numReachable == cfg->getNumBlockIDs()) return; @@ -323,7 +668,7 @@ void FindUnreachableCode(AnalysisDeclContext &AC, Callback &CB) { if (reachable[block->getBlockID()]) continue; - DeadCodeScan DS(reachable); + DeadCodeScan DS(reachable, PP); numReachable += DS.scanBackwards(block, CB); if (numReachable == cfg->getNumBlockIDs()) diff --git a/contrib/llvm/tools/clang/lib/Analysis/ScanfFormatString.cpp b/contrib/llvm/tools/clang/lib/Analysis/ScanfFormatString.cpp index f5ce84f..ed28627 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/ScanfFormatString.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/ScanfFormatString.cpp @@ -50,6 +50,15 @@ static bool ParseScanList(FormatStringHandler &H, } } + // Special case: "^]" are the first characters. + if (I + 1 != E && I[0] == '^' && I[1] == ']') { + I += 2; + if (I == E) { + H.HandleIncompleteScanList(start, I - 1); + return true; + } + } + // Look for a ']' character which denotes the end of the scan list. while (*I != ']') { if (++I == E) { @@ -73,7 +82,7 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, using namespace clang::analyze_scanf; const char *I = Beg; - const char *Start = 0; + const char *Start = nullptr; UpdateOnReturn <const char*> UpdateBeg(Beg, I); // Look for a '%' character that indicates the start of a format specifier. @@ -379,21 +388,23 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { return ArgType(); } -bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, +bool ScanfSpecifier::fixType(QualType QT, QualType RawQT, + const LangOptions &LangOpt, ASTContext &Ctx) { - if (!QT->isPointerType()) - return false; // %n is different from other conversion specifiers; don't try to fix it. if (CS.getKind() == ConversionSpecifier::nArg) return false; + if (!QT->isPointerType()) + return false; + QualType PT = QT->getPointeeType(); // If it's an enum, get its underlying type. - if (const EnumType *ETy = QT->getAs<EnumType>()) - QT = ETy->getDecl()->getIntegerType(); - + if (const EnumType *ETy = PT->getAs<EnumType>()) + PT = ETy->getDecl()->getIntegerType(); + const BuiltinType *BT = PT->getAs<BuiltinType>(); if (!BT) return false; @@ -405,6 +416,15 @@ bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, LM.setKind(LengthModifier::AsWideChar); else LM.setKind(LengthModifier::None); + + // If we know the target array length, we can use it as a field width. + if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(RawQT)) { + if (CAT->getSizeModifier() == ArrayType::Normal) + FieldWidth = OptionalAmount(OptionalAmount::Constant, + CAT->getSize().getZExtValue() - 1, + "", 0, false); + + } return true; } diff --git a/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp index 6e0e173..11df61f 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp @@ -10,18 +10,22 @@ // A intra-procedural analysis for thread safety (e.g. deadlocks and race // conditions), based off of an annotation system. // -// See http://clang.llvm.org/docs/LanguageExtensions.html#thread-safety-annotation-checking +// See http://clang.llvm.org/docs/ThreadSafetyAnalysis.html // for more information. // //===----------------------------------------------------------------------===// -#include "clang/Analysis/Analyses/ThreadSafety.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/Analyses/PostOrderCFGView.h" +#include "clang/Analysis/Analyses/ThreadSafety.h" +#include "clang/Analysis/Analyses/ThreadSafetyLogical.h" +#include "clang/Analysis/Analyses/ThreadSafetyTIL.h" +#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h" +#include "clang/Analysis/Analyses/ThreadSafetyCommon.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" @@ -172,12 +176,9 @@ private: const Expr* const* FunArgs; // Function arguments CallingContext* PrevCtx; // The previous context; or 0 if none. - CallingContext(const NamedDecl *D = 0, const Expr *S = 0, - unsigned N = 0, const Expr* const *A = 0, - CallingContext *P = 0) - : AttrDecl(D), SelfArg(S), SelfArrow(false), - NumArgs(N), FunArgs(A), PrevCtx(P) - { } + CallingContext(const NamedDecl *D) + : AttrDecl(D), SelfArg(nullptr), SelfArrow(false), NumArgs(0), + FunArgs(nullptr), PrevCtx(nullptr) {} }; typedef SmallVector<SExprNode, 4> NodeVector; @@ -188,44 +189,41 @@ private: NodeVector NodeVec; private: + unsigned make(ExprOp O, unsigned F = 0, const void *D = nullptr) { + NodeVec.push_back(SExprNode(O, F, D)); + return NodeVec.size() - 1; + } + unsigned makeNop() { - NodeVec.push_back(SExprNode(EOP_Nop, 0, 0)); - return NodeVec.size()-1; + return make(EOP_Nop); } unsigned makeWildcard() { - NodeVec.push_back(SExprNode(EOP_Wildcard, 0, 0)); - return NodeVec.size()-1; + return make(EOP_Wildcard); } unsigned makeUniversal() { - NodeVec.push_back(SExprNode(EOP_Universal, 0, 0)); - return NodeVec.size()-1; + return make(EOP_Universal); } unsigned makeNamedVar(const NamedDecl *D) { - NodeVec.push_back(SExprNode(EOP_NVar, 0, D)); - return NodeVec.size()-1; + return make(EOP_NVar, 0, D); } unsigned makeLocalVar(const NamedDecl *D) { - NodeVec.push_back(SExprNode(EOP_LVar, 0, D)); - return NodeVec.size()-1; + return make(EOP_LVar, 0, D); } unsigned makeThis() { - NodeVec.push_back(SExprNode(EOP_This, 0, 0)); - return NodeVec.size()-1; + return make(EOP_This); } unsigned makeDot(const NamedDecl *D, bool Arrow) { - NodeVec.push_back(SExprNode(EOP_Dot, Arrow ? 1 : 0, D)); - return NodeVec.size()-1; + return make(EOP_Dot, Arrow ? 1 : 0, D); } unsigned makeCall(unsigned NumArgs, const NamedDecl *D) { - NodeVec.push_back(SExprNode(EOP_Call, NumArgs, D)); - return NodeVec.size()-1; + return make(EOP_Call, NumArgs, D); } // Grab the very first declaration of virtual method D @@ -238,32 +236,27 @@ private: return D; // Method does not override anything D = *I; // FIXME: this does not work with multiple inheritance. } - return 0; + return nullptr; } unsigned makeMCall(unsigned NumArgs, const CXXMethodDecl *D) { - NodeVec.push_back(SExprNode(EOP_MCall, NumArgs, getFirstVirtualDecl(D))); - return NodeVec.size()-1; + return make(EOP_MCall, NumArgs, getFirstVirtualDecl(D)); } unsigned makeIndex() { - NodeVec.push_back(SExprNode(EOP_Index, 0, 0)); - return NodeVec.size()-1; + return make(EOP_Index); } unsigned makeUnary() { - NodeVec.push_back(SExprNode(EOP_Unary, 0, 0)); - return NodeVec.size()-1; + return make(EOP_Unary); } unsigned makeBinary() { - NodeVec.push_back(SExprNode(EOP_Binary, 0, 0)); - return NodeVec.size()-1; + return make(EOP_Binary); } unsigned makeUnknown(unsigned Arity) { - NodeVec.push_back(SExprNode(EOP_Unknown, Arity, 0)); - return NodeVec.size()-1; + return make(EOP_Unknown, Arity); } inline bool isCalleeArrow(const Expr *E) { @@ -277,10 +270,10 @@ private: /// ensure that the original expression is a valid mutex expression. /// /// NDeref returns the number of Derefence and AddressOf operations - /// preceeding the Expr; this is used to decide whether to pretty-print + /// preceding the Expr; this is used to decide whether to pretty-print /// SExprs with . or ->. - unsigned buildSExpr(const Expr *Exp, CallingContext* CallCtx, - int* NDeref = 0) { + unsigned buildSExpr(const Expr *Exp, CallingContext *CallCtx, + int *NDeref = nullptr) { if (!Exp) return 0; @@ -377,7 +370,7 @@ private: } } unsigned NumCallArgs = CE->getNumArgs(); - unsigned Root = makeCall(NumCallArgs, 0); + unsigned Root = makeCall(NumCallArgs, nullptr); unsigned Sz = buildSExpr(CE->getCallee(), CallCtx); const Expr* const* CallArgs = CE->getArgs(); for (unsigned i = 0; i < NumCallArgs; ++i) { @@ -470,7 +463,7 @@ private: /// occurs. /// \param D The declaration to which the lock/unlock attribute is attached. void buildSExprFromExpr(const Expr *MutexExp, const Expr *DeclExp, - const NamedDecl *D, VarDecl *SelfDecl = 0) { + const NamedDecl *D, VarDecl *SelfDecl = nullptr) { CallingContext CallCtx(D); if (MutexExp) { @@ -487,8 +480,8 @@ private: } // If we are processing a raw attribute expression, with no substitutions. - if (DeclExp == 0) { - buildSExpr(MutexExp, 0); + if (!DeclExp) { + buildSExpr(MutexExp, nullptr); return; } @@ -508,7 +501,7 @@ private: CallCtx.FunArgs = CE->getArgs(); } else if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(DeclExp)) { - CallCtx.SelfArg = 0; // Will be set below + CallCtx.SelfArg = nullptr; // Will be set below CallCtx.NumArgs = CE->getNumArgs(); CallCtx.FunArgs = CE->getArgs(); } else if (D && isa<CXXDestructorDecl>(D)) { @@ -524,16 +517,16 @@ private: CallCtx.SelfArg = &SelfDRE; // If the attribute has no arguments, then assume the argument is "this". - if (MutexExp == 0) - buildSExpr(CallCtx.SelfArg, 0); + if (!MutexExp) + buildSExpr(CallCtx.SelfArg, nullptr); else // For most attributes. buildSExpr(MutexExp, &CallCtx); return; } // If the attribute has no arguments, then assume the argument is "this". - if (MutexExp == 0) - buildSExpr(CallCtx.SelfArg, 0); + if (!MutexExp) + buildSExpr(CallCtx.SelfArg, nullptr); else // For most attributes. buildSExpr(MutexExp, &CallCtx); } @@ -551,8 +544,8 @@ public: /// occurs. /// \param D The declaration to which the lock/unlock attribute is attached. /// Caller must check isValid() after construction. - SExpr(const Expr* MutexExp, const Expr *DeclExp, const NamedDecl* D, - VarDecl *SelfDecl=0) { + SExpr(const Expr *MutexExp, const Expr *DeclExp, const NamedDecl *D, + VarDecl *SelfDecl = nullptr) { buildSExprFromExpr(MutexExp, DeclExp, D, SelfDecl); } @@ -575,15 +568,15 @@ public: /// Issue a warning about an invalid lock expression static void warnInvalidLock(ThreadSafetyHandler &Handler, - const Expr *MutexExp, - const Expr *DeclExp, const NamedDecl* D) { + const Expr *MutexExp, const Expr *DeclExp, + const NamedDecl *D, StringRef Kind) { SourceLocation Loc; if (DeclExp) Loc = DeclExp->getExprLoc(); // FIXME: add a note about the attribute location in MutexExp or D if (Loc.isValid()) - Handler.handleInvalidLockExp(Loc); + Handler.handleInvalidLockExp(Kind, Loc); } bool operator==(const SExpr &other) const { @@ -716,27 +709,16 @@ public: } }; - - /// \brief A short list of SExprs class MutexIDList : public SmallVector<SExpr, 3> { public: - /// \brief Return true if the list contains the specified SExpr - /// Performs a linear search, because these lists are almost always very small. - bool contains(const SExpr& M) { - for (iterator I=begin(),E=end(); I != E; ++I) - if ((*I) == M) return true; - return false; - } - - /// \brief Push M onto list, bud discard duplicates + /// \brief Push M onto list, but discard duplicates. void push_back_nodup(const SExpr& M) { - if (!contains(M)) push_back(M); + if (end() == std::find(begin(), end(), M)) + push_back(M); } }; - - /// \brief This is a helper class that stores info about the most recent /// accquire of a Lock. /// @@ -867,44 +849,37 @@ public: return false; } - // Returns an iterator iterator findLockIter(FactManager &FM, const SExpr &M) { - for (iterator I = begin(), E = end(); I != E; ++I) { - const SExpr &Exp = FM[*I].MutID; - if (Exp.matches(M)) - return I; - } - return end(); + return std::find_if(begin(), end(), [&](FactID ID) { + return FM[ID].MutID.matches(M); + }); } - LockData* findLock(FactManager &FM, const SExpr &M) const { - for (const_iterator I = begin(), E = end(); I != E; ++I) { - const SExpr &Exp = FM[*I].MutID; - if (Exp.matches(M)) - return &FM[*I].LDat; - } - return 0; - } + LockData *findLock(FactManager &FM, const SExpr &M) const { + auto I = std::find_if(begin(), end(), [&](FactID ID) { + return FM[ID].MutID.matches(M); + }); - LockData* findLockUniv(FactManager &FM, const SExpr &M) const { - for (const_iterator I = begin(), E = end(); I != E; ++I) { - const SExpr &Exp = FM[*I].MutID; - if (Exp.matches(M) || Exp.isUniversal()) - return &FM[*I].LDat; - } - return 0; + return I != end() ? &FM[*I].LDat : nullptr; } - FactEntry* findPartialMatch(FactManager &FM, const SExpr &M) const { - for (const_iterator I=begin(), E=end(); I != E; ++I) { - const SExpr& Exp = FM[*I].MutID; - if (Exp.partiallyMatches(M)) return &FM[*I]; - } - return 0; + LockData *findLockUniv(FactManager &FM, const SExpr &M) const { + auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool { + const SExpr &Expr = FM[ID].MutID; + return Expr.isUniversal() || Expr.matches(M); + }); + + return I != end() ? &FM[*I].LDat : nullptr; } -}; + FactEntry *findPartialMatch(FactManager &FM, const SExpr &M) const { + auto I = std::find_if(begin(), end(), [&](FactID ID) { + return FM[ID].MutID.partiallyMatches(M); + }); + return I != end() ? &FM[*I] : nullptr; + } +}; /// A Lockset maps each SExpr (defined above) to information about how it has /// been locked. @@ -987,7 +962,7 @@ public: // Create reference to previous definition VarDefinition(const NamedDecl *D, unsigned R, Context C) - : Dec(D), Exp(0), Ref(R), Ctx(C) + : Dec(D), Exp(nullptr), Ref(R), Ctx(C) { } }; @@ -1000,14 +975,14 @@ private: public: LocalVariableMap() { // index 0 is a placeholder for undefined variables (aka phi-nodes). - VarDefinitions.push_back(VarDefinition(0, 0u, getEmptyContext())); + VarDefinitions.push_back(VarDefinition(nullptr, 0u, getEmptyContext())); } /// Look up a definition, within the given context. const VarDefinition* lookup(const NamedDecl *D, Context Ctx) { const unsigned *i = Ctx.lookup(D); if (!i) - return 0; + return nullptr; assert(*i < VarDefinitions.size()); return &VarDefinitions[*i]; } @@ -1018,7 +993,7 @@ public: const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) { const unsigned *P = Ctx.lookup(D); if (!P) - return 0; + return nullptr; unsigned i = *P; while (i > 0) { @@ -1028,7 +1003,7 @@ public: } i = VarDefinitions[i].Ref; } - return 0; + return nullptr; } Context getEmptyContext() { return ContextFactory.getEmptyMap(); } @@ -1088,8 +1063,8 @@ public: } /// Builds the variable map. - void traverseCFG(CFG *CFGraph, PostOrderCFGView *SortedGraph, - std::vector<CFGBlockInfo> &BlockInfo); + void traverseCFG(CFG *CFGraph, const PostOrderCFGView *SortedGraph, + std::vector<CFGBlockInfo> &BlockInfo); protected: // Get the current context index @@ -1102,7 +1077,7 @@ protected: // Adds a new definition to the given context, and returns a new context. // This method should be called when declaring a new variable. - Context addDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) { + Context addDefinition(const NamedDecl *D, const Expr *Exp, Context Ctx) { assert(!Ctx.contains(D)); unsigned newID = VarDefinitions.size(); Context NewCtx = ContextFactory.add(Ctx, D, newID); @@ -1183,9 +1158,9 @@ public: void VarMapBuilder::VisitDeclStmt(DeclStmt *S) { bool modifiedCtx = false; DeclGroupRef DGrp = S->getDeclGroup(); - for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) { - if (VarDecl *VD = dyn_cast_or_null<VarDecl>(*I)) { - Expr *E = VD->getInit(); + for (const auto *D : DGrp) { + if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) { + const Expr *E = VD->getInit(); // Add local variables with trivial type to the variable map QualType T = VD->getType(); @@ -1227,13 +1202,12 @@ void VarMapBuilder::VisitBinaryOperator(BinaryOperator *BO) { LocalVariableMap::Context LocalVariableMap::intersectContexts(Context C1, Context C2) { Context Result = C1; - for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) { - const NamedDecl *Dec = I.getKey(); - unsigned i1 = I.getData(); + for (const auto &P : C1) { + const NamedDecl *Dec = P.first; const unsigned *i2 = C2.lookup(Dec); if (!i2) // variable doesn't exist on second path Result = removeDefinition(Dec, Result); - else if (*i2 != i1) // variable exists, but has different definition + else if (*i2 != P.second) // variable exists, but has different definition Result = clearDefinition(Dec, Result); } return Result; @@ -1244,11 +1218,8 @@ LocalVariableMap::intersectContexts(Context C1, Context C2) { // (We use this for a naive implementation of SSA on loop back-edges.) LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) { Context Result = getEmptyContext(); - for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) { - const NamedDecl *Dec = I.getKey(); - unsigned i = I.getData(); - Result = addReference(Dec, i, Result); - } + for (const auto &P : C) + Result = addReference(P.first, P.second, Result); return Result; } @@ -1256,13 +1227,12 @@ LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) { // altering the VarDefinitions. C1 must be the result of an earlier call to // createReferenceContext. void LocalVariableMap::intersectBackEdge(Context C1, Context C2) { - for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) { - const NamedDecl *Dec = I.getKey(); - unsigned i1 = I.getData(); + for (const auto &P : C1) { + unsigned i1 = P.second; VarDefinition *VDef = &VarDefinitions[i1]; assert(VDef->isReference()); - const unsigned *i2 = C2.lookup(Dec); + const unsigned *i2 = C2.lookup(P.first); if (!i2 || (*i2 != i1)) VDef->Ref = 0; // Mark this variable as undefined } @@ -1308,15 +1278,13 @@ void LocalVariableMap::intersectBackEdge(Context C1, Context C2) { // ... { y -> y1 | x3 = 2, x2 = 1, ... } // void LocalVariableMap::traverseCFG(CFG *CFGraph, - PostOrderCFGView *SortedGraph, + const PostOrderCFGView *SortedGraph, std::vector<CFGBlockInfo> &BlockInfo) { PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph); CtxIndices.resize(CFGraph->getNumBlockIDs()); - for (PostOrderCFGView::iterator I = SortedGraph->begin(), - E = SortedGraph->end(); I!= E; ++I) { - const CFGBlock *CurrBlock = *I; + for (const auto *CurrBlock : *SortedGraph) { int CurrBlockID = CurrBlock->getBlockID(); CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID]; @@ -1328,7 +1296,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph, for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(), PE = CurrBlock->pred_end(); PI != PE; ++PI) { // if *PI -> CurrBlock is a back edge, so skip it - if (*PI == 0 || !VisitedBlocks.alreadySet(*PI)) { + if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI)) { HasBackEdges = true; continue; } @@ -1354,7 +1322,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph, createReferenceContext(CurrBlockInfo->EntryContext); // Create a starting context index for the current block - saveContext(0, CurrBlockInfo->EntryContext); + saveContext(nullptr, CurrBlockInfo->EntryContext); CurrBlockInfo->EntryIndex = getContextIndex(); // Visit all the statements in the basic block. @@ -1377,7 +1345,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph, for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), SE = CurrBlock->succ_end(); SI != SE; ++SI) { // if CurrBlock -> *SI is *not* a back edge - if (*SI == 0 || !VisitedBlocks.alreadySet(*SI)) + if (*SI == nullptr || !VisitedBlocks.alreadySet(*SI)) continue; CFGBlock *FirstLoopBlock = *SI; @@ -1389,17 +1357,15 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph, // Put an extra entry at the end of the indexed context array unsigned exitID = CFGraph->getExit().getBlockID(); - saveContext(0, BlockInfo[exitID].ExitContext); + saveContext(nullptr, BlockInfo[exitID].ExitContext); } /// Find the appropriate source locations to use when producing diagnostics for /// each block in the CFG. static void findBlockLocations(CFG *CFGraph, - PostOrderCFGView *SortedGraph, + const PostOrderCFGView *SortedGraph, std::vector<CFGBlockInfo> &BlockInfo) { - for (PostOrderCFGView::iterator I = SortedGraph->begin(), - E = SortedGraph->end(); I!= E; ++I) { - const CFGBlock *CurrBlock = *I; + for (const auto *CurrBlock : *SortedGraph) { CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()]; // Find the source location of the last statement in the block, if the @@ -1450,13 +1416,14 @@ class ThreadSafetyAnalyzer { public: ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {} - void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat); - void removeLock(FactSet &FSet, const SExpr &Mutex, - SourceLocation UnlockLoc, bool FullyRemove=false); + void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat, + StringRef DiagKind); + void removeLock(FactSet &FSet, const SExpr &Mutex, SourceLocation UnlockLoc, + bool FullyRemove, LockKind Kind, StringRef DiagKind); template <typename AttrType> void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp, - const NamedDecl *D, VarDecl *SelfDecl=0); + const NamedDecl *D, VarDecl *SelfDecl = nullptr); template <class AttrType> void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp, @@ -1485,12 +1452,89 @@ public: void runAnalysis(AnalysisDeclContext &AC); }; +/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs. +static const ValueDecl *getValueDecl(const Expr *Exp) { + if (const auto *CE = dyn_cast<ImplicitCastExpr>(Exp)) + return getValueDecl(CE->getSubExpr()); + + if (const auto *DR = dyn_cast<DeclRefExpr>(Exp)) + return DR->getDecl(); + + if (const auto *ME = dyn_cast<MemberExpr>(Exp)) + return ME->getMemberDecl(); + + return nullptr; +} + +template <typename Ty> +class has_arg_iterator_range { + typedef char yes[1]; + typedef char no[2]; + + template <typename Inner> + static yes& test(Inner *I, decltype(I->args()) * = nullptr); + + template <typename> + static no& test(...); + +public: + static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes); +}; + +static StringRef ClassifyDiagnostic(const CapabilityAttr *A) { + return A->getName(); +} + +static StringRef ClassifyDiagnostic(QualType VDT) { + // We need to look at the declaration of the type of the value to determine + // which it is. The type should either be a record or a typedef, or a pointer + // or reference thereof. + if (const auto *RT = VDT->getAs<RecordType>()) { + if (const auto *RD = RT->getDecl()) + if (const auto *CA = RD->getAttr<CapabilityAttr>()) + return ClassifyDiagnostic(CA); + } else if (const auto *TT = VDT->getAs<TypedefType>()) { + if (const auto *TD = TT->getDecl()) + if (const auto *CA = TD->getAttr<CapabilityAttr>()) + return ClassifyDiagnostic(CA); + } else if (VDT->isPointerType() || VDT->isReferenceType()) + return ClassifyDiagnostic(VDT->getPointeeType()); + + return "mutex"; +} + +static StringRef ClassifyDiagnostic(const ValueDecl *VD) { + assert(VD && "No ValueDecl passed"); + + // The ValueDecl is the declaration of a mutex or role (hopefully). + return ClassifyDiagnostic(VD->getType()); +} + +template <typename AttrTy> +static typename std::enable_if<!has_arg_iterator_range<AttrTy>::value, + StringRef>::type +ClassifyDiagnostic(const AttrTy *A) { + if (const ValueDecl *VD = getValueDecl(A->getArg())) + return ClassifyDiagnostic(VD); + return "mutex"; +} + +template <typename AttrTy> +static typename std::enable_if<has_arg_iterator_range<AttrTy>::value, + StringRef>::type +ClassifyDiagnostic(const AttrTy *A) { + for (const auto *Arg : A->args()) { + if (const ValueDecl *VD = getValueDecl(Arg)) + return ClassifyDiagnostic(VD); + } + return "mutex"; +} /// \brief Add a new lock to the lockset, warning if the lock is already there. /// \param Mutex -- the Mutex expression for the lock /// \param LDat -- the LockData for the lock void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex, - const LockData &LDat) { + const LockData &LDat, StringRef DiagKind) { // FIXME: deal with acquired before/after annotations. // FIXME: Don't always warn when we have support for reentrant locks. if (Mutex.shouldIgnore()) @@ -1498,7 +1542,7 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex, if (FSet.findLock(FactMan, Mutex)) { if (!LDat.Asserted) - Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc); + Handler.handleDoubleLock(DiagKind, Mutex.toString(), LDat.AcquireLoc); } else { FSet.addLock(FactMan, Mutex, LDat); } @@ -1508,16 +1552,24 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex, /// \brief Remove a lock from the lockset, warning if the lock is not there. /// \param Mutex The lock expression corresponding to the lock to be removed /// \param UnlockLoc The source location of the unlock (only used in error msg) -void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, - const SExpr &Mutex, +void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const SExpr &Mutex, SourceLocation UnlockLoc, - bool FullyRemove) { + bool FullyRemove, LockKind ReceivedKind, + StringRef DiagKind) { if (Mutex.shouldIgnore()) return; const LockData *LDat = FSet.findLock(FactMan, Mutex); if (!LDat) { - Handler.handleUnmatchedUnlock(Mutex.toString(), UnlockLoc); + Handler.handleUnmatchedUnlock(DiagKind, Mutex.toString(), UnlockLoc); + return; + } + + // Generic lock removal doesn't care about lock kind mismatches, but + // otherwise diagnose when the lock kinds are mismatched. + if (ReceivedKind != LK_Generic && LDat->LKind != ReceivedKind) { + Handler.handleIncorrectUnlockKind(DiagKind, Mutex.toString(), LDat->LKind, + ReceivedKind, UnlockLoc); return; } @@ -1532,8 +1584,8 @@ void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, // We're releasing the underlying mutex, but not destroying the // managing object. Warn on dual release. if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) { - Handler.handleUnmatchedUnlock(LDat->UnderlyingMutex.toString(), - UnlockLoc); + Handler.handleUnmatchedUnlock( + DiagKind, LDat->UnderlyingMutex.toString(), UnlockLoc); } FSet.removeLock(FactMan, LDat->UnderlyingMutex); return; @@ -1549,22 +1601,21 @@ template <typename AttrType> void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp, const NamedDecl *D, VarDecl *SelfDecl) { - typedef typename AttrType::args_iterator iterator_type; - if (Attr->args_size() == 0) { // The mutex held is the "this" object. - SExpr Mu(0, Exp, D, SelfDecl); + SExpr Mu(nullptr, Exp, D, SelfDecl); if (!Mu.isValid()) - SExpr::warnInvalidLock(Handler, 0, Exp, D); + SExpr::warnInvalidLock(Handler, nullptr, Exp, D, + ClassifyDiagnostic(Attr)); else Mtxs.push_back_nodup(Mu); return; } - for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) { - SExpr Mu(*I, Exp, D, SelfDecl); + for (const auto *Arg : Attr->args()) { + SExpr Mu(Arg, Exp, D, SelfDecl); if (!Mu.isValid()) - SExpr::warnInvalidLock(Handler, *I, Exp, D); + SExpr::warnInvalidLock(Handler, Arg, Exp, D, ClassifyDiagnostic(Attr)); else Mtxs.push_back_nodup(Mu); } @@ -1581,23 +1632,22 @@ void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, const CFGBlock *CurrBlock, Expr *BrE, bool Neg) { // Find out which branch has the lock - bool branch = 0; - if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) { + bool branch = false; + if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) branch = BLE->getValue(); - } - else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) { + else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) branch = ILE->getValue().getBoolValue(); - } + int branchnum = branch ? 0 : 1; - if (Neg) branchnum = !branchnum; + if (Neg) + branchnum = !branchnum; // If we've taken the trylock branch, then add the lock int i = 0; for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(), SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) { - if (*SI == CurrBlock && i == branchnum) { + if (*SI == CurrBlock && i == branchnum) getMutexIDs(Mtxs, Attr, Exp, D); - } } } @@ -1626,7 +1676,7 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, LocalVarContext C, bool &Negate) { if (!Cond) - return 0; + return nullptr; if (const CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) { return CallExp; @@ -1649,7 +1699,7 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, Negate = !Negate; return getTrylockCallExpr(UOP->getSubExpr(), C, Negate); } - return 0; + return nullptr; } else if (const BinaryOperator *BOP = dyn_cast<BinaryOperator>(Cond)) { if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) { @@ -1666,7 +1716,7 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, if (!TCond) Negate = !Negate; return getTrylockCallExpr(BOP->getRHS(), C, Negate); } - return 0; + return nullptr; } if (BOP->getOpcode() == BO_LAnd) { // LHS must have been evaluated in a different block. @@ -1675,9 +1725,9 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, if (BOP->getOpcode() == BO_LOr) { return getTrylockCallExpr(BOP->getRHS(), C, Negate); } - return 0; + return nullptr; } - return 0; + return nullptr; } @@ -1697,6 +1747,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result, bool Negate = false; const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()]; const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext; + StringRef CapDiagKind = "mutex"; CallExpr *Exp = const_cast<CallExpr*>(getTrylockCallExpr(Cond, LVarCtx, Negate)); @@ -1711,15 +1762,14 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result, MutexIDList SharedLocksToAdd; // If the condition is a call to a Trylock function, then grab the attributes - AttrVec &ArgAttrs = FunDecl->getAttrs(); - for (unsigned i = 0; i < ArgAttrs.size(); ++i) { - Attr *Attr = ArgAttrs[i]; + for (auto *Attr : FunDecl->getAttrs()) { switch (Attr->getKind()) { case attr::ExclusiveTrylockFunction: { ExclusiveTrylockFunctionAttr *A = cast<ExclusiveTrylockFunctionAttr>(Attr); getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(), Negate); + CapDiagKind = ClassifyDiagnostic(A); break; } case attr::SharedTrylockFunction: { @@ -1727,6 +1777,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result, cast<SharedTrylockFunctionAttr>(Attr); getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(), Negate); + CapDiagKind = ClassifyDiagnostic(A); break; } default: @@ -1736,17 +1787,13 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result, // Add and remove locks. SourceLocation Loc = Exp->getExprLoc(); - for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { - addLock(Result, ExclusiveLocksToAdd[i], - LockData(Loc, LK_Exclusive)); - } - for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { - addLock(Result, SharedLocksToAdd[i], - LockData(Loc, LK_Shared)); - } + for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd) + addLock(Result, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive), + CapDiagKind); + for (const auto &SharedLockToAdd : SharedLocksToAdd) + addLock(Result, SharedLockToAdd, LockData(Loc, LK_Shared), CapDiagKind); } - /// \brief We use this class to visit different types of expressions in /// CFGBlocks, and build up the lockset. /// An expression may cause us to add or remove locks from the lockset, or else @@ -1761,16 +1808,17 @@ class BuildLockset : public StmtVisitor<BuildLockset> { unsigned CtxIndex; // Helper functions - const ValueDecl *getValueDecl(const Expr *Exp); void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK, - Expr *MutexExp, ProtectedOperationKind POK); - void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp); + Expr *MutexExp, ProtectedOperationKind POK, + StringRef DiagKind); + void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp, + StringRef DiagKind); void checkAccess(const Expr *Exp, AccessKind AK); void checkPtAccess(const Expr *Exp, AccessKind AK); - void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = 0); + void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = nullptr); public: BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info) @@ -1789,31 +1837,17 @@ public: void VisitDeclStmt(DeclStmt *S); }; - -/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs -const ValueDecl *BuildLockset::getValueDecl(const Expr *Exp) { - if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Exp)) - return getValueDecl(CE->getSubExpr()); - - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp)) - return DR->getDecl(); - - if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) - return ME->getMemberDecl(); - - return 0; -} - /// \brief Warn if the LSet does not contain a lock sufficient to protect access /// of at least the passed in AccessKind. void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK, Expr *MutexExp, - ProtectedOperationKind POK) { + ProtectedOperationKind POK, + StringRef DiagKind) { LockKind LK = getLockKindFromAccessKind(AK); SExpr Mutex(MutexExp, Exp, D); if (!Mutex.isValid()) { - SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D); + SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind); return; } else if (Mutex.shouldIgnore()) { return; @@ -1829,40 +1863,38 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, LDat = &FEntry->LDat; std::string PartMatchStr = FEntry->MutID.toString(); StringRef PartMatchName(PartMatchStr); - Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, - Exp->getExprLoc(), &PartMatchName); + Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), + LK, Exp->getExprLoc(), + &PartMatchName); } else { // Warn that there's no match at all. - Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, - Exp->getExprLoc()); + Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), + LK, Exp->getExprLoc()); } NoError = false; } // Make sure the mutex we found is the right kind. if (NoError && LDat && !LDat->isAtLeast(LK)) - Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, + Analyzer->Handler.handleMutexNotHeld(DiagKind, D, POK, Mutex.toString(), LK, Exp->getExprLoc()); } /// \brief Warn if the LSet contains the given lock. -void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr* Exp, - Expr *MutexExp) { +void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, + Expr *MutexExp, + StringRef DiagKind) { SExpr Mutex(MutexExp, Exp, D); if (!Mutex.isValid()) { - SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D); + SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D, DiagKind); return; } LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex); - if (LDat) { - std::string DeclName = D->getNameAsString(); - StringRef DeclNameSR (DeclName); - Analyzer->Handler.handleFunExcludesLock(DeclNameSR, Mutex.toString(), - Exp->getExprLoc()); - } + if (LDat) + Analyzer->Handler.handleFunExcludesLock( + DiagKind, D->getNameAsString(), Mutex.toString(), Exp->getExprLoc()); } - /// \brief Checks guarded_by and pt_guarded_by attributes. /// Whenever we identify an access (read or write) to a DeclRefExpr that is /// marked with guarded_by, we must ensure the appropriate mutexes are held. @@ -1879,10 +1911,8 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) { } if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(Exp)) { - if (Analyzer->Handler.issueBetaWarnings()) { - checkPtAccess(AE->getLHS(), AK); - return; - } + checkPtAccess(AE->getLHS(), AK); + return; } if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) { @@ -1896,55 +1926,48 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) { if (!D || !D->hasAttrs()) return; - if (D->getAttr<GuardedVarAttr>() && FSet.isEmpty()) - Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK, + if (D->hasAttr<GuardedVarAttr>() && FSet.isEmpty()) + Analyzer->Handler.handleNoMutexHeld("mutex", D, POK_VarAccess, AK, Exp->getExprLoc()); - const AttrVec &ArgAttrs = D->getAttrs(); - for (unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) - if (GuardedByAttr *GBAttr = dyn_cast<GuardedByAttr>(ArgAttrs[i])) - warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarAccess); + for (const auto *I : D->specific_attrs<GuardedByAttr>()) + warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarAccess, + ClassifyDiagnostic(I)); } /// \brief Checks pt_guarded_by and pt_guarded_var attributes. void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) { - if (Analyzer->Handler.issueBetaWarnings()) { - while (true) { - if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) { - Exp = PE->getSubExpr(); - continue; - } - if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) { - if (CE->getCastKind() == CK_ArrayToPointerDecay) { - // If it's an actual array, and not a pointer, then it's elements - // are protected by GUARDED_BY, not PT_GUARDED_BY; - checkAccess(CE->getSubExpr(), AK); - return; - } - Exp = CE->getSubExpr(); - continue; + while (true) { + if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) { + Exp = PE->getSubExpr(); + continue; + } + if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) { + if (CE->getCastKind() == CK_ArrayToPointerDecay) { + // If it's an actual array, and not a pointer, then it's elements + // are protected by GUARDED_BY, not PT_GUARDED_BY; + checkAccess(CE->getSubExpr(), AK); + return; } - break; + Exp = CE->getSubExpr(); + continue; } + break; } - else - Exp = Exp->IgnoreParenCasts(); const ValueDecl *D = getValueDecl(Exp); if (!D || !D->hasAttrs()) return; - if (D->getAttr<PtGuardedVarAttr>() && FSet.isEmpty()) - Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK, + if (D->hasAttr<PtGuardedVarAttr>() && FSet.isEmpty()) + Analyzer->Handler.handleNoMutexHeld("mutex", D, POK_VarDereference, AK, Exp->getExprLoc()); - const AttrVec &ArgAttrs = D->getAttrs(); - for (unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) - if (PtGuardedByAttr *GBAttr = dyn_cast<PtGuardedByAttr>(ArgAttrs[i])) - warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarDereference); + for (auto const *I : D->specific_attrs<PtGuardedByAttr>()) + warnIfMutexNotHeld(D, Exp, AK, I->getArg(), POK_VarDereference, + ClassifyDiagnostic(I)); } - /// \brief Process a function call, method call, constructor call, /// or destructor call. This involves looking at the attributes on the /// corresponding function/method/constructor/destructor, issuing warnings, @@ -1958,26 +1981,22 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) { void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { SourceLocation Loc = Exp->getExprLoc(); const AttrVec &ArgAttrs = D->getAttrs(); - MutexIDList ExclusiveLocksToAdd; - MutexIDList SharedLocksToAdd; - MutexIDList LocksToRemove; + MutexIDList ExclusiveLocksToAdd, SharedLocksToAdd; + MutexIDList ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove; + StringRef CapDiagKind = "mutex"; for(unsigned i = 0; i < ArgAttrs.size(); ++i) { Attr *At = const_cast<Attr*>(ArgAttrs[i]); switch (At->getKind()) { - // When we encounter an exclusive lock function, we need to add the lock - // to our lockset with kind exclusive. - case attr::ExclusiveLockFunction: { - ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(At); - Analyzer->getMutexIDs(ExclusiveLocksToAdd, A, Exp, D, VD); - break; - } - - // When we encounter a shared lock function, we need to add the lock - // to our lockset with kind shared. - case attr::SharedLockFunction: { - SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(At); - Analyzer->getMutexIDs(SharedLocksToAdd, A, Exp, D, VD); + // When we encounter a lock function, we need to add the lock to our + // lockset. + case attr::AcquireCapability: { + auto *A = cast<AcquireCapabilityAttr>(At); + Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd + : ExclusiveLocksToAdd, + A, Exp, D, VD); + + CapDiagKind = ClassifyDiagnostic(A); break; } @@ -1989,10 +2008,10 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { MutexIDList AssertLocks; Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD); - for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) { - Analyzer->addLock(FSet, AssertLocks[i], - LockData(Loc, LK_Exclusive, false, true)); - } + for (const auto &AssertLock : AssertLocks) + Analyzer->addLock(FSet, AssertLock, + LockData(Loc, LK_Exclusive, false, true), + ClassifyDiagnostic(A)); break; } case attr::AssertSharedLock: { @@ -2000,50 +2019,44 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { MutexIDList AssertLocks; Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD); - for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) { - Analyzer->addLock(FSet, AssertLocks[i], - LockData(Loc, LK_Shared, false, true)); - } + for (const auto &AssertLock : AssertLocks) + Analyzer->addLock(FSet, AssertLock, + LockData(Loc, LK_Shared, false, true), + ClassifyDiagnostic(A)); break; } // When we encounter an unlock function, we need to remove unlocked // mutexes from the lockset, and flag a warning if they are not there. - case attr::UnlockFunction: { - UnlockFunctionAttr *A = cast<UnlockFunctionAttr>(At); - Analyzer->getMutexIDs(LocksToRemove, A, Exp, D, VD); - break; - } - - case attr::ExclusiveLocksRequired: { - ExclusiveLocksRequiredAttr *A = cast<ExclusiveLocksRequiredAttr>(At); + case attr::ReleaseCapability: { + auto *A = cast<ReleaseCapabilityAttr>(At); + if (A->isGeneric()) + Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp, D, VD); + else if (A->isShared()) + Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp, D, VD); + else + Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp, D, VD); - for (ExclusiveLocksRequiredAttr::args_iterator - I = A->args_begin(), E = A->args_end(); I != E; ++I) - warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall); + CapDiagKind = ClassifyDiagnostic(A); break; } - case attr::SharedLocksRequired: { - SharedLocksRequiredAttr *A = cast<SharedLocksRequiredAttr>(At); - - for (SharedLocksRequiredAttr::args_iterator I = A->args_begin(), - E = A->args_end(); I != E; ++I) - warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall); + case attr::RequiresCapability: { + RequiresCapabilityAttr *A = cast<RequiresCapabilityAttr>(At); + for (auto *Arg : A->args()) + warnIfMutexNotHeld(D, Exp, A->isShared() ? AK_Read : AK_Written, Arg, + POK_FunctionCall, ClassifyDiagnostic(A)); break; } case attr::LocksExcluded: { LocksExcludedAttr *A = cast<LocksExcludedAttr>(At); - - for (LocksExcludedAttr::args_iterator I = A->args_begin(), - E = A->args_end(); I != E; ++I) { - warnIfMutexHeld(D, Exp, *I); - } + for (auto *Arg : A->args()) + warnIfMutexHeld(D, Exp, Arg, ClassifyDiagnostic(A)); break; } - // Ignore other (non thread-safety) attributes + // Ignore attributes unrelated to thread-safety default: break; } @@ -2054,44 +2067,43 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { if (VD) { if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) { const CXXRecordDecl* PD = CD->getParent(); - if (PD && PD->getAttr<ScopedLockableAttr>()) + if (PD && PD->hasAttr<ScopedLockableAttr>()) isScopedVar = true; } } // Add locks. - for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { - Analyzer->addLock(FSet, ExclusiveLocksToAdd[i], - LockData(Loc, LK_Exclusive, isScopedVar)); - } - for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { - Analyzer->addLock(FSet, SharedLocksToAdd[i], - LockData(Loc, LK_Shared, isScopedVar)); - } + for (const auto &M : ExclusiveLocksToAdd) + Analyzer->addLock(FSet, M, LockData(Loc, LK_Exclusive, isScopedVar), + CapDiagKind); + for (const auto &M : SharedLocksToAdd) + Analyzer->addLock(FSet, M, LockData(Loc, LK_Shared, isScopedVar), + CapDiagKind); // Add the managing object as a dummy mutex, mapped to the underlying mutex. // FIXME -- this doesn't work if we acquire multiple locks. if (isScopedVar) { SourceLocation MLoc = VD->getLocation(); DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation()); - SExpr SMutex(&DRE, 0, 0); + SExpr SMutex(&DRE, nullptr, nullptr); - for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { - Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, - ExclusiveLocksToAdd[i])); - } - for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { - Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, - SharedLocksToAdd[i])); - } + for (const auto &M : ExclusiveLocksToAdd) + Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, M), + CapDiagKind); + for (const auto &M : SharedLocksToAdd) + Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, M), + CapDiagKind); } // Remove locks. // FIXME -- should only fully remove if the attribute refers to 'this'. bool Dtor = isa<CXXDestructorDecl>(D); - for (unsigned i=0,n=LocksToRemove.size(); i<n; ++i) { - Analyzer->removeLock(FSet, LocksToRemove[i], Loc, Dtor); - } + for (const auto &M : ExclusiveLocksToRemove) + Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Exclusive, CapDiagKind); + for (const auto &M : SharedLocksToRemove) + Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Shared, CapDiagKind); + for (const auto &M : GenericLocksToRemove) + Analyzer->removeLock(FSet, M, Loc, Dtor, LK_Generic, CapDiagKind); } @@ -2168,11 +2180,9 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) { case OO_Star: case OO_Arrow: case OO_Subscript: { - if (Analyzer->Handler.issueBetaWarnings()) { - const Expr *Obj = OE->getArg(0); - checkAccess(Obj, AK_Read); - checkPtAccess(Obj, AK_Read); - } + const Expr *Obj = OE->getArg(0); + checkAccess(Obj, AK_Read); + checkPtAccess(Obj, AK_Read); break; } default: { @@ -2201,9 +2211,7 @@ void BuildLockset::VisitDeclStmt(DeclStmt *S) { // adjust the context LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx); - DeclGroupRef DGrp = S->getDeclGroup(); - for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) { - Decl *D = *I; + for (auto *D : S->getDeclGroup()) { if (VarDecl *VD = dyn_cast_or_null<VarDecl>(D)) { Expr *E = VD->getInit(); // handle constructors that involve temporaries @@ -2245,26 +2253,24 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1, FactSet FSet1Orig = FSet1; // Find locks in FSet2 that conflict or are not in FSet1, and warn. - for (FactSet::const_iterator I = FSet2.begin(), E = FSet2.end(); - I != E; ++I) { - const SExpr &FSet2Mutex = FactMan[*I].MutID; - const LockData &LDat2 = FactMan[*I].LDat; + for (const auto &Fact : FSet2) { + const SExpr &FSet2Mutex = FactMan[Fact].MutID; + const LockData &LDat2 = FactMan[Fact].LDat; FactSet::iterator I1 = FSet1.findLockIter(FactMan, FSet2Mutex); if (I1 != FSet1.end()) { const LockData* LDat1 = &FactMan[*I1].LDat; if (LDat1->LKind != LDat2.LKind) { - Handler.handleExclusiveAndShared(FSet2Mutex.toString(), - LDat2.AcquireLoc, - LDat1->AcquireLoc); + Handler.handleExclusiveAndShared("mutex", FSet2Mutex.toString(), + LDat2.AcquireLoc, LDat1->AcquireLoc); if (Modify && LDat1->LKind != LK_Exclusive) { // Take the exclusive lock, which is the one in FSet2. - *I1 = *I; + *I1 = Fact; } } else if (LDat1->Asserted && !LDat2.Asserted) { // The non-asserted lock in FSet2 is the one we want to track. - *I1 = *I; + *I1 = Fact; } } else { if (LDat2.UnderlyingMutex.isValid()) { @@ -2272,23 +2278,21 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1, // If this is a scoped lock that manages another mutex, and if the // underlying mutex is still held, then warn about the underlying // mutex. - Handler.handleMutexHeldEndOfScope(LDat2.UnderlyingMutex.toString(), - LDat2.AcquireLoc, - JoinLoc, LEK1); + Handler.handleMutexHeldEndOfScope("mutex", + LDat2.UnderlyingMutex.toString(), + LDat2.AcquireLoc, JoinLoc, LEK1); } } else if (!LDat2.Managed && !FSet2Mutex.isUniversal() && !LDat2.Asserted) - Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(), - LDat2.AcquireLoc, - JoinLoc, LEK1); + Handler.handleMutexHeldEndOfScope("mutex", FSet2Mutex.toString(), + LDat2.AcquireLoc, JoinLoc, LEK1); } } // Find locks in FSet1 that are not in FSet2, and remove them. - for (FactSet::const_iterator I = FSet1Orig.begin(), E = FSet1Orig.end(); - I != E; ++I) { - const SExpr &FSet1Mutex = FactMan[*I].MutID; - const LockData &LDat1 = FactMan[*I].LDat; + for (const auto &Fact : FSet1Orig) { + const SExpr &FSet1Mutex = FactMan[Fact].MutID; + const LockData &LDat1 = FactMan[Fact].LDat; if (!FSet2.findLock(FactMan, FSet1Mutex)) { if (LDat1.UnderlyingMutex.isValid()) { @@ -2296,15 +2300,14 @@ void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1, // If this is a scoped lock that manages another mutex, and if the // underlying mutex is still held, then warn about the underlying // mutex. - Handler.handleMutexHeldEndOfScope(LDat1.UnderlyingMutex.toString(), - LDat1.AcquireLoc, - JoinLoc, LEK1); + Handler.handleMutexHeldEndOfScope("mutex", + LDat1.UnderlyingMutex.toString(), + LDat1.AcquireLoc, JoinLoc, LEK1); } } else if (!LDat1.Managed && !FSet1Mutex.isUniversal() && !LDat1.Asserted) - Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(), - LDat1.AcquireLoc, - JoinLoc, LEK2); + Handler.handleMutexHeldEndOfScope("mutex", FSet1Mutex.toString(), + LDat1.AcquireLoc, JoinLoc, LEK2); if (Modify) FSet1.removeLock(FactMan, FSet1Mutex); } @@ -2334,16 +2337,21 @@ inline bool neverReturns(const CFGBlock* B) { /// at the end of each block, and issue warnings for thread safety violations. /// Each block in the CFG is traversed exactly once. void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { - CFG *CFGraph = AC.getCFG(); - if (!CFGraph) return; - const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl()); + // TODO: this whole function needs be rewritten as a visitor for CFGWalker. + // For now, we just use the walker to set things up. + threadSafety::CFGWalker walker; + if (!walker.init(AC)) + return; // AC.dumpCFG(true); + // threadSafety::printSCFG(walker); - if (!D) - return; // Ignore anonymous functions for now. - if (D->getAttr<NoThreadSafetyAnalysisAttr>()) + CFG *CFGraph = walker.getGraph(); + const NamedDecl *D = walker.getDecl(); + + if (D->hasAttr<NoThreadSafetyAnalysisAttr>()) return; + // FIXME: Do something a bit more intelligent inside constructor and // destructor code. Constructors and destructors must assume unique access // to 'this', so checks on member variable access is disabled, but we should @@ -2359,7 +2367,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { // We need to explore the CFG via a "topological" ordering. // That way, we will be guaranteed to have information about required // predecessor locksets when exploring a new block. - PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>(); + const PostOrderCFGView *SortedGraph = walker.getSortedGraph(); PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph); // Mark entry block as reachable @@ -2385,35 +2393,31 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { MutexIDList ExclusiveLocksToAdd; MutexIDList SharedLocksToAdd; + StringRef CapDiagKind = "mutex"; SourceLocation Loc = D->getLocation(); - for (unsigned i = 0; i < ArgAttrs.size(); ++i) { - Attr *Attr = ArgAttrs[i]; + for (const auto *Attr : ArgAttrs) { Loc = Attr->getLocation(); - if (ExclusiveLocksRequiredAttr *A - = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) { - getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D); - } else if (SharedLocksRequiredAttr *A - = dyn_cast<SharedLocksRequiredAttr>(Attr)) { - getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D); - } else if (UnlockFunctionAttr *A = dyn_cast<UnlockFunctionAttr>(Attr)) { + if (const auto *A = dyn_cast<RequiresCapabilityAttr>(Attr)) { + getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A, + nullptr, D); + CapDiagKind = ClassifyDiagnostic(A); + } else if (const auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) { // UNLOCK_FUNCTION() is used to hide the underlying lock implementation. // We must ignore such methods. if (A->args_size() == 0) return; // FIXME -- deal with exclusive vs. shared unlock functions? - getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D); - getMutexIDs(LocksReleased, A, (Expr*) 0, D); - } else if (ExclusiveLockFunctionAttr *A - = dyn_cast<ExclusiveLockFunctionAttr>(Attr)) { + getMutexIDs(ExclusiveLocksToAdd, A, nullptr, D); + getMutexIDs(LocksReleased, A, nullptr, D); + CapDiagKind = ClassifyDiagnostic(A); + } else if (const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) { if (A->args_size() == 0) return; - getMutexIDs(ExclusiveLocksAcquired, A, (Expr*) 0, D); - } else if (SharedLockFunctionAttr *A - = dyn_cast<SharedLockFunctionAttr>(Attr)) { - if (A->args_size() == 0) - return; - getMutexIDs(SharedLocksAcquired, A, (Expr*) 0, D); + getMutexIDs(A->isShared() ? SharedLocksAcquired + : ExclusiveLocksAcquired, + A, nullptr, D); + CapDiagKind = ClassifyDiagnostic(A); } else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) { // Don't try to check trylock functions for now return; @@ -2424,19 +2428,15 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { } // FIXME -- Loc can be wrong here. - for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { - addLock(InitialLockset, ExclusiveLocksToAdd[i], - LockData(Loc, LK_Exclusive)); - } - for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { - addLock(InitialLockset, SharedLocksToAdd[i], - LockData(Loc, LK_Shared)); - } + for (const auto &ExclusiveLockToAdd : ExclusiveLocksToAdd) + addLock(InitialLockset, ExclusiveLockToAdd, LockData(Loc, LK_Exclusive), + CapDiagKind); + for (const auto &SharedLockToAdd : SharedLocksToAdd) + addLock(InitialLockset, SharedLockToAdd, LockData(Loc, LK_Shared), + CapDiagKind); } - for (PostOrderCFGView::iterator I = SortedGraph->begin(), - E = SortedGraph->end(); I!= E; ++I) { - const CFGBlock *CurrBlock = *I; + for (const auto *CurrBlock : *SortedGraph) { int CurrBlockID = CurrBlock->getBlockID(); CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID]; @@ -2462,7 +2462,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { PE = CurrBlock->pred_end(); PI != PE; ++PI) { // if *PI -> CurrBlock is a back edge - if (*PI == 0 || !VisitedBlocks.alreadySet(*PI)) + if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI)) continue; int PrevBlockID = (*PI)->getBlockID(); @@ -2505,9 +2505,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { // Process continue and break blocks. Assume that the lockset for the // resulting block is unaffected by any discrepancies in them. - for (unsigned SpecialI = 0, SpecialN = SpecialBlocks.size(); - SpecialI < SpecialN; ++SpecialI) { - CFGBlock *PrevBlock = SpecialBlocks[SpecialI]; + for (const auto *PrevBlock : SpecialBlocks) { int PrevBlockID = PrevBlock->getBlockID(); CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID]; @@ -2576,7 +2574,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { SE = CurrBlock->succ_end(); SI != SE; ++SI) { // if CurrBlock -> *SI is *not* a back edge - if (*SI == 0 || !VisitedBlocks.alreadySet(*SI)) + if (*SI == nullptr || !VisitedBlocks.alreadySet(*SI)) continue; CFGBlock *FirstLoopBlock = *SI; @@ -2603,17 +2601,14 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { // by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then // issue the appropriate warning. // FIXME: the location here is not quite right. - for (unsigned i=0,n=ExclusiveLocksAcquired.size(); i<n; ++i) { - ExpectedExitSet.addLock(FactMan, ExclusiveLocksAcquired[i], + for (const auto &Lock : ExclusiveLocksAcquired) + ExpectedExitSet.addLock(FactMan, Lock, LockData(D->getLocation(), LK_Exclusive)); - } - for (unsigned i=0,n=SharedLocksAcquired.size(); i<n; ++i) { - ExpectedExitSet.addLock(FactMan, SharedLocksAcquired[i], + for (const auto &Lock : SharedLocksAcquired) + ExpectedExitSet.addLock(FactMan, Lock, LockData(D->getLocation(), LK_Shared)); - } - for (unsigned i=0,n=LocksReleased.size(); i<n; ++i) { - ExpectedExitSet.removeLock(FactMan, LocksReleased[i]); - } + for (const auto &Lock : LocksReleased) + ExpectedExitSet.removeLock(FactMan, Lock); // FIXME: Should we call this function for all blocks which exit the function? intersectAndWarn(ExpectedExitSet, Final->ExitSet, diff --git a/contrib/llvm/tools/clang/lib/Analysis/ThreadSafetyCommon.cpp b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafetyCommon.cpp new file mode 100644 index 0000000..da88b78 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -0,0 +1,794 @@ +//===- ThreadSafetyCommon.cpp ----------------------------------*- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of the interfaces declared in ThreadSafetyCommon.h +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/ThreadSafetyCommon.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Analysis/Analyses/PostOrderCFGView.h" +#include "clang/Analysis/Analyses/ThreadSafetyTIL.h" +#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Basic/OperatorKinds.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +#include <algorithm> +#include <climits> +#include <vector> + + +namespace clang { +namespace threadSafety { + +// From ThreadSafetyUtil.h +std::string getSourceLiteralString(const clang::Expr *CE) { + switch (CE->getStmtClass()) { + case Stmt::IntegerLiteralClass: + return cast<IntegerLiteral>(CE)->getValue().toString(10, true); + case Stmt::StringLiteralClass: { + std::string ret("\""); + ret += cast<StringLiteral>(CE)->getString(); + ret += "\""; + return ret; + } + case Stmt::CharacterLiteralClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::GNUNullExprClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::FloatingLiteralClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::ObjCStringLiteralClass: + default: + return "#lit"; + } +} + +namespace til { + +// Return true if E is a variable that points to an incomplete Phi node. +static bool isIncompleteVar(const SExpr *E) { + if (const auto *V = dyn_cast<Variable>(E)) { + if (const auto *Ph = dyn_cast<Phi>(V->definition())) + return Ph->status() == Phi::PH_Incomplete; + } + return false; +} + +} // end namespace til + + +typedef SExprBuilder::CallingContext CallingContext; + + +til::SExpr *SExprBuilder::lookupStmt(const Stmt *S) { + auto It = SMap.find(S); + if (It != SMap.end()) + return It->second; + return nullptr; +} + + +til::SCFG *SExprBuilder::buildCFG(CFGWalker &Walker) { + Walker.walk(*this); + return Scfg; +} + + +// Translate a clang statement or expression to a TIL expression. +// Also performs substitution of variables; Ctx provides the context. +// Dispatches on the type of S. +til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) { + if (!S) + return nullptr; + + // Check if S has already been translated and cached. + // This handles the lookup of SSA names for DeclRefExprs here. + if (til::SExpr *E = lookupStmt(S)) + return E; + + switch (S->getStmtClass()) { + case Stmt::DeclRefExprClass: + return translateDeclRefExpr(cast<DeclRefExpr>(S), Ctx); + case Stmt::CXXThisExprClass: + return translateCXXThisExpr(cast<CXXThisExpr>(S), Ctx); + case Stmt::MemberExprClass: + return translateMemberExpr(cast<MemberExpr>(S), Ctx); + case Stmt::CallExprClass: + return translateCallExpr(cast<CallExpr>(S), Ctx); + case Stmt::CXXMemberCallExprClass: + return translateCXXMemberCallExpr(cast<CXXMemberCallExpr>(S), Ctx); + case Stmt::CXXOperatorCallExprClass: + return translateCXXOperatorCallExpr(cast<CXXOperatorCallExpr>(S), Ctx); + case Stmt::UnaryOperatorClass: + return translateUnaryOperator(cast<UnaryOperator>(S), Ctx); + case Stmt::BinaryOperatorClass: + case Stmt::CompoundAssignOperatorClass: + return translateBinaryOperator(cast<BinaryOperator>(S), Ctx); + + case Stmt::ArraySubscriptExprClass: + return translateArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Ctx); + case Stmt::ConditionalOperatorClass: + return translateConditionalOperator(cast<ConditionalOperator>(S), Ctx); + case Stmt::BinaryConditionalOperatorClass: + return translateBinaryConditionalOperator( + cast<BinaryConditionalOperator>(S), Ctx); + + // We treat these as no-ops + case Stmt::ParenExprClass: + return translate(cast<ParenExpr>(S)->getSubExpr(), Ctx); + case Stmt::ExprWithCleanupsClass: + return translate(cast<ExprWithCleanups>(S)->getSubExpr(), Ctx); + case Stmt::CXXBindTemporaryExprClass: + return translate(cast<CXXBindTemporaryExpr>(S)->getSubExpr(), Ctx); + + // Collect all literals + case Stmt::CharacterLiteralClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::GNUNullExprClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::FloatingLiteralClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::IntegerLiteralClass: + case Stmt::StringLiteralClass: + case Stmt::ObjCStringLiteralClass: + return new (Arena) til::Literal(cast<Expr>(S)); + + case Stmt::DeclStmtClass: + return translateDeclStmt(cast<DeclStmt>(S), Ctx); + default: + break; + } + if (const CastExpr *CE = dyn_cast<CastExpr>(S)) + return translateCastExpr(CE, Ctx); + + return new (Arena) til::Undefined(S); +} + + +til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE, + CallingContext *Ctx) { + const ValueDecl *VD = cast<ValueDecl>(DRE->getDecl()->getCanonicalDecl()); + + // Function parameters require substitution and/or renaming. + if (const ParmVarDecl *PV = dyn_cast_or_null<ParmVarDecl>(VD)) { + const FunctionDecl *FD = + cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl(); + unsigned I = PV->getFunctionScopeIndex(); + + if (Ctx && Ctx->FunArgs && FD == Ctx->AttrDecl->getCanonicalDecl()) { + // Substitute call arguments for references to function parameters + assert(I < Ctx->NumArgs); + return translate(Ctx->FunArgs[I], Ctx->Prev); + } + // Map the param back to the param of the original function declaration + // for consistent comparisons. + VD = FD->getParamDecl(I); + } + + // For non-local variables, treat it as a referenced to a named object. + return new (Arena) til::LiteralPtr(VD); +} + + +til::SExpr *SExprBuilder::translateCXXThisExpr(const CXXThisExpr *TE, + CallingContext *Ctx) { + // Substitute for 'this' + if (Ctx && Ctx->SelfArg) + return translate(Ctx->SelfArg, Ctx->Prev); + assert(SelfVar && "We have no variable for 'this'!"); + return SelfVar; +} + + +til::SExpr *SExprBuilder::translateMemberExpr(const MemberExpr *ME, + CallingContext *Ctx) { + til::SExpr *E = translate(ME->getBase(), Ctx); + E = new (Arena) til::SApply(E); + return new (Arena) til::Project(E, ME->getMemberDecl()); +} + + +til::SExpr *SExprBuilder::translateCallExpr(const CallExpr *CE, + CallingContext *Ctx) { + // TODO -- Lock returned + til::SExpr *E = translate(CE->getCallee(), Ctx); + for (const auto *Arg : CE->arguments()) { + til::SExpr *A = translate(Arg, Ctx); + E = new (Arena) til::Apply(E, A); + } + return new (Arena) til::Call(E, CE); +} + + +til::SExpr *SExprBuilder::translateCXXMemberCallExpr( + const CXXMemberCallExpr *ME, CallingContext *Ctx) { + return translateCallExpr(cast<CallExpr>(ME), Ctx); +} + + +til::SExpr *SExprBuilder::translateCXXOperatorCallExpr( + const CXXOperatorCallExpr *OCE, CallingContext *Ctx) { + return translateCallExpr(cast<CallExpr>(OCE), Ctx); +} + + +til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, + CallingContext *Ctx) { + switch (UO->getOpcode()) { + case UO_PostInc: + case UO_PostDec: + case UO_PreInc: + case UO_PreDec: + return new (Arena) til::Undefined(UO); + + // We treat these as no-ops + case UO_AddrOf: + case UO_Deref: + case UO_Plus: + return translate(UO->getSubExpr(), Ctx); + + case UO_Minus: + return new (Arena) + til::UnaryOp(til::UOP_Minus, translate(UO->getSubExpr(), Ctx)); + case UO_Not: + return new (Arena) + til::UnaryOp(til::UOP_BitNot, translate(UO->getSubExpr(), Ctx)); + case UO_LNot: + return new (Arena) + til::UnaryOp(til::UOP_LogicNot, translate(UO->getSubExpr(), Ctx)); + + // Currently unsupported + case UO_Real: + case UO_Imag: + case UO_Extension: + return new (Arena) til::Undefined(UO); + } + return new (Arena) til::Undefined(UO); +} + + +til::SExpr *SExprBuilder::translateBinOp(til::TIL_BinaryOpcode Op, + const BinaryOperator *BO, + CallingContext *Ctx, bool Reverse) { + til::SExpr *E0 = translate(BO->getLHS(), Ctx); + til::SExpr *E1 = translate(BO->getRHS(), Ctx); + if (Reverse) + return new (Arena) til::BinaryOp(Op, E1, E0); + else + return new (Arena) til::BinaryOp(Op, E0, E1); +} + + +til::SExpr *SExprBuilder::translateBinAssign(til::TIL_BinaryOpcode Op, + const BinaryOperator *BO, + CallingContext *Ctx, + bool Assign) { + const Expr *LHS = BO->getLHS(); + const Expr *RHS = BO->getRHS(); + til::SExpr *E0 = translate(LHS, Ctx); + til::SExpr *E1 = translate(RHS, Ctx); + + const ValueDecl *VD = nullptr; + til::SExpr *CV = nullptr; + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(LHS)) { + VD = DRE->getDecl(); + CV = lookupVarDecl(VD); + } + + if (!Assign) { + til::SExpr *Arg = CV ? CV : new (Arena) til::Load(E0); + E1 = new (Arena) til::BinaryOp(Op, Arg, E1); + E1 = addStatement(E1, nullptr, VD); + } + if (VD && CV) + return updateVarDecl(VD, E1); + return new (Arena) til::Store(E0, E1); +} + + +til::SExpr *SExprBuilder::translateBinaryOperator(const BinaryOperator *BO, + CallingContext *Ctx) { + switch (BO->getOpcode()) { + case BO_PtrMemD: + case BO_PtrMemI: + return new (Arena) til::Undefined(BO); + + case BO_Mul: return translateBinOp(til::BOP_Mul, BO, Ctx); + case BO_Div: return translateBinOp(til::BOP_Div, BO, Ctx); + case BO_Rem: return translateBinOp(til::BOP_Rem, BO, Ctx); + case BO_Add: return translateBinOp(til::BOP_Add, BO, Ctx); + case BO_Sub: return translateBinOp(til::BOP_Sub, BO, Ctx); + case BO_Shl: return translateBinOp(til::BOP_Shl, BO, Ctx); + case BO_Shr: return translateBinOp(til::BOP_Shr, BO, Ctx); + case BO_LT: return translateBinOp(til::BOP_Lt, BO, Ctx); + case BO_GT: return translateBinOp(til::BOP_Lt, BO, Ctx, true); + case BO_LE: return translateBinOp(til::BOP_Leq, BO, Ctx); + case BO_GE: return translateBinOp(til::BOP_Leq, BO, Ctx, true); + case BO_EQ: return translateBinOp(til::BOP_Eq, BO, Ctx); + case BO_NE: return translateBinOp(til::BOP_Neq, BO, Ctx); + case BO_And: return translateBinOp(til::BOP_BitAnd, BO, Ctx); + case BO_Xor: return translateBinOp(til::BOP_BitXor, BO, Ctx); + case BO_Or: return translateBinOp(til::BOP_BitOr, BO, Ctx); + case BO_LAnd: return translateBinOp(til::BOP_LogicAnd, BO, Ctx); + case BO_LOr: return translateBinOp(til::BOP_LogicOr, BO, Ctx); + + case BO_Assign: return translateBinAssign(til::BOP_Eq, BO, Ctx, true); + case BO_MulAssign: return translateBinAssign(til::BOP_Mul, BO, Ctx); + case BO_DivAssign: return translateBinAssign(til::BOP_Div, BO, Ctx); + case BO_RemAssign: return translateBinAssign(til::BOP_Rem, BO, Ctx); + case BO_AddAssign: return translateBinAssign(til::BOP_Add, BO, Ctx); + case BO_SubAssign: return translateBinAssign(til::BOP_Sub, BO, Ctx); + case BO_ShlAssign: return translateBinAssign(til::BOP_Shl, BO, Ctx); + case BO_ShrAssign: return translateBinAssign(til::BOP_Shr, BO, Ctx); + case BO_AndAssign: return translateBinAssign(til::BOP_BitAnd, BO, Ctx); + case BO_XorAssign: return translateBinAssign(til::BOP_BitXor, BO, Ctx); + case BO_OrAssign: return translateBinAssign(til::BOP_BitOr, BO, Ctx); + + case BO_Comma: + // The clang CFG should have already processed both sides. + return translate(BO->getRHS(), Ctx); + } + return new (Arena) til::Undefined(BO); +} + + +til::SExpr *SExprBuilder::translateCastExpr(const CastExpr *CE, + CallingContext *Ctx) { + clang::CastKind K = CE->getCastKind(); + switch (K) { + case CK_LValueToRValue: { + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CE->getSubExpr())) { + til::SExpr *E0 = lookupVarDecl(DRE->getDecl()); + if (E0) + return E0; + } + til::SExpr *E0 = translate(CE->getSubExpr(), Ctx); + return new (Arena) til::Load(E0); + } + case CK_NoOp: + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: + case CK_ArrayToPointerDecay: + case CK_FunctionToPointerDecay: { + til::SExpr *E0 = translate(CE->getSubExpr(), Ctx); + return E0; + } + default: { + // FIXME: handle different kinds of casts. + til::SExpr *E0 = translate(CE->getSubExpr(), Ctx); + return new (Arena) til::Cast(til::CAST_none, E0); + } + } +} + + +til::SExpr * +SExprBuilder::translateArraySubscriptExpr(const ArraySubscriptExpr *E, + CallingContext *Ctx) { + til::SExpr *E0 = translate(E->getBase(), Ctx); + til::SExpr *E1 = translate(E->getIdx(), Ctx); + return new (Arena) til::ArrayIndex(E0, E1); +} + + +til::SExpr * +SExprBuilder::translateConditionalOperator(const ConditionalOperator *C, + CallingContext *Ctx) { + return new (Arena) til::Undefined(C); +} + + +til::SExpr *SExprBuilder::translateBinaryConditionalOperator( + const BinaryConditionalOperator *C, CallingContext *Ctx) { + return new (Arena) til::Undefined(C); +} + + +til::SExpr * +SExprBuilder::translateDeclStmt(const DeclStmt *S, CallingContext *Ctx) { + DeclGroupRef DGrp = S->getDeclGroup(); + for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) { + if (VarDecl *VD = dyn_cast_or_null<VarDecl>(*I)) { + Expr *E = VD->getInit(); + til::SExpr* SE = translate(E, Ctx); + + // Add local variables with trivial type to the variable map + QualType T = VD->getType(); + if (T.isTrivialType(VD->getASTContext())) { + return addVarDecl(VD, SE); + } + else { + // TODO: add alloca + } + } + } + return nullptr; +} + + + +// If (E) is non-trivial, then add it to the current basic block, and +// update the statement map so that S refers to E. Returns a new variable +// that refers to E. +// If E is trivial returns E. +til::SExpr *SExprBuilder::addStatement(til::SExpr* E, const Stmt *S, + const ValueDecl *VD) { + if (!E) + return nullptr; + if (til::ThreadSafetyTIL::isTrivial(E)) + return E; + + til::Variable *V = new (Arena) til::Variable(E, VD); + CurrentInstructions.push_back(V); + if (S) + insertStmt(S, V); + return V; +} + + +// Returns the current value of VD, if known, and nullptr otherwise. +til::SExpr *SExprBuilder::lookupVarDecl(const ValueDecl *VD) { + auto It = LVarIdxMap.find(VD); + if (It != LVarIdxMap.end()) { + assert(CurrentLVarMap[It->second].first == VD); + return CurrentLVarMap[It->second].second; + } + return nullptr; +} + + +// if E is a til::Variable, update its clangDecl. +inline void maybeUpdateVD(til::SExpr *E, const ValueDecl *VD) { + if (!E) + return; + if (til::Variable *V = dyn_cast<til::Variable>(E)) { + if (!V->clangDecl()) + V->setClangDecl(VD); + } +} + +// Adds a new variable declaration. +til::SExpr *SExprBuilder::addVarDecl(const ValueDecl *VD, til::SExpr *E) { + maybeUpdateVD(E, VD); + LVarIdxMap.insert(std::make_pair(VD, CurrentLVarMap.size())); + CurrentLVarMap.makeWritable(); + CurrentLVarMap.push_back(std::make_pair(VD, E)); + return E; +} + + +// Updates a current variable declaration. (E.g. by assignment) +til::SExpr *SExprBuilder::updateVarDecl(const ValueDecl *VD, til::SExpr *E) { + maybeUpdateVD(E, VD); + auto It = LVarIdxMap.find(VD); + if (It == LVarIdxMap.end()) { + til::SExpr *Ptr = new (Arena) til::LiteralPtr(VD); + til::SExpr *St = new (Arena) til::Store(Ptr, E); + return St; + } + CurrentLVarMap.makeWritable(); + CurrentLVarMap.elem(It->second).second = E; + return E; +} + + +// Make a Phi node in the current block for the i^th variable in CurrentVarMap. +// If E != null, sets Phi[CurrentBlockInfo->ArgIndex] = E. +// If E == null, this is a backedge and will be set later. +void SExprBuilder::makePhiNodeVar(unsigned i, unsigned NPreds, til::SExpr *E) { + unsigned ArgIndex = CurrentBlockInfo->ProcessedPredecessors; + assert(ArgIndex > 0 && ArgIndex < NPreds); + + til::Variable *V = dyn_cast<til::Variable>(CurrentLVarMap[i].second); + if (V && V->getBlockID() == CurrentBB->blockID()) { + // We already have a Phi node in the current block, + // so just add the new variable to the Phi node. + til::Phi *Ph = dyn_cast<til::Phi>(V->definition()); + assert(Ph && "Expecting Phi node."); + if (E) + Ph->values()[ArgIndex] = E; + return; + } + + // Make a new phi node: phi(..., E) + // All phi args up to the current index are set to the current value. + til::SExpr *CurrE = CurrentLVarMap[i].second; + til::Phi *Ph = new (Arena) til::Phi(Arena, NPreds); + Ph->values().setValues(NPreds, nullptr); + for (unsigned PIdx = 0; PIdx < ArgIndex; ++PIdx) + Ph->values()[PIdx] = CurrE; + if (E) + Ph->values()[ArgIndex] = E; + // If E is from a back-edge, or either E or CurrE are incomplete, then + // mark this node as incomplete; we may need to remove it later. + if (!E || isIncompleteVar(E) || isIncompleteVar(CurrE)) { + Ph->setStatus(til::Phi::PH_Incomplete); + } + + // Add Phi node to current block, and update CurrentLVarMap[i] + auto *Var = new (Arena) til::Variable(Ph, CurrentLVarMap[i].first); + CurrentArguments.push_back(Var); + if (Ph->status() == til::Phi::PH_Incomplete) + IncompleteArgs.push_back(Var); + + CurrentLVarMap.makeWritable(); + CurrentLVarMap.elem(i).second = Var; +} + + +// Merge values from Map into the current variable map. +// This will construct Phi nodes in the current basic block as necessary. +void SExprBuilder::mergeEntryMap(LVarDefinitionMap Map) { + assert(CurrentBlockInfo && "Not processing a block!"); + + if (!CurrentLVarMap.valid()) { + // Steal Map, using copy-on-write. + CurrentLVarMap = std::move(Map); + return; + } + if (CurrentLVarMap.sameAs(Map)) + return; // Easy merge: maps from different predecessors are unchanged. + + unsigned NPreds = CurrentBB->numPredecessors(); + unsigned ESz = CurrentLVarMap.size(); + unsigned MSz = Map.size(); + unsigned Sz = std::min(ESz, MSz); + + for (unsigned i=0; i<Sz; ++i) { + if (CurrentLVarMap[i].first != Map[i].first) { + // We've reached the end of variables in common. + CurrentLVarMap.makeWritable(); + CurrentLVarMap.downsize(i); + break; + } + if (CurrentLVarMap[i].second != Map[i].second) + makePhiNodeVar(i, NPreds, Map[i].second); + } + if (ESz > MSz) { + CurrentLVarMap.makeWritable(); + CurrentLVarMap.downsize(Map.size()); + } +} + + +// Merge a back edge into the current variable map. +// This will create phi nodes for all variables in the variable map. +void SExprBuilder::mergeEntryMapBackEdge() { + // We don't have definitions for variables on the backedge, because we + // haven't gotten that far in the CFG. Thus, when encountering a back edge, + // we conservatively create Phi nodes for all variables. Unnecessary Phi + // nodes will be marked as incomplete, and stripped out at the end. + // + // An Phi node is unnecessary if it only refers to itself and one other + // variable, e.g. x = Phi(y, y, x) can be reduced to x = y. + + assert(CurrentBlockInfo && "Not processing a block!"); + + if (CurrentBlockInfo->HasBackEdges) + return; + CurrentBlockInfo->HasBackEdges = true; + + CurrentLVarMap.makeWritable(); + unsigned Sz = CurrentLVarMap.size(); + unsigned NPreds = CurrentBB->numPredecessors(); + + for (unsigned i=0; i < Sz; ++i) { + makePhiNodeVar(i, NPreds, nullptr); + } +} + + +// Update the phi nodes that were initially created for a back edge +// once the variable definitions have been computed. +// I.e., merge the current variable map into the phi nodes for Blk. +void SExprBuilder::mergePhiNodesBackEdge(const CFGBlock *Blk) { + til::BasicBlock *BB = lookupBlock(Blk); + unsigned ArgIndex = BBInfo[Blk->getBlockID()].ProcessedPredecessors; + assert(ArgIndex > 0 && ArgIndex < BB->numPredecessors()); + + for (til::Variable *V : BB->arguments()) { + til::Phi *Ph = dyn_cast_or_null<til::Phi>(V->definition()); + assert(Ph && "Expecting Phi Node."); + assert(Ph->values()[ArgIndex] == nullptr && "Wrong index for back edge."); + assert(V->clangDecl() && "No local variable for Phi node."); + + til::SExpr *E = lookupVarDecl(V->clangDecl()); + assert(E && "Couldn't find local variable for Phi node."); + + Ph->values()[ArgIndex] = E; + } +} + +void SExprBuilder::enterCFG(CFG *Cfg, const NamedDecl *D, + const CFGBlock *First) { + // Perform initial setup operations. + unsigned NBlocks = Cfg->getNumBlockIDs(); + Scfg = new (Arena) til::SCFG(Arena, NBlocks); + + // allocate all basic blocks immediately, to handle forward references. + BBInfo.resize(NBlocks); + BlockMap.resize(NBlocks, nullptr); + // create map from clang blockID to til::BasicBlocks + for (auto *B : *Cfg) { + auto *BB = new (Arena) til::BasicBlock(Arena); + BB->reserveInstructions(B->size()); + BlockMap[B->getBlockID()] = BB; + } + CallCtx.reset(new SExprBuilder::CallingContext(D)); + + CurrentBB = lookupBlock(&Cfg->getEntry()); + auto Parms = isa<ObjCMethodDecl>(D) ? cast<ObjCMethodDecl>(D)->parameters() + : cast<FunctionDecl>(D)->parameters(); + for (auto *Pm : Parms) { + QualType T = Pm->getType(); + if (!T.isTrivialType(Pm->getASTContext())) + continue; + + // Add parameters to local variable map. + // FIXME: right now we emulate params with loads; that should be fixed. + til::SExpr *Lp = new (Arena) til::LiteralPtr(Pm); + til::SExpr *Ld = new (Arena) til::Load(Lp); + til::SExpr *V = addStatement(Ld, nullptr, Pm); + addVarDecl(Pm, V); + } +} + + +void SExprBuilder::enterCFGBlock(const CFGBlock *B) { + // Intialize TIL basic block and add it to the CFG. + CurrentBB = lookupBlock(B); + CurrentBB->reservePredecessors(B->pred_size()); + Scfg->add(CurrentBB); + + CurrentBlockInfo = &BBInfo[B->getBlockID()]; + + // CurrentLVarMap is moved to ExitMap on block exit. + // FIXME: the entry block will hold function parameters. + // assert(!CurrentLVarMap.valid() && "CurrentLVarMap already initialized."); +} + + +void SExprBuilder::handlePredecessor(const CFGBlock *Pred) { + // Compute CurrentLVarMap on entry from ExitMaps of predecessors + + CurrentBB->addPredecessor(BlockMap[Pred->getBlockID()]); + BlockInfo *PredInfo = &BBInfo[Pred->getBlockID()]; + assert(PredInfo->UnprocessedSuccessors > 0); + + if (--PredInfo->UnprocessedSuccessors == 0) + mergeEntryMap(std::move(PredInfo->ExitMap)); + else + mergeEntryMap(PredInfo->ExitMap.clone()); + + ++CurrentBlockInfo->ProcessedPredecessors; +} + + +void SExprBuilder::handlePredecessorBackEdge(const CFGBlock *Pred) { + mergeEntryMapBackEdge(); +} + + +void SExprBuilder::enterCFGBlockBody(const CFGBlock *B) { + // The merge*() methods have created arguments. + // Push those arguments onto the basic block. + CurrentBB->arguments().reserve( + static_cast<unsigned>(CurrentArguments.size()), Arena); + for (auto *V : CurrentArguments) + CurrentBB->addArgument(V); +} + + +void SExprBuilder::handleStatement(const Stmt *S) { + til::SExpr *E = translate(S, CallCtx.get()); + addStatement(E, S); +} + + +void SExprBuilder::handleDestructorCall(const VarDecl *VD, + const CXXDestructorDecl *DD) { + til::SExpr *Sf = new (Arena) til::LiteralPtr(VD); + til::SExpr *Dr = new (Arena) til::LiteralPtr(DD); + til::SExpr *Ap = new (Arena) til::Apply(Dr, Sf); + til::SExpr *E = new (Arena) til::Call(Ap); + addStatement(E, nullptr); +} + + + +void SExprBuilder::exitCFGBlockBody(const CFGBlock *B) { + CurrentBB->instructions().reserve( + static_cast<unsigned>(CurrentInstructions.size()), Arena); + for (auto *V : CurrentInstructions) + CurrentBB->addInstruction(V); + + // Create an appropriate terminator + unsigned N = B->succ_size(); + auto It = B->succ_begin(); + if (N == 1) { + til::BasicBlock *BB = *It ? lookupBlock(*It) : nullptr; + // TODO: set index + unsigned Idx = BB ? BB->findPredecessorIndex(CurrentBB) : 0; + til::SExpr *Tm = new (Arena) til::Goto(BB, Idx); + CurrentBB->setTerminator(Tm); + } + else if (N == 2) { + til::SExpr *C = translate(B->getTerminatorCondition(true), CallCtx.get()); + til::BasicBlock *BB1 = *It ? lookupBlock(*It) : nullptr; + ++It; + til::BasicBlock *BB2 = *It ? lookupBlock(*It) : nullptr; + unsigned Idx1 = BB1 ? BB1->findPredecessorIndex(CurrentBB) : 0; + unsigned Idx2 = BB2 ? BB2->findPredecessorIndex(CurrentBB) : 0; + til::SExpr *Tm = new (Arena) til::Branch(C, BB1, BB2, Idx1, Idx2); + CurrentBB->setTerminator(Tm); + } +} + + +void SExprBuilder::handleSuccessor(const CFGBlock *Succ) { + ++CurrentBlockInfo->UnprocessedSuccessors; +} + + +void SExprBuilder::handleSuccessorBackEdge(const CFGBlock *Succ) { + mergePhiNodesBackEdge(Succ); + ++BBInfo[Succ->getBlockID()].ProcessedPredecessors; +} + + +void SExprBuilder::exitCFGBlock(const CFGBlock *B) { + CurrentArguments.clear(); + CurrentInstructions.clear(); + CurrentBlockInfo->ExitMap = std::move(CurrentLVarMap); + CurrentBB = nullptr; + CurrentBlockInfo = nullptr; +} + + +void SExprBuilder::exitCFG(const CFGBlock *Last) { + for (auto *V : IncompleteArgs) { + til::Phi *Ph = dyn_cast<til::Phi>(V->definition()); + if (Ph && Ph->status() == til::Phi::PH_Incomplete) + simplifyIncompleteArg(V, Ph); + } + + CurrentArguments.clear(); + CurrentInstructions.clear(); + IncompleteArgs.clear(); +} + + + +class TILPrinter : public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {}; + + +void printSCFG(CFGWalker &Walker) { + llvm::BumpPtrAllocator Bpa; + til::MemRegionRef Arena(&Bpa); + SExprBuilder builder(Arena); + til::SCFG *Cfg = builder.buildCFG(Walker); + TILPrinter::print(Cfg, llvm::errs()); +} + + + +} // end namespace threadSafety + +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/Analysis/ThreadSafetyLogical.cpp b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafetyLogical.cpp new file mode 100644 index 0000000..facfa11 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafetyLogical.cpp @@ -0,0 +1,112 @@ +//===- ThreadSafetyLogical.cpp ---------------------------------*- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file defines a representation for logical expressions with SExpr leaves +// that are used as part of fact-checking capability expressions. +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/ThreadSafetyLogical.h" + +using namespace llvm; +using namespace clang::threadSafety::lexpr; + +// Implication. We implement De Morgan's Laws by maintaining LNeg and RNeg +// to keep track of whether LHS and RHS are negated. +static bool implies(const LExpr *LHS, bool LNeg, const LExpr *RHS, bool RNeg) { + // In comments below, we write => for implication. + + // Calculates the logical AND implication operator. + const auto LeftAndOperator = [=](const BinOp *A) { + return implies(A->left(), LNeg, RHS, RNeg) && + implies(A->right(), LNeg, RHS, RNeg); + }; + const auto RightAndOperator = [=](const BinOp *A) { + return implies(LHS, LNeg, A->left(), RNeg) && + implies(LHS, LNeg, A->right(), RNeg); + }; + + // Calculates the logical OR implication operator. + const auto LeftOrOperator = [=](const BinOp *A) { + return implies(A->left(), LNeg, RHS, RNeg) || + implies(A->right(), LNeg, RHS, RNeg); + }; + const auto RightOrOperator = [=](const BinOp *A) { + return implies(LHS, LNeg, A->left(), RNeg) || + implies(LHS, LNeg, A->right(), RNeg); + }; + + // Recurse on right. + switch (RHS->kind()) { + case LExpr::And: + // When performing right recursion: + // C => A & B [if] C => A and C => B + // When performing right recursion (negated): + // C => !(A & B) [if] C => !A | !B [===] C => !A or C => !B + return RNeg ? RightOrOperator(cast<And>(RHS)) + : RightAndOperator(cast<And>(RHS)); + case LExpr::Or: + // When performing right recursion: + // C => (A | B) [if] C => A or C => B + // When performing right recursion (negated): + // C => !(A | B) [if] C => !A & !B [===] C => !A and C => !B + return RNeg ? RightAndOperator(cast<Or>(RHS)) + : RightOrOperator(cast<Or>(RHS)); + case LExpr::Not: + // Note that C => !A is very different from !(C => A). It would be incorrect + // to return !implies(LHS, RHS). + return implies(LHS, LNeg, cast<Not>(RHS)->exp(), !RNeg); + case LExpr::Terminal: + // After reaching the terminal, it's time to recurse on the left. + break; + } + + // RHS is now a terminal. Recurse on Left. + switch (LHS->kind()) { + case LExpr::And: + // When performing left recursion: + // A & B => C [if] A => C or B => C + // When performing left recursion (negated): + // !(A & B) => C [if] !A | !B => C [===] !A => C and !B => C + return LNeg ? LeftAndOperator(cast<And>(LHS)) + : LeftOrOperator(cast<And>(LHS)); + case LExpr::Or: + // When performing left recursion: + // A | B => C [if] A => C and B => C + // When performing left recursion (negated): + // !(A | B) => C [if] !A & !B => C [===] !A => C or !B => C + return LNeg ? LeftOrOperator(cast<Or>(LHS)) + : LeftAndOperator(cast<Or>(LHS)); + case LExpr::Not: + // Note that A => !C is very different from !(A => C). It would be incorrect + // to return !implies(LHS, RHS). + return implies(cast<Not>(LHS)->exp(), !LNeg, RHS, RNeg); + case LExpr::Terminal: + // After reaching the terminal, it's time to perform identity comparisons. + break; + } + + // A => A + // !A => !A + if (LNeg != RNeg) + return false; + + // FIXME -- this should compare SExprs for equality, not pointer equality. + return cast<Terminal>(LHS)->expr() == cast<Terminal>(RHS)->expr(); +} + +namespace clang { +namespace threadSafety { +namespace lexpr { + +bool implies(const LExpr *LHS, const LExpr *RHS) { + // Start out by assuming that LHS and RHS are not negated. + return ::implies(LHS, false, RHS, false); +} +} +} +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/ThreadSafetyTIL.cpp b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafetyTIL.cpp new file mode 100644 index 0000000..f67cbb9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafetyTIL.cpp @@ -0,0 +1,153 @@ +//===- ThreadSafetyTIL.cpp -------------------------------------*- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT in the llvm repository for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/ThreadSafetyTIL.h" +#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h" + +namespace clang { +namespace threadSafety { +namespace til { + + +StringRef getUnaryOpcodeString(TIL_UnaryOpcode Op) { + switch (Op) { + case UOP_Minus: return "-"; + case UOP_BitNot: return "~"; + case UOP_LogicNot: return "!"; + } + return ""; +} + + +StringRef getBinaryOpcodeString(TIL_BinaryOpcode Op) { + switch (Op) { + case BOP_Mul: return "*"; + case BOP_Div: return "/"; + case BOP_Rem: return "%"; + case BOP_Add: return "+"; + case BOP_Sub: return "-"; + case BOP_Shl: return "<<"; + case BOP_Shr: return ">>"; + case BOP_BitAnd: return "&"; + case BOP_BitXor: return "^"; + case BOP_BitOr: return "|"; + case BOP_Eq: return "=="; + case BOP_Neq: return "!="; + case BOP_Lt: return "<"; + case BOP_Leq: return "<="; + case BOP_LogicAnd: return "&&"; + case BOP_LogicOr: return "||"; + } + return ""; +} + + +unsigned BasicBlock::addPredecessor(BasicBlock *Pred) { + unsigned Idx = Predecessors.size(); + Predecessors.reserveCheck(1, Arena); + Predecessors.push_back(Pred); + for (Variable *V : Args) { + if (Phi* Ph = dyn_cast<Phi>(V->definition())) { + Ph->values().reserveCheck(1, Arena); + Ph->values().push_back(nullptr); + } + } + return Idx; +} + +void BasicBlock::reservePredecessors(unsigned NumPreds) { + Predecessors.reserve(NumPreds, Arena); + for (Variable *V : Args) { + if (Phi* Ph = dyn_cast<Phi>(V->definition())) { + Ph->values().reserve(NumPreds, Arena); + } + } +} + +void BasicBlock::renumberVars() { + unsigned VID = 0; + for (Variable *V : Args) { + V->setID(BlockID, VID++); + } + for (Variable *V : Instrs) { + V->setID(BlockID, VID++); + } +} + +void SCFG::renumberVars() { + for (BasicBlock *B : Blocks) { + B->renumberVars(); + } +} + + + + +// If E is a variable, then trace back through any aliases or redundant +// Phi nodes to find the canonical definition. +SExpr *getCanonicalVal(SExpr *E) { + while (auto *V = dyn_cast<Variable>(E)) { + SExpr *D; + do { + if (V->kind() != Variable::VK_Let) + return V; + D = V->definition(); + auto *V2 = dyn_cast<Variable>(D); + if (V2) + V = V2; + else + break; + } while (true); + + if (ThreadSafetyTIL::isTrivial(D)) + return D; + + if (Phi *Ph = dyn_cast<Phi>(D)) { + if (Ph->status() == Phi::PH_Incomplete) + simplifyIncompleteArg(V, Ph); + + if (Ph->status() == Phi::PH_SingleVal) { + E = Ph->values()[0]; + continue; + } + } + return V; + } + return E; +} + + +// Trace the arguments of an incomplete Phi node to see if they have the same +// canonical definition. If so, mark the Phi node as redundant. +// getCanonicalVal() will recursively call simplifyIncompletePhi(). +void simplifyIncompleteArg(Variable *V, til::Phi *Ph) { + assert(Ph && Ph->status() == Phi::PH_Incomplete); + + // eliminate infinite recursion -- assume that this node is not redundant. + Ph->setStatus(Phi::PH_MultiVal); + + SExpr *E0 = getCanonicalVal(Ph->values()[0]); + for (unsigned i=1, n=Ph->values().size(); i<n; ++i) { + SExpr *Ei = getCanonicalVal(Ph->values()[i]); + if (Ei == V) + continue; // Recursive reference to itself. Don't count. + if (Ei != E0) { + return; // Status is already set to MultiVal. + } + } + Ph->setStatus(Phi::PH_SingleVal); + // Eliminate Redundant Phi node. + V->setDefinition(Ph->values()[0]); +} + + +} // end namespace til +} // end namespace threadSafety +} // end namespace clang + diff --git a/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp b/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp index 332c02c..f5c786a 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp @@ -34,7 +34,7 @@ using namespace clang; static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) { if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() && - !vd->isExceptionVariable() && + !vd->isExceptionVariable() && !vd->isInitCapture() && vd->getDeclContext() == dc) { QualType ty = vd->getType(); return ty->isScalarType() || ty->isVectorType(); @@ -236,7 +236,7 @@ void DataflowWorklist::enqueueSuccessors(const clang::CFGBlock *block) { } const CFGBlock *DataflowWorklist::dequeue() { - const CFGBlock *B = 0; + const CFGBlock *B = nullptr; // First dequeue from the worklist. This can represent // updates along backedges that we want propagated as quickly as possible. @@ -250,7 +250,7 @@ const CFGBlock *DataflowWorklist::dequeue() { ++PO_I; } else { - return 0; + return nullptr; } assert(enqueuedBlocks[B->getBlockID()] == true); @@ -295,7 +295,7 @@ static FindVarResult findVar(const Expr *E, const DeclContext *DC) { if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) if (isTrackedVar(VD, DC)) return FindVarResult(VD, DRE); - return FindVarResult(0, 0); + return FindVarResult(nullptr, nullptr); } /// \brief Classify each DeclRefExpr as an initialization or a use. Any @@ -353,7 +353,7 @@ static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) { if (DRE && DRE->getDecl() == VD) return DRE; } - return 0; + return nullptr; } void ClassifyRefs::classify(const Expr *E, Class C) { @@ -373,9 +373,8 @@ void ClassifyRefs::classify(const Expr *E, Class C) { } void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) { - for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); - DI != DE; ++DI) { - VarDecl *VD = dyn_cast<VarDecl>(*DI); + for (auto *DI : DS->decls()) { + VarDecl *VD = dyn_cast<VarDecl>(DI); if (VD && isTrackedVar(VD)) if (const DeclRefExpr *DRE = getSelfInitExpr(VD)) Classification[DRE] = SelfInit; @@ -535,12 +534,15 @@ public: for (CFGBlock::const_pred_iterator I = B->pred_begin(), E = B->pred_end(); I != E; ++I) { const CFGBlock *Pred = *I; + if (!Pred) + continue; + Value AtPredExit = vals.getValue(Pred, B, vd); if (AtPredExit == Initialized) // This block initializes the variable. continue; if (AtPredExit == MayUninitialized && - vals.getValue(B, 0, vd) == Uninitialized) { + vals.getValue(B, nullptr, vd) == Uninitialized) { // This block declares the variable (uninitialized), and is reachable // from a block that initializes the variable. We can't guarantee to // give an earlier location for the diagnostic (and it appears that @@ -630,12 +632,11 @@ void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) { void TransferFunctions::VisitBlockExpr(BlockExpr *be) { const BlockDecl *bd = be->getBlockDecl(); - for (BlockDecl::capture_const_iterator i = bd->capture_begin(), - e = bd->capture_end() ; i != e; ++i) { - const VarDecl *vd = i->getVariable(); + for (const auto &I : bd->captures()) { + const VarDecl *vd = I.getVariable(); if (!isTrackedVar(vd)) continue; - if (i->isByRef()) { + if (I.isByRef()) { vals[vd] = Initialized; continue; } @@ -691,9 +692,8 @@ void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) { } void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { - for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); - DI != DE; ++DI) { - VarDecl *VD = dyn_cast<VarDecl>(*DI); + for (auto *DI : DS->decls()) { + VarDecl *VD = dyn_cast<VarDecl>(DI); if (VD && isTrackedVar(VD)) { if (getSelfInitExpr(VD)) { // If the initializer consists solely of a reference to itself, we @@ -751,6 +751,8 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg, for (CFGBlock::const_pred_iterator I = block->pred_begin(), E = block->pred_end(); I != E; ++I) { const CFGBlock *pred = *I; + if (!pred) + continue; if (wasAnalyzed[pred->getBlockID()]) { vals.mergeIntoScratch(vals.getValueVector(pred), isFirst); isFirst = false; @@ -787,8 +789,8 @@ struct PruneBlocksHandler : public UninitVariablesHandler { /// The current block to scribble use information. unsigned currentBlock; - virtual void handleUseOfUninitVariable(const VarDecl *vd, - const UninitUse &use) { + void handleUseOfUninitVariable(const VarDecl *vd, + const UninitUse &use) override { hadUse[currentBlock] = true; hadAnyUse = true; } @@ -796,7 +798,7 @@ struct PruneBlocksHandler : public UninitVariablesHandler { /// Called when the uninitialized variable analysis detects the /// idiom 'int x = x'. All other uses of 'x' within the initializer /// are handled by handleUseOfUninitVariable. - virtual void handleSelfInit(const VarDecl *vd) { + void handleSelfInit(const VarDecl *vd) override { hadUse[currentBlock] = true; hadAnyUse = true; } |