[PATCH] Fix bugs in C99 designated initializers

Jakub Jelinek jakub@redhat.com
Fri Dec 1 09:48:00 GMT 2000


Hi!

This patch fixes a couple of bugs I found in the designated initializers
support. E.g. C99 designator lists were supported basically in c-parse.in
only. C99 requires the start of the designator list to move current object
pointer to the closest braces level and each consecutive designator goes one
level down from that level (previous code would just apply them over each
other at the level of current object).
Also, add_pending_init/pending_init_member were just comparing tree pointers
to purpose constants, which works well for field names but does not for
array indexes. This caused e.g. strange results like in:

int a[10] = { 10, [4] = 15, [2] = 12, [4] = 14, [7] = 17 };
int b[10] = { 10, [4] = 15, [2] = 12, [3] = 13, 14, [7] = 17 };

a[4] was 15 and b[4] was 14 (due to different position in the AVL tree).

What is not solved yet is that once we emit some part of array/structure, we
cannot write back (I think this has been discussed here already).

I'm also not sure whether overwriting of already initialized fields should
be allowed in C90. This patch allows it in -std=c99 resp. -std=gnu99 only,
so the above two lines will not compile unless one of these std switches are
passed to it (but it still errors if all fields are consecutive and then
comes an initializer to already output field). If overwriting should be
allowed in C90 as well, then I could just delete pending_init_member and
associated logic in output_init_element.

Bootstrap pending, ok to commit if it does not introduce any regressions?

2000-12-01  Jakub Jelinek  <jakub@redhat.com>

	* c-typeck.c (designator_depth): New variable.
	(really_start_incremental_init, push_init_level,
	process_init_element): Clear it.
	(set_designator): New function.
	(set_init_index): Use it.  Bump designator_depth if successful.
	(set_init_label): Likewise.  Error if constructor_type is not
	record nor union.
	(pending_init_member): Cannot compare tree constant pointers.
	Optimize search for field members.
	(add_pending_init): Likewise.
	Allow overwriting previously initialized field.
	(output_init_element): Don't check for duplicates in C99 mode.

	* gcc.dg/c99-init-1.c: New test.
	* gcc.dg/c99-init-2.c: New test.

