On 4/15/25 6:31 AM, Alfie Richards wrote:
This change refactors FMV handling in the frontend to allows greater
reasoning about versions in shared code.

Looking at the cp/ changes:

diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index c28d9e5b3ab..4f195ae06cd 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
  #include "gimplify.h"
  #include "intl.h"
  #include "asan.h"
+#include "attribs.h"

This seems unneeded since distinct_ is declared in tree.h.

  /* Id for dumping the class hierarchy.  */
  int class_dump_id;
@@ -8999,8 +9000,7 @@ resolve_address_of_overloaded_function (tree target_type,
         decls_match will return false as they are different.  */
        for (match = TREE_CHAIN (matches); match; match = TREE_CHAIN (match))
        if (!decls_match (fn, TREE_PURPOSE (match))
-           && !targetm.target_option.function_versions
-              (fn, TREE_PURPOSE (match)))
+           && !distinct_version_decls (fn, TREE_PURPOSE (match)))
            break;
if (match)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 898054c2891..ccd9547c2c8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7121,7 +7121,7 @@ extern void note_iteration_stmt_body_end  (bool);
  extern void determine_local_discriminator     (tree, tree = NULL_TREE);
  extern bool member_like_constrained_friend_p  (tree);
  extern bool fns_correspond                    (tree, tree);
-extern int decls_match                         (tree, tree, bool = true);
+extern int decls_match                         (tree, tree, bool = true, 
string_slice* = NULL);

I'm a bit uncomfortable with adding yet another parameter to decls_match for versioning. Do we really need to pass back the version for incorporating in the diagnostics? Isn't it enough to print the two declarations? If not, you could call a function to give more information about the version mismatch without needing to store it in duplicate_decls.

  extern bool maybe_version_functions           (tree, tree);
  extern bool validate_constexpr_redeclaration  (tree, tree);
  extern bool merge_default_template_args               (tree, tree, bool);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index ec4b1755741..4a374fa29e3 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -1120,7 +1120,10 @@ fns_correspond (tree newdecl, tree olddecl)
     `const int&'.  */
int
-decls_match (tree newdecl, tree olddecl, bool record_versions /* = true */)
+decls_match (tree newdecl,
+            tree olddecl,
+            bool record_versions /* = true */,
+            string_slice *conflicting_version)
  {
    int types_match;
@@ -1213,7 +1216,7 @@ decls_match (tree newdecl, tree olddecl, bool record_versions /* = true */)
        if (types_match
          && !DECL_EXTERN_C_P (newdecl)
          && !DECL_EXTERN_C_P (olddecl)
-         && targetm.target_option.function_versions (newdecl, olddecl))
+         && distinct_version_decls (newdecl, olddecl, conflicting_version))
        {
          if (record_versions)
            maybe_version_functions (newdecl, olddecl);
@@ -1296,7 +1299,7 @@ maybe_mark_function_versioned (tree decl)
  bool
  maybe_version_functions (tree newdecl, tree olddecl)
  {
-  if (!targetm.target_option.function_versions (newdecl, olddecl))
+  if (!distinct_version_decls (newdecl, olddecl))
      return false;
maybe_mark_function_versioned (olddecl);
@@ -1686,11 +1689,12 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
    tree new_template_info;
    location_t olddecl_loc = DECL_SOURCE_LOCATION (olddecl);
    location_t newdecl_loc = DECL_SOURCE_LOCATION (newdecl);
+  string_slice conflicting_version = string_slice::invalid ();
if (newdecl == olddecl)
      return olddecl;
- types_match = decls_match (newdecl, olddecl);
+  types_match = decls_match (newdecl, olddecl, true, &conflicting_version);
/* If either the type of the new decl or the type of the old decl is an
       error_mark_node, then that implies that we have already issued an
@@ -2106,6 +2110,16 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
        /* Leave it to update_binding to merge or report error.  */
        return NULL_TREE;
      }
+  else if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+          && !mergeable_version_decls (newdecl, olddecl))
+    {
+      /* newdecl defines an overlapping FMV version with olddecl but they
+        cannot be merged so are conflicting.  */
+      gcc_assert (conflicting_version.is_valid ());
+      error_at (newdecl_loc, "conflicting %qB versions", &conflicting_version);
+      inform (olddecl_loc, "previous definition");

That is, here you could call an explain_nonmergeable_versions function.

+      return error_mark_node;
+    }
    else
      {
        const char *errmsg = redeclaration_error_message (newdecl, olddecl);
@@ -2114,10 +2128,23 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
          auto_diagnostic_group d;
          error_at (newdecl_loc, errmsg, newdecl);
          if (DECL_NAME (olddecl) != NULL_TREE)
-           inform (olddecl_loc,
-                   (DECL_INITIAL (olddecl) && namespace_bindings_p ())
-                   ? G_("%q#D previously defined here")
-                   : G_("%q#D previously declared here"), olddecl);
+           {
+             /* If conflicting_version is set then this collision is between
+                two FMV annotated functions.  */
+             if (conflicting_version.is_valid ())
+               inform (olddecl_loc,
+                       (DECL_INITIAL (olddecl) && namespace_bindings_p ())
+                       ? G_("%qB version of %q#D previously defined here")
+                       : G_("%qB version of %q#D previously declared here"),

...and this seems redundant. Isn't the version printed by %D? If not, that would be good to do.

+                       &conflicting_version,
+                       olddecl);
+             else
+               inform (olddecl_loc,
+                       (DECL_INITIAL (olddecl) && namespace_bindings_p ())
+                       ? G_("%q#D previously defined here")
+                       : G_("%q#D previously declared here"),
+                       olddecl);
+           }
          if (cxx_dialect >= cxx26
              && DECL_NAME (newdecl)
              && id_equal (DECL_NAME (newdecl), "_")

Reply via email to