I experimented with doing the explanations recursively (so e.g. when
explaining that a field doesn't have unique object representations, go
on to explain why that field's type doesn't have unique object
representations, and so forth until we stop) but that seemed liable to
generate a lot of unhelpful diagnostics for complex types, and if needed
a user can always add a `static_assert` with the affected type to get
the next set of diagnostics anyway.  Alternatively could add a flag to
do recursive explanations but again, didn't seem overly useful.

(This also applies for the later patches in this series.)

OK for trunk?

-- >8 --

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]>
---
 gcc/cp/constraint.cc                          |   4 +-
 gcc/cp/cp-tree.h                              |   2 +-
 gcc/cp/tree.cc                                | 122 +++++++++++++++---
 .../cpp1z/has-unique-obj-representations5.C   |  45 +++++++
 4 files changed, 150 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/has-unique-obj-representations5.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 90c35023953..1ab5a2902d3 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3110,7 +3110,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 fcba9f5c0b0..844dc3a577e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8377,7 +8377,7 @@ extern bool trivial_type_p                        
(const_tree);
 extern bool trivially_relocatable_type_p       (tree);
 extern bool replaceable_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 814465c6529..7cfdfca32ae 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -5149,22 +5149,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 = UNKNOWN_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))
@@ -5182,16 +5195,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:
@@ -5199,10 +5217,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;
@@ -5218,15 +5236,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;
@@ -5235,9 +5269,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");
       return false;
 
     default:
@@ -5248,7 +5281,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)
@@ -5260,19 +5294,42 @@ 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 subobject with type %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;
   for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
     if (TREE_CODE (field) == FIELD_DECL && !DECL_UNNAMED_BIT_FIELD (field))
       {
@@ -5280,15 +5337,38 @@ record_has_unique_obj_representations (const_tree t, 
const_tree sz)
        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_field)
+                 inform (DECL_SOURCE_LOCATION (last_field),
+                         "padding occurs between %qD and %qD",
+                         last_field, 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_field = field;
       }
   if (cur != wi::to_offset (sz))
-    return false;
+    {
+      if (explain)
+       {
+         if (last_field)
+           inform (DECL_SOURCE_LOCATION (last_field),
+                   "padding occurs after %qD", last_field);
+         else
+           inform (DECL_SOURCE_LOCATION (t), "%qT has padding", 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 00000000000..8b7db915403
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/has-unique-obj-representations5.C
@@ -0,0 +1,45 @@
+// { 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;
+};
+struct A : AA {};  // { dg-message "base" }
+static_assert(is_unique<A>);  // { dg-error "assert" }
+
+struct B {
+  char   : 1;
+  char x : 7;  // { dg-message "padding occurs before" }
+};
+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" }
-- 
2.51.0


Reply via email to