The problem in this testcase is that the C++ front end modifies the
TYPE_PURPOSE of TYPE_ARG_TYPES after a function type has been created
and added to the hash table, so a later creation of an equivalent
function type won't necessarily find the earlier type. Usually this
just means extra copies of function types, but it causes
verify_unstripped_types to abort, as it expects strip_typedefs to always
return its input for template arguments.
So this modification of Jakub's first patch from the PR modifies
strip_typedefs to avoid re-hashing a function type if we think nothing
is changing about that type. The problem with his original patch was
that it didn't consider attributes or the type itself being a typedef to
be a change, and was therefore returning types that still contained
typedefs.
Tested x86_64-pc-linux-gnu, applying to trunk.
I'm not adding the testcase at this point, because it is huge and
depends on boost. Any thoughts about how to deal with the test?
commit db13eaa81c01d232a92449004f76f9d640321f1e
Author: Jason Merrill <ja...@redhat.com>
Date: Sun Jan 31 14:58:04 2016 +0100
PR c++/68763
* tree.c (strip_typedefs) [FUNCTION_TYPE]: Avoid building a new
function type if nothing is changing.
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index d4cf310..2bf37bca 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -1314,7 +1314,22 @@ strip_typedefs (tree t, bool *remove_attributes)
case FUNCTION_TYPE:
case METHOD_TYPE:
{
- tree arg_types = NULL, arg_node, arg_type;
+ tree arg_types = NULL, arg_node, arg_node2, arg_type;
+ bool changed;
+
+ /* Because we stomp on TREE_PURPOSE of TYPE_ARG_TYPES in many places
+ around the compiler (e.g. cp_parser_late_parsing_default_args), we
+ can't expect that re-hashing a function type will find a previous
+ equivalent type, so try to reuse the input type if nothing has
+ changed. If the type is itself a variant, that will change. */
+ bool is_variant = typedef_variant_p (t);
+ if (remove_attributes
+ && (TYPE_ATTRIBUTES (t) || TYPE_USER_ALIGN (t)))
+ is_variant = true;
+
+ type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+ changed = type != TREE_TYPE (t) || is_variant;
+
for (arg_node = TYPE_ARG_TYPES (t);
arg_node;
arg_node = TREE_CHAIN (arg_node))
@@ -1324,11 +1339,27 @@ strip_typedefs (tree t, bool *remove_attributes)
arg_type = strip_typedefs (TREE_VALUE (arg_node),
remove_attributes);
gcc_assert (arg_type);
+ if (arg_type == TREE_VALUE (arg_node) && !changed)
+ continue;
- arg_types =
- tree_cons (TREE_PURPOSE (arg_node), arg_type, arg_types);
+ if (!changed)
+ {
+ changed = true;
+ for (arg_node2 = TYPE_ARG_TYPES (t);
+ arg_node2 != arg_node;
+ arg_node2 = TREE_CHAIN (arg_node2))
+ arg_types
+ = tree_cons (TREE_PURPOSE (arg_node2),
+ TREE_VALUE (arg_node2), arg_types);
+ }
+
+ arg_types
+ = tree_cons (TREE_PURPOSE (arg_node), arg_type, arg_types);
}
+ if (!changed)
+ return t;
+
if (arg_types)
arg_types = nreverse (arg_types);
@@ -1337,7 +1368,6 @@ strip_typedefs (tree t, bool *remove_attributes)
if (arg_node)
arg_types = chainon (arg_types, void_list_node);
- type = strip_typedefs (TREE_TYPE (t), remove_attributes);
if (TREE_CODE (t) == METHOD_TYPE)
{
tree class_type = TREE_TYPE (TREE_VALUE (arg_types));