https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110853

            Bug ID: 110853
           Summary: [c++-concepts] Bad interaction between deduction guide
                    with decay and constraints
           Product: gcc
           Version: 14.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: daniel.kruegler at googlemail dot com
  Target Milestone: ---

The following simplified code snippet demonstrates a breakage our company code
when updating gcc from C++17 to C++20. I'm starting first with the original
example, because it provides a bit more context information:

------------------------------------
#include <utility>

void value_twice(int& result)
{
  result = 2 * result;
}

auto key_value_gen()
{
  return std::pair("key", value_twice);
}

int main()
{
}
------------------------------------

Using gcc HEAD 14.0.0 20230728 and compiled with the previous settings
-Wall -Wextra -pedantic -std=c++17
the code is accepted, but switching to
-Wall -Wextra -pedantic -std=c++20
it becomes rejected with the following diagnostics:

------------------------------------
In file included from /opt/wandbox/gcc-head/include/c++/14.0.0/utility:69,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/14.0.0/bits/stl_pair.h: In instantiation of
'struct std::pair<char [4], void(int&)>':
/opt/wandbox/gcc-head/include/c++/14.0.0/bits/stl_pair.h:307:57:   required by
substitution of 'template<class _T1, class _T2> pair(const _T1&, const _T2&)->
std::pair<_T1, _T2> requires (std::pair<_T1, _T2>::_S_constructible<const _T1&,
const _T2&>)() [with _T1 = char [4]; _T2 = void(int&)]'
prog.cc:10:38:   required from here
/opt/wandbox/gcc-head/include/c++/14.0.0/bits/stl_pair.h:194:11: error: data
member 'std::pair<char [4], void(int&)>::second' invalidly declared function
type
  194 |       _T2 second;                ///< The second member
      |           ^~~~~~
------------------------------------
Note that std::pair has a "decaying" deduction guide

template<class T1, class T2>
pair(T1, T2) -> pair<T1, T2>;

but the error message reveals that attempts the produce a std::pair of the
undecayed types.

Additional information:
a) Current MSVC accepts the code but clang also rejects it but for different
reasons than gcc (see below).
b) I'm aware that a simple workaround exists by returning std::pair("key",
&value_twice) instead, and this is what we did to fix this. Nonetheless I think
that not everyone is able to fix such a problem in similar code when it was
provided by thirdparty libraries.

Basically the same error occurs when we use std::tuple(value_twice) instead.

The C++20 std::pair implementation uses noexcept, conditional explicit, and
trailing requires-clause based on static member functions as predicates, but
for gcc the problem can be reduced to the trailing requires-clause alone:

--------------------------------------
#define WITH_FUNC 1

template<class T>
constexpr bool is_copy_constructible_v = true;

template<class T1>
struct p
{
  T1 first;

  static constexpr bool do_is_copy_constructible()
  {
    return true;
  }

  p(const T1& t1) 
     requires (
#if WITH_FUNC
      do_is_copy_constructible()  // Line 19
#else
      is_copy_constructible_v<T1>
#endif
     )
      : first(t1) 
    {}
};

template<class T1>
p(T1) -> p<T1>;

void value_twice(int& result)
{
  result = 2 * result;
}

auto value_gen()
{
  return p(value_twice); // line 38
}

int main() {}
--------------------------------------

If we define WITH_FUNC to 0, the code is accepted, otherwise (as shown above),
the code is rejected with:

--------------------------------------
prog.cc: In instantiation of 'struct p<void(int&)>':
prog.cc:19:31:   required by substitution of 'template<class T1> p(const T1&)->
p<T1> requires (p<T1>::do_is_copy_constructible)() [with T1 = void(int&)]'
prog.cc:38:23:   required from here
prog.cc:9:6: error: data member 'p<void(int&)>::first' invalidly declared
function type
    9 |   T1 first;
      |      ^~~~~
--------------------------------------

Note that clang does accept the reduced case, even though it rejects the
original example as well due to slightly different reasons, which I will report
separately to them.

The reduced case uses the same implementation strategy as libstdc++ by means of
a static member function. What's special here is that the body of such a
function is a complete-class context of p, which I guess causes the error here
because the trailing requires-clause is not a complete-class context of p, so
one could say that this is actually a libstdc++ defect to use this
implementation strategy, it seems odd to me that the compiler attempts to
instantiate p with the undecayed function here and would like to open this
initially as compiler defect since the corresponding function does not actually
depend on p being complete. Feel free to correct me, but in case of a
correction of my understanding I would like to change this to a libstdc++ issue
instead of closing it because that would mean that libstdc++ cannot use there
static member function predicate approach (A free function template could be
used instead of a static member function, I tested that).
  • [Bug c++/110853] New: [... daniel.kruegler at googlemail dot com via Gcc-bugs

Reply via email to