diff --git a/tcc.c b/tcc.c index 53d0d9f..ca22f83 100644 --- a/tcc.c +++ b/tcc.c @@ -264,7 +264,7 @@ int expr_const(); void expr_eq(); void expr(); void decl(); -void decl_assign(int t, int c, int first); +void decl_initializer(int t, int c, int first, int size_only); int gv(); void move_reg(); void save_reg(); @@ -1168,7 +1168,7 @@ int *macro_arg_subst(Sym **nested_list, int *macro_str, Sym *args) tok_add(&str, &len, t); } } else if (t == TOK_NUM || t == TOK_CCHAR || t == TOK_STR) { - tok_add2(&str, &len, t, *macro_ptr++); + tok_add2(&str, &len, t, *macro_str++); } else { s = sym_find2(args, t); if (s) { @@ -2906,8 +2906,10 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg) } /* 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 */ -void decl_designator(int t, int c, int *cur_index, Sym **cur_field) + 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) */ +void decl_designator(int t, int c, int *cur_index, Sym **cur_field, int size_only) { Sym *s, *f; int notfirst, index, align; @@ -2964,39 +2966,79 @@ void decl_designator(int t, int c, int *cur_index, Sym **cur_field) c += f->c; } } - decl_assign(t, c, 0); + decl_initializer(t, c, 0, size_only); +} + +/* store a value or an expression directly in global data or in local array */ + +void init_putv(int t, int c, int v, int is_expr) +{ + if ((t & VT_VALMASK) == VT_CONST) { + if (is_expr) + v = expr_const(); + if (t & VT_BYTE) + *(char *)c = v; + else if (t & VT_SHORT) + *(short *)c = v; + else + *(int *)c = v; + } else { + vt = t; + vc = c; + vpush(); + if (is_expr) + expr_eq(); + else + vset(VT_CONST, v); + vstore(); + } } /* 't' contains the type and storage info. c is the address of the object. 'first' is true if array '{' must be read (multi dimension - implicit array init handling). */ -void decl_assign(int t, int c, int first) + implicit array init handling). 'size_only' is true if size only + evaluation is wanted (only for arrays). */ +void decl_initializer(int t, int c, int first, int size_only) { - int v, index, index_max, t1, n, no_oblock; + int index, array_length, t1, n, no_oblock, nb, parlevel, i; Sym *s, *f; TokenSym *ts; if (t & VT_ARRAY) { s = sym_find(((unsigned)t >> VT_STRUCT_SHIFT)); n = s->c; - index_max = 0; + array_length = 0; if (tok == TOK_STR) { t1 = pointed_type(t); if (!(t1 & VT_BYTE)) error("invalid type"); - if ((t & VT_VALMASK) == VT_CONST) { - while (tok == TOK_STR) { - ts = (TokenSym *)tokc; - memcpy((void *)c, ts->str, ts->len); - c += ts->len; - index_max += ts->len; - next(); + /* XXX: move multiple string parsing in parser ? */ + while (tok == TOK_STR) { + ts = (TokenSym *)tokc; + /* compute maximum number of chars wanted */ + nb = ts->len; + if (n >= 0 && nb > (n - array_length)) + nb = n - array_length; + if (!size_only) { + if (ts->len > nb) + warning("initializer-string for array is too long"); + for(i=0;istr[i], 0); + c++; + } } - *(char *)c++ = 0; - } else { - error("local string init not handled"); + array_length += nb; + next(); + } + /* only add trailing zero if enough storage (no + warning in this case since it is standard) */ + if (n < 0 || array_length < n) { + if (!size_only) { + init_putv(t1, c, 0, 0); + c++; + } + array_length++; } - /* string init */ } else { no_oblock = 0; if (!first && tok != '{') @@ -3005,12 +3047,12 @@ void decl_assign(int t, int c, int first) skip('{'); index = 0; while (tok != '}') { - decl_designator(t, c, &index, NULL); + decl_designator(t, c, &index, NULL, size_only); if (n >= 0 && index >= n) error("index too large"); - if (index > index_max) - index_max = index; index++; + if (index > array_length) + array_length = index; /* special test for multi dimensional arrays (may not be strictly correct if designators are used at the same time) */ @@ -3025,45 +3067,43 @@ void decl_assign(int t, int c, int first) } /* patch type size if needed */ if (n < 0) - s->c = index_max + 1; + s->c = array_length; } else if (t & VT_STRUCT) { /* XXX: union needs only one init */ skip('{'); s = sym_find(((unsigned)t >> VT_STRUCT_SHIFT) | SYM_STRUCT); f = s->next; while (tok != '}') { - decl_designator(t, c, NULL, &f); + decl_designator(t, c, NULL, &f, size_only); if (tok == '}') break; skip(','); f = f->next; } skip('}'); + } else if (size_only) { + /* just skip expression */ + parlevel = 0; + while ((parlevel > 0 || (tok != '}' && tok != ',')) && + tok != -1) { + if (tok == '(') + parlevel++; + else if (tok == ')') + parlevel--; + next(); + } } else { - if ((t & VT_VALMASK) == VT_CONST) { - v = expr_const(); - if (t & VT_BYTE) - *(char *)c = v; - else if (t & VT_SHORT) - *(short *)c = v; - else - *(int *)c = v; - } else { - vt = t; - vc = c; - vpush(); - expr_eq(); - vstore(); - } + init_putv(t, c, 0, 1); } } /* 'l' is VT_LOCAL or VT_CONST to define default storage type */ void decl(l) { - int *a, t, b, size, align, v, u, n, addr; + int *a, t, b, size, align, v, u, n, addr, tok1; Sym *sym; - + int *init_str, init_len, level, *saved_macro_ptr; + while (b = ist()) { if ((b & (VT_ENUM | VT_STRUCT)) && tok == ';') { /* we accept no variable after */ @@ -3130,16 +3170,67 @@ void decl(l) u = VT_CONST; u |= t; size = type_size(t, &align); + /* If unknown size, we must evaluate it before + evaluating initializers because + initializers can generate global data too + (e.g. string pointers or ISOC99 compound + literals). It also simplifies local + initializers handling */ + init_len = 0; + init_str = NULL; + saved_macro_ptr = NULL; /* avoid warning */ + tok1 = 0; + if (size < 0) { + if (tok != '=') + error("unknown size for '%s'", get_tok_str(v, 0)); + tok_add2(&init_str, &init_len, tok, tokc); + next(); + /* get all init string */ + level = 0; + while (level > 0 || (tok != ',' && tok != ';')) { + if (tok < 0) + error("unexpect end of file in initializer for '%s'", + get_tok_str(v, 0)); + tok_add2(&init_str, &init_len, tok, tokc); + if (tok == '{') + level++; + else if (tok == '}') { + if (level == 0) + break; + level--; + } + next(); + } + tok1 = tok; + tok_add(&init_str, &init_len, -1); + tok_add(&init_str, &init_len, 0); + + /* compute size */ + saved_macro_ptr = macro_ptr; + macro_ptr = init_str; + next(); + skip('='); + decl_initializer(u, 0, 1, 1); + /* prepare second initializer parsing */ + macro_ptr = init_str; + next(); + + /* if still unknown size, error */ + size = type_size(t, &align); + if (size < 0) + error("unknown size for '%s'", get_tok_str(v, 0)); + } if ((u & VT_VALMASK) == VT_LOCAL) { - /* XXX: cannot use implicit size for local - storage */ - if (size < 0) - error("size must be known for locals"); loc = (loc - size) & -align; addr = loc; } else { glo = (glo + align - 1) & -align; addr = glo; + /* very important to increment global + pointer at this time because + initializers themselves can create new + initializers */ + glo += size; } if (tok == '=') { next(); @@ -3149,20 +3240,17 @@ void decl(l) n = 1; next(); } - decl_assign(u, addr, 1); + decl_initializer(u, addr, 1, 0); if (n) skip('}'); + /* restore parse state if needed */ + if (init_str) { + free(init_str); + macro_ptr = saved_macro_ptr; + tok = tok1; + } } sym_push(v, u, addr); - /* if global, add size */ - if ((u & VT_VALMASK) == VT_CONST) { - /* must recompute size if it was an array - with implicit size */ - size = type_size(t, &align); - if (size < 0) - error("invalid size"); - glo += size; - } } } if (tok != ',') {