Fixed const qualifiers on named structs/unions members that should prevent assignments to the whole structs/unions.

Added warning on ignored qualifiers on anonymous structs/unions.
This commit is contained in:
acqn 2023-11-13 17:17:46 +08:00
parent d7d1d89698
commit 0eb38770bd
5 changed files with 163 additions and 23 deletions

View File

@ -44,6 +44,7 @@
#include "scanner.h"
#include "stackptr.h"
#include "stdnames.h"
#include "symentry.h"
#include "typecmp.h"
#include "typeconv.h"
@ -82,6 +83,8 @@ static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr)
if (TypeCmp (ltype, RExpr->Type).C < TC_STRICT_COMPATIBLE) {
TypeCompatibilityDiagnostic (ltype, RExpr->Type, 1,
"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? */
@ -627,7 +630,7 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op)
}
} else if (IsQualConst (ltype)) {
/* 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");
} else if (IsQualConst (Expr->Type)) {
/* The left side must not be const qualified */
Error ("Assignment to const");
Error ("Assignment to const variable");
}
}

View File

@ -994,10 +994,14 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
** 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);
/* This is an anonymous struct or union */
AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), UnionTagEntry->V.S.ACount);
/* Ignore CVR qualifiers */
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");
@ -1011,13 +1015,18 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
GetFullTypeName (Decl.Type));
}
/* Check for const types */
if (IsQualConst (Decl.Type)) {
Flags |= SC_HAVECONST;
}
/* Handle sizes */
FieldSize = SizeOf (Decl.Type);
if (FieldSize > UnionSize) {
UnionSize = FieldSize;
}
/* Add a field entry to the table. */
/* Add a field entry to the table */
if (FieldWidth > 0) {
/* For a union, allocate space for the type specified by the
** bit-field.
@ -1025,19 +1034,30 @@ static SymEntry* ParseUnionSpec (const char* Name, unsigned* DSFlags)
AddBitField (Decl.Ident, Decl.Type, 0, 0, FieldWidth,
SignednessSpecified);
} else if (Decl.Ident[0] != '\0') {
/* Add the new field to the table */
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)) {
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)) {
Field->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;
}
}
}
@ -1190,10 +1210,14 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
*/
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", StructTagEntry->V.S.ACount);
/* This is an anonymous struct or union */
AnonFieldName (Decl.Ident, GetBasicTypeName (Decl.Type), StructTagEntry->V.S.ACount);
/* Ignore CVR qualifiers */
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");
@ -1211,6 +1235,11 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
GetFullTypeName (Decl.Type));
}
/* Check for const types */
if (IsQualConst (Decl.Type)) {
Flags |= SC_HAVECONST;
}
/* Add a field entry to the table */
if (FieldWidth > 0) {
/* Full bytes have already been added to the StructSize,
@ -1223,24 +1252,35 @@ static SymEntry* ParseStructSpec (const char* Name, unsigned* DSFlags)
FieldWidth, SignednessSpecified);
BitOffs += FieldWidth;
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;
BitOffs %= CHAR_BITS;
} else if (Decl.Ident[0] != '\0') {
/* Add the new field to the table */
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)) {
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)) {
Field->Flags |= SC_HAVEFAM;
Flags |= SC_HAVEFAM;
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) {

View File

@ -108,6 +108,7 @@ struct CodeEntry;
#define SC_ALIAS 0x01000000U /* Alias of global or anonymous field */
#define SC_FICTITIOUS 0x02000000U /* Symbol is fictitious (for error recovery) */
#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)
#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)
INLINE const char* SymGetAsmName (const SymEntry* Sym)
/* 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;
}
#else
# define SymGetAsmName(Sym) ((Sym)->AsmName)
# define SymGetAsmName(Sym) ((Sym)->AsmName)
#endif
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;
}

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

@ -0,0 +1,61 @@
/* Bug #2018 - Compiler has problems with const struct fields */
#include <stdio.h>
unsigned failures;
struct X {
const struct { /* Qualifier ignored in cc65 */
int a;
};
const union { /* Qualifier ignored in cc65 */
int b;
};
};
union Y {
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;
}