This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug c++/69005] [5/6 Regression] infinite(?) recursion in template instantiations


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

--- Comment #2 from Jason Merrill <jason at gcc dot gnu.org> ---
If we remove the copy constructor declaration or make it defaulted, clang
rejects this testcase, also due to excessive recursive instantiation, and GCC
starts accepting it.  We're definitely into corner cases here.

Let's simplify the testcase:

template<typename T> T&& declval();

template<typename _Sig> class function;

template<typename _Res, typename _Arg>
struct function<_Res(_Arg)>
{
  function() noexcept { }

  function(const function&) { }

  template<typename _Functor,
           typename = decltype(declval<_Functor&>()(declval<_Arg>()))>
  function(_Functor) { }

  _Res operator()(_Arg) const;
};

struct Foo {
  function<void(Foo)> Func;
};

extern Foo exfoo;
Foo f (exfoo);

The problem is that when we decide we need to figure out the exception
specification for the Foo copy constructor, we look for which Foo constructor
it will call.  We consider the template constructor as a possible candidate and
do template argument deduction, which means substituting into the default
argument.  As part of that we need to copy Foo into the parameter of
operator(), so we need the exception specification for the Foo copy constructor
again, and so on ad infinitum.

I think the NotSelf business is trying to guard against this sort of thing, but
it doesn't work, because there's no shortcut evaluation of __and_ as there is
with &&.  It seems entirely useless on this constructor, since the standard
already says that a template constructor will never be instantiated to produce
a X(X) constructor:

12.8/6 "A declaration of a constructor for a class X is ill-formed if its first
parameter is of type (optionally cv-qualified) X and either there are no other
parameters or else all other parameters have default arguments. A member
function template is never instantiated to produce such a constructor
signature."

Given that passage, we could disqualify this template sooner; since the first
function parameter is just a template parameter, if we're copying an object of
the class type we know deduction would try to give us an X(X) constructor, so
we might as well not bother with deduction.  That would short-circuit this
process and make us accept the testcase.  I think I'll go ahead and do this for
GCC 6.

But really, this is a serious edge case.  It would be more portable to make
your substitution fail sooner so that we  we don't even get into the decltype
if NotSelf is false.  This works for me (and clang without the copy
constructor), though there are certainly many ways to formulate it:

      template<typename _Functor,
               typename = _Requires<_NotSelf<_Functor>, void>,
               typename = _Requires<_Callable<_Functor>, void>>
      function(_Functor) { }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]