From: Nathan Sidwell Date: Mon, 18 Dec 2017 16:30:58 +0000 (+0000) Subject: [PR c++/59930] template friend injection X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=02c7dd78fa0a3d05231f6340c4cc80d91b2393a5;p=gcc.git [PR c++/59930] template friend injection https://gcc.gnu.org/ml/gcc-patches/2017-12/msg01168.html 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. From-SVN: r255780 --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 5c281004ef6..3885e566d0c 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,12 @@ +2017-12-18 Nathan Sidwell + + 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. + 2017-12-15 Jakub Jelinek PR c++/83205 diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 97d0632301d..978ff77ae75 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -711,6 +711,15 @@ name_lookup::search_unqualified (tree scope, cp_binding_level *level) 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); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index dc50f31034b..4b42f03c27f 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -10005,57 +10005,23 @@ tsubst_friend_function (tree decl, tree args) 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); - } - - context = CP_DECL_CONTEXT (friend_tmpl); - - 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)); + tmpl = tsubst (TREE_TYPE (friend_tmpl), args, tf_none, NULL_TREE); + return TREE_TYPE (tmpl); } - /* 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 friend class F; - }; - template struct B { - template 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 - struct S { - template - friend struct S; - }; + tree context = CP_DECL_CONTEXT (friend_tmpl); + if (TREE_CODE (context) == NAMESPACE_DECL) + push_nested_namespace (context); + else + push_nested_class (context); - Here, in the scope of (say) S, `S' is bound to a TYPE_DECL - for `S', 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, tree args) 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. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c00a20f79d6..831a9bb1126 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2017-12-18 Nathan Sidwell + + PR c++/59930 + * g++.dg/parse/pr81247-c.C: Adjust. + * g++.dg/template/pr59930-[123].C: New. + 2017-12-18 Claudiu Zissulescu * gcc.target/arc/tumaddsidi4.c: New test. diff --git a/gcc/testsuite/g++.dg/parse/pr81247-c.C b/gcc/testsuite/g++.dg/parse/pr81247-c.C index 32f41f28f41..33183c5294b 100644 --- a/gcc/testsuite/g++.dg/parse/pr81247-c.C +++ b/gcc/testsuite/g++.dg/parse/pr81247-c.C @@ -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; }; } diff --git a/gcc/testsuite/g++.dg/template/pr59930-1.C b/gcc/testsuite/g++.dg/template/pr59930-1.C new file mode 100644 index 00000000000..7a8c17de1a7 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/pr59930-1.C @@ -0,0 +1,18 @@ +// PR c++/59930 + +namespace N { + template class A { + // The injected name is N::B, because we don;t look outside of N + template friend struct B; + private: + int n; // { dg-message "declared private here" } + public: + A (int); + }; +} + +template struct B { + int f(N::A ai) { return ai.n; } // { dg-error "is private" } +}; + +int k = B().f(0); diff --git a/gcc/testsuite/g++.dg/template/pr59930-2.C b/gcc/testsuite/g++.dg/template/pr59930-2.C new file mode 100644 index 00000000000..a7e6ea4ea9a --- /dev/null +++ b/gcc/testsuite/g++.dg/template/pr59930-2.C @@ -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 a2; +} diff --git a/gcc/testsuite/g++.dg/template/pr59930-3.C b/gcc/testsuite/g++.dg/template/pr59930-3.C new file mode 100644 index 00000000000..1769a3ce026 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/pr59930-3.C @@ -0,0 +1,29 @@ +// PR c++/59930 + +namespace NS { + template class Holder + { + private: + void func(); + + template friend class User; + }; + + template class Holder; + + template class User + { + public: + void method() const + { + Holder x; + x.func(); + } + }; +} // namespace + +void Foo() +{ + NS::User decl; + decl.method(); +}