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.

Reply via email to