Bug 116476 - [14 Regression] error: binding reference of type 'int&&' to 'const int' discards qualifiers for std::vector<field<int>> f{2}; (OK under Clang and GCC<=13)
Summary: [14 Regression] error: binding reference of type 'int&&' to 'const int' disca...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 15.0
: P2 normal
Target Milestone: 14.3
Assignee: Marek Polacek
URL:
Keywords: rejects-valid
Depends on:
Blocks:
 
Reported: 2024-08-24 12:08 UTC by наб
Modified: 2025-01-03 18:19 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2024-08-24 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description наб 2024-08-24 12:08:28 UTC
minified reproducer, reduced from the original (https://git.sr.ht/~nabijaczleweli/voreutils/tree/5781698e96ad3a085a2f3d712dc61b020ae287ad/item/cmd/df.cpp#L80):

#include <vector>
template <class T>
struct field {
    field(T &&) {}
};
std::vector<field<int>> fields_normal{2};       // Error
std::vector<field<int>> fields_workaround{{2}}; // OK

All Clangs I cared to try and GCCs <=13 accept this and do the expected thing. GCC 14 (and trunk according to godbolt) explode with

<source>:6:40: error: binding reference of type 'int&&' to 'const int' discards qualifiers
    6 | std::vector<field<int>> fields_normal{2};       // Error
      |                                        ^
<source>:4:11: note:   initializing argument 1 of 'field<T>::field(T&&) [with T = int]'
    4 |     field(T &&) {}
      |           ^~~~
<source>: In function 'void __static_initialization_and_destruction_0()':
<source>:6:40: error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'const int'
    6 | std::vector<field<int>> fields_normal{2};       // Error
      |                                        ^
<source>:4:11: note:   initializing argument 1 of 'field<T>::field(T&&) [with T = int]'
    4 |     field(T &&) {}
      |           ^~~~
Compiler returned: 1

which feels nonsensical to me.
Comment 1 Jonathan Wakely 2024-08-24 14:17:21 UTC
Reduced further:

#include <initializer_list>

template <class T>
struct field {
    field(T &&) {}
};
struct vector {
  vector(std::initializer_list<field<int>>) { }
};

vector fields_normal{2};       // Error


init.cc:11:23: error: binding reference of type 'int&&' to 'const int' discards qualifiers
   11 | vector fields_normal{2};       // Error
      |                       ^
init.cc:5:11: note:   initializing argument 1 of 'field<T>::field(T&&) [with T = int]'
    5 |     field(T &&) {}
      |           ^~~~
init.cc: In function 'void __static_initialization_and_destruction_0()':
init.cc:11:23: error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'const int'
   11 | vector fields_normal{2};       // Error
      |                       ^
init.cc:5:11: note:   initializing argument 1 of 'field<T>::field(T&&) [with T = int]'
    5 |     field(T &&) {}
      |           ^~~~


Regression started with r14-1705:
 c++: build initializer_list<string> in a loop [PR105838]
Comment 2 Marek Polacek 2024-08-28 19:04:01 UTC
I have a patch.
Comment 3 GCC Commits 2024-08-28 22:46:36 UTC
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:9f79c7ddff5f1b004803931406ad17eaba095fff

commit r15-3274-g9f79c7ddff5f1b004803931406ad17eaba095fff
Author: Marek Polacek <polacek@redhat.com>
Date:   Wed Aug 28 15:45:49 2024 -0400

    c++: wrong error due to std::initializer_list opt [PR116476]
    
    Here maybe_init_list_as_array gets elttype=field, init={NON_LVALUE_EXPR <2>}
    and it tries to convert the init's element type (int) to field
    using implicit_conversion, which works, so overall maybe_init_list_as_array
    is successful.
    
    But it constifies init_elttype so we end up with "const int".  Later,
    when we actually perform the conversion and invoke field::field(T&&),
    we end up with this error:
    
      error: binding reference of type 'int&&' to 'const int' discards qualifiers
    
    So I think maybe_init_list_as_array should try to perform the conversion,
    like it does below with fc.
    
            PR c++/116476
    
    gcc/cp/ChangeLog:
    
            * call.cc (maybe_init_list_as_array): Try convert_like and see if it
            worked.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp0x/initlist-opt2.C: New test.
    
    Reviewed-by: Jason Merrill <jason@redhat.com>
Comment 4 Marek Polacek 2024-08-28 22:49:04 UTC
Fixed on trunk so far.
Comment 5 GCC Commits 2024-10-17 13:42:41 UTC
The releases/gcc-14 branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:0784e8934e96187e17c1b02dce1e0ed35d2229dd

commit r14-10799-g0784e8934e96187e17c1b02dce1e0ed35d2229dd
Author: Marek Polacek <polacek@redhat.com>
Date:   Wed Aug 28 15:45:49 2024 -0400

    c++: wrong error due to std::initializer_list opt [PR116476]
    
    Here maybe_init_list_as_array gets elttype=field, init={NON_LVALUE_EXPR <2>}
    and it tries to convert the init's element type (int) to field
    using implicit_conversion, which works, so overall maybe_init_list_as_array
    is successful.
    
    But it constifies init_elttype so we end up with "const int".  Later,
    when we actually perform the conversion and invoke field::field(T&&),
    we end up with this error:
    
      error: binding reference of type 'int&&' to 'const int' discards qualifiers
    
    So I think maybe_init_list_as_array should try to perform the conversion,
    like it does below with fc.
    
            PR c++/116476
    
    gcc/cp/ChangeLog:
    
            * call.cc (maybe_init_list_as_array): Try convert_like and see if it
            worked.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp0x/initlist-opt2.C: New test.
    
    Reviewed-by: Jason Merrill <jason@redhat.com>
    (cherry picked from commit 9f79c7ddff5f1b004803931406ad17eaba095fff)
Comment 6 Marek Polacek 2024-10-17 13:45:46 UTC
Fixed.