static void dump_substitution_candidates (void);
static tree mangle_decl_string (const tree);
static void maybe_check_abi_tags (tree, tree = NULL_TREE, int = 10);
-static bool equal_abi_tags (tree, tree);
/* Control functions. */
/* Compare two TREE_STRINGs like strcmp. */
-int
+static int
tree_string_cmp (const void *p1, const void *p2)
{
if (p1 == p2)
/* True iff the TREE_LISTS T1 and T2 of ABI tags are equivalent. */
-static bool
+bool
equal_abi_tags (tree t1, tree t2)
{
releasing_vec v1 = sorted_abi_tags (t1);
if (len1 != v2->length())
return false;
for (unsigned i = 0; i < len1; ++i)
- if (tree_string_cmp (v1[i], v2[i]) != 0)
+ if (strcmp (TREE_STRING_POINTER (v1[i]),
+ TREE_STRING_POINTER (v2[i])) != 0)
return false;
return true;
}
bool read_definition (tree decl);
private:
+ void check_abi_tags (tree existing, tree decl, tree &eattr, tree &dattr);
bool is_matching_decl (tree existing, tree decl, bool is_typedef);
static bool install_implicit_member (tree decl);
bool read_function_def (tree decl, tree maybe_template);
tree etype = TREE_TYPE (existing);
/* Handle separate declarations with different attributes. */
+ tree &dattr = TYPE_ATTRIBUTES (type);
tree &eattr = TYPE_ATTRIBUTES (etype);
- eattr = merge_attributes (eattr, TYPE_ATTRIBUTES (type));
+ check_abi_tags (existing, decl, eattr, dattr);
+ // TODO: handle other conflicting type attributes
+ eattr = merge_attributes (eattr, dattr);
/* When merging a partial specialisation, the existing decl may have
had its TYPE_CANONICAL adjusted. If so we should use structural
return u ();
}
+/* DECL is a just streamed declaration with attributes DATTR that should
+ have matching ABI tags as EXISTING's attributes EATTR. Check that the
+ ABI tags match, and report an error if not. */
+
+void
+trees_in::check_abi_tags (tree existing, tree decl, tree &eattr, tree &dattr)
+{
+ tree etags = lookup_attribute ("abi_tag", eattr);
+ tree dtags = lookup_attribute ("abi_tag", dattr);
+ if ((etags == nullptr) != (dtags == nullptr)
+ || (etags && !attribute_value_equal (etags, dtags)))
+ {
+ if (etags)
+ etags = TREE_VALUE (etags);
+ if (dtags)
+ dtags = TREE_VALUE (dtags);
+
+ /* We only error if mangling wouldn't consider the tags equivalent. */
+ if (!equal_abi_tags (etags, dtags))
+ {
+ auto_diagnostic_group d;
+ if (dtags)
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "mismatching abi tags for %qD with tags %qE",
+ decl, dtags);
+ else
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "mismatching abi tags for %qD with no tags", decl);
+ if (etags)
+ inform (DECL_SOURCE_LOCATION (existing),
+ "existing declaration here with tags %qE", etags);
+ else
+ inform (DECL_SOURCE_LOCATION (existing),
+ "existing declaration here with no tags");
+ }
+
+ /* Always use the existing abi_tags as the canonical set so that
+ later processing doesn't get confused. */
+ if (dtags)
+ dattr = remove_attribute ("abi_tag", dattr);
+ if (etags)
+ duplicate_one_attribute (&dattr, eattr, "abi_tag");
+ }
+}
+
/* DECL is a just streamed mergeable decl that should match EXISTING. Check
it does and issue an appropriate diagnostic if not. Merge any
bits from DECL to EXISTING. This is stricter matching than
if (!DECL_EXTERNAL (d_inner))
DECL_EXTERNAL (e_inner) = false;
+ if (VAR_OR_FUNCTION_DECL_P (d_inner))
+ check_abi_tags (existing, decl,
+ DECL_ATTRIBUTES (e_inner), DECL_ATTRIBUTES (d_inner));
+
if (TREE_CODE (decl) == TEMPLATE_DECL)
{
/* Merge default template arguments. */
--- /dev/null
+// PR c++/118920
+// { dg-additional-options "-fmodules -fno-module-lazy" }
+
+struct A {}; // { dg-message "existing declaration here" }
+struct [[gnu::abi_tag("b", "a")]] B; // OK
+struct [[gnu::abi_tag("x")]] C; // { dg-message "existing declaration here" }
+struct [[gnu::abi_tag("d")]] D; // { dg-message "existing declaration here" }
+
+void f(); // { dg-message "existing declaration here" }
+[[gnu::abi_tag("g")]] void g(); // { dg-message "existing declaration here" }
+[[gnu::abi_tag("y", "x")]] void h(); // OK
+
+[[gnu::abi_tag("more", "test")]] extern int i; // { dg-message "existing declaration here" }
+[[gnu::abi_tag("more", "test", "really", "some")]] extern int j; // { dg-message "existing declaration here" }
+
+import "attrib-3_a.H";
+
+struct [[gnu::abi_tag("e")]] E; // { dg-error "adds abi tag" }
+
+// { dg-error "mismatching abi tags for .struct A." "" { target *-*-* } 0 }
+// B is OK
+// { dg-error "mismatching abi tags for .struct C." "" { target *-*-* } 0 }
+// { dg-error "mismatching abi tags for .struct D." "" { target *-*-* } 0 }
+
+// { dg-error "mismatching abi tags for .void f()." "" { target *-*-* } 0 }
+// { dg-error "mismatching abi tags for .void g()." "" { target *-*-* } 0 }
+// h is OK
+
+// { dg-error "mismatching abi tags for .i." "" { target *-*-* } 0 }
+// { dg-error "mismatching abi tags for .j." "" { target *-*-* } 0 }