[PATCH] D29183: [Analysis] Fix for call graph to correctly handle nested call expressions

Balogh, Ádám via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 26 09:12:55 PST 2017


baloghadamsoftware added a comment.

Some comments how I found this bug. I got some strange false positives even after the fix for the iterator past end checker (https://reviews.llvm.org/D28771). I could reproduce it with the following code:

  #include <list>
  
  using std::list;
  using std::prev;
  
  namespace {
  
    class Strange {
    public:
      Strange(list<int> &l) : Back(prev(l.end())), Item(*Back) {}
      void reset(list<int> &l) { Back = prev(l.end()); Item = *Back; }
      int get1(list<int> &l) { Back = prev(l.end()); return *Back; }
      int get2(list<int> &l) { list<int>::iterator i; i = prev(l.end()); return *i; }
      
    private:
      list<int>::iterator Back;
      int Item;
    };
  
  }

This example was analyzed without errors, but if I removed get2(), I got iterator past end errors for every other function. Even more strange was that I could not reproduce this with std::vector, just with std::list. I started a long debugging session (days) and it turned out, that if I delete get2(), std::prev() is analyzed as top-level. Since std::prev() calls std::advance(), which calls an __advance() overload for bidirectional operators which increments or decrements the iterator in a loop. Since the number of iterations is undefined, after 4 iterations on both branch (++ and --) the analysis stops and marks the function as non-inlinable. This, of course does not happen for std::vector because it contains no loop but a simple += operation. If __advance() is marked as non-inlinable, it does not change its argument, so prev(l.end()) is the same as l.end(), so dereferencing the iterator is indeed an error.


https://reviews.llvm.org/D29183





More information about the cfe-commits mailing list