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.