aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Matz <matz@suse.de>2016-07-31 05:43:17 +0200
committerMichael Matz <matz@suse.de>2016-12-15 17:47:09 +0100
commited7d54651d2ef33dd6f644f23ba2fa64706e84db (patch)
tree6e9f026ed352aa684be2b6554be698cb2112a4c8
parent968bccdd2aae0c737c3e4c7ba0fc7e8900d1055a (diff)
downloadtinycc-ed7d54651d2ef33dd6f644f23ba2fa64706e84db.tar.gz
tinycc-ed7d54651d2ef33dd6f644f23ba2fa64706e84db.tar.bz2
struct-init: Implement initializing subaggregates
E.g. "struct { struct S s; int a;} = { others, 42 };" if 'others' is also a 'struct S'. Also when the value is a compound literal. See added testcases.
-rw-r--r--tccgen.c98
-rw-r--r--tests/tests2/86-struct-init.c139
-rw-r--r--tests/tests2/86-struct-init.expect27
-rw-r--r--tests/tests2/Makefile2
4 files changed, 226 insertions, 40 deletions
diff --git a/tccgen.c b/tccgen.c
index a6c14d4..064a9fe 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -74,7 +74,7 @@ static int is_compatible_types(CType *type1, CType *type2);
static int parse_btype(CType *type, AttributeDef *ad);
static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
static void parse_expr_type(CType *type);
-static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);
+static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only, int have_elem);
static void block(int *bsym, int *csym, int is_expr);
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
static int decl0(int l, int is_for_loop_init);
@@ -5650,13 +5650,37 @@ static void block(int *bsym, int *csym, int is_expr)
}
}
+#define EXPR_VAL 0
+#define EXPR_CONST 1
+#define EXPR_ANY 2
+
+static void parse_init_elem(int expr_type)
+{
+ int saved_global_expr;
+ switch(expr_type) {
+ case EXPR_CONST:
+ /* compound literals must be allocated globally in this case */
+ saved_global_expr = global_expr;
+ global_expr = 1;
+ expr_const1();
+ global_expr = saved_global_expr;
+ /* NOTE: symbols are accepted */
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST)
+ tcc_error("initializer element is not constant");
+ break;
+ case EXPR_ANY:
+ expr_eq();
+ break;
+ }
+}
+
/* t is the array or struct type. c is the array or struct
address. cur_index/cur_field is the pointer to the current
value. 'size_only' is true if only size info is needed (only used
in arrays) */
static void decl_designator(CType *type, Section *sec, unsigned long c,
int *cur_index, Sym **cur_field,
- int size_only)
+ int size_only, int have_elem)
{
Sym *s, *f;
int notfirst, index, index_last, align, l, nb_elems, elem_size;
@@ -5748,7 +5772,7 @@ static void decl_designator(CType *type, Section *sec, unsigned long c,
c += f->c;
}
}
- decl_initializer(type, sec, c, 0, size_only);
+ decl_initializer(type, sec, c, 0, size_only, have_elem);
/* XXX: make it more general */
if (!size_only && nb_elems > 1) {
@@ -5770,30 +5794,6 @@ static void decl_designator(CType *type, Section *sec, unsigned long c,
}
}
-#define EXPR_VAL 0
-#define EXPR_CONST 1
-#define EXPR_ANY 2
-
-static void parse_init_elem(int expr_type)
-{
- int saved_global_expr;
- switch(expr_type) {
- case EXPR_CONST:
- /* compound literals must be allocated globally in this case */
- saved_global_expr = global_expr;
- global_expr = 1;
- expr_const1();
- global_expr = saved_global_expr;
- /* NOTE: symbols are accepted */
- if ((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST)
- tcc_error("initializer element is not constant");
- break;
- case EXPR_ANY:
- expr_eq();
- break;
- }
-}
-
/* store a value or an expression directly in global data or in local array */
static void init_putv(CType *type, Section *sec, unsigned long c,
int v, int expr_type)
@@ -5957,7 +5957,7 @@ static void init_putz(CType *t, Section *sec, unsigned long c, int size)
dimension implicit array init handling). 'size_only' is true if
size only evaluation is wanted (only for arrays). */
static void decl_initializer(CType *type, Section *sec, unsigned long c,
- int first, int size_only)
+ int first, int size_only, int have_elem)
{
int index, array_length, n, no_oblock, nb, parlevel, parlevel1, i;
int size1, align1, expr_type;
@@ -5979,6 +5979,13 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
gen_vla_sp_save(c);
vla_sp_loc = c;
vlas_in_scope++;
+ } else if (!have_elem && tok != '{' && tok != TOK_LSTR &&
+ tok != TOK_STR && !size_only) {
+ parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
+ have_elem = 1;
+ }
+
+ if (type->t & VT_VLA) {
} else if (type->t & VT_ARRAY) {
s = type->ref;
n = s->c;
@@ -6051,8 +6058,9 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
}
} else {
index = 0;
- while (tok != '}') {
- decl_designator(type, sec, c, &index, NULL, size_only);
+ while (tok != '}' || have_elem) {
+ decl_designator(type, sec, c, &index, NULL, size_only, have_elem);
+ have_elem = 0;
if (n >= 0 && index >= n)
tcc_error("index too large");
/* must put zero in holes (note that doing it that way
@@ -6084,6 +6092,11 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
/* patch type size if needed */
if (n < 0)
s->c = array_length;
+ } else if (1 && have_elem && is_compatible_types(type, &vtop->type)) {
+ /* currently, we always use constant expression for globals
+ (may change for scripting case) */
+ expr_type = !sec ? EXPR_ANY : EXPR_CONST;
+ init_putv(type, sec, c, 0, expr_type);
} else if ((type->t & VT_BTYPE) == VT_STRUCT &&
(!first || tok == '{')) {
@@ -6140,8 +6153,9 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
array_length = 0;
index = 0;
n = s->c;
- while (tok != '}') {
- decl_designator(type, sec, c, NULL, &f, size_only);
+ while (tok != '}' || have_elem) {
+ decl_designator(type, sec, c, NULL, &f, size_only, have_elem);
+ have_elem = 0;
index = f->c;
if (!size_only && array_length < index) {
init_putz(type, sec, c + array_length,
@@ -6208,7 +6222,9 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
}
} else if (tok == '{') {
next();
- decl_initializer(type, sec, c, first, size_only);
+ if (have_elem)
+ tcc_error("shouldn't have parsed init element");
+ decl_initializer(type, sec, c, first, size_only, 0);
skip('}');
} else if (size_only) {
/* just skip expression */
@@ -6232,12 +6248,16 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
next();
}
} else {
+ if (!have_elem) {
+ /* This should happen only when we haven't parsed
+ the init element above for fear of committing a
+ string constant to memory too early. */
+ if (tok != TOK_STR && tok != TOK_LSTR)
+ expect("string constant");
+ parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
+ }
/* currently, we always use constant expression for globals
(may change for scripting case) */
- expr_type = EXPR_CONST;
- if (!sec)
- expr_type = EXPR_ANY;
- parse_init_elem(expr_type);
init_putv(type, sec, c, 0, expr_type);
}
}
@@ -6314,7 +6334,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
begin_macro(init_str, 1);
next();
- decl_initializer(type, NULL, 0, 1, 1);
+ decl_initializer(type, NULL, 0, 1, 1, 0);
/* prepare second initializer parsing */
macro_ptr = init_str->str;
next();
@@ -6473,7 +6493,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
#endif
}
if (has_init || (type->t & VT_VLA)) {
- decl_initializer(type, sec, addr, 1, 0);
+ decl_initializer(type, sec, addr, 1, 0, 0);
/* patch flexible array member size back to -1, */
/* for possible subsequent similar declarations */
if (flexible_array)
diff --git a/tests/tests2/86-struct-init.c b/tests/tests2/86-struct-init.c
new file mode 100644
index 0000000..71a2810
--- /dev/null
+++ b/tests/tests2/86-struct-init.c
@@ -0,0 +1,139 @@
+typedef unsigned char u8;
+typedef struct {} empty_s;
+struct contains_empty {
+ u8 a;
+ empty_s empty;
+ u8 b;
+};
+struct contains_empty ce = { { (1) }, (empty_s){}, 022, };
+/* The following decl of 'q' would demonstrate the TCC bug in init_putv when
+ handling copying compound literals. (Compound literals
+ aren't acceptable constant initializers in isoc99, but
+ we accept them like gcc, except for this case)
+//char *q = (char *){ "trara" }; */
+struct SS {u8 a[3], b; };
+struct SS sinit16[] = { { 1 }, 2 };
+struct S
+{
+ u8 a,b;
+ u8 c[2];
+};
+
+struct T
+{
+ u8 s[16];
+ u8 a;
+};
+
+struct U
+{
+ u8 a;
+ struct S s;
+ u8 b;
+ struct T t;
+};
+
+struct V
+{
+ struct S s;
+ struct T t;
+ u8 a;
+};
+
+struct W
+{
+ struct V t;
+ struct S s[];
+};
+
+struct S gs = ((struct S){1, 2, 3, 4});
+struct S gs2 = {1, 2, {3, 4}};
+struct T gt = {"hello", 42};
+struct U gu = {3, 5,6,7,8, 4, "huhu", 43};
+struct U gu2 = {3, {5,6,7,8}, 4, {"huhu", 43}};
+/* Optional braces around scalar initializers. Accepted, but with
+ a warning. */
+struct U gu3 = { {3}, {5,6,7,8,}, 4, {"huhu", 43}};
+/* Many superfluous braces and leaving out one initializer for U.s.c[1] */
+struct U gu4 = { 3, {5,6,7,}, 5, { "bla", {44}} };
+/* Superfluous braces and useless parens around values */
+struct S gs3 = { (1), {(2)}, {(((3))), {4}}};
+/* Superfluous braces, and leaving out braces for V.t, plus cast */
+struct V gv = {{{3},4,{5,6}}, "haha", (u8)45, 46};
+/* Compund literal */
+struct V gv2 = {(struct S){7,8,{9,10}}, {"hihi", 47}, 48};
+/* Parens around compound literal */
+struct V gv3 = {((struct S){7,8,{9,10}}), {"hoho", 49}, 50};
+/* Initialization of a flex array member (warns in GCC) */
+struct W gw = {{1,2,3,4}, {1,2,3,4,5}};
+
+#include <stdio.h>
+void print_ (const char *name, const u8 *p, long size)
+{
+ printf ("%s:", name);
+ while (size--) {
+ printf (" %x", *p++);
+ }
+ printf ("\n");
+}
+#define print(x) print_(#x, (u8*)&x, sizeof (x))
+#if 1
+void foo (struct W *w)
+{
+ struct S ls = {1, 2, 3, 4};
+ struct S ls2 = {1, 2, {3, 4}};
+ struct T lt = {"hello", 42};
+ struct U lu = {3, 5,6,7,8, 4, "huhu", 43};
+ struct U lu1 = {3, ls, 4, {"huhu", 43}};
+ struct U lu2 = {3, (ls), 4, {"huhu", 43}};
+ /* Incomplete bracing. */
+ struct U lu21 = {3, ls, 4, "huhu", 43};
+ /* Optional braces around scalar initializers. Accepted, but with
+ a warning. */
+ struct U lu3 = { 3, {5,6,7,8,}, 4, {"huhu", 43}};
+ /* Many superfluous braces and leaving out one initializer for U.s.c[1] */
+ struct U lu4 = { 3, {5,6,7,}, 5, { "bla", 44} };
+ /* Superfluous braces and useless parens around values */
+ struct S ls3 = { (1), (2), {(((3))), 4}};
+ /* Superfluous braces, and leaving out braces for V.t, plus cast */
+ struct V lv = {{3,4,{5,6}}, "haha", (u8)45, 46};
+ /* Compund literal */
+ struct V lv2 = {(struct S)w->t.s, {"hihi", 47}, 48};
+ /* Parens around compound literal */
+ struct V lv3 = {((struct S){7,8,{9,10}}), w->t.t, 50};
+ print(ls);
+ print(ls2);
+ print(lt);
+ print(lu);
+ print(lu1);
+ print(lu2);
+ print(lu21);
+ print(lu3);
+ print(lu4);
+ print(ls3);
+ print(lv);
+ print(lv2);
+ print(lv3);
+}
+#endif
+
+int main()
+{
+ print(ce);
+ print(gs);
+ print(gs2);
+ print(gt);
+ print(gu);
+ print(gu2);
+ print(gu3);
+ print(gu4);
+ print(gs3);
+ print(gv);
+ print(gv2);
+ print(gv3);
+ print(sinit16);
+ print(gw);
+ foo(&gw);
+ //printf("q: %s\n", q);
+ return 0;
+}
diff --git a/tests/tests2/86-struct-init.expect b/tests/tests2/86-struct-init.expect
new file mode 100644
index 0000000..221048f
--- /dev/null
+++ b/tests/tests2/86-struct-init.expect
@@ -0,0 +1,27 @@
+ce: 1 12
+gs: 1 2 3 4
+gs2: 1 2 3 4
+gt: 68 65 6c 6c 6f 0 0 0 0 0 0 0 0 0 0 0 2a
+gu: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
+gu2: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
+gu3: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
+gu4: 3 5 6 7 0 5 62 6c 61 0 0 0 0 0 0 0 0 0 0 0 0 0 2c
+gs3: 1 2 3 4
+gv: 3 4 5 6 68 61 68 61 0 0 0 0 0 0 0 0 0 0 0 0 2d 2e
+gv2: 7 8 9 a 68 69 68 69 0 0 0 0 0 0 0 0 0 0 0 0 2f 30
+gv3: 7 8 9 a 68 6f 68 6f 0 0 0 0 0 0 0 0 0 0 0 0 31 32
+sinit16: 1 0 0 0 2 0 0 0
+gw: 1 2 3 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ls: 1 2 3 4
+ls2: 1 2 3 4
+lt: 68 65 6c 6c 6f 0 0 0 0 0 0 0 0 0 0 0 2a
+lu: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
+lu1: 3 1 2 3 4 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
+lu2: 3 1 2 3 4 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
+lu21: 3 1 2 3 4 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
+lu3: 3 5 6 7 8 4 68 75 68 75 0 0 0 0 0 0 0 0 0 0 0 0 2b
+lu4: 3 5 6 7 0 5 62 6c 61 0 0 0 0 0 0 0 0 0 0 0 0 0 2c
+ls3: 1 2 3 4
+lv: 3 4 5 6 68 61 68 61 0 0 0 0 0 0 0 0 0 0 0 0 2d 2e
+lv2: 1 2 3 4 68 69 68 69 0 0 0 0 0 0 0 0 0 0 0 0 2f 30
+lv3: 7 8 9 a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32
diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile
index d8556ad..50cff85 100644
--- a/tests/tests2/Makefile
+++ b/tests/tests2/Makefile
@@ -54,7 +54,7 @@ all test: $(filter-out $(SKIP),$(TESTS))
# automatically generate .expect files with gcc:
%.expect :
- (gcc $*.c -o a.exe && ./a.exe $(ARGS)) >$*.expect 2>&1; rm -f a.exe
+ (gcc -w $*.c -o a.exe && ./a.exe $(ARGS)) >$*.expect 2>&1; rm -f a.exe
# tell make not to delete
.PRECIOUS: %.expect