Tue Mar 30 14:25:28 2021 UTC ()
lint: rewrite handling of initializations, fixing several bugs

The previous implementation had a wrong model of how initialization
happens in C99, its assertions failed in all kind of edge cases and it
was not possible to fix the remaining bugs one at a time without running
into even more obscure assertion failures.

The debug logging was detailed but did not help to clarify the
situation.  After about 20 failed attempts at fixing the small details I
decided to start all over and rewrite the initialization code from
scratch.  I left the low-level parts of handling designators, the code
that is independent of brace_level and the high-level parts of how the
parser calls into this module.  Everything else is completely new.

The concept of a brace level stays since that is how C99 describes
initialization.  The previous code could not handle multi-level
designations (see d_init_pop_member.c).  There are no more assertion
failures in the initialization code.

Some TODO comments have been left in the tests to keep the line numbers
the same in this commit.  These will be cleaned up in a follow-up
commit.

The new implementation does not handle initialization with "missing"
braces.  This is an edge case that both GCC and Clang warn about, so it
is not widely used.  If necessary, it may be added later.

The new implementation does not use any global variables in the vast
majority of the functions, to make all dependencies and possible
modifications obvious.


(rillig)
diff -r1.6 -r1.7 src/tests/usr.bin/xlint/lint1/d_c99_bool.c
diff -r1.6 -r1.7 src/tests/usr.bin/xlint/lint1/d_init_pop_member.c
diff -r1.23 -r1.24 src/tests/usr.bin/xlint/lint1/d_c99_init.c
diff -r1.16 -r1.17 src/tests/usr.bin/xlint/lint1/d_c99_init.exp
diff -r1.2 -r1.3 src/tests/usr.bin/xlint/lint1/d_init_array_using_string.c
diff -r1.2 -r1.3 src/tests/usr.bin/xlint/lint1/d_init_array_using_string.exp
diff -r1.5 -r1.6 src/tests/usr.bin/xlint/lint1/d_init_pop_member.exp
diff -r1.206 -r1.207 src/usr.bin/xlint/lint1/cgram.y
diff -r1.166 -r1.167 src/usr.bin/xlint/lint1/decl.c
diff -r1.94 -r1.95 src/usr.bin/xlint/lint1/externs1.h
diff -r1.178 -r1.179 src/usr.bin/xlint/lint1/init.c

cvs diff -r1.6 -r1.7 src/tests/usr.bin/xlint/lint1/d_c99_bool.c (expand / switch to context diff)
--- src/tests/usr.bin/xlint/lint1/d_c99_bool.c 2021/02/21 09:07:58 1.6
+++ src/tests/usr.bin/xlint/lint1/d_c99_bool.c 2021/03/30 14:25:28 1.7
@@ -1,4 +1,4 @@
-/*	$NetBSD: d_c99_bool.c,v 1.6 2021/02/21 09:07:58 rillig Exp $	*/
+/*	$NetBSD: d_c99_bool.c,v 1.7 2021/03/30 14:25:28 rillig Exp $	*/
 # 3 "d_c99_bool.c"
 
 /*
@@ -9,7 +9,7 @@
  * invoke undefined behavior.
  */
 
-/* Below, each wrong assertion produces "negative array dimension" [20]. */
+/* Below, each false statement produces "negative array dimension" [20]. */
 
 int int_0_converts_to_false[(_Bool)0 ? -1 : 1];
 int int_0_converts_to_true_[(_Bool)0 ? 1 : -1];			/* expect: 20 */

cvs diff -r1.6 -r1.7 src/tests/usr.bin/xlint/lint1/d_init_pop_member.c (expand / switch to context diff)
--- src/tests/usr.bin/xlint/lint1/d_init_pop_member.c 2021/03/19 17:40:37 1.6
+++ src/tests/usr.bin/xlint/lint1/d_init_pop_member.c 2021/03/30 14:25:28 1.7
@@ -1,4 +1,4 @@
-/*	$NetBSD: d_init_pop_member.c,v 1.6 2021/03/19 17:40:37 rillig Exp $	*/
+/*	$NetBSD: d_init_pop_member.c,v 1.7 2021/03/30 14:25:28 rillig Exp $	*/
 # 3 "d_init_pop_member.c"
 
 /*
@@ -35,7 +35,7 @@
 
 void func(void)
 {
-	struct state st = {
+	struct state st = {	/* expect: set but not used */
 	    .capital.mayor.hobbies.dancing = 1,
 	    /*
 	     * Since 2015-07-28:
@@ -45,7 +45,7 @@
 	     * Before init.c 1.52 from 2020-01-01:
 	     * wrong "warning: bit-field initializer does not fit [180]"
 	     */
-	    .capital.mayor.favorite_color.green = 0xFF,	/*FIXME*//* expect: 101 */
+	    .capital.mayor.favorite_color.green = 0xFF,
 	    /*
 	     * Since 2015-07-28:
 	     * wrong "undefined struct/union member: capital [101]"
@@ -54,6 +54,6 @@
 	     * Before init.c 1.52 from 2020-01-01:
 	     * wrong "warning: bit-field initializer does not fit [180]"
 	     */
-	    .capital.mayor.favorite_color.red = 0xFF, /*FIXME*//* expect: 101 */
+	    .capital.mayor.favorite_color.red = 0xFF,
 	};
 }

cvs diff -r1.23 -r1.24 src/tests/usr.bin/xlint/lint1/d_c99_init.c (expand / switch to context diff)
--- src/tests/usr.bin/xlint/lint1/d_c99_init.c 2021/03/29 22:42:10 1.23
+++ src/tests/usr.bin/xlint/lint1/d_c99_init.c 2021/03/30 14:25:28 1.24
@@ -1,4 +1,4 @@
-/*	$NetBSD: d_c99_init.c,v 1.23 2021/03/29 22:42:10 rillig Exp $	*/
+/*	$NetBSD: d_c99_init.c,v 1.24 2021/03/30 14:25:28 rillig Exp $	*/
 # 3 "d_c99_init.c"
 
 /*
@@ -23,7 +23,7 @@
 int scalar_with_too_many_initializers = { 3, 5 };	/* expect: 174 */
 
 
-// See init_using_expr, 'handing over to ASSIGN'.
+// See init_expr, 'handing over to ASSIGN'.
 void
 struct_initialization_via_assignment(any arg)
 {
@@ -32,13 +32,13 @@
 }
 
 
-// See init_using_expr, initstack_string.
+// See init_expr, initialization_init_array_using_string.
 char static_duration[] = "static duration";
 signed char static_duration_signed[] = "static duration";
 unsigned char static_duration_unsigned[] = "static duration";
 int static_duration_wchar[] = L"static duration";
 
-// See init_using_expr.
+// See init_expr.
 void
 initialization_by_braced_string(void)
 {
@@ -80,7 +80,7 @@
 	444,			/* expect: too many array initializers */
 };
 
-// See initstack_push, 'extending array of unknown size'.
+// See initialization_set_set_of_unknown_array.
 int array_of_unknown_size[] = {
 	111,
 	222,
@@ -135,8 +135,8 @@
 struct point point_with_mixed_designators = {
 	.x = 3,
 	4,
-	// FIXME: assertion failure '== ARRAY'
-	// 5,
+	/* TODO: remove me */
+	5,			/* expect: too many struct/union initializers */
 	.x = 3,
 };
 
@@ -211,8 +211,8 @@
  * structs.
  */
 struct geometry geometry = {
-	// FIXME: assertion "istk->i_type != NULL" failed in initstack_push
-	//.pentagons[0].points[4].x = 1,
+	/* TODO: remove me */
+	.pentagons[0].points[4].x = 1,
 	.points[0][0][0] = { 0, 0 },
 	.points[2][4][1] = {301, 302 },
 	/* TODO: expect+1: array index 3 must be between 0 and 2 */
@@ -239,8 +239,8 @@
 };
 
 char message_with_suffix[] = {
-	"message",
-	/* expect+1: too many array initializers */
+	"message",		/* expect: illegal combination */
+	/* */
 	'\n',
 };
 
@@ -279,7 +279,7 @@
 	int a[3], b;
 } c99_6_7_8_p28_example5[] = {
 	{ 1 },
-	2,
+	2,			/* XXX *//* expect: cannot initialize */
 };
 
 short c99_6_7_8_p29_example6a[4][3][2] = {
@@ -330,8 +330,8 @@
 }
 
 struct point unknown_member_name_beginning = {
-	// FIXME: assertion "bl->bl_type != NULL" failed in initialization_push
-	// .r = 5,
+	/* TODO: remove me */
+	.r = 5,			/* expect: undefined struct/union member: r */
 	.x = 4,
 	.y = 3,
 };
