https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127341
Bug ID: 127341
Summary: [modules] vtable not emitted for extern "C++" class
when an imported module forward-declares it
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: samuelgardner101 at gmail dot com
Target Milestone: ---
Created attachment 65567
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65567&action=edit
ixx/cpp/sh file to show issue
a class attached to the global module (via `export extern "C++"`) is defined in
module `b`, and its key function is defined in b's implementation unit. if a
module imported by `b` also forward-declares the class, no TU emits the vtable
or typeinfo: b.cpp.o defines the key function but only references
`vtable for B`, and b.ixx.o does not emit it either. but, removing the forward
declaration from `a` makes b.cpp.o emit the vtable and typeinfo (as `V`), and
the program links.
```c++
// a.ixx
export module a;
export extern "C++" struct B; // remove this line and the link succeeds
```
```c++
// b.ixx
export module b;
import a;
export extern "C++" struct B {
virtual int f() const; // key function
virtual ~B();
};
```
```c++
// b.cpp
module b;
extern "C++" int B::f() const { return 42; }
extern "C++" B::~B() = default;
```
```c++
// main.cpp
import b;
int main() {
B *b = new B;
int r = b->f();
delete b;
return r == 42 ? 0 : 1;
}
```
```sh
$ for f in a.ixx b.ixx b.cpp main.cpp; do g++ -std=c++26 -fmodules -x c++ -c $f
-o $f.o; done
$ nm -C b.cpp.o | grep 'vtable for B'
U vtable for B
$ g++ a.ixx.o b.ixx.o b.cpp.o main.cpp.o
ld: b.cpp.o: in function `B::~B()':
b.cpp:(.text+0x19): undefined reference to `vtable for B'
ld: main.cpp.o: in function `B::B()':
main.cpp:(.text._ZN1BC2Ev[_ZN1BC5Ev]+0x9): undefined reference to `vtable for
B'
collect2: error: ld returned 1 exit status
```
without the forward declaration in a.ixx:
```sh
$ nm -C b.cpp.o | grep -E '(vtable|typeinfo) for B$'
0000000000000000 V typeinfo for B
0000000000000000 V vtable for B
```
also reproduces with the block form `export extern "C++" { struct B; }`, and
when B derives from a polymorphic base in `a` (the original case).
repro on:
- g++ (GCC) 17.0.0 20260903 (experimental) [master r16-5787-gc44586acdc6]
- g++ (GCC) 14.2.0 (-std=c++23 -fmodules-ts)
ie it is not a regression i don't think?
there is a workaround: give the class an extra virtual whose out-of-line
definition sits in the interface unit (b.ixx) after the class; the vtable is
then emitted in b.ixx.o. even then, b.cpp.o still emits `typeinfo for B` but
leaves `vtable for B` as `U`, so b.cpp's TU still never treats B::f() as the
key function.