1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-28 19:29:53 +00:00

Merge pull request #2266 from acqn/StructFix

[cc65] Fixes for structs/unions
This commit is contained in:
Bob Andrews 2023-11-28 15:00:58 +01:00 committed by GitHub
commit 97cfb8c67a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 230 additions and 68 deletions

View File

@ -44,6 +44,7 @@
#include "scanner.h" #include "scanner.h"
#include "stackptr.h" #include "stackptr.h"
#include "stdnames.h" #include "stdnames.h"
#include "symentry.h"
#include "typecmp.h" #include "typecmp.h"
#include "typeconv.h" #include "typeconv.h"
@ -82,6 +83,8 @@ static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr)
if (TypeCmp (ltype, RExpr->Type).C < TC_STRICT_COMPATIBLE) { if (TypeCmp (ltype, RExpr->Type).C < TC_STRICT_COMPATIBLE) {
TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1, TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1,
"Incompatible types in assignment to '%s' from '%s'"); "Incompatible types in assignment to '%s' from '%s'");
} else if (SymHasConstMember (ltype->A.S)) {
Error ("Assignment to read only variable");
} }
/* Do we copy the value directly using the primary? */ /* Do we copy the value directly using the primary? */
@ -627,7 +630,7 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op)
} }
} else if (IsQualConst (ltype)) { } else if (IsQualConst (ltype)) {
/* Check for assignment to const */ /* Check for assignment to const */
Error ("Assignment to const"); Error ("Assignment to const variable");
} }
} }
@ -686,7 +689,7 @@ void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op)
Error ("Invalid lvalue in assignment"); Error ("Invalid lvalue in assignment");
} else if (IsQualConst (Expr->Type)) { } else if (IsQualConst (Expr->Type)) {
/* The left side must not be const qualified */ /* The left side must not be const qualified */
Error ("Assignment to const"); Error ("Assignment to const variable");
} }
} }

View File

