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

            Bug ID: 116038
           Summary: [14/15 Regression] ambiguous overload in bind_front
                    caused by deducing this
           Product: gcc
           Version: 14.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: valentin at tolmer dot fr
  Target Milestone: ---

The following snippet compiles well until 13.3 and breaks with 14.1 (and
trunk), where it complains of ambiguous overload:

$ cat <source>
#include<functional>
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
struct A{};
struct B{};
int apply_a(int i, A) { return i; }
int apply_b(int i, B) { return i; }
int main() {
    overloaded{
        std::bind_front(apply_a, 1),
        std::bind_front(apply_b, 2),
      }(A());
    return 0;
}

$ g++ -std=c++2b <source>
In file included from <source>:1:
/opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional: In
instantiation of 'static constexpr decltype(auto) std::_Bind_front<_Fd,
_BoundArgs>::_S_call(_Tp&&, std::index_sequence<_Is ...>, _CallArgs&& ...)
[with _Tp = overloaded<std::_Bind_front<int (*)(int, A), int>,
std::_Bind_front<int (*)(int, B), int> >; long unsigned int ..._Ind = {0};
_CallArgs = {A}; _Fd = int (*)(int, A); _BoundArgs = {int};
std::index_sequence<_Is ...> = std::integer_sequence<long unsigned int, 0>]':
/opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:947:18:
  required from 'constexpr std::invoke_result_t<decltype
(forward_like<_Self>(declval<_Fd>())), decltype
(forward_like<_Self>(declval<_BoundArgs>()))..., _CallArgs ...>
std::_Bind_front<_Fd, _BoundArgs>::operator()(this _Self&&, _CallArgs&& ...)
[with _Self = overloaded<std::_Bind_front<int (*)(int, A), int>,
std::_Bind_front<int (*)(int, B), int> >; _CallArgs = {A}; _Fd = int (*)(int,
A); _BoundArgs = {int}; std::invoke_result_t<decltype
(forward_like<_Self>(declval<_Fd>())), decltype
(forward_like<_Self>(declval<_BoundArgs>()))..., _CallArgs ...> = int; decltype
(forward_like<_Self>(declval<_Fd>())) = int (*&&)(int, A)]'
  947 |           return _S_call(std::forward<_Self>(__self), _BoundIndices(),
      |                  ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  948 |                          std::forward<_CallArgs>(__call_args)...);
      |                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:12:8:   required from here
    9 |     overloaded{
      |     ~~~~~~~~~~~
   10 |         std::bind_front(apply_a, 1),
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   11 |         std::bind_front(apply_b, 2),
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   12 |       }(A());
      |       ~^~~~~
/opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1018:53:
error: request for member '_M_fd' is ambiguous
 1018 |           return std::invoke(std::forward<_Tp>(__g)._M_fd,
      |                              ~~~~~~~~~~~~~~~~~~~~~~~^~~~~
/opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1023:33:
note: candidates are: 'int (* std::_Bind_front<int (*)(int, B),
int>::_M_fd)(int, B)'
 1023 |       [[no_unique_address]] _Fd _M_fd;
      |                                 ^~~~~
/opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1023:33:
note:                 'int (* std::_Bind_front<int (*)(int, A),
int>::_M_fd)(int, A)'
/opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1019:53:
error: request for member '_M_bound_args' is ambiguous
 1019 |               std::get<_Ind>(std::forward<_Tp>(__g)._M_bound_args)...,
      |                              ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
/opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1024:55:
note: candidates are: 'std::tuple<int> std::_Bind_front<int (*)(int, B),
int>::_M_bound_args'
 1024 |       [[no_unique_address]] std::tuple<_BoundArgs...> _M_bound_args;
      |                                                       ^~~~~~~~~~~~~
/opt/compiler-explorer/gcc-trunk-20240722/include/c++/15.0.0/functional:1024:55:
note:                 'std::tuple<int> std::_Bind_front<int (*)(int, A),
int>::_M_bound_args'
Compiler returned: 1


A smaller reproduction without including the stdlib is:

$ cat <source>
template <typename Fn>
struct invoke_result {
  Fn fn;
  template <typename Self, typename CallArg>
  void
  operator()(this Self self, CallArg arg) 
  requires requires {self.fn(arg);}{}
};
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
struct A{};
struct B{};
auto a = overloaded{invoke_result<int(*)(A)>(),
                    invoke_result<int(*)(B)>(),
         }(A());

