aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Analysis
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2014-11-24 09:15:30 +0000
committerDimitry Andric <dim@FreeBSD.org>2014-11-24 09:15:30 +0000
commit9f4dbff6669c8037f3b036bcf580d14f1a4f12a5 (patch)
tree47df2c12b57214af6c31e47404b005675b8b7ffc /lib/Analysis
parentf73d5f23a889b93d89ddef61ac0995df40286bb8 (diff)
downloadsrc-9f4dbff6669c8037f3b036bcf580d14f1a4f12a5.tar.gz
src-9f4dbff6669c8037f3b036bcf580d14f1a4f12a5.zip
Vendor import of clang RELEASE_350/final tag r216957 (effectively, 3.5.0 release):vendor/clang/clang-release_350-r216957
Notes
Notes: svn path=/vendor/clang/dist/; revision=274958 svn path=/vendor/clang/clang-release_350-r216957/; revision=274959; tag=vendor/clang/clang-release_350-r216957
Diffstat (limited to 'lib/Analysis')
-rw-r--r--lib/Analysis/AnalysisDeclContext.cpp86
-rw-r--r--lib/Analysis/BodyFarm.cpp164
-rw-r--r--lib/Analysis/BodyFarm.h7
-rw-r--r--lib/Analysis/CFG.cpp845
-rw-r--r--lib/Analysis/CFGReachabilityAnalysis.cpp3
-rw-r--r--lib/Analysis/CFGStmtMap.cpp6
-rw-r--r--lib/Analysis/CMakeLists.txt23
-rw-r--r--lib/Analysis/CallGraph.cpp26
-rw-r--r--lib/Analysis/Consumed.cpp712
-rw-r--r--lib/Analysis/FormatString.cpp6
-rw-r--r--lib/Analysis/FormatStringParsing.h4
-rw-r--r--lib/Analysis/LiveVariables.cpp67
-rw-r--r--lib/Analysis/PostOrderCFGView.cpp2
-rw-r--r--lib/Analysis/PrintfFormatString.cpp6
-rw-r--r--lib/Analysis/ProgramPoint.cpp7
-rw-r--r--lib/Analysis/PseudoConstantAnalysis.cpp7
-rw-r--r--lib/Analysis/ReachableCode.cpp515
-rw-r--r--lib/Analysis/ScanfFormatString.cpp34
-rw-r--r--lib/Analysis/ThreadSafety.cpp791
-rw-r--r--lib/Analysis/ThreadSafetyCommon.cpp794
-rw-r--r--lib/Analysis/ThreadSafetyLogical.cpp112
-rw-r--r--lib/Analysis/ThreadSafetyTIL.cpp153
-rw-r--r--lib/Analysis/UninitializedValues.cpp40
23 files changed, 3112 insertions, 1298 deletions
diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp
index 465f0c383486..90d4b13b88be 100644
--- a/lib/Analysis/AnalysisDeclContext.cpp
+++ b/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/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp
index 4d5c2ee236f9..316a18b421b5 100644
--- a/lib/Analysis/BodyFarm.cpp
+++ b/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/lib/Analysis/BodyFarm.h b/lib/Analysis/BodyFarm.h
index 96f61df40d7f..2d200fb755c1 100644
--- a/lib/Analysis/BodyFarm.h
+++ b/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/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 8b8c573feade..842a385fbcdb 100644
--- a/lib/Analysis/CFG.cpp
+++ b/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/lib/Analysis/CFGReachabilityAnalysis.cpp b/lib/Analysis/CFGReachabilityAnalysis.cpp
index 492e66fe8172..4ae135f1ea77 100644
--- a/lib/Analysis/CFGReachabilityAnalysis.cpp
+++ b/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/lib/Analysis/CFGStmtMap.cpp b/lib/Analysis/CFGStmtMap.cpp
index 87c2f5bdc130..19b80191f7a7 100644
--- a/lib/Analysis/CFGStmtMap.cpp
+++ b/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/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt
index deab8f1f22fd..461ffb0900bb 100644
--- a/lib/Analysis/CMakeLists.txt
+++ b/lib/Analysis/CMakeLists.txt
@@ -1,3 +1,7 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
add_clang_library(clangAnalysis
AnalysisDeclContext.cpp
BodyFarm.cpp
@@ -18,20 +22,13 @@ add_clang_library(clangAnalysis
ReachableCode.cpp
ScanfFormatString.cpp
ThreadSafety.cpp
+ ThreadSafetyCommon.cpp
+ ThreadSafetyLogical.cpp
+ ThreadSafetyTIL.cpp
UninitializedValues.cpp
- )
-add_dependencies(clangAnalysis
- ClangAttrClasses
- ClangAttrList
- ClangCommentNodes
- ClangDiagnosticCommon
- ClangDeclNodes
- ClangDiagnosticAnalysis
- ClangStmtNodes
- )
-
-target_link_libraries(clangAnalysis
- clangBasic
+ LINK_LIBS
clangAST
+ clangBasic
+ clangLex
)
diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp
index 33870158b384..f41a96d30ea5 100644
--- a/lib/Analysis/CallGraph.cpp
+++ b/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/lib/Analysis/Consumed.cpp b/lib/Analysis/Consumed.cpp
index e5ec3e6b927d..2b2da2c69a41 100644
--- a/lib/Analysis/Consumed.cpp
+++ b/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/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp
index 43ecc6682e3f..851b97e5bc1d 100644
--- a/lib/Analysis/FormatString.cpp
+++ b/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";
@@ -551,7 +551,7 @@ const char *ConversionSpecifier::toString() const {
// GlibC specific specifiers.
case PrintErrno: return "m";
}
- return NULL;
+ return nullptr;
}
Optional<ConversionSpecifier>
diff --git a/lib/Analysis/FormatStringParsing.h b/lib/Analysis/FormatStringParsing.h
index 6b251230136a..fba318042cb0 100644
--- a/lib/Analysis/FormatStringParsing.h
+++ b/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/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp
index 9e5ec557bca2..3d6fc039fd77 100644
--- a/lib/Analysis/LiveVariables.cpp
+++ b/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/lib/Analysis/PostOrderCFGView.cpp b/lib/Analysis/PostOrderCFGView.cpp
index cfd66f7aa1f2..5a3c8182a140 100644
--- a/lib/Analysis/PostOrderCFGView.cpp
+++ b/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/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index f21b407bcb51..082a8327a346 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/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/lib/Analysis/ProgramPoint.cpp b/lib/Analysis/ProgramPoint.cpp
index 7d67e8a91c8a..26b59bb71de2 100644
--- a/lib/Analysis/ProgramPoint.cpp
+++ b/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/lib/Analysis/PseudoConstantAnalysis.cpp b/lib/Analysis/PseudoConstantAnalysis.cpp
index 5d659ce5851f..3f96ca877f19 100644
--- a/lib/Analysis/PseudoConstantAnalysis.cpp
+++ b/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/lib/Analysis/ReachableCode.cpp b/lib/Analysis/ReachableCode.cpp
index a2d19c050829..b4a72a7f8006 100644
--- a/lib/Analysis/ReachableCode.cpp
+++ b/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/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp
index f5ce84fe3615..ed286274950b 100644
--- a/lib/Analysis/ScanfFormatString.cpp
+++ b/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/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 6e0e1732bae9..11df61f80fa0 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/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/lib/Analysis/ThreadSafetyCommon.cpp b/lib/Analysis/ThreadSafetyCommon.cpp
new file mode 100644
index 000000000000..da88b78126fa
--- /dev/null
+++ b/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/lib/Analysis/ThreadSafetyLogical.cpp b/lib/Analysis/ThreadSafetyLogical.cpp
new file mode 100644
index 000000000000..facfa11a39ab
--- /dev/null
+++ b/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/lib/Analysis/ThreadSafetyTIL.cpp b/lib/Analysis/ThreadSafetyTIL.cpp
new file mode 100644
index 000000000000..f67cbb90e5ab
--- /dev/null
+++ b/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/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp
index 332c02cf1852..f5c786ac3456 100644
--- a/lib/Analysis/UninitializedValues.cpp
+++ b/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;
}