diff mbox series

[C++] C++2A P0683R1 - default member initializers for bit-fields (take 2)

Message ID 20170929122655.GJ1701@tucnak
State New
Headers show
Series [C++] C++2A P0683R1 - default member initializers for bit-fields (take 2) | expand

Commit Message

Jakub Jelinek Sept. 29, 2017, 12:26 p.m. UTC
Hi!

On Wed, Sep 27, 2017 at 07:55:20AM -0700, Nathan Sidwell wrote:
> 	1) fix the parsing bug you found and move to (ab)using
> DECL_BIT_FIELD_REPRESENTATIVE
> 
> 	2) the new c++2a feature

And here is the 2) patch, on top of the previous 3 ones. 
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2017-09-29  Jakub Jelinek  <jakub@redhat.com>

	P0683R1 - default member initializers for bit-fields
cp/
	* cp-tree.h (grokbitfield): Add INIT parameter.
	* parser.c (cp_parser_constant_expression): Add STRICT_P argument,
	if true, parse a conditional-expression rather than
	assignment-expression.
	(cp_parser_member_declaration): For C++11 and later pass true
	as STRICT_P to cp_parser_constant_expression.  Parse C++2A bitfield
	NSDMIs.  Adjust grokbitfield caller.  Handle DECL_INITIAL also for
	DECL_C_BIT_FIELDs.
	(cp_parser_objc_class_ivars): Adjust grokbitfield caller.
	* class.c (check_field_decl): Recurse even for DECL_C_BIT_FIELDs.
	(check_field_decls): Call check_field_decl even for DECL_C_BIT_FIELDs.
	* decl2.c (grokbitfield): Add INIT parameter, pass it to
	cp_finish_decl.
	* pt.c (tsubst_decl): Handle DECL_INITIAL for all FIELD_DECLs, not
	just non-bitfields.
testsuite/
	* g++.dg/ext/bitfield6.C: New test.
	* g++.dg/cpp2a/bitfield1.C: New test.
	* g++.dg/cpp2a/bitfield2.C: New test.
	* g++.dg/cpp2a/bitfield3.C: New test.



	Jakub

Comments

Nathan Sidwell Sept. 29, 2017, 2:15 p.m. UTC | #1
On 09/29/2017 05:26 AM, Jakub Jelinek wrote:
> Hi!
> 
> On Wed, Sep 27, 2017 at 07:55:20AM -0700, Nathan Sidwell wrote:
>> 	1) fix the parsing bug you found and move to (ab)using
>> DECL_BIT_FIELD_REPRESENTATIVE
>>
>> 	2) the new c++2a feature
> 
> And here is the 2) patch, on top of the previous 3 ones.
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Ok.  One nit ...

> --- gcc/cp/decl2.c.jj	2017-09-29 09:57:12.586423527 +0200
> +++ gcc/cp/decl2.c	2017-09-29 10:24:26.864372956 +0200
> @@ -978,10 +978,11 @@ grokfield (const cp_declarator *declarat
>   
>   tree
>   grokbitfield (const cp_declarator *declarator,
> -	      cp_decl_specifier_seq *declspecs, tree width,
> +	      cp_decl_specifier_seq *declspecs, tree width, tree init,
>   	      tree attrlist)

The new parm needs documenting.  I notice the current description of 
WIDTH is wonky:
   WIDTH is non-NULL for bit fields only, and is an INTEGER_CST node.
while true, makes one wonder if it could be NULL.  Feel free to make 
that comment saner.

thanks

nathan
diff mbox series

Patch

--- gcc/cp/cp-tree.h.jj	2017-09-29 09:07:33.554063918 +0200
+++ gcc/cp/cp-tree.h	2017-09-29 10:24:26.846373177 +0200
@@ -6159,7 +6159,7 @@  extern void check_member_template		(tree
 extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *,
 		       tree, bool, tree, tree);
 extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *,
-			  tree, tree);
+			  tree, tree, tree);
 extern bool any_dependent_type_attributes_p	(tree);
 extern tree cp_reconstruct_complex_type		(tree, tree);
 extern bool attributes_naming_typedef_ok	(tree);
