diff mbox

C++ PATCH for P0017, C++17 aggregates with bases

Message ID CADzB+2n2mPb37TAJkPO2QcAqZGNFOak98M0Okm4e-cvhwpHVGQ@mail.gmail.com
State New
Headers show

Commit Message

Jason Merrill Oct. 14, 2016, 9:54 p.m. UTC
Implementing this proposal was a pretty straightforward matter of
changing the definition of aggregate and treating artificial base
fields as initializable in aggregate initialization.  For this to work
with empty bases, I needed to create base fields for them, which we
haven't done in the past.  build_base_field warned about problems with
empty base fields confusing the back end, but I wasn't able to find
any such trouble.  For the time being I'm only creating them in C++17
mode, to limit any regressions.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 99ba4ce4ec3daa3897b6bc971381ca4b1cdc54b1
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Oct 14 07:45:02 2016 -0400

            Implement P0017R1, C++17 aggregates with bases.
    
            * class.c (build_base_field_1): Split out from...
            (build_base_field): ...here.  In C++17 mode, build a field for
            empty bases.
            * decl.c (xref_basetypes): In C++17 aggregates can have bases.
            (next_initializable_field): Allow base fields in C++17.
            * typeck2.c (process_init_constructor_record): Likewise.
diff mbox

Patch

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 46f1bac..9a6ea97 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -4452,6 +4452,34 @@  layout_empty_base (record_layout_info rli, tree binfo,
   return atend;
 }
 
+/* Build the FIELD_DECL for BASETYPE as a base of T, add it to the chain of
+   fields at NEXT_FIELD, and return it.  */
+
+static tree
+build_base_field_1 (tree t, tree basetype, tree *&next_field)
+{
+  /* Create the FIELD_DECL.  */
+  gcc_assert (CLASSTYPE_AS_BASE (basetype));
+  tree decl = build_decl (input_location,
+			  FIELD_DECL, NULL_TREE, CLASSTYPE_AS_BASE (basetype));
+  DECL_ARTIFICIAL (decl) = 1;
+  DECL_IGNORED_P (decl) = 1;
+  DECL_FIELD_CONTEXT (decl) = t;
+  DECL_SIZE (decl) = CLASSTYPE_SIZE (basetype);
+  DECL_SIZE_UNIT (decl) = CLASSTYPE_SIZE_UNIT (basetype);
+  SET_DECL_ALIGN (decl, CLASSTYPE_ALIGN (basetype));
+  DECL_USER_ALIGN (decl) = CLASSTYPE_USER_ALIGN (basetype);
+  DECL_MODE (decl) = TYPE_MODE (basetype);
+  DECL_FIELD_IS_BASE (decl) = 1;
+
+  /* Add the new FIELD_DECL to the list of fields for T.  */
+  DECL_CHAIN (decl) = *next_field;
+  *next_field = decl;
+  next_field = &DECL_CHAIN (decl);
+
+  return decl;
+}
+
 /* Layout the base given by BINFO in the class indicated by RLI.
    *BASE_ALIGN is a running maximum of the alignments of
    any base class.  OFFSETS gives the location of empty base
@@ -4483,29 +4511,12 @@  build_base_field (record_layout_info rli, tree binfo,
       CLASSTYPE_EMPTY_P (t) = 0;
 
       /* Create the FIELD_DECL.  */