@ -983,41 +983,58 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
/* Check for a bit-field declaration */ /* Check for a bit-field declaration */
FieldWidth = ParseFieldWidth (&Decl); FieldWidth = ParseFieldWidth (&Decl);
/* Ignore zero sized bit fields in a union */ /* Check for fields without names */
if (FieldWidth == 0) {
goto NextMember;
}
/* Check for fields without a name */
if (Decl.Ident[0] == '\0') { if (Decl.Ident[0] == '\0') {
/* In cc65 mode, we allow anonymous structs/unions within if (FieldWidth < 0) {
** a union. /* In cc65 mode, we allow anonymous structs/unions within
*/ ** 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.
*/ */
AnonFieldName (Decl.Ident, "field", UnionTagEntry->V.S.ACount); SymEntry* TagEntry;
} else { if (IS_Get (&Standard) >= STD_CC65 &&
/* A non bit-field without a name is legal but useless */ IsClassStruct (Decl.Type) &&
Warning ("Declaration does not declare anything"); (TagEntry = GetESUTagSym (Decl.Type)) &&
} SymHasAnonName (TagEntry)) {
} /* This is an anonymous struct or union */
AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount);
/* Check for incomplete types including 'void' */ /* Ignore CVR qualifiers */
if (IsIncompleteType (Decl.Type)) { if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) {
Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type));
Decl.Type[0].C &= ~T_QUAL_CVR;
}
} else {
/* A non bit-field without a name is legal but useless */
Warning ("Declaration does not declare anything");
goto NextMember;
}
} else if (FieldWidth > 0) {
/* A bit-field without a name will get an anonymous one */
AnonName (Decl.Ident, "bit-field");
}
} else if (IsIncompleteType (Decl.Type)) {
Error ("Field '%s' has incomplete type '%s'", Error ("Field '%s' has incomplete type '%s'",
Decl.Ident, Decl.Ident,
GetFullTypeName (Decl.Type)); GetFullTypeName (Decl.Type));
} }
/* Check for const types */
if (IsQualConst (Decl.Type)) {
Flags |= SC_HAVECONST;
}
/* Ignore zero sized bit fields in a union */
if (FieldWidth == 0) {
goto NextMember;
}
/* Handle sizes */ /* Handle sizes */
FieldSize = SizeOf (Decl.Type); FieldSize = SizeOf (Decl.Type);
if (FieldSize > UnionSize) { if (FieldSize > UnionSize) {
UnionSize = FieldSize; UnionSize = FieldSize;
} }
/* Add a field entry to the table. */ /* Add a field entry to the table */
if (FieldWidth > 0) { if (FieldWidth > 0) {
/* For a union, allocate space for the type specified by the /* For a union, allocate space for the type specified by the
** bit-field. ** bit-field.
@ -1025,19 +1042,30 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth, AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth,
SignednessSpecified); SignednessSpecified);
} else if (Decl.Ident[0] != '\0') { } else if (Decl.Ident[0] != '\0') {
/* Add the new field to the table */
Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0);
if (IsAnonName (Decl.Ident)) {
Field->V.A.ANumber = UnionTagEntry->V.S.ACount++;
AliasAnonStructFields (&Decl, Field);
}
/* Check if the field itself has a flexible array member */ /* Check the new field for certain kinds of members */
if (IsClassStruct (Decl.Type)) { if (IsClassStruct (Decl.Type)) {
SymEntry* TagEntry = GetESUTagSym (Decl.Type); SymEntry* TagEntry = GetESUTagSym (Decl.Type);
/* Alias the fields of the anonymous member on the current level */
if (IsAnonName (Decl.Ident)) {
Field->V.A.ANumber = UnionTagEntry->V.S.ACount++;
AliasAnonStructFields (&Decl, Field);
}
/* Check if the field itself has a flexible array member */
if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) {
Field->Flags |= SC_HAVEFAM; Field->Flags |= SC_HAVEFAM;
Flags |= SC_HAVEFAM; Flags |= SC_HAVEFAM;
} }
/* Check if the field itself has a const member */
if (TagEntry && SymHasConstMember (TagEntry)) {
Field->Flags |= SC_HAVECONST;
Flags |= SC_HAVECONST;
}
} }
} }
@ -1160,55 +1188,68 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
} }
} }
/* Apart from the above, a bit field with width 0 is not processed
** further.
*/
if (FieldWidth == 0) {
goto NextMember;
}
/* Check if this field is a flexible array member, and
** calculate the size of the field.
*/
if (IsTypeArray (Decl.Type) && GetElementCount (Decl.Type) == UNSPECIFIED) {
/* Array with unspecified size */
if (StructSize == 0) {
Error ("Flexible array member cannot be first struct field");
}
FlexibleMember = 1;
Flags |= SC_HAVEFAM;
/* Assume zero for size calculations */
SetElementCount (Decl.Type, FLEXIBLE);
}
/* Check for fields without names */ /* Check for fields without names */
if (Decl.Ident[0] == '\0') { if (Decl.Ident[0] == '\0') {
if (FieldWidth < 0) { if (FieldWidth < 0) {
/* In cc65 mode, we allow anonymous structs/unions within /* In cc65 mode, we allow anonymous structs/unions within
** a struct. ** a struct.
*/ */
if (IS_Get (&Standard) >= STD_CC65 && IsClassStruct (Decl.Type)) { SymEntry* TagEntry;
if (IS_Get (&Standard) >= STD_CC65 &&
IsClassStruct (Decl.Type) &&
(TagEntry = GetESUTagSym (Decl.Type)) &&
SymHasAnonName (TagEntry)) {
/* This is an anonymous struct or union */
AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount);
/* This is an anonymous struct or union. Copy the /* Ignore CVR qualifiers */
** fields into the current level. if (IsQualConst (Decl.Type) || IsQualVolatile (Decl.Type) || IsQualRestrict (Decl.Type)) {
*/ Warning ("Anonymous %s qualifiers are ignored", GetBasicTypeName (Decl.Type));
AnonFieldName (Decl.Ident, "field", StructTagEntry->V.S.ACount); Decl.Type[0].C &= ~T_QUAL_CVR;
}
} else { } else {
/* A non bit-field without a name is legal but useless */ /* A non bit-field without a name is legal but useless */
Warning ("Declaration does not declare anything"); Warning ("Declaration does not declare anything");
goto NextMember;
} }
} else { } else if (FieldWidth > 0) {
/* A bit-field without a name will get an anonymous one */ /* A bit-field without a name will get an anonymous one */
AnonName (Decl.Ident, "bit-field"); AnonName (Decl.Ident, "bit-field");
} }
} else {
/* Check if this field is a flexible array member, and
** calculate the size of the field.
*/
if (IsTypeArray (Decl.Type) && GetElementCount (Decl.Type) == UNSPECIFIED) {
/* Array with unspecified size */
if (StructSize == 0) {
Error ("Flexible array member cannot be first struct field");
}
FlexibleMember = 1;
Flags |= SC_HAVEFAM;
/* Assume zero for size calculations */
SetElementCount (Decl.Type, FLEXIBLE);
}
if (IsIncompleteType (Decl.Type)) {
Error ("Field '%s' has incomplete type '%s'",
Decl.Ident,
GetFullTypeName (Decl.Type));
}
} }
/* Check for incomplete types including 'void' */ /* Check for const types */
if (IsIncompleteType (Decl.Type)) { if (IsQualConst (Decl.Type)) {
Error ("Field '%s' has incomplete type '%s'", Flags |= SC_HAVECONST;
Decl.Ident, }
GetFullTypeName (Decl.Type));
/* Apart from the above, a bit field with width 0 is not processed
** further.
*/
if (FieldWidth == 0) {
goto NextMember;
} }
/* Add a field entry to the table */ /* Add a field entry to the table */
@ -1223,24 +1264,35 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
FieldWidth, SignednessSpecified); FieldWidth, SignednessSpecified);
BitOffs += FieldWidth; BitOffs += FieldWidth;
CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type)); CHECK (BitOffs <= CHAR_BITS * SizeOf (Decl.Type));
/* Add any full bytes to the struct size. */ /* Add any full bytes to the struct size */
StructSize += BitOffs / CHAR_BITS; StructSize += BitOffs / CHAR_BITS;
BitOffs %= CHAR_BITS; BitOffs %= CHAR_BITS;
} else if (Decl.Ident[0] != '\0') { } else if (Decl.Ident[0] != '\0') {
/* Add the new field to the table */
Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); Field = AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize);
if (IsAnonName (Decl.Ident)) {
Field->V.A.ANumber = StructTagEntry->V.S.ACount++;
AliasAnonStructFields (&Decl, Field);
}
/* Check if the field itself has a flexible array member */ /* Check the new field for certain kinds of members */
if (IsClassStruct (Decl.Type)) { if (IsClassStruct (Decl.Type)) {
SymEntry* TagEntry = GetESUTagSym (Decl.Type); SymEntry* TagEntry = GetESUTagSym (Decl.Type);
/* Alias the fields of the anonymous member on the current level */
if (IsAnonName (Decl.Ident)) {
Field->V.A.ANumber = StructTagEntry->V.S.ACount++;
AliasAnonStructFields (&Decl, Field);
}
/* Check if the field itself has a flexible array member */
if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) { if (TagEntry && SymHasFlexibleArrayMember (TagEntry)) {
Field->Flags |= SC_HAVEFAM; Field->Flags |= SC_HAVEFAM;
Flags |= SC_HAVEFAM; Flags |= SC_HAVEFAM;
Error ("Invalid use of struct with flexible array member"); Error ("Invalid use of struct with flexible array member");
} }
/* Check if the field itself has a const member */
if (TagEntry && SymHasConstMember (TagEntry)) {
Field->Flags |= SC_HAVECONST;
Flags |= SC_HAVECONST;
}
} }
if (!FlexibleMember) { if (!FlexibleMember) {

View File

@ -108,6 +108,7 @@ struct CodeEntry;
#define SC_ALIAS 0x01000000U /* Alias of global or anonymous field */ #define SC_ALIAS 0x01000000U /* Alias of global or anonymous field */
#define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious (for error recovery) */ #define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious (for error recovery) */
#define SC_HAVEFAM 0x04000000U /* Type has a Flexible Array Member */ #define SC_HAVEFAM 0x04000000U /* Type has a Flexible Array Member */
#define SC_HAVECONST 0x08000000U /* Type has a const member */
@ -275,6 +276,16 @@ INLINE int SymHasFlexibleArrayMember (const SymEntry* Sym)
# define SymHasFlexibleArrayMember(Sym) (((Sym)->Flags & SC_HAVEFAM) == SC_HAVEFAM) # define SymHasFlexibleArrayMember(Sym) (((Sym)->Flags & SC_HAVEFAM) == SC_HAVEFAM)
#endif #endif
#if defined(HAVE_INLINE)
INLINE int SymHasConstMember (const SymEntry* Sym)
/* Return true if the given entry has a const member */
{
return ((Sym->Flags & SC_HAVECONST) == SC_HAVECONST);
}
#else
# define SymHasConstMember(Sym) (((Sym)->Flags & SC_HAVECONST) == SC_HAVECONST)
#endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE const char* SymGetAsmName (const SymEntry* Sym) INLINE const char* SymGetAsmName (const SymEntry* Sym)
/* Return the assembler label name for the symbol (beware: may be NULL!) */ /* Return the assembler label name for the symbol (beware: may be NULL!) */
@ -282,7 +293,7 @@ INLINE const char* SymGetAsmName (const SymEntry* Sym)
return Sym->AsmName; return Sym->AsmName;
} }
#else #else
# define SymGetAsmName(Sym) ((Sym)->AsmName) # define SymGetAsmName(Sym) ((Sym)->AsmName)
#endif #endif
const DeclAttr* SymGetAttr (const SymEntry* Sym, DeclAttrType AttrType); const DeclAttr* SymGetAttr (const SymEntry* Sym, DeclAttrType AttrType);

