Fix some string literal expressions in initializers
Things like 'char x[] = { "foo"[1], 0 }' (which should initialize a two-character array 'x' with "o\0"). See added testcase.
This commit is contained in:
parent
b2d351e0ec
commit
7b6931ed1f
3 changed files with 97 additions and 37 deletions
2
tcc.h
2
tcc.h
|
@ -449,8 +449,8 @@ typedef union CValue {
|
||||||
float f;
|
float f;
|
||||||
uint64_t i;
|
uint64_t i;
|
||||||
struct {
|
struct {
|
||||||
int size;
|
|
||||||
const void *data;
|
const void *data;
|
||||||
|
int size;
|
||||||
} str;
|
} str;
|
||||||
int tab[LDOUBLE_SIZE/4];
|
int tab[LDOUBLE_SIZE/4];
|
||||||
} CValue;
|
} CValue;
|
||||||
|
|
88
tccgen.c
88
tccgen.c
|
@ -81,6 +81,7 @@ ST_DATA int func_vc;
|
||||||
static int last_line_num, new_file, func_ind; /* debug info control */
|
static int last_line_num, new_file, func_ind; /* debug info control */
|
||||||
ST_DATA const char *funcname;
|
ST_DATA const char *funcname;
|
||||||
ST_DATA CType int_type, func_old_type, char_pointer_type;
|
ST_DATA CType int_type, func_old_type, char_pointer_type;
|
||||||
|
static CString initstr;
|
||||||
|
|
||||||
#if PTR_SIZE == 4
|
#if PTR_SIZE == 4
|
||||||
#define VT_SIZE_T (VT_INT | VT_UNSIGNED)
|
#define VT_SIZE_T (VT_INT | VT_UNSIGNED)
|
||||||
|
@ -779,6 +780,7 @@ ST_FUNC void tccgen_init(TCCState *s1)
|
||||||
#ifdef precedence_parser
|
#ifdef precedence_parser
|
||||||
init_prec();
|
init_prec();
|
||||||
#endif
|
#endif
|
||||||
|
cstr_new(&initstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ST_FUNC int tccgen_compile(TCCState *s1)
|
ST_FUNC int tccgen_compile(TCCState *s1)
|
||||||
|
@ -810,6 +812,7 @@ ST_FUNC int tccgen_compile(TCCState *s1)
|
||||||
|
|
||||||
ST_FUNC void tccgen_finish(TCCState *s1)
|
ST_FUNC void tccgen_finish(TCCState *s1)
|
||||||
{
|
{
|
||||||
|
cstr_free(&initstr);
|
||||||
free_inline_functions(s1);
|
free_inline_functions(s1);
|
||||||
sym_pop(&global_stack, NULL, 0);
|
sym_pop(&global_stack, NULL, 0);
|
||||||
sym_pop(&local_stack, NULL, 0);
|
sym_pop(&local_stack, NULL, 0);
|
||||||
|
@ -3219,7 +3222,6 @@ redo:
|
||||||
gen_cast_s(VT_INT);
|
gen_cast_s(VT_INT);
|
||||||
#endif
|
#endif
|
||||||
type1 = vtop[-1].type;
|
type1 = vtop[-1].type;
|
||||||
type1.t &= ~VT_ARRAY;
|
|
||||||
if (vtop[-1].type.t & VT_VLA)
|
if (vtop[-1].type.t & VT_VLA)
|
||||||
vla_runtime_pointed_size(&vtop[-1].type);
|
vla_runtime_pointed_size(&vtop[-1].type);
|
||||||
else {
|
else {
|
||||||
|
@ -3249,6 +3251,7 @@ redo:
|
||||||
{
|
{
|
||||||
gen_opic(op);
|
gen_opic(op);
|
||||||
}
|
}
|
||||||
|
type1.t &= ~VT_ARRAY;
|
||||||
/* put again type if gen_opic() swaped operands */
|
/* put again type if gen_opic() swaped operands */
|
||||||
vtop->type = type1;
|
vtop->type = type1;
|
||||||
}
|
}
|
||||||
|
@ -7229,12 +7232,12 @@ static void parse_init_elem(int expr_type)
|
||||||
/* NOTE: symbols are accepted, as well as lvalue for anon symbols
|
/* NOTE: symbols are accepted, as well as lvalue for anon symbols
|
||||||
(compound literals). */
|
(compound literals). */
|
||||||
if (((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST
|
if (((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST
|
||||||
&& ((vtop->r & (VT_SYM|VT_LVAL)) != (VT_SYM|VT_LVAL)
|
&& ((vtop->r & (VT_SYM|VT_LVAL)) != (VT_SYM|VT_LVAL)
|
||||||
|| vtop->sym->v < SYM_FIRST_ANOM))
|
|| vtop->sym->v < SYM_FIRST_ANOM))
|
||||||
#ifdef TCC_TARGET_PE
|
#ifdef TCC_TARGET_PE
|
||||||
|| ((vtop->r & VT_SYM) && vtop->sym->a.dllimport)
|
|| ((vtop->r & VT_SYM) && vtop->sym->a.dllimport)
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
tcc_error("initializer element is not constant");
|
tcc_error("initializer element is not constant");
|
||||||
break;
|
break;
|
||||||
case EXPR_ANY:
|
case EXPR_ANY:
|
||||||
|
@ -7442,7 +7445,7 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
|
||||||
anonymous one. That is, there's no difference in vtop
|
anonymous one. That is, there's no difference in vtop
|
||||||
between '(void *){x}' and '&(void *){x}'. Ignore
|
between '(void *){x}' and '&(void *){x}'. Ignore
|
||||||
pointer typed entities here. Hopefully no real code
|
pointer typed entities here. Hopefully no real code
|
||||||
will every use compound literals with scalar type. */
|
will ever use compound literals with scalar type. */
|
||||||
(vtop->type.t & VT_BTYPE) != VT_PTR) {
|
(vtop->type.t & VT_BTYPE) != VT_PTR) {
|
||||||
/* These come from compound literals, memcpy stuff over. */
|
/* These come from compound literals, memcpy stuff over. */
|
||||||
Section *ssec;
|
Section *ssec;
|
||||||
|
@ -7450,7 +7453,7 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
|
||||||
ElfW_Rel *rel;
|
ElfW_Rel *rel;
|
||||||
esym = elfsym(vtop->sym);
|
esym = elfsym(vtop->sym);
|
||||||
ssec = tcc_state->sections[esym->st_shndx];
|
ssec = tcc_state->sections[esym->st_shndx];
|
||||||
memmove (ptr, ssec->data + esym->st_value, size);
|
memmove (ptr, ssec->data + esym->st_value + (int)vtop->c.i, size);
|
||||||
if (ssec->reloc) {
|
if (ssec->reloc) {
|
||||||
/* We need to copy over all memory contents, and that
|
/* We need to copy over all memory contents, and that
|
||||||
includes relocations. Use the fact that relocs are
|
includes relocations. Use the fact that relocs are
|
||||||
|
@ -7593,7 +7596,7 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
|
||||||
static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
int len, n, no_oblock, nb, i;
|
int len, n, no_oblock, i;
|
||||||
int size1, align1;
|
int size1, align1;
|
||||||
Sym *s, *f;
|
Sym *s, *f;
|
||||||
Sym indexsym;
|
Sym indexsym;
|
||||||
|
@ -7641,41 +7644,54 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
||||||
(t1->t & VT_BTYPE) == VT_INT
|
(t1->t & VT_BTYPE) == VT_INT
|
||||||
#endif
|
#endif
|
||||||
) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
|
) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
|
||||||
|
int nb;
|
||||||
len = 0;
|
len = 0;
|
||||||
|
cstr_reset(&initstr);
|
||||||
|
if (size1 != (tok == TOK_STR ? 1 : sizeof(nwchar_t)))
|
||||||
|
tcc_error("unhandled string literal merging");
|
||||||
while (tok == TOK_STR || tok == TOK_LSTR) {
|
while (tok == TOK_STR || tok == TOK_LSTR) {
|
||||||
int cstr_len, ch;
|
if (initstr.size)
|
||||||
|
initstr.size -= size1;
|
||||||
/* compute maximum number of chars wanted */
|
|
||||||
if (tok == TOK_STR)
|
if (tok == TOK_STR)
|
||||||
cstr_len = tokc.str.size;
|
len += tokc.str.size;
|
||||||
else
|
else
|
||||||
cstr_len = tokc.str.size / sizeof(nwchar_t);
|
len += tokc.str.size / sizeof(nwchar_t);
|
||||||
cstr_len--;
|
len--;
|
||||||
nb = cstr_len;
|
cstr_cat(&initstr, tokc.str.data, tokc.str.size);
|
||||||
if (n >= 0 && nb > (n - len))
|
next();
|
||||||
nb = n - len;
|
}
|
||||||
if (!(flags & DIF_SIZE_ONLY)) {
|
if (tok != ')' && tok != '}' && tok != ',' && tok != ';'
|
||||||
if (cstr_len > nb)
|
&& tok != TOK_EOF) {
|
||||||
tcc_warning("initializer-string for array is too long");
|
/* Not a lone literal but part of a bigger expression. */
|
||||||
/* in order to go faster for common case (char
|
unget_tok(size1 == 1 ? TOK_STR : TOK_LSTR);
|
||||||
string in global variable, we handle it
|
tokc.str.size = initstr.size;
|
||||||
specifically */
|
tokc.str.data = initstr.data;
|
||||||
if (sec && tok == TOK_STR && size1 == 1) {
|
indexsym.c = 0;
|
||||||
if (!NODATA_WANTED)
|
f = &indexsym;
|
||||||
memcpy(sec->data + c + len, tokc.str.data, nb);
|
goto do_init_list;
|
||||||
} else {
|
}
|
||||||
for(i=0;i<nb;i++) {
|
nb = len;
|
||||||
if (tok == TOK_STR)
|
if (n >= 0 && len > n)
|
||||||
ch = ((unsigned char *)tokc.str.data)[i];
|
nb = n;
|
||||||
else
|
if (!(flags & DIF_SIZE_ONLY)) {
|
||||||
ch = ((nwchar_t *)tokc.str.data)[i];
|
if (len > nb)
|
||||||
vpushi(ch);
|
tcc_warning("initializer-string for array is too long");
|
||||||
init_putv(t1, sec, c + (len + i) * size1);
|
/* in order to go faster for common case (char
|
||||||
}
|
string in global variable, we handle it
|
||||||
|
specifically */
|
||||||
|
if (sec && size1 == 1) {
|
||||||
|
if (!NODATA_WANTED)
|
||||||
|
memcpy(sec->data + c, initstr.data, nb);
|
||||||
|
} else {
|
||||||
|
for(i=0;i<nb;i++) {
|
||||||
|
if (size1 == 1)
|
||||||
|
ch = ((unsigned char *)initstr.data)[i];
|
||||||
|
else
|
||||||
|
ch = ((nwchar_t *)initstr.data)[i];
|
||||||
|
vpushi(ch);
|
||||||
|
init_putv(t1, sec, c + i * size1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
len += nb;
|
|
||||||
next();
|
|
||||||
}
|
}
|
||||||
/* only add trailing zero if enough storage (no
|
/* only add trailing zero if enough storage (no
|
||||||
warning in this case since it is standard) */
|
warning in this case since it is standard) */
|
||||||
|
|
|
@ -467,6 +467,48 @@ int ret(a)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char str_ag1[] = "b";
|
||||||
|
char str_ag2[] = { "b" };
|
||||||
|
/*char str_bg1[] = ("cccc"); GCC accepts this with pedantic warning, TCC not */
|
||||||
|
char str_ag3[] = { "ab"[1], 0 };
|
||||||
|
char str_ag4[2] = { "b" };
|
||||||
|
char str_x[2] = { "xy" "z"[2], 0 };
|
||||||
|
char *str_ar[] = { "one", "two" };
|
||||||
|
struct str_SS {unsigned char a[3], b; };
|
||||||
|
struct str_SS str_sinit15 = { "r" };
|
||||||
|
struct str_SS str_sinit16[] = { { "q" }, 2 };
|
||||||
|
|
||||||
|
static void string_test2()
|
||||||
|
{
|
||||||
|
char *p = "hello";
|
||||||
|
char a3[2] = { "p" };
|
||||||
|
char a4[2] = { "ab" "c"[2], 0 };
|
||||||
|
char *pa1 = "def" + 1;
|
||||||
|
char *pa2 = { "xyz" + 1 };
|
||||||
|
int i = 0;
|
||||||
|
struct str_SS ss = { { [0 ... 1] = 'a' }, 0 };
|
||||||
|
puts("string_test2");
|
||||||
|
puts(str_ag1);
|
||||||
|
puts(str_ag2);
|
||||||
|
/*puts(str_bg1);*/
|
||||||
|
puts(str_ag3);
|
||||||
|
puts(str_ag4);
|
||||||
|
puts(str_x);
|
||||||
|
puts(str_sinit15.a);
|
||||||
|
puts(str_sinit16[0].a);
|
||||||
|
puts(a3);
|
||||||
|
puts(a4);
|
||||||
|
puts(p);
|
||||||
|
puts("world");
|
||||||
|
printf("%s\n", "bla");
|
||||||
|
puts(str_ar[0]);
|
||||||
|
puts(str_ar[1]);
|
||||||
|
puts(ss.a);
|
||||||
|
puts(i >= 0 ? "one" : "two");
|
||||||
|
puts(pa1);
|
||||||
|
puts(pa2);
|
||||||
|
}
|
||||||
|
|
||||||
void ps(const char *s)
|
void ps(const char *s)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
@ -511,8 +553,10 @@ void string_test()
|
||||||
num(b);
|
num(b);
|
||||||
b = b * 2;
|
b = b * 2;
|
||||||
}
|
}
|
||||||
|
string_test2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void if1t(int n, int a, int b, int c)
|
void if1t(int n, int a, int b, int c)
|
||||||
{
|
{
|
||||||
if (a && b) printf("if1t: %d 1 %d %d\n", n, a, b);
|
if (a && b) printf("if1t: %d 1 %d %d\n", n, a, b);
|
||||||
|
|
Loading…
Reference in a new issue