On Wed, 9 Feb 2022, Jason Merrill wrote:

> On 2/9/22 10:45, Patrick Palka wrote:
> > In filter_memfn_lookup, we weren't correctly recognizing and matching up
> > member functions introduced via a non-dependent using-decl.  This caused
> > us to crash in the below testcases in which we correctly pruned the
> > overload set for the non-dependent call ahead of time, but then at
> > instantiation time filter_memfn_lookup failed to match the selected
> > function (introduced in each case by a non-dependent using-decl) to the
> > corresponding function from the new lookup set.  Such member functions
> > need special handling in filter_memfn_lookup because they look exactly
> > the same in the old and new lookup sets, whereas ordinary member
> > functions that're defined in the (dependent) current class become more
> > specialized in the new lookup set.
> > 
> > This patch reworks the matching logic in filter_memfn_lookup so that it
> > handles non-dependent using-decls correctly, and is hopefully simpler to
> > follow.
> > 
> > Bootstrapped and regtested on x86_64-pc-linux, does this look OK for
> > trunk?
> > 
> >     PR c++/104432
> > 
> > gcc/cp/ChangeLog:
> > 
> >     * call.cc (build_new_method_call): When a non-dependent call
> >     resolves to a specialization of a member template, always build
> >     the pruned overload set using the member template, not the
> >     specialization.
> >     * pt.cc (filter_memfn_lookup): New parameter newtype.  Simplify
> >     and correct how members from the new lookup set are matched to
> >     those from the old one.
> >     (tsubst_baselink): Pass binfo_type as newtype to
> >     filter_memfn_lookup.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> >     * g++.dg/template/non-dependent19.C: New test.
> >     * g++.dg/template/non-dependent19a.C: New test.
> >     * g++.dg/template/non-dependent20.C: New test.
> > ---
> >   gcc/cp/call.cc                                |  9 ++--
> >   gcc/cp/pt.cc                                  | 49 +++++++++----------
> >   .../g++.dg/template/non-dependent19.C         | 14 ++++++
> >   .../g++.dg/template/non-dependent19a.C        | 16 ++++++
> >   .../g++.dg/template/non-dependent20.C         | 16 ++++++
> >   5 files changed, 73 insertions(+), 31 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/template/non-dependent19.C
> >   create mode 100644 gcc/testsuite/g++.dg/template/non-dependent19a.C
> >   create mode 100644 gcc/testsuite/g++.dg/template/non-dependent20.C
> > 
> > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> > index b2e89c5d783..d6eed5ed835 100644
> > --- a/gcc/cp/call.cc
> > +++ b/gcc/cp/call.cc
> > @@ -11189,12 +11189,11 @@ build_new_method_call (tree instance, tree fns,
> > vec<tree, va_gc> **args,
> >         if (really_overloaded_fn (fns))
> >     {
> >       if (DECL_TEMPLATE_INFO (fn)
> > -         && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (fn))
> > -         && dependent_type_p (DECL_CONTEXT (fn)))
> > +         && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (fn)))
> >         {
> > -         /* FIXME: We're not prepared to fully instantiate "inside-out"
> > -            partial instantiations such as A<T>::f<int>().  So instead
> > -            use the selected template, not the specialization.  */
> > +         /* Use the selected template, not the specialization, so that
> > +            this looks like an actual lookup result for sake of
> > +            filter_memfn_lookup.  */
> >                   if (OVL_SINGLE_P (fns))
> >             /* If the original overload set consists of a single function
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index 862f337886c..3a5d06bf297 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -16311,12 +16311,12 @@ tsubst (tree t, tree args, tsubst_flags_t
> > complain, tree in_decl)
> >   }
> >     /* OLDFNS is a lookup set of member functions from some class template,
> > and
> > -   NEWFNS is a lookup set of member functions from a specialization of that
> > -   class template.  Return the subset of NEWFNS which are specializations
> > of
> > -   a function from OLDFNS.  */
> > +   NEWFNS is a lookup set of member functions from NEWTYPE, a
> > specialization
> > +   of that class template.  Return the subset of NEWFNS which are
> > +   specializations of a function from OLDFNS.  */
> >     static tree
> > -filter_memfn_lookup (tree oldfns, tree newfns)
> > +filter_memfn_lookup (tree oldfns, tree newfns, tree newtype)
> >   {
> >     /* Record all member functions from the old lookup set OLDFNS into
> >        VISIBLE_SET.  */
> > @@ -16326,38 +16326,34 @@ filter_memfn_lookup (tree oldfns, tree newfns)
> >         if (TREE_CODE (fn) == USING_DECL)
> >     {
> >       /* FIXME: Punt on (dependent) USING_DECL for now; mapping
> > -        a dependent USING_DECL to its instantiation seems
> > -        tricky.  */
> > +        a dependent USING_DECL to the member functions it introduces
> > +        seems tricky.  */
> 
> FWIW I still think this shouldn't be very tricky.

I tried implementing this by substituting into the USING_DECL_SCOPE and
then during the filtering step keeping the member functions from the new
lookup set whose DECL_CONTEXT is the same as the substituted scope, i.e.
keeping the member functions from the new lookup set that're defined in
the base class named by the using-decl.

This seems to work but not in all cases.  The problem is that a member
function introduced by a using-decl could be defined in a base of the
base class named by the using-decl.  For example:

  template<class T>
  struct A : T
  {
    using T::f;
    decltype(f()) g(); // filter_memfn_lookup with T=C needs to map
                       // USING_DECL from the old lookup set to B::f
                       // from the new lookup set, but the substituted
                       // USING_DECL_SCOPE is C.
  };
  struct B {
    static void f();
  };
  struct C : B { };

  template struct A<C>;

So I'm not sure what's the right approach for handling dependent
USING_DECL in light of this example..

> 
> The patch is OK.

Thanks a lot, committed as r12-7134.

> 
> >       gcc_checking_assert (DECL_DEPENDENT_P (fn));
> >       return newfns;
> >     }
> > -      else if (TREE_CODE (fn) == TEMPLATE_DECL)
> > -   /* A member function template.  */
> > -   visible_set.add (fn);
> > -      else if (TREE_CODE (fn) == FUNCTION_DECL)
> > -   {
> > -     if (DECL_TEMPLATE_INFO (fn))
> > -       /* A non-template member function.  */
> > -       visible_set.add (DECL_TI_TEMPLATE (fn));
> > -     else
> > -       /* A non-template member function from a non-template base,
> > -          injected via a using-decl.  */
> > -       visible_set.add (fn);
> > -   }
> >         else
> > -   gcc_unreachable ();
> > +   visible_set.add (fn);
> >       }
> >       /* Returns true iff (a less specialized version of) FN appeared in
> >        the old lookup set OLDFNS.  */
> > -  auto visible_p = [&visible_set] (tree fn) {
> > -    if (TREE_CODE (fn) == FUNCTION_DECL
> > -   && !DECL_TEMPLATE_INFO (fn))
> > +  auto visible_p = [newtype, &visible_set] (tree fn) {
> > +    if (DECL_CONTEXT (fn) != newtype)
> > +      /* FN is a member function from a base class, introduced via a
> > +    non-dependent using-decl; look in the old lookup set for
> > +    FN exactly.  */
> >         return visible_set.contains (fn);
> > -    else if (DECL_TEMPLATE_INFO (fn))
> > +    else if (TREE_CODE (fn) == TEMPLATE_DECL)
> > +      /* FN is a member function template from the current class;
> > +    look in the old lookup set for the TEMPLATE_DECL from which
> > +    it was specialized.  */
> >         return visible_set.contains (DECL_TI_TEMPLATE (fn));
> >       else
> > -      gcc_unreachable ();
> > +      /* FN is a non-template member function from the current class;
> > +    look in the old lookup set for the FUNCTION_DECL from which
> > +    it was specialized.  */
> > +      return visible_set.contains (DECL_TEMPLATE_RESULT
> > +                              (DECL_TI_TEMPLATE (fn)));
> >     };
> >       bool lookup_changed_p = false;
> > @@ -16449,7 +16445,8 @@ tsubst_baselink (tree baselink, tree object_type,
> >          performed in an incomplete-class context, within which
> >          later-declared members ought to remain invisible.  */
> >       BASELINK_FUNCTIONS (baselink)
> > -       = filter_memfn_lookup (fns, BASELINK_FUNCTIONS (baselink));
> > +       = filter_memfn_lookup (fns, BASELINK_FUNCTIONS (baselink),
> > +                              binfo_type);
> >       BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (baselink) = true;
> >     }
> >   diff --git a/gcc/testsuite/g++.dg/template/non-dependent19.C
> > b/gcc/testsuite/g++.dg/template/non-dependent19.C
> > new file mode 100644
> > index 00000000000..d690e80f2eb
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/template/non-dependent19.C
> > @@ -0,0 +1,14 @@
> > +// PR c++/104432
> > +// { dg-do compile { target c++11 } }
> > +
> > +struct A {
> > +  template<class=int> void f();
> > +  void f(int);
> > +};
> > +
> > +template<class> struct B : A {
> > +  using A::f;
> > +  void g() { f(); }
> > +};
> > +
> > +template struct B<int>;
> > diff --git a/gcc/testsuite/g++.dg/template/non-dependent19a.C
> > b/gcc/testsuite/g++.dg/template/non-dependent19a.C
> > new file mode 100644
> > index 00000000000..2f523c81988
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/template/non-dependent19a.C
> > @@ -0,0 +1,16 @@
> > +// PR c++/104432
> > +// { dg-do compile { target c++11 } }
> > +// A variant of non-dependent19.C where A is a template.
> > +
> > +template<class>
> > +struct A {
> > +  template<class=int> void f();
> > +  void f(int);
> > +};
> > +
> > +template<class> struct B : A<int> {
> > +  using A<int>::f;
> > +  void g() { f(); }
> > +};
> > +
> > +template struct B<int>;
> > diff --git a/gcc/testsuite/g++.dg/template/non-dependent20.C
> > b/gcc/testsuite/g++.dg/template/non-dependent20.C
> > new file mode 100644
> > index 00000000000..ebf7d7f40fb
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/template/non-dependent20.C
> > @@ -0,0 +1,16 @@
> > +// PR c++/104432
> > +// { dg-do compile { target c++11 } }
> > +
> > +template<class T>
> > +struct A {
> > +  template<class=int> static void f(T);
> > +  static void f();
> > +};
> > +
> > +template<class> struct B : A<int>, A<int&> {
> > +  using A<int>::f;
> > +  using A<int&>::f;
> > +  void g() { f(0); }
> > +};
> > +
> > +template struct B<int>;
> 
> 

Reply via email to