https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108565

            Bug ID: 108565
           Summary: -Wuse-after-free false positive on a shared_ptr
                    implementation triggered by -O2
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: egor_suvorov at mail dot ru
  Target Milestone: ---

May or may not be a duplicate of
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106119

Consider the following code, which is a subset of a shared_ptr's
implementation:

struct shared_ptr {
    int *counter_ = nullptr;
    int *data_ = nullptr;
    shared_ptr(int *data)
        : counter_(data ? new int(1) : nullptr), data_(data) {
    }
    shared_ptr(const shared_ptr &other)
        : counter_(other.counter_), data_(other.data_) {
        if (counter_ != nullptr) {
            ++*counter_;
        }
    }
    ~shared_ptr() {
        if (counter_ != nullptr) {
            --*counter_;
            if (*counter_ == 0) {
                delete counter_;
                delete data_;
            }
        }
    }
};
void foo() {
    shared_ptr a(new int(10));  // should be non-nullptr
    shared_ptr b(a);
    shared_ptr c(new int(20));  // should be non-nullptr
}
int main() {
    foo();
}

`g++ -std=c++17 -Wuse-after-free -O1` compiles this code without any problems,
and it runs without memory leaks or double-frees. However, `g++ -std=c++17
-Wuse-after-free -O2` generates a bogus warning:

In destructor 'shared_ptr::~shared_ptr()',
    inlined from 'void foo()' at x.cpp:27:1:
x.cpp:15:15: warning: pointer used after 'void operator delete(void*, long long
unsigned int)' [-Wuse-after-free]
   15 |             --*counter_;
      |               ^~~~~~~~~
In destructor 'shared_ptr::~shared_ptr()',
    inlined from 'void foo()' at x.cpp:27:1:
x.cpp:17:24: note: call to 'void operator delete(void*, long long unsigned
int)' here
   17 |                 delete counter_;
      |                        ^~~~~~~~

Seems like GCC is not smart enough to understand the underlying logic of
'shared_ptr'. Any of the following transformations remove the warning for me:

* Replacing `new int(10)` or `new int(20)` with nullptr
* Removing the ternary operator from `counter_`'s initialization.
* Replacing `-O2` with `-O1` or `-O0`

I've tested it both on trunk via Godbolt (https://godbolt.org/z/6ed1GY9Y7), and
on my local GCC 12.2 (Windows, msys2) and GCC 12.1 (Ubuntu 22.04).

Reply via email to