https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62051
--- Comment #4 from Jason Merrill <jason at gcc dot gnu.org> --- So what's happening here is that the compiler sees that *m is a Derived, so it can devirtualize the destructor call to ~Derived, and ~Derived refers to the vtable. One solution would be to give ~Derived default visibility and move its definition out of line. But calls to virt_func would probably produce direct calls and thus undefined symbol errors as well. It seems like your code is designed for calling through the vtable, and devirtualization breaks that design, so turning off devirtualization is the right approach. It might be possible to make the compiler more clever about recognizing patterns that don't work well with devirtualization and automatically suppressing it in such cases. Such as, in this case, seeing explicit default visibility on a constructor.