Home > Blockchain >  g : detect use after std::move
g : detect use after std::move

Time:06-08

I just found an error in my code due to using an std::vector after having moved it. The code was something like :

std::vector<SomeObject> v1;
//fill v1;
this->v2=std::move(v1);
for(unsigned int i=0; i<v1.size(); i  )
{
    //the code in the for loop was not executed
}

The behavior is not surprising (the state of v1 is undefined, as long as it is valid; so an empty vector seems a logical choice).

My question is : is it possible to make g give me a warning when using a variable after moving it? If so, what flag should I add?

NB : I suppose it is not possible to guarantee the detection of such warning (it would, I think, be equivalent to the halting problem, that can't be solved for the general case). But it would already be very useful if I get a warning either for simple cases like the example (use after move can be proven), or if I get a warning when g can't prove that it is safe.

Thanks a lot in advance

CodePudding user response:

I don't think GCC has a diagnostic for that.

However the Clang static analyzer and Clang-tidy do. If you run them (both) on such code with all diagnostics enabled you get (excerpt):

<source>:7:34: warning: 'v1' used after it was moved [bugprone-use-after-move,hicpp-invalid-access-moved]
    for (unsigned int i = 0; i < v1.size(); i  ) {
                                 ^
<source>:6:15: note: move occurred here
    auto v2 = std::move(v1);

/*...*/

<source>:7:34: warning: Method called on moved-from object 'v1' of type 'std::vector' [clang-analyzer-cplusplus.Move]
    for (unsigned int i = 0; i < v1.size(); i  ) {
                                 ^~~~~~~~~
<source>:6:15: note: Object 'v1' of type 'std::vector' is left in a valid but unspecified state after move
    auto v2 = std::move(v1);
              ^~~~~~~~~~~~~
<source>:7:34: note: Method called on moved-from object 'v1' of type 'std::vector'
    for (unsigned int i = 0; i < v1.size(); i  ) {
                                 ^~~~~~~~~

See godbolt. The first being the clang-tidy diagnostic and the second being the static analyzer diagnostic.

Other linters and static analyzers probably have similar diagnostics implemented. GCC also now has a static analyzer (enabled with -fanalyzer), but it doesn't seem to have a diagnostic for this implemented yet.


Such diagnostics may however easily be false-positives (which may be a reason that the compilers don't implement them as direct compiler warnings).

For example std::unique_ptr has a very clear behavior when moved from. The resulting state is that of an empty std::unique_ptr. If it is then later checked for whether it is empty or not, there is no issue.

Also, objects of all well-behaved types can be reused after a move. The state immediately after the move may simply be unspecified. But e.g. assigning a new state should be fine.

CodePudding user response:

I do not believe this is possible in general. The move semantic does not specify the state of an object that was moved from. That depends on the implementation.

For example a very reasonable implementation for the move assignment of vector is to swap the data between the two vectors. So in you code example the v1.size() would be the size of v2 from before the move assignment. Who is to say the class does not specify that semantic? It would be perfectly reasonable to use-after-move.

Apparently in the STL the move constructor leaves the moved from container empty. But that would still have a perfectly valid v1.size()of 0.

Every moved-from object has some methods that are valid to use. At a minimum assignment should always be allowed. For STL containers I expect size() to also be valid in all cases but maybe not return what you expect. But really how is the compiler supposed to know which methods are save to call on moved-from objects?

For the compiler to give sensical warnings I think the compiler (or C ) needs to introduce some new attributes and the STL needs to annotate it's move constructors / move assignment. That way the compiler could know if a move constructor / assignment follows a certain semantic beyond valid but unspecified state.

  • Related