Author: george.karpenkov Date: Fri Oct 20 16:29:59 2017 New Revision: 316249
URL: http://llvm.org/viewvc/llvm-project?rev=316249&view=rev Log: [Analyzer] Correctly handle parameters passed by reference when bodyfarming std::call_once Explicitly not supporting functor objects. Differential Revision: https://reviews.llvm.org/D39031 Modified: cfe/trunk/lib/Analysis/BodyFarm.cpp cfe/trunk/test/Analysis/call_once.cpp Modified: cfe/trunk/lib/Analysis/BodyFarm.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/BodyFarm.cpp?rev=316249&r1=316248&r2=316249&view=diff ============================================================================== --- cfe/trunk/lib/Analysis/BodyFarm.cpp (original) +++ cfe/trunk/lib/Analysis/BodyFarm.cpp Fri Oct 20 16:29:59 2017 @@ -264,11 +264,8 @@ static CallExpr *create_call_once_funcpt static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M, const ParmVarDecl *Callback, - QualType CallbackType, + CXXRecordDecl *CallbackDecl, ArrayRef<Expr *> 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 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<RecordDecl>(FlagType->getAsTagDecl()); @@ -348,28 +348,58 @@ static Stmt *create_call_once(ASTContext 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<Expr *, 5> 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<FunctionProtoType>(); + } else { + CallbackFunctionType = + CallbackType->getPointeeType()->getAs<FunctionProtoType>(); + } - // 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. Modified: cfe/trunk/test/Analysis/call_once.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/call_once.cpp?rev=316249&r1=316248&r2=316249&view=diff ============================================================================== --- cfe/trunk/test/Analysis/call_once.cpp (original) +++ cfe/trunk/test/Analysis/call_once.cpp Fri Oct 20 16:29:59 2017 @@ -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}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits