My earlier patch for 50800 fixed the ICE by consistently stripping
non-mangled attributes from template arguments, and mangling those that
affect type identity. At the C++ meeting this week someone pointed out
to me that this is a real problem for x86 vector code, which relies on
may_alias semantics: if may_alias is stripped from __m128, users can't
use templates with vectors.
So, it seems that the solution is to mangle may_alias by saying that it
affects type identity. But since we still want to be able to convert
back and forth, I thought that it would make sense to treat the
may_alias version of a type as a variant, rather than a new distinct
type. So the first patch creates a new category of attributes that are
treated as type variants.
An alternative patch just sets affects_type_identity and adjusts the C++
front end to allow conversion between pointers to add or discard may_alias.
Thoughts?
Tested x86_64-pc-linux-gnu.
commit b9722b2721f8e3901a7343b9c373d37a9e4ecefd
Author: Jason Merrill <ja...@redhat.com>
Date: Tue Jun 21 22:26:38 2016 +0300
PR c++/50800 - may_alias and templates
gcc/c/
* tree-core.h (ATTRIBUTE_TYPE_VARIANT): New enumerator.
* tree.c (comp_type_attributes): Check variant_attribute_p.
(build_type_attribute_qual_variant): Handle variant attributes.
(check_attribute_qualified_type, get_attribute_qualified_type): New.
* tree.h: Declare get_attribute_qualified_type.
* attribs.c (variant_attribute_p, remove_variant_attributes): New.
* attribs.h: Declare them.
gcc/c-family/
* c-common.c (c_common_attribute_table): may_alias affects type
identity.
gcc/cp/
* tree.c (apply_identity_attributes): No longer static.
* mangle.c (canonicalize_for_substitution): Call it.
* cp-tree.h: Declare it.
* typeck.c (underlying_type_name): New.
(structural_comptypes): Do structurally compare arithmetic types.
diff --git a/gcc/attribs.c b/gcc/attribs.c
index 9a88621..c36f4b4 100644
--- a/gcc/attribs.c
+++ b/gcc/attribs.c
@@ -690,3 +690,43 @@ make_attribute (const char *name, const char *arg_name, tree chain)
attr = tree_cons (attr_name, attr_args, chain);
return attr;
}
+
+/* True iff ATTR is a type attribute that should be treated as creating a
+ variant of a base type, rather than a completely distinct type. */
+
+bool
+variant_attribute_p (const_tree attr)
+{
+ const attribute_spec *s = lookup_attribute_spec (TREE_PURPOSE (attr));
+ return s && s->affects_type_identity == ATTRIBUTE_TYPE_VARIANT;
+}
+
+/* Return either ATTRS or a list of attributes without any attributes for which
+ variant_attribute_p is true. Does not modify ATTRs. */
+
+tree
+remove_variant_attributes (tree attrs)
+{
+ tree last = NULL_TREE;
+ for (tree a = attrs; a; a = TREE_CHAIN (a))
+ if (variant_attribute_p (a))
+ last = a;
+ if (!last)
+ return attrs;
+ tree l = NULL_TREE;
+ tree *p = &l;
+ for (tree a = attrs; a; a = TREE_CHAIN (a))
+ {
+ if (a == last)
+ {
+ *p = TREE_CHAIN (a);
+ break;
+ }
+ if (!variant_attribute_p (a))
+ {
+ *p = build_tree_list (TREE_PURPOSE (a), TREE_VALUE (a));
+ p = &TREE_CHAIN (*p);
+ }
+ }
+ return l;
+}
diff --git a/gcc/attribs.h b/gcc/attribs.h
index 23d3043..9a4d4e0 100644
--- a/gcc/attribs.h
+++ b/gcc/attribs.h
@@ -41,4 +41,7 @@ extern tree make_attribute (const char *, const char *, tree);
extern struct scoped_attributes* register_scoped_attributes (const struct attribute_spec *,
const char *);
+extern bool variant_attribute_p (const_tree);
+extern tree remove_variant_attributes (tree);
+
#endif // GCC_ATTRIBS_H
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 8f21fd1..96e97c4 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -757,7 +757,8 @@ const struct attribute_spec c_common_attribute_table[] =
handle_nonnull_attribute, false },
{ "nothrow", 0, 0, true, false, false,
handle_nothrow_attribute, false },
- { "may_alias", 0, 0, false, true, false, NULL, false },
+ { "may_alias", 0, 0, false, true, false, NULL,
+ ATTRIBUTE_TYPE_VARIANT },
{ "cleanup", 1, 1, true, false, false,
handle_cleanup_attribute, false },
{ "warn_unused_result", 0, 0, false, true, true,
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5b87bb3..418b650 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6508,6 +6508,7 @@ extern bool class_tmpl_impl_spec_p (const_tree);
extern int zero_init_p (const_tree);
extern bool check_abi_tag_redeclaration (const_tree, const_tree, const_tree);
extern bool check_abi_tag_args (tree, tree);
+extern tree apply_identity_attributes (tree, tree, bool * = NULL);
extern tree strip_typedefs (tree, bool * = NULL);
extern tree strip_typedefs_expr (tree, bool * = NULL);
extern tree copy_binfo (tree, tree, tree,
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 0e44409..19208a2 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -368,6 +368,8 @@ canonicalize_for_substitution (tree node)
else
node = cp_build_qualified_type (TYPE_MAIN_VARIANT (node),
cp_type_quals (node));
+ if (abi_version_at_least (10))
+ node = apply_identity_attributes (node, TYPE_ATTRIBUTES (orig));
if (TREE_CODE (node) == FUNCTION_TYPE
|| TREE_CODE (node) == METHOD_TYPE)
node = build_ref_qualified_type (node, type_memfn_rqual (orig));
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 6010f63..9193b2d 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -1225,7 +1225,7 @@ cv_unqualified (tree type)
from ATTRIBS that affect type identity, and no others. If any are not
applied, set *remove_attributes to true. */
-static tree
+tree
apply_identity_attributes (tree result, tree attribs, bool *remove_attributes)
{
tree first_ident = NULL_TREE;
@@ -1268,7 +1268,7 @@ apply_identity_attributes (tree result, tree attribs, bool *remove_attributes)
if (first_ident == attribs)
/* All attributes affected type identity. */;
- else
+ else if (remove_attributes)
*remove_attributes = true;
return cp_build_type_attribute_variant (result, new_attribs);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 3704b88..9dd5ed6 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1181,6 +1181,17 @@ comp_template_parms_position (tree t1, tree t2)
return true;
}
+/* Look through typedefs to find the name of the underlying type of T. */
+
+static tree
+underlying_type_name (tree t)
+{
+ tree d = TYPE_NAME (t);
+ while (d && is_typedef_decl (d))
+ d = TYPE_NAME (DECL_ORIGINAL_TYPE (d));
+ return d;
+}
+
/* Subroutine in comptypes. */
static bool
@@ -1235,7 +1246,7 @@ structural_comptypes (tree t1, tree t2, int strict)
if (TREE_CODE (t1) != ARRAY_TYPE
&& TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2))
- return true;
+ return comp_type_attributes (t1, t2);
/* Compare the types. Break out if they could be the same. */
@@ -1249,7 +1260,7 @@ structural_comptypes (tree t1, tree t2, int strict)
case INTEGER_TYPE:
case FIXED_POINT_TYPE:
case REAL_TYPE:
- /* With these nodes, we can't determine type equivalence by
+ /* With these nodes, it isn't simple to determine type equivalence by
looking at what is stored in the nodes themselves, because
two nodes might have different TYPE_MAIN_VARIANTs but still
represent the same type. For example, wchar_t and int could
@@ -1261,9 +1272,21 @@ structural_comptypes (tree t1, tree t2, int strict)
typedef int INT __attribute((may_alias));
have identical properties, different TYPE_MAIN_VARIANTs, but
- represent the same type. The canonical type system keeps
- track of equivalence in this case, so we fall back on it. */
- return TYPE_CANONICAL (t1) == TYPE_CANONICAL (t2);
+ represent the same type. */
+ /* If the properties are different, they are different types. */
+ if (TYPE_UNSIGNED (t1) != TYPE_UNSIGNED (t2)
+ || TYPE_PRECISION (t1) != TYPE_PRECISION (t2))
+ return false;
+ {
+ /* If one of the types is based on a named built-in type and the other
+ one is not based on the same type, they are different types. */
+ tree n1 = underlying_type_name (t1);
+ tree n2 = underlying_type_name (t2);
+ if ((n1 || n2) && n1 != n2)
+ return false;
+ }
+ /* Otherwise, compare attributes. */
+ break;
case TEMPLATE_TEMPLATE_PARM:
case BOUND_TEMPLATE_TEMPLATE_PARM:
diff --git a/gcc/testsuite/c-c++-common/attr-may-alias-1.c b/gcc/testsuite/c-c++-common/attr-may-alias-1.c
index 978b9a5..5b2986f 100644
--- a/gcc/testsuite/c-c++-common/attr-may-alias-1.c
+++ b/gcc/testsuite/c-c++-common/attr-may-alias-1.c
@@ -1,13 +1,13 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -Wall" } */
+/* { dg-options "-O2 -Wall" { target c } } */
typedef int T __attribute__((may_alias));
extern T *p;
-extern int *p;
+extern int *p; /* { dg-error "conflicting" "" { target c++ } } */
extern int *p2;
-extern T *p2;
+extern T *p2; /* { dg-error "conflicting" "" { target c++ } } */
void fn1 (T);
void fn1 (int);
diff --git a/gcc/testsuite/g++.dg/ext/alias-mangle.C b/gcc/testsuite/g++.dg/ext/alias-mangle.C
index d804c1a..062b365 100644
--- a/gcc/testsuite/g++.dg/ext/alias-mangle.C
+++ b/gcc/testsuite/g++.dg/ext/alias-mangle.C
@@ -1,6 +1,6 @@
// PR c++/34936
// { dg-do compile }
-/* { dg-final { scan-assembler "_ZN1AIdEC1Ev" } } */
+/* { dg-final { scan-assembler "_ZN1AIU9may_aliasdEC1Ev" } } */
typedef double X __attribute((may_alias)) ;
template<typename> struct A
@@ -8,4 +8,4 @@ template<typename> struct A
A();
};
-A<X> a; // { dg-warning "ignoring attributes on template argument" }
+A<X> a;
diff --git a/gcc/testsuite/g++.dg/ext/attrib50.C b/gcc/testsuite/g++.dg/ext/attrib50.C
index 9ff9918..a46c9f7 100644
--- a/gcc/testsuite/g++.dg/ext/attrib50.C
+++ b/gcc/testsuite/g++.dg/ext/attrib50.C
@@ -8,4 +8,4 @@ struct A {
typedef int TA __attribute__((__may_alias__));
};
void d() { B<int &> b; }
-int main() { B<A::TA &> b; } // { dg-warning "attributes" }
+int main() { B<A::TA &> b; }
diff --git a/gcc/testsuite/g++.dg/warn/Wignored-attributes-1.C b/gcc/testsuite/g++.dg/warn/Wignored-attributes-1.C
index 46ccc4a..fb95f7e 100644
--- a/gcc/testsuite/g++.dg/warn/Wignored-attributes-1.C
+++ b/gcc/testsuite/g++.dg/warn/Wignored-attributes-1.C
@@ -3,4 +3,4 @@
typedef float __m128 __attribute__((__vector_size__(16), __may_alias__));
template <typename> struct A;
-template <> struct A<__m128>; // { dg-warning "ignoring attributes on template argument" }
+template <> struct A<__m128>;
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index db5b470..6c098f6 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1860,10 +1860,13 @@ struct attribute_spec {
by the rest of this structure. */
tree (*handler) (tree *node, tree name, tree args,
int flags, bool *no_add_attrs);
- /* Specifies if attribute affects type's identity. */
- bool affects_type_identity;
+ /* Specifies if attribute affects type's identity. 0 means no, 1 means it
+ creates a new distinct type, and 2 means it creates a variant. */
+ int affects_type_identity;
};
+enum { ATTRIBUTE_TYPE_VARIANT = 2 };
+
/* These functions allow a front-end to perform a manual layout of a
RECORD_TYPE. (For instance, if the placement of subsequent fields
depends on the placement of fields so far.) Begin by calling
diff --git a/gcc/tree.c b/gcc/tree.c
index bc60190..8ce23e4 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -4836,6 +4836,23 @@ build_type_attribute_qual_variant (tree ttype, tree attribute, int quals)
}
ttype = build_qualified_type (ttype, TYPE_UNQUALIFIED);
+
+ tree a;
+ if (a = remove_variant_attributes (attribute),
+ a != attribute)
+ {
+ /* Some of the attributes create a variant type. First apply any
+ other attributes, then create the variant. */
+ t = build_type_attribute_variant (ttype, a);
+ ntype = get_attribute_qualified_type (t, attribute);
+ if (!ntype)
+ {
+ ntype = build_variant_type_copy (t);
+ TYPE_ATTRIBUTES (ntype) = attribute;
+ }
+ }
+ else
+ {
ntype = build_distinct_type_copy (ttype);
TYPE_ATTRIBUTES (ntype) = attribute;
@@ -4871,6 +4888,7 @@ build_type_attribute_qual_variant (tree ttype, tree attribute, int quals)
}
ntype = type_hash_canon (hstate.end(), ntype);
+ }
/* If the target-dependent attributes make NTYPE different from
its canonical type, we will need to use structural equality
@@ -5082,6 +5100,9 @@ comp_type_attributes (const_tree type1, const_tree type2)
}
if (lookup_attribute ("transaction_safe", CONST_CAST_TREE (a)))
return 0;
+ /* An attribute that creates a type variant changes the type. */
+ if (variant_attribute_p (a))
+ return 0;
/* As some type combinations - like default calling-convention - might
be compatible, we have to call the target hook to get the final result. */
return targetm.comp_type_attributes (type1, type2);
@@ -6550,6 +6571,20 @@ check_aligned_type (const_tree cand, const_tree base, unsigned int align)
TYPE_ATTRIBUTES (base)));
}
+/* True iff type CAND is equivalent to BASE with ATTRIBUTE. */
+
+static bool
+check_attribute_qualified_type (const_tree cand, const_tree base,
+ tree attribute)
+{
+ return (TYPE_QUALS (cand) == TYPE_QUALS (base)
+ && TYPE_NAME (cand) == TYPE_NAME (base)
+ && TYPE_CONTEXT (cand) == TYPE_CONTEXT (base)
+ && TYPE_ALIGN (cand) == TYPE_ALIGN (base)
+ && attribute_list_equal (TYPE_ATTRIBUTES (cand),
+ attribute));
+}
+
/* This function checks to see if TYPE matches the size one of the built-in
atomic types, and returns that core atomic type. */
@@ -6614,6 +6649,22 @@ get_qualified_type (tree type, int type_quals)
return NULL_TREE;
}
+/* Return a version of TYPE with ATTRIBUTES, if one exists. If no suitable
+ type exists yet, return NULL_TREE. */
+
+tree
+get_attribute_qualified_type (tree type, tree attribute)
+{
+ if (attribute_list_equal (TYPE_ATTRIBUTES (type), attribute))
+ return type;
+
+ for (tree t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))
+ if (check_attribute_qualified_type (t, type, attribute))
+ return t;
+
+ return NULL_TREE;
+}
+
/* Like get_qualified_type, but creates the type if it does not
exist. This function never returns NULL_TREE. */
diff --git a/gcc/tree.h b/gcc/tree.h
index 012fa54..92b53f6 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4196,6 +4196,11 @@ extern bool check_qualified_type (const_tree, const_tree, int);
extern tree get_qualified_type (tree, int);
+/* Return a version of TYPE with ATTRIBUTES, if one exists. If no suitable
+ type exists yet, return NULL_TREE. */
+
+extern tree get_attribute_qualified_type (tree, tree);
+
/* Like get_qualified_type, but creates the type if it does not
exist. This function never returns NULL_TREE. */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 8f21fd1..6a8c114 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -757,7 +757,7 @@ const struct attribute_spec c_common_attribute_table[] =
handle_nonnull_attribute, false },
{ "nothrow", 0, 0, true, false, false,
handle_nothrow_attribute, false },
- { "may_alias", 0, 0, false, true, false, NULL, false },
+ { "may_alias", 0, 0, false, true, false, NULL, true },
{ "cleanup", 1, 1, true, false, false,
handle_cleanup_attribute, false },
{ "warn_unused_result", 0, 0, false, true, true,
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 0e44409..ca01286 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -2296,6 +2296,8 @@ write_builtin_type (tree type)
{
if (TYPE_CANONICAL (type))
type = TYPE_CANONICAL (type);
+ else
+ type = TREE_TYPE (TYPE_NAME (TYPE_MAIN_VARIANT (type)));
switch (TREE_CODE (type))
{
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 3704b88..9e44c07 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1181,6 +1181,17 @@ comp_template_parms_position (tree t1, tree t2)
return true;
}
+/* Look through typedefs to find the name of the underlying type of T. */
+
+static tree
+underlying_type_name (tree t)
+{
+ tree d = TYPE_NAME (t);
+ while (d && is_typedef_decl (d))
+ d = TYPE_NAME (DECL_ORIGINAL_TYPE (d));
+ return d;
+}
+
/* Subroutine in comptypes. */
static bool
@@ -1249,7 +1260,7 @@ structural_comptypes (tree t1, tree t2, int strict)
case INTEGER_TYPE:
case FIXED_POINT_TYPE:
case REAL_TYPE:
- /* With these nodes, we can't determine type equivalence by
+ /* With these nodes, it isn't simple to determine type equivalence by
looking at what is stored in the nodes themselves, because
two nodes might have different TYPE_MAIN_VARIANTs but still
represent the same type. For example, wchar_t and int could
@@ -1261,9 +1272,21 @@ structural_comptypes (tree t1, tree t2, int strict)
typedef int INT __attribute((may_alias));
have identical properties, different TYPE_MAIN_VARIANTs, but
- represent the same type. The canonical type system keeps
- track of equivalence in this case, so we fall back on it. */
- return TYPE_CANONICAL (t1) == TYPE_CANONICAL (t2);
+ represent the same type. */
+ /* If the properties are different, they are different types. */
+ if (TYPE_UNSIGNED (t1) != TYPE_UNSIGNED (t2)
+ || TYPE_PRECISION (t1) != TYPE_PRECISION (t2))
+ return false;
+ {
+ /* If one of the types is based on a named built-in type and the other
+ one is not based on the same type, they are different types. */
+ tree n1 = underlying_type_name (t1);
+ tree n2 = underlying_type_name (t2);
+ if ((n1 || n2) && n1 != n2)
+ return false;
+ }
+ /* Otherwise, compare attributes. */
+ break;
case TEMPLATE_TEMPLATE_PARM:
case BOUND_TEMPLATE_TEMPLATE_PARM:
@@ -9010,6 +9033,24 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
if (VECTOR_TYPE_P (to))
is_opaque_pointer = vector_targets_convertible_p (to, from);
+ /* Allow conversion to add or remove may_alias. */
+ if (!OVERLOAD_TYPE_P (to) && !OVERLOAD_TYPE_P (from))
+ {
+ tree talias = lookup_attribute ("may_alias", TYPE_ATTRIBUTES (to));
+ tree falias = lookup_attribute ("may_alias", TYPE_ATTRIBUTES (from));
+ if (!talias != !falias)
+ {
+ tree *lacks, a;
+ if (talias)
+ lacks = &from, a = talias;
+ else
+ lacks = &to, a = falias;
+ tree lacka = tree_cons (TREE_PURPOSE (a), TREE_VALUE (a),
+ TYPE_ATTRIBUTES (*lacks));
+ *lacks = cp_build_type_attribute_variant (*lacks, lacka);
+ }
+ }
+
if (!TYPE_PTR_P (to) && !TYPE_PTRDATAMEM_P (to))
return ((constp >= 0 || to_more_cv_qualified)
&& (is_opaque_pointer
diff --git a/gcc/testsuite/g++.dg/ext/alias-mangle.C b/gcc/testsuite/g++.dg/ext/alias-mangle.C
index d804c1a..062b365 100644
--- a/gcc/testsuite/g++.dg/ext/alias-mangle.C
+++ b/gcc/testsuite/g++.dg/ext/alias-mangle.C
@@ -1,6 +1,6 @@
// PR c++/34936
// { dg-do compile }
-/* { dg-final { scan-assembler "_ZN1AIdEC1Ev" } } */
+/* { dg-final { scan-assembler "_ZN1AIU9may_aliasdEC1Ev" } } */
typedef double X __attribute((may_alias)) ;
template<typename> struct A
@@ -8,4 +8,4 @@ template<typename> struct A
A();
};
-A<X> a; // { dg-warning "ignoring attributes on template argument" }
+A<X> a;
diff --git a/gcc/testsuite/g++.dg/ext/attrib50.C b/gcc/testsuite/g++.dg/ext/attrib50.C
index 9ff9918..a46c9f7 100644
--- a/gcc/testsuite/g++.dg/ext/attrib50.C
+++ b/gcc/testsuite/g++.dg/ext/attrib50.C
@@ -8,4 +8,4 @@ struct A {
typedef int TA __attribute__((__may_alias__));
};
void d() { B<int &> b; }
-int main() { B<A::TA &> b; } // { dg-warning "attributes" }
+int main() { B<A::TA &> b; }
diff --git a/gcc/testsuite/g++.dg/warn/Wignored-attributes-1.C b/gcc/testsuite/g++.dg/warn/Wignored-attributes-1.C
index 46ccc4a..fb95f7e 100644
--- a/gcc/testsuite/g++.dg/warn/Wignored-attributes-1.C
+++ b/gcc/testsuite/g++.dg/warn/Wignored-attributes-1.C
@@ -3,4 +3,4 @@
typedef float __m128 __attribute__((__vector_size__(16), __may_alias__));
template <typename> struct A;
-template <> struct A<__m128>; // { dg-warning "ignoring attributes on template argument" }
+template <> struct A<__m128>;
diff --git a/gcc/tree.c b/gcc/tree.c
index bc60190..45e56bf 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -5082,6 +5082,8 @@ comp_type_attributes (const_tree type1, const_tree type2)
}
if (lookup_attribute ("transaction_safe", CONST_CAST_TREE (a)))
return 0;
+ if (lookup_attribute ("may_alias", CONST_CAST_TREE (a)))
+ return 0;
/* As some type combinations - like default calling-convention - might
be compatible, we have to call the target hook to get the final result. */
return targetm.comp_type_attributes (type1, type2);