@@ -354,13 +354,31 @@
 };
 
 union value unknown_union_member_name_first = {
-	// FIXME: assertion "bl->bl_type != NULL" failed in initialization_push
-	// .unknown_value = 4,
+	/* TODO: remove me */
+	.unknown_value = 4,	/* expect: undefined struct/union member */
 	.int_value = 3,
 };
 
 union value unknown_union_member_name_second = {
 	.int_value = 3,
-	// FIXME: assertion "bl->bl_type->t_tspec == ARRAY" failed in brace_level_extend_if_array_of_unknown_size
-	// .unknown_value = 4,
+	/* TODO: remove me */
+	.unknown_value = 4,	/* expect: undefined struct/union member */
+};
+
+struct point designators_with_subscript = {
+	[0] = 3,		/* expect: only for arrays */
+	.member[0][0].member = 4, /* expect: undefined struct/union member */
+	.x.y.z = 5,	/* intentionally not caught, see designator_look_up */
+};
+
+struct {
+	int : 16;
+} struct_with_only_unnamed_members = {	/* expect: has no named members */
+	123,		/* expect: too many struct/union initializers */
+};
+
+union {
+	int : 16;
+} union_with_only_unnamed_members = {	/* expect: has no named members */
+	123,		/* expect: too many struct/union initializers */
 };

cvs diff -r1.16 -r1.17 src/tests/usr.bin/xlint/lint1/Attic/d_c99_init.exp (expand / switch to context diff)
--- src/tests/usr.bin/xlint/lint1/Attic/d_c99_init.exp 2021/03/29 22:36:31 1.16
+++ src/tests/usr.bin/xlint/lint1/Attic/d_c99_init.exp 2021/03/30 14:25:28 1.17
@@ -1,10 +1,21 @@
 d_c99_init.c(23): error: too many initializers [174]
 d_c99_init.c(63): error: cannot initialize 'pointer to const void' from 'struct any' [185]
 d_c99_init.c(80): error: too many array initializers, expected 3 [173]
+d_c99_init.c(139): error: too many struct/union initializers [172]
 d_c99_init.c(145): error: syntax error 'named member must only be used with struct/union' [249]
 d_c99_init.c(232): error: too many struct/union initializers [172]
 d_c99_init.c(238): warning: illegal combination of integer (char) and pointer (pointer to char) [183]
-d_c99_init.c(244): error: too many array initializers, expected 8 [173]
+d_c99_init.c(242): warning: illegal combination of integer (char) and pointer (pointer to char) [183]
+d_c99_init.c(282): error: cannot initialize 'struct <unnamed>' from 'int' [185]
 d_c99_init.c(324): error: duplicate case in switch: 0 [199]
+d_c99_init.c(334): error: undefined struct/union member: r [101]
 d_c99_init.c(341): error: undefined struct/union member: r [101]
 d_c99_init.c(348): error: undefined struct/union member: r [101]
+d_c99_init.c(358): error: undefined struct/union member: unknown_value [101]
+d_c99_init.c(365): error: undefined struct/union member: unknown_value [101]
+d_c99_init.c(369): error: syntax error 'designator '[...]' is only for arrays' [249]
+d_c99_init.c(370): error: undefined struct/union member: member [101]
+d_c99_init.c(376): warning: structure has no named members [65]
+d_c99_init.c(377): error: too many struct/union initializers [172]
+d_c99_init.c(382): warning: union has no named members [65]
+d_c99_init.c(383): error: too many struct/union initializers [172]

cvs diff -r1.2 -r1.3 src/tests/usr.bin/xlint/lint1/d_init_array_using_string.c (expand / switch to context diff)
--- src/tests/usr.bin/xlint/lint1/d_init_array_using_string.c 2021/03/23 22:58:08 1.2
+++ src/tests/usr.bin/xlint/lint1/d_init_array_using_string.c 2021/03/30 14:25:28 1.3
@@ -1,4 +1,4 @@
-/*	$NetBSD: d_init_array_using_string.c,v 1.2 2021/03/23 22:58:08 rillig Exp $	*/
+/*	$NetBSD: d_init_array_using_string.c,v 1.3 2021/03/30 14:25:28 rillig Exp $	*/
 # 3 "d_init_array_using_string.c"
 
 /*
@@ -56,8 +56,8 @@
 	};
 
 	struct cs_ws type_mismatch = {
-	    	L"",		/* expect: illegal combination */
-	    	"",		/* expect: illegal combination */
+	    	L"",		/* expect: cannot initialize */
+	    	"",		/* expect: cannot initialize */
 	};
 
 	struct cs_ws no_terminating_null = {
@@ -74,4 +74,6 @@
 		{ "" },
 		{ L"" },
 	};
+	/* expect-3: illegal combination of integer (char) and pointer (pointer to char) */
+	/* expect-3: illegal combination of integer (int) and pointer (pointer to int) */
 }

cvs diff -r1.2 -r1.3 src/tests/usr.bin/xlint/lint1/Attic/d_init_array_using_string.exp (expand / switch to context diff)
--- src/tests/usr.bin/xlint/lint1/Attic/d_init_array_using_string.exp 2021/03/23 22:58:08 1.2
+++ src/tests/usr.bin/xlint/lint1/Attic/d_init_array_using_string.exp 2021/03/30 14:25:28 1.3
@@ -2,7 +2,9 @@
 d_init_array_using_string.c(17): warning: illegal pointer combination (pointer to const int) and (pointer to char), op = [124]
 d_init_array_using_string.c(34): warning: illegal pointer combination [184]
 d_init_array_using_string.c(35): warning: illegal pointer combination [184]
-d_init_array_using_string.c(59): warning: illegal combination of integer (char) and pointer (pointer to int) [183]
-d_init_array_using_string.c(60): warning: illegal combination of integer (char) and pointer (pointer to char) [183]
+d_init_array_using_string.c(59): error: cannot initialize 'array[10] of const char' from 'pointer to int' [185]
+d_init_array_using_string.c(60): error: cannot initialize 'array[10] of const int' from 'pointer to char' [185]
 d_init_array_using_string.c(69): warning: non-null byte ignored in string initializer [187]
 d_init_array_using_string.c(70): warning: non-null byte ignored in string initializer [187]
+d_init_array_using_string.c(74): warning: illegal combination of integer (char) and pointer (pointer to char) [183]
+d_init_array_using_string.c(75): warning: illegal combination of integer (int) and pointer (pointer to int) [183]

cvs diff -r1.5 -r1.6 src/tests/usr.bin/xlint/lint1/Attic/d_init_pop_member.exp (expand / switch to context diff)
--- src/tests/usr.bin/xlint/lint1/Attic/d_init_pop_member.exp 2021/03/21 20:44:59 1.5
+++ src/tests/usr.bin/xlint/lint1/Attic/d_init_pop_member.exp 2021/03/30 14:25:28 1.6
@@ -1,2 +1 @@
-d_init_pop_member.c(48): error: undefined struct/union member: capital [101]
+d_init_pop_member.c(38): warning: st set but not used in function func [191]
-d_init_pop_member.c(57): error: undefined struct/union member: capital [101]

