this patch fixes the handling of template friend classes of template
classes. Our current handling was somewhat messed up, possibly because
of now-clarified ambiguities in earlier versions of the std. In
particular we'd search too many namespaces looking for an existing
declaration, and if not found inject into the global namespace as a
non-hidden name.
This patch:
a) removes some now-unneeded handling for self friendliness
b) looks only in the innermost containing namespace for an unqualified
friend [*]
c) injects into that namespace as a hidden name.
[*] fixing this may permit removal of some checks in other friend cases
too. I have not investigated.
applying to trunk
nathan
--
Nathan Sidwell
2017-12-18 Nathan Sidwell <nat...@acm.org>
PR c++/59930
* name-lookup.c (name_lookup::search_unqualified): Don't search
parent namespace when looking for hidden things.
* pt.c (tsubst_friend_class): Always push to friend scope, drop
unneeded self-friend check. Inject new hidden friend into correct
scope.
PR c++/59930
* g++.dg/parse/pr81247-c.C: Adjust.
* g++.dg/template/pr59930-[123].C: New.
Index: cp/name-lookup.c
===================================================================
--- cp/name-lookup.c (revision 255777)
+++ cp/name-lookup.c (working copy)
@@ -711,6 +711,15 @@ name_lookup::search_unqualified (tree sc
done:;
if (scope == global_namespace)
break;
+
+ /* If looking for hidden names, we only look in the innermost
+ namespace scope. [namespace.memdef]/3 If a friend
+ declaration in a non-local class first declares a class,
+ function, class template or function template the friend is a
+ member of the innermost enclosing namespace. See also
+ [basic.lookup.unqual]/7 */
+ if (flags & LOOKUP_HIDDEN)
+ break;
}
vec_safe_truncate (queue, length);
Index: cp/pt.c
===================================================================
--- cp/pt.c (revision 255777)
+++ cp/pt.c (working copy)
@@ -10005,57 +10005,23 @@ tsubst_friend_function (tree decl, tree
static tree
tsubst_friend_class (tree friend_tmpl, tree args)
{
- tree friend_type;
tree tmpl;
- tree context;
if (DECL_TEMPLATE_TEMPLATE_PARM_P (friend_tmpl))
{
- tree t = tsubst (TREE_TYPE (friend_tmpl), args, tf_none, NULL_TREE);
- return TREE_TYPE (t);
+ tmpl = tsubst (TREE_TYPE (friend_tmpl), args, tf_none, NULL_TREE);
+ return TREE_TYPE (tmpl);
}
- context = CP_DECL_CONTEXT (friend_tmpl);
+ tree context = CP_DECL_CONTEXT (friend_tmpl);
+ if (TREE_CODE (context) == NAMESPACE_DECL)
+ push_nested_namespace (context);
+ else
+ push_nested_class (context);
- if (context != global_namespace)
- {
- if (TREE_CODE (context) == NAMESPACE_DECL)
- push_nested_namespace (context);
- else
- push_nested_class (tsubst (context, args, tf_none, NULL_TREE));
- }
-
- /* Look for a class template declaration. We look for hidden names
- because two friend declarations of the same template are the
- same. For example, in:
-
- struct A {
- template <typename> friend class F;
- };
- template <typename> struct B {
- template <typename> friend class F;
- };
-
- both F templates are the same. */
- tmpl = lookup_name_real (DECL_NAME (friend_tmpl), 0, 0,
- /*block_p=*/true, 0, LOOKUP_HIDDEN);
-
- /* But, if we don't find one, it might be because we're in a
- situation like this:
-
- template <class T>
- struct S {
- template <class U>
- friend struct S;
- };
-
- Here, in the scope of (say) S<int>, `S' is bound to a TYPE_DECL
- for `S<int>', not the TEMPLATE_DECL. */
- if (!tmpl || !DECL_CLASS_TEMPLATE_P (tmpl))
- {
- tmpl = lookup_name_prefer_type (DECL_NAME (friend_tmpl), 1);
- tmpl = maybe_get_template_decl_from_type_decl (tmpl);
- }
+ tmpl = lookup_name_real (DECL_NAME (friend_tmpl), /*prefer_type=*/false,
+ /*non_class=*/false, /*block_p=*/false,
+ /*namespaces_only=*/false, LOOKUP_HIDDEN);
if (tmpl && DECL_CLASS_TEMPLATE_P (tmpl))
{
@@ -10068,53 +10034,50 @@ tsubst_friend_class (tree friend_tmpl, t
if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl))
> TMPL_ARGS_DEPTH (args))
{
- tree parms;
- location_t saved_input_location;
- parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
- args, tf_warning_or_error);
-
- saved_input_location = input_location;
+ tree parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
+ args, tf_warning_or_error);
+ location_t saved_input_location = input_location;
input_location = DECL_SOURCE_LOCATION (friend_tmpl);
tree cons = get_constraints (tmpl);
redeclare_class_template (TREE_TYPE (tmpl), parms, cons);
input_location = saved_input_location;
-
}
-
- friend_type = TREE_TYPE (tmpl);
}
else
{
/* The friend template has not already been declared. In this
case, the instantiation of the template class will cause the
- injection of this template into the global scope. */
+ injection of this template into the namespace scope. */
tmpl = tsubst (friend_tmpl, args, tf_warning_or_error, NULL_TREE);
- if (tmpl == error_mark_node)
- return error_mark_node;
- /* The new TMPL is not an instantiation of anything, so we
- forget its origins. We don't reset CLASSTYPE_TI_TEMPLATE for
- the new type because that is supposed to be the corresponding
- template decl, i.e., TMPL. */
- DECL_USE_TEMPLATE (tmpl) = 0;
- DECL_TEMPLATE_INFO (tmpl) = NULL_TREE;
- CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0;
- CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl))
- = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)));
+ 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
+ for the new type because that is supposed to be the
+ corresponding template decl, i.e., TMPL. */
+ DECL_USE_TEMPLATE (tmpl) = 0;
+ DECL_TEMPLATE_INFO (tmpl) = NULL_TREE;
+ CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0;
+ CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl))
+ = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)));
+
+ /* It is hidden. */
+ retrofit_lang_decl (DECL_TEMPLATE_RESULT (tmpl));
+ DECL_ANTICIPATED (tmpl)
+ = DECL_ANTICIPATED (DECL_TEMPLATE_RESULT (tmpl)) = true;
- /* Inject this template into the global scope. */
- friend_type = TREE_TYPE (pushdecl_top_level (tmpl, true));
+ /* Inject this template into the enclosing namspace scope. */
+ tmpl = pushdecl_namespace_level (tmpl, true);
+ }
}
- if (context != global_namespace)
- {
- if (TREE_CODE (context) == NAMESPACE_DECL)
- pop_nested_namespace (context);
- else
- pop_nested_class ();
- }
+ if (TREE_CODE (context) == NAMESPACE_DECL)
+ pop_nested_namespace (context);
+ else
+ pop_nested_class ();
- return friend_type;
+ return TREE_TYPE (tmpl);
}
/* Returns zero if TYPE cannot be completed later due to circularity.
Index: testsuite/g++.dg/parse/pr81247-c.C
===================================================================
--- testsuite/g++.dg/parse/pr81247-c.C (revision 255777)
+++ testsuite/g++.dg/parse/pr81247-c.C (working copy)
@@ -1,8 +1,9 @@
-// PR c++/81247 confused error
+// PR c++/81247 rejected well-formed
-namespace N { // { dg-message "previous declaration" }
+namespace N {
template < typename T > class A
- { // { dg-error "conflicts with a previous" }
+ {
+ // injects a hidden class N::N at instantiation time
template < T > friend class N;
};
}
Index: testsuite/g++.dg/template/pr59930-1.C
===================================================================
--- testsuite/g++.dg/template/pr59930-1.C (revision 0)
+++ testsuite/g++.dg/template/pr59930-1.C (working copy)
@@ -0,0 +1,18 @@
+// PR c++/59930
+
+namespace N {
+ template<typename T> class A {
+ // The injected name is N::B, because we don;t look outside of N
+ template<typename U> friend struct B;
+ private:
+ int n; // { dg-message "declared private here" }
+ public:
+ A (int);
+ };
+}
+
+template<typename T> struct B {
+ int f(N::A<int> ai) { return ai.n; } // { dg-error "is private" }
+};
+
+int k = B<int>().f(0);
Index: testsuite/g++.dg/template/pr59930-2.C
===================================================================
--- testsuite/g++.dg/template/pr59930-2.C (revision 0)
+++ testsuite/g++.dg/template/pr59930-2.C (working copy)
@@ -0,0 +1,17 @@
+// PR c++/59930
+
+namespace N {
+ template < typename T > class A
+ {
+ // Injects N::N
+ template < T > friend class N;
+ // { dg-error "template parameter" "" { target *-*-* } .-1 }
+ // { dg-error "redeclared" "" { target *-*-* } .-2 }
+ };
+}
+
+void f ()
+{
+ N::A < int > a1;
+ N::A <short > a2;
+}
Index: testsuite/g++.dg/template/pr59930-3.C
===================================================================
--- testsuite/g++.dg/template/pr59930-3.C (revision 0)
+++ testsuite/g++.dg/template/pr59930-3.C (working copy)
@@ -0,0 +1,29 @@
+// PR c++/59930
+
+namespace NS {
+ template<typename T> class Holder
+ {
+ private:
+ void func();
+
+ template<typename> friend class User;
+ };
+
+ template class Holder<long>;
+
+ template<typename T> class User
+ {
+ public:
+ void method() const
+ {
+ Holder<T> x;
+ x.func();
+ }
+ };
+} // namespace
+
+void Foo()
+{
+ NS::User<long> decl;
+ decl.method();
+}