@@ -7417,6 +7417,8 @@ 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 remove_defining_module (tree decl);
extern void mangle_module (int m, bool include_partition);
extern void mangle_module_fini ();
@@ -7650,6 +7652,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,35 @@ 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))
+ /* The old declaration should match the exportingness of the new
+ declaration. But hidden friend declarations just keep the
+ 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 (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. */
@@ -3321,6 +3326,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
if (flag_concepts)
remove_constraints (newdecl);
+ /* And similarly for any module tracking data. */
+ if (modules_p ())
+ remove_defining_module (newdecl);
+
ggc_free (newdecl);
return olddecl;
@@ -2727,6 +2727,12 @@ 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 attached to the same module as the temploid. This maps
+ these decls to the temploid they are instantiated them, as there is
+ no other easy way to get this information. */
+static GTY((cache)) decl_tree_cache_map *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 +7826,12 @@ 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,
+ since importers will need to merge this decl even if it was
+ attached to a different module. */
+ if (imported_temploid_friends->get (decl))
+ is_attached = false;
+
bits.b (is_attached);
}
bits.b (dep && dep->has_defn ());
@@ -7997,6 +8009,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 +8324,12 @@ trees_in::decl_value ()
}
}
+ if (TREE_CODE (inner) == FUNCTION_DECL
+ || TREE_CODE (inner) == TYPE_DECL)
+ if (tree owner = tree_node ())
+ if (is_new)
+ 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;
@@ -18941,6 +18968,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))
{
@@ -19249,6 +19282,46 @@ 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);
+ }
+}
+
+/* DECL is being freed, clear data we don't need anymore. */
+
+void
+remove_defining_module (tree decl)
+{
+ if (!modules_p ())
+ return;
+
+ if (imported_temploid_friends)
+ imported_temploid_friends->remove (decl);
+}
+
/* Create the flat name string. It is simplest to have it handy. */
void
@@ -20462,6 +20535,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
+ = decl_tree_cache_map::create_ggc (EXPERIMENT (1, 400));
}
#if CHECKING_P
@@ -4131,6 +4131,22 @@ mergeable_namespace_slots (tree ns, tree name, bool is_attached, tree *vec)
return vslot;
}
+/* Retrieve the bindings for an existing mergeable entity in namespace
+ NS slot NAME. Returns NULL if no such bindings exists. */
+
+static tree
+get_mergeable_namespace_binding (tree ns, tree name, bool is_attached)
+{
+ tree *mslot = find_namespace_slot (ns, name, false);
+ if (!mslot || !*mslot || TREE_CODE (*mslot) != BINDING_VECTOR)
+ return NULL_TREE;
+
+ tree *vslot = get_fixed_binding_slot
+ (mslot, name, is_attached ? BINDING_SLOT_PARTITION : BINDING_SLOT_GLOBAL,
+ false);
+ return vslot ? *vslot : NULL_TREE;
+}
+
/* DECL is a new mergeable namespace-scope decl. Add it to the
mergeable entities on GSLOT. */
@@ -4453,6 +4469,43 @@ push_local_binding (tree id, tree decl, bool is_using)
add_decl_to_level (b, decl);
}
+/* Lookup the FRIEND_TMPL within all merged module imports. Used to dedup
+ instantiations of temploid hidden friends from imported modules. */
+
+tree
+lookup_imported_hidden_friend (tree friend_tmpl)
+{
+ /* For a class-scope friend class it should have been found by regular
+ name lookup. Otherwise we're looking in the current namespace. */
+ gcc_checking_assert (CP_DECL_CONTEXT (friend_tmpl) == current_namespace);
+
+ tree inner = DECL_TEMPLATE_RESULT (friend_tmpl);
+ if (!DECL_LANG_SPECIFIC (inner)
+ || !DECL_MODULE_IMPORT_P (inner))
+ return NULL_TREE;
+
+ /* Imported temploid friends are not considered as attached to this
+ module for merging purposes. */
+ tree bind = get_mergeable_namespace_binding (current_namespace,
+ DECL_NAME (inner), false);
+ if (!bind)
+ 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);
+
+ /* There should be at most one class template from the module we're
+ looking for, return it. */
+ for (ovl_iterator iter (bind); iter; ++iter)
+ if (DECL_CLASS_TEMPLATE_P (*iter)
+ && 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,12 @@ 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 the existing declaration's module attachment is
+ 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))
{
@@ -11751,9 +11767,19 @@ tsubst_friend_class (tree friend_tmpl, tree args)
if (tmpl != error_mark_node)
{
/* The new TMPL is not an instantiation of anything, so we
- forget its origins. We don't reset CLASSTYPE_TI_TEMPLATE
+ forget its origins. It is also not a specialization of
+ anything. We don't reset CLASSTYPE_TI_TEMPLATE
for the new type because that is supposed to be the
corresponding template decl, i.e., TMPL. */
+ if (modules_p ())
+ {
+ spec_entry elt;
+ elt.tmpl = friend_tmpl;
+ elt.args = CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl));
+ elt.spec = TREE_TYPE (tmpl);
+ type_specializations->remove_elt (&elt);
+ }
+
DECL_USE_TEMPLATE (tmpl) = 0;
DECL_TEMPLATE_INFO (tmpl) = NULL_TREE;
CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0;
@@ -11772,6 +11798,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:part }
+
+export module user:part;
+import test_support;
+export A<int> b;
new file mode 100644
@@ -0,0 +1,8 @@
+// PR c++/105320
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi user }
+
+export module user;
+export import :part;
+import test_support;
+A<double> c;
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 { };