https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127346
Bug ID: 127346
Summary: [16/17 Regression] [modules] "conflicting declaration"
when redeclaring an extern "C++" class whose
definition is reachable but not visible
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 65570
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65570&action=edit
c++ files and sh runner
1. module `a` defines a class attached to the global module (via `export extern
"C++"`).
2. module `b` imports `a` without re-exporting it.
3. module `c` imports only `b`, so the definition of `A` is reachable in `c`
but not visible. when `c` redeclares `A`, GCC reports a conflict with a's
definition. it then drops c's declaration, so the name is unusable for the rest
of the TU. both declarations attach to the global module, so they declare the
same entity, and the redeclaration should make `A` visible in `c`.
```c++
// a.ixx
export module a;
export extern "C++" struct A { int x = 42; }; // a declaration alone (no
definition) does not trigger it
```
```c++
// b.ixx
export module b;
import a; // not re-exported: A is reachable from importers of b, but not
visible
export int b_fn();
```
```c++
// c.ixx
export module c;
import b; // add "import a;" and it compiles
export extern "C++" struct A; // same result without "export"
export A *c_ptr();
```
```sh
$ for f in a.ixx b.ixx c.ixx; do g++ -std=c++26 -fmodules -x c++ -c $f -o $f.o;
done
c.ixx:3:28: error: conflicting declaration ‘struct A’
3 | export extern "C++" struct A; // same result without "export"
| ^
In module a, imported at b.ixx:2,
of module b, imported at c.ixx:2:
a.ixx:2:28: note: previous declaration as ‘struct A’
2 | export extern "C++" struct A { int x = 42; }; // a declaration alone
(no definition) does not trigger it
| ^
c.ixx:4:8: error: ‘A’ does not name a type
4 | export A *c_ptr();
| ^
```
it compiles if any of these changes is made:
- c imports a directly (the definition becomes visible)
- a only declares A (`export extern "C++" struct A;`), with no definition
- b uses `export import a;`
it still fails if c's redeclaration drops `export` (`extern "C++" struct A;`),
or if the redeclaration is a definition that uses A through a template from b.
regression:
- GCC 15.1.0 OK
- GCC 15.2.0 OK
- GCC 16.1.0 FAIL
- GCC 17.0.0 FAIL
- clang++ 23.1.0 accepts it (-std=c++2c, --precompile each unit).
workaround: import the module that defines the class directly. it is already in
the import closure, so obvs this doesn't/can't create an import cycle.