cvs diff -r1.206 -r1.207 src/usr.bin/xlint/lint1/cgram.y (expand / switch to context diff)
--- src/usr.bin/xlint/lint1/cgram.y 2021/03/29 20:39:18 1.206
+++ src/usr.bin/xlint/lint1/cgram.y 2021/03/30 14:25:28 1.207
@@ -1,5 +1,5 @@
 %{
-/* $NetBSD: cgram.y,v 1.206 2021/03/29 20:39:18 rillig Exp $ */
+/* $NetBSD: cgram.y,v 1.207 2021/03/30 14:25:28 rillig Exp $ */
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
@@ -35,7 +35,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: cgram.y,v 1.206 2021/03/29 20:39:18 rillig Exp $");
+__RCSID("$NetBSD: cgram.y,v 1.207 2021/03/30 14:25:28 rillig Exp $");
 #endif
 
 #include <limits.h>
@@ -1345,7 +1345,7 @@
 
 initializer:			/* C99 6.7.8 "Initialization" */
 	  expr				%prec T_COMMA {
-		init_using_expr($1);
+		init_expr($1);
 	  }
 	| init_lbrace init_rbrace {
 		/* XXX: Empty braces are not covered by C99 6.7.8. */

cvs diff -r1.166 -r1.167 src/usr.bin/xlint/lint1/decl.c (expand / switch to context diff)
--- src/usr.bin/xlint/lint1/decl.c 2021/03/27 22:13:55 1.166
+++ src/usr.bin/xlint/lint1/decl.c 2021/03/30 14:25:28 1.167
@@ -1,4 +1,4 @@
-/* $NetBSD: decl.c,v 1.166 2021/03/27 22:13:55 rillig Exp $ */
+/* $NetBSD: decl.c,v 1.167 2021/03/30 14:25:28 rillig Exp $ */
 
 /*
  * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
@@ -38,7 +38,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: decl.c,v 1.166 2021/03/27 22:13:55 rillig Exp $");
+__RCSID("$NetBSD: decl.c,v 1.167 2021/03/30 14:25:28 rillig Exp $");
 #endif
 
 #include <sys/param.h>
@@ -1913,7 +1913,7 @@
 
 	check_type(dsym);
 
-	if (initflg && !(*current_initerr() = check_init(dsym)))
+	if (initflg && !check_init(dsym))
 		dsym->s_def = DEF;
 
 	/*
@@ -2043,9 +2043,6 @@
 		} else
 			declare_local(decl, initflg);
 	}
-
-	if (initflg && !*current_initerr())
-		initstack_init();
 }
 
 /*
@@ -2416,7 +2413,6 @@
 	if (initflg) {
 		/* cannot initialize parameter: %s */
 		error(52, sym->s_name);
-		*current_initerr() = true;
 	}
 
 	if ((t = sym->s_type->t_tspec) == ARRAY) {
@@ -2751,7 +2747,7 @@
 
 	}
 
-	if (initflg && !(*current_initerr() = check_init(dsym))) {
+	if (initflg && !check_init(dsym)) {
 		dsym->s_def = DEF;
 		mark_as_set(dsym);
 	}

cvs diff -r1.94 -r1.95 src/usr.bin/xlint/lint1/externs1.h (expand / switch to context diff)
--- src/usr.bin/xlint/lint1/externs1.h 2021/03/28 13:09:43 1.94
+++ src/usr.bin/xlint/lint1/externs1.h 2021/03/30 14:25:28 1.95
@@ -1,4 +1,4 @@
-/*	$NetBSD: externs1.h,v 1.94 2021/03/28 13:09:43 rillig Exp $	*/
+/*	$NetBSD: externs1.h,v 1.95 2021/03/30 14:25:28 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
@@ -292,13 +292,11 @@
  */
 extern	void	begin_initialization(sym_t *);
 extern	void	end_initialization(void);
-extern	bool	*current_initerr(void);
 extern	sym_t	**current_initsym(void);
 
-extern	void	initstack_init(void);
 extern	void	init_rbrace(void);
 extern	void	init_lbrace(void);
-extern	void	init_using_expr(tnode_t *);
+extern	void	init_expr(tnode_t *);
 extern	void	add_designator_member(sbuf_t *);
 extern	void	add_designator_subscript(range_t);
 

cvs diff -r1.178 -r1.179 src/usr.bin/xlint/lint1/init.c (expand / switch to context diff)
--- src/usr.bin/xlint/lint1/init.c 2021/03/29 21:34:17 1.178
+++ src/usr.bin/xlint/lint1/init.c 2021/03/30 14:25:28 1.179
@@ -1,7 +1,8 @@
-/*	$NetBSD: init.c,v 1.178 2021/03/29 21:34:17 rillig Exp $	*/
+/*	$NetBSD: init.c,v 1.179 2021/03/30 14:25:28 rillig Exp $	*/
 
 /*
  * Copyright (c) 1994, 1995 Jochen Pohl
+ * Copyright (c) 2021 Roland Illig
  * All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,7 +38,7 @@
 
 #include <sys/cdefs.h>
 #if defined(__RCSID) && !defined(lint)
-__RCSID("$NetBSD: init.c,v 1.178 2021/03/29 21:34:17 rillig Exp $");
+__RCSID("$NetBSD: init.c,v 1.179 2021/03/30 14:25:28 rillig Exp $");
 #endif
 
 #include <stdlib.h>
@@ -47,10 +48,8 @@
 
 
 /*
- * Initialization
+ * Initialization of global or local objects, like in:
  *
- * Handles initializations of global or local objects, like in:
- *
  *	int number = 12345;
  *	int number_with_braces = { 12345 };
  *
@@ -66,158 +65,37 @@
  * 6.7.8p11).
  *
  * For multi-dimensional arrays, the inner braces may be omitted like in
- * array_flat or spelled out like in array_nested.
+ * array_flat or spelled out like in array_nested.  This is unusual in
+ * practice and therefore only supported very basically.
  *
- * For the initializer, the grammar parser calls these functions:
+ * During initialization, the grammar parser calls these functions:
  *
  *	begin_initialization
  *		init_lbrace			for each '{'
  *		add_designator_member		for each '.member' before '='
  *		add_designator_subscript	for each '[123]' before '='
- *		init_using_expr			for each expression
+ *		init_expr			for each expression
  *		init_rbrace			for each '}'
  *	end_initialization
  *
  * Each '{' begins a new brace level, each '}' ends the current brace level.
  * Each brace level has an associated "current object".
  *
- * Most of the time, the topmost level of brace_level contains a scalar type,
- * and its remaining count toggles between 1 and 0.
- *
  * See also:
  *	C99 6.7.8 "Initialization"
  *	d_c99_init.c for more examples
  */
 
-
 /*
- * Describes a single brace level of an ongoing initialization.
- *
- * XXX: Since C99, the initializers can be listed in arbitrary order by using
- * designators to specify the sub-object to be initialized.  The member names
- * of non-leaf structs may thus appear repeatedly, as demonstrated in
- * d_init_pop_member.c.
- *
- * See C99 6.7.8, which spans 6 pages full of tricky details and carefully
- * selected examples.
- */
-struct brace_level {
-
-	/*
-	 * The type of the current object that is initialized at this brace
-	 * level.
-	 *
-	 * On the outermost element, this is always NULL since the outermost
-	 * initializer-expression may be enclosed in an optional pair of
-	 * braces, as of the current implementation.
-	 *
-	 * FIXME: This approach is wrong.  It's not that the outermost
-	 * initializer may be enclosed in additional braces, it's every scalar
-	 * that may be enclosed in additional braces, as of C99 6.7.8p11.
-	 *
-	 * Everywhere else it is nonnull.
-	 */
-	type_t	*bl_type;
-
-	/*
-	 * The type that will be initialized at the next initialization level,
-	 * usually enclosed by another pair of braces.
-	 *
-	 * For an array, it is the element type, but without 'const'.
-	 *
-	 * For a struct or union type, it is one of the member types, but
-	 * without 'const'.
-	 *
-	 * The outermost stack element has no bl_type but nevertheless has
-	 * bl_subtype.  For example, in 'int var = { 12345 }', initially there
-	 * is a brace_level with bl_subtype 'int'.  When the '{' is processed,
-	 * an element with bl_type 'int' is pushed to the stack.  When the
-	 * corresponding '}' is processed, the inner element is popped again.
-	 *
-	 * During initialization, only the top 2 elements of the stack are
-	 * looked at.
-	 *
-	 * XXX: Having bl_subtype here is the wrong approach, it should not be
-	 * necessary at all; see bl_type.
-	 */
-	type_t	*bl_subtype;
-
-	/*
-	 * Whether this level of the initializer requires a '}' to be
-	 * completed.
-	 *
-	 * Multidimensional arrays do not need a closing brace to complete
-	 * an inner array; for example, { 1, 2, 3, 4 } is a valid initializer
-	 * for 'int arr[2][2]'.
-	 *
-	 * XXX: Double-check whether this is the correct approach at all; see
-	 * bl_type.
-	 */
-	bool bl_brace: 1;
-
-	/* Whether bl_type is an array of unknown size. */
-	bool bl_array_of_unknown_size: 1;
-
-	/*
-	 * XXX: This feels wrong.  Whether or not there has been a named
-	 * initializer (called 'designation' since C99) should not matter at
-	 * all.  Even after an initializer with designation, counting of the
-	 * remaining elements continues, see C99 6.7.8p17.
-	 */
-	bool bl_seen_named_member: 1;
-
-	/*
-	 * For structs, the next member to be initialized by a designator-less
-	 * initializer.
-	 */
-	sym_t *bl_next_member;
-
-	/* TODO: Add bl_next_subscript for arrays. */
-
-	/* TODO: Understand C99 6.7.8p17 and footnote 128 for unions. */
-
-	/*
-	 * The number of remaining elements to be used by expressions without
-	 * designator.
-	 *
-	 * This says nothing about which members have been initialized or not
-	 * since starting with C99, members may be initialized in arbitrary
-	 * order by using designators.
-	 *
-	 * For an array of unknown size, this is always 0 and thus irrelevant.
-	 *
-	 * XXX: for scalars?
-	 * XXX: for structs?
-	 * XXX: for unions?
-	 * XXX: for arrays?
-	 *
-	 * XXX: Having the count of remaining objects should not be necessary.
-	 * It is probably clearer to use bl_next_member and bl_next_subscript
-	 * for this purpose.
-	 */
-	int bl_remaining;
-
-	/*
-	 * The initialization state of the enclosing data structure
-	 * (struct, union, array).
-	 *
-	 * XXX: Or for a scalar, for the top-level element, or for expressions
-	 * in redundant braces such as '{{{{ 0 }}}}' (not yet implemented as
-	 * of 2021-03-25).
-	 */
-	struct brace_level *bl_enclosing;
-};
-
-/*
  * A single component on the path to the sub-object that is initialized by an
  * initializer expression.  Either a struct or union member, or an array
  * subscript.
  *
- * See also: C99 6.7.8 "Initialization"
+ * C99 6.7.8p6, 6.7.8p7
  */
 struct designator {
-	const char	*dr_name;		/* for struct and union */
-	/* TODO: add 'dr_subscript' for arrays */
+	const char	*dr_name;	/* for struct and union */
+	size_t		dr_subscript;	/* for array */
 	struct designator *dr_next;
 };
 
@@ -226,16 +104,36 @@
  * initialize.  Examples for designations are '.member' or
  * '.member[123].member.member[1][1]'.
  *
- * See also: C99 6.7.8 "Initialization"
+ * C99 6.7.8p6, 6.7.8p7
  */
 struct designation {
 	struct designator *dn_head;
 	struct designator *dn_tail;
 };
 
+/*
+ * Describes a single brace level of an ongoing initialization.
+ *
+ * See C99 6.7.8p17.
+ */
+struct brace_level {
+	/*
+	 * The type of the current object that is initialized at this brace
+	 * level.
+	 */
+	const type_t	*bl_type;
+	const sym_t	*bl_next_member;	/* for structs and unions */
+	size_t		bl_array_next_subscript;
+	bool		bl_array_of_unknown_size: 1;
+	bool		bl_scalar_done: 1;	/* for scalars */
+	bool		bl_omitted_braces: 1;	/* skip further checks */
+	struct designation bl_designation;	/* .member[123].member */
+	struct brace_level *bl_enclosing;
+};
+
 struct initialization {
 	/*
-	 * is set as soon as a fatal error occurred in the initialization.
+	 * Is set as soon as a fatal error occurred in the initialization.
 	 * The effect is that the rest of the initialization is ignored
 	 * (parsed by yacc, expression trees built, but no initialization
 	 * takes place).
@@ -248,21 +146,12 @@
 	/* The innermost brace level. */
 	struct brace_level *in_brace_level;
 
-	/*
-	 * The C99 designator, if any, for the current initialization
-	 * expression.
-	 */
-	struct designation in_designation;
-
-	struct initialization *in_next;
+	struct initialization *in_enclosing;
 };
 
 
-static struct initialization *init;
-
-
 #ifdef DEBUG
-static int debug_ind = 0;
+static int debug_indentation = 0;
 #endif
 
 
@@ -282,14 +171,14 @@
 debug_indent(void)
 {
 
-	debug_printf("%*s", 2 * debug_ind, "");
+	debug_printf("%*s", 2 * debug_indentation, "");
 }
 
 static void
 debug_enter(const char *func)
 {
 
-	printf("%*s+ %s\n", 2 * debug_ind++, "", func);
+	printf("%*s+ %s\n", 2 * debug_indentation++, "", func);
 }
 
 static void __printflike(1, 2)
@@ -308,7 +197,7 @@
 debug_leave(const char *func)
 {
 
-	printf("%*s- %s\n", 2 * --debug_ind, "", func);
+	printf("%*s- %s\n", 2 * --debug_indentation, "", func);
 }
 
 #define debug_enter()		(debug_enter)(__func__)
@@ -316,6 +205,7 @@
 
 #else
 
+/* TODO: This is C99 */
 #define debug_printf(fmt, ...)	do { } while (false)
 #define debug_indent()		do { } while (false)
 #define debug_enter()		do { } while (false)
@@ -334,6 +224,7 @@
 	return r;
 }
 
+/* C99 6.7.8p7 */
 static bool
 is_struct_or_union(tspec_t t)
 {
@@ -365,23 +256,76 @@
 
 /* C99 6.7.8p9 */
 static bool
-is_unnamed_member(const sym_t *m)
+is_unnamed(const sym_t *m)
 {
 
 	return m->s_bitfield && m->s_name == unnamed;
 }
 
+/* C99 6.7.8p9 */
 static const sym_t *
-look_up_member(const sym_t *m, const char *name)
+skip_unnamed(const sym_t *m)
 {
 
-	for (; m != NULL; m = m->s_next)
-		if (!is_unnamed_member(m) && strcmp(m->s_name, name) == 0)
+	while (m != NULL && is_unnamed(m))
+		m = m->s_next;
+	return m;
+}
+
+static const sym_t *
+first_named_member(const type_t *tp)
+{
+
+	lint_assert(is_struct_or_union(tp->t_tspec));
+	return skip_unnamed(tp->t_str->sou_first_member);
+}
+
+static const sym_t *
+look_up_member(const type_t *tp, const char *name)
+{
+	const sym_t *m;
+
+	lint_assert(is_struct_or_union(tp->t_tspec));
+	for (m = tp->t_str->sou_first_member; m != NULL; m = m->s_next)
+		if (!is_unnamed(m) && strcmp(m->s_name, name) == 0)
 			return m;
 	return NULL;
 }
 
+static const type_t *
+sym_type(const sym_t *sym)
+{
 
+	return sym != NULL ? sym->s_type : NULL;
+}
+
+static const type_t *
+look_up_member_type(const type_t *tp, const char *name)
+{
+	const sym_t *member;
+
+	member = look_up_member(tp, name);
+	if (member == NULL) {
+		/* TODO: add type information */
+		/* undefined struct/union member: %s */
+		error(101, name);
+	}
+
+	return sym_type(member);
+}
+
+static void
+update_type_of_array_of_unknown_size(sym_t *sym, size_t size)
+{
+	type_t *tp;
+
+	tp = duptyp(sym->s_type);
+	tp->t_dim = (int)size;
+	tp->t_incomplete_array = false;
+	sym->s_type = tp;
+}
+
+
 /* In traditional C, bit-fields can be initialized only by integer constants. */
 static void
 check_bit_field_init(const tnode_t *ln, tspec_t lt, tspec_t rt)
@@ -418,6 +362,18 @@
 }
 
 static void
+check_no_auto_aggregate(const sym_t *sym)
+{
+
+	if (tflag &&
+	    has_automatic_storage_duration(sym) &&
+	    !is_scalar(sym->s_type->t_tspec)) {
+		/* no automatic aggregate initialization in trad. C */
+		warning(188);
+	}
+}
+
+static void
 check_init_expr(const type_t *tp, sym_t *sym, tnode_t *tn)
 {
 	tnode_t *ln;
@@ -463,12 +419,13 @@
 
 
 static struct designator *
-designator_new(const char *name)
+designator_new(const char *name, size_t subscript)
 {
 	struct designator *dr;
 
 	dr = xcalloc(1, sizeof *dr);
 	dr->dr_name = name;
+	dr->dr_subscript = subscript;
 	return dr;
 }
 
@@ -480,6 +437,35 @@
 }
 
 
+static const type_t *
+designator_look_up(const struct designator *dr, const type_t *tp)
+{
+	switch (tp->t_tspec) {
+	case STRUCT:
+	case UNION:
+		if (dr->dr_name == NULL) {
+			/* syntax error '%s' */
+			error(249, "designator '[...]' is only for arrays");
+			return sym_type(first_named_member(tp));
+		}
+
+		return look_up_member_type(tp, dr->dr_name);
+	case ARRAY:
+		if (dr->dr_name != NULL) {
+			/* TODO: reword; kept for compatibility */
+			/* syntax error '%s' */
+			error(249,
+			    "named member must only be used with struct/union");
+		}
+		return tp->t_subt;
+	default:
+		/* syntax error '%s' */
+		error(249, "scalar type cannot use designator");
+		return tp;
+	}
+}
+
+
 #ifdef DEBUG
 static void
 designation_debug(const struct designation *dn)
@@ -491,8 +477,13 @@
 
 	debug_indent();
 	debug_printf("designation: ");
-	for (dr = dn->dn_head; dr != NULL; dr = dr->dr_next)
-		debug_printf(".%s", dr->dr_name);
+	for (dr = dn->dn_head; dr != NULL; dr = dr->dr_next) {
+		if (dr->dr_name != NULL) {
+			debug_printf(".%s", dr->dr_name);
+			lint_assert(dr->dr_subscript == 0);
+		} else
+			debug_printf("[%zu]", dr->dr_subscript);
+	}
 	debug_printf("\n");
 }
 #else