-      decl = build_decl (input_location,
-			 FIELD_DECL, NULL_TREE, CLASSTYPE_AS_BASE (basetype));
-      DECL_ARTIFICIAL (decl) = 1;
-      DECL_IGNORED_P (decl) = 1;
-      DECL_FIELD_CONTEXT (decl) = t;
-      if (CLASSTYPE_AS_BASE (basetype))
-	{
-	  DECL_SIZE (decl) = CLASSTYPE_SIZE (basetype);
-	  DECL_SIZE_UNIT (decl) = CLASSTYPE_SIZE_UNIT (basetype);
-	  SET_DECL_ALIGN (decl, CLASSTYPE_ALIGN (basetype));
-	  DECL_USER_ALIGN (decl) = CLASSTYPE_USER_ALIGN (basetype);
-	  DECL_MODE (decl) = TYPE_MODE (basetype);
-	  DECL_FIELD_IS_BASE (decl) = 1;
+      decl = build_base_field_1 (t, basetype, next_field);
 
-	  /* Try to place the field.  It may take more than one try if we
-	     have a hard time placing the field without putting two
-	     objects of the same type at the same address.  */
-	  layout_nonempty_base_or_field (rli, decl, binfo, offsets);
-	  /* Add the new FIELD_DECL to the list of fields for T.  */
-	  DECL_CHAIN (decl) = *next_field;
-	  *next_field = decl;
-	  next_field = &DECL_CHAIN (decl);
-	}
+      /* Try to place the field.  It may take more than one try if we
+	 have a hard time placing the field without putting two
+	 objects of the same type at the same address.  */
+      layout_nonempty_base_or_field (rli, decl, binfo, offsets);
     }
   else
     {
@@ -4541,6 +4552,13 @@  build_base_field (record_layout_info rli, tree binfo,
 	 create CONSTRUCTORs for the class by iterating over the
 	 FIELD_DECLs, and the back end does not handle overlapping
 	 FIELD_DECLs.  */
+      if (cxx_dialect >= cxx1z && !BINFO_VIRTUAL_P (binfo))
+	{
+	  tree decl = build_base_field_1 (t, basetype, next_field);
+	  DECL_FIELD_OFFSET (decl) = BINFO_OFFSET (binfo);
+	  DECL_FIELD_BIT_OFFSET (decl) = bitsize_zero_node;
+	  SET_DECL_OFFSET_ALIGN (decl, BITS_PER_UNIT);
+	}
 
       /* An empty virtual base causes a class to be non-empty
 	 -- but in that case we do not need to clear CLASSTYPE_EMPTY_P
@@ -6586,7 +6604,7 @@  layout_class_type (tree t, tree *virtuals_p)
 
   /* Make sure that empty classes are reflected in RLI at this
      point.  */
-  include_empty_classes(rli);
+  include_empty_classes (rli);
 
   /* Make sure not to create any structures with zero size.  */
   if (integer_zerop (rli_size_unit_so_far (rli)) && CLASSTYPE_EMPTY_P (t))
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index f761d0d..d9850e7 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5569,7 +5569,8 @@  next_initializable_field (tree field)
   while (field
 	 && (TREE_CODE (field) != FIELD_DECL
 	     || (DECL_C_BIT_FIELD (field) && !DECL_NAME (field))
-	     || DECL_ARTIFICIAL (field)))
+	     || (DECL_ARTIFICIAL (field)
+		 && !(cxx_dialect >= cxx1z && DECL_FIELD_IS_BASE (field)))))
     field = DECL_CHAIN (field);
 
   return field;
