Created attachment 55495 [details] nar-accessor.cc.cc.xz Originally observed the build failure on nix-2.16.1 source when building with gcc r14-2344-g9f4f833455bb35. I suspect it might be a form of https://gcc.gnu.org/PR110523 regression. But filing just in case. Attached unmodified preprocessed example. The build succeeds on gcc-13: $ g++-13 -c nar-accessor.cc.cc -std=c++2a # ok $ g++-14 -c nar-accessor.cc.cc -std=c++2a nar-accessor.cc.cc: In instantiation of 'std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Val>, std::_Select1st<std::pair<const _Key, _Val> >, _Compare, typename __gnu_cxx::__alloc_traits<_Allocator>::rebind<std::pair<const _Key, _Val> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::emplace(_Args&& ...) [with _Args = {std::basic_string_view<char, std::char_traits<char> >, nix::NarMember}; _Key = std::__cxx11::basic_string<char>; _Tp = nix::NarMember; _Compare = std::less<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, nix::NarMember> >; typename std::_Rb_tree<_Key, std::pair<const _Key, _Val>, std::_Select1st<std::pair<const _Key, _Val> >, _Compare, typename __gnu_cxx::__alloc_traits<_Allocator>::rebind<std::pair<const _Key, _Val> >::other>::iterator = std::_Rb_tree_iterator<std::pair<const std::__cxx11::basic_string<char>, nix::NarMember> >; typename __gnu_cxx::__alloc_traits<_Allocator>::rebind<std::pair<const _Key, _Val> >::other = std::allocator<std::pair<const std::__cxx11::basic_string<char>, nix::NarMember> >; typename __gnu_cxx::__alloc_traits<_Allocator>::rebind<std::pair<const _Key, _Val> > = __gnu_cxx::__alloc_traits<std::allocator<std::pair<const std::__cxx11::basic_string<char>, nix::NarMember> >, std::pair<const std::__cxx11::basic_string<char>, nix::NarMember> >::rebind<std::pair<const std::__cxx11::basic_string<char>, nix::NarMember> >; typename _Allocator::value_type = std::pair<const std::__cxx11::basic_string<char>, nix::NarMember>]': nar-accessor.cc.cc:126951:62: required from here nar-accessor.cc.cc:33745:23: error: invalid initialization of reference of type 'const std::map<std::__cxx11::basic_string<char>, nix::NarMember>::key_type&' {aka 'const std::__cxx11::basic_string<char>&'} from expression of type 'std::basic_string_view<char>' 33745 | const key_type& __k = __a; | ^~~ $ g++ -v |& unnix Using built-in specs. COLLECT_GCC=/<<NIX>>/gcc-14.0.0/bin/g++ COLLECT_LTO_WRAPPER=/<<NIX>>/gcc-14.0.0/libexec/gcc/x86_64-unknown-linux-gnu/14.0.0/lto-wrapper Target: x86_64-unknown-linux-gnu Configured with: Thread model: posix Supported LTO compression algorithms: zlib gcc version 14.0.0 99999999 (experimental) (GCC)
Reducing ...
Reduced un-preprocessed source: ``` #include <map> #include <stack> #include <string> struct NarMember { std::map<std::string, NarMember> children; }; std::stack<NarMember *> parents; std::string_view g(); void createMember(NarMember member) { parents.top()->children.emplace(g(), std::move(member)); } ```
Reduced all the way: ``` template <typename _Tp> struct remove_reference { using type = __remove_reference(_Tp); }; template <typename _Tp> using remove_reference_t = typename remove_reference<_Tp>::type; template <typename _Tp, typename _Up> inline constexpr bool is_same_v = __is_same(_Tp, _Up); template <typename _Key> class s3 { public: template <typename _Up, typename _Vp = remove_reference_t<_Up>> static constexpr bool __usable_key = is_same_v<const _Vp, const _Key>; template <typename _Args> void f(_Args __args, int t) { if constexpr (__usable_key<_Args>) { const _Key &__k = __args; } } }; struct s0 {}; struct s1 {}; s1 g(); void createMember(int member, s3<s0> &children) { children.f(g(), member); } ``` Or if using the internal type_traits is bad idea here is better testcase: ``` #include <type_traits> using namespace std; template <typename _Key> class s3 { public: template <typename _Up, typename _Vp = remove_reference_t<_Up>> static constexpr bool __usable_key = is_same_v<const _Vp, const _Key>; template <typename _Args> void f(_Args __args, int t) { if constexpr (__usable_key<_Args>) { const _Key &__k = __args; } } }; struct s0 {}; struct s1 {}; s1 g(); void createMember(int member, s3<s0> &children) { children.f(g(), member); } ``` Here is a testcase using static assert rather than if constexpr: ``` #include <type_traits> using namespace std; template <typename _Key> class s3 { public: template <typename _Up, typename _Vp = remove_reference_t<_Up>> static constexpr bool __usable_key = is_same_v<const _Vp, const _Key>; template <typename _Args> void f(_Args __args, int t) { static_assert(!__usable_key<_Args>); } }; struct s0 {}; struct s1 {}; s1 g(); void createMember(int member, s3<s0> &children) { children.f(g(), member); } ```
Slightly more reduced: ``` #include <type_traits> using std::is_same_v; struct s0 {}; struct s1 {}; template <typename _Key> struct s3 { template <typename _Up, typename _Vp = _Up> static constexpr bool __usable_key = is_same_v<_Vp, _Key>; static_assert(!__usable_key<s1>); }; s3<s0> t; ``` If I remove _Vp or remove it from being type dependent, it works.
Here is another testcase: ``` struct s0 {}; struct s1 {}; template <typename T> struct t { static constexpr bool v = false; }; template<> struct t<s0> { static constexpr bool v = true; }; template <typename _Key> struct s3 { template <typename _Up, typename _Vp = _Up> static constexpr bool __usable_key = t<_Vp>::v; static_assert(__usable_key<s0>); static_assert(!__usable_key<s1>); }; s3<s0> t2; ``` I get the feeling _Up in the default template argument of __usable_key is being replaced with _Key template argument for some reason.
yes my theory is correct and becomes obvious with the error message for: ``` struct s0 {}; struct s1 {}; struct s2 {}; template <typename T> struct t; template <typename _Key> struct s3 { template <typename _Up, typename _Vp = _Up> static constexpr bool __usable_key = t<_Vp>::v; static_assert(__usable_key<s0>); static_assert(__usable_key<s1>); }; s3<s2> t2; ``` Error message: ``` <source>: In instantiation of 'constexpr const bool s3<s2>::__usable_key<s0, s2>': ... <source>: In instantiation of 'constexpr const bool s3<s2>::__usable_key<s1, s2>': ``` s2 there is definitely incorrect
Started with r14-2170-g4cf64d9cc2faf4. (In reply to Andrew Pinski from comment #5) > I get the feeling _Up in the default template argument of __usable_key is > being replaced with _Key template argument for some reason. Interesting bug.. Thanks for the nice reduction.
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>: https://gcc.gnu.org/g:b76d71564925abcabe6f5ad61d904b23c682cdfb commit r14-2433-gb76d71564925abcabe6f5ad61d904b23c682cdfb Author: Patrick Palka <ppalka@redhat.com> Date: Tue Jul 11 10:05:19 2023 -0400 c++: coercing variable template from current inst [PR110580] Here during ahead of time coercion of the variable template-id v1<int>, since we pass only the innermost arguments to coerce_template_parms (and outer arguments are still dependent at this point), substitution of the default template argument V=U just lowers U from level 2 to level 1 rather than replacing it with int as expected. Thus after coercion we incorrectly end up with (effectively) v1<int, T> instead of v1<int, int>. Coercion of a class/alias template-id on the other hand always passes all levels arguments, which avoids this issue. So this patch makes us do the same for variable template-ids. PR c++/110580 gcc/cp/ChangeLog: * pt.cc (lookup_template_variable): Pass all levels of arguments to coerce_template_parms, and use the parameters from the most general template. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/var-templ83.C: New test.
Fixed.
I confirm the change fixed nix-2.16.1 and json-3.11.2 builds for me. Thank you!