This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] MoveChecker Pt.4: Add a few more state reset methods.
ClosedPublic

Authored by NoQ on Nov 14 2018, 6:57 PM.

Details

Summary

This covers methods that may reset the state depending on the value of the argument: resize(), shrink_to_fit(), and shrink(). The latter is something i've seen on a custom collection; we don't have to hardcode those, but i guess it's a good safe way to be a bit better out of the box. As a TODO, we might want to warn anyway when the value of the argument is not 0.

Diff Detail

Repository
rC Clang

Event Timeline

NoQ created this revision.Nov 14 2018, 6:57 PM
Szelethus accepted this revision.Nov 15 2018, 7:52 AM

How about std::list<T>::splice?

This revision is now accepted and ready to land.Nov 15 2018, 7:52 AM
NoQ added a comment.Nov 16 2018, 4:31 PM

How about std::list<T>::splice?

Hmm, you mean that it leaves its argument empty? Interesting, i guess we may need a separate facility for that. I wonder how many other state-reset methods are we forgetting.

Szelethus added a comment.EditedNov 22 2018, 7:50 AM
In D54563#1301893, @NoQ wrote:

How about std::list<T>::splice?

Hmm, you mean that it leaves its argument empty? Interesting, i guess we may need a separate facility for that.

I looked it up, and this is what I found: (source: http://eel.is/c++draft/list.ops)

list provides three splice operations that destructively move elements from one list to another. The behavior of splice operations is undefined if get_­allocator() != x.get_­allocator().

void splice(const_iterator position, list& x);
void splice(const_iterator position, list&& x);

Effects: Inserts the contents of x before position and x becomes empty. Pointers and references to the moved elements of x now refer to those same elements but as members of *this. Iterators referring to the moved >elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.

This suggests to me that list1.splice(list1.begin(), std::move(list2)) leaves list2 in a valid, well-defined, but empty state.

I wonder how many other state-reset methods are we forgetting.

merge does something similar too for map, multimap, set, multiset, as well as their unordered counterparts.

As a TODO, we might want to warn anyway when the value of the argument is not 0.

Maybe leave a TODO in the code as well? ^-^

NoQ added a comment.EditedDec 3 2018, 4:22 PM

This suggests to me that list1.splice(list1.begin(), std::move(list2)) leaves list2 in a valid, well-defined, but empty state.

Wait, i think this should work automagically. Because list2 is passed by non-const reference (eg., rvalue reference) into an unknown function, it will be invalidated when the call is modeled conservatively, and therefore we will stop tracking it in the checkRegionChanges callback.

NoQ updated this revision to Diff 176512.Dec 3 2018, 4:32 PM

Add a TODO.

Can you add tests for that just in case? :)

test/Analysis/use-after-move.cpp
330–331

Because list2 is passed by non-const reference (eg., rvalue reference) into an unknown function, it will be invalidated when the call is modeled conservatively, and therefore we will stop tracking it in the checkRegionChanges callback.

Hmmm. Doesn't this check something similar, but still cause an warning?

NoQ added inline comments.Dec 3 2018, 4:43 PM
test/Analysis/use-after-move.cpp
260–262

This would have been the test for our case, but in this test the function has a body and will not be evaluated conservatively.

330–331

In this case a is passed by value. Therefore a move-constructor is called, and then the function does not have access to a.

597–615

This would have been the test for our case, but the && case isn't tested.

Okay, I submit! :D

test/Analysis/use-after-move.cpp
260–262

Hmm, since those are all STL containers, we should be able to have their definition? Or only with c++-container-inlining=true? Take this as more of a question than anything else.

NoQ added a comment.Dec 3 2018, 5:03 PM

I'm glad you brought this stuff up, found two more bugs to fix.

Can you add tests for that just in case? :)

The test works, but i noticed that we aren't respecting const references. That is, we believe that passing the object by const reference or pointer may reset it. I guess i'll make a separate commit.

test/Analysis/use-after-move.cpp
260–262

Yeah, assuming that we only need containers.

As usual, i expect only good things to happen when we inline them. Say, if a field is overwritten in the inlined call, this would also trigger checkRegionChanges. And i doubt that in these examples there will be a path through the function that leaves the object entirely untouched.

As a side note, i doubt that we'll actually react to field assignment, because this checker's checkRegionChanges callback doesn't account for that: it only iterates through explicit regions. I guess this needs fixing.

NoQ added a comment.Dec 3 2018, 7:39 PM

Hmm, i shouldn't have included shrink_to_fit. It definitely keeps the object in unspecified state if it is already in unspecified state.

This revision was automatically updated to reflect the committed changes.