On Thu, Oct 06, 2016 at 08:46:26AM -0400, Jason Merrill wrote:
> >>> >I would think that we want the same answer for bool and enums: Either
> >>> >the whole type size participates in the value representation, but only
> >>> >certain values have defined behavior (so we should return true), or
> >>> >only certain bits participate in the value representation (so we
> >>> >should return false).  This is a question for the committee, but I'm
> >>> >inclined to use the INTEGER_TYPE code for BOOLEAN_TYPE and
> >>> >ENUMERAL_TYPE as well, since we don't mask off the other bits when
> >>> >loading one of these types.
> >>>
> >>> Yes, my new, slightly improved understanding is that if the front-end
> >>> always did a mask operation when accessing enumeration types and bool
> >>> then the bits outside its valid range of values would be padding. They
> >>> would not contribute to its value if set to non-zero values e.g.  by
> >>> memset. But since we just load those values without masking, and rely
> >>> on a well-defined program not to set bits outside the valid range, we
> >>> should return true for those types.
> >>
> >> And if bool is always implemented as load of byte and comparison against 0,
> >> then the other bits wouldn't be padding, they would participate in the
> >> value, but it would mean the representation of true is not unique.
> >> But as we do sometimes comparison, sometimes masking and sometimes neither,
> >> the behavior with values other than 0/1 is really not well defined, they
> >> just shouldn't appear in valid programs.
> >>
> >> So, shall I just return true for INTEGER_TYPE/BOOLEAN_TYPE/ENUMERAL_TYPE
> >> as well as POINTER_TYPE/REFERENCE_TYPE, and if some target needs something
> >> different, add (later on if the need arises or right away?) a target hook
> >> where target can override.
> >
> > That makes sense to me.
> 
> And to me.

Ok, here is the updated patch, without addition of the target hook (until we
know for sure we need it).  I've added further testsuite coverage to check
the oversized bitfields, they actually work even with the old code due to
their DECL_SIZE being the actual underlying type's DECL_SIZE and then just
padding gap.

Bootstrap/regtest pending, ok for trunk if it succeeds?

2016-10-06  Jakub Jelinek  <ja...@redhat.com>

        Implement P0258R2 - helper for C++17
        std::has_unique_object_representations trait
c-family/
        * c-common.h (enum rid): Add RID_HAS_UNIQUE_OBJ_REPRESENTATIONS.
        * c-common.c (c_common_reswords): Add
        __has_unique_object_representations.
cp/
        * cp-tree.h (enum cp_trait_kind): Add
        CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS.
        (struct lang_type_class): Add unique_obj_representations
        and unique_obj_representations_set bitfields.
        (CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS,
        CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET): Define.
        (type_has_unique_obj_representations): Declare.
        * parser.c (cp_parser_primary_expression): Handle
        RID_HAS_UNIQUE_OBJ_REPRESENTATIONS.
        (cp_parser_trait_expr): Likewise.  Formatting fix.
        * semantics.c (trait_expr_value, finish_trait_expr): Handle
        CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS.
        * tree.c (type_has_unique_obj_representations): New function.
        (record_has_unique_obj_representations): New function.
        * cxx-pretty-print.c (pp_cxx_trait_expression): Handle
        CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS.
testsuite/
        * g++.dg/cpp1z/has-unique-obj-representations1.C: New test.
        * g++.dg/cpp1z/has-unique-obj-representations2.C: New test.

--- gcc/c-family/c-common.h.jj  2016-10-06 14:26:18.217598064 +0200
+++ gcc/c-family/c-common.h     2016-10-06 16:06:00.361315374 +0200
@@ -151,7 +151,8 @@ enum rid
   RID_HAS_NOTHROW_ASSIGN,      RID_HAS_NOTHROW_CONSTRUCTOR,
   RID_HAS_NOTHROW_COPY,        RID_HAS_TRIVIAL_ASSIGN,
   RID_HAS_TRIVIAL_CONSTRUCTOR, RID_HAS_TRIVIAL_COPY,