$ g++ -std=c++2b -fconcepts-diagnostics-depth=2 <source>
<source>:15:11: error: no match for call to '(overloaded<invoke_result<int
(*)(A)>, invoke_result<int (*)(B)> >) (A)'
   13 | auto a = overloaded{invoke_result<int(*)(A)>(),
      |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   14 |                     invoke_result<int(*)(B)>(),
      |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
   15 |          }(A());
      |          ~^~~~~
<source>:6:3: note: candidate: 'template<class Self, class CallArg> void
invoke_result<Fn>::operator()(this Self, CallArg) requires
requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);}
[with CallArg = Self; Fn = int (*)(A)]'
    6 |   operator()(this Self self, CallArg arg)
      |   ^~~~~~~~
<source>:6:3: note:   template argument deduction/substitution failed:
<source>:6:3: note: constraints not satisfied
<source>: In substitution of 'template<class Self, class CallArg> void
invoke_result<int (*)(A)>::operator()(this Self, CallArg) requires
requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);}
[with Self = int (*)(A); CallArg = <missing>]':
<source>:15:11:   required from here
   13 | auto a = overloaded{invoke_result<int(*)(A)>(),
      |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   14 |                     invoke_result<int(*)(B)>(),
      |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
   15 |          }(A());
      |          ~^~~~~
<source>:6:3:   required by the constraints of 'template<class Fn>
template<class Self, class CallArg> void invoke_result<Fn>::operator()(this
Self, CallArg) requires
requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);}'
<source>:7:12:   in requirements  [with Self = overloaded<invoke_result<int
(*)(A)>, invoke_result<int (*)(B)> >; CallArg = A]
<source>:7:29: note: the required expression 'self.fn(arg)' is invalid, because
    7 |   requires requires {self.fn(arg);}{}
      |                      ~~~~~~~^~~~~
<source>:7:27: error: request for member 'fn' is ambiguous
    7 |   requires requires {self.fn(arg);}{}
      |                      ~~~~~^~
<source>:3:6: note: candidates are: 'int (* invoke_result<int (*)(B)>::fn)(B)'
    3 |   Fn fn;
      |      ^~
<source>:3:6: note:                 'int (* invoke_result<int (*)(A)>::fn)(A)'
<source>:6:3: note: candidate: 'template<class Self, class CallArg> void
invoke_result<Fn>::operator()(this Self, CallArg) requires
requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);}
[with CallArg = Self; Fn = int (*)(B)]'
    6 |   operator()(this Self self, CallArg arg)
      |   ^~~~~~~~
<source>:6:3: note:   template argument deduction/substitution failed:
<source>:6:3: note: constraints not satisfied
<source>: In substitution of 'template<class Self, class CallArg> void
invoke_result<int (*)(B)>::operator()(this Self, CallArg) requires
requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);}
[with Self = int (*)(B); CallArg = <missing>]':
<source>:15:11:   required from here
   13 | auto a = overloaded{invoke_result<int(*)(A)>(),
      |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   14 |                     invoke_result<int(*)(B)>(),
      |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
   15 |          }(A());
      |          ~^~~~~
<source>:6:3:   required by the constraints of 'template<class Fn>
template<class Self, class CallArg> void invoke_result<Fn>::operator()(this
Self, CallArg) requires
requires{invoke_result<Fn>::operator()::self.fn(invoke_result<Fn>::operator()::arg);}'
<source>:7:12:   in requirements  [with Self = overloaded<invoke_result<int
(*)(A)>, invoke_result<int (*)(B)> >; CallArg = A]
<source>:7:29: note: the required expression 'self.fn(arg)' is invalid, because
    7 |   requires requires {self.fn(arg);}{}
      |                      ~~~~~~~^~~~~
<source>:7:27: error: request for member 'fn' is ambiguous
    7 |   requires requires {self.fn(arg);}{}
      |                      ~~~~~^~
<source>:3:6: note: candidates are: 'int (* invoke_result<int (*)(B)>::fn)(B)'
    3 |   Fn fn;
      |      ^~
<source>:3:6: note:                 'int (* invoke_result<int (*)(A)>::fn)(A)'
Compiler returned: 1

The issue can be fixed by changing the definition of the operator() to:
template <typename CallArg>
void operator()(CallArg arg) 
requires requires {fn(arg);}{}

This bug comes up relatively frequently in a codebase that uses `std::visit` in
conjunction with `std::bind_front`, in perfectly innocent-looking code.

Reply via email to