--- gcc/cp/parser.c.jj	2017-09-29 09:47:43.978411525 +0200
+++ gcc/cp/parser.c	2017-09-29 10:55:51.364348848 +0200
@@ -2089,7 +2089,7 @@  static enum tree_code cp_parser_assignme
 static cp_expr cp_parser_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
 static cp_expr cp_parser_constant_expression
-  (cp_parser *, bool = false, bool * = NULL);
+  (cp_parser *, bool = false, bool * = NULL, bool = false);
 static cp_expr cp_parser_builtin_offsetof
   (cp_parser *);
 static cp_expr cp_parser_lambda_expression
@@ -9626,12 +9626,15 @@  cp_parser_expression (cp_parser* parser,
   If ALLOW_NON_CONSTANT_P a non-constant expression is silently
   accepted.  If ALLOW_NON_CONSTANT_P is true and the expression is not
   constant, *NON_CONSTANT_P is set to TRUE.  If ALLOW_NON_CONSTANT_P
-  is false, NON_CONSTANT_P should be NULL.  */
+  is false, NON_CONSTANT_P should be NULL.  If STRICT_P is true,
+  only parse a conditional-expression, otherwise parse an
+  assignment-expression.  See below for rationale.  */
 
 static cp_expr
 cp_parser_constant_expression (cp_parser* parser,
 			       bool allow_non_constant_p,
-			       bool *non_constant_p)
+			       bool *non_constant_p,
+			       bool strict_p)
 {
   bool saved_integral_constant_expression_p;
   bool saved_allow_non_integral_constant_expression_p;
@@ -9665,16 +9668,27 @@  cp_parser_constant_expression (cp_parser
   parser->allow_non_integral_constant_expression_p
     = (allow_non_constant_p || cxx_dialect >= cxx11);
   parser->non_integral_constant_expression_p = false;
-  /* Although the grammar says "conditional-expression", we parse an
-     "assignment-expression", which also permits "throw-expression"
-     and the use of assignment operators.  In the case that
-     ALLOW_NON_CONSTANT_P is false, we get better errors than we would
+  /* Although the grammar says "conditional-expression", when not STRICT_P,
+     we parse an "assignment-expression", which also permits
+     "throw-expression" and the use of assignment operators.  In the case
+     that ALLOW_NON_CONSTANT_P is false, we get better errors than we would
      otherwise.  In the case that ALLOW_NON_CONSTANT_P is true, it is
      actually essential that we look for an assignment-expression.
      For example, cp_parser_initializer_clauses uses this function to
      determine whether a particular assignment-expression is in fact
      constant.  */
-  expression = cp_parser_assignment_expression (parser);
+  if (strict_p)
+    {
+      /* Parse the binary expressions (logical-or-expression).  */
+      expression = cp_parser_binary_expression (parser, false, false, false,
+						PREC_NOT_OPERATOR, NULL);
+      /* If the next token is a `?' then we're actually looking at
+	 a conditional-expression; otherwise we're done.  */
+      if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY))
+	expression = cp_parser_question_colon_clause (parser, expression);
+    }
+  else
+    expression = cp_parser_assignment_expression (parser);
   /* Restore the old settings.  */
   parser->integral_constant_expression_p
     = saved_integral_constant_expression_p;
