Bug 110580 - [14 Regression] gcc fails to typecheck nix-2.16.1 source: error: invalid initialization of reference of type
Summary: [14 Regression] gcc fails to typecheck nix-2.16.1 source: error: invalid init...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 14.0
: P3 normal
Target Milestone: 14.0
Assignee: Patrick Palka
URL:
Keywords: accepts-invalid, rejects-valid, wrong-code
Depends on:
Blocks:
 
Reported: 2023-07-06 21:46 UTC by Sergei Trofimovich
Modified: 2023-07-11 19:02 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2023-07-09 00:00:00


Attachments
nar-accessor.cc.cc.xz (444.82 KB, application/x-xz)
2023-07-06 21:46 UTC, Sergei Trofimovich
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Sergei Trofimovich 2023-07-06 21:46:50 UTC
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)
Comment 1 Andrew Pinski 2023-07-09 07:02:14 UTC
Reducing ...
Comment 2 Andrew Pinski 2023-07-09 15:56:46 UTC
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));
}
```
Comment 3 Andrew Pinski 2023-07-09 18:26:49 UTC
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); }
```
Comment 4 Andrew Pinski 2023-07-09 18:37:05 UTC
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.
Comment 5 Andrew Pinski 2023-07-10 05:47:33 UTC
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.
Comment 6 Andrew Pinski 2023-07-10 05:53:42 UTC
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
Comment 7 Patrick Palka 2023-07-10 18:24:33 UTC
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.
Comment 8 GCC Commits 2023-07-11 14:06:24 UTC
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.
Comment 9 Patrick Palka 2023-07-11 14:06:53 UTC
Fixed.
Comment 10 Sergei Trofimovich 2023-07-11 19:02:33 UTC
I confirm the change fixed nix-2.16.1 and json-3.11.2 builds for me. Thank you!