@@ -500,9 +491,12 @@
 #endif
 
 static void
-designation_add(struct designation *dn, struct designator *dr)
+designation_add(struct designation *dn, const char *name, size_t subscript)
 {
+	struct designator *dr;
 
+	dr = designator_new(name, subscript);
+
 	if (dn->dn_head != NULL) {
 		dn->dn_tail->dr_next = dr;
 		dn->dn_tail = dr;
@@ -510,44 +504,46 @@
 		dn->dn_head = dr;
 		dn->dn_tail = dr;
 	}
-
-	designation_debug(dn);
 }
 
-/* TODO: add support for array subscripts, not only named members */
 /*
- * TODO: This function should not be necessary at all.  There is no need to
- *  remove the head of the list.
+ * Starting at the type of the current object, resolve the type of the
+ * sub-object by following each designator in the list.
  */
+static const type_t *
+designation_look_up(const struct designation *dn, const type_t *tp)
+{
+	const struct designator *dr;
+
+	for (dr = dn->dn_head; dr != NULL && tp != NULL; dr = dr->dr_next)
+		tp = designator_look_up(dr, tp);
+	return tp;
+}
+
+
+
 static void
-designation_shift_level(struct designation *dn)
+designation_reset(struct designation *dn)
 {
-	lint_assert(dn->dn_head != NULL);
+	struct designator *dr, *next;
 
-	if (dn->dn_head == dn->dn_tail) {
-		designator_free(dn->dn_head);
-		dn->dn_head = NULL;
-		dn->dn_tail = NULL;
-	} else {
-		struct designator *head = dn->dn_head;
-		dn->dn_head = dn->dn_head->dr_next;
-		designator_free(head);
+	for (dr = dn->dn_head; dr != NULL; dr = next) {
+		next = dr->dr_next;
+		designator_free(dr);
 	}
-
-	designation_debug(dn);
 }
 
 
 static struct brace_level *
-brace_level_new(type_t *type, type_t *subtype, int remaining,
-		struct brace_level *enclosing)
+brace_level_new(const type_t *tp, struct brace_level *enclosing)
 {
-	struct brace_level *bl = xcalloc(1, sizeof(*bl));
+	struct brace_level *bl;
 
-	bl->bl_type = type;
-	bl->bl_subtype = subtype;
-	bl->bl_remaining = remaining;
+	bl = xcalloc(1, sizeof(*bl));
+	bl->bl_type = tp;
 	bl->bl_enclosing = enclosing;
+	if (is_struct_or_union(tp->t_tspec))
+		bl->bl_next_member = first_named_member(tp);
 
 	return bl;
 }
@@ -555,246 +551,173 @@
 static void
 brace_level_free(struct brace_level *bl)
 {
+
+	designation_reset(&bl->bl_designation);
 	free(bl);
 }
 
 #ifdef DEBUG
-/*
- * TODO: only log the top of the stack after each modifying operation
- *
- * TODO: wrap all write accesses to brace_level in setter functions
- */
 static void
 brace_level_debug(const struct brace_level *bl)
 {
-	if (bl->bl_type != NULL)
-		debug_printf("type '%s'", type_name(bl->bl_type));
-	if (bl->bl_type != NULL && bl->bl_subtype != NULL)
-		debug_printf(", ");
-	if (bl->bl_subtype != NULL)
-		debug_printf("subtype '%s'", type_name(bl->bl_subtype));
 
-	if (bl->bl_brace)
-		debug_printf(", needs closing brace");
+	lint_assert(bl->bl_type != NULL);
+	lint_assert(bl->bl_next_member == NULL ||
+	    !is_unnamed(bl->bl_next_member));
+
+	debug_printf("type '%s'", type_name(bl->bl_type));
+
 	if (bl->bl_array_of_unknown_size)
 		debug_printf(", array of unknown size");
-	if (bl->bl_seen_named_member)
-		debug_printf(", seen named member");
 
-	const type_t *eff_type = bl->bl_type != NULL
-	    ? bl->bl_type : bl->bl_subtype;
-	if (eff_type->t_tspec == STRUCT && bl->bl_next_member != NULL)
+	if (is_struct_or_union(bl->bl_type->t_tspec) &&
+	    bl->bl_next_member != NULL)
 		debug_printf(", next member '%s'",
 		    bl->bl_next_member->s_name);
+	if (bl->bl_type->t_tspec == ARRAY)
+		debug_printf(", next array subscript %zu",
+		    bl->bl_array_next_subscript);
 
-	debug_printf(", remaining %d\n", bl->bl_remaining);
+	debug_printf("\n");
 }
 #else
-#define brace_level_debug(bl) do { } while (false)
+#define brace_level_debug(level) do { } while (false)
 #endif
 
 static void
-brace_level_assert_struct_or_union(const struct brace_level *bl)
+brace_level_remove_designation(struct brace_level *bl)
 {
-	lint_assert(is_struct_or_union(bl->bl_type->t_tspec));
-}
+	struct designator *dr, *next;
 
-static void
-brace_level_assert_array(const struct brace_level *bl)
-{
-	lint_assert(bl->bl_type->t_tspec == ARRAY);
-}
+	for (dr = bl->bl_designation.dn_head; dr != NULL; dr = next) {
+		next = dr->dr_next;
+		designator_free(dr);
+	}
 
-static type_t *
-brace_level_subtype(struct brace_level *bl)
-{
-
-	if (bl->bl_subtype != NULL)
-		return bl->bl_subtype;
-
-	return bl->bl_type;
+	bl->bl_designation.dn_head = NULL;
+	bl->bl_designation.dn_tail = NULL;
 }
 
-static void
-brace_level_set_array_dimension(struct brace_level *bl, int dim)
-{
-	brace_level_assert_array(bl);
 
-	debug_step("setting the array size to %d", dim);
-	bl->bl_type->t_dim = dim;
-	debug_indent();
-	brace_level_debug(bl);
-}
-
-static void
-brace_level_next_member(struct brace_level *bl)
+static const type_t *
+brace_level_sub_type_struct_or_union(const struct brace_level *bl)
 {
-	const sym_t *m;
 
-	brace_level_assert_struct_or_union(bl);
-	do {
-		m = bl->bl_next_member = bl->bl_next_member->s_next;
-		/* XXX: can this assertion be made to fail? */
-		lint_assert(m != NULL);
-	} while (m->s_bitfield && m->s_name == unnamed);
+	if (bl->bl_next_member == NULL) {
+		/* too many struct/union initializers */
+		error(172);
+		return NULL;
+	}
 
-	debug_indent();
-	brace_level_debug(bl);
+	lint_assert(!is_unnamed(bl->bl_next_member));
+	return sym_type(bl->bl_next_member);
 }
 
-static const sym_t *
-brace_level_look_up_member(const struct brace_level *bl, const char *name)
+static const type_t *
+brace_level_sub_type_array(const struct brace_level *bl)
 {
 
-	brace_level_assert_struct_or_union(bl);
-	return look_up_member(bl->bl_type->t_str->sou_first_member, name);
+	if (!bl->bl_type->t_incomplete_array &&
+	    !bl->bl_omitted_braces &&
+	    bl->bl_array_next_subscript >= (size_t)bl->bl_type->t_dim) {
+		/* too many array initializers, expected %d */
+		error(173, bl->bl_type->t_dim);
+	}
+
+	return bl->bl_type->t_subt;
 }
 
-static sym_t *
-brace_level_look_up_first_member_named(struct brace_level *bl,
-				       const char *name, int *count)
+static const type_t *
+brace_level_sub_type_scalar(const struct brace_level *bl)
 {
-	sym_t *m;
 
-	for (m = bl->bl_type->t_str->sou_first_member;
-	     m != NULL; m = m->s_next) {
-		if (is_unnamed_member(m))
-			continue;
-		if (strcmp(m->s_name, name) != 0)
-			continue;
-		(*count)++;
-		break;
+	if (bl->bl_scalar_done) {
+		/* too many initializers */
+		error(174);
 	}
 
-	return m;
+	return bl->bl_type;
 }
 
-static sym_t *
-brace_level_look_up_first_member_unnamed(struct brace_level *bl, int *count)
+/* Return the type of the sub-object that is currently being initialized. */
+static const type_t *
+brace_level_sub_type(const struct brace_level *bl)
 {
-	sym_t *m;
 
-	brace_level_assert_struct_or_union(bl);
+	if (bl->bl_designation.dn_head != NULL)
+		return designation_look_up(&bl->bl_designation, bl->bl_type);
 
-	for (m = bl->bl_type->t_str->sou_first_member;
-	     m != NULL; m = m->s_next) {
-		if (is_unnamed_member(m))
-			continue;
-		/* XXX: What is this code for? */
-		if (++(*count) == 1) {
-			bl->bl_next_member = m;
-			bl->bl_subtype = m->s_type;
-		}
+	switch (bl->bl_type->t_tspec) {
+	case STRUCT:
+	case UNION:
+		return brace_level_sub_type_struct_or_union(bl);
+	case ARRAY:
+		return brace_level_sub_type_array(bl);
+	default:
+		return brace_level_sub_type_scalar(bl);
 	}
-
-	return m;
 }
 
-/* TODO: document me */
-/* TODO: think of a better name than 'push' */
-static bool
-brace_level_push_array(struct brace_level *bl)
+static void
+brace_level_apply_designation(struct brace_level *bl)
 {
-	brace_level_assert_array(bl);
+	const struct designator *dr = bl->bl_designation.dn_head;
 
-	if (bl->bl_enclosing->bl_seen_named_member) {
-		bl->bl_brace = true;
-		debug_step("ARRAY, seen named member, needs closing brace");
-	}
+	if (dr == NULL)
+		return;
 
-	if (is_incomplete(bl->bl_type) &&
-	    bl->bl_enclosing->bl_enclosing != NULL) {
-		/* initialization of an incomplete type */
-		error(175);
-		return false;
-	}
+	designation_debug(&bl->bl_designation);
 
-	bl->bl_subtype = bl->bl_type->t_subt;
-	bl->bl_array_of_unknown_size = is_incomplete(bl->bl_type);
-	bl->bl_remaining = bl->bl_type->t_dim;
-	debug_step("type '%s' remaining %d",
-	    type_name(bl->bl_type), bl->bl_remaining);
-	return true;
+	switch (bl->bl_type->t_tspec) {
+	case STRUCT:
+	case UNION:
+		if (dr->dr_name == NULL)
+			return;	/* error, silently ignored */
+		bl->bl_next_member = look_up_member(bl->bl_type, dr->dr_name);
+		break;
+	case ARRAY:
+		if (dr->dr_name != NULL)
+			return;	/* error, silently ignored */
+		bl->bl_array_next_subscript = dr->dr_subscript;
+		break;
+	default:
+		break;		/* error, silently ignored */
+	}
 }
 
 /*
- * If the removed element was a structure member, we must go
- * to the next structure member.
+ * After initializing a sub-object, advance to the next sub-object.
  *
- * XXX: Nothing should ever be "removed" at this point.
- *
- * TODO: think of a better name than 'pop'
+ * C99 6.7.8p17
  */
 static void
-brace_level_pop_item_unnamed(struct brace_level *bl)
+brace_level_advance(struct brace_level *bl)
 {
-	if (bl->bl_remaining > 0 && bl->bl_type->t_tspec == STRUCT &&
-	    !bl->bl_seen_named_member) {
-		brace_level_next_member(bl);
-		bl->bl_subtype = bl->bl_next_member->s_type;
-	}
-}
 
-static bool
-brace_level_check_too_many_initializers(struct brace_level *bl)
-{
-	if (bl->bl_remaining > 0)
-		return true;
-	/*
-	 * FIXME: even with named members, there can be too many initializers
-	 */
-	if (bl->bl_array_of_unknown_size || bl->bl_seen_named_member)
-		return true;
-
-	tspec_t t = bl->bl_type->t_tspec;
-	if (t == ARRAY) {
-		/* too many array initializers, expected %d */
-		error(173, bl->bl_type->t_dim);
-	} else if (is_struct_or_union(t)) {
-		/* too many struct/union initializers */
-		error(172);
-	} else {
-		/* too many initializers */
-		error(174);
+	switch (bl->bl_type->t_tspec) {
+	case STRUCT:
+		lint_assert(bl->bl_next_member != NULL);
+		bl->bl_next_member = skip_unnamed(bl->bl_next_member->s_next);
+		break;
+	case UNION:
+		bl->bl_next_member = NULL;
+		break;
+	case ARRAY:
+		bl->bl_array_next_subscript++;
+		break;
+	default:
+		bl->bl_scalar_done = true;
+		break;
 	}
-	return false;
 }
 
-/* Extend an array of unknown size by one element */
-static void
-brace_level_extend_if_array_of_unknown_size(struct brace_level *bl)
-{
 
-	if (bl->bl_remaining != 0)
-		return;
-	/*
-	 * XXX: According to the function name, there should be a 'return' if
-	 * bl_array_of_unknown_size is false.  There's probably a test missing
-	 * for that case.
-	 */
-
-	/*
-	 * The only place where an incomplete array may appear is at the
-	 * outermost aggregate bl of the object to be initialized.
-	 */
-	lint_assert(bl->bl_enclosing->bl_enclosing == NULL);
-	lint_assert(bl->bl_type->t_tspec == ARRAY);
-
-	debug_step("extending array of unknown size '%s'",
-	    type_name(bl->bl_type));
-	bl->bl_remaining = 1;
-	bl->bl_type->t_dim++;
-	setcomplete(bl->bl_type, true);
-
-	debug_step("extended type is '%s'", type_name(bl->bl_type));
-}
-
-
 static struct initialization *
 initialization_new(sym_t *sym)
 {
-	struct initialization *in = xcalloc(1, sizeof(*in));
+	struct initialization *in;
 
+	in = xcalloc(1, sizeof(*in));
 	in->in_sym = sym;
 
 	return in;
@@ -814,20 +737,19 @@
 }
 
 #ifdef DEBUG
-/*
- * TODO: only call debug_initstack after each push/pop.
- */
 static void
 initialization_debug(const struct initialization *in)
 {
+	size_t i;
+	const struct brace_level *bl;
+
 	if (in->in_brace_level == NULL) {
-		debug_step("no brace level in the current initialization");
+		debug_step("no brace level");
 		return;
 	}
 
-	size_t i = 0;
-	for (const struct brace_level *bl = in->in_brace_level;
-	     bl != NULL; bl = bl->bl_enclosing) {
+	i = 0;
+	for (bl = in->in_brace_level; bl != NULL; bl = bl->bl_enclosing) {
 		debug_indent();
 		debug_printf("brace level %zu: ", i);
 		brace_level_debug(bl);
@@ -839,476 +761,136 @@
 #endif
 
 /*
- * Initialize the initialization stack by putting an entry for the object
- * which is to be initialized on it.
- *
- * TODO: merge into initialization_new if possible
+ * Return the type of the object or sub-object that is currently being
+ * initialized.
  */
-static void
-initialization_init(struct initialization *in)
+static const type_t *
+initialization_sub_type(struct initialization *in)
 {
-	if (in->in_err)
-		return;
+	const type_t *tp;
 
-	debug_enter();
-
-	/*
-	 * If the type which is to be initialized is an incomplete array,
-	 * it must be duplicated.
-	 */
-	if (in->in_sym->s_type->t_tspec == ARRAY && is_incomplete(in->in_sym->s_type))
-		in->in_sym->s_type = duptyp(in->in_sym->s_type);
-	/* TODO: does 'duptyp' create a memory leak? */
-
-	in->in_brace_level = brace_level_new(NULL, in->in_sym->s_type, 1, NULL);
-
-	initialization_debug(in);
-	debug_leave();
+	tp = in->in_brace_level != NULL
+	    ? brace_level_sub_type(in->in_brace_level)
+	    : in->in_sym->s_type;
+	if (tp == NULL)
+		in->in_err = true;
+	return tp;
 }
 
 static void
-initialization_set_error(struct initialization *in)
+initialization_begin_brace_level(struct initialization *in)
 {
-	in->in_err = true;
-}
+	const type_t *tp;
 
-/* TODO: document me */
-/* TODO: think of a better name than 'push' */
-static bool
-initialization_push_struct_or_union(struct initialization *in)
-{
-	struct brace_level *bl = in->in_brace_level;
-	int cnt;
-	sym_t *m;
+	if (in->in_err)
+		return;
 
-	if (is_incomplete(bl->bl_type)) {
-		/* initialization of an incomplete type */
-		error(175);
-		initialization_set_error(in);
-		return false;
-	}
-
-	cnt = 0;
-	designation_debug(&in->in_designation);
-	debug_step("lookup for '%s'%s",
-	    type_name(bl->bl_type),
-	    bl->bl_seen_named_member ? ", seen named member" : "");
-
-	if (in->in_designation.dn_head != NULL)
-		m = brace_level_look_up_first_member_named(bl,
-		    in->in_designation.dn_head->dr_name, &cnt);
-	else
-		m = brace_level_look_up_first_member_unnamed(bl, &cnt);
-
-	if (in->in_designation.dn_head != NULL) {
-		if (m == NULL) {
-			debug_step("pop struct");
-			return true;
-		}
-		bl->bl_next_member = m;
-		bl->bl_subtype = m->s_type;
-		bl->bl_seen_named_member = true;
-		debug_step("named member '%s'",
-		    in->in_designation.dn_head->dr_name);
-		designation_shift_level(&in->in_designation);
-		cnt = bl->bl_type->t_tspec == STRUCT ? 2 : 1;
-	}
-	bl->bl_brace = true;
-	debug_step("unnamed element with type '%s'%s",
-	    type_name(
-		bl->bl_type != NULL ? bl->bl_type : bl->bl_subtype),
-	    bl->bl_brace ? ", needs closing brace" : "");
-	if (cnt == 0) {
-		/* cannot init. struct/union with no named member */
-		error(179);
-		initialization_set_error(in);
-		return false;
-	}
-	bl->bl_remaining = bl->bl_type->t_tspec == STRUCT ? cnt : 1;
-	return false;
-}
-
-static void
-initialization_end_brace_level(struct initialization *in)
-{
-	struct brace_level *bl = in->in_brace_level;
-	in->in_brace_level = bl->bl_enclosing;
-	brace_level_free(bl);
-}
-
-/* TODO: document me */
-/* TODO: think of a better name than 'push' */
-static void
-initialization_push(struct initialization *in)
-{
-	struct brace_level *bl;
-
 	debug_enter();
 
-	brace_level_extend_if_array_of_unknown_size(in->in_brace_level);
-
-	bl = in->in_brace_level;
-	lint_assert(bl->bl_remaining > 0);
-
-	in->in_brace_level = brace_level_new(brace_level_subtype(bl), NULL, 0,
-	    bl);
-	lint_assert(in->in_brace_level->bl_type != NULL);
-	lint_assert(in->in_brace_level->bl_type->t_tspec != FUNC);
-
-again:
-	bl = in->in_brace_level;
-
-	debug_step("expecting type '%s'", type_name(bl->bl_type));
-	lint_assert(bl->bl_type != NULL);
-	switch (bl->bl_type->t_tspec) {
-	case ARRAY:
-		if (in->in_designation.dn_head != NULL) {
-			debug_step("pop array, named member '%s'%s",
-			    in->in_designation.dn_head->dr_name,
-			    bl->bl_brace ? ", needs closing brace" : "");
-			goto pop;
-		}
-
-		if (!brace_level_push_array(bl))
-			initialization_set_error(in);
-		break;
-
-	case UNION:
-		if (tflag)
-			/* initialization of union is illegal in trad. C */
-			warning(238);
-		/* FALLTHROUGH */
-	case STRUCT:
-		if (initialization_push_struct_or_union(in))
-			goto pop;
-		break;
-	default:
-		if (in->in_designation.dn_head != NULL) {
-			debug_step("pop scalar");
-		pop:
-			initialization_end_brace_level(in);
-			goto again;
-		}
-		/* The initialization stack now expects a single scalar. */
-		bl->bl_remaining = 1;
-		break;
+	tp = initialization_sub_type(in);
+	if (tp == NULL) {
+		in->in_err = true;
+		goto done;
 	}
 
-	initialization_debug(in);
-	debug_leave();
-}
+	if (tflag && in->in_brace_level == NULL)
+		check_no_auto_aggregate(in->in_sym);
 
-/* TODO: document me */
-static void
-initialization_pop_item_named(struct initialization *in, const char *name)
-{
-	struct brace_level *bl = in->in_brace_level;
-	const sym_t *m;
-
-	/*
-	 * TODO: fix wording of the debug message; this doesn't seem to be
-	 * related to initializing the named member.
-	 */
-	debug_step("initializing named member '%s'", name);
-
-	if (!is_struct_or_union(bl->bl_type->t_tspec)) {
-		/* syntax error '%s' */
-		error(249, "named member must only be used with struct/union");
-		initialization_set_error(in);
-		return;
+	if (tflag && tp->t_tspec == UNION) {
+		/* initialization of union is illegal in traditional C */
+		warning(238);
 	}
 
-	m = brace_level_look_up_member(bl, name);
-	if (m == NULL) {
-		/* TODO: add type information to the message */
-		/* undefined struct/union member: %s */
-		error(101, name);
-
-		designation_shift_level(&in->in_designation);
-		bl->bl_seen_named_member = true;
-		return;
+	if (tp->t_tspec == STRUCT && tp->t_str->sou_incomplete) {
+		/* TODO: add type information */
+		/* initialization of an incomplete type */
+		error(175);
+		in->in_err = true;
+		goto done;
 	}
 
-	debug_step("found matching member");
-	bl->bl_subtype = m->s_type;
-	/* XXX: why ++? */
-	bl->bl_remaining++;
-	/* XXX: why is bl_seen_named_member not set? */
-	designation_shift_level(&in->in_designation);
-}
+	if (in->in_brace_level != NULL)
+		brace_level_apply_designation(in->in_brace_level);
 
-/* TODO: think of a better name than 'pop' */
-static void
-initialization_pop_item(struct initialization *in)
-{
-	struct brace_level *bl;
+	in->in_brace_level = brace_level_new(tp, in->in_brace_level);
 
-	debug_enter();
-
-	bl = in->in_brace_level;
-	debug_indent();
-	debug_printf("popping: ");
-	brace_level_debug(bl);
-
-	in->in_brace_level = bl->bl_enclosing;
-	brace_level_free(bl);
-	bl = in->in_brace_level;
-	lint_assert(bl != NULL);
-
-	bl->bl_remaining--;
-	lint_assert(bl->bl_remaining >= 0);
-	debug_step("%d elements remaining", bl->bl_remaining);
-
-	if (in->in_designation.dn_head != NULL && in->in_designation.dn_head->dr_name != NULL)
-		initialization_pop_item_named(in, in->in_designation.dn_head->dr_name);
-	else
-		brace_level_pop_item_unnamed(bl);
-
+done:
 	initialization_debug(in);
 	debug_leave();
 }
 
-/*
- * Take all entries which cannot be used for further initializers from the
- * stack, but do this only if they do not require a closing brace.
- */
-/* TODO: think of a better name than 'pop' */
+/* C99 6.7.8p22 */
 static void
-initialization_pop_nobrace(struct initialization *in)
+initialization_set_size_of_unknown_array(struct initialization *in)
 {
 
-	debug_enter();
-	while (!in->in_brace_level->bl_brace &&
-	       in->in_brace_level->bl_remaining == 0 &&
-	       !in->in_brace_level->bl_array_of_unknown_size)
-		initialization_pop_item(in);
-	debug_leave();
+	if (in->in_sym->s_type->t_incomplete_array &&
+	    in->in_brace_level->bl_enclosing == NULL)
+		update_type_of_array_of_unknown_size(in->in_sym,
+		    in->in_brace_level->bl_array_next_subscript);
 }
 
-/*
- * Process a '{' in an initializer by starting the initialization of the
- * nested data structure, with bl_type being the bl_subtype of the outer
- * initialization level.
- */
 static void
-initialization_next_brace(struct initialization *in)
+initialization_end_brace_level(struct initialization *in)
 {
+	struct brace_level *bl;
 
-	debug_enter();
-	initialization_debug(in);
-
-	if (!in->in_err &&
-	    !brace_level_check_too_many_initializers(in->in_brace_level))
-		initialization_set_error(in);
-
-	if (!in->in_err)
-		initialization_push(in);
-
-	if (!in->in_err) {
-		in->in_brace_level->bl_brace = true;
-		designation_debug(&in->in_designation);
-		if (in->in_brace_level->bl_type != NULL)
-			debug_step("expecting type '%s'",
-			    type_name(in->in_brace_level->bl_type));
-	}
-
-	initialization_debug(in);
-	debug_leave();
-}
-
-static void
-check_no_auto_aggregate(scl_t sclass, const struct brace_level *bl)
-{
-	if (!tflag)
-		return;
-	if (!(sclass == AUTO || sclass == REG))
-		return;
-	if (!(bl->bl_enclosing == NULL))
-		return;
-	if (is_scalar(bl->bl_subtype->t_tspec))
-		return;
-
-	/* no automatic aggregate initialization in trad. C */
-	warning(188);
-}
-
-static void
-initialization_lbrace(struct initialization *in)
-{
 	if (in->in_err)
 		return;
 
 	debug_enter();
-	initialization_debug(in);
 
-	check_no_auto_aggregate(in->in_sym->s_scl, in->in_brace_level);
+	initialization_set_size_of_unknown_array(in);
 
-	/*
-	 * Remove all entries which cannot be used for further initializers
-	 * and do not expect a closing brace.
-	 */
-	initialization_pop_nobrace(in);
+	bl = in->in_brace_level;
+	in->in_brace_level = bl->bl_enclosing;
+	brace_level_free(bl);
 
-	initialization_next_brace(in);
+	if (in->in_brace_level != NULL) {
+		brace_level_advance(in->in_brace_level);
+		brace_level_remove_designation(in->in_brace_level);
+	}
 
 	initialization_debug(in);
 	debug_leave();
 }
 
-/*
- * Process a '}' in an initializer by finishing the current level of the
- * initialization stack.
- *
- * Take all entries, including the first which requires a closing brace,
- * from the stack.
- */
 static void
-initialization_rbrace(struct initialization *in)
+initialization_add_designator(struct initialization *in,
+			      const char *name, size_t subscript)
 {
-	bool brace;
 
 	if (in->in_err)
 		return;
 
-	debug_enter();
-	do {
-		brace = in->in_brace_level->bl_brace;
-		/* TODO: improve wording of the debug message */
-		debug_step("loop brace=%d", brace);
-		initialization_pop_item(in);
-	} while (!brace);
-	initialization_debug(in);
-	debug_leave();
+	lint_assert(in->in_brace_level != NULL);
+	designation_add(&in->in_brace_level->bl_designation, name, subscript);
 }
 
-/*
- * A sub-object of an array is initialized using a designator.  This does not
- * have to be an array element directly, it can also be used to initialize
- * only a sub-object of the array element.
- *
- * C99 example: struct { int member[4]; } var = { [2] = 12345 };
- *
- * GNU example: struct { int member[4]; } var = { [1 ... 3] = 12345 };
- *
- * TODO: test the following initialization with an outer and an inner type:
- *
- * .deeply[0].nested = {
- *	.deeply[1].nested = {
- *		12345,
- *	},
- * }
- */
 static void
-initialization_add_designator_subscript(struct initialization *in,
-					range_t range)
+initialization_remove_designation(struct initialization *in)
 {
-	struct brace_level *bl;
 
-	debug_enter();
-	if (range.lo == range.hi)
-		debug_step("subscript is %zu", range.hi);
-	else
-		debug_step("subscript range is %zu ... %zu",
-		    range.lo, range.hi);
-
-	/* XXX: This call is wrong here, it must be somewhere else. */
-	initialization_pop_nobrace(in);
-
-	bl = in->in_brace_level;
-	if (bl->bl_array_of_unknown_size) {
-		/* No +1 here, extend_if_array_of_unknown_size will add it. */
-		int auto_dim = (int)range.hi;
-		if (auto_dim > bl->bl_type->t_dim)
-			brace_level_set_array_dimension(bl, auto_dim);
-	}
-
-	debug_leave();
+	if (in->in_brace_level != NULL)
+		brace_level_remove_designation(in->in_brace_level);
 }
 
-/* Initialize a character array or wchar_t array with a string literal. */
-static bool
-initialization_init_array_using_string(struct initialization *in, tnode_t *tn)
-{
-	struct brace_level *bl;
-	strg_t	*strg;
-
-	if (tn->tn_op != STRING)
-		return false;
-
-	debug_enter();
-
-	bl = in->in_brace_level;
-	strg = tn->tn_string;
-
-	/*
-	 * Check if we have an array type which can be initialized by
-	 * the string.
-	 */
-	if (is_string_array(bl->bl_subtype, strg->st_tspec)) {
-		debug_step("subtype is an array");
-
-		/* Put the array at top of stack */
-		initialization_push(in);
-		bl = in->in_brace_level;
-
-	} else if (is_string_array(bl->bl_type, strg->st_tspec)) {
-		debug_step("type is an array");
-
-		/*
-		 * If the array is already partly initialized, we are
-		 * wrong here.
-		 */
-		if (bl->bl_remaining != bl->bl_type->t_dim)
-			goto nope;
-	} else
-		goto nope;
-
-	if (bl->bl_array_of_unknown_size) {
-		bl->bl_array_of_unknown_size = false;
-		bl->bl_type->t_dim = (int)(strg->st_len + 1);
-		setcomplete(bl->bl_type, true);
-	} else {
-		/*
-		 * TODO: check for buffer overflow in the object to be
-		 * initialized
-		 */
-		/* XXX: double-check for off-by-one error */
-		if (bl->bl_type->t_dim < (int)strg->st_len) {
-			/* non-null byte ignored in string initializer */
-			warning(187);
-		}
-
-		/*
-		 * TODO: C99 6.7.8p14 allows a string literal to be enclosed
-		 * in optional redundant braces, just like scalars.  Add tests
-		 * for this.
-		 */
-	}
-
-	/* In every case the array is initialized completely. */
-	bl->bl_remaining = 0;
-
-	initialization_debug(in);
-	debug_leave();
-	return true;
-nope:
-	debug_leave();
-	return false;
-}
-
 /*
- * Initialize a non-array object with automatic storage duration and only a
- * single initializer expression without braces by delegating to ASSIGN.
+ * An object with automatic storage duration that has a single initializer
+ * expression without braces and is not an array is initialized by delegating
+ * to the ASSIGN operator.
  */
 static bool
-initialization_init_using_assign(struct initialization *in, tnode_t *rn)
+initialization_expr_using_assign(struct initialization *in, tnode_t *rn)
 {
 	tnode_t *ln, *tn;
 
-	if (in->in_sym->s_type->t_tspec == ARRAY)
+	if (!has_automatic_storage_duration(in->in_sym))
 		return false;
-	if (in->in_brace_level->bl_enclosing != NULL)
+	if (in->in_brace_level != NULL)
 		return false;
+	if (in->in_sym->s_type->t_tspec == ARRAY)
+		return false;
 
 	debug_step("handing over to ASSIGN");
 
@@ -1319,93 +901,114 @@
 	tn = build(ASSIGN, ln, rn);
 	expr(tn, false, false, false, false);
 
-	/* XXX: why not clean up the initstack here already? */
 	return true;
 }
 
-/* TODO: document me, or think of a better name */
-static void
-initialization_next_nobrace(struct initialization *in, tnode_t *tn)
+/* Initialize a character array or wchar_t array with a string literal. */
+static bool
+initialization_init_array_using_string(struct initialization *in, tnode_t *tn)
 {
-	debug_enter();
+	struct brace_level *bl;
+	const type_t *tp;
+	strg_t	*strg;
 
-	if (in->in_brace_level->bl_type == NULL &&
-	    !is_scalar(in->in_brace_level->bl_subtype->t_tspec)) {
-		/* {}-enclosed initializer required */
-		error(181);
-		/* XXX: maybe set initerr here */
-	}
+	if (tn->tn_op != STRING)
+		return false;
 
-	if (!in->in_err &&
-	    !brace_level_check_too_many_initializers(in->in_brace_level))
-		initialization_set_error(in);
+	bl = in->in_brace_level;
+	tp = initialization_sub_type(in);
+	strg = tn->tn_string;
 
-	while (!in->in_err) {
-		struct brace_level *bl = in->in_brace_level;
+	if (!is_string_array(tp, strg->st_tspec))
+		return false;
+	if (bl != NULL && tp->t_tspec != ARRAY &&
+	    bl->bl_array_next_subscript != 0)
+		return false;
 
-		if (tn->tn_type->t_tspec == STRUCT &&
-		    bl->bl_type == tn->tn_type &&
-		    bl->bl_enclosing != NULL &&
-		    bl->bl_enclosing->bl_enclosing != NULL) {
-			bl->bl_brace = false;
-			bl->bl_remaining = 1; /* the struct itself */
-			break;
-		}
+	if (bl != NULL && tp->t_dim < (int)strg->st_len) {
+		/* non-null byte ignored in string initializer */
+		warning(187);
+	}
 
-		if (bl->bl_type != NULL &&
-		    is_scalar(bl->bl_type->t_tspec))
-			break;
-		initialization_push(in);
+	if (tp == in->in_sym->s_type && tp->t_incomplete_array) {
+		if (bl != NULL) {
+			bl->bl_array_next_subscript = strg->st_len + 1;
+			/* see initialization_set_size_of_unknown_array */
+		} else
+			update_type_of_array_of_unknown_size(in->in_sym,
+			    strg->st_len + 1);
 	}
 
-	initialization_debug(in);
-	debug_leave();
+	return true;
 }
 
+/*
+ * Initialize a single sub-object as part of the currently ongoing
+ * initialization.
+ */
 static void
 initialization_expr(struct initialization *in, tnode_t *tn)
 {
+	const type_t *tp;
 
+	if (in->in_err)
+		return;
+
+	if (in->in_brace_level != NULL &&
+	    in->in_brace_level->bl_omitted_braces)
+		return;
+
 	debug_enter();
-	initialization_debug(in);
-	designation_debug(&in->in_designation);
-	debug_step("expr:");
-	debug_node(tn, debug_ind + 1);
 
-	if (in->in_err || tn == NULL)
+	if (tn == NULL)
+		goto advance;
+	if (initialization_expr_using_assign(in, tn))
 		goto done;
+	if (initialization_init_array_using_string(in, tn))
+		goto advance;
 
-	if (has_automatic_storage_duration(in->in_sym) &&
-	    initialization_init_using_assign(in, tn))
+	if (in->in_brace_level != NULL)
+		brace_level_apply_designation(in->in_brace_level);
+	tp = initialization_sub_type(in);
+	if (tp == NULL)
 		goto done;
 
-	initialization_pop_nobrace(in);
+	if (in->in_brace_level == NULL && !is_scalar(tp->t_tspec)) {
+		/* {}-enclosed initializer required */
+		error(181);
+		goto done;
+	}
 
-	if (initialization_init_array_using_string(in, tn)) {
-		debug_step("after initializing the string:");
-		goto done_debug;
+	/*
+	 * Hack to accept initializations with omitted braces, see
+	 * c99_6_7_8_p28_example5 in test d_c99_init.c.  Since both GCC and
+	 * Clang already warn about this at level -Wall, there is no point
+	 * in letting lint check this again.
+	 */
+	if (is_scalar(tn->tn_type->t_tspec) &&
+	    tp->t_tspec == ARRAY &&
+	    in->in_brace_level != NULL) {
+		in->in_brace_level->bl_omitted_braces = true;
+		goto done;
 	}
 
-	initialization_next_nobrace(in, tn);
-	if (in->in_err || tn == NULL)
-		goto done_debug;
+	debug_step("expecting '%s', expression has '%s'",
+	    type_name(tp), type_name(tn->tn_type));
+	check_init_expr(tp, in->in_sym, tn);
 
-	/* Using initsym here is better than nothing. */
-	check_init_expr(in->in_brace_level->bl_type, in->in_sym, tn);
-
-	in->in_brace_level->bl_remaining--;
-	debug_step("%d elements remaining", in->in_brace_level->bl_remaining);
-
-done_debug:
-	initialization_debug(in);
-
+advance:
+	if (in->in_brace_level != NULL)
+		brace_level_advance(in->in_brace_level);
 done:
-	while (in->in_designation.dn_head != NULL)
-		designation_shift_level(&in->in_designation);
-
+	initialization_remove_designation(in);
+	initialization_debug(in);
 	debug_leave();
 }
 
+
+static struct initialization *init;
+
+
 static struct initialization *
 current_init(void)
 {
@@ -1414,13 +1017,6 @@
 	return init;
 }
 
-bool *
-current_initerr(void)
-{
-
-	return &current_init()->in_err;
-}
-
 sym_t **
 current_initsym(void)
 {
@@ -1434,8 +1030,12 @@
 	struct initialization *in;
 
 	debug_step("begin initialization of '%s'", type_name(sym->s_type));
+#ifdef DEBUG
+	debug_indentation++;
+#endif
+
 	in = initialization_new(sym);
-	in->in_next = init;
+	in->in_enclosing = init;
 	init = in;
 }
 
@@ -1445,8 +1045,12 @@
 	struct initialization *in;
 
 	in = init;
-	init = init->in_next;
+	init = in->in_enclosing;
 	initialization_free(in);
+
+#ifdef DEBUG
+	debug_indentation--;
+#endif
 	debug_step("end initialization");
 }
 
@@ -1454,33 +1058,25 @@
 add_designator_member(sbuf_t *sb)
 {
 
-	designation_add(&current_init()->in_designation,
-	    designator_new(sb->sb_name));
+	initialization_add_designator(current_init(), sb->sb_name, 0);
 }
 
 void
 add_designator_subscript(range_t range)
 {
 
-	initialization_add_designator_subscript(current_init(), range);
+	initialization_add_designator(current_init(), NULL, range.hi);
 }
 
 void
-initstack_init(void)
-{
-
-	initialization_init(current_init());
-}
-
-void
 init_lbrace(void)
 {
 
-	initialization_lbrace(current_init());
+	initialization_begin_brace_level(current_init());
 }
 
 void
-init_using_expr(tnode_t *tn)
+init_expr(tnode_t *tn)
 {
 
 	initialization_expr(current_init(), tn);
@@ -1490,5 +1086,5 @@
 init_rbrace(void)
 {
 
-	initialization_rbrace(current_init());
+	initialization_end_brace_level(current_init());
 }