@@ -23445,6 +23459,7 @@  cp_parser_member_declaration (cp_parser*
 	{
 	  tree attributes = NULL_TREE;
 	  tree first_attribute;
+	  tree initializer;
 	  bool is_bitfld = false;
 	  bool named_bitfld = false;
 
@@ -23492,18 +23507,48 @@  cp_parser_member_declaration (cp_parser*
 	      cp_lexer_consume_token (parser->lexer);
 
 	      /* Get the width of the bitfield.  */
-	      width = cp_parser_constant_expression (parser);
+	      width = cp_parser_constant_expression (parser, false, NULL,
+						     cxx_dialect >= cxx11);
 
-	      /* Look for attributes that apply to the bitfield after
-		 the `:' token and width.  This is where GCC used to
-		 parse attributes in the past, pedwarn if there is
-		 a std attribute.  */
-	      if (cp_next_tokens_can_be_std_attribute_p (parser))
-		pedwarn (input_location, OPT_Wpedantic,
-			 "ISO C++ allows bit-field attributes only before "
-			 "the %<:%> token");
+	      /* In C++2A and as extension for C++11 and above we allow
+		 default member initializers for bit-fields.  */
+	      initializer = NULL_TREE;
+	      if (cxx_dialect >= cxx11
+		  && (cp_lexer_next_token_is (parser->lexer, CPP_EQ)
+		      || cp_lexer_next_token_is (parser->lexer,
+						 CPP_OPEN_BRACE)))
+		{
+		  location_t loc
+		    = cp_lexer_peek_token (parser->lexer)->location;
+		  if (cxx_dialect < cxx2a
+		      && !in_system_header_at (loc)
+		      && identifier != NULL_TREE)
+		    pedwarn (loc, 0,
+			     "default member initializers for bit-fields "
+			     "only available with -std=c++2a or "
+			     "-std=gnu++2a");
 
-	      late_attributes = cp_parser_attributes_opt (parser);
+		  initializer = cp_parser_save_nsdmi (parser);
+		  if (identifier == NULL_TREE)
+		    {
+		      error_at (loc, "default member initializer for "
+				     "unnamed bit-field");
+		      initializer = NULL_TREE;
+		    }
+		}
+	      else
+		{ 
+		  /* Look for attributes that apply to the bitfield after
+		     the `:' token and width.  This is where GCC used to
+		     parse attributes in the past, pedwarn if there is
+		     a std attribute.  */
+		  if (cp_next_tokens_can_be_std_attribute_p (parser))
+		    pedwarn (input_location, OPT_Wpedantic,
+			     "ISO C++ allows bit-field attributes only "
+			     "before the %<:%> token");
+
+		  late_attributes = cp_parser_attributes_opt (parser);
+		}
 
 	      attributes = chainon (attributes, late_attributes);
 
@@ -23520,13 +23565,12 @@  cp_parser_member_declaration (cp_parser*
 							 sfk_none)
 				   : NULL,
 				   &decl_specifiers,
-				   width,
+				   width, initializer,
 				   attributes);
 	    }
 	  else
 	    {
 	      cp_declarator *declarator;
-	      tree initializer;
 	      tree asm_specification;
 	      int ctor_dtor_or_conv_p;
 
@@ -23745,7 +23789,6 @@  cp_parser_member_declaration (cp_parser*
 	      if (TREE_CODE (decl) == FUNCTION_DECL)
 		cp_parser_save_default_args (parser, decl);
 	      else if (TREE_CODE (decl) == FIELD_DECL
-		       && !DECL_C_BIT_FIELD (decl)
 		       && DECL_INITIAL (decl))
 		/* Add DECL to the queue of NSDMI to be parsed later.  */
 		vec_safe_push (unparsed_nsdmis, decl);
@@ -30086,10 +30129,9 @@  cp_parser_objc_class_ivars (cp_parser* p
 	  attributes = chainon (prefix_attributes, attributes);
 
 	  if (width)
-	      /* Create the bitfield declaration.  */
-	      decl = grokbitfield (declarator, &declspecs,
-				   width,
-				   attributes);
+	    /* Create the bitfield declaration.  */
+	    decl = grokbitfield (declarator, &declspecs,
+				 width, NULL_TREE, attributes);
 	  else
 	    decl = grokfield (declarator, &declspecs,
 			      NULL_TREE, /*init_const_expr_p=*/false,
--- gcc/cp/class.c.jj	2017-09-29 09:57:12.585423540 +0200
+++ gcc/cp/class.c	2017-09-29 10:24:26.863372969 +0200
@@ -3324,7 +3324,7 @@  check_field_decl (tree field,
     {
       for (tree fields = TYPE_FIELDS (type); fields;
 	   fields = DECL_CHAIN (fields))
-	if (TREE_CODE (fields) == FIELD_DECL && !DECL_C_BIT_FIELD (field))
+	if (TREE_CODE (fields) == FIELD_DECL)
 	  any_default_members |= check_field_decl (fields, t,
 						   cant_have_const_ctor,
 						   no_const_asn_ref);
@@ -3636,10 +3636,10 @@  check_field_decls (tree t, tree *access_
 
       /* We set DECL_C_BIT_FIELD in grokbitfield.
 	 If the type and width are valid, we'll also set DECL_BIT_FIELD.  */
-      if ((! DECL_C_BIT_FIELD (x) || ! check_bitfield_decl (x))
-	  && check_field_decl (x, t,
-			       cant_have_const_ctor_p,
-			       no_const_asn_ref_p))
+      if (DECL_C_BIT_FIELD (x))
+	check_bitfield_decl (x);
+
+      if (check_field_decl (x, t, cant_have_const_ctor_p, no_const_asn_ref_p))
 	{
 	  if (any_default_members
 	      && TREE_CODE (t) == UNION_TYPE)
--- gcc/cp/decl2.c.jj	2017-09-29 09:57:12.586423527 +0200
+++ gcc/cp/decl2.c	2017-09-29 10:24:26.864372956 +0200
@@ -978,10 +978,11 @@  grokfield (const cp_declarator *declarat
 
 tree
 grokbitfield (const cp_declarator *declarator,
-	      cp_decl_specifier_seq *declspecs, tree width,
+	      cp_decl_specifier_seq *declspecs, tree width, tree init,
 	      tree attrlist)
 {
-  tree value = grokdeclarator (declarator, declspecs, BITFIELD, 0, &attrlist);
+  tree value = grokdeclarator (declarator, declspecs, BITFIELD,
+			       init != NULL_TREE, &attrlist);
 
   if (value == error_mark_node)
     return NULL_TREE; /* friends went bad.  */
@@ -1036,7 +1037,11 @@  grokbitfield (const cp_declarator *decla
       error ("static member %qD cannot be a bit-field", value);
       return NULL_TREE;
     }
-  cp_finish_decl (value, NULL_TREE, false, NULL_TREE, 0);
+
+  int flags = LOOKUP_IMPLICIT;
+  if (init && DIRECT_LIST_INIT_P (init))
+    flags = LOOKUP_NORMAL;
+  cp_finish_decl (value, init, false, NULL_TREE, flags);
 
   if (width != error_mark_node)
     {
--- gcc/cp/pt.c.jj	2017-09-29 10:24:26.872372858 +0200
+++ gcc/cp/pt.c	2017-09-29 10:24:58.639983280 +0200
@@ -12841,7 +12841,7 @@  tsubst_decl (tree t, tree args, tsubst_f
 		= tsubst_expr (DECL_BIT_FIELD_REPRESENTATIVE (t), args,
 			       complain, in_decl,
 			       /*integral_constant_expression_p=*/true);
-	    else if (DECL_INITIAL (t))
+	    if (DECL_INITIAL (t))
 	      {
 		/* Set up DECL_TEMPLATE_INFO so that we can get at the
 		   NSDMI in perform_member_init.  Still set DECL_INITIAL
--- gcc/testsuite/g++.dg/ext/bitfield6.C.jj	2017-09-29 10:24:26.874372834 +0200
+++ gcc/testsuite/g++.dg/ext/bitfield6.C	2017-09-29 10:24:26.874372834 +0200
@@ -0,0 +1,15 @@ 
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+struct S {
+  char a [[gnu::packed]] = 1;		// { dg-warning "attribute ignored for field of type" }
+  char b [[gnu::packed]] : 8;
+  char c [[gnu::packed]] : 8 = 2;	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+};
+template <typename U>
+struct T {
+  U d [[gnu::packed]] = 1;		// { dg-warning "attribute ignored for field of type" }
+  U e [[gnu::packed]] : 8;
+  U f [[gnu::packed]] : 8 = 2;		// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+};
+T<char> t;
--- gcc/testsuite/g++.dg/cpp2a/bitfield1.C.jj	2017-09-29 10:24:26.874372834 +0200
+++ gcc/testsuite/g++.dg/cpp2a/bitfield1.C	2017-09-29 10:24:26.874372834 +0200
@@ -0,0 +1,77 @@ 
+// P0683R1
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+extern "C" void abort ();
+int a;
+const int b = 0;
+struct S {
+  int c : 5 = 1;		// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int d : 6 { 2 };		// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int e : true ? 7 : a = 3;
+  int f : (true ? 8 : b) = 4;	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int g : (true ? 9 : b) { 5 };	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int h : 1 || new int { 0 };
+};
+#if __cplusplus >= 201402L
+static_assert (S{}.c == 1);
+static_assert (S{}.d == 2);
+static_assert (S{}.e == 0);
+static_assert (S{}.f == 4);
+static_assert (S{}.g == 5);
+static_assert (S{}.h == 0);
+#endif
+template <bool V, int W>
+struct U {
+  int j : W = 7;		// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int k : W { 8 };		// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int l : V ? 7 : a = 3;
+  int m : (V ? W : b) = 9;	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int n : (V ? W : b) { 10 };	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int o : 1 || new int { 0 };
+};
+#if __cplusplus >= 201402L
+static_assert (U<true, 12>{}.j == 7);
+static_assert (U<true, 13>{}.k == 8);
+static_assert (U<true, 10>{}.l == 0);
+static_assert (U<true, 11>{}.m == 9);
+static_assert (U<true, 8>{}.n == 10);
+static_assert (U<true, 7>{}.o == 0);
+#endif
+S s;
+U<true, 10> u;
+
+int
+main ()
+{
+  if (s.c != 1 || s.d != 2 || s.e != 0 || s.f != 4 || s.g != 5 || s.h != 0)
+    abort ();
+  s.c = 47;		// { dg-warning "overflow in conversion from" }
+  s.d = 47 * 2;		// { dg-warning "overflow in conversion from" }
+  s.e = 47 * 4;		// { dg-warning "overflow in conversion from" }
+  s.f = 47 * 8;		// { dg-warning "overflow in conversion from" }
+  s.g = 47 * 16;	// { dg-warning "overflow in conversion from" }
+  s.h = 2;		// { dg-warning "overflow in conversion from" }
+  if (s.c != 15 || s.d != 15 * 2 || s.e != 15 * 4 || s.f != 15 * 8 || s.g != 15 * 16 || s.h != 0)
+    abort ();
+  if (u.j != 7 || u.k != 8 || u.l != 0 || u.m != 9 || u.n != 10 || u.o != 0)
+    abort ();
+  u.j = 47 * 32;	// { dg-warning "overflow in conversion from" }
+  u.k = 47 * 32;	// { dg-warning "overflow in conversion from" }
+  u.l = 47 * 4;		// { dg-warning "overflow in conversion from" }
+  u.m = 47 * 32;	// { dg-warning "overflow in conversion from" }
+  u.n = 47 * 32;	// { dg-warning "overflow in conversion from" }
+  u.o = 2;		// { dg-warning "overflow in conversion from" }
+  if (u.j != 15 * 32 || u.k != 15 * 32 || u.l != 15 * 4 || u.m != 15 * 32 || u.n != 15 * 32 || u.o != 0)
+    abort ();
+  s.c = 15;
+  s.d = 15 * 2;
+  s.e = 15 * 4;
+  s.f = 16 * 8;
+  s.g = 15 * 16;
+  u.j = 15 * 32;
+  u.k = 15 * 32;
+  u.l = 15 * 4;
+  u.m = 15 * 32;
+  u.n = 15 * 32;
+}
--- gcc/testsuite/g++.dg/cpp2a/bitfield2.C.jj	2017-09-29 10:24:26.875372822 +0200
+++ gcc/testsuite/g++.dg/cpp2a/bitfield2.C	2017-09-29 10:24:26.875372822 +0200
@@ -0,0 +1,26 @@ 
+// P0683R1
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+int a;
+const int b = 0;
+struct T {
+  int i : true ? 10 : b = 6;	// { dg-error "assignment of read-only variable" }
+  int : 4 = 10;			// { dg-error "default member initializer for unnamed bit-field" }
+  int : 5 = a + b;		// { dg-error "default member initializer for unnamed bit-field" }
+};
+template <bool V, int W>
+struct U {
+  int j : W = 7;		// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int k : W { 8 };		// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int l : V ? 7 : a = 3;	// { dg-error "modification of .a. is not a constant expression" }
+				// { dg-error "width not an integer constant" "" { target *-*-* } .-1 }
+  int m : (V ? W : b) = 9;	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+				// { dg-error "zero width for bit-field" "" { target *-*-* } .-1 }
+  int n : (V ? W : b) { 10 };	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+				// { dg-error "zero width for bit-field" "" { target *-*-* } .-1 }
+  int o : 1 || new int { 0 };
+  int : 4 = 10;			// { dg-error "default member initializer for unnamed bit-field" }
+  int : 5 = a + b;		// { dg-error "default member initializer for unnamed bit-field" }
+};
+U<false, 10> u;
--- gcc/testsuite/g++.dg/cpp2a/bitfield3.C.jj	2017-09-29 10:24:26.875372822 +0200
+++ gcc/testsuite/g++.dg/cpp2a/bitfield3.C	2017-09-29 10:24:26.875372822 +0200
@@ -0,0 +1,55 @@ 
+// P0683R1
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+extern "C" void abort ();
+
+int
+foo ()
+{
+  return 2;
+}
+
+int a = foo ();
+const int b = 0;
+struct S {
+  int c : 5 = 2 * a;			// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int d : 6 { c + a };			// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+					// { dg-warning "narrowing conversion of" "" { target *-*-* } .-1 }
+  int e : true ? 7 : a = 3;
+  int f : (true ? 8 : b) = d + a;	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int g : (true ? 9 : b) { f + a };	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+					// { dg-warning "narrowing conversion of" "" { target *-*-* } .-1 }
+  int h : 1 || new int { 0 };
+  int i = g + a;
+};
+S c;
+template <bool V, int W>
+struct U {
+  int j : W = 3 * a;			// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int k : W { j + a };			// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+					// { dg-warning "narrowing conversion of" "" { target *-*-* } .-1 }
+  int l : V ? 7 : a = 3;
+  int m : (V ? W : b) = k + a;		// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+  int n : (V ? W : b) { m + a };	// { dg-warning "default member initializers for bit-fields only available with" "" { target c++17_down } }
+					// { dg-warning "narrowing conversion of" "" { target *-*-* } .-1 }
+  int o : 1 || new int { 0 };
+  int p = n + a;
+};
+U<true, 10> d;
+
+int
+main ()
+{
+  a = 1;
+  if (c.c != 4 || c.d != 6 || c.e != 0 || c.f != 8 || c.g != 10 || c.h != 0 || c.i != 12)
+    abort ();
+  if (d.j != 6 || d.k != 8 || d.l != 0 || d.m != 10 || d.n != 12 || d.o != 0 || d.p != 14)
+    abort ();
+  S s;
+  U<true, 10> u;
+  if (s.c != 2 || s.d != 3 || s.f != 4 || s.g != 5 || s.i != 6)
+    abort ();
+  if (u.j != 3 || u.k != 4 || u.m != 5 || u.n != 6 || u.p != 7)
+    abort ();
+}