-  RID_HAS_TRIVIAL_DESTRUCTOR,  RID_HAS_VIRTUAL_DESTRUCTOR,
+  RID_HAS_TRIVIAL_DESTRUCTOR,  RID_HAS_UNIQUE_OBJ_REPRESENTATIONS,
+  RID_HAS_VIRTUAL_DESTRUCTOR,
   RID_IS_ABSTRACT,             RID_IS_BASE_OF,
   RID_IS_CLASS,
   RID_IS_EMPTY,                RID_IS_ENUM,
--- gcc/c-family/c-common.c.jj  2016-10-06 14:25:30.158207635 +0200
+++ gcc/c-family/c-common.c     2016-10-06 16:06:00.364315336 +0200
@@ -487,6 +487,8 @@ const struct c_common_resword c_common_r
   { "__has_trivial_constructor", RID_HAS_TRIVIAL_CONSTRUCTOR, D_CXXONLY },
   { "__has_trivial_copy", RID_HAS_TRIVIAL_COPY, D_CXXONLY },
   { "__has_trivial_destructor", RID_HAS_TRIVIAL_DESTRUCTOR, D_CXXONLY },
+  { "__has_unique_object_representations", RID_HAS_UNIQUE_OBJ_REPRESENTATIONS,
+                                       D_CXXONLY },
   { "__has_virtual_destructor", RID_HAS_VIRTUAL_DESTRUCTOR, D_CXXONLY },
   { "__imag",          RID_IMAGPART,   0 },
   { "__imag__",                RID_IMAGPART,   0 },
--- gcc/cp/cp-tree.h.jj 2016-10-06 15:19:29.254072523 +0200
+++ gcc/cp/cp-tree.h    2016-10-06 16:06:00.366315311 +0200
@@ -723,6 +723,7 @@ enum cp_trait_kind
   CPTK_HAS_TRIVIAL_CONSTRUCTOR,
   CPTK_HAS_TRIVIAL_COPY,
   CPTK_HAS_TRIVIAL_DESTRUCTOR,
+  CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS,
   CPTK_HAS_VIRTUAL_DESTRUCTOR,
   CPTK_IS_ABSTRACT,
   CPTK_IS_BASE_OF,
