https://gcc.gnu.org/g:ee0671c1bd40f8009705aaab06858fd84f471d06

commit r16-8876-gee0671c1bd40f8009705aaab06858fd84f471d06
Author: Patrick Palka <[email protected]>
Date:   Tue May 5 11:26:16 2026 -0400

    c++/modules: false positive abi_tag mismatch [PR124957]
    
    Here x in _a.C is defined in terms of A, which has an abi_tag, so it
    should inherit A's abi_tag.  It turns out this abi_tag propagation
    happens only during mangling via class.cc:check_abi_tags, and we happen
    to never mangle x in this TU, so we stream out x with no abi_tag.
    
    In _b.C we import and re-export x, and we also happen to mangle x (for
    arbitrary reasons that I didn't question), which means when we stream it
    out this time it has an abi_tag.
    
    In _c.C we import both versions of x, merge them, during which we compare
    their abi_tag, notice a mismatch, and diagnose.  But the mismatch is
    solely due to one version ('existing') being mangled, and therefore went
    through class.cc:check_abi_tags, and the other ('decl') not.
    
    Idiosyncracies of this testcase aside (like why x gets mangled in _b.C,
    why we stream in two apparent x's in _c.C), it does seem like this
    diagnostic routine should be robust to this situation.  To that end this
    patch makes the routine ignore such inherited tags during the comparison
    if there's a mangled-ness mismatch, via a new flag ABI_TAG_INHERITED
    that's set on all inherited tags.  In passing, rename the existing flag
    ABI_TAG_IMPLICIT to ABI_TAG_NOT_MANGLED to better describe and
    differentiate it from the new flag.
    
            PR c++/124957
    
    gcc/cp/ChangeLog:
    
            * class.cc (check_tag): Set ABI_TAG_INHERITED on the TREE_LIST
            of an inherited tag.  Adjust after ABI_TAG_IMPLICIT renaming.
            * cp-tree.h (ABI_TAG_IMPLICIT): Rename to ...
            (ABI_TAG_NOT_MANGLED): ... this.
            (equal_abi_tags): Adjust forward declaration.
            * mangle.cc (write_unqualified_name): Adjust equal_abi_tags call.
            (sorted_abi_tags): New ignore_inherited_p parameter, for ignoring
            ABI_TAG_INHERITED tags.  Adjust after ABI_TAG_INHERITED renaming.
            (write_abi_tags): Adjust sorted_abi_tags call.
            (equal_abi_tags): New ignore_inherited_p parameter.  Pass it to
            sorted_abi_tags.
            * module.cc (trees_in::check_abi_tags): Pass
            ignore_inherited_p=true to equal_abi_tags iff there's a
            mangled-ness mismatch.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/modules/attrib-6_a.C: New test.
            * g++.dg/modules/attrib-6_b.C: New test.
            * g++.dg/modules/attrib-6_c.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>
    (cherry picked from commit 6112538b060d8a973500a3ae5e930545b422600f)

Diff:
---
 gcc/cp/class.cc                           |  3 ++-
 gcc/cp/cp-tree.h                          | 15 +++++++++++----
 gcc/cp/mangle.cc                          | 15 ++++++++-------
 gcc/cp/module.cc                          |  9 +++++++--
 gcc/testsuite/g++.dg/modules/attrib-6_a.C | 10 ++++++++++
 gcc/testsuite/g++.dg/modules/attrib-6_b.C |  8 ++++++++
 gcc/testsuite/g++.dg/modules/attrib-6_c.C |  8 ++++++++
 7 files changed, 54 insertions(+), 14 deletions(-)

diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 8beeb797a08b..020cbd6ae2ca 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -1711,11 +1711,12 @@ check_tag (tree tag, tree id, tree *tp, abi_tag_data *p)
          /* Don't inherit this tag multiple times.  */
          IDENTIFIER_MARKED (id) = true;
 
+         ABI_TAG_INHERITED (p->tags) = true;
          if (TYPE_P (p->t))
            {
              /* Tags inherited from type template arguments are only used
                 to avoid warnings.  */
-             ABI_TAG_IMPLICIT (p->tags) = true;
+             ABI_TAG_NOT_MANGLED (p->tags) = true;
              return;
            }
          /* For functions and variables we want to warn, too.  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ff8fcca72fed..fc5bb5638be1 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -429,7 +429,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       TARGET_EXPR_IMPLICIT_P (in TARGET_EXPR)
       TEMPLATE_PARM_PARAMETER_PACK (in TEMPLATE_PARM_INDEX)
       ATTR_IS_DEPENDENT (in the TREE_LIST for an attribute)
-      ABI_TAG_IMPLICIT (in the TREE_LIST for the argument of abi_tag)
+      ABI_TAG_NOT_MANGLED (in the TREE_LIST for the argument of abi_tag)
       LAMBDA_CAPTURE_EXPLICIT_P (in a TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST)
       PARENTHESIZED_LIST_P (in the TREE_LIST for a parameter-declaration-list)
       CONSTRUCTOR_IS_DIRECT_INIT (in CONSTRUCTOR)
@@ -480,6 +480,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (in BASELINK)
       BIND_EXPR_VEC_DTOR (in BIND_EXPR)
       ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (in ATOMIC_CONSTR)
+      ABI_TAG_INHERITED (in the TREE_LIST for the argument of abi_tag)
       STATIC_INIT_DECOMP_BASE_P (in the TREE_LIST for {static,tls}_aggregates)
       MUST_NOT_THROW_THROW_P (in MUST_NOT_THROW_EXPR)
       LAMBDA_EXPR_CONST_QUAL_P (in LAMBDA_EXPR)
@@ -3962,8 +3963,14 @@ struct GTY(()) lang_decl {
 #define ATTR_IS_DEPENDENT(NODE) TREE_LANG_FLAG_0 (TREE_LIST_CHECK (NODE))
 
 /* In a TREE_LIST in the argument of attribute abi_tag, indicates that the tag
-   was inherited from a template parameter, not explicitly indicated.  */
-#define ABI_TAG_IMPLICIT(NODE) TREE_LANG_FLAG_0 (TREE_LIST_CHECK (NODE))
+   was inherited from a template parameter, not explicitly indicated.
+   These are not mangled because they're already represented in the mangling
+   of the template argument.  */
+#define ABI_TAG_NOT_MANGLED(NODE) TREE_LANG_FLAG_0 (TREE_LIST_CHECK (NODE))
+
+/* In a TREE_LIST in the argument of attribute abi_tag, indicates that the tag
+ * was added by check_abi_tags, not explicitly specified.  */
+#define ABI_TAG_INHERITED(NODE) TREE_LANG_FLAG_1 (TREE_LIST_CHECK (NODE))
 
 /* In a TREE_LIST for a parameter-declaration-list, indicates that all the
    parameters in the list have declarators enclosed in ().  */
@@ -9114,7 +9121,7 @@ extern void mangle_module_substitution            (int);
 extern int mangle_module_component             (tree id, bool partition);
 extern tree mangle_module_global_init          (int);
 extern unsigned HOST_WIDE_INT range_expr_nelts (tree);
-extern bool equal_abi_tags                     (tree, tree);
+extern bool equal_abi_tags                     (tree, tree, bool);
 
 /* in dump.cc */
 extern bool cp_dump_tree                       (void *, tree);
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 6e6b16d389d0..2605f8505cdc 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -1645,7 +1645,7 @@ write_unqualified_name (tree decl)
 
          if (!G.need_abi_warning
              && abi_warn_or_compat_version_crosses (11)
-             && !equal_abi_tags (dtags, mtags))
+             && !equal_abi_tags (dtags, mtags, /*ignore_inherited_p=*/false))
            G.need_abi_warning = 1;
 
          if (!abi_version_at_least (10))
@@ -1709,13 +1709,14 @@ tree_string_cmp (const void *p1, const void *p2)
 /* Return the TREE_LIST of TAGS as a sorted VEC.  */
 
 static vec<tree, va_gc> *
-sorted_abi_tags (tree tags)
+sorted_abi_tags (tree tags, bool ignore_inherited_p)
 {
   vec<tree, va_gc> * vec = make_tree_vector();
 
   for (tree t = tags; t; t = TREE_CHAIN (t))
     {
-      if (ABI_TAG_IMPLICIT (t))
+      if (ABI_TAG_NOT_MANGLED (t)
+         || (ignore_inherited_p && ABI_TAG_INHERITED (t)))
        continue;
       tree str = TREE_VALUE (t);
       vec_safe_push (vec, str);
@@ -1735,7 +1736,7 @@ write_abi_tags (tree tags)
   if (tags == NULL_TREE)
     return;
 
-  vec<tree, va_gc> * vec = sorted_abi_tags (tags);
+  vec<tree, va_gc> * vec = sorted_abi_tags (tags, 
/*ignore_inherited_p=*/false);
 
   unsigned i; tree str;
   FOR_EACH_VEC_ELT (*vec, i, str)
@@ -1751,10 +1752,10 @@ write_abi_tags (tree tags)
 /* True iff the TREE_LISTS T1 and T2 of ABI tags are equivalent.  */
 
 bool
-equal_abi_tags (tree t1, tree t2)
+equal_abi_tags (tree t1, tree t2, bool ignore_inherited_p)
 {
-  releasing_vec v1 = sorted_abi_tags (t1);
-  releasing_vec v2 = sorted_abi_tags (t2);
+  releasing_vec v1 = sorted_abi_tags (t1, ignore_inherited_p);
+  releasing_vec v2 = sorted_abi_tags (t2, ignore_inherited_p);
 
   unsigned len1 = v1->length();
   if (len1 != v2->length())
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 622e4f7398d8..f4f0e6f6b969 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -12542,8 +12542,13 @@ trees_in::check_abi_tags (tree existing, tree decl, 
tree &eattr, tree &dattr)
       if (dtags)
        dtags = TREE_VALUE (dtags);
 
-      /* We only error if mangling wouldn't consider the tags equivalent.  */
-      if (!equal_abi_tags (etags, dtags))
+      /* We only error if mangling wouldn't consider the tags equivalent.
+        Since tags might have been inherited during mangling, ignore
+        inherited tags if there's a mangled-ness mismatch.  */
+      bool ignore_inherited_p
+       = (DECL_ASSEMBLER_NAME_SET_P (STRIP_TEMPLATE (existing))
+          != DECL_ASSEMBLER_NAME_SET_P (STRIP_TEMPLATE (decl)));
+      if (!equal_abi_tags (etags, dtags, ignore_inherited_p))
        {
          auto_diagnostic_group d;
          if (dtags)
diff --git a/gcc/testsuite/g++.dg/modules/attrib-6_a.C 
b/gcc/testsuite/g++.dg/modules/attrib-6_a.C
new file mode 100644
index 000000000000..ea8508c25f39
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/attrib-6_a.C
@@ -0,0 +1,10 @@
+// PR c++/124957
+// { dg-do compile { target c++17 } }
+// { dg-additional-options "-fmodules" }
+
+export module mod:partition;
+
+int dummy;
+struct [[gnu::abi_tag("cxx11")]] A { int m; };
+export inline int A::*x = &A::m;
+// x does not get mangled in this TU => cxx11 abi_tag not propagated to x
diff --git a/gcc/testsuite/g++.dg/modules/attrib-6_b.C 
b/gcc/testsuite/g++.dg/modules/attrib-6_b.C
new file mode 100644
index 000000000000..3015aa6f8b76
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/attrib-6_b.C
@@ -0,0 +1,8 @@
+// PR c++/124957
+// { dg-do compile { target c++17 } }
+// { dg-additional-options "-fmodules" }
+
+export module mod; // { dg-module-cmi "mod" }
+export import :partition;
+
+// x gets mangled in this TU => cxx11 abi_tag propagated to x
diff --git a/gcc/testsuite/g++.dg/modules/attrib-6_c.C 
b/gcc/testsuite/g++.dg/modules/attrib-6_c.C
new file mode 100644
index 000000000000..c0c55a2166c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/attrib-6_c.C
@@ -0,0 +1,8 @@
+// PR c++/124957
+// { dg-do compile { target c++17 } }
+// { dg-additional-options "-fmodules -fno-module-lazy" }
+
+module mod;
+import :partition;
+
+// OK, no bogus abi_tag mismatch error for the imported x's

Reply via email to