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

            Bug ID: 128126
           Summary: Devirtualization through an abstract final class emits
                    an undefined reference to a pure virtual function
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: link-failure, rejects-valid
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: yqtian668 at gmail dot com
  Target Milestone: ---
              Host: x86_64-pc-linux-gnu
            Target: x86_64-pc-linux-gnu
             Build: x86_64-pc-linux-gnu

Created attachment 65947
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65947&action=edit
Preprocessed C++ testcase

G++ emits a direct reference to an undefined pure virtual function when the
static type of a virtual call is an abstract final class. The program is valid:
no object of the abstract class can exist, the failed dynamic_cast makes the
call unevaluated at run time, and an ordinary virtual call to a pure virtual
function does not require a definition of that function.

Reproducer:

struct A { virtual int f() = 0; };
struct B final : A {};
struct C : A { int f() override { return 1; } };
__attribute__((noinline)) int g(A* p) {
  B* q = dynamic_cast<B*>(p);
  return q ? q->f() : 7;
}
int main() { C c; return g(&c) == 7 ? 0 : 1; }

Command:

g++ -std=c++17 -O0 testcase.cpp -o testcase

Actual behavior:

G++ emits a direct reference to A::f(), and the linker reports an undefined
reference to A::f(). The executable is not produced.

/usr/bin/ld: /tmp/ccl2Hggj.o: in function 'g(A*)':
testcase.cpp:(.text+0x49): undefined reference to 'A::f()'
collect2: error: ld returned 1 exit status

Expected behavior:

G++ should link the program, and the executable should return 0.
dynamic_cast<B*>(p) returns null because p points to a C object, so the
conditional expression selects 7 and never calls A::f().

Clang trunk links the testcase, and the resulting executable returns 0. GCC 9.5
also links it, whereas GCC 10.5 through 16 reproduce the link failure.

Compiler version:

g++ (GCC) 17.0.0 20261021 (experimental)
GCC revision 8cc6e7178c2e3b6156d7992bd18285be0da3c92c
Target: x86_64-pc-linux-gnu
Configured with: /src/configure --prefix=/opt/gcc --enable-languages=c,c++
--disable-multilib --disable-bootstrap --enable-checking=yes

Suspected cause:

The regression begins with commit 0ddb93ce77374004c49cdfbd748ba35867620cf1 for
PR95158. In build_over_call, lookup_vfn_in_binfo returns the inherited pure
final overrider A::f, after which LOOKUP_NONVIRTUAL is set unconditionally. The
later build_addr_func(fn) path emits the direct reference. The devirtualized
path should not be selected when the final overrider is pure.

Reply via email to