aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Matz <matz@suse.de>2016-08-01 22:11:49 +0200
committerMichael Matz <matz@suse.de>2016-12-15 17:47:09 +0100
commit9e86ebee944e7fc480fe49bf4f4f406aab672200 (patch)
tree159bf595663596d64aac563929c125e1c4fdd5ca
parent21da73c383f84b29c04a504d3d7ec56008029db6 (diff)
downloadtinycc-9e86ebee944e7fc480fe49bf4f4f406aab672200.tar.gz
tinycc-9e86ebee944e7fc480fe49bf4f4f406aab672200.tar.bz2
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.
-rw-r--r--tccgen.c72
-rw-r--r--tests/tests2/86-struct-init.c23
-rw-r--r--tests/tests2/86-struct-init.expect4
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 <stdio.h>
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