I've been running clang 3.3
's static analyser on various projects of mine. Except some issues that were my own fault (which was to be expected, I would have been both very sad and very smug otherwise), all went pretty smoothly except the following problem concerning std::function
's move constructor which is a false positive.
Before discussing it further, here's a simple test case:
int main() {
std::function<void ()> f1;
std::function<void ()> f2 = std::move(f1);
}
Run it through clang++ -std=c++11 --analyze -Xanalyzer -analyzer-output=text foo.cpp
(which uses GCC's libstdc++
-- namely the 4.8.1 version -- not clang
's libc++
) and you get the following trace:
In file included from foo.cpp:1:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/iostream:39:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ostream:38:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ios:40:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/char_traits.h:39:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_algobase.h:64:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_pair.h:59:
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:7: warning: Assigned value is garbage or undefined
_Tp __tmp = _GLIBCXX_MOVE(__a);
^~~~~~~~~ ~~~~~~~~~~~~~~~~~~
foo.cpp:30:31: note: Calling move constructor for 'function'
std::function<void ()> f2 = std::move(f1);
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2232:2: note: Calling 'function::swap'
__x.swap(*this);
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2359:2: note: Calling 'swap'
std::swap(_M_invoker, __x._M_invoker);
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:19: note: Calling 'move'
_Tp __tmp = _GLIBCXX_MOVE(__a);
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:142:30: note: expanded from macro '_GLIBCXX_MOVE'
#define _GLIBCXX_MOVE(__val) std::move(__val)
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:19: note: Returning from 'move'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:142:30: note: expanded from macro '_GLIBCXX_MOVE'
#define _GLIBCXX_MOVE(__val) std::move(__val)
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:7: note: Assigned value is garbage or undefined
_Tp __tmp = _GLIBCXX_MOVE(__a);
^
1 warning generated.
As you can see, the move constructor std::function(std::function&&)
is implemented in terms of swap
. The steps of the whole operation are (if we are to believe clang
):
f1
is correctly constructedf2
is not yet constructed and thus contains garbagef1
is moved intof2
but actually it's really aswap
f2
now contains the oldf1
, butf1
contains the oldf2
ie. garbage- at some point,
f1
containing garbage is destructed... what happens then?
The theory says, this is very bad. In practice, looking at the implementation, it appears that std::function
privately inherits from _Function_base
which takes care to initialize all that matters (ie. _M_manager
) to null, so clang
's warning about _M_invoker
is pointless.
Just in case someone is doubting, moving an object is supposed to leave it in an indeterminate state, where you can only either assign to it or destruct it. GCC's function
implementation does exactly that: only _M_manager
is important as far as resource management is involved, the rest (including _M_invoker
) are just "convenience" pointers.
I dug far enough in GCC's function
implementation to have absolutely no doubt as to the status of false positive of clang
's diagnostic. But since I have literally hundreds of places where this happens in my code, this makes going through the static analyser's results quite a pain to say the least.
How can I instruct clang
not to report this very issue?
Again, in case you missed it, I'm using clang 3.3
along with GCC's libstdc++ 4.8.1
.
Note: If you are running a clang 3.4
build and if it doesn't trigger this false positive, please let me know. As far as I tried, I couldn't get 3.4 to run on my system yet (Debian Jessie) but if it solves this problem I'll just try harder.