diff --git a/src/cc65/declare.c b/src/cc65/declare.c index a6b2329051..19100781cb 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -656,13 +656,16 @@ static SymEntry* ParseUnionDecl (const char* Name) /* Check for fields without a name */ if (Decl.Ident[0] == '\0') { /* In cc65 mode, we allow anonymous structs/unions within - ** a struct. + ** a union. */ if (IS_Get (&Standard) >= STD_CC65 && IsClassStruct (Decl.Type)) { /* This is an anonymous struct or union. Copy the fields ** into the current level. */ - CopyAnonStructFields (&Decl, 0); + FieldSize = CopyAnonStructFields (&Decl, 0); + if (FieldSize > UnionSize) { + UnionSize = FieldSize; + } } else { /* A non bit-field without a name is legal but useless */ diff --git a/test/val/anon-struct1.c b/test/val/anon-struct1.c new file mode 100644 index 0000000000..9a737adca0 --- /dev/null +++ b/test/val/anon-struct1.c @@ -0,0 +1,111 @@ +/* + !!DESCRIPTION!! Make sure that structs/unions know the sizes of anonymous struct/union members + !!ORIGIN!! cc65 regression tests + !!LICENCE!! Public Domain + !!AUTHOR!! Greg King +*/ + +/* + see https://github.com/cc65/cc65/issues/641 +*/ + +#include + +static unsigned char fails = 0; + +typedef struct { + short s1; + struct { + int i1; + long l1; + char c1; + }; + char c2; +} s1_t; + +typedef struct { + short s1; + union { + int i1; + long l1; + char c1; + }; + char c2; +} s2_t; + +typedef union { + short s1; + struct { + int i1; + long l1; + char c1; + }; + char c2; +} u1_t; + +typedef union { + short s1; + union { + int i1; + long l1; + char c1; + }; + char c2; +} u2_t; + +static s1_t s1; +static s2_t s2; +static u1_t u1; +static u2_t u2; + +/* We use "variables" in the comparisons, so that we can avoid "constant +** comparison" and "Unreachable code" warnings (the second one currently +** can't be suppressed). +*/ + +static size_t const four = 4; +static size_t const seven = 7; +static size_t const ten = 10; + +int main(void) +{ + /* Check the types' sizes. */ + + if (sizeof (s1_t) != ten) { + printf("s1_t size is %u; it should be 10.\n", sizeof (s1_t)); + ++fails; + } + if (sizeof (s2_t) != seven) { + printf("s2_t size is %u; it should be 7.\n", sizeof (s2_t)); + ++fails; + } + if (sizeof (u1_t) != seven) { + printf("u1_t size is %u; it should be 7.\n", sizeof (u1_t)); + ++fails; + } + if (sizeof (u2_t) != four) { + printf("u2_t size is %u; it should be 4.\n", sizeof (u2_t)); + ++fails; + } + + /* Check the variables' sizes. */ + + if (sizeof s1 != ten) { + printf("s1 size is %u; it should be 10.\n", sizeof s1); + ++fails; + } + if (sizeof s2 != seven) { + printf("s2 size is %u; it should be 7.\n", sizeof s2); + ++fails; + } + if (sizeof u1 != seven) { + printf("u1 size is %u; it should be 7.\n", sizeof u1); + ++fails; + } + if (sizeof u2 != four) { + printf("u2 size is %u; it should be 4.\n", sizeof u2); + ++fails; + } + + return fails; +} diff --git a/test/val/anon-struct2.c b/test/val/anon-struct2.c new file mode 100644 index 0000000000..bdaddc8239 --- /dev/null +++ b/test/val/anon-struct2.c @@ -0,0 +1,152 @@ +/* + !!DESCRIPTION!! Make sure that the fields of anonymous structs/unions can be reached properly. + !!ORIGIN!! cc65 regression tests + !!LICENCE!! Public Domain + !!AUTHOR!! Greg King +*/ + +#include +#include + +static unsigned char fails = 0; + +typedef struct { + short s1; + struct { + char c1; + int i1; + long l1; + }; + char c2; +} s1_t; + +typedef struct { + char c2; + union { + int i1; + char c1; + long l1; + }; + short s1; +} s2_t; + +typedef union { + short s1; + struct { + int i1; + long l1; + char c1; + }; + char c2; +} u1_t; + +typedef union { + short s1; + union { + long l1; + char c1; + int i1; + }; + char c2; +} u2_t; + +typedef struct { + union { + short s1; + struct { + int i1; + long l1; + char c1; + }; + char c2; + }; + short s2; +} s3_t; + +static s1_t s1; +static s2_t s2; +static u1_t u1; +static u2_t u2; + +static long l2; +static int i2; + +/* We use "variables" in the comparisons, so that we can avoid "constant +** comparison" and "Unreachable code" warnings (the second one currently +** can't be suppressed). +*/ + +static size_t const zero = 0; +static size_t const one = 1; +static size_t const three = 3; +static size_t const five = 5; +static size_t const six = 6; +static size_t const seven = 7; +static size_t const nine = 9; + +int main(void) +{ + /* Can cc65 see the names of members of anonymous structs/unions? */ + + l2 = s1.l1; + l2 = s2.l1; + l2 = u1.l1; + l2 = u2.l1; + + i2 = s1.c1; + i2 = s1.c2; + i2 = s2.c1; + i2 = s2.c2; + i2 = u1.c1; + i2 = u1.c2; + i2 = u2.c1; + i2 = u2.c2; + + /* Does cc65 use the correct offsets of + ** the members of anonymous structs/unions? + */ + + if (offsetof(s1_t, i1) != three) { + printf("The offset of s1.i1 is %u; it should be 3.\n", offsetof(s1_t, i1)); + ++fails; + } + if (offsetof(s2_t, l1) != one) { + printf("The offset of s2.l1 is %u; it should be 1.\n", offsetof(s2_t, l1)); + ++fails; + } + if (offsetof(u1_t, c1) != six) { + printf("The offset of u1.c1 is %u; it should be 6.\n", offsetof(u1_t, c1)); + ++fails; + } + if (offsetof(u2_t, i1) != zero) { + printf("The offset of u2.i1 is %u; it should be 0.\n", offsetof(u2_t, i1)); + ++fails; + } + + /* Does cc65 use the correct offset of a member + ** that's later than an anonymous struct/union? + */ + + if (offsetof(s1_t, c2) != nine) { + printf("The offset of s1.c2 is %u; it should be 9.\n", offsetof(s1_t, c2)); + ++fails; + } + if (offsetof(s2_t, s1) != five) { + printf("The offset of s2.s1 is %u; it should be 5.\n", offsetof(s2_t, s1)); + ++fails; + } + if (offsetof(u1_t, c2) != zero) { + printf("The offset of u1.c2 is %u; it should be 0.\n", offsetof(u1_t, c2)); + ++fails; + } + if (offsetof(u2_t, c2) != zero) { + printf("The offset of u2.c2 is %u; it should be 0.\n", offsetof(u2_t, c2)); + ++fails; + } + if (offsetof(s3_t, s2) != seven) { + printf("The offset of s3.s2 is %u; it should be 7.\n", offsetof(s3_t, s2)); + ++fails; + } + + return fails; +}