PR c++/89089 - ICE with [[no_unique_address]].
authorJason Merrill <jason@redhat.com>
Tue, 29 Jan 2019 15:39:40 +0000 (10:39 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 29 Jan 2019 15:39:40 +0000 (10:39 -0500)
In 89089, we were never actually setting DECL_SIZE on an empty data member,
because its type is a POD, so we didn't set it in the maybe-overlapping
section.  Fixed by also handling empty types there.

In 88865, we were failing to consider empty data members in
include_empty_classes.  Fixed by making end_of_class always include them.

While looking at these I noticed that the ABI says that a
potentially-overlapping data member makes its class non-layout-POD, and that
an empty data member doesn't prevent its class from being empty, so I've
implemented those points as well.

PR c++/88865 - wrong layout with [[no_unique_address]].
* class.c (check_field_decls): A potentially-overlapping field makes
the class non-layout-POD, but not non-empty.
(end_of_class): Always consider empty data members.
(layout_class_type): Set DECL_SIZE for empty fields.

From-SVN: r268368

gcc/cp/ChangeLog
gcc/cp/class.c
gcc/testsuite/g++.dg/abi/no_unique_address4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/no_unique_address5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/no_unique_address2.C [new file with mode: 0644]

index 5e2f69d..3cdc779 100644 (file)
@@ -1,3 +1,12 @@
+2019-01-28  Jason Merrill  <jason@redhat.com>
+
+       PR c++/89089 - ICE with [[no_unique_address]].
+       PR c++/88865 - wrong layout with [[no_unique_address]].
+       * class.c (check_field_decls): A potentially-overlapping field makes
+       the class non-layout-POD, but not non-empty.
+       (end_of_class): Always consider empty data members.
+       (layout_class_type): Set DECL_SIZE for empty fields.
+
 2019-01-28  Marek Polacek  <polacek@redhat.com>
 
        PR c++/88358 - name wrongly treated as type.
index e8773c2..48da081 100644 (file)
@@ -206,6 +206,7 @@ static int empty_base_at_nonzero_offset_p (tree, tree, splay_tree);
 static tree end_of_base (tree);
 static tree get_vcall_index (tree, tree);
 static bool type_maybe_constexpr_default_constructor (tree);
+static bool field_poverlapping_p (tree);
 
 /* Return a COND_EXPR that executes TRUE_STMT if this execution of the
    'structor is in charge of 'structing virtual bases, or FALSE_STMT
@@ -3556,6 +3557,11 @@ check_field_decls (tree t, tree *access_decls,
        /* We don't treat zero-width bitfields as making a class
           non-empty.  */
        ;
+      else if (field_poverlapping_p (x) && is_empty_class (type))
+       {
+         /* Empty data members also don't make a class non-empty.  */
+         CLASSTYPE_CONTAINS_EMPTY_CLASS_P (t) = 1;
+       }
       else
        {
          /* The class is non-empty.  */
@@ -3608,6 +3614,11 @@ check_field_decls (tree t, tree *access_decls,
           to be allowed in POD structs.  */
        CLASSTYPE_NON_LAYOUT_POD_P (t) = 1;
 
+      if (field_poverlapping_p (x))
+       /* A potentially-overlapping non-static data member makes the class
+          non-layout-POD.  */
+       CLASSTYPE_NON_LAYOUT_POD_P (t) = 1;
+
       if (!std_layout_type_p (type))
        CLASSTYPE_NON_STD_LAYOUT (t) = 1;
 
@@ -5926,13 +5937,12 @@ end_of_base (tree binfo)
   return size_binop (PLUS_EXPR, BINFO_OFFSET (binfo), size);
 }
 
-/* Returns the offset of the byte just past the end of the base class
-   with the highest offset in T.  If INCLUDE_VIRTUALS_P is zero, then
-   only non-virtual bases are included.  If INCLUDE_FIELDS_P is true,
-   then also consider non-static data members.  */
+/* Returns the offset of the byte just past the end of the base class or empty
+   data member with the highest offset in T.  If INCLUDE_VIRTUALS_P is zero,
+   then only non-virtual bases are included.  */
 
 static tree
-end_of_class (tree t, bool include_virtuals_p, bool include_fields_p = false)
+end_of_class (tree t, bool include_virtuals_p)
 {
   tree result = size_zero_node;
   vec<tree, va_gc> *vbases;
@@ -5955,15 +5965,19 @@ end_of_class (tree t, bool include_virtuals_p, bool include_fields_p = false)
        result = offset;
     }
 
-  if (include_fields_p)
-    for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
-      if (TREE_CODE (field) == FIELD_DECL)
-       {
-         offset = size_binop (PLUS_EXPR, DECL_FIELD_OFFSET (field),
-                              DECL_SIZE_UNIT (field));
-         if (tree_int_cst_lt (result, offset))
-           result = offset;
-       }
+  /* Also consider empty data members.  */
+  for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
+    if (TREE_CODE (field) == FIELD_DECL
+       && !DECL_ARTIFICIAL (field)
+       && field_poverlapping_p (field)
+       && is_empty_class (TREE_TYPE (field)))
+      {
+       /* Update sizeof(C) to max (sizeof(C), offset(D)+sizeof(D)) */
+       offset = size_binop (PLUS_EXPR, DECL_FIELD_OFFSET (field),
+                            TYPE_SIZE_UNIT (TREE_TYPE (field)));
+       if (tree_int_cst_lt (result, offset))
+         result = offset;
+      }
 
   if (include_virtuals_p)
     for (vbases = CLASSTYPE_VBASECLASSES (t), i = 0;
@@ -6154,12 +6168,14 @@ layout_class_type (tree t, tree *virtuals_p)
       bool might_overlap = field_poverlapping_p (field);
 
       if (might_overlap && CLASS_TYPE_P (type)
-         && CLASSTYPE_NON_LAYOUT_POD_P (type))
+         && (CLASSTYPE_NON_LAYOUT_POD_P (type) || CLASSTYPE_EMPTY_P (type)))
        {
          /* if D is a potentially-overlapping data member, update sizeof(C) to
             max (sizeof(C), offset(D)+max (nvsize(D), dsize(D))).  */
          tree nvsize = CLASSTYPE_SIZE_UNIT (type);
-         tree dsize = end_of_class (type, /*vbases*/true, /*fields*/true);
+         /* end_of_class doesn't always give dsize, but it does in the case of
+            a class with virtual bases, which is when dsize > nvsize.  */
+         tree dsize = end_of_class (type, /*vbases*/true);
          if (tree_int_cst_le (dsize, nvsize))
            {
              DECL_SIZE_UNIT (field) = nvsize;
diff --git a/gcc/testsuite/g++.dg/abi/no_unique_address4.C b/gcc/testsuite/g++.dg/abi/no_unique_address4.C
new file mode 100644 (file)
index 0000000..c62cfe1
--- /dev/null
@@ -0,0 +1,25 @@
+// Test that [[no_unique_address]] makes the enclosing class non-layout-POD.
+// { dg-do compile { target c++11 } }
+
+struct A {};
+struct B1: A {
+  int i;
+  char c;
+};
+
+struct B2 {
+  [[no_unique_address]] A a;
+  int i;
+  char c;
+};
+
+struct C1: B1 {
+  char d;
+};
+
+struct C2: B2 {
+  char d;
+};
+
+#define SA(X) static_assert((X),#X)
+SA(sizeof(C1) == sizeof(C2));
diff --git a/gcc/testsuite/g++.dg/abi/no_unique_address5.C b/gcc/testsuite/g++.dg/abi/no_unique_address5.C
new file mode 100644 (file)
index 0000000..3e94048
--- /dev/null
@@ -0,0 +1,18 @@
+// PR c++/88865
+// { dg-do compile { target c++11 } }
+
+struct B {};
+struct A {
+    [[no_unique_address]] B a;
+    [[no_unique_address]] B b;
+    [[no_unique_address]] B c;
+    [[no_unique_address]] B d;
+};
+
+#define SA(X) static_assert((X),#X)
+SA(sizeof(A) == 4);
+
+A a;
+SA(&a.a != &a.b);
+SA(&a.c != &a.b);
+SA(&a.c != &a.d);
diff --git a/gcc/testsuite/g++.dg/cpp2a/no_unique_address2.C b/gcc/testsuite/g++.dg/cpp2a/no_unique_address2.C
new file mode 100644 (file)
index 0000000..aae2394
--- /dev/null
@@ -0,0 +1,12 @@
+// PR c++/89089
+// { dg-do compile { target c++11 } }
+
+template <typename...> struct A {};
+template <typename T, typename... U> struct A<T, U...> {
+private:
+  [[no_unique_address]] A<U...> a;
+};
+struct B {
+  template <typename... U> A<U...> operator()(U...) { return A<U...>(); }
+} f;
+auto fn = f (int{}, [] {});