@@ -13150,8 +13151,8 @@  xref_basetypes (tree ref, tree base_list)
   if (max_bases)
     {
       vec_alloc (BINFO_BASE_ACCESSES (binfo), max_bases);
-      /* An aggregate cannot have baseclasses.  */
-      CLASSTYPE_NON_AGGREGATE (ref) = 1;
+      /* A C++98 POD cannot have base classes.  */
+      CLASSTYPE_NON_LAYOUT_POD_P (ref) = true;
 
       if (TREE_CODE (ref) == UNION_TYPE)
 	error ("derived union %qT invalid", ref);
@@ -13179,6 +13180,13 @@  xref_basetypes (tree ref, tree base_list)
       if (access == access_default_node)
 	access = default_access;
 
+      /* Before C++17, an aggregate cannot have base classes.  In C++17, an
+	 aggregate can't have virtual, private, or protected base classes.  */
+      if (cxx_dialect < cxx1z
+	  || access != access_public_node
+	  || via_virtual)
+	CLASSTYPE_NON_AGGREGATE (ref) = true;
+
       if (PACK_EXPANSION_P (basetype))
         basetype = PACK_EXPANSION_PATTERN (basetype);
       if (TREE_CODE (basetype) == TYPE_DECL)
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 121da32..022a478 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1352,6 +1352,7 @@  process_init_constructor_record (tree type, tree init,
   gcc_assert (TREE_CODE (type) == RECORD_TYPE);
   gcc_assert (!CLASSTYPE_VBASECLASSES (type));
   gcc_assert (!TYPE_BINFO (type)
+	      || cxx_dialect >= cxx1z
 	      || !BINFO_N_BASE_BINFOS (TYPE_BINFO (type)));
   gcc_assert (!TYPE_POLYMORPHIC_P (type));
 
@@ -1369,7 +1370,9 @@  process_init_constructor_record (tree type, tree init,
       if (!DECL_NAME (field) && DECL_C_BIT_FIELD (field))
 	continue;
 
-      if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
+      if (TREE_CODE (field) != FIELD_DECL
+	  || (DECL_ARTIFICIAL (field)
+	      && !(cxx_dialect >= cxx1z && DECL_FIELD_IS_BASE (field))))
 	continue;
 
       /* If this is a bitfield, first convert to the declared type.  */
diff --git a/gcc/testsuite/g++.dg/cpp1z/aggr-base1.C b/gcc/testsuite/g++.dg/cpp1z/aggr-base1.C
new file mode 100644
index 0000000..37bb472
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/aggr-base1.C
@@ -0,0 +1,23 @@ 
+// { dg-options -std=c++1z }
+// { dg-do run }
+
+struct base1 { int b1, b2 = 42; };
+struct base2 {
+  base2() {
+    b3 = 42;
+  }
+  int b3;
+};
+struct derived : base1, base2 {
+  int d;
+};
+
+derived d1{{1, 2}, {}, 4};
+derived d2{{}, {}, 4};
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while(0)
+int main()
+{
+  assert (d1.b1 == 1 && d1.b2 == 2 && d1.b3 == 42 && d1.d == 4);
+  assert (d2.b1 == 0 && d2.b2 == 42 && d2.b3 == 42 && d2.d == 4);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/aggr-base1a.C b/gcc/testsuite/g++.dg/cpp1z/aggr-base1a.C
new file mode 100644
index 0000000..4b28485
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/aggr-base1a.C
@@ -0,0 +1,15 @@ 
+// { dg-do compile { target { c++11 && c++14_down } } }
+
+struct base1 { int b1, b2 = 42; };
+struct base2 {
+  base2() {
+    b3 = 42;
+  }
+  int b3;
+};
+struct derived : base1, base2 {
+  int d;
+};
+
+derived d1{{1, 2}, {}, 4};	// { dg-error "" }
+derived d2{{}, {}, 4};		// { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/aggr-base2.C b/gcc/testsuite/g++.dg/cpp1z/aggr-base2.C
new file mode 100644
index 0000000..9da5ebf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/aggr-base2.C
@@ -0,0 +1,12 @@ 
+// { dg-options -std=c++1z }
+
+struct derived;
+struct base {
+  friend struct derived;
+private: 
+  base();
+};
+struct derived : base {};
+
+derived d1{};			// { dg-error "" "" { target c++1z } }
+derived d2;			// still OK
diff --git a/gcc/testsuite/g++.dg/cpp1z/aggr-base2a.C b/gcc/testsuite/g++.dg/cpp1z/aggr-base2a.C
new file mode 100644
index 0000000..821dce1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/aggr-base2a.C
@@ -0,0 +1,12 @@ 
+// { dg-do compile { target c++11 } }
+
+struct derived;
+struct base {
+  friend struct derived;
+private: 
+  base();
+};
+struct derived : base {};
+
+derived d1{};			// { dg-error "" "" { target c++1z } }
+derived d2;			// still OK
diff --git a/gcc/testsuite/g++.dg/cpp1z/aggr-base3.C b/gcc/testsuite/g++.dg/cpp1z/aggr-base3.C
new file mode 100644
index 0000000..4acbc0b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/aggr-base3.C
@@ -0,0 +1,11 @@ 
+// { dg-options -std=c++1z }
+
+struct derived;
+struct base { };
+struct derived : base {
+  int i;
+};
+
+derived d1{1};			// { dg-error "base" }
+derived d2{{},1};		// OK
+
diff --git a/gcc/testsuite/g++.dg/cpp1z/aggr-base4.C b/gcc/testsuite/g++.dg/cpp1z/aggr-base4.C
new file mode 100644
index 0000000..fd93f4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/aggr-base4.C
@@ -0,0 +1,21 @@ 
+// { dg-options -std=c++1z }
+// { dg-do run }
+
+struct derived;
+struct base { };
+struct derived : base {
+  int i;
+};
+
+bool flag;
+base f() {
+  flag = true;
+  return base();
+}
+
+derived d2{f(),1};
+
+int main()
+{
+  return (!flag || d2.i != 1);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/aggr-base5.C b/gcc/testsuite/g++.dg/cpp1z/aggr-base5.C
new file mode 100644
index 0000000..85dd365
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/aggr-base5.C
@@ -0,0 +1,14 @@ 
+// { dg-options "-std=c++1z -w" }
+// { dg-do run }
+
+struct A { };
+struct B: A { int i; };
+struct C: A, B { int j; };
+
+constexpr C c = { {}, { {}, 1 }, 2 };
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while(0)
+int main()
+{
+  assert (c.i == 1 && c.j == 2);
+}