25
test/err/bug2018.c Normal file
View File

@ -0,0 +1,25 @@
/* Bug #2018 - Compiler has problems with const struct fields */
struct X {
struct {
int a;
} a;
union {
int a;
const int b;
} b;
};
struct X f(void)
{
struct X x = { 42 };
return x;
}
int main(void)
{
struct X x = { 0 };
x = f(); /* Error since X is read only */
return 0;
}

71
test/val/bug2018-ok.c Normal file
View File

@ -0,0 +1,71 @@
/* Bug #2018 - Compiler has problems with const struct fields */
#include <stdio.h>
unsigned failures;
struct X {
const int; /* Useless empty declaration */
const void; /* Useless empty declaration */
const struct U; /* Useless(?) declaration */
const struct V { int a; }; /* Useless(?) declaration */
const struct { /* Qualifier ignored in cc65 */
int a;
};
const union { /* Qualifier ignored in cc65 */
int b;
};
};
union Y {
const int; /* Useless empty declaration */
const void; /* Useless empty declaration */
const union W; /* Useless(?) declaration */
const union T { int a; }; /* Useless(?) declaration */
const struct { /* Qualifier ignored in cc65 */
int a;
};
const union { /* Qualifier ignored in cc65 */
int b;
};
};
struct X f(struct X a)
{
struct X x = { 42 };
return --a.a ? a : x;
}
union Y g(union Y a)
{
union Y y = { 42 };
return --a.a ? a : y;
}
int main(void)
{
struct X x = { 1 };
union Y y = { 1 };
x = f(x); /* Allowed in cc65 since X is not read only */
y = g(y); /* Allowed in cc65 since Y is not read only */
if (x.a != 42)
{
++failures;
}
if (y.a != 42)
{
++failures;
}
if (failures > 0)
{
printf("failures: %u\n", failures);
}
return failures;
}