This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH for c++/41305 (infinite recursion in arg-dep lookup)
- From: Jason Merrill <jason at redhat dot com>
- To: gcc-patches List <gcc-patches at gcc dot gnu dot org>
- Date: Thu, 24 Dec 2009 16:02:38 -0500
- Subject: C++ PATCH for c++/41305 (infinite recursion in arg-dep lookup)
The testcase is pretty wierd code: a class A<T> derived from B<A<C<T>>.
If we instantiate classes which appear as template arguments to base
classes, we get infinite recursion. But while investigating this issue,
I found that DR 384 was resolved to say that we shouldn't look at
template arguments to base classes, only to the argument type itself.
So this patch fixes G++ accordingly.
While looking at this, I noticed that we were finding friend functions
two different ways, both by looking at DECL_FRIENDLIST for associated
classes and also by looking at hidden friends in associated namespaces
and checking to see if they are friends of an argument type. This is
redundant, so I removed the second method.
With that gone, I needed to tweak the first method to actually have the
right semantics: specifically, to treat unions like other classes, and
to look at the enclosing class where appropriate.
While looking at arg-dep lookup issues I also noticed that our handling
of template-ids was rather outdated; the standardese quoted there hasn't
been part of the draft for quite a few years now, and was supposed to be
talking about types, not functions. So I replaced the code with
something more sensible. I could defer this hunk to 4.6 if the release
managers would prefer, but it seems both safe and correct to me.
Tested x86_64-pc-linux-gnu, applied to trunk.
commit 5ca70d6e987d8573ac3f31170cee5d844508e90c
Author: Jason Merrill <jason@redhat.com>
Date: Thu Dec 24 11:41:25 2009 -0500
PR c++/41305, DR 384
* name-lookup.c (arg_assoc_class): Split out arg_assoc_class_only
and arg_assoc_bases.
(friend_of_associated_class_p): Remove.
(arg_assoc_namespace): Don't call it.
(arg_assoc_template_arg): Use arg_assoc_class_only for member
template context.
(arg_assoc_type): Handle UNION_TYPE and ENUMERAL_TYPE properly.
* name-lookup.c (arg_assoc): Handle TEMPLATE_ID_EXPR properly.
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 1371c8a..8f69346 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -4549,6 +4549,8 @@ static bool arg_assoc_args_vec (struct arg_lookup*, VEC(tree,gc) *);
static bool arg_assoc_type (struct arg_lookup*, tree);
static bool add_function (struct arg_lookup *, tree);
static bool arg_assoc_namespace (struct arg_lookup *, tree);
+static bool arg_assoc_class_only (struct arg_lookup *, tree);
+static bool arg_assoc_bases (struct arg_lookup *, tree);
static bool arg_assoc_class (struct arg_lookup *, tree);
static bool arg_assoc_template_arg (struct arg_lookup*, tree);
@@ -4606,53 +4608,6 @@ is_associated_namespace (tree current, tree scope)
}
}
-/* Return whether FN is a friend of an associated class of ARG. */
-
-static bool
-friend_of_associated_class_p (tree arg, tree fn)
-{
- tree type;
-
- if (TYPE_P (arg))
- type = arg;
- else if (type_unknown_p (arg))
- return false;
- else
- type = TREE_TYPE (arg);
-
- /* If TYPE is a class, the class itself and all base classes are
- associated classes. */
- if (CLASS_TYPE_P (type))
- {
- if (is_friend (type, fn))
- return true;
-
- if (TYPE_BINFO (type))
- {
- tree binfo, base_binfo;
- int i;
-
- for (binfo = TYPE_BINFO (type), i = 0;
- BINFO_BASE_ITERATE (binfo, i, base_binfo);
- i++)
- if (is_friend (BINFO_TYPE (base_binfo), fn))
- return true;
- }
- }
-
- /* If TYPE is a class member, the class of which it is a member is
- an associated class. */
- if ((CLASS_TYPE_P (type)
- || TREE_CODE (type) == UNION_TYPE
- || TREE_CODE (type) == ENUMERAL_TYPE)
- && TYPE_CONTEXT (type)
- && CLASS_TYPE_P (TYPE_CONTEXT (type))
- && is_friend (TYPE_CONTEXT (type), fn))
- return true;
-
- return false;
-}
-
/* Add functions of a namespace to the lookup structure.
Returns true on error. */
@@ -4686,18 +4641,9 @@ arg_assoc_namespace (struct arg_lookup *k, tree scope)
{
/* We don't want to find arbitrary hidden functions via argument
dependent lookup. We only want to find friends of associated
- classes. */
+ classes, which we'll do via arg_assoc_class. */
if (hidden_name_p (OVL_CURRENT (value)))
- {
- unsigned int ix;
- tree arg;
-
- for (ix = 0; VEC_iterate (tree, k->args, ix, arg); ++ix)
- if (friend_of_associated_class_p (arg, OVL_CURRENT (value)))
- break;
- if (ix >= VEC_length (tree, k->args))
- continue;
- }
+ continue;
if (add_function (k, OVL_CURRENT (value)))
return true;
@@ -4736,7 +4682,7 @@ arg_assoc_template_arg (struct arg_lookup *k, tree arg)
return arg_assoc_namespace (k, ctx);
/* Otherwise, it must be member template. */
else
- return arg_assoc_class (k, ctx);
+ return arg_assoc_class_only (k, ctx);
}
/* It's an argument pack; handle it recursively. */
else if (ARGUMENT_PACK_P (arg))
@@ -4758,41 +4704,25 @@ arg_assoc_template_arg (struct arg_lookup *k, tree arg)
return false;
}
-/* Adds everything associated with class to the lookup structure.
+/* Adds the class and its friends to the lookup structure.
Returns true on error. */
static bool
-arg_assoc_class (struct arg_lookup *k, tree type)
+arg_assoc_class_only (struct arg_lookup *k, tree type)
{
tree list, friends, context;
- int i;
- /* Backend build structures, such as __builtin_va_list, aren't
+ /* Backend-built structures, such as __builtin_va_list, aren't
affected by all this. */
if (!CLASS_TYPE_P (type))
return false;
- if (purpose_member (type, k->classes))
- return false;
- k->classes = tree_cons (type, NULL_TREE, k->classes);
-
context = decl_namespace_context (type);
if (arg_assoc_namespace (k, context))
return true;
complete_type (type);
- if (TYPE_BINFO (type))
- {
- /* Process baseclasses. */
- tree binfo, base_binfo;
-
- for (binfo = TYPE_BINFO (type), i = 0;
- BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
- if (arg_assoc_class (k, BINFO_TYPE (base_binfo)))
- return true;
- }
-
/* Process friends. */
for (list = DECL_FRIENDLIST (TYPE_MAIN_DECL (type)); list;
list = TREE_CHAIN (list))
@@ -4815,13 +4745,79 @@ arg_assoc_class (struct arg_lookup *k, tree type)
return true;
}
+ return false;
+}
+
+/* Adds the class and its bases to the lookup structure.
+ Returns true on error. */
+
+static bool
+arg_assoc_bases (struct arg_lookup *k, tree type)
+{
+ if (arg_assoc_class_only (k, type))
+ return true;
+
+ if (TYPE_BINFO (type))
+ {
+ /* Process baseclasses. */
+ tree binfo, base_binfo;
+ int i;
+
+ for (binfo = TYPE_BINFO (type), i = 0;
+ BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+ if (arg_assoc_bases (k, BINFO_TYPE (base_binfo)))
+ return true;
+ }
+
+ return false;
+}
+
+/* Adds everything associated with a class argument type to the lookup
+ structure. Returns true on error.
+
+ If T is a class type (including unions), its associated classes are: the
+ class itself; the class of which it is a member, if any; and its direct
+ and indirect base classes. Its associated namespaces are the namespaces
+ of which its associated classes are members. Furthermore, if T is a
+ class template specialization, its associated namespaces and classes
+ also include: the namespaces and classes associated with the types of
+ the template arguments provided for template type parameters (excluding
+ template template parameters); the namespaces of which any template
+ template arguments are members; and the classes of which any member
+ templates used as template template arguments are members. [ Note:
+ non-type template arguments do not contribute to the set of associated
+ namespaces. --end note] */
+
+static bool
+arg_assoc_class (struct arg_lookup *k, tree type)
+{
+ tree list;
+ int i;
+
+ /* Backend build structures, such as __builtin_va_list, aren't
+ affected by all this. */
+ if (!CLASS_TYPE_P (type))
+ return false;
+
+ if (purpose_member (type, k->classes))
+ return false;
+ k->classes = tree_cons (type, NULL_TREE, k->classes);
+
+ if (TYPE_CLASS_SCOPE_P (type)
+ && arg_assoc_class_only (k, TYPE_CONTEXT (type)))
+ return true;
+
+ if (arg_assoc_bases (k, type))
+ return true;
+
/* Process template arguments. */
if (CLASSTYPE_TEMPLATE_INFO (type)
&& PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (type)))
{
list = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
for (i = 0; i < TREE_VEC_LENGTH (list); ++i)
- arg_assoc_template_arg (k, TREE_VEC_ELT (list, i));
+ if (arg_assoc_template_arg (k, TREE_VEC_ELT (list, i)))
+ return true;
}
return false;
@@ -4861,13 +4857,16 @@ arg_assoc_type (struct arg_lookup *k, tree type)
case RECORD_TYPE:
if (TYPE_PTRMEMFUNC_P (type))
return arg_assoc_type (k, TYPE_PTRMEMFUNC_FN_TYPE (type));
+ case UNION_TYPE:
return arg_assoc_class (k, type);
case POINTER_TYPE:
case REFERENCE_TYPE:
case ARRAY_TYPE:
return arg_assoc_type (k, TREE_TYPE (type));
- case UNION_TYPE:
case ENUMERAL_TYPE:
+ if (TYPE_CLASS_SCOPE_P (type)
+ && arg_assoc_class_only (k, TYPE_CONTEXT (type)))
+ return true;
return arg_assoc_namespace (k, decl_namespace_context (type));
case METHOD_TYPE:
/* The basetype is referenced in the first arg type, so just
@@ -4951,34 +4950,17 @@ arg_assoc (struct arg_lookup *k, tree n)
return arg_assoc_type (k, TREE_TYPE (n));
if (TREE_CODE (n) == TEMPLATE_ID_EXPR)
{
- /* [basic.lookup.koenig]
-
- If T is a template-id, its associated namespaces and classes
- are the namespace in which the template is defined; for
- member templates, the member template's class... */
+ /* The working paper doesn't currently say how to handle template-id
+ arguments. The sensible thing would seem to be to handle the list
+ of template candidates like a normal overload set, and handle the
+ template arguments like we do for class template
+ specializations. */
tree templ = TREE_OPERAND (n, 0);
tree args = TREE_OPERAND (n, 1);
- tree ctx;
int ix;
- if (TREE_CODE (templ) == COMPONENT_REF)
- templ = TREE_OPERAND (templ, 1);
-
- /* First, the template. There may actually be more than one if
- this is an overloaded function template. But, in that case,
- we only need the first; all the functions will be in the same
- namespace. */
- templ = OVL_CURRENT (templ);
-
- ctx = CP_DECL_CONTEXT (templ);
-
- if (TREE_CODE (ctx) == NAMESPACE_DECL)
- {
- if (arg_assoc_namespace (k, ctx) == 1)
- return true;
- }
- /* It must be a member template. */
- else if (arg_assoc_class (k, ctx) == 1)
+ /* First the templates. */
+ if (arg_assoc (k, templ))
return true;
/* Now the arguments. */
diff --git a/gcc/testsuite/g++.dg/lookup/koenig10.C b/gcc/testsuite/g++.dg/lookup/koenig10.C
new file mode 100644
index 0000000..f2fce9c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/koenig10.C
@@ -0,0 +1,12 @@
+// Test for proper handling of class-scope enums.
+
+struct A
+{
+ enum E { e };
+ friend void f (E);
+};
+
+int main()
+{
+ f(A::e);
+}
diff --git a/gcc/testsuite/g++.dg/lookup/koenig11.C b/gcc/testsuite/g++.dg/lookup/koenig11.C
new file mode 100644
index 0000000..dab853b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/koenig11.C
@@ -0,0 +1,12 @@
+// Test that we treat unions like other classes in arg-dep lookup.
+
+union U
+{
+ friend void f (U);
+};
+
+int main()
+{
+ U u;
+ f(u);
+}
diff --git a/gcc/testsuite/g++.dg/lookup/koenig12.C b/gcc/testsuite/g++.dg/lookup/koenig12.C
new file mode 100644
index 0000000..c135899
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/koenig12.C
@@ -0,0 +1,18 @@
+// PR c++/41305
+// We got into infinite recursion instantiating the B<U> series.
+
+template <class T> struct A { };
+template <class T, class U = A<T> > struct B;
+template <class T> struct C { };
+
+template <class T, class U> struct B: C<B<U> >
+{
+ friend void f(B) { }
+};
+
+B<int> b;
+
+int main()
+{
+ f(b);
+}
diff --git a/gcc/testsuite/g++.dg/lookup/koenig9.C b/gcc/testsuite/g++.dg/lookup/koenig9.C
new file mode 100644
index 0000000..f867a32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/koenig9.C
@@ -0,0 +1,25 @@
+// Test for sensible handling of template-ids with arg-dep lookup.
+// This is still an open issue.
+
+namespace N
+{
+ struct A { };
+ void f(void (*)(int, N::A));
+}
+
+namespace M
+{
+ struct B { };
+ void f(void (*)(B, N::A));
+}
+
+template <class T>
+void g(T, N::A);
+
+void g();
+
+int main()
+{
+ f(g<int>);
+ f(g<M::B>);
+}