On Thu, 8 Aug 2024, Nathaniel Shead wrote:

> On Wed, Aug 07, 2024 at 01:44:31PM -0400, Jason Merrill wrote:
> > On 8/6/24 2:35 AM, Nathaniel Shead wrote:
> > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> > > 
> > > Another potential approach would be to go searching for this unexported
> > > type and load it, either with a new LOOK_want::ANY_REACHABLE flag or by
> > > expanding on the lookup_imported_temploid_friend hack.  I'm still not
> > > exactly sure how name lookup for template friends is supposed to behave
> > > though, specifically in terms of when and where they should conflict
> > > with other entities with the same name.
> > 
> > CWG2588 tried to clarify this in https://eel.is/c++draft/basic#link-4.8 --
> > if there's a matching reachable declaration, the friend refers to it even if
> > it isn't visible to name lookup.
> > 
> > It seems like an oversight that the new second bullet refers specifically to
> > functions, it seems natural for it to apply to classes as well.
> > 
> > So, they correspond but do not conflict because they declare the same
> > entity.
> > 
> 
> Right, makes sense.  OK, I'll work on filling out our testcases to make
> sure that we cover everything under that interpretation and potentially
> come back to making an ANY_REACHABLE flag for this.
> 
> > > The relevant paragraphs seem to be https://eel.is/c++draft/temp.friend#2
> > > and/or https://eel.is/c++draft/dcl.meaning.general#2.2.2, in addition to
> > > the usual rules in [basic.link] and [basic.scope.scope], but how these
> > > all are supposed to interact isn't super clear to me right now.
> > > 
> > > Additionally I wonder if maybe the better approach long-term would be to
> > > focus on getting textual redefinitions working first, and then reuse
> > > whatever logic we build for that to handle template friends rather than
> > > relying on finding these hidden 'mergeable' slots first.
> > 
> > I have a WIP patch to allow textual redefinitions by teaching
> > duplicate_decls that it's OK to redefine an imported GM entity, so
> > check_module_override works.
> > 
> > My current plan is to then just token-skip the bodies.  This won't diagnose
> > ODR problems, but our module merging doesn't do that consistently either.
> > 
> > > @@ -11800,6 +11800,15 @@ tsubst_friend_class (tree friend_tmpl, tree args)
> > >             input_location = saved_input_location;
> > >           }
> > >       }
> > > +  else if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl))
> > > +    <= TMPL_ARGS_DEPTH (args))
> > 
> > This condition seems impossible normally; it's only satisfied in this
> > testcase because friend_tmpl doesn't actually represent the friend
> > declaration, it's already the named class template.  So the substitution in
> > the next else fails because it was done already.
> > 
> > If this condition is true, we could set tmpl = friend_tmpl earlier, and skip
> > doing name lookup at all.
> > 
> > It's interesting that the previous if does the same depth comparison, and
> > that dates back to 2002; I wonder why it was needed then?
> > 
> > Jason
> > 
> 
> Ah right, I see.  I think the depth comparison in the previous if
> actually is for exactly the same reason, just for the normal case when
> the template *is* found by name lookup, e.g. 
> 
>   template <typename> struct A {};
>   template <typename> struct B {
>     template <typename> friend struct ::A;
>   };
> 
> This is g++.dg/template/friend5.  Here's an updated patch which is so
> far very lightly tested, OK for trunk if full bootstrap+regtest
> succeeds?
> 
> -- >8 --
> 
> With modules it may be the case that a template friend class provided
> with a qualified name is not found by name lookup at instantiation time,
> due to the class not being exported from its module.  This causes issues
> in tsubst_friend_class which did not handle this case.
> 
> This is a more general issue, in fact, caused by the named friend class
> not actually requiring tsubsting.  This was already worked around for
> the "found by name lookup" case (g++.dg/template/friend5.C), but it
> looks like there's no need to do name lookup at all for this to work.
> 
>       PR c++/115801
> 
> gcc/cp/ChangeLog:
> 
>       * pt.cc (tsubst_friend_class): Return the type directly when no
>       tsubsting is required.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/modules/tpl-friend-16_a.C: New test.
>       * g++.dg/modules/tpl-friend-16_b.C: New test.
> 
> Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> ---
>  gcc/cp/pt.cc                                  | 39 ++++++++++--------
>  .../g++.dg/modules/tpl-friend-16_a.C          | 40 +++++++++++++++++++
>  .../g++.dg/modules/tpl-friend-16_b.C          | 17 ++++++++
>  3 files changed, 79 insertions(+), 17 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-16_a.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-16_b.C
> 
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 2db59213c54..ea00577fd37 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -11732,6 +11732,15 @@ tsubst_friend_class (tree friend_tmpl, tree args)
>        return TREE_TYPE (tmpl);
>      }
>  
> +  if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl))
> +      <= TMPL_ARGS_DEPTH (args))
> +    /* The template has already been subsituted, e.g. for
> +
> +      template <typename> friend class ::C;
> +
> +       so we just need to return it directly.  */
> +    return TREE_TYPE (friend_tmpl);

