From ed7d54651d2ef33dd6f644f23ba2fa64706e84db Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Sun, 31 Jul 2016 05:43:17 +0200 Subject: [PATCH] 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. --- tccgen.c | 98 ++++++++++++-------- tests/tests2/86-struct-init.c | 139 +++++++++++++++++++++++++++++ tests/tests2/86-struct-init.expect | 27 ++++++ tests/tests2/Makefile | 2 +- 4 files changed, 226 insertions(+), 40 deletions(-) create mode 100644 tests/tests2/86-struct-init.c create mode 100644 tests/tests2/86-struct-init.expect diff --git a/tccgen.c b/tccgen.c index a6c14d4f..064a9fe5 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 00000000..71a28109 --- /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 +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 00000000..221048f4 --- /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 d8556ad0..50cff859 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