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.
Details
- Reviewers
dcoughlin xazax.hun a.sidorin george.karpenkov szepet rnkovacs Szelethus - Commits
- rGf3f036629618: [analyzer] MoveChecker: Add more common state resetting methods.
rL348235: [analyzer] MoveChecker: Add more common state resetting methods.
rC348235: [analyzer] MoveChecker: Add more common state resetting methods.
Diff Detail
- Repository
- rC Clang
Event Timeline
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.
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? ^-^
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.
Can you add tests for that just in case? :)
test/Analysis/use-after-move.cpp | ||
---|---|---|
330–331 |
Hmmm. Doesn't this check something similar, but still cause an warning? |
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. |
I'm glad you brought this stuff up, found two more bugs to fix.
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. |
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 would have been the test for our case, but in this test the function has a body and will not be evaluated conservatively.