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
