mirror of
https://github.com/mirror/tinycc.git
synced 2025-02-24 07:50:12 +08:00
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.
This commit is contained in:
parent
968bccdd2a
commit
ed7d54651d
98
tccgen.c
98
tccgen.c
@ -74,7 +74,7 @@ static int is_compatible_types(CType *type1, CType *type2);
|
|||||||
static int parse_btype(CType *type, AttributeDef *ad);
|
static int parse_btype(CType *type, AttributeDef *ad);
|
||||||
static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
|
static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
|
||||||
static void parse_expr_type(CType *type);
|
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 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 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);
|
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
|
/* t is the array or struct type. c is the array or struct
|
||||||
address. cur_index/cur_field is the pointer to the current
|
address. cur_index/cur_field is the pointer to the current
|
||||||
value. 'size_only' is true if only size info is needed (only used
|
value. 'size_only' is true if only size info is needed (only used
|
||||||
in arrays) */
|
in arrays) */
|
||||||
static void decl_designator(CType *type, Section *sec, unsigned long c,
|
static void decl_designator(CType *type, Section *sec, unsigned long c,
|
||||||
int *cur_index, Sym **cur_field,
|
int *cur_index, Sym **cur_field,
|
||||||
int size_only)
|
int size_only, int have_elem)
|
||||||
{
|
{
|
||||||
Sym *s, *f;
|
Sym *s, *f;
|
||||||
int notfirst, index, index_last, align, l, nb_elems, elem_size;
|
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;
|
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 */
|
/* XXX: make it more general */
|
||||||
if (!size_only && nb_elems > 1) {
|
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 */
|
/* 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,
|
static void init_putv(CType *type, Section *sec, unsigned long c,
|
||||||
int v, int expr_type)
|
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
|
dimension implicit array init handling). 'size_only' is true if
|
||||||
size only evaluation is wanted (only for arrays). */
|
size only evaluation is wanted (only for arrays). */
|
||||||
static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
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 index, array_length, n, no_oblock, nb, parlevel, parlevel1, i;
|
||||||
int size1, align1, expr_type;
|
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);
|
gen_vla_sp_save(c);
|
||||||
vla_sp_loc = c;
|
vla_sp_loc = c;
|
||||||
vlas_in_scope++;
|
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) {
|
} else if (type->t & VT_ARRAY) {
|
||||||
s = type->ref;
|
s = type->ref;
|
||||||
n = s->c;
|
n = s->c;
|
||||||
@ -6051,8 +6058,9 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
index = 0;
|
index = 0;
|
||||||
while (tok != '}') {
|
while (tok != '}' || have_elem) {
|
||||||
decl_designator(type, sec, c, &index, NULL, size_only);
|
decl_designator(type, sec, c, &index, NULL, size_only, have_elem);
|
||||||
|
have_elem = 0;
|
||||||
if (n >= 0 && index >= n)
|
if (n >= 0 && index >= n)
|
||||||
tcc_error("index too large");
|
tcc_error("index too large");
|
||||||
/* must put zero in holes (note that doing it that way
|
/* 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 */
|
/* patch type size if needed */
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
s->c = array_length;
|
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 &&
|
} else if ((type->t & VT_BTYPE) == VT_STRUCT &&
|
||||||
(!first || tok == '{')) {
|
(!first || tok == '{')) {
|
||||||
|
|
||||||
@ -6140,8 +6153,9 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
|||||||
array_length = 0;
|
array_length = 0;
|
||||||
index = 0;
|
index = 0;
|
||||||
n = s->c;
|
n = s->c;
|
||||||
while (tok != '}') {
|
while (tok != '}' || have_elem) {
|
||||||
decl_designator(type, sec, c, NULL, &f, size_only);
|
decl_designator(type, sec, c, NULL, &f, size_only, have_elem);
|
||||||
|
have_elem = 0;
|
||||||
index = f->c;
|
index = f->c;
|
||||||
if (!size_only && array_length < index) {
|
if (!size_only && array_length < index) {
|
||||||
init_putz(type, sec, c + array_length,
|
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 == '{') {
|
} else if (tok == '{') {
|
||||||
next();
|
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('}');
|
skip('}');
|
||||||
} else if (size_only) {
|
} else if (size_only) {
|
||||||
/* just skip expression */
|
/* just skip expression */
|
||||||
@ -6232,12 +6248,16 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
|||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
/* currently, we always use constant expression for globals
|
||||||
(may change for scripting case) */
|
(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);
|
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);
|
begin_macro(init_str, 1);
|
||||||
next();
|
next();
|
||||||
decl_initializer(type, NULL, 0, 1, 1);
|
decl_initializer(type, NULL, 0, 1, 1, 0);
|
||||||
/* prepare second initializer parsing */
|
/* prepare second initializer parsing */
|
||||||
macro_ptr = init_str->str;
|
macro_ptr = init_str->str;
|
||||||
next();
|
next();
|
||||||
@ -6473,7 +6493,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (has_init || (type->t & VT_VLA)) {
|
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, */
|
/* patch flexible array member size back to -1, */
|
||||||
/* for possible subsequent similar declarations */
|
/* for possible subsequent similar declarations */
|
||||||
if (flexible_array)
|
if (flexible_array)
|
||||||
|
139
tests/tests2/86-struct-init.c
Normal file
139
tests/tests2/86-struct-init.c
Normal file
@ -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;
|
||||||
|
}
|
27
tests/tests2/86-struct-init.expect
Normal file
27
tests/tests2/86-struct-init.expect
Normal file
@ -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
|
@ -54,7 +54,7 @@ all test: $(filter-out $(SKIP),$(TESTS))
|
|||||||
|
|
||||||
# automatically generate .expect files with gcc:
|
# automatically generate .expect files with gcc:
|
||||||
%.expect :
|
%.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
|
# tell make not to delete
|
||||||
.PRECIOUS: %.expect
|
.PRECIOUS: %.expect
|
||||||
|
Loading…
Reference in New Issue
Block a user