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

commit r16-5520-gf1e596c9526fbb4cf0bfa56b07d3c52aab9c9938
Author: Nathaniel Shead <[email protected]>
Date:   Thu Oct 23 13:51:57 2025 +1100

    c++: Add detailed diagnostics for 
__builtin_has_unique_object_representations
    
    gcc/cp/ChangeLog:
    
            * constraint.cc (diagnose_trait_expr)
            <case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS>: Explain failure with
            more detail.
            * cp-tree.h (type_has_unique_obj_representations): Add explain
            parameter.
            * tree.cc (record_has_unique_obj_representations): Explain when
            returning false.
            (type_has_unique_obj_representations): Likewise.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp1z/has-unique-obj-representations5.C: New test.
    
    Signed-off-by: Nathaniel Shead <[email protected]>
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/constraint.cc                               |   4 +-
 gcc/cp/cp-tree.h                                   |   2 +-
 gcc/cp/tree.cc                                     | 134 +++++++++++++++++----
 .../g++.dg/cpp1z/has-unique-obj-representations5.C |  47 ++++++++
 4 files changed, 163 insertions(+), 24 deletions(-)

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 8efe4a45348d..ebc3b8a40200 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3109,7 +3109,9 @@ diagnose_trait_expr (location_t loc, tree expr, tree args)
       inform (decl_loc, "%qT is not trivially destructible", t1);
       break;
     case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
-      inform (decl_loc, "%qT does not have unique object representations", t1);
+      inform (decl_loc, "%qT does not have unique object "
+             "representations, because", t1);
+      type_has_unique_obj_representations (t1, /*explain=*/true);
       break;
     case CPTK_HAS_VIRTUAL_DESTRUCTOR:
       {
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9b679fcf16a6..21befdb7c247 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8367,7 +8367,7 @@ extern bool std_layout_type_p                     
(const_tree);
 extern bool trivial_type_p                     (const_tree);
 extern bool implicit_lifetime_type_p           (tree);
 extern bool trivially_copyable_p               (const_tree);
-extern bool type_has_unique_obj_representations (const_tree);
+extern bool type_has_unique_obj_representations (const_tree, bool = false);
 extern bool scalarish_type_p                   (const_tree);
 extern bool structural_type_p                  (tree, bool = false);
 extern bool type_has_nontrivial_default_init   (const_tree);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index c1b46deb4e41..35abe553a1bb 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -4898,22 +4898,35 @@ std_layout_type_p (const_tree t)
     return scalarish_type_p (t);
 }
 
-static bool record_has_unique_obj_representations (const_tree, const_tree);
+static bool record_has_unique_obj_representations (const_tree, const_tree, 
bool);
 
 /* Returns true iff T satisfies std::has_unique_object_representations<T>,
-   as defined in [meta.unary.prop].  */
+   as defined in [meta.unary.prop].  If EXPLAIN is true, explain why not.  */
 
 bool
