Adjust mangling of ABI tags on class template member functions.
authorJason Merrill <jason@redhat.com>
Tue, 9 Aug 2016 16:55:01 +0000 (12:55 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 9 Aug 2016 16:55:01 +0000 (12:55 -0400)
* class.c (missing_abi_tags): New.
(check_abi_tags): Don't check template. Add just_checking mode.
* mangle.c (abi_flag_at_least, any_abi_below, equal_abi_tags): New.
(sorted_abi_tags): Split out from write_abi_tags.
(struct releasing_vec): New.
(write_unqualified_name): Only look for the primary
template for types.  Implement backward compatibility.

From-SVN: r239298

gcc/cp/ChangeLog
gcc/cp/class.c
gcc/cp/cp-tree.h
gcc/cp/mangle.c
gcc/testsuite/g++.dg/abi/abi-tag21.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/abi-tag21a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/abi-tag21b.C [new file with mode: 0644]

index ffb0b85e0038a987c068e4a513f8c25dc3077669..81ca1a1b04f473c5721d9b2a44b02a9471e8a1ee 100644 (file)
@@ -1,5 +1,14 @@
 2016-08-09  Jason Merrill  <jason@redhat.com>
 
+       Adjust mangling of ABI tags on class template member functions.
+       * class.c (missing_abi_tags): New.
+       (check_abi_tags): Don't check template. Add just_checking mode.
+       * mangle.c (abi_flag_at_least, any_abi_below, equal_abi_tags): New.
+       (sorted_abi_tags): Split out from write_abi_tags.
+       (struct releasing_vec): New.
+       (write_unqualified_name): Only look for the primary
+       template for types.  Implement backward compatibility.
+
        PR c++/72849
        * constexpr.c (cxx_eval_constant_expression): Check
        COMPLETE_TYPE_P before calling is_really_empty_class.
index 8249d9342299a2eb8fcb602924b130325b49073b..e21647a642eff4b23c4208fc4560553274eaab17 100644 (file)
@@ -1561,20 +1561,20 @@ mark_abi_tags (tree t, bool val)
 
 /* Check that T has all the ABI tags that subobject SUBOB has, or
    warn if not.  If T is a (variable or function) declaration, also
-   add any missing tags.  */
+   return any missing tags, and add them to T if JUST_CHECKING is false.  */
 
-static void
-check_abi_tags (tree t, tree subob)
+static tree
+check_abi_tags (tree t, tree subob, bool just_checking = false)
 {
   bool inherit = DECL_P (t);
 
   if (!inherit && !warn_abi_tag)
-    return;
+    return NULL_TREE;
 
   tree decl = TYPE_P (t) ? TYPE_NAME (t) : t;
   if (!TREE_PUBLIC (decl))
     /* No need to worry about things local to this TU.  */
-    return;
+    return NULL_TREE;
 
   mark_abi_tags (t, true);
 
@@ -1585,7 +1585,15 @@ check_abi_tags (tree t, tree subob)
 
   cp_walk_tree_without_duplicates (&subtype, find_abi_tags_r, &data);
 
-  if (inherit && data.tags)
+  if (!(inherit && data.tags))
+    /* We don't need to do anything with data.tags.  */;
+  else if (just_checking)
+    for (tree t = data.tags; t; t = TREE_CHAIN (t))
+      {
+       tree id = get_identifier (TREE_STRING_POINTER (TREE_VALUE (t)));
+       IDENTIFIER_MARKED (id) = false;
+      }
+  else
     {
       tree attr = lookup_attribute ("abi_tag", DECL_ATTRIBUTES (t));
       if (attr)
@@ -1597,6 +1605,8 @@ check_abi_tags (tree t, tree subob)
     }
 
   mark_abi_tags (t, false);
+
+  return data.tags;
 }
 
 /* Check that DECL has all the ABI tags that are used in parts of its type
@@ -1605,15 +1615,6 @@ check_abi_tags (tree t, tree subob)
 void
 check_abi_tags (tree decl)
 {
-  tree t;
-  if (abi_version_at_least (10)
-      && DECL_LANG_SPECIFIC (decl)
-      && DECL_USE_TEMPLATE (decl)
-      && (t = DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (decl)),
-         t != decl))
-    /* Make sure that our template has the appropriate tags, since
-       write_unqualified_name looks for them there.  */
-    check_abi_tags (t);
   if (VAR_P (decl))
     check_abi_tags (decl, TREE_TYPE (decl));
   else if (TREE_CODE (decl) == FUNCTION_DECL
@@ -1621,6 +1622,22 @@ check_abi_tags (tree decl)
     check_abi_tags (decl, TREE_TYPE (TREE_TYPE (decl)));
 }
 
+/* Return any ABI tags that are used in parts of the type of DECL
+   that are not reflected in its mangled name.  This function is only
+   used in backward-compatible mangling for ABI <11.  */
+
+tree
+missing_abi_tags (tree decl)
+{
+  if (VAR_P (decl))
+    return check_abi_tags (decl, TREE_TYPE (decl), true);
+  else if (TREE_CODE (decl) == FUNCTION_DECL
+          && !mangle_return_type_p (decl))
+    return check_abi_tags (decl, TREE_TYPE (TREE_TYPE (decl)), true);
+  else
+    return NULL_TREE;
+}
+
 void
 inherit_targ_abi_tags (tree t)
 {
index 70a42f87103abc814194c9febef1aaaa2cad0dfd..f32613c05b700822215c6935b50b95bab8258766 100644 (file)
@@ -5686,6 +5686,7 @@ extern void inherit_targ_abi_tags         (tree);
 extern void defaulted_late_check               (tree);
 extern bool defaultable_fn_check               (tree);
 extern void check_abi_tags                     (tree);
+extern tree missing_abi_tags                   (tree);
 extern void fixup_type_variants                        (tree);
 extern void fixup_attribute_variants           (tree);
 extern tree* decl_cloned_function_p            (const_tree, bool);
index b6c96283f4fee5ec62d58b100cfefcc7d7c12427..f7ff221d9416d3faf0369e5fded1d6bf4c3f1bf5 100644 (file)
@@ -91,6 +91,14 @@ along with GCC; see the file COPYING3.  If not see
 #define abi_warn_or_compat_version_crosses(N) \
   (abi_version_crosses (N) || abi_compat_version_crosses (N))
 
+/* And sometimes we can simplify the code path if we don't need to worry about
+   previous ABIs.  */
+#define abi_flag_at_least(flag,N) (flag == 0 || flag >= N)
+#define any_abi_below(N) \
+  (!abi_version_at_least (N) \
+   || !abi_flag_at_least (warn_abi_version, (N)) \
+   || !abi_flag_at_least (flag_abi_compat_version, (N)))
+
 /* Things we only need one of.  This module is not reentrant.  */
 struct GTY(()) globals {
   /* An array of the current substitution candidates, in the order
@@ -224,6 +232,7 @@ static void dump_substitution_candidates (void);
 static tree mangle_decl_string (const tree);
 static int local_class_index (tree);
 static void maybe_check_abi_tags (tree, tree = NULL_TREE);
+static bool equal_abi_tags (tree, tree);
 
 /* Control functions.  */
 
@@ -1329,16 +1338,52 @@ write_unqualified_name (tree decl)
         write_source_name (DECL_NAME (decl));
     }
 
-  /* We use the ABI tags from the primary template, ignoring tags on any
+  /* We use the ABI tags from the primary class template, ignoring tags on any
      specializations.  This is necessary because C++ doesn't require a
-     specialization to be declared before it is used unless the use
-     requires a complete type, but we need to get the tags right on
-     incomplete types as well.  */
+     specialization to be declared before it is used unless the use requires a
+     complete type, but we need to get the tags right on incomplete types as
+     well.  */
   if (tree tmpl = most_general_template (decl))
-    decl = DECL_TEMPLATE_RESULT (tmpl);
-  /* Don't crash on an unbound class template.  */
-  if (decl && TREE_CODE (decl) != NAMESPACE_DECL)
-    write_abi_tags (get_abi_tags (decl));
+    {
+      tree res = DECL_TEMPLATE_RESULT (tmpl);
+      if (res == NULL_TREE)
+       /* UNBOUND_CLASS_TEMPLATE.  */;
+      else if (DECL_DECLARES_TYPE_P (decl))
+       decl = res;
+      else if (any_abi_below (11))
+       {
+         /* ABI v10 implicit tags on the template.  */
+         tree mtags = missing_abi_tags (res);
+         /* Explicit tags on the template.  */
+         tree ttags = get_abi_tags (res);
+         /* Tags on the instantiation.  */
+         tree dtags = get_abi_tags (decl);
+
+         if (mtags && abi_warn_or_compat_version_crosses (10))
+           G.need_abi_warning = 1;
+
+         /* Add the v10 tags to the explicit tags now.  */
+         mtags = chainon (mtags, ttags);
+
+         if (!G.need_abi_warning
+             && abi_warn_or_compat_version_crosses (11)
+             && !equal_abi_tags (dtags, mtags))
+           G.need_abi_warning = 1;
+
+         if (!abi_version_at_least (10))
+           /* In abi <10, we only got the explicit tags.  */
+           decl = res;
+         else if (flag_abi_version == 10)
+           {
+             /* In ABI 10, we want explict and implicit tags.  */
+             write_abi_tags (mtags);
+             return;
+           }
+       }
+    }
+
+  tree tags = get_abi_tags (decl);
+  write_abi_tags (tags);
 }
 
 /* Write the unqualified-name for a conversion operator to TYPE.  */
@@ -1381,15 +1426,11 @@ tree_string_cmp (const void *p1, const void *p2)
                 TREE_STRING_POINTER (s2));
 }
 