IIUC I don't think this would do the right thing for a template friend
declaration that directly names a template from an outer current
instantiation:

    template<class T>
    struct A {
      template<class U> struct B;

      template<class U>
      struct C {
        template<class V> friend class A::B;
      };
    };

    template struct A<int*>::C<long>;

Here TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl)) is 2, and
so is TMPL_ARGS_DEPTH (args), so we'd exit early and return a fully
dependent TEMPLATE_DECL A<T>::B, but what we want to return is
A<int*>::B.

It should always be safe to exit early when
  TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl)) == 1
though, and that should cover the most common case.

> +
>    tree context = CP_DECL_CONTEXT (friend_tmpl);
>    if (TREE_CODE (context) == NAMESPACE_DECL)
>      push_nested_namespace (context);
> @@ -11764,23 +11773,19 @@ tsubst_friend_class (tree friend_tmpl, tree args)
>          compatible with the attachment of the friend template.  */
>       module_may_redeclare (tmpl, friend_tmpl);
>  
> -      if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl))
> -       > TMPL_ARGS_DEPTH (args))
> -     {
> -       tree parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
> -                                           args, tf_warning_or_error);
> -       tsubst_each_template_parm_constraints (parms, args,
> -                                              tf_warning_or_error);
> -          location_t saved_input_location = input_location;
> -          input_location = DECL_SOURCE_LOCATION (friend_tmpl);
> -       tree cons = get_constraints (friend_tmpl);
> -       ++processing_template_decl;
> -       cons = tsubst_constraint_info (cons, args, tf_warning_or_error,
> -                                      DECL_FRIEND_CONTEXT (friend_tmpl));
> -       --processing_template_decl;
> -          redeclare_class_template (TREE_TYPE (tmpl), parms, cons);
> -          input_location = saved_input_location;
> -     }
> +      tree parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
> +                                       args, tf_warning_or_error);
> +      tsubst_each_template_parm_constraints (parms, args,
> +                                          tf_warning_or_error);
> +      location_t saved_input_location = input_location;
> +      input_location = DECL_SOURCE_LOCATION (friend_tmpl);
> +      tree cons = get_constraints (friend_tmpl);
> +      ++processing_template_decl;
> +      cons = tsubst_constraint_info (cons, args, tf_warning_or_error,
> +                                  DECL_FRIEND_CONTEXT (friend_tmpl));
> +      --processing_template_decl;
> +      redeclare_class_template (TREE_TYPE (tmpl), parms, cons);
> +      input_location = saved_input_location;
>      }
>    else
>      {
> diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-16_a.C 
> b/gcc/testsuite/g++.dg/modules/tpl-friend-16_a.C
> new file mode 100644
> index 00000000000..e1cdcd98e1e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-16_a.C
> @@ -0,0 +1,40 @@
> +// PR c++/115801
> +// { dg-additional-options "-fmodules-ts -Wno-global-module" }
> +// { dg-module-cmi test }
> +
> +module;
> +
> +template <typename T> struct GMF;
> +template <typename T> struct GMF_Hidden {
> +  int go() { GMF<T> gmf; return gmf.x; }
> +};
> +
> +template <typename T> struct GMF {
> +private:
> +  template <typename> friend struct ::GMF_Hidden;
> +  int x = 1;
> +};
> +
> +template <typename T> int test_gmf() {
> +  GMF_Hidden<T> h; return h.go();
> +}
> +
> +export module test;
> +
> +export using ::GMF;
> +export using ::test_gmf;
> +
> +export template <typename> struct Attached;
> +template <typename T> struct Attached_Hidden {
> +  int go() { Attached<T> attached; return attached.x; }
> +};
> +
> +template <typename T> struct Attached {
> +private:
> +  template <typename> friend struct ::Attached_Hidden;
> +  int x = 2;
> +};
> +
> +export template <typename T> int test_attached() {
> +  Attached_Hidden<T> h; return h.go();
> +}
> diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-16_b.C 
> b/gcc/testsuite/g++.dg/modules/tpl-friend-16_b.C
> new file mode 100644
> index 00000000000..d3484ab19b1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-16_b.C
> @@ -0,0 +1,17 @@
> +// PR c++/115801
> +// { dg-additional-options "-fmodules-ts" }
> +
> +import test;
> +
> +int main() {
> +  GMF<int> gmf;
> +  Attached<int> attached;
> +
> +  int a = test_gmf<double>();
> +  int b = test_attached<double>();
> +
> +  GMF_Hidden<int> gmf_hidden;  // { dg-error "not declared" }
> +  Attached_Hidden<int> attached_hidden;  // { dg-error "not declared" }
> +}
> +
> +// { dg-prune-output "expected primary-expression" }
> -- 
> 2.43.2
> 
> 

Reply via email to