-type_has_unique_obj_representations (const_tree t)
+type_has_unique_obj_representations (const_tree t, bool explain/*=false*/)
 {
   bool ret;
 
   t = strip_array_types (CONST_CAST_TREE (t));
 
-  if (!trivially_copyable_p (t))
+  if (t == error_mark_node)
     return false;
 
-  if (CLASS_TYPE_P (t) && CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET (t))
+  location_t loc = input_location;
+  if (tree m = TYPE_MAIN_DECL (t))
+    loc = DECL_SOURCE_LOCATION (m);
+
+  if (!trivially_copyable_p (t))
+    {
+      if (explain)
+       inform (loc, "%qT is not trivially copyable", t);
+      return false;
+    }
+
+  if (CLASS_TYPE_P (t)
+      && CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET (t)
+      && !explain)
     return CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS (t);
 
   switch (TREE_CODE (t))
@@ -4931,16 +4944,21 @@ type_has_unique_obj_representations (const_tree t)
       return true;
 
     case ENUMERAL_TYPE:
-      return type_has_unique_obj_representations (ENUM_UNDERLYING_TYPE (t));
+      return type_has_unique_obj_representations (ENUM_UNDERLYING_TYPE (t),
+                                                 explain);
 
     case REAL_TYPE:
       /* XFmode certainly contains padding on x86, which the CPU doesn't store
         when storing long double values, so for that we have to return false.
         Other kinds of floating point values are questionable due to +.0/-.0
         and NaNs, let's play safe for now.  */
+      if (explain)
+       inform (loc, "%qT is a floating-point type", t);
       return false;
 
     case FIXED_POINT_TYPE:
+      if (explain)
+       inform (loc, "%qT is a fixed-point type", t);
       return false;
 
     case OFFSET_TYPE:
@@ -4948,10 +4966,10 @@ type_has_unique_obj_representations (const_tree t)
 
     case COMPLEX_TYPE:
     case VECTOR_TYPE:
-      return type_has_unique_obj_representations (TREE_TYPE (t));
+      return type_has_unique_obj_representations (TREE_TYPE (t), explain);
 
     case RECORD_TYPE:
-      ret = record_has_unique_obj_representations (t, TYPE_SIZE (t));
+      ret = record_has_unique_obj_representations (t, TYPE_SIZE (t), explain);
       if (CLASS_TYPE_P (t))
        {
          CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET (t) = 1;
@@ -4967,15 +4985,31 @@ type_has_unique_obj_representations (const_tree t)
        if (TREE_CODE (field) == FIELD_DECL)
          {
            any_fields = true;
-           if (!type_has_unique_obj_representations (TREE_TYPE (field))
-               || simple_cst_equal (DECL_SIZE (field), TYPE_SIZE (t)) != 1)
+           if (simple_cst_equal (DECL_SIZE (field), TYPE_SIZE (t)) != 1)
+             {
+               if (explain)
+                 inform (DECL_SOURCE_LOCATION (field),
+                         "%qD does not fill all bits of %q#T", field, t);
+               ret = false;
+               break;
+             }
+           if (!type_has_unique_obj_representations (TREE_TYPE (field)))
              {
+               if (explain)
+                 inform (DECL_SOURCE_LOCATION (field),
+                         "%qD has type %qT that does not have "
+                         "unique object representations",
+                         field, TREE_TYPE (field));
                ret = false;
                break;
              }
          }
       if (!any_fields && !integer_zerop (TYPE_SIZE (t)))
-       ret = false;
+       {
+         if (explain)
+           inform (loc, "%q#T has no data fields", t);
+         ret = false;
+       }
       if (CLASS_TYPE_P (t))
        {
          CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET (t) = 1;
@@ -4984,9 +5018,8 @@ type_has_unique_obj_representations (const_tree t)
       return ret;
 
     case NULLPTR_TYPE:
-      return false;
-
-    case ERROR_MARK:
+      if (explain)
+       inform (loc, "%<std::nullptr_t%> has padding bits and no value bits");
       return false;
 
     default:
@@ -4997,7 +5030,8 @@ type_has_unique_obj_representations (const_tree t)
 /* Helper function for type_has_unique_obj_representations.  */
 
 static bool
-record_has_unique_obj_representations (const_tree t, const_tree sz)
+record_has_unique_obj_representations (const_tree t, const_tree sz,
+                                      bool explain)
 {
   for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
     if (TREE_CODE (field) != FIELD_DECL)
@@ -5009,35 +5043,91 @@ record_has_unique_obj_representations (const_tree t, 
const_tree sz)
     else if (DECL_FIELD_IS_BASE (field))
       {
        if (!record_has_unique_obj_representations (TREE_TYPE (field),
-                                                   DECL_SIZE (field)))
-         return false;
+                                                   DECL_SIZE (field),
+                                                   /*explain=*/false))
+         {
+           if (explain)
+             inform (DECL_SOURCE_LOCATION (field),
+                     "base %qT does not have unique object representations",
+                     TREE_TYPE (field));
+           return false;
+         }
       }
     else if (DECL_C_BIT_FIELD (field) && !DECL_UNNAMED_BIT_FIELD (field))
       {
        tree btype = DECL_BIT_FIELD_TYPE (field);
        if (!type_has_unique_obj_representations (btype))
-         return false;
+         {
+           if (explain)
+             inform (DECL_SOURCE_LOCATION (field),
+                     "%qD with type %qT does not have "
+                     "unique object representations",
+                     field, btype);
+           return false;
+         }
       }
     else if (!type_has_unique_obj_representations (TREE_TYPE (field)))
-      return false;
+      {
+       if (explain)
+         inform (DECL_SOURCE_LOCATION (field),
+                 "%qD with type %qT does not have "
+                 "unique object representations",
+                 field, TREE_TYPE (field));
+       return false;
+      }
 
   offset_int cur = 0;
+  tree last_field = NULL_TREE;
+  tree last_named_field = NULL_TREE;
   for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
-    if (TREE_CODE (field) == FIELD_DECL && !DECL_UNNAMED_BIT_FIELD (field))
+    if (TREE_CODE (field) == FIELD_DECL)
       {
+       if (DECL_UNNAMED_BIT_FIELD (field))
+         {
+           last_field = field;
+           continue;
+         }
        offset_int fld = wi::to_offset (DECL_FIELD_OFFSET (field));
        offset_int bitpos = wi::to_offset (DECL_FIELD_BIT_OFFSET (field));
        fld = fld * BITS_PER_UNIT + bitpos;
        if (cur != fld)
-         return false;
+         {
+           if (explain)
+             {
+               if (last_named_field)
+                 inform (DECL_SOURCE_LOCATION (last_named_field),
+                         "padding occurs between %qD and %qD",
+                         last_named_field, field);
+               else if (last_field)
+                 inform (DECL_SOURCE_LOCATION (last_field),
+                         "unnamed bit-field inserts padding before %qD",
+                         field);
+               else
+                 inform (DECL_SOURCE_LOCATION (field),
+                         "padding occurs before %qD", field);
+             }
+           return false;
+         }
        if (DECL_SIZE (field))
          {
            offset_int size = wi::to_offset (DECL_SIZE (field));
            cur += size;
          }
+       last_named_field = last_field = field;
       }
   if (cur != wi::to_offset (sz))
-    return false;
+    {
+      if (explain)
+       {
+         if (last_named_field)
+           inform (DECL_SOURCE_LOCATION (last_named_field),
+                   "padding occurs after %qD", last_named_field);
+         else
+           inform (DECL_SOURCE_LOCATION (t),
+                   "%qT has padding and no data fields", t);
+       }
+      return false;
+    }
 
   return true;
 }
diff --git a/gcc/testsuite/g++.dg/cpp1z/has-unique-obj-representations5.C 
b/gcc/testsuite/g++.dg/cpp1z/has-unique-obj-representations5.C
new file mode 100644
index 000000000000..9380e044fe2a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/has-unique-obj-representations5.C
@@ -0,0 +1,47 @@
+// { dg-do compile { target c++17 } }
+// Test diagnostics on static assertion failure.
+
+template <typename T>
+constexpr bool is_unique = __has_unique_object_representations(T);
+
+struct AA {
+  float x;  // { dg-message "float" }
+};
+static_assert(is_unique<AA>);  // { dg-error "assert" }
+
+struct A : AA {};  // { dg-message "base" }
+static_assert(is_unique<A>);  // { dg-error "assert" }
+
+struct B {
+  char   : 1;  // { dg-message "unnamed bit-field inserts padding before" }
+  char x : 7;
+};
+static_assert(is_unique<B>);  // { dg-error "assert" }
+
+struct C {
+  char x : 5;  // { dg-message "padding occurs between 'C::x' and 'C::y'" }
+  char y;
+};
+static_assert(is_unique<C>);  // { dg-error "assert" }
+
+struct D {
+  int x : 5;  // { dg-message "padding occurs" }
+};
+static_assert(is_unique<D>);  // { dg-error "assert" }
+
+union E {
+  char x[1];  // { dg-message "does not fill all bits" }
+  char y[2];
+};
+static_assert(is_unique<E>);  // { dg-error "assert" }
+
+union F {};  // { dg-message "no data fields" }
+static_assert(is_unique<F>);  // { dg-error "assert" }
+
+struct G { G(const G&); };  // { dg-message "trivially copyable" }
+static_assert(is_unique<G>);  // { dg-error "assert" }
+
+struct H {
+  AA a;  // { dg-message "unique object representations" }
+};
+static_assert(is_unique<H>);  // { dg-error "assert" }

Reply via email to