@@ -1713,6 +1714,8 @@ struct GTY(()) lang_type_class {
   unsigned has_complex_move_ctor : 1;
   unsigned has_complex_move_assign : 1;
   unsigned has_constexpr_ctor : 1;
+  unsigned unique_obj_representations : 1;
+  unsigned unique_obj_representations_set : 1;
 
   /* When adding a flag here, consider whether or not it ought to
      apply to a template instance if it applies to the template.  If
@@ -1721,7 +1724,7 @@ struct GTY(()) lang_type_class {
   /* There are some bits left to fill out a 32-bit word.  Keep track
      of this by updating the size of this bitfield whenever you add or
      remove a flag.  */
-  unsigned dummy : 4;
+  unsigned dummy : 2;
 
   tree primary_base;
   vec<tree_pair_s, va_gc> *vcall_indices;
@@ -2010,6 +2013,16 @@ struct GTY(()) lang_type {
 #define CLASSTYPE_NON_STD_LAYOUT(NODE) \
   (LANG_TYPE_CLASS_CHECK (NODE)->non_std_layout)
 
+/* Nonzero means that this class type does have unique object
+   representations.  */
+#define CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS(NODE) \
+  (LANG_TYPE_CLASS_CHECK (NODE)->unique_obj_representations)
+
+/* Nonzero means that this class type has
+   CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS computed.  */
+#define CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET(NODE) \
+  (LANG_TYPE_CLASS_CHECK (NODE)->unique_obj_representations_set)
+
 /* Nonzero means that this class contains pod types whose default
    initialization is not a zero initialization (namely, pointers to
    data members).  */
@@ -6481,6 +6494,7 @@ extern bool layout_pod_type_p                     (const_t
 extern bool std_layout_type_p                  (const_tree);
 extern bool trivial_type_p                     (const_tree);
 extern bool trivially_copyable_p               (const_tree);
+extern bool type_has_unique_obj_representations (const_tree);
 extern bool scalarish_type_p                   (const_tree);
 extern bool type_has_nontrivial_default_init   (const_tree);
 extern bool type_has_nontrivial_copy_init      (const_tree);
--- gcc/cp/parser.c.jj  2016-10-06 14:55:56.163032446 +0200
+++ gcc/cp/parser.c     2016-10-06 16:06:00.375315196 +0200
@@ -5110,6 +5110,7 @@ cp_parser_primary_expression (cp_parser
        case RID_HAS_TRIVIAL_CONSTRUCTOR:
        case RID_HAS_TRIVIAL_COPY:        
        case RID_HAS_TRIVIAL_DESTRUCTOR:
+       case RID_HAS_UNIQUE_OBJ_REPRESENTATIONS:
        case RID_HAS_VIRTUAL_DESTRUCTOR:
        case RID_IS_ABSTRACT:
        case RID_IS_BASE_OF:
@@ -9532,6 +9533,9 @@ cp_parser_trait_expr (cp_parser* parser,
     case RID_HAS_TRIVIAL_DESTRUCTOR:
       kind = CPTK_HAS_TRIVIAL_DESTRUCTOR;
       break;
+    case RID_HAS_UNIQUE_OBJ_REPRESENTATIONS:
+      kind = CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS;
+      break;
     case RID_HAS_VIRTUAL_DESTRUCTOR:
       kind = CPTK_HAS_VIRTUAL_DESTRUCTOR;
       break;
@@ -9646,7 +9650,7 @@ cp_parser_trait_expr (cp_parser* parser,
 
   /* Complete the trait expression, which may mean either processing
      the trait expr now or saving it for template instantiation.  */
-  switch(kind)
+  switch (kind)
     {
     case CPTK_UNDERLYING_TYPE:
       return finish_underlying_type (type1);
--- gcc/cp/semantics.c.jj       2016-10-06 15:43:12.762978280 +0200
+++ gcc/cp/semantics.c  2016-10-06 16:06:00.377315171 +0200
@@ -9113,6 +9113,9 @@ trait_expr_value (cp_trait_kind kind, tr
     case CPTK_HAS_VIRTUAL_DESTRUCTOR:
       return type_has_virtual_destructor (type1);
 
+    case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
+      return type_has_unique_obj_representations (type1);
+
     case CPTK_IS_ABSTRACT:
       return (ABSTRACT_CLASS_TYPE_P (type1));
 
@@ -9220,6 +9223,7 @@ finish_trait_expr (cp_trait_kind kind, t
     case CPTK_HAS_NOTHROW_COPY:
     case CPTK_HAS_TRIVIAL_COPY:
     case CPTK_HAS_TRIVIAL_DESTRUCTOR:
+    case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
     case CPTK_HAS_VIRTUAL_DESTRUCTOR:
     case CPTK_IS_ABSTRACT:
     case CPTK_IS_EMPTY:
--- gcc/cp/tree.c.jj    2016-10-05 19:36:59.103212746 +0200
+++ gcc/cp/tree.c       2016-10-06 16:17:05.491850731 +0200
@@ -3575,6 +3575,150 @@ std_layout_type_p (const_tree t)
     return scalarish_type_p (t);
 }
 
+static bool record_has_unique_obj_representations (const_tree, const_tree);
+
+/* Returns true iff T satisfies std::has_unique_object_representations<T>,
+   as defined in [meta.unary.prop].  */
+
+bool
+type_has_unique_obj_representations (const_tree t)
+{
+  bool ret;
+
+  t = strip_array_types (CONST_CAST_TREE (t));
+
+  if (!trivially_copyable_p (t))
+    return false;
+
+  if (CLASS_TYPE_P (t) && CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET (t))
+    return CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS (t);
+
+  switch (TREE_CODE (t))
+    {
+    case INTEGER_TYPE:
+    case POINTER_TYPE:
+    case REFERENCE_TYPE:
+      /* If some backend has any paddings in these types, we should add
+        a target hook for this and handle it there.  */
+      return true;
+
+    case BOOLEAN_TYPE:
+      /* For bool values other than 0 and 1 should only appear with
+        undefined behavior.  */
+      return true;
+
+    case ENUMERAL_TYPE:
+      return type_has_unique_obj_representations (ENUM_UNDERLYING_TYPE (t));
+
+    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.  */
+      return false;
+
+    case FIXED_POINT_TYPE:
+      return false;
+
+    case OFFSET_TYPE:
+      return true;
+
+    case COMPLEX_TYPE:
+    case VECTOR_TYPE:
+      return type_has_unique_obj_representations (TREE_TYPE (t));
+
+    case RECORD_TYPE:
+      ret = record_has_unique_obj_representations (t, TYPE_SIZE (t));
+      if (CLASS_TYPE_P (t))
+       {
+         CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET (t) = 1;
+         CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS (t) = ret;
+       }
+      return ret;
+
+    case UNION_TYPE:
+      ret = true;
+      bool any_fields;
+      any_fields = false;
+      for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
+       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)
+             {
+               ret = false;
+               break;
+             }
+         }
+      if (!any_fields && !integer_zerop (TYPE_SIZE (t)))
+       ret = false;
+      if (CLASS_TYPE_P (t))
+       {
+         CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET (t) = 1;
+         CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS (t) = ret;
+       }
+      return ret;
+
+    case NULLPTR_TYPE:
+      return false;
+
+    case ERROR_MARK:
+      return false;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Helper function for type_has_unique_obj_representations.  */
+
+static bool
+record_has_unique_obj_representations (const_tree t, const_tree sz)
+{
+  for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
+    if (TREE_CODE (field) != FIELD_DECL)
+      ;
+    /* For bases, can't use type_has_unique_obj_representations here, as in
+       struct S { int i : 24; S (); };
+       struct T : public S { int j : 8; T (); };
+       S doesn't have unique obj representations, but T does.  */
+    else if (DECL_FIELD_IS_BASE (field))
+      {
+       if (!record_has_unique_obj_representations (TREE_TYPE (field),
+                                                   DECL_SIZE (field)))
+         return false;
+      }
+    else if (DECL_C_BIT_FIELD (field))
+      {
+       tree btype = DECL_BIT_FIELD_TYPE (field);
+       if (!type_has_unique_obj_representations (btype))
+         return false;
+      }
+    else if (!type_has_unique_obj_representations (TREE_TYPE (field)))
+      return false;
+
+  offset_int cur = 0;
+  for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
+    if (TREE_CODE (field) == FIELD_DECL)
+      {
+       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 (DECL_SIZE (field))
+         {
+           offset_int size = wi::to_offset (DECL_SIZE (field));
+           cur += size;
+         }
+      }
+  if (cur != wi::to_offset (sz))
+    return false;
+
+  return true;
+}
+
 /* Nonzero iff type T is a class template implicit specialization.  */
 
 bool
--- gcc/cp/cxx-pretty-print.c.jj        2016-10-06 15:41:49.232040043 +0200
+++ gcc/cp/cxx-pretty-print.c   2016-10-06 16:06:00.379315145 +0200
@@ -2561,6 +2561,9 @@ pp_cxx_trait_expression (cxx_pretty_prin
     case CPTK_HAS_TRIVIAL_DESTRUCTOR:
       pp_cxx_ws_string (pp, "__has_trivial_destructor");
       break;
+    case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
+      pp_cxx_ws_string (pp, "__has_unique_object_representations");
+      break;
     case CPTK_HAS_VIRTUAL_DESTRUCTOR:
       pp_cxx_ws_string (pp, "__has_virtual_destructor");
       break;
--- gcc/testsuite/g++.dg/cpp1z/has-unique-obj-representations1.C.jj     
2016-10-06 16:06:00.380315133 +0200
+++ gcc/testsuite/g++.dg/cpp1z/has-unique-obj-representations1.C        
2016-10-06 16:54:53.131906764 +0200
@@ -0,0 +1,47 @@
+// { dg-do compile { target c++11 } }
+
+#define INTB (__SIZEOF_INT__ * __CHAR_BIT__)
+struct S { int i : INTB * 3 / 4; S (); };
+struct T : public S { int j : INTB / 4; T (); };
+struct U { int i : INTB * 3 / 4; int j : INTB / 4; };
+struct V { int i : INTB * 3 / 4; int j : INTB / 4 + 1; };
+struct W {};
+struct X : public W { int i; void bar (); };
+struct Y { char a[3]; char b[]; };
+struct Z { int a; float b; };
+struct A { int i : INTB * 2; int j; };                 // { dg-warning 
"exceeds its type" }
+union B { long a; unsigned long b; };
+union C { int a; int b : INTB - 1; };
+struct D { int a : INTB + 1; int b : INTB - 1; };      // { dg-warning 
"exceeds its type" }
+static_assert (__has_unique_object_representations (char) == true, "");
+static_assert (__has_unique_object_representations (unsigned char) == true, 
"");
+static_assert (__has_unique_object_representations (int) == true, "");
+static_assert (__has_unique_object_representations (unsigned int) == true, "");
+static_assert (__has_unique_object_representations (bool) == true, "");
+static_assert (sizeof (S) != sizeof (int) || 
__has_unique_object_representations (S) == false, "");
+static_assert (sizeof (T) != sizeof (int) || 
__has_unique_object_representations (T) == true, "");
+static_assert (sizeof (U) != sizeof (int) || 
__has_unique_object_representations (U) == true, "");
+static_assert (__has_unique_object_representations (V) == false, "");
+static_assert (__has_unique_object_representations (W) == false, "");
+static_assert (sizeof (X) != sizeof (int) || 
__has_unique_object_representations (X) == true, "");
+static_assert (__has_unique_object_representations (float) == false, "");
+static_assert (__has_unique_object_representations (double) == false, "");
+static_assert (__has_unique_object_representations (long double) == false, "");
+static_assert (__has_unique_object_representations (void) == false, "");
+static_assert (__has_unique_object_representations (_Complex int) == true, "");
+static_assert (__has_unique_object_representations (_Complex float) == false, 
"");
+static_assert (__has_unique_object_representations (_Complex double) == false, 
"");
+static_assert (__has_unique_object_representations (_Complex long double) == 
false, "");
+static_assert (__has_unique_object_representations (int 
__attribute__((vector_size (16)))) == true, "");
+static_assert (__has_unique_object_representations (float 
__attribute__((vector_size (16)))) == false, "");
+static_assert (__has_unique_object_representations (int X::*) == true, "");
+static_assert (__has_unique_object_representations (void (X::*) ()) == true, 
"");
+static_assert (__has_unique_object_representations (int *) == true, "");
+static_assert (__has_unique_object_representations (int (*) ()) == true, "");
+static_assert (__has_unique_object_representations (decltype (nullptr)) == 
false, "");
+static_assert (__has_unique_object_representations (Y) == (sizeof (Y) == 3 * 
sizeof (char)), "");
+static_assert (__has_unique_object_representations (Z) == false, "");
+static_assert (__has_unique_object_representations (A) == false, "");
+static_assert (sizeof (B) != sizeof (long) || 
__has_unique_object_representations (B) == true, "");
+static_assert (__has_unique_object_representations (C) == false, "");
+static_assert (__has_unique_object_representations (D) == false, "");
--- gcc/testsuite/g++.dg/cpp1z/has-unique-obj-representations2.C.jj     
2016-10-06 16:06:00.380315133 +0200
+++ gcc/testsuite/g++.dg/cpp1z/has-unique-obj-representations2.C        
2016-10-06 16:29:43.051191257 +0200
@@ -0,0 +1,8 @@
+struct S;
+struct T { S t; };                                     // { dg-error 
"incomplete type" }
+struct U { int u[sizeof (S)]; };                       // { dg-error 
"incomplete type" }
+union V { char c; char d[]; };                         // { dg-error "flexible 
array member in union" }
+bool a = __has_unique_object_representations (S);      // { dg-error 
"incomplete type" }
+bool b = __has_unique_object_representations (T);
+bool c = __has_unique_object_representations (U);
+bool d = __has_unique_object_representations (V);

        Jakub

Reply via email to