r314571 - [Analyzer] Synthesize function body for std::call_once
Alexander Kornienko via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 7 03:56:57 PDT 2017
This revision might be the cause of
https://bugs.llvm.org/show_bug.cgi?id=34869. I'm still working on a reduced
test case.
On Sat, Sep 30, 2017 at 2:03 AM, George Karpenkov via cfe-commits <
cfe-commits at lists.llvm.org> wrote:
> Author: george.karpenkov
> Date: Fri Sep 29 17:03:22 2017
> New Revision: 314571
>
> URL: http://llvm.org/viewvc/llvm-project?rev=314571&view=rev
> Log:
> [Analyzer] Synthesize function body for std::call_once
>
> Differential Revision: https://reviews.llvm.org/D37840
>
> Added:
> cfe/trunk/test/Analysis/call_once.cpp
> Modified:
> cfe/trunk/lib/Analysis/BodyFarm.cpp
>
> Modified: cfe/trunk/lib/Analysis/BodyFarm.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/
> Analysis/BodyFarm.cpp?rev=314571&r1=314570&r2=314571&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/Analysis/BodyFarm.cpp (original)
> +++ cfe/trunk/lib/Analysis/BodyFarm.cpp Fri Sep 29 17:03:22 2017
> @@ -14,11 +14,18 @@
>
> #include "BodyFarm.h"
> #include "clang/AST/ASTContext.h"
> +#include "clang/AST/CXXInheritance.h"
> #include "clang/AST/Decl.h"
> #include "clang/AST/Expr.h"
> +#include "clang/AST/ExprCXX.h"
> #include "clang/AST/ExprObjC.h"
> +#include "clang/AST/NestedNameSpecifier.h"
> #include "clang/Analysis/CodeInjector.h"
> +#include "clang/Basic/OperatorKinds.h"
> #include "llvm/ADT/StringSwitch.h"
> +#include "llvm/Support/Debug.h"
> +
> +#define DEBUG_TYPE "body-farm"
>
> using namespace clang;
>
> @@ -55,7 +62,9 @@ public:
> CompoundStmt *makeCompound(ArrayRef<Stmt*>);
>
> /// Create a new DeclRefExpr for the referenced variable.
> - DeclRefExpr *makeDeclRefExpr(const VarDecl *D);
> + DeclRefExpr *makeDeclRefExpr(const VarDecl *D,
> + bool RefersToEnclosingVariableOrCapture =
> false,
> + bool GetNonReferenceType = false);
>
> /// Create a new UnaryOperator representing a dereference.
> UnaryOperator *makeDereference(const Expr *Arg, QualType Ty);
> @@ -66,9 +75,24 @@ public:
> /// Create an implicit cast to a builtin boolean type.
> ImplicitCastExpr *makeIntegralCastToBoolean(const Expr *Arg);
>
> - // Create an implicit cast for lvalue-to-rvaluate conversions.
> + /// Create an implicit cast for lvalue-to-rvaluate conversions.
> ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg, QualType Ty);
>
> + /// Create an implicit cast for lvalue-to-rvaluate conversions.
> + ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg,
> + bool GetNonReferenceType = false);
> +
> + /// Make RValue out of variable declaration, creating a temporary
> + /// DeclRefExpr in the process.
> + ImplicitCastExpr *
> + makeLvalueToRvalue(const VarDecl *Decl,
> + bool RefersToEnclosingVariableOrCapture = false,
> + bool GetNonReferenceType = false);
> +
> + /// Create an implicit cast of the given type.
> + ImplicitCastExpr *makeImplicitCast(const Expr *Arg, QualType Ty,
> + CastKind CK = CK_LValueToRValue);
> +
> /// Create an Objective-C bool literal.
> ObjCBoolLiteralExpr *makeObjCBool(bool Val);
>
> @@ -78,6 +102,18 @@ public:
> /// Create a Return statement.
> ReturnStmt *makeReturn(const Expr *RetVal);
>
> + /// Create an integer literal.
> + IntegerLiteral *makeIntegerLiteral(uint64_t value);
> +
> + /// Create a member expression.
> + MemberExpr *makeMemberExpression(Expr *base, ValueDecl *MemberDecl,
> + bool IsArrow = false,
> + ExprValueKind ValueKind = VK_LValue);
> +
> + /// Returns a *first* member field of a record declaration with a given
> name.
> + /// \return an nullptr if no member with such a name exists.
> + NamedDecl *findMemberField(const CXXRecordDecl *RD, StringRef Name);
> +
> private:
> ASTContext &C;
> };
> @@ -106,16 +142,16 @@ CompoundStmt *ASTMaker::makeCompound(Arr
> return new (C) CompoundStmt(C, Stmts, SourceLocation(),
> SourceLocation());
> }
>
> -DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D) {
> - DeclRefExpr *DR =
> - DeclRefExpr::Create(/* Ctx = */ C,
> - /* QualifierLoc = */ NestedNameSpecifierLoc(),
> - /* TemplateKWLoc = */ SourceLocation(),
> - /* D = */ const_cast<VarDecl*>(D),
> - /* RefersToEnclosingVariableOrCapture = */ false,
> - /* NameLoc = */ SourceLocation(),
> - /* T = */ D->getType(),
> - /* VK = */ VK_LValue);
> +DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D,
> + bool RefersToEnclosingVariableOrCap
> ture,
> + bool GetNonReferenceType) {
> + auto Type = D->getType();
> + if (GetNonReferenceType)
> + Type = Type.getNonReferenceType();
> +
> + DeclRefExpr *DR = DeclRefExpr::Create(
> + C, NestedNameSpecifierLoc(), SourceLocation(), const_cast<VarDecl
> *>(D),
> + RefersToEnclosingVariableOrCapture, SourceLocation(), Type,
> VK_LValue);
> return DR;
> }
>
> @@ -125,8 +161,38 @@ UnaryOperator *ASTMaker::makeDereference
> }
>
> ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType
> Ty) {
> - return ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue,
> - const_cast<Expr*>(Arg), nullptr,
> VK_RValue);
> + return makeImplicitCast(Arg, Ty, CK_LValueToRValue);
> +}
> +
> +ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg,
> + bool GetNonReferenceType) {
> +
> + QualType Type = Arg->getType();
> + if (GetNonReferenceType)
> + Type = Type.getNonReferenceType();
> + return makeImplicitCast(Arg, Type, CK_LValueToRValue);
> +}
> +
> +ImplicitCastExpr *
> +ASTMaker::makeLvalueToRvalue(const VarDecl *Arg,
> + bool RefersToEnclosingVariableOrCapture,
> + bool GetNonReferenceType) {
> + auto Type = Arg->getType();
> + if (GetNonReferenceType)
> + Type = Type.getNonReferenceType();
> + return makeLvalueToRvalue(makeDeclRefExpr(Arg,
> + RefersToEnclosingVariableOrCap
> ture,
> + GetNonReferenceType),
> + Type);
> +}
> +
> +ImplicitCastExpr *ASTMaker::makeImplicitCast(const Expr *Arg, QualType
> Ty,
> + CastKind CK) {
> + return ImplicitCastExpr::Create(C, Ty,
> + /* CastKind= */ CK,
> + /* Expr= */ const_cast<Expr *>(Arg),
> + /* CXXCastPath= */ nullptr,
> + /* ExprValueKind= */ VK_RValue);
> }
>
> Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) {
> @@ -161,12 +227,196 @@ ReturnStmt *ASTMaker::makeReturn(const E
> nullptr);
> }
>
> +IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t value) {
> + return IntegerLiteral::Create(C,
> + llvm::APInt(
> + /*numBits=*/C.getTypeSize(C.IntTy),
> value),
> + /*QualType=*/C.IntTy, SourceLocation());
> +}
> +
> +MemberExpr *ASTMaker::makeMemberExpression(Expr *base, ValueDecl
> *MemberDecl,
> + bool IsArrow,
> + ExprValueKind ValueKind) {
> +
> + DeclAccessPair FoundDecl = DeclAccessPair::make(MemberDecl, AS_public);
> + return MemberExpr::Create(
> + C, base, IsArrow, SourceLocation(), NestedNameSpecifierLoc(),
> + SourceLocation(), MemberDecl, FoundDecl,
> + DeclarationNameInfo(MemberDecl->getDeclName(), SourceLocation()),
> + /* TemplateArgumentListInfo= */ nullptr, MemberDecl->getType(),
> ValueKind,
> + OK_Ordinary);
> +}
> +
> +NamedDecl *ASTMaker::findMemberField(const CXXRecordDecl *RD, StringRef
> Name) {
> +
> + CXXBasePaths Paths(
> + /* FindAmbiguities=*/false,
> + /* RecordPaths=*/false,
> + /* DetectVirtual= */ false);
> + const IdentifierInfo &II = C.Idents.get(Name);
> + DeclarationName DeclName = C.DeclarationNames.getIdentifier(&II);
> +
> + DeclContextLookupResult Decls = RD->lookup(DeclName);
> + for (NamedDecl *FoundDecl : Decls)
> + if (!FoundDecl->getDeclContext()->isFunctionOrMethod())
> + return FoundDecl;
> +
> + return nullptr;
> +}
> +
> //===-------------------------------------------------------
> ---------------===//
> // Creation functions for faux ASTs.
> //===-------------------------------------------------------
> ---------------===//
>
> typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D);
>
> +static CallExpr *
> +create_call_once_funcptr_call(ASTContext &C, ASTMaker M,
> + const ParmVarDecl *Callback,
> + SmallVectorImpl<Expr *> &CallArgs) {
> +
> + return new (C) CallExpr(
> + /*ASTContext=*/C,
> + /*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Callback),
> + /*args=*/CallArgs,
> + /*QualType=*/C.VoidTy,
> + /*ExprValueType=*/VK_RValue,
> + /*SourceLocation=*/SourceLocation());
> +}
> +
> +static CallExpr *
> +create_call_once_lambda_call(ASTContext &C, ASTMaker M,
> + const ParmVarDecl *Callback, QualType
> CallbackType,
> + SmallVectorImpl<Expr *> &CallArgs) {
> +
> + CXXRecordDecl *CallbackDecl = CallbackType->getAsCXXRecordDecl();
> +
> + assert(CallbackDecl != nullptr);
> + assert(CallbackDecl->isLambda());
> + FunctionDecl *callOperatorDecl = CallbackDecl->getLambdaCallOperator();
> + assert(callOperatorDecl != nullptr);
> +
> + DeclRefExpr *callOperatorDeclRef =
> + DeclRefExpr::Create(/* Ctx = */ C,
> + /* QualifierLoc = */ NestedNameSpecifierLoc(),
> + /* TemplateKWLoc = */ SourceLocation(),
> + const_cast<FunctionDecl *>(callOperatorDecl),
> + /* RefersToEnclosingVariableOrCapture= */
> false,
> + /* NameLoc = */ SourceLocation(),
> + /* T = */ callOperatorDecl->getType(),
> + /* VK = */ VK_LValue);
> +
> + CallArgs.insert(
> + CallArgs.begin(),
> + M.makeDeclRefExpr(Callback,
> + /* RefersToEnclosingVariableOrCapture= */ true,
> + /* GetNonReferenceType= */ true));
> +
> + return new (C)
> + CXXOperatorCallExpr(/*AstContext=*/C, OO_Call, callOperatorDeclRef,
> + /*args=*/CallArgs,
> + /*QualType=*/C.VoidTy,
> + /*ExprValueType=*/VK_RValue,
> + /*SourceLocation=*/SourceLocation(),
> FPOptions());
> +}
> +
> +/// Create a fake body for std::call_once.
> +/// Emulates the following function body:
> +///
> +/// \code
> +/// typedef struct once_flag_s {
> +/// unsigned long __state = 0;
> +/// } once_flag;
> +/// template<class Callable>
> +/// void call_once(once_flag& o, Callable func) {
> +/// if (!o.__state) {
> +/// func();
> +/// }
> +/// o.__state = 1;
> +/// }
> +/// \endcode
> +static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
> + DEBUG(llvm::dbgs() << "Generating body for call_once\n");
> +
> + // We need at least two parameters.
> + if (D->param_size() < 2)
> + return nullptr;
> +
> + ASTMaker M(C);
> +
> + const ParmVarDecl *Flag = D->getParamDecl(0);
> + const ParmVarDecl *Callback = D->getParamDecl(1);
> + QualType CallbackType = Callback->getType().getNonReferenceType();
> +
> + SmallVector<Expr *, 5> CallArgs;
> +
> + // 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)));
> +
> + CallExpr *CallbackCall;
> + if (CallbackType->getAsCXXRecordDecl() &&
> + CallbackType->getAsCXXRecordDecl()->isLambda()) {
> +
> + CallbackCall =
> + create_call_once_lambda_call(C, M, Callback, CallbackType,
> CallArgs);
> + } else {
> +
> + // Function pointer case.
> + CallbackCall = create_call_once_funcptr_call(C, M, Callback,
> CallArgs);
> + }
> +
> + QualType FlagType = Flag->getType().getNonReferenceType();
> + DeclRefExpr *FlagDecl =
> + M.makeDeclRefExpr(Flag,
> + /* RefersToEnclosingVariableOrCapture=*/true,
> + /* GetNonReferenceType=*/true);
> +
> + CXXRecordDecl *FlagCXXDecl = FlagType->getAsCXXRecordDecl();
> +
> + // Note: here we are assuming libc++ implementation of call_once,
> + // which has a struct with a field `__state_`.
> + // Body farming might not work for other `call_once` implementations.
> + NamedDecl *FoundDecl = M.findMemberField(FlagCXXDecl, "__state_");
> + ValueDecl *FieldDecl;
> + if (FoundDecl) {
> + FieldDecl = dyn_cast<ValueDecl>(FoundDecl);
> + } else {
> + DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag
> struct, "
> + << "unable to synthesize call_once body, ignoring "
> + << "the call.\n");
> + return nullptr;
> + }
> +
> + MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FieldDecl);
> + assert(Deref->isLValue());
> + QualType DerefType = Deref->getType();
> +
> + // Negation predicate.
> + UnaryOperator *FlagCheck = new (C) UnaryOperator(
> + /* input= */
> + M.makeImplicitCast(M.makeLvalueToRvalue(Deref, DerefType),
> DerefType,
> + CK_IntegralToBoolean),
> + /* opc= */ UO_LNot,
> + /* QualType= */ C.IntTy,
> + /* ExprValueKind= */ VK_RValue,
> + /* ExprObjectKind= */ OK_Ordinary, SourceLocation());
> +
> + // Create assignment.
> + BinaryOperator *FlagAssignment = M.makeAssignment(
> + Deref, M.makeIntegralCast(M.makeIntegerLiteral(1), DerefType),
> DerefType);
> +
> + IfStmt *Out = new (C)
> + IfStmt(C, SourceLocation(),
> + /* IsConstexpr= */ false,
> + /* init= */ nullptr,
> + /* var= */ nullptr,
> + /* cond= */ FlagCheck,
> + /* then= */ M.makeCompound({CallbackCall, FlagAssignment}));
> +
> + return Out;
> +}
> +
> /// Create a fake body for dispatch_once.
> static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
> // Check if we have at least two parameters.
> @@ -202,15 +452,17 @@ static Stmt *create_dispatch_once(ASTCon
> ASTMaker M(C);
>
> // (1) Create the call.
> - DeclRefExpr *DR = M.makeDeclRefExpr(Block);
> - ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty);
> - CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue,
> - SourceLocation());
> + CallExpr *CE = new (C) CallExpr(
> + /*ASTContext=*/C,
> + /*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block),
> + /*args=*/None,
> + /*QualType=*/C.VoidTy,
> + /*ExprValueType=*/VK_RValue,
> + /*SourceLocation=*/SourceLocation());
>
> // (2) Create the assignment to the predicate.
> - IntegerLiteral *IL =
> - IntegerLiteral::Create(C, llvm::APInt(C.getTypeSize(C.IntTy),
> (uint64_t) 1),
> - C.IntTy, SourceLocation());
> + IntegerLiteral *IL = M.makeIntegerLiteral(1);
> +
> BinaryOperator *B =
> M.makeAssignment(
> M.makeDereference(
> @@ -234,13 +486,20 @@ static Stmt *create_dispatch_once(ASTCon
> PredicateTy),
> PredicateTy);
>
> - UnaryOperator *UO = new (C) UnaryOperator(LValToRval, UO_LNot, C.IntTy,
> - VK_RValue, OK_Ordinary,
> - SourceLocation());
> + UnaryOperator *UO = new (C) UnaryOperator(
> + /* input= */ LValToRval,
> + /* opc= */ UO_LNot,
> + /* QualType= */ C.IntTy,
> + /* ExprValueKind= */ VK_RValue,
> + /* ExprObjectKind= */ OK_Ordinary, SourceLocation());
>
> // (5) Create the 'if' statement.
> - IfStmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr,
> nullptr,
> - UO, CS);
> + IfStmt *If = new (C) IfStmt(C, SourceLocation(),
> + /* IsConstexpr= */ false,
> + /* init= */ nullptr,
> + /* var= */ nullptr,
> + /* cond= */ UO,
> + /* then= */ CS);
> return If;
> }
>
> @@ -370,8 +629,9 @@ Stmt *BodyFarm::getBody(const FunctionDe
> if (Name.startswith("OSAtomicCompareAndSwap") ||
> Name.startswith("objc_atomicCompareAndSwap")) {
> FF = create_OSAtomicCompareAndSwap;
> - }
> - else {
> + } else if (Name == "call_once" && D->getDeclContext()->isStdNamespace())
> {
> + FF = create_call_once;
> + } else {
> FF = llvm::StringSwitch<FunctionFarmer>(Name)
> .Case("dispatch_sync", create_dispatch_sync)
> .Case("dispatch_once", create_dispatch_once)
>
> Added: cfe/trunk/test/Analysis/call_once.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
> Analysis/call_once.cpp?rev=314571&view=auto
> ============================================================
> ==================
> --- cfe/trunk/test/Analysis/call_once.cpp (added)
> +++ cfe/trunk/test/Analysis/call_once.cpp Fri Sep 29 17:03:22 2017
> @@ -0,0 +1,233 @@
> +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks
> -analyzer-checker=core,debug.ExprInspection -w -verify %s
> +
> +void clang_analyzer_eval(bool);
> +
> +// Faking std::std::call_once implementation.
> +namespace std {
> +typedef struct once_flag_s {
> + unsigned long __state_ = 0;
> +} once_flag;
> +
> +template <class Callable, class... Args>
> +void call_once(once_flag &o, Callable func, Args... args);
> +} // namespace std
> +
> +// Check with Lambdas.
> +void test_called_warning() {
> + std::once_flag g_initialize;
> + int z;
> +
> + std::call_once(g_initialize, [&] {
> + int *x = nullptr;
> + int y = *x; // expected-warning{{Dereference of null pointer (loaded
> from variable 'x')}}
> + z = 200;
> + });
> +}
> +
> +void test_called_on_path_inside_no_warning() {
> + std::once_flag g_initialize;
> +
> + int *x = nullptr;
> + int y = 100;
> + int z;
> +
> + std::call_once(g_initialize, [&] {
> + z = 200;
> + x = &z;
> + });
> +
> + *x = 100; // no-warning
> + clang_analyzer_eval(z == 100); // expected-warning{{TRUE}}
> +}
> +
> +void test_called_on_path_no_warning() {
> + std::once_flag g_initialize;
> +
> + int *x = nullptr;
> + int y = 100;
> +
> + std::call_once(g_initialize, [&] {
> + x = &y;
> + });
> +
> + *x = 100; // no-warning
> +}
> +
> +void test_called_on_path_warning() {
> + std::once_flag g_initialize;
> +
> + int y = 100;
> + int *x = &y;
> +
> + std::call_once(g_initialize, [&] {
> + x = nullptr;
> + });
> +
> + *x = 100; // expected-warning{{Dereference of null pointer (loaded from
> variable 'x')}}
> +}
> +
> +void test_called_once_warning() {
> + std::once_flag g_initialize;
> +
> + int *x = nullptr;
> + int y = 100;
> +
> + std::call_once(g_initialize, [&] {
> + x = nullptr;
> + });
> +
> + std::call_once(g_initialize, [&] {
> + x = &y;
> + });
> +
> + *x = 100; // expected-warning{{Dereference of null pointer (loaded from
> variable 'x')}}
> +}
> +
> +void test_called_once_no_warning() {
> + std::once_flag g_initialize;
> +
> + int *x = nullptr;
> + int y = 100;
> +
> + std::call_once(g_initialize, [&] {
> + x = &y;
> + });
> +
> + std::call_once(g_initialize, [&] {
> + x = nullptr;
> + });
> +
> + *x = 100; // no-warning
> +}
> +
> +static int global = 0;
> +void funcPointer() {
> + global = 1;
> +}
> +
> +void test_func_pointers() {
> + static std::once_flag flag;
> + std::call_once(flag, &funcPointer);
> + clang_analyzer_eval(global == 1); // expected-warning{{TRUE}}
> +}
> +
> +template <class _Fp>
> +class function; // undefined
> +template <class _Rp, class... _ArgTypes>
> +struct function<_Rp(_ArgTypes...)> {
> + _Rp operator()(_ArgTypes...) const;
> + template <class _Fp>
> + function(_Fp);
> +};
> +
> +// Note: currently we do not support calls to std::function,
> +// but the analyzer should not crash either.
> +void test_function_objects_warning() {
> + int x = 0;
> + int *y = &x;
> +
> + std::once_flag flag;
> +
> + function<void()> func = [&]() {
> + y = nullptr;
> + };
> +
> + std::call_once(flag, func);
> +
> + func();
> + int z = *y;
> +}
> +
> +void test_param_passing_lambda() {
> + std::once_flag flag;
> + int x = 120;
> + int y = 0;
> +
> + std::call_once(flag, [&](int p) {
> + y = p;
> + },
> + x);
> +
> + clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
> +}
> +
> +void test_param_passing_lambda_false() {
> + std::once_flag flag;
> + int x = 120;
> +
> + std::call_once(flag, [&](int p) {
> + x = 0;
> + },
> + x);
> +
> + clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
> +}
> +
> +void test_param_passing_stored_lambda() {
> + std::once_flag flag;
> + int x = 120;
> + int y = 0;
> +
> + auto lambda = [&](int p) {
> + y = p;
> + };
> +
> + std::call_once(flag, lambda, x);
> + clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
> +}
> +
> +void test_multiparam_passing_lambda() {
> + std::once_flag flag;
> + int x = 120;
> +
> + std::call_once(flag, [&](int a, int b, int c) {
> + x = a + b + c;
> + },
> + 1, 2, 3);
> +
> + clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
> + clang_analyzer_eval(x == 6); // expected-warning{{TRUE}}
> +}
> +
> +static int global2 = 0;
> +void test_param_passing_lambda_global() {
> + std::once_flag flag;
> + global2 = 0;
> + std::call_once(flag, [&](int a, int b, int c) {
> + global2 = a + b + c;
> + },
> + 1, 2, 3);
> + clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}}
> +}
> +
> +static int global3 = 0;
> +void funcptr(int a, int b, int c) {
> + global3 = a + b + c;
> +}
> +
> +void test_param_passing_funcptr() {
> + std::once_flag flag;
> + global3 = 0;
> +
> + std::call_once(flag, &funcptr, 1, 2, 3);
> +
> + clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}}
> +}
> +
> +void test_blocks() {
> + global3 = 0;
> + std::once_flag flag;
> + std::call_once(flag, ^{
> + global3 = 120;
> + });
> + clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}}
> +}
> +
> +int call_once() {
> + return 5;
> +}
> +
> +void test_non_std_call_once() {
> + int x = call_once();
> + clang_analyzer_eval(x == 5); // expected-warning{{TRUE}}
> +}
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20171007/81ece291/attachment-0001.html>
More information about the cfe-commits
mailing list