--- gcc/c-typeck.c.jj	Fri Dec  1 13:55:39 2000
+++ gcc/c-typeck.c	Fri Dec  1 17:32:12 2000
@@ -76,6 +76,7 @@ static void warning_init		PARAMS ((const
 static tree digest_init			PARAMS ((tree, tree, int, int));
 static void output_init_element		PARAMS ((tree, tree, tree, int));
 static void output_pending_init_elements PARAMS ((int));
+static int set_designator		PARAMS ((int));
 static void add_pending_init		PARAMS ((tree, tree));
 static int pending_init_member		PARAMS ((tree));
 
@@ -4943,6 +4944,9 @@ static const char *constructor_asmspec;
 /* Nonzero if this is an initializer for a top-level decl.  */
 static int constructor_top_level;
 
+/* Nesting depth of designator list.  */
+static int designator_depth;
+
 
 /* This stack has a level for each implicit or explicit level of
    structuring in the initializer, including the outermost one.  It
@@ -5142,6 +5146,7 @@ really_start_incremental_init (type)
   constructor_elements = 0;
   constructor_pending_elts = 0;
   constructor_type = type;
+  designator_depth = 0;
 
   if (TREE_CODE (constructor_type) == RECORD_TYPE
       || TREE_CODE (constructor_type) == UNION_TYPE)
@@ -5231,6 +5236,8 @@ push_init_level (implicit)
   constructor_depth = SPELLING_DEPTH ();
   constructor_elements = 0;
   constructor_pending_elts = 0;
+  if (!implicit)
+    designator_depth = 0;
 
   /* Don't die if an entire brace-pair level is superfluous
      in the containing level.  */
@@ -5439,6 +5446,72 @@ pop_init_level (implicit)
   return constructor;
 }
 
+/* Common handling for both array range and field name designators.
+   ARRAY argument is non-zero for array ranges.  Returns zero for success.  */
+
+static int
+set_designator (array)
+     int array;
+{
+  tree subtype;
+  enum tree_code subcode;
+
+  /* Don't die if an entire brace-pair level is superfluous
+     in the containing level.  */
+  if (constructor_type == 0)
+    return 1;
+
+  if (!designator_depth)
+    {
+      /* Designator list starts at the level of closest explicit
+	 braces.  */
+      while (constructor_stack->implicit)
+	process_init_element (pop_init_level (1));
+      return 0;
+    }
+
+  if (constructor_range_end)
+    {
+      error_init ("range of elements to initialize not at the end of designators");
+      constructor_range_end = 0;
+    }
+
+  if (constructor_no_implicit)
+    {
+      error_init ("initialization designators may not nest");
+      return 1;
+    }
+
+  if (TREE_CODE (constructor_type) == RECORD_TYPE
+      || TREE_CODE (constructor_type) == UNION_TYPE)
+    {
+      subtype = TREE_TYPE (constructor_fields);
+      if (subtype != error_mark_node)
+	subtype = TYPE_MAIN_VARIANT (subtype);
+    }
+  else if (TREE_CODE (constructor_type) == ARRAY_TYPE)
+    {
+      subtype = TYPE_MAIN_VARIANT (TREE_TYPE (constructor_type));
+    }
+  else
+    abort ();
+
+  subcode = TREE_CODE (subtype);
+  if (array && subcode != ARRAY_TYPE)
+    {
+      error_init ("array index in non-array initializer");
+      return 1;
+    }
+  else if (!array && subcode != RECORD_TYPE && subcode != UNION_TYPE)
+    {
+      error_init ("field name not in record or union initializer");
+      return 1;
+    }
+
+  push_init_level (1);
+  return 0;
+}
+
 /* Within an array initializer, specify the next index to be initialized.
    FIRST is that index.  If LAST is nonzero, then initialize a range
    of indices, running from FIRST through LAST.  */
@@ -5447,6 +5520,9 @@ void
 set_init_index (first, last)
      tree first, last;
 {
+  if (set_designator (1))
+    return;
+
   while ((TREE_CODE (first) == NOP_EXPR
 	  || TREE_CODE (first) == CONVERT_EXPR
 	  || TREE_CODE (first) == NON_LVALUE_EXPR)
@@ -5466,18 +5542,20 @@ set_init_index (first, last)
     error_init ("nonconstant array index in initializer");
   else if (last != 0 && TREE_CODE (last) != INTEGER_CST)
     error_init ("nonconstant array index in initializer");
-  else if (! constructor_unfilled_index)
+  else if (TREE_CODE (constructor_type) != ARRAY_TYPE)
     error_init ("array index in non-array initializer");
   else if (tree_int_cst_lt (first, constructor_unfilled_index))
     error_init ("duplicate array index in initializer");
   else
     {
       constructor_index = convert (bitsizetype, first);
+      constructor_range_end = 0;
 
       if (last != 0 && tree_int_cst_lt (last, first))
 	error_init ("empty index range in initializer");
-      else
-	constructor_range_end = last ? convert (bitsizetype, last) : 0;
+      else if (last)
+	constructor_range_end = convert (bitsizetype, last);
+      designator_depth++;
     }
 }
 
@@ -5490,11 +5568,16 @@ set_init_label (fieldname)
   tree tail;
   int passed = 0;
 
-  /* Don't die if an entire brace-pair level is superfluous
-     in the containing level.  */
-  if (constructor_type == 0)
+  if (set_designator (0))
     return;
 
+  if (TREE_CODE (constructor_type) != RECORD_TYPE
+      && TREE_CODE (constructor_type) != UNION_TYPE)
+    {
+      error_init ("field name not in record or union initializer");
+      return;
+    }
+    
   for (tail = TYPE_FIELDS (constructor_type); tail;
        tail = TREE_CHAIN (tail))
     {
@@ -5511,7 +5594,10 @@ set_init_label (fieldname)
     error ("field `%s' already initialized",
 	   IDENTIFIER_POINTER (fieldname));
   else
-    constructor_fields = tail;
+    {
+      constructor_fields = tail;
+      designator_depth++;
+    }
 }
 
 /* Add a new initializer to the tree of pending initializers.  PURPOSE
@@ -5534,24 +5620,32 @@ add_pending_init (purpose, value)
 	  p = *q;
 	  if (tree_int_cst_lt (purpose, p->purpose))
 	    q = &p->left;
-	  else if (p->purpose != purpose)
+	  else if (tree_int_cst_lt (p->purpose, purpose))
 	    q = &p->right;
 	  else
-	    abort ();
+	    {
+	      p->value = value;
+	      return;
+	    }
 	}
     }
   else
     {
+      tree bitpos;
+
+      bitpos = bit_position (purpose);
       while (*q != NULL)
 	{
 	  p = *q;
-	  if (tree_int_cst_lt (bit_position (purpose),
-			       bit_position (p->purpose)))
+	  if (tree_int_cst_lt (bitpos, bit_position (p->purpose)))
 	    q = &p->left;
 	  else if (p->purpose != purpose)
 	    q = &p->right;
 	  else
-	    abort ();
+	    {
+	      p->value = value;
+	      return;
+	    }
 	}
     }
 
@@ -5733,22 +5827,24 @@ pending_init_member (field)
     {
       while (p)
 	{
-	  if (field == p->purpose)
-	    return 1;
-	  else if (tree_int_cst_lt (field, p->purpose))
+	  if (tree_int_cst_lt (field, p->purpose))
 	    p = p->left;
-	  else
+	  else if (tree_int_cst_lt (p->purpose, field))
 	    p = p->right;
+	  else
+	    return 1;
 	}
     }
   else
     {
+      tree bitpos;
+
+      bitpos = bit_position (field);
       while (p)
 	{
 	  if (field == p->purpose)
 	    return 1;
-	  else if (tree_int_cst_lt (bit_position (field),
-				    bit_position (p->purpose)))
+	  else if (tree_int_cst_lt (bitpos, bit_position (p->purpose)))
 	    p = p->left;
 	  else
 	    p = p->right;
@@ -5810,8 +5906,9 @@ output_init_element (value, type, field,
   /* If this element duplicates one on constructor_pending_elts,
      print a message and ignore it.  Don't do this when we're
      processing elements taken off constructor_pending_elts,
-     because we'd always get spurious errors.  */
-  if (pending)
+     because we'd always get spurious errors.
+     C99 allows this though via designated initializers.  */
+  if (pending && !flag_isoc99)
     {
       if (TREE_CODE (constructor_type) == RECORD_TYPE
 	  || TREE_CODE (constructor_type) == UNION_TYPE
@@ -6042,6 +6139,8 @@ process_init_element (value)
 {
   tree orig_value = value;
   int string_flag = value != 0 && TREE_CODE (value) == STRING_CST;
+
+  designator_depth = 0;
 
   /* Handle superfluous braces around string cst as in
      char x[] = {"foo"}; */
--- gcc/testsuite/gcc.dg/c99-init-1.c.jj	Fri Dec  1 17:35:22 2000
+++ gcc/testsuite/gcc.dg/c99-init-1.c	Fri Dec  1 18:00:48 2000
@@ -0,0 +1,56 @@
+/* Test for C99 designated initializers */
+/* Origin: Jakub Jelinek <jakub@redhat.com> */
+/* { dg-do run } */
+/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */
+
+typedef __SIZE_TYPE__ size_t;
+extern int memcmp (const void *, const void *, size_t);
+extern void abort (void);
+extern void exit (int);
+
+int a[10] = { 10, 0, 12, 13, 14, 0, 0, 17, 0, 0 };
+int b[10] = { 10, [4] = 15, [2] = 12, [4] = 14, [7] = 17 };
+int c[10] = { 10, [4] = 15, [2] = 12, [3] = 13, 14, [7] = 17 };
+struct A {
+  int B;
+  short C[2];
+};
+struct A d[] = { { 0, { 1, 2 } }, { 0, { 0, 0 } }, { 10, { 11, 12 } } };
+struct A e[] = { 0, 1, 2, [2] = 10, 11, 12 };
+struct A f[] = { 0, 1, 2, [2].C = 11, 12, 13 };
+struct A g[] = { 0, 1, 2, [2].C[1] = 12, 13, 14 };
+struct A h[] = { 0, 1, 2, [2] = { .C[1] = 12 }, 13, 14 };
+struct A i[] = { 0, 1, 2, [2] = { .C = { [1] = 12 } }, 13, 14 };
+union D {
+  int E;
+  double F;
+  struct A G;
+};
+union D j[] = { [2] = 1, [2].F = 1.0, [1].G.C[1] = 4 };
+
+int main (void)
+{
+  if (b[3])
+    abort ();
+  b[3] = 13;
+  if (memcmp (a, b, sizeof (a)) || memcmp (a, c, sizeof (a)))
+    abort ();
+  if (memcmp (d, e, sizeof (d)) || sizeof (d) != sizeof (e))
+    abort ();
+  if (f[2].B != 0 || g[2].B != 0 || g[2].C[0] != 0)
+    abort ();
+  if (memcmp (g, h, sizeof (g)) || memcmp (g, i, sizeof (g)))
+    abort ();
+  f[2].B = 10;
+  g[2].B = 10;
+  g[2].C[0] = 11;
+  if (memcmp (d, f, sizeof (d)) || memcmp (d, g, sizeof (d)))
+    abort ();
+  if (f[3].B != 13 || g[3].B != 13 || g[3].C[0] != 14)
+    abort ();
+  if (j[0].E || j[1].G.B || j[1].G.C[0] || j[1].G.C[1] != 4)
+    abort ();
+  if (j[2].E || j[3].E || j[4].F != 1.0)
+    abort ();
+  exit (0);
+}
--- gcc/testsuite/gcc.dg/c99-init-2.c.jj	Fri Dec  1 18:01:29 2000
+++ gcc/testsuite/gcc.dg/c99-init-2.c	Fri Dec  1 18:10:42 2000
@@ -0,0 +1,12 @@
+/* Test for C99 designated initializer errors */
+/* Origin: Jakub Jelinek <jakub@redhat.com> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */
+
+struct A {
+  int B;
+  short C[2];
+};
+struct A a = { [2] = 1 };	/* { dg-error "(array index in non-array)|(near initialization)" } */
+struct A b[] = { .B = 1 };	/* { dg-error "field name not in record" } */
+struct A c[] = { [0].D = 1 };	/* { dg-error "unknown field" } */

	Jakub


More information about the Gcc-patches mailing list