From: George Karpenkov Date: Fri, 20 Oct 2017 23:29:59 +0000 (+0000) Subject: [Analyzer] Correctly handle parameters passed by reference when bodyfarming std:... X-Git-Tag: llvmorg-6.0.0-rc1~5240 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=bd4254c69235db8db02449a50b2e946c2241a74f;p=platform%2Fupstream%2Fllvm.git [Analyzer] Correctly handle parameters passed by reference when bodyfarming std::call_once Explicitly not supporting functor objects. Differential Revision: https://reviews.llvm.org/D39031 llvm-svn: 316249 --- diff --git a/clang/lib/Analysis/BodyFarm.cpp b/clang/lib/Analysis/BodyFarm.cpp index 9568ec7..6a8b529 100644 --- a/clang/lib/Analysis/BodyFarm.cpp +++ b/clang/lib/Analysis/BodyFarm.cpp @@ -264,11 +264,8 @@ static CallExpr *create_call_once_funcptr_call(ASTContext &C, ASTMaker M, static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M, const ParmVarDecl *Callback, - QualType CallbackType, + CXXRecordDecl *CallbackDecl, ArrayRef CallArgs) { - - CXXRecordDecl *CallbackDecl = CallbackType->getAsCXXRecordDecl(); - assert(CallbackDecl != nullptr); assert(CallbackDecl->isLambda()); FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOperator(); @@ -319,6 +316,9 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) { const ParmVarDecl *Flag = D->getParamDecl(0); const ParmVarDecl *Callback = D->getParamDecl(1); QualType CallbackType = Callback->getType().getNonReferenceType(); + + // Nullable pointer, non-null iff function is a CXXRecordDecl. + CXXRecordDecl *CallbackRecordDecl = CallbackType->getAsCXXRecordDecl(); QualType FlagType = Flag->getType().getNonReferenceType(); auto *FlagRecordDecl = dyn_cast_or_null(FlagType->getAsTagDecl()); @@ -348,28 +348,58 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) { return nullptr; } - bool isLambdaCall = CallbackType->getAsCXXRecordDecl() && - CallbackType->getAsCXXRecordDecl()->isLambda(); + bool isLambdaCall = CallbackRecordDecl && CallbackRecordDecl->isLambda(); + if (CallbackRecordDecl && !isLambdaCall) { + DEBUG(llvm::dbgs() << "Not supported: synthesizing body for functors when " + << "body farming std::call_once, ignoring the call."); + return nullptr; + } SmallVector CallArgs; + const FunctionProtoType *CallbackFunctionType; + if (isLambdaCall) { - if (isLambdaCall) // Lambda requires callback itself inserted as a first parameter. CallArgs.push_back( M.makeDeclRefExpr(Callback, /* RefersToEnclosingVariableOrCapture= */ true)); + CallbackFunctionType = CallbackRecordDecl->getLambdaCallOperator() + ->getType() + ->getAs(); + } else { + CallbackFunctionType = + CallbackType->getPointeeType()->getAs(); + } - // All arguments past first two ones are passed to the callback. - for (unsigned int i = 2; i < D->getNumParams(); i++) - CallArgs.push_back( - M.makeLvalueToRvalue(D->getParamDecl(i), - /* RefersToEnclosingVariableOrCapture= */ false)); + if (!CallbackFunctionType) + return nullptr; + + // First two arguments are used for the flag and for the callback. + if (D->getNumParams() != CallbackFunctionType->getNumParams() + 2) { + DEBUG(llvm::dbgs() << "Number of params of the callback does not match " + << "the number of params passed to std::call_once, " + << "ignoring the call"); + return nullptr; + } + + // All arguments past first two ones are passed to the callback, + // and we turn lvalues into rvalues if the argument is not passed by + // reference. + for (unsigned int ParamIdx = 2; ParamIdx < D->getNumParams(); ParamIdx++) { + const ParmVarDecl *PDecl = D->getParamDecl(ParamIdx); + Expr *ParamExpr = M.makeDeclRefExpr(PDecl); + if (!CallbackFunctionType->getParamType(ParamIdx - 2)->isReferenceType()) { + QualType PTy = PDecl->getType().getNonReferenceType(); + ParamExpr = M.makeLvalueToRvalue(ParamExpr, PTy); + } + CallArgs.push_back(ParamExpr); + } CallExpr *CallbackCall; if (isLambdaCall) { - CallbackCall = - create_call_once_lambda_call(C, M, Callback, CallbackType, CallArgs); + CallbackCall = create_call_once_lambda_call(C, M, Callback, + CallbackRecordDecl, CallArgs); } else { // Function pointer case. diff --git a/clang/test/Analysis/call_once.cpp b/clang/test/Analysis/call_once.cpp index befddca..db70145 100644 --- a/clang/test/Analysis/call_once.cpp +++ b/clang/test/Analysis/call_once.cpp @@ -249,3 +249,44 @@ void g(); void test_no_segfault_on_different_impl() { std::call_once(g, false); // no-warning } + +void test_lambda_refcapture() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, [&](int &a) { a = 42; }, a); + clang_analyzer_eval(a == 42); // expected-warning{{TRUE}} +} + +void test_lambda_refcapture2() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, [=](int &a) { a = 42; }, a); + clang_analyzer_eval(a == 42); // expected-warning{{TRUE}} +} + +void test_lambda_fail_refcapture() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, [=](int a) { a = 42; }, a); + clang_analyzer_eval(a == 42); // expected-warning{{FALSE}} +} + +void mutator(int ¶m) { + param = 42; +} +void test_reftypes_funcptr() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, &mutator, a); + clang_analyzer_eval(a == 42); // expected-warning{{TRUE}} +} + +void fail_mutator(int param) { + param = 42; +} +void test_mutator_noref() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, &fail_mutator, a); + clang_analyzer_eval(a == 42); // expected-warning{{FALSE}} +}