-/* ID is the name of a function or type with abi_tags attribute TAGS.
-   Write out the name, suitably decorated.  */
+/* Return the TREE_LIST of TAGS as a sorted VEC.  */
 
-static void
-write_abi_tags (tree tags)
+static vec<tree, va_gc> *
+sorted_abi_tags (tree tags)
 {
-  if (tags == NULL_TREE)
-    return;
-
   vec<tree, va_gc> * vec = make_tree_vector();
 
   for (tree t = tags; t; t = TREE_CHAIN (t))
@@ -1402,6 +1443,20 @@ write_abi_tags (tree tags)
 
   vec->qsort (tree_string_cmp);
 
+  return vec;
+}
+
+/* ID is the name of a function or type with abi_tags attribute TAGS.
+   Write out the name, suitably decorated.  */
+
+static void
+write_abi_tags (tree tags)
+{
+  if (tags == NULL_TREE)
+    return;
+
+  vec<tree, va_gc> * vec = sorted_abi_tags (tags);
+
   unsigned i; tree str;
   FOR_EACH_VEC_ELT (*vec, i, str)
     {
@@ -1413,6 +1468,43 @@ write_abi_tags (tree tags)
   release_tree_vector (vec);
 }
 
+/* Simplified unique_ptr clone to release a tree vec on exit.  */
+
+struct releasing_vec
+{
+  typedef vec<tree, va_gc> vec_t;
+
+  releasing_vec (vec_t *v): v(v) { }
+  releasing_vec (): v(make_tree_vector ()) { }
+
+  vec_t &operator* () const { return *v; }
+  vec_t *operator-> () const { return v; }
+  vec_t *get () const { return v; }
+  operator vec_t *() const { return v; }
+  tree& operator[] (unsigned i) const { return (*v)[i]; }
+
+  ~releasing_vec() { release_tree_vector (v); }
+private:
+  vec_t *v;
+};
+
+/* True iff the TREE_LISTS T1 and T2 of ABI tags are equivalent.  */
+
+static bool
+equal_abi_tags (tree t1, tree t2)
+{
+  releasing_vec v1 = sorted_abi_tags (t1);
+  releasing_vec v2 = sorted_abi_tags (t2);
+
+  unsigned len1 = v1->length();
+  if (len1 != v2->length())
+    return false;
+  for (unsigned i = 0; i < len1; ++i)
+    if (tree_string_cmp (v1[i], v2[i]) != 0)
+      return false;
+  return true;
+}
+
 /* Write a user-defined literal operator.
           ::= li <source-name>    # "" <source-name>
    IDENTIFIER is an LITERAL_IDENTIFIER_NODE.  */
diff --git a/gcc/testsuite/g++.dg/abi/abi-tag21.C b/gcc/testsuite/g++.dg/abi/abi-tag21.C
new file mode 100644 (file)
index 0000000..53599f7
--- /dev/null
@@ -0,0 +1,27 @@
+// { dg-do compile { target c++11 } }
+// { dg-options -Wabi=10 }
+
+struct [[gnu::abi_tag ("foo")]] A
+{
+  template <class T> static T f();
+  template <class T> static A g();
+};
+
+template <class T> struct B
+{
+  static decltype(A::f<T>()) fa(decltype(A::f<T>()));
+  static decltype(A::f<T>()) fv(); // { dg-warning "mangled name" }
+  static decltype(A::g<T>()) ga(decltype(A::g<T>()));
+  static decltype(A::g<T>()) gv();
+  template <class U> 
+  static decltype(A::f<U>()) hv();
+};
+
+int main()
+{
+  B<int>::fa(0);     // { dg-final { scan-assembler "_ZN1BIiE2faEi" } }
+  B<int>::fv();             // { dg-final { scan-assembler "_ZN1BIiE2fvEv" } }
+  B<int>::ga(A());   // { dg-final { scan-assembler "_ZN1BIiE2gaE1AB3foo" } }
+  B<int>::gv();             // { dg-final { scan-assembler "_ZN1BIiE2gvB3fooEv" } }
+  B<int>::hv<int>(); // { dg-final { scan-assembler "_ZN1BIiE2hvIiEEDTclsr1AB3foo1fIT_EEEv" } }
+}
diff --git a/gcc/testsuite/g++.dg/abi/abi-tag21a.C b/gcc/testsuite/g++.dg/abi/abi-tag21a.C
new file mode 100644 (file)
index 0000000..3c51333
--- /dev/null
@@ -0,0 +1,27 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-fabi-version=10 -Wabi" }
+
+struct [[gnu::abi_tag ("foo")]] A
+{
+  template <class T> static T f();
+  template <class T> static A g();
+};
+
+template <class T> struct B
+{
+  static decltype(A::f<T>()) fa(decltype(A::f<T>()));
+  static decltype(A::f<T>()) fv(); // { dg-warning "mangled name" }
+  static decltype(A::g<T>()) ga(decltype(A::g<T>()));
+  static decltype(A::g<T>()) gv();
+  template <class U> 
+  static decltype(A::f<U>()) hv();
+};
+
+int main()
+{
+  B<int>::fa(0);     // { dg-final { scan-assembler "_ZN1BIiE2faEi" } }
+  B<int>::fv();             // { dg-final { scan-assembler "_ZN1BIiE2fvB3fooEv" } }
+  B<int>::ga(A());   // { dg-final { scan-assembler "_ZN1BIiE2gaE1AB3foo" } }
+  B<int>::gv();             // { dg-final { scan-assembler "_ZN1BIiE2gvB3fooEv" } }
+  B<int>::hv<int>(); // { dg-final { scan-assembler "_ZN1BIiE2hvIiEEDTclsr1AB3foo1fIT_EEEv" } }
+}
diff --git a/gcc/testsuite/g++.dg/abi/abi-tag21b.C b/gcc/testsuite/g++.dg/abi/abi-tag21b.C
new file mode 100644 (file)
index 0000000..d4090f4
--- /dev/null
@@ -0,0 +1,27 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-fabi-version=9 -Wabi" }
+
+struct [[gnu::abi_tag ("foo")]] A
+{
+  template <class T> static T f();
+  template <class T> static A g();
+};
+
+template <class T> struct B
+{
+  static decltype(A::f<T>()) fa(decltype(A::f<T>()));
+  static decltype(A::f<T>()) fv();
+  static decltype(A::g<T>()) ga(decltype(A::g<T>()));
+  static decltype(A::g<T>()) gv(); // { dg-warning "mangled name" }
+  template <class U> 
+  static decltype(A::f<U>()) hv();
+};
+
+int main()
+{
+  B<int>::fa(0);     // { dg-final { scan-assembler "_ZN1BIiE2faEi" } }
+  B<int>::fv();             // { dg-final { scan-assembler "_ZN1BIiE2fvEv" } }
+  B<int>::ga(A());   // { dg-final { scan-assembler "_ZN1BIiE2gaE1AB3foo" } }
+  B<int>::gv();             // { dg-final { scan-assembler "_ZN1BIiE2gvEv" } }
+  B<int>::hv<int>(); // { dg-final { scan-assembler "_ZN1BIiE2hvIiEEDTclsr1AB3foo1fIT_EEEv" } }
+}