r254702 - [analyzer] Support inlining lambda-converted blocks.
Devin Coughlin via cfe-commits
cfe-commits at lists.llvm.org
Thu Dec 3 21:00:37 PST 2015
Author: dcoughlin
Date: Thu Dec 3 23:00:36 2015
New Revision: 254702
URL: http://llvm.org/viewvc/llvm-project?rev=254702&view=rev
Log:
[analyzer] Support inlining lambda-converted blocks.
clang converts C++ lambdas to blocks with an implicit user-defined conversion
operator method on the lambda record. This method returns a block that captures a copy
of the lambda. To inline a lambda-converted block, the analyzer now calls the lambda
records's call operator method on the lambda captured by the block.
Modified:
cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
cfe/trunk/test/Analysis/lambdas.mm
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h?rev=254702&r1=254701&r2=254702&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h Thu Dec 3 23:00:36 2015
@@ -506,8 +506,57 @@ public:
return BR->getDecl();
}
+ bool isConversionFromLambda() const {
+ const BlockDecl *BD = getDecl();
+ if (!BD)
+ return false;
+
+ return BD->isConversionFromLambda();
+ }
+
+ /// \brief For a block converted from a C++ lambda, returns the block
+ /// VarRegion for the variable holding the captured C++ lambda record.
+ const VarRegion *getRegionStoringCapturedLambda() const {
+ assert(isConversionFromLambda());
+ const BlockDataRegion *BR = getBlockRegion();
+ assert(BR && "Block converted from lambda must have a block region");
+
+ BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(),
+ E = BR->referenced_vars_end();
+ assert(I != E);
+
+ return I.getCapturedRegion();
+ }
+
RuntimeDefinition getRuntimeDefinition() const override {
- return RuntimeDefinition(getDecl());
+ if (!isConversionFromLambda())
+ return RuntimeDefinition(getDecl());
+
+ // Clang converts lambdas to blocks with an implicit user-defined
+ // conversion operator method on the lambda record that looks (roughly)
+ // like:
+ //
+ // typedef R(^block_type)(P1, P2, ...);
+ // operator block_type() const {
+ // auto Lambda = *this;
+ // return ^(P1 p1, P2 p2, ...){
+ // /* return Lambda(p1, p2, ...); */
+ // };
+ // }
+ //
+ // Here R is the return type of the lambda and P1, P2, ... are
+ // its parameter types. 'Lambda' is a fake VarDecl captured by the block
+ // that is initialized to a copy of the the lambda.
+ //
+ // Sema leaves the body of a lambda-converted block empty (it is
+ // produced by CodeGen), so we can't analyze it directly. Instead, we skip
+ // the block body and analyze the operator() method on the the captured
+ // lambda.
+ const VarDecl *LambdaVD = getRegionStoringCapturedLambda()->getDecl();
+ const CXXRecordDecl *LambdaDecl = LambdaVD->getType()->getAsCXXRecordDecl();
+ CXXMethodDecl* LambdaCallOperator = LambdaDecl->getLambdaCallOperator();
+
+ return RuntimeDefinition(LambdaCallOperator);
}
bool argumentsMayEscape() const override {
Modified: cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp?rev=254702&r1=254701&r2=254702&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp Thu Dec 3 23:00:36 2015
@@ -598,10 +598,25 @@ void BlockCall::getExtraInvalidatedValue
void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
BindingsTy &Bindings) const {
- const BlockDecl *D = cast<BlockDecl>(CalleeCtx->getDecl());
SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ ArrayRef<ParmVarDecl*> Params;
+ if (isConversionFromLambda()) {
+ auto *LambdaOperatorDecl = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Params = LambdaOperatorDecl->parameters();
+
+ // For blocks converted from a C++ lambda, the callee declaration is the
+ // operator() method on the the lambda so we bind "this" to
+ // the lambda captured by the block.
+ const VarRegion *CapturedLambdaRegion = getRegionStoringCapturedLambda();
+ SVal ThisVal = loc::MemRegionVal(CapturedLambdaRegion);
+ Loc ThisLoc = SVB.getCXXThis(LambdaOperatorDecl, CalleeCtx);
+ Bindings.push_back(std::make_pair(ThisLoc, ThisVal));
+ } else {
+ Params = cast<BlockDecl>(CalleeCtx->getDecl())->parameters();
+ }
+
addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
- D->parameters());
+ Params);
}
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp?rev=254702&r1=254701&r2=254702&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp Thu Dec 3 23:00:36 2015
@@ -189,8 +189,9 @@ void ExprEngine::VisitBlockExpr(const Bl
CanQualType T = getContext().getCanonicalType(BE->getType());
+ const BlockDecl *BD = BE->getBlockDecl();
// Get the value of the block itself.
- SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T,
+ SVal V = svalBuilder.getBlockPointer(BD, T,
Pred->getLocationContext(),
currBldrCtx->blockCount());
@@ -204,11 +205,32 @@ void ExprEngine::VisitBlockExpr(const Bl
BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
E = BDR->referenced_vars_end();
+ auto CI = BD->capture_begin();
+ auto CE = BD->capture_end();
for (; I != E; ++I) {
- const MemRegion *capturedR = I.getCapturedRegion();
- const MemRegion *originalR = I.getOriginalRegion();
+ const VarRegion *capturedR = I.getCapturedRegion();
+ const VarRegion *originalR = I.getOriginalRegion();
+
+ // If the capture had a copy expression, use the result of evaluating
+ // that expression, otherwise use the original value.
+ // We rely on the invariant that the block declaration's capture variables
+ // are a prefix of the BlockDataRegion's referenced vars (which may include
+ // referenced globals, etc.) to enable fast lookup of the capture for a
+ // given referenced var.
+ const Expr *copyExpr = nullptr;
+ if (CI != CE) {
+ assert(CI->getVariable() == capturedR->getDecl());
+ copyExpr = CI->getCopyExpr();
+ CI++;
+ }
+
if (capturedR != originalR) {
- SVal originalV = State->getSVal(loc::MemRegionVal(originalR));
+ SVal originalV;
+ if (copyExpr) {
+ originalV = State->getSVal(copyExpr, Pred->getLocationContext());
+ } else {
+ originalV = State->getSVal(loc::MemRegionVal(originalR));
+ }
State = State->bindLoc(loc::MemRegionVal(capturedR), originalV);
}
}
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=254702&r1=254701&r2=254702&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Thu Dec 3 23:00:36 2015
@@ -421,7 +421,8 @@ bool ExprEngine::inlineCall(const CallEv
const LocationContext *CurLC = Pred->getLocationContext();
const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame();
const LocationContext *ParentOfCallee = CallerSFC;
- if (Call.getKind() == CE_Block) {
+ if (Call.getKind() == CE_Block &&
+ !cast<BlockCall>(Call).isConversionFromLambda()) {
const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion();
assert(BR && "If we have the block definition we should have its region");
AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D);
Modified: cfe/trunk/test/Analysis/lambdas.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lambdas.mm?rev=254702&r1=254701&r2=254702&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/lambdas.mm (original)
+++ cfe/trunk/test/Analysis/lambdas.mm Thu Dec 3 23:00:36 2015
@@ -70,9 +70,53 @@ void castToBlockAndInline() {
return p;
})(7);
- // FIXME: This should be TRUE. We're not handling lambda to block conversions
- // properly in ExprEngine::VisitBlockExpr.
- clang_analyzer_eval(result == 7); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(result == 7); // expected-warning{{TRUE}}
+}
+
+void castToBlockWithCaptureAndInline() {
+ int y = 7;
+
+ auto lambda = [y]{ return y; };
+ int(^block)() = lambda;
+
+ int result = block();
+ clang_analyzer_eval(result == 7); // expected-warning{{TRUE}}
+}
+
+void castMutableLambdaToBlock() {
+ int x = 0;
+
+ auto lambda = [x]() mutable {
+ x = x + 1;
+ return x;
+ };
+
+ // The block should copy the lambda before capturing.
+ int(^block)() = lambda;
+
+ int r1 = block();
+ clang_analyzer_eval(r1 == 1); // expected-warning{{TRUE}}
+
+ int r2 = block();
+ clang_analyzer_eval(r2 == 2); // expected-warning{{TRUE}}
+
+ // Because block copied the lambda, r3 should be 1.
+ int r3 = lambda();
+ clang_analyzer_eval(r3 == 1); // expected-warning{{TRUE}}
+
+ // Aliasing the block shouldn't copy the lambda.
+ int(^blockAlias)() = block;
+
+ int r4 = blockAlias();
+ clang_analyzer_eval(r4 == 3); // expected-warning{{TRUE}}
+
+ int r5 = block();
+ clang_analyzer_eval(r5 == 4); // expected-warning{{TRUE}}
+
+ // Another copy of lambda
+ int(^blockSecondCopy)() = lambda;
+ int r6 = blockSecondCopy();
+ clang_analyzer_eval(r6 == 2); // expected-warning{{TRUE}}
}
void castLambdaInLocalBlock() {
More information about the cfe-commits
mailing list