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);
+
   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