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.
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. -- >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 patch fixes the issue by just doing nothing when it detects this case; this causes the named class to be added as a pending friend which will be resolved once a definition is seen (lazy loaded). PR c++/115801 gcc/cp/ChangeLog: * pt.cc (tsubst_friend_class): Handle friend_tmpl without extra template parms. 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 | 9 +++++ .../g++.dg/modules/tpl-friend-16_a.C | 40 +++++++++++++++++++ .../g++.dg/modules/tpl-friend-16_b.C | 17 ++++++++ 3 files changed, 66 insertions(+) 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 77fa5907c3d..f7c57d16666 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -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)) + /* The friend template has not been declared, but is something like + + template <typename> friend class ::C; + + for an existing declaration that was not found by name lookup (not + exported from its module interface). There's nothing to do here. */ + tmpl = friend_tmpl; else { /* The friend template has not already been declared. In this 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