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
```

Reply via email to