https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125230

            Bug ID: 125230
           Summary: [modules] #include transformed into import
           Product: gcc
           Version: 16.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: rainerd at eldwood dot com
  Target Milestone: ---

Created attachment 64394
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=64394&action=edit
example project that demonstrates the problem

It seems that when compiling with C++ modules, #include statements are silently
transformed into import statements.  This may be a clever optimization in some
cases, but I believe it is not standard-conforming and it breaks real code. 
Consider a header-only library like Doctest.  Such libraries often use a
preprocessor macro to conditionally compile function definitions in order to
compile them exactly once.  Here is a minimal example:

a.hpp (the library, cannot be changed):
  void f();

  #ifdef A_IMPL
  void f() {
  }
  #endif

b.cpp (part of the program that uses the library):
  #define A_IMPL
  #include "a.hpp"

c.cpp (part of the program that uses the library):
  import "a.hpp";

  int main() {
    f();
  }

test.sh (the build script):
  ~/toolchains/gcc/16.1.0/bin/g++ -Wall -Wextra -fmodules -x c++-header a.hpp
  ~/toolchains/gcc/16.1.0/bin/g++ -Wall -Wextra -fmodules -c b.cpp
  ~/toolchains/gcc/16.1.0/bin/g++ -Wall -Wextra -fmodules -c c.cpp
  ~/toolchains/gcc/16.1.0/bin/g++ -Wall -Wextra c.o b.o

Output:
  /usr/bin/ld: c.o: in function `main':
  c.cpp:(.text+0x5): undefined reference to `f()'
  collect2: error: ld returned 1 exit status

Changing the import in c.cpp into a #include does not change the error.  The
mere existence of gcm.cache/,/a.hpp.gcm on the file system is enough to turn
the #include into an import, even if the program consistently uses #include
over import.  However, the problem does not appear if a.hpp is never compiled.

For reference, here are the preprocessed files, confirming that #include has
been transformed into import:

b.ii:
  # 0 "b.cpp"
  # 0 "<built-in>"
  # 0 "<command-line>"
  # 1 "/usr/include/stdc-predef.h" 1 3
  # 0 "<command-line>" 2
  # 1 "b.cpp"

c.ii:
  # 0 "c.cpp"
  # 0 "<built-in>"
  # 0 "<command-line>"
  # 1 "/usr/include/stdc-predef.h" 1 3
  # 0 "<command-line>" 2
  # 1 "c.cpp"

  import "./a.hpp";

  int main() {
    f();
  }

import "./a.hpp" [[__translated]];

I have attached the complete example, including preprocessed files.

Information on my build environment:
  rainer@rainer10:~/work/gcc_header_unit_test$ ~/toolchains/gcc/16.1.0/bin/gcc
-v
  Using built-in specs.
  COLLECT_GCC=/home/rainer/toolchains/gcc/16.1.0/bin/gcc
 
COLLECT_LTO_WRAPPER=/home/rainer/toolchains/gcc/16.1.0/libexec/gcc/x86_64-pc-linux-gnu/16.1.0/lto-wrapper
  Target: x86_64-pc-linux-gnu
  Configured with: ../configure --prefix=/home/rainer/toolchains/gcc/16.1.0
--disable-multilib
  Thread model: posix
  Supported LTO compression algorithms: zlib
  gcc version 16.1.0 (GCC) 
  rainer@rainer10:~/work/gcc_header_unit_test$ uname -a
  Linux rainer10 6.8.0-111-generic #111-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 11
23:16:02 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux

Reply via email to