https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128168
Bug ID: 128168
Summary: [C++] virtual call in a base constructor through a
virtual base is devirtualized to a more-derived
override
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: ipa
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 65958
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65958&action=edit
Preprocessed testcase (gcc-anon-ctor-vbase-derived-override.ii)
## Description
At `-O2`, GCC devirtualizes a virtual call made while constructing `B` to the
override in the
more-derived class `C`. During `B::B()`, virtual dispatch must use `B` as the
dynamic type.
## Reproducer
```cpp
#include <cstdio>
namespace {
struct A {
virtual int f() = 0;
};
int call(A& r) { return r.f(); }
struct B : virtual A {
B() { std::printf("%d\n", call(*this)); }
int f() override { return 1; }
};
struct C : B {
int f() override { return 2; }
};
}
int main() {
C c;
}
```
```text
g++ -O2 testcase.cpp -o test
./test
```
## Actual behavior
The program prints `2`. GCC reports that the call in `call(A&)` is
devirtualized to `C::f()`.
## Expected behavior
The program should print `1`. During `B::B()`, virtual dispatch uses the final
overrider in `B`,
so the call must select `B::f()`.
## Controls
The testcase prints `1` with GCC at `-O0` and `-O1`, with `-O2
-fno-devirtualize`, and with Clang
at `-O2`. GCC at `-O0` with AddressSanitizer and UndefinedBehaviorSanitizer
also prints `1` without
diagnostics. Making `B : A` non-virtual or moving the classes out of the
anonymous namespace makes
GCC print `1` at `-O2`.
The failure reproduces with GCC trunk commit
`cd7a85774a992c4759ebd03a26ce03cbb934e4d9` and with GCC 15.3, which contains
the PR120098
backport. GCC 4.9.4 prints `1`; GCC 10.5 and later tested releases print `2`.
## Suspected cause
The direct call to `C::f()` is already present during symbol-table visibility
analysis. `B` is in
an anonymous namespace, its ordinary vtable is unused, and it is instantiated
as a base of `C`
through `C`'s construction vtable. This appears related to PR120098, but the
constructor case still
fails after its construction-vtable fix and GCC 15 backport.
See also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120098
## Compiler version
```text
g++ (GCC) 17.0.0 20261025 (experimental)
Target: x86_64-pc-linux-gnu
Configured with: /src/configure --prefix=/opt/gcc --enable-languages=c,c++
--disable-multilib --disable-bootstrap --enable-checking=yes
```