$ cat b.cpp && echo EOFFFFFF template <class T> void foo() // 1 {} template <class T> void foo() requires (sizeof(char) == sizeof(T)) // 2 { foo<int>();// call 1 - instantiate "void foo<int>()" } template <class T> void foo() requires (sizeof(int) == sizeof(T)) // 3 {} void use() { foo<char>(); // call 2 foo<int>(); // call 3 - instantiate "void foo<int>() requires (sizeof(int) == sizeof(T))" } // so, two different function instantiated with same mangled name EOFFFFFF $ g++ -std=c++20 -v -c b.cpp Using built-in specs. COLLECT_GCC=g++ Target: x86_64-pc-linux-gnu Configured with: /var/tmp/portage/sys-devel/gcc-10.2.0-r5/work/gcc-10.2.0/configure --host=x86_64-pc-linux-gnu --build=x86_64-pc-linux-gnu --prefix=/usr --bindir=/usr/x86_64-pc-linux-gnu/gcc-bin/10.2.0 --includedir=/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include --datadir=/usr/share/gcc-data/x86_64-pc-linux-gnu/10.2.0 --mandir=/usr/share/gcc-data/x86_64-pc-linux-gnu/10.2.0/man --infodir=/usr/share/gcc-data/x86_64-pc-linux-gnu/10.2.0/info --with-gxx-include-dir=/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10 --with-python-dir=/share/gcc-data/x86_64-pc-linux-gnu/10.2.0/python --enable-languages=c,c++,fortran --enable-obsolete --enable-secureplt --disable-werror --with-system-zlib --disable-nls --enable-checking=release --with-bugurl=https://bugs.gentoo.org/ --with-pkgversion='Gentoo 10.2.0-r5 p6' --disable-esp --enable-libstdcxx-time --with-build-config=bootstrap-lto --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --enable-multilib --with-multilib-list=m32,m64 --disable-fixed-point --enable-targets=all --enable-libgomp --disable-libssp --disable-libada --enable-systemtap --enable-vtable-verify --with-zstd --enable-lto --with-isl --disable-isl-version-check --enable-default-pie --disable-default-ssp Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 10.2.0 (Gentoo 10.2.0-r5 p6) COLLECT_GCC_OPTIONS='-std=c++2a' '-v' '-c' '-shared-libgcc' '-mtune=generic' '-march=x86-64' /usr/libexec/gcc/x86_64-pc-linux-gnu/10.2.0/cc1plus -quiet -v -D_GNU_SOURCE b.cpp -quiet -dumpbase b.cpp -mtune=generic -march=x86-64 -auxbase b -std=c++2a -version -o /tmp/ccdHBZw0.s GNU C++17 (Gentoo 10.2.0-r5 p6) version 10.2.0 (x86_64-pc-linux-gnu) compiled by GNU C version 10.2.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.2.1, isl version isl-0.23-GMP GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 ignoring nonexistent directory "/usr/local/include" ignoring nonexistent directory "/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/include" #include "..." search starts here: #include <...> search starts here: /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10 /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/x86_64-pc-linux-gnu /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/backward /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include-fixed /usr/include End of search list. GNU C++17 (Gentoo 10.2.0-r5 p6) version 10.2.0 (x86_64-pc-linux-gnu) compiled by GNU C version 10.2.0, GMP version 6.2.1, MPFR version 4.1.0, MPC version 1.2.1, isl version isl-0.23-GMP GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: 109a96b688365221cca69113b161e683 COLLECT_GCC_OPTIONS='-std=c++2a' '-v' '-c' '-shared-libgcc' '-mtune=generic' '-march=x86-64' /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/as -v --64 -o b.o /tmp/ccdHBZw0.s GNU assembler version 2.35.2 (x86_64-pc-linux-gnu) using BFD version (Gentoo 2.35.2 p1) 2.35.2 /tmp/ccdHBZw0.s: Assembler messages: /tmp/ccdHBZw0.s:61: Error: symbol `_Z3fooIiEvv' is already defined --------------- My expectations: mangled names must be different, because mangled name is based on the signature and a signature includes the requires-clause as a part
a little more minimized: template <class> void foo() {} //1 void useFirst() { foo<int>();// call 1 - instantiate "void foo<int>()" } template <class> void foo() requires true {} //2 void useSecond() { foo<int>();// call 2 - instantiate "void foo<int>() requires true" } $ g++ -std=c++20 -c b.cpp /tmp/ccnRtmjt.s: Assembler messages: /tmp/ccnRtmjt.s:59: Error: symbol `_Z3fooIiEvv' is already defined
https://bugs.llvm.org/show_bug.cgi?id=50540
Clang and EDG agree with GCC here. I think your code is ill-formed due to [temp.constr.atomic] p3: "If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required."
(In reply to Jonathan Wakely from comment #3) > Clang and EDG agree with GCC here. > > I think your code is ill-formed due to [temp.constr.atomic] p3: > > "If, at different points in the program, the satisfaction result is > different for identical atomic constraints and template arguments, the > program is ill-formed, no diagnostic required." Please, take a look at [temp.constr.atomic] p2: Two atomic constraints, e1 and e2, are identical if they are formed from the same appearance of the same expression and... My code contains two functions (last case, from Comment#1): template <class T> void foo() /*empty constraints*/; template <class T> void foo() requires true; empty constraints from first functions is not identical "requires true" from second one, so, [temp.constr.atomic] p3 is not applicable here. ------------ Here is a sample with constraints in both functions: template <class T> concept C1 = sizeof(T) > 1; template <class T> concept C2 = C1<T> && sizeof(T) < 24; template <class T> void foo() requires C1<T> {} void useFirst() { foo<int>(); } template <class T> void foo() requires C2<T> {} void useSecond() { foo<int>(); } /tmp/ccZLqFRh.s:69: Error: symbol `_Z3fooIiEvv' is already defined Thanks.
Yes, I realise that, but I think that is the same rule that means you can't change the result of overload resolution for a given call, which is why the second definition gets emitted using the same symbol name as the first. If the constrained overload is declared before the first call to foo<int>() then there is no error.
(In reply to Jonathan Wakely from comment #5) > Yes, I realise that, but I think that is the same rule that means you can't > change the result of overload resolution for a given call, But I have a precedent: void foo(char) {} void useFirst() { foo(0); // "void foo(char)" used, no "void foo(int)" visible at now } void foo(int) {} // introduce second function void useSecond() { foo(0); // "void foo(int)" selected as more suitable } > which is why the > second definition gets emitted using the same symbol name as the first. [defns.signature.templ] states that trailing require-clause is a part of function signature, so these are two different functions: template <class T> void foo() {} template <class T> void foo() requires true {} Since the signature is the basis for name mangling - different names are expected for different functions.. > If the constrained overload is declared before the first call to foo<int>() > then there is no error. Aha, in such situation there is an only call, no second one, so, no second symbol and no conflicts. Thanks.
I think the code is valid; it's just that the ABI doesn't have a mangling for constraints yet: https://github.com/itanium-cxx-abi/cxx-abi/issues/24
(In reply to Jonathan Wakely from comment #3) > Clang and EDG agree with GCC here. > > I think your code is ill-formed due to [temp.constr.atomic] p3: > > "If, at different points in the program, the satisfaction result is > different for identical atomic constraints and template arguments, the > program is ill-formed, no diagnostic required." Of course the constraints are different! First constraint is empty, second is always-true. So, these are different overloads. Okay, let's help the compiler giving different mangled names: https://gcc.godbolt.org/z/K8d9vv8oT namespace a {} namespace b {} using namespace a; using namespace b; namespace a { template<class T> void f() { std::cout << __PRETTY_FUNCTION__ << std::endl; } } void g() { f<int>(); } namespace b { template<class T> void f() requires true { std::cout << __PRETTY_FUNCTION__ << std::endl; } } void h() { f<int>(); } g addresses to a::f, h addresses to b::f. Is this still "ill-formed, no diagnostics required"? Does it mean that a compiler may produce any corrupted binary code with any undefined behavior? Just because we wrote same "f<int>()" both times? I believe, not, it does not. The program is well-formed. Both overloads are valid. And both are different, - it is not an ODR violation. So, the issue is on the compiler's side: wrong rules of mangling.
As comment 7 already said.
*** Bug 82467 has been marked as a duplicate of this bug. ***
*** Bug 101719 has been marked as a duplicate of this bug. ***
At the very least, GCC should give better errors instead of just letting the assembler complain. Clang tells you where the conflicting definitions come from, e.g. for the code in comment 1: 1.C:7:23: error: definition with same mangled name '_Z3fooIiEvv' as another definition template <class> void foo() requires true {} //2 ^ 1.C:1:23: note: previous definition is here template <class> void foo() {} //1 ^ 1 error generated. Similarly with EDG: eccp: diagnostics generated from compilation of 1.int.c: 1.C:7:65: error: redefinition of ‘_Z3fooIiEvv’ 7 | template <class> void foo() requires true {} //2 | ^ 1.C:1:65: note: previous definition of ‘_Z3fooIiEvv’ with type ‘void(void)’ 1 | template <class> void foo() {} //1 | ^ eccp: end of diagnostics from compilation of 1.int.c eccp: gcc compilation of 1.int.c returned an exit status of 1 These are both much better than the result with GCC: /tmp/ccnRtmjt.s: Assembler messages: /tmp/ccnRtmjt.s:59: Error: symbol `_Z3fooIiEvv' is already defined
Confirmed.
I've proposed mangling rules on the ABI list: https://github.com/itanium-cxx-abi/cxx-abi/issues/24#issuecomment-1491130332 We're ready to land an implementation in Clang; I would appreciate feedback from GCC folks first, though.