Commit 02c7dd78 by Nathan Sidwell Committed by Nathan Sidwell

[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
parent 0cf0bc67
2017-12-18 Nathan Sidwell <nathan@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.
2017-12-15 Jakub Jelinek <jakub@redhat.com> 2017-12-15 Jakub Jelinek <jakub@redhat.com>
PR c++/83205 PR c++/83205
......
...@@ -711,6 +711,15 @@ name_lookup::search_unqualified (tree scope, cp_binding_level *level) ...@@ -711,6 +711,15 @@ name_lookup::search_unqualified (tree scope, cp_binding_level *level)
done:; done:;
if (scope == global_namespace) if (scope == global_namespace)
break; 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); vec_safe_truncate (queue, length);
......
...@@ -10005,57 +10005,23 @@ tsubst_friend_function (tree decl, tree args) ...@@ -10005,57 +10005,23 @@ tsubst_friend_function (tree decl, tree args)
static tree static tree
tsubst_friend_class (tree friend_tmpl, tree args) tsubst_friend_class (tree friend_tmpl, tree args)
{ {
tree friend_type;
tree tmpl; tree tmpl;
tree context;
if (DECL_TEMPLATE_TEMPLATE_PARM_P (friend_tmpl)) if (DECL_TEMPLATE_TEMPLATE_PARM_P (friend_tmpl))
{ {
tree t = tsubst (TREE_TYPE (friend_tmpl), args, tf_none, NULL_TREE); tmpl = tsubst (TREE_TYPE (friend_tmpl), args, tf_none, NULL_TREE);
return TREE_TYPE (t); return TREE_TYPE (tmpl);
}
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));
} }
/* Look for a class template declaration. We look for hidden names tree context = CP_DECL_CONTEXT (friend_tmpl);
because two friend declarations of the same template are the if (TREE_CODE (context) == NAMESPACE_DECL)
same. For example, in: push_nested_namespace (context);
else
struct A { push_nested_class (context);
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 tmpl = lookup_name_real (DECL_NAME (friend_tmpl), /*prefer_type=*/false,
for `S<int>', not the TEMPLATE_DECL. */ /*non_class=*/false, /*block_p=*/false,
if (!tmpl || !DECL_CLASS_TEMPLATE_P (tmpl)) /*namespaces_only=*/false, LOOKUP_HIDDEN);
{
tmpl = lookup_name_prefer_type (DECL_NAME (friend_tmpl), 1);
tmpl = maybe_get_template_decl_from_type_decl (tmpl);
}
if (tmpl && DECL_CLASS_TEMPLATE_P (tmpl)) if (tmpl && DECL_CLASS_TEMPLATE_P (tmpl))
{ {
...@@ -10068,53 +10034,50 @@ tsubst_friend_class (tree friend_tmpl, tree args) ...@@ -10068,53 +10034,50 @@ tsubst_friend_class (tree friend_tmpl, tree args)
if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl)) if (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (friend_tmpl))
> TMPL_ARGS_DEPTH (args)) > TMPL_ARGS_DEPTH (args))
{ {
tree parms; tree parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl),
location_t saved_input_location; args, tf_warning_or_error);
parms = tsubst_template_parms (DECL_TEMPLATE_PARMS (friend_tmpl), location_t saved_input_location = input_location;
args, tf_warning_or_error);
saved_input_location = input_location;
input_location = DECL_SOURCE_LOCATION (friend_tmpl); input_location = DECL_SOURCE_LOCATION (friend_tmpl);
tree cons = get_constraints (tmpl); tree cons = get_constraints (tmpl);
redeclare_class_template (TREE_TYPE (tmpl), parms, cons); redeclare_class_template (TREE_TYPE (tmpl), parms, cons);
input_location = saved_input_location; input_location = saved_input_location;
} }
friend_type = TREE_TYPE (tmpl);
} }
else else
{ {
/* The friend template has not already been declared. In this /* The friend template has not already been declared. In this
case, the instantiation of the template class will cause the 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); 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 if (tmpl != error_mark_node)
forget its origins. We don't reset CLASSTYPE_TI_TEMPLATE for {
the new type because that is supposed to be the corresponding /* The new TMPL is not an instantiation of anything, so we
template decl, i.e., TMPL. */ forget its origins. We don't reset CLASSTYPE_TI_TEMPLATE
DECL_USE_TEMPLATE (tmpl) = 0; for the new type because that is supposed to be the
DECL_TEMPLATE_INFO (tmpl) = NULL_TREE; corresponding template decl, i.e., TMPL. */
CLASSTYPE_USE_TEMPLATE (TREE_TYPE (tmpl)) = 0; DECL_USE_TEMPLATE (tmpl) = 0;
CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)) DECL_TEMPLATE_INFO (tmpl) = NULL_TREE;
= INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl))); 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. */ /* Inject this template into the enclosing namspace scope. */
friend_type = TREE_TYPE (pushdecl_top_level (tmpl, true)); tmpl = pushdecl_namespace_level (tmpl, true);
}
} }
if (context != global_namespace) if (TREE_CODE (context) == NAMESPACE_DECL)
{ pop_nested_namespace (context);
if (TREE_CODE (context) == NAMESPACE_DECL) else
pop_nested_namespace (context); pop_nested_class ();
else
pop_nested_class ();
}
return friend_type; return TREE_TYPE (tmpl);
} }
/* Returns zero if TYPE cannot be completed later due to circularity. /* Returns zero if TYPE cannot be completed later due to circularity.
......
2017-12-18 Nathan Sidwell <nathan@acm.org>
PR c++/59930
* g++.dg/parse/pr81247-c.C: Adjust.
* g++.dg/template/pr59930-[123].C: New.
2017-12-18 Claudiu Zissulescu <claziss@synopsys.com> 2017-12-18 Claudiu Zissulescu <claziss@synopsys.com>
* gcc.target/arc/tumaddsidi4.c: New test. * gcc.target/arc/tumaddsidi4.c: New test.
......
// PR c++/81247 confused error // PR c++/81247 rejected well-formed
namespace N { // { dg-message "previous declaration" } namespace N {
template < typename T > class A 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; template < T > friend class N;
}; };
} }
......
// 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);
// 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;
}
// 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();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment