From 9e86ebee944e7fc480fe49bf4f4f406aab672200 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Mon, 1 Aug 2016 22:11:49 +0200 Subject: [PATCH] struct-init: Correctly parse unnamed member initializers For union U { struct {int a,b}; int c; }; union U u = {{ 1, 2, }}; The unnamed first member of union U needs to actually exist in the structure so initializer parsing isn't confused about the double braces. That means also the a and b members must be part of _that_, not of union U directly. Which in turn means we need to do a bit more work for field lookup. See the testcase extension for more things that need to work. --- tccgen.c | 72 +++++++++++++++++++++--------- tests/tests2/86-struct-init.c | 23 ++++++++++ tests/tests2/86-struct-init.expect | 4 ++ 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/tccgen.c b/tccgen.c index 05fb50a..cb7e221 100644 --- a/tccgen.c +++ b/tccgen.c @@ -3181,6 +3181,22 @@ static void parse_attribute(AttributeDef *ad) } } +static Sym * find_field (CType *type, int v) +{ + Sym *s = type->ref; + v |= SYM_FIELD; + while ((s = s->next) != NULL) { + if ((s->v & SYM_FIELD) && (s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM) { + Sym *ret = find_field (&s->type, v); + if (ret) + return ret; + } + if (s->v == v) + break; + } + return s; +} + /* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */ static void struct_decl(CType *type, AttributeDef *ad, int u) { @@ -3404,13 +3420,40 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) #endif } if (v == 0 && (type1.t & VT_BTYPE) == VT_STRUCT) { + /* An anonymous struct/union. Adjust member offsets + to reflect the real offset of our containing struct. + Also set the offset of this anon member inside + the outer struct to be zero. Via this it + works when accessing the field offset directly + (from base object), as well as when recursing + members in initializer handling. */ + int v2 = btype.ref->v; + if (!(v2 & SYM_FIELD) && + (v2 & ~SYM_STRUCT) < SYM_FIRST_ANOM) { + Sym **pps; + /* This happens only with MS extensions. The + anon member has a named struct type, so it + potentially is shared with other references. + We need to unshare members so we can modify + them. */ + ass = type1.ref; + type1.ref = sym_push(anon_sym++ | SYM_FIELD, + &type1.ref->type, 0, + type1.ref->c); + pps = &type1.ref->next; + while ((ass = ass->next) != NULL) { + *pps = sym_push(ass->v, &ass->type, 0, ass->c); + pps = &((*pps)->next); + } + *pps = NULL; + } ass = type1.ref; - while ((ass = ass->next) != NULL) { - ss = sym_push(ass->v, &ass->type, 0, offset + ass->c); - *ps = ss; - ps = &ss->next; - } - } else if (v) { + while ((ass = ass->next) != NULL) + ass->c += offset; + offset = 0; + v = anon_sym++; + } + if (v) { ss = sym_push(v | SYM_FIELD, &type1, 0, offset); *ps = ss; ps = &ss->next; @@ -4536,13 +4579,7 @@ ST_FUNC void unary(void) next(); if (tok == TOK_CINT || tok == TOK_CUINT) expect("field name"); - s = vtop->type.ref; - /* find field */ - tok |= SYM_FIELD; - while ((s = s->next) != NULL) { - if (s->v == tok) - break; - } + s = find_field(&vtop->type, tok); if (!s) tcc_error("field not found: %s", get_tok_str(tok & ~SYM_FIELD, &tokc)); /* add field offset to pointer */ @@ -5729,14 +5766,7 @@ static void decl_designator(CType *type, Section *sec, unsigned long c, struct_field: if ((type->t & VT_BTYPE) != VT_STRUCT) expect("struct/union type"); - s = type->ref; - l |= SYM_FIELD; - f = s->next; - while (f) { - if (f->v == l) - break; - f = f->next; - } + f = find_field(type, l); if (!f) expect("field"); if (!notfirst) diff --git a/tests/tests2/86-struct-init.c b/tests/tests2/86-struct-init.c index fa0d967..6749f98 100644 --- a/tests/tests2/86-struct-init.c +++ b/tests/tests2/86-struct-init.c @@ -77,6 +77,25 @@ struct SU { }; struct SU gsu = {5,6}; +/* Unnamed struct/union members aren't ISO C, but it's a widely accepted + extension. See below for further extensions to that under -fms-extension.*/ +union UV { + struct {u8 a,b;}; + struct S s; +}; +union UV guv = {{6,5}}; +union UV guv2 = {{.b = 7, .a = 8}}; +union UV guv3 = {.b = 8, .a = 7}; + +/* Under -fms-extensions also the following is valid: +union UV2 { + struct Anon {u8 a,b;}; // unnamed member, but tagged struct, ... + struct S s; +}; +struct Anon gan = { 10, 11 }; // ... which makes it available here. +union UV2 guv4 = {{4,3}}; // and the other inits from above as well +*/ + #include void print_ (const char *name, const u8 *p, long size) { @@ -144,6 +163,10 @@ int main() print(sinit16); print(gw); print(gsu); + print(guv); + print(guv.b); + print(guv2); + print(guv3); 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 index 6ae60e0..a5333dc 100644 --- a/tests/tests2/86-struct-init.expect +++ b/tests/tests2/86-struct-init.expect @@ -13,6 +13,10 @@ 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 gsu: 5 6 +guv: 6 5 0 0 +guv.b: 5 +guv2: 8 7 0 0 +guv3: 7 8 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