https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128040
Bug ID: 128040
Summary: [modules] std::string::npos defined as a strong global
symbol in every TU importing the <iomanip> header unit
at -O1+ in C++/20 ("multiple definition" at link time)
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: oleg at smolsky dot net
Target Milestone: ---
A TU that imports the <iomanip> header unit and odr-uses std::string::npos
emits a non-COMDAT, non-unique global definition of npos at -O1 and -O2, with
-std=c++17 or -std=c++20. Linking two such objects fails:
multiple definition of `std::__cxx11::basic_string<char,
std::char_traits<char>, std::allocator<char> >::npos'
With a textual #include <iomanip> the same TUs reference libstdc++'s
definition, which comes from its explicit instantiation of basic_string<char>:
nm x.o | grep npos: #include <iomanip> import <iomanip>;
-O0 u u
-O1 U R <- GLOBAL OBJECT in
.rodata
-O2 U R
The trigger is narrower than "any std header unit". A header unit of <string>
alone is fine at -O2 (npos stays U). <iomanip>, or any header unit that
contains it, e.g. <bits/stdc++.h>, is not. We hit this through the
<bits/stdc++.h> header unit that include translation uses for every standard
#include.
Reproducer (GCC 16.2.0, configured with ../configure --prefix=/opt/gcc-16
--enable-languages=c,c++ --enable-gold=default --disable-multilib
--with-zstd=no):
$ cat x.cc
import <iomanip>;
const std::string::size_type *x() { return &std::string::npos; }
$ cat y.cc
import <iomanip>;
const std::string::size_type *x();
int main() { return x() == &std::string::npos ? 0 : 1; }
$ g++ -std=c++20 -O2 -fmodules -x c++-system-header iomanip
$ g++ -std=c++20 -O2 -fmodules -c x.cc
$ g++ -std=c++20 -O2 -fmodules -c y.cc
$ g++ x.o y.o
.../x86_64-pc-linux-gnu/bin/ld: error: y.o: multiple definition of
'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>
>::npos'
.../x86_64-pc-linux-gnu/bin/ld: x.o: previous definition here
collect2: error: ld returned 1 exit status
(The linker is gold, from --enable-gold=default.)
$ readelf -sW x.o | grep 4nposE
4: 0000000000000000 8 OBJECT GLOBAL DEFAULT 5
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4nposE
Expected: as with #include <iomanip>, an undefined reference at -O1/-O2
(libstdc++ provides npos through its explicit instantiation of
basic_string<char>), or at worst a COMDAT/unique definition that the linker
merges.
Standard and optimization levels, with import <iomanip>:
-std=c++17 -O2 R (fails)
-std=c++20 -O1 R (fails)
-std=c++20 -O2 R (fails)
-std=c++23 -O2 u (links)
-std=c++20 -O0 u (links)