Skip to content
Snippets Groups Projects
Commit 1f1e821d authored by George Karpenkov's avatar George Karpenkov
Browse files

[analyzer] do not crash on libcxx03 call_once implementation

Addresses https://bugs.llvm.org/show_bug.cgi?id=35075, rdar://35230961

Differential Revision: https://reviews.llvm.org/D39518

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@317293 91177308-0d34-0410-b5e6-96231b3b80d8
parent 4e3af044
No related branches found
No related tags found
No related merge requests found
...@@ -325,6 +325,16 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) { ...@@ -325,6 +325,16 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
const ParmVarDecl *Flag = D->getParamDecl(0); const ParmVarDecl *Flag = D->getParamDecl(0);
const ParmVarDecl *Callback = D->getParamDecl(1); const ParmVarDecl *Callback = D->getParamDecl(1);
if (!Callback->getType()->isReferenceType()) {
llvm::dbgs() << "libcxx03 std::call_once implementation, skipping.\n";
return nullptr;
}
if (!Flag->getType()->isReferenceType()) {
llvm::dbgs() << "unknown std::call_once implementation, skipping.\n";
return nullptr;
}
QualType CallbackType = Callback->getType().getNonReferenceType(); QualType CallbackType = Callback->getType().getNonReferenceType();
// Nullable pointer, non-null iff function is a CXXRecordDecl. // Nullable pointer, non-null iff function is a CXXRecordDecl.
...@@ -346,15 +356,13 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) { ...@@ -346,15 +356,13 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
// Otherwise, try libstdc++ implementation, with a field // Otherwise, try libstdc++ implementation, with a field
// `_M_once` // `_M_once`
if (!FlagFieldDecl) { if (!FlagFieldDecl) {
DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag struct, "
<< "assuming libstdc++ implementation\n");
FlagFieldDecl = M.findMemberField(FlagRecordDecl, "_M_once"); FlagFieldDecl = M.findMemberField(FlagRecordDecl, "_M_once");
} }
if (!FlagFieldDecl) { if (!FlagFieldDecl) {
DEBUG(llvm::dbgs() << "No field _M_once found on std::once flag struct: " DEBUG(llvm::dbgs() << "No field _M_once or __state_ found on "
<< "unknown std::call_once implementation, " << "std::once_flag struct: unknown std::call_once "
<< "ignoring the call"); << "implementation, ignoring the call.");
return nullptr; return nullptr;
} }
...@@ -388,9 +396,9 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) { ...@@ -388,9 +396,9 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
// First two arguments are used for the flag and for the callback. // First two arguments are used for the flag and for the callback.
if (D->getNumParams() != CallbackFunctionType->getNumParams() + 2) { if (D->getNumParams() != CallbackFunctionType->getNumParams() + 2) {
DEBUG(llvm::dbgs() << "Number of params of the callback does not match " DEBUG(llvm::dbgs() << "Types of params of the callback do not match "
<< "the number of params passed to std::call_once, " << "params passed to std::call_once, "
<< "ignoring the call"); << "ignoring the call\n");
return nullptr; return nullptr;
} }
......
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -verify %s // RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -verify %s
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -DEMULATE_LIBSTDCPP -verify %s // RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -DEMULATE_LIBSTDCPP -verify %s
// We do NOT model libcxx03 implementation, but the analyzer should still
// not crash.
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -DEMULATE_LIBCXX03 -verify %s
// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -DEMULATE_LIBCXX03 -DEMULATE_LIBSTDCPP -verify %s
void clang_analyzer_eval(bool); void clang_analyzer_eval(bool);
// Faking std::std::call_once implementation. // Faking std::std::call_once implementation.
...@@ -16,8 +21,13 @@ typedef struct once_flag_s { ...@@ -16,8 +21,13 @@ typedef struct once_flag_s {
} once_flag; } once_flag;
#endif #endif
#ifndef EMULATE_LIBCXX03
template <class Callable, class... Args> template <class Callable, class... Args>
void call_once(once_flag &o, Callable&& func, Args&&... args) {}; void call_once(once_flag &o, Callable&& func, Args&&... args) {};
#else
template <class Callable, class... Args> // libcxx03 call_once
void call_once(once_flag &o, Callable func, Args&&... args) {};
#endif
} // namespace std } // namespace std
...@@ -28,7 +38,9 @@ void test_called_warning() { ...@@ -28,7 +38,9 @@ void test_called_warning() {
std::call_once(g_initialize, [&] { std::call_once(g_initialize, [&] {
int *x = nullptr; int *x = nullptr;
#ifndef EMULATE_LIBCXX03
int y = *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}} int y = *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
#endif
z = 200; z = 200;
}); });
} }
...@@ -45,8 +57,10 @@ void test_called_on_path_inside_no_warning() { ...@@ -45,8 +57,10 @@ void test_called_on_path_inside_no_warning() {
x = &z; x = &z;
}); });
#ifndef EMULATE_LIBCXX03
*x = 100; // no-warning *x = 100; // no-warning
clang_analyzer_eval(z == 100); // expected-warning{{TRUE}} clang_analyzer_eval(z == 100); // expected-warning{{TRUE}}
#endif
} }
void test_called_on_path_no_warning() { void test_called_on_path_no_warning() {
...@@ -59,7 +73,11 @@ void test_called_on_path_no_warning() { ...@@ -59,7 +73,11 @@ void test_called_on_path_no_warning() {
x = &y; x = &y;
}); });
#ifndef EMULATE_LIBCXX03
*x = 100; // no-warning *x = 100; // no-warning
#else
*x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
#endif
} }
void test_called_on_path_warning() { void test_called_on_path_warning() {
...@@ -72,7 +90,9 @@ void test_called_on_path_warning() { ...@@ -72,7 +90,9 @@ void test_called_on_path_warning() {
x = nullptr; x = nullptr;
}); });
#ifndef EMULATE_LIBCXX03
*x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}} *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
#endif
} }
void test_called_once_warning() { void test_called_once_warning() {
...@@ -89,7 +109,9 @@ void test_called_once_warning() { ...@@ -89,7 +109,9 @@ void test_called_once_warning() {
x = &y; x = &y;
}); });
#ifndef EMULATE_LIBCXX03
*x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}} *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
#endif
} }
void test_called_once_no_warning() { void test_called_once_no_warning() {
...@@ -106,7 +128,9 @@ void test_called_once_no_warning() { ...@@ -106,7 +128,9 @@ void test_called_once_no_warning() {
x = nullptr; x = nullptr;
}); });
#ifndef EMULATE_LIBCXX03
*x = 100; // no-warning *x = 100; // no-warning
#endif
} }
static int global = 0; static int global = 0;
...@@ -117,7 +141,9 @@ void funcPointer() { ...@@ -117,7 +141,9 @@ void funcPointer() {
void test_func_pointers() { void test_func_pointers() {
static std::once_flag flag; static std::once_flag flag;
std::call_once(flag, &funcPointer); std::call_once(flag, &funcPointer);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(global == 1); // expected-warning{{TRUE}} clang_analyzer_eval(global == 1); // expected-warning{{TRUE}}
#endif
} }
template <class _Fp> template <class _Fp>
...@@ -157,7 +183,9 @@ void test_param_passing_lambda() { ...@@ -157,7 +183,9 @@ void test_param_passing_lambda() {
}, },
x); x);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(y == 120); // expected-warning{{TRUE}} clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
#endif
} }
void test_param_passing_lambda_false() { void test_param_passing_lambda_false() {
...@@ -169,7 +197,9 @@ void test_param_passing_lambda_false() { ...@@ -169,7 +197,9 @@ void test_param_passing_lambda_false() {
}, },
x); x);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(x == 120); // expected-warning{{FALSE}} clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
#endif
} }
void test_param_passing_stored_lambda() { void test_param_passing_stored_lambda() {
...@@ -182,7 +212,9 @@ void test_param_passing_stored_lambda() { ...@@ -182,7 +212,9 @@ void test_param_passing_stored_lambda() {
}; };
std::call_once(flag, lambda, x); std::call_once(flag, lambda, x);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(y == 120); // expected-warning{{TRUE}} clang_analyzer_eval(y == 120); // expected-warning{{TRUE}}
#endif
} }
void test_multiparam_passing_lambda() { void test_multiparam_passing_lambda() {
...@@ -194,8 +226,10 @@ void test_multiparam_passing_lambda() { ...@@ -194,8 +226,10 @@ void test_multiparam_passing_lambda() {
}, },
1, 2, 3); 1, 2, 3);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(x == 120); // expected-warning{{FALSE}} clang_analyzer_eval(x == 120); // expected-warning{{FALSE}}
clang_analyzer_eval(x == 6); // expected-warning{{TRUE}} clang_analyzer_eval(x == 6); // expected-warning{{TRUE}}
#endif
} }
static int global2 = 0; static int global2 = 0;
...@@ -206,7 +240,9 @@ void test_param_passing_lambda_global() { ...@@ -206,7 +240,9 @@ void test_param_passing_lambda_global() {
global2 = a + b + c; global2 = a + b + c;
}, },
1, 2, 3); 1, 2, 3);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}} clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}}
#endif
} }
static int global3 = 0; static int global3 = 0;
...@@ -220,7 +256,9 @@ void test_param_passing_funcptr() { ...@@ -220,7 +256,9 @@ void test_param_passing_funcptr() {
std::call_once(flag, &funcptr, 1, 2, 3); std::call_once(flag, &funcptr, 1, 2, 3);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}} clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}}
#endif
} }
void test_blocks() { void test_blocks() {
...@@ -229,7 +267,9 @@ void test_blocks() { ...@@ -229,7 +267,9 @@ void test_blocks() {
std::call_once(flag, ^{ std::call_once(flag, ^{
global3 = 120; global3 = 120;
}); });
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}} clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}}
#endif
} }
int call_once() { int call_once() {
...@@ -238,7 +278,9 @@ int call_once() { ...@@ -238,7 +278,9 @@ int call_once() {
void test_non_std_call_once() { void test_non_std_call_once() {
int x = call_once(); int x = call_once();
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(x == 5); // expected-warning{{TRUE}} clang_analyzer_eval(x == 5); // expected-warning{{TRUE}}
#endif
} }
namespace std { namespace std {
...@@ -247,28 +289,36 @@ void call_once(d, e); ...@@ -247,28 +289,36 @@ void call_once(d, e);
} }
void g(); void g();
void test_no_segfault_on_different_impl() { void test_no_segfault_on_different_impl() {
#ifndef EMULATE_LIBCXX03
std::call_once(g, false); // no-warning std::call_once(g, false); // no-warning
#endif
} }
void test_lambda_refcapture() { void test_lambda_refcapture() {
static std::once_flag flag; static std::once_flag flag;
int a = 6; int a = 6;
std::call_once(flag, [&](int &a) { a = 42; }, a); std::call_once(flag, [&](int &a) { a = 42; }, a);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}} clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
#endif
} }
void test_lambda_refcapture2() { void test_lambda_refcapture2() {
static std::once_flag flag; static std::once_flag flag;
int a = 6; int a = 6;
std::call_once(flag, [=](int &a) { a = 42; }, a); std::call_once(flag, [=](int &a) { a = 42; }, a);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}} clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
#endif
} }
void test_lambda_fail_refcapture() { void test_lambda_fail_refcapture() {
static std::once_flag flag; static std::once_flag flag;
int a = 6; int a = 6;
std::call_once(flag, [=](int a) { a = 42; }, a); std::call_once(flag, [=](int a) { a = 42; }, a);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{FALSE}} clang_analyzer_eval(a == 42); // expected-warning{{FALSE}}
#endif
} }
void mutator(int &param) { void mutator(int &param) {
...@@ -278,7 +328,9 @@ void test_reftypes_funcptr() { ...@@ -278,7 +328,9 @@ void test_reftypes_funcptr() {
static std::once_flag flag; static std::once_flag flag;
int a = 6; int a = 6;
std::call_once(flag, &mutator, a); std::call_once(flag, &mutator, a);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{TRUE}} clang_analyzer_eval(a == 42); // expected-warning{{TRUE}}
#endif
} }
void fail_mutator(int param) { void fail_mutator(int param) {
...@@ -288,7 +340,9 @@ void test_mutator_noref() { ...@@ -288,7 +340,9 @@ void test_mutator_noref() {
static std::once_flag flag; static std::once_flag flag;
int a = 6; int a = 6;
std::call_once(flag, &fail_mutator, a); std::call_once(flag, &fail_mutator, a);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(a == 42); // expected-warning{{FALSE}} clang_analyzer_eval(a == 42); // expected-warning{{FALSE}}
#endif
} }
// Function is implicitly treated as a function pointer // Function is implicitly treated as a function pointer
...@@ -301,5 +355,7 @@ void test_implicit_funcptr() { ...@@ -301,5 +355,7 @@ void test_implicit_funcptr() {
static std::once_flag flagn; static std::once_flag flagn;
std::call_once(flagn, callbackn, x); std::call_once(flagn, callbackn, x);
#ifndef EMULATE_LIBCXX03
clang_analyzer_eval(x == 42); // expected-warning{{TRUE}} clang_analyzer_eval(x == 42); // expected-warning{{TRUE}}
#endif
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment