@@ -7417,6 +7417,7 @@ extern unsigned get_importing_module (tree, bool = false) ATTRIBUTE_PURE;
extern void set_instantiating_module (tree);
extern void set_defining_module (tree);
extern void maybe_key_decl (tree ctx, tree decl);
+extern void propagate_defining_module (tree decl, tree orig);
extern void mangle_module (int m, bool include_partition);
extern void mangle_module_fini ();
@@ -7649,6 +7650,7 @@ extern bool template_guide_p (const_tree);
extern bool builtin_guide_p (const_tree);
extern void store_explicit_specifier (tree, tree);
extern tree lookup_explicit_specifier (tree);
+extern tree lookup_imported_hidden_friend (tree);
extern void walk_specializations (bool,
void (*)(bool, spec_entry *,
void *),
@@ -2276,30 +2276,34 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
if (modules_p ()
&& TREE_CODE (CP_DECL_CONTEXT (olddecl)) == NAMESPACE_DECL
- && TREE_CODE (olddecl) != NAMESPACE_DECL
- && !hiding)
+ && TREE_CODE (olddecl) != NAMESPACE_DECL)
{
if (!module_may_redeclare (olddecl, newdecl))
return error_mark_node;
- tree not_tmpl = STRIP_TEMPLATE (olddecl);
- if (DECL_LANG_SPECIFIC (not_tmpl)
- && DECL_MODULE_ATTACH_P (not_tmpl)
- /* Typedefs are not entities and so are OK to be redeclared
- as exported: see [module.interface]/p6. */
- && TREE_CODE (olddecl) != TYPE_DECL)
+ if (!hiding)
{
- if (DECL_MODULE_EXPORT_P (STRIP_TEMPLATE (newdecl))
- && !DECL_MODULE_EXPORT_P (not_tmpl))
+ /* Hidden friend declarations just use exportingness of the
+ old declaration; see CWG2588. */
+ tree not_tmpl = STRIP_TEMPLATE (olddecl);
+ if (DECL_LANG_SPECIFIC (not_tmpl)
+ && DECL_MODULE_ATTACH_P (not_tmpl)
+ /* Typedefs are not entities and so are OK to be redeclared
+ as exported: see [module.interface]/p6. */
+ && TREE_CODE (olddecl) != TYPE_DECL)
{
- auto_diagnostic_group d;
- error ("conflicting exporting for declaration %qD", newdecl);
- inform (olddecl_loc,
- "previously declared here without exporting");
+ if (DECL_MODULE_EXPORT_P (STRIP_TEMPLATE (newdecl))
+ && !DECL_MODULE_EXPORT_P (not_tmpl))
+ {
+ auto_diagnostic_group d;
+ error ("conflicting exporting for declaration %qD", newdecl);
+ inform (olddecl_loc,
+ "previously declared here without exporting");
+ }
}
+ else if (DECL_MODULE_EXPORT_P (newdecl))
+ DECL_MODULE_EXPORT_P (not_tmpl) = true;
}
- else if (DECL_MODULE_EXPORT_P (newdecl))
- DECL_MODULE_EXPORT_P (not_tmpl) = true;
}
/* We have committed to returning OLDDECL at this point. */
@@ -2727,6 +2727,11 @@ vec<tree, va_heap, vl_embed> *post_load_decls;
typedef hash_map<tree, auto_vec<tree>> keyed_map_t;
static keyed_map_t *keyed_table;
+/* Instantiations of temploid friends imported from another module
+ need to be owned by the same module as their instantiating template.
+ This maps these to the template that instantiated them. */
+static hash_map<tree, tree> *imported_temploid_friends;
+
/********************************************************************/
/* Tree streaming. The tree streaming is very specific to the tree
structures themselves. A tag indicates the kind of tree being
@@ -7820,6 +7825,10 @@ trees_out::decl_value (tree decl, depset *dep)
&& DECL_MODULE_ATTACH_P (not_tmpl))
is_attached = true;
+ /* But don't consider imported temploid friends as attached. */
+ if (imported_temploid_friends->get (decl))
+ is_attached = false;
+
bits.b (is_attached);
}
bits.b (dep && dep->has_defn ());
@@ -7997,6 +8006,15 @@ trees_out::decl_value (tree decl, depset *dep)
}
}
+ if (TREE_CODE (inner) == FUNCTION_DECL
+ || TREE_CODE (inner) == TYPE_DECL)
+ {
+ /* Write imported temploid friends so that importers can reconstruct
+ this information on stream-in. */
+ tree* slot = imported_temploid_friends->get (decl);
+ tree_node (slot ? *slot : NULL_TREE);
+ }
+
bool is_typedef = false;
if (!type && TREE_CODE (inner) == TYPE_DECL)
{
@@ -8303,6 +8321,11 @@ trees_in::decl_value ()
}
}
+ if (TREE_CODE (inner) == FUNCTION_DECL
+ || TREE_CODE (inner) == TYPE_DECL)
+ if (tree owner = tree_node ())
+ imported_temploid_friends->put (decl, owner);
+
/* Regular typedefs will have a NULL TREE_TYPE at this point. */
unsigned tdef_flags = 0;
bool is_typedef = false;
@@ -13370,10 +13393,17 @@ depset::hash::add_specializations (bool decl_p)
int use_tpl = 0;
bool is_friend = false;
- if (decl_p && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (entry->tmpl))
- /* A friend of a template. This is keyed to the
- instantiation. */
- is_friend = true;
+ if (DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (entry->tmpl))
+ {
+ if (decl_p)
+ /* A friend of a template. This is keyed to the
+ instantiation. */
+ is_friend = true;
+ else
+ /* An instantiated friend struct. Don't count this as
+ a specialization, it'll be picked up later. */
+ continue;
+ }
if (decl_p)
{
@@ -18930,6 +18960,12 @@ get_originating_module_decl (tree decl)
&& DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))
decl = TYPE_NAME (DECL_CHAIN (decl));
+ /* An imported temploid friend is attached to the same module the
+ befriending class was. */
+ if (imported_temploid_friends)
+ if (tree *slot = imported_temploid_friends->get (decl))
+ decl = *slot;
+
int use;
if (tree ti = node_template_info (decl, use))
{
@@ -19238,6 +19274,34 @@ maybe_key_decl (tree ctx, tree decl)
vec.safe_push (decl);
}
+/* DECL is an instantiated friend that should be attached to the same
+ module that ORIG is. */
+
+void
+propagate_defining_module (tree decl, tree orig)
+{
+ if (!modules_p ())
+ return;
+
+ tree not_tmpl = STRIP_TEMPLATE (orig);
+ if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_ATTACH_P (not_tmpl))
+ {
+ tree inner = STRIP_TEMPLATE (decl);
+ retrofit_lang_decl (inner);
+ DECL_MODULE_ATTACH_P (inner) = true;
+ }
+
+ if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_IMPORT_P (not_tmpl))
+ {
+ bool exists = imported_temploid_friends->put (decl, orig);
+
+ /* We should only be called if lookup for an existing decl
+ failed, in which case there shouldn't already be an entry
+ in the map. */
+ gcc_assert (!exists);
+ }
+}
+
/* Create the flat name string. It is simplest to have it handy. */
void
@@ -20451,6 +20515,8 @@ init_modules (cpp_reader *reader)
pending_table = new pending_map_t (EXPERIMENT (1, 400));
entity_map = new entity_map_t (EXPERIMENT (1, 400));
vec_safe_reserve (entity_ary, EXPERIMENT (1, 400));
+ imported_temploid_friends
+ = new hash_map<tree,tree> (EXPERIMENT (1, 400));
}
#if CHECKING_P
@@ -4453,6 +4453,48 @@ push_local_binding (tree id, tree decl, bool is_using)
add_decl_to_level (b, decl);
}
+/* Lookup the FRIEND_TMPL within all module imports. Used to dedup
+ instantiations of temploid hidden friends from imported modules. */
+
+tree
+lookup_imported_hidden_friend (tree friend_tmpl)
+{
+ tree inner = DECL_TEMPLATE_RESULT (friend_tmpl);
+ if (!DECL_LANG_SPECIFIC (inner)
+ || !DECL_MODULE_IMPORT_P (inner))
+ return NULL_TREE;
+
+ tree name = DECL_NAME (inner);
+ tree *slot = find_namespace_slot (current_namespace, name);
+ if (!slot || !*slot || TREE_CODE (*slot) != BINDING_VECTOR)
+ return NULL_TREE;
+
+ /* Look in the appropriate slot, as with check_module_override. */
+ binding_slot mslot;
+ if (named_module_p ())
+ mslot = BINDING_VECTOR_CLUSTER (*slot, BINDING_SLOT_PARTITION
+ / BINDING_VECTOR_SLOTS_PER_CLUSTER)
+ .slots[BINDING_SLOT_PARTITION % BINDING_VECTOR_SLOTS_PER_CLUSTER];
+ else
+ mslot = BINDING_VECTOR_CLUSTER (*slot, 0).slots[BINDING_SLOT_GLOBAL];
+ gcc_assert (!mslot.is_lazy ());
+
+ tree ovl = mslot;
+ if (!ovl)
+ return NULL_TREE;
+
+ /* We're only interested in declarations coming from the same module
+ of the friend class we're attempting to instantiate. */
+ int m = get_originating_module (friend_tmpl);
+ gcc_assert (m != 0);
+
+ for (ovl_iterator iter (ovl); iter; ++iter)
+ if (get_originating_module (*iter) == m)
+ return *iter;
+
+ return NULL_TREE;
+}
+
/* true means unconditionally make a BLOCK for the next level pushed. */
@@ -11512,6 +11512,10 @@ tsubst_friend_function (tree decl, tree args)
new_friend_result_template_info = DECL_TEMPLATE_INFO (not_tmpl);
}
+ /* We need to propagate module attachment for the new friend from the
+ owner of this template. */
+ propagate_defining_module (new_friend, decl);
+
/* Inside pushdecl_namespace_level, we will push into the
current namespace. However, the friend function should go
into the namespace of the template. */
@@ -11715,6 +11719,12 @@ tsubst_friend_class (tree friend_tmpl, tree args)
tmpl = lookup_name (DECL_NAME (friend_tmpl), LOOK_where::CLASS_NAMESPACE,
LOOK_want::NORMAL | LOOK_want::HIDDEN_FRIEND);
+ if (!tmpl)
+ /* If we didn't find by name lookup, the type may still exist but as a
+ 'hidden' import; we should check for this too to avoid accidentally
+ instantiating a duplicate. */
+ tmpl = lookup_imported_hidden_friend (friend_tmpl);
+
if (tmpl && DECL_CLASS_TEMPLATE_P (tmpl))
{
/* The friend template has already been declared. Just
@@ -11723,6 +11733,11 @@ tsubst_friend_class (tree friend_tmpl, tree args)
of course. We only need the innermost template parameters
because that is all that redeclare_class_template will look
at. */
+
+ if (modules_p ())
+ /* Check that we can redeclare TMPL in the current context. */
+ module_may_redeclare (tmpl, friend_tmpl);
+
if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl))
> TMPL_ARGS_DEPTH (args))
{
@@ -11772,6 +11787,10 @@ tsubst_friend_class (tree friend_tmpl, tree args)
args, tf_warning_or_error);
}
+ /* We need to propagate the attachment of the original template to the
+ newly instantiated template type. */
+ propagate_defining_module (tmpl, friend_tmpl);
+
/* Inject this template into the enclosing namspace scope. */
tmpl = pushdecl_namespace_level (tmpl, /*hiding=*/true);
}
new file mode 100644
@@ -0,0 +1,15 @@
+// PR c++/105320
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+// { dg-module-cmi test_support }
+
+module;
+template<class> struct _Sp_atomic;
+template<class> struct shared_ptr {
+ template<class> friend struct _Sp_atomic;
+ using atomic_type = _Sp_atomic<int>;
+};
+export module test_support;
+export
+template<class T> struct A {
+ shared_ptr<T> data;
+};
new file mode 100644
@@ -0,0 +1,5 @@
+// PR c++/105320
+// { dg-additional-options "-fmodules-ts" }
+
+import test_support;
+A<int> a;
new file mode 100644
@@ -0,0 +1,7 @@
+// PR c++/105320
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi user }
+
+export module user;
+import test_support;
+A<int> b;
new file mode 100644
@@ -0,0 +1,14 @@
+// PR c++/114275
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+// { dg-module-cmi M }
+
+module;
+
+template <typename... _Elements> struct T;
+
+template <typename H> struct T<H> {
+ template <typename...> friend struct T;
+};
+
+export module M;
+export template <typename=void> void fun() { T<int> t; }
new file mode 100644
@@ -0,0 +1,5 @@
+// PR c++/114275
+// { dg-additional-options "-fmodules-ts" }
+
+import M;
+int main() { fun(); }
new file mode 100644
@@ -0,0 +1,10 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi M:A }
+
+module M:A;
+
+template <typename T> struct A {
+ template <typename U> friend struct B;
+private:
+ int x = 42;
+};
new file mode 100644
@@ -0,0 +1,9 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi M:B }
+
+export module M:B;
+import :A;
+
+export template <typename U> struct B {
+ int foo(A<U> a) { return a.x; }
+};
new file mode 100644
@@ -0,0 +1,10 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi M:C }
+
+export module M:C;
+import :A;
+
+template <typename T> struct B;
+export template <typename T, typename U> int bar(B<T> t, U u) {
+ return t.foo(u);
+}
new file mode 100644
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi M }
+
+export module M;
+export import :B;
+export import :C;
+
+export int go_in_module();
new file mode 100644
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fmodules-ts" }
+
+module M;
+
+int go_in_module() {
+ return bar(B<int>{}, A<int>{});
+}
new file mode 100644
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fmodules-ts" }
+
+import M;
+
+int main() {
+ B<double> b{};
+ go_in_module();
+}
new file mode 100644
@@ -0,0 +1,13 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi M }
+
+export module M;
+export template <typename> struct A {
+ friend struct S;
+ template <typename> friend struct T;
+};
+
+export template <typename> struct B {
+ friend void f();
+ template <typename> friend void g();
+};
new file mode 100644
@@ -0,0 +1,11 @@
+// { dg-additional-options "-fmodules-ts" }
+
+import M;
+
+A<int> a;
+struct S {}; // { dg-error "conflicts with import" }
+template <typename> struct T {}; // { dg-error "conflicts with import" }
+
+B<int> c;
+void f() {} // { dg-error "conflicts with import" }
+template <typename> void g() {} // { dg-error "conflicts with import" }
new file mode 100644
@@ -0,0 +1,13 @@
+// { dg-additional-options "-fmodules-ts" }
+
+import M;
+
+struct S {}; // { dg-error "conflicts with import" }
+template <typename> struct T {}; // { dg-message "previously declared" }
+A<int> a; // { dg-message "required from here" }
+
+void f() {} // { dg-message "previously declared" }
+template <typename> void g() {} // { dg-message "previously declared" }
+B<int> b; // { dg-message "required from here" }
+
+// { dg-error "conflicting declaration" "" { target *-*-* } 0 }
new file mode 100644
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi X }
+
+export module X;
+export import M;
+A<int> ax;
+B<int> bx;
new file mode 100644
@@ -0,0 +1,18 @@
+// { dg-additional-options "-fmodules-ts" }
+
+// 'import X' does not correctly notice that S has already been declared.
+struct S {}; // { dg-message "previously declared" "" { xfail *-*-* } }
+template <typename> struct T {}; // { dg-message "previously declared" }
+void f() {} // { dg-message "previously declared" }
+template <typename T> void g() {} // { dg-message "previously declared" }
+
+import X;
+A<double> a2; // { dg-message "required from here" }
+B<double> b2; // { dg-message "required from here" }
+
+// specifically, S and T are defined in M, not X, despite the instantiation being in X
+// { dg-error "conflicting declaration \[^\n\r\]* S@M" "" { xfail *-*-* } 0 }
+// { dg-error "conflicting declaration \[^\n\r\]* T@M" "" { target *-*-* } 0 }
+// and similarly for f and g
+// { dg-error "conflicting declaration \[^\n\r\]* f@M" "" { target *-*-* } 0 }
+// { dg-error "conflicting declaration \[^\n\r\]* g@M" "" { target *-*-* } 0 }
new file mode 100644
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi Y }
+
+export module Y;
+export import M;
+A<double> ay;
+B<double> by;
new file mode 100644
@@ -0,0 +1,11 @@
+// { dg-additional-options "-fmodules-ts" }
+
+import X;
+import Y;
+
+// This should happily refer to the same S and T
+// as already instantiated in both X and Y
+A<long> az;
+
+// And same for f and g
+B<long> bz;
new file mode 100644
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi M }
+
+export module M;
+
+export extern "C++" template <typename> struct A {
+ template <typename> friend struct B;
+};
new file mode 100644
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi X }
+
+export module X;
+export import M;
+
+A<int> x;
+export extern "C++" template <typename T> struct B { using type = T; };
new file mode 100644
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi Y }
+
+export module Y;
+export import M;
+
+A<double> x;
new file mode 100644
@@ -0,0 +1,9 @@
+// { dg-additional-options "-fmodules-ts" }
+
+import X;
+import Y;
+
+int main() {
+ A<long> a;
+ B<int>::type r = 10;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+// PR c++/114275
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi M }
+
+export module M;
+
+template<class> struct A {
+ template<class> friend struct B;
+ friend void C();
+};
+A<int> a;
+void C() {}
+template<class> struct B { };