X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fdeclare.c;h=587d7ce74b34a2ab84cf0fb83ae0e1d4b2069dbe;hb=05f72963695f843cca3fd3dac1be0175b470b179;hp=9aabf0eabc9346364403e8d5a7a685d1a6989dc3;hpb=f4fbbc3dccb1353242a6ab69d6c5aa92e71f6673;p=cc65 diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 9aabf0eab..587d7ce74 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 1998-2001 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 1998-2010, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -38,12 +38,15 @@ #include /* common */ +#include "addrsize.h" +#include "mmodel.h" #include "xmalloc.h" /* cc65 */ #include "anonname.h" #include "codegen.h" #include "datatype.h" +#include "declare.h" #include "declattr.h" #include "error.h" #include "expr.h" @@ -53,52 +56,148 @@ #include "litpool.h" #include "pragma.h" #include "scanner.h" +#include "standard.h" #include "symtab.h" -#include "declare.h" +#include "typeconv.h" /*****************************************************************************/ -/* Forwards */ +/* Data */ /*****************************************************************************/ -static void ParseTypeSpec (DeclSpec* D, int Default); +typedef struct StructInitData StructInitData; +struct StructInitData { + unsigned Size; /* Size of struct */ + unsigned Offs; /* Current offset in struct */ + unsigned BitVal; /* Summed up bit-field value */ + unsigned ValBits; /* Valid bits in Val */ +}; + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers); /* Parse a type specificier */ +static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers); +/* Parse initialization of variables. Return the number of data bytes. */ + /*****************************************************************************/ -/* internal functions */ +/* Internal functions */ /*****************************************************************************/ -static type OptionalQualifiers (type Q) -/* Read type qualifiers if we have any */ +static void DuplicateQualifier (const char* Name) +/* Print an error message */ { - while (CurTok.Tok == TOK_CONST || CurTok.Tok == TOK_VOLATILE) { + Warning ("Duplicate qualifier: `%s'", Name); +} - switch (CurTok.Tok) { - case TOK_CONST: - if (Q & T_QUAL_CONST) { - Error ("Duplicate qualifier: `const'"); - } - Q |= T_QUAL_CONST; - break; + +static TypeCode OptionalQualifiers (TypeCode Allowed) +/* Read type qualifiers if we have any. Allowed specifies the allowed + * qualifiers. + */ +{ + /* We start without any qualifiers */ + TypeCode Q = T_QUAL_NONE; + + /* Check for more qualifiers */ + while (1) { + + switch (CurTok.Tok) { + + case TOK_CONST: + if (Allowed & T_QUAL_CONST) { + if (Q & T_QUAL_CONST) { + DuplicateQualifier ("const"); + } + Q |= T_QUAL_CONST; + } else { + goto Done; + } + break; case TOK_VOLATILE: - if (Q & T_QUAL_VOLATILE) { - Error ("Duplicate qualifier: `volatile'"); - } - Q |= T_QUAL_VOLATILE; - break; + if (Allowed & T_QUAL_VOLATILE) { + if (Q & T_QUAL_VOLATILE) { + DuplicateQualifier ("volatile"); + } + Q |= T_QUAL_VOLATILE; + } else { + goto Done; + } + break; + + case TOK_RESTRICT: + if (Allowed & T_QUAL_RESTRICT) { + if (Q & T_QUAL_RESTRICT) { + DuplicateQualifier ("restrict"); + } + Q |= T_QUAL_RESTRICT; + } else { + goto Done; + } + break; + + case TOK_NEAR: + if (Allowed & T_QUAL_NEAR) { + if (Q & T_QUAL_NEAR) { + DuplicateQualifier ("near"); + } + Q |= T_QUAL_NEAR; + } else { + goto Done; + } + break; + + case TOK_FAR: + if (Allowed & T_QUAL_FAR) { + if (Q & T_QUAL_FAR) { + DuplicateQualifier ("far"); + } + Q |= T_QUAL_FAR; + } else { + goto Done; + } + break; + + case TOK_FASTCALL: + if (Allowed & T_QUAL_FASTCALL) { + if (Q & T_QUAL_FASTCALL) { + DuplicateQualifier ("fastcall"); + } + Q |= T_QUAL_FASTCALL; + } else { + goto Done; + } + break; + + case TOK_CDECL: + if (Allowed & T_QUAL_CDECL) { + if (Q & T_QUAL_CDECL) { + DuplicateQualifier ("cdecl"); + } + Q |= T_QUAL_CDECL; + } else { + goto Done; + } + break; default: - /* Keep gcc silent */ - break; + goto Done; } @@ -106,13 +205,40 @@ static type OptionalQualifiers (type Q) NextToken (); } +Done: + /* We cannot have more than one address size far qualifier */ + switch (Q & T_QUAL_ADDRSIZE) { + + case T_QUAL_NONE: + case T_QUAL_NEAR: + case T_QUAL_FAR: + break; + + default: + Error ("Cannot specify more than one address size qualifier"); + Q &= ~T_QUAL_ADDRSIZE; + } + + /* We cannot have more than one calling convention specifier */ + switch (Q & T_QUAL_CCONV) { + + case T_QUAL_NONE: + case T_QUAL_FASTCALL: + case T_QUAL_CDECL: + break; + + default: + Error ("Cannot specify more than one calling convention qualifier"); + Q &= ~T_QUAL_CCONV; + } + /* Return the qualifiers read */ return Q; } -static void optionalint (void) +static void OptionalInt (void) /* Eat an optional "int" token */ { if (CurTok.Tok == TOK_INT) { @@ -123,7 +249,7 @@ static void optionalint (void) -static void optionalsigned (void) +static void OptionalSigned (void) /* Eat an optional "signed" token */ { if (CurTok.Tok == TOK_SIGNED) { @@ -137,9 +263,9 @@ static void optionalsigned (void) static void InitDeclSpec (DeclSpec* D) /* Initialize the DeclSpec struct for use */ { - D->StorageClass = 0; - D->Type[0] = T_END; - D->Flags = 0; + D->StorageClass = 0; + D->Type[0].C = T_END; + D->Flags = 0; } @@ -147,9 +273,124 @@ static void InitDeclSpec (DeclSpec* D) static void InitDeclaration (Declaration* D) /* Initialize the Declaration struct for use */ { - D->Ident[0] = '\0'; - D->Type[0] = T_END; - D->T = D->Type; + D->Ident[0] = '\0'; + D->Type[0].C = T_END; + D->Index = 0; + D->Attributes = 0; +} + + + +static void NeedTypeSpace (Declaration* D, unsigned Count) +/* Check if there is enough space for Count type specifiers within D */ +{ + if (D->Index + Count >= MAXTYPELEN) { + /* We must call Fatal() here, since calling Error() will try to + * continue, and the declaration type is not correctly terminated + * in case we come here. + */ + Fatal ("Too many type specifiers"); + } +} + + + +static void AddTypeToDeclaration (Declaration* D, TypeCode T) +/* Add a type specifier to the type of a declaration */ +{ + NeedTypeSpace (D, 1); + D->Type[D->Index++].C = T; +} + + + +static void FixQualifiers (Type* DataType) +/* Apply several fixes to qualifiers */ +{ + Type* T; + TypeCode Q; + + /* Using typedefs, it is possible to generate declarations that have + * type qualifiers attached to an array, not the element type. Go and + * fix these here. + */ + T = DataType; + Q = T_QUAL_NONE; + while (T->C != T_END) { + if (IsTypeArray (T)) { + /* Extract any type qualifiers */ + Q |= GetQualifier (T); + T->C = UnqualifiedType (T->C); + } else { + /* Add extracted type qualifiers here */ + T->C |= Q; + Q = T_QUAL_NONE; + } + ++T; + } + /* Q must be empty now */ + CHECK (Q == T_QUAL_NONE); + + /* Do some fixes on pointers and functions. */ + T = DataType; + while (T->C != T_END) { + if (IsTypePtr (T)) { + + /* Fastcall qualifier on the pointer? */ + if (IsQualFastcall (T)) { + /* Pointer to function which is not fastcall? */ + if (IsTypeFunc (T+1) && !IsQualFastcall (T+1)) { + /* Move the fastcall qualifier from the pointer to + * the function. + */ + T[0].C &= ~T_QUAL_FASTCALL; + T[1].C |= T_QUAL_FASTCALL; + } else { + Error ("Invalid `_fastcall__' qualifier for pointer"); + } + } + + /* Apply the default far and near qualifiers if none are given */ + Q = (T[0].C & T_QUAL_ADDRSIZE); + if (Q == T_QUAL_NONE) { + /* No address size qualifiers specified */ + if (IsTypeFunc (T+1)) { + /* Pointer to function. Use the qualifier from the function + * or the default if the function don't has one. + */ + Q = (T[1].C & T_QUAL_ADDRSIZE); + if (Q == T_QUAL_NONE) { + Q = CodeAddrSizeQualifier (); + } + } else { + Q = DataAddrSizeQualifier (); + } + T[0].C |= Q; + } else { + /* We have address size qualifiers. If followed by a function, + * apply these also to the function. + */ + if (IsTypeFunc (T+1)) { + TypeCode FQ = (T[1].C & T_QUAL_ADDRSIZE); + if (FQ == T_QUAL_NONE) { + T[1].C |= Q; + } else if (FQ != Q) { + Error ("Address size qualifier mismatch"); + T[1].C = (T[1].C & ~T_QUAL_ADDRSIZE) | Q; + } + } + } + + } else if (IsTypeFunc (T)) { + + /* Apply the default far and near qualifiers if none are given */ + if ((T[0].C & T_QUAL_ADDRSIZE) == 0) { + T[0].C |= CodeAddrSizeQualifier (); + } + + } + ++T; + } } @@ -219,7 +460,7 @@ static void ParseEnumDecl (void) /* We expect an identifier */ if (CurTok.Tok != TOK_IDENT) { Error ("Identifier expected"); - continue; + continue; } /* Remember the identifier and skip it */ @@ -228,16 +469,16 @@ static void ParseEnumDecl (void) /* Check for an assigned value */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc lval; + ExprDesc Expr; NextToken (); - constexpr (&lval); - EnumVal = lval.ConstVal; + ConstAbsIntExpr (hie1, &Expr); + EnumVal = Expr.IVal; } /* Add an entry to the symbol table */ AddConstSym (Ident, type_int, SC_ENUM, EnumVal++); - /* Check for end of definition */ + /* Check for end of definition */ if (CurTok.Tok != TOK_COMMA) break; NextToken (); @@ -247,29 +488,77 @@ static void ParseEnumDecl (void) -static SymEntry* ParseStructDecl (const char* Name, type StructType) -/* Parse a struct/union declaration. */ +static int ParseFieldWidth (Declaration* Decl) +/* Parse an optional field width. Returns -1 if no field width is speficied, + * otherwise the width of the field. + */ { + ExprDesc Expr; - unsigned Size; - unsigned Offs; + if (CurTok.Tok != TOK_COLON) { + /* No bit-field declaration */ + return -1; + } + + /* Read the width */ + NextToken (); + ConstAbsIntExpr (hie1, &Expr); + if (Expr.IVal < 0) { + Error ("Negative width in bit-field"); + return -1; + } + if (Expr.IVal > (int) INT_BITS) { + Error ("Width of bit-field exceeds its type"); + return -1; + } + if (Expr.IVal == 0 && Decl->Ident[0] != '\0') { + Error ("Zero width for named bit-field"); + return -1; + } + if (!IsTypeInt (Decl->Type)) { + /* Only integer types may be used for bit-fields */ + Error ("Bit-field has invalid type"); + return -1; + } + + /* Return the field width */ + return (int) Expr.IVal; +} + + + +static SymEntry* StructOrUnionForwardDecl (const char* Name) +/* Handle a struct or union forward decl */ +{ + /* Try to find a struct with the given name. If there is none, + * insert a forward declaration into the current lexical level. + */ + SymEntry* Entry = FindTagSym (Name); + if (Entry == 0) { + Entry = AddStructSym (Name, 0, 0); + } else if (SymIsLocal (Entry) && (Entry->Flags & SC_STRUCT) != SC_STRUCT) { + /* Already defined in the level, but no struct */ + Error ("Symbol `%s' is already different kind", Name); + } + return Entry; +} + + + +static SymEntry* ParseUnionDecl (const char* Name) +/* Parse a union declaration. */ +{ + + unsigned UnionSize; + unsigned FieldSize; + int FieldWidth; /* Width in bits, -1 if not a bit-field */ SymTable* FieldTab; SymEntry* Entry; if (CurTok.Tok != TOK_LCURLY) { - /* Just a forward declaration. Try to find a struct with the given - * name. If there is none, insert a forward declaration into the - * current lexical level. - */ - Entry = FindTagSym (Name); - if (Entry == 0) { - Entry = AddStructSym (Name, 0, 0); - } else if (SymIsLocal (Entry) && (Entry->Flags & SC_STRUCT) == 0) { - /* Already defined in the level but no struct */ - Error ("Symbol `%s' is already different kind", Name); - } - return Entry; + /* Just a forward declaration. */ + return StructOrUnionForwardDecl (Name); } /* Add a forward declaration for the struct in the current lexical level */ @@ -281,40 +570,215 @@ static SymEntry* ParseStructDecl (const char* Name, type StructType) /* Enter a new lexical level for the struct */ EnterStructLevel (); - /* Parse struct fields */ - Size = 0; + /* Parse union fields */ + UnionSize = 0; while (CurTok.Tok != TOK_RCURLY) { /* Get the type of the entry */ DeclSpec Spec; InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1); + ParseTypeSpec (&Spec, -1, T_QUAL_NONE); /* Read fields with this type */ while (1) { + Declaration Decl; + /* Get type and name of the struct field */ + ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + + /* Check for a bit-field declaration */ + FieldWidth = ParseFieldWidth (&Decl); + + /* Ignore zero sized bit fields in a union */ + if (FieldWidth == 0) { + goto NextMember; + } + + /* Check for fields without a name */ + if (Decl.Ident[0] == '\0') { + /* Any field without a name is legal but useless in a union */ + Warning ("Declaration does not declare anything"); + goto NextMember; + } + + /* Handle sizes */ + FieldSize = CheckedSizeOf (Decl.Type); + if (FieldSize > UnionSize) { + UnionSize = FieldSize; + } + + /* Add a field entry to the table. */ + if (FieldWidth > 0) { + AddBitField (Decl.Ident, 0, 0, FieldWidth); + } else { + AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, 0); + } + +NextMember: if (CurTok.Tok != TOK_COMMA) { + break; + } + NextToken (); + } + ConsumeSemi (); + } + + /* Skip the closing brace */ + NextToken (); + + /* Remember the symbol table and leave the struct level */ + FieldTab = GetSymTab (); + LeaveStructLevel (); + + /* Make a real entry from the forward decl and return it */ + return AddStructSym (Name, UnionSize, FieldTab); +} + + + +static SymEntry* ParseStructDecl (const char* Name) +/* Parse a struct declaration. */ +{ + + unsigned StructSize; + int FlexibleMember; + int BitOffs; /* Bit offset for bit-fields */ + int FieldWidth; /* Width in bits, -1 if not a bit-field */ + SymTable* FieldTab; + SymEntry* Entry; + + + if (CurTok.Tok != TOK_LCURLY) { + /* Just a forward declaration. */ + return StructOrUnionForwardDecl (Name); + } + + /* Add a forward declaration for the struct in the current lexical level */ + Entry = AddStructSym (Name, 0, 0); + + /* Skip the curly brace */ + NextToken (); + + /* Enter a new lexical level for the struct */ + EnterStructLevel (); + + /* Parse struct fields */ + FlexibleMember = 0; + StructSize = 0; + BitOffs = 0; + while (CurTok.Tok != TOK_RCURLY) { + + /* Get the type of the entry */ + DeclSpec Spec; + InitDeclSpec (&Spec); + ParseTypeSpec (&Spec, -1, T_QUAL_NONE); + + /* Read fields with this type */ + while (1) { + Declaration Decl; - ParseDecl (&Spec, &Decl, 0); + ident Ident; - /* Add a field entry to the table */ - AddLocalSym (Decl.Ident, Decl.Type, SC_SFLD, (StructType == T_STRUCT)? Size : 0); + /* If we had a flexible array member before, no other fields can + * follow. + */ + if (FlexibleMember) { + Error ("Flexible array member must be last field"); + FlexibleMember = 0; /* Avoid further errors */ + } - /* Calculate offset of next field/size of the union */ - Offs = SizeOf (Decl.Type); - if (StructType == T_STRUCT) { - Size += Offs; - } else { - if (Offs > Size) { - Size = Offs; - } - } + /* Get type and name of the struct field */ + ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT); + + /* Check for a bit-field declaration */ + FieldWidth = ParseFieldWidth (&Decl); + + /* If this is not a bit field, or the bit field is too large for + * the remainder of the current member, or we have a bit field + * with width zero, align the struct to the next member by adding + * a member with an anonymous name. + */ + if (BitOffs > 0) { + if (FieldWidth <= 0 || (BitOffs + FieldWidth) > (int) INT_BITS) { + + /* We need an anonymous name */ + AnonName (Ident, "bit-field"); + + /* Add an anonymous bit-field that aligns to the next + * storage unit. + */ + AddBitField (Ident, StructSize, BitOffs, INT_BITS - BitOffs); + + /* No bits left */ + StructSize += SIZEOF_INT; + BitOffs = 0; + } + } + + /* Apart from the above, a bit field with width 0 is not processed + * further. + */ + if (FieldWidth == 0) { + goto NextMember; + } + + /* Check for fields without names */ + if (Decl.Ident[0] == '\0') { + if (FieldWidth < 0) { + /* A non bit-field without a name is legal but useless */ + Warning ("Declaration does not declare anything"); + goto NextMember; + } else { + /* A bit-field without a name will get an anonymous one */ + AnonName (Decl.Ident, "bit-field"); + } + } + + /* 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; + /* Assume zero for size calculations */ + SetElementCount (Decl.Type, FLEXIBLE); + } + + /* Add a field entry to the table */ + if (FieldWidth > 0) { + /* Add full byte from the bit offset to the variable offset. + * This simplifies handling he bit-field as a char type + * in expressions. + */ + unsigned Offs = StructSize + (BitOffs / CHAR_BITS); + AddBitField (Decl.Ident, Offs, BitOffs % CHAR_BITS, FieldWidth); + BitOffs += FieldWidth; + CHECK (BitOffs <= (int) INT_BITS); + if (BitOffs == INT_BITS) { + StructSize += SIZEOF_INT; + BitOffs = 0; + } + } else { + AddLocalSym (Decl.Ident, Decl.Type, SC_STRUCTFIELD, StructSize); + if (!FlexibleMember) { + StructSize += CheckedSizeOf (Decl.Type); + } + } + +NextMember: if (CurTok.Tok != TOK_COMMA) { + break; + } + NextToken (); + } + ConsumeSemi (); + } - if (CurTok.Tok != TOK_COMMA) - break; - NextToken (); - } - ConsumeSemi (); + /* If we have bits from bit-fields left, add them to the size. */ + if (BitOffs > 0) { + StructSize += ((BitOffs + CHAR_BITS - 1) / CHAR_BITS); } /* Skip the closing brace */ @@ -325,52 +789,50 @@ static SymEntry* ParseStructDecl (const char* Name, type StructType) LeaveStructLevel (); /* Make a real entry from the forward decl and return it */ - return AddStructSym (Name, Size, FieldTab); + return AddStructSym (Name, StructSize, FieldTab); } -static void ParseTypeSpec (DeclSpec* D, int Default) +static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) /* Parse a type specificier */ { ident Ident; SymEntry* Entry; - type StructType; - type Qualifiers; /* Type qualifiers */ /* Assume we have an explicit type */ D->Flags &= ~DS_DEF_TYPE; /* Read type qualifiers if we have any */ - Qualifiers = OptionalQualifiers (T_QUAL_NONE); + Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE); /* Look at the data type */ switch (CurTok.Tok) { case TOK_VOID: NextToken (); - D->Type[0] = T_VOID; - D->Type[1] = T_END; + D->Type[0].C = T_VOID; + D->Type[1].C = T_END; break; case TOK_CHAR: NextToken (); - D->Type[0] = GetDefaultChar(); - D->Type[1] = T_END; + D->Type[0].C = GetDefaultChar(); + D->Type[1].C = T_END; break; case TOK_LONG: NextToken (); if (CurTok.Tok == TOK_UNSIGNED) { NextToken (); - optionalint (); - D->Type[0] = T_ULONG; - D->Type[1] = T_END; + OptionalInt (); + D->Type[0].C = T_ULONG; + D->Type[1].C = T_END; } else { - optionalsigned (); - optionalint (); - D->Type[0] = T_LONG; - D->Type[1] = T_END; + OptionalSigned (); + OptionalInt (); + D->Type[0].C = T_LONG; + D->Type[1].C = T_END; } break; @@ -378,21 +840,21 @@ static void ParseTypeSpec (DeclSpec* D, int Default) NextToken (); if (CurTok.Tok == TOK_UNSIGNED) { NextToken (); - optionalint (); - D->Type[0] = T_USHORT; - D->Type[1] = T_END; + OptionalInt (); + D->Type[0].C = T_USHORT; + D->Type[1].C = T_END; } else { - optionalsigned (); - optionalint (); - D->Type[0] = T_SHORT; - D->Type[1] = T_END; + OptionalSigned (); + OptionalInt (); + D->Type[0].C = T_SHORT; + D->Type[1].C = T_END; } - break; + break; case TOK_INT: NextToken (); - D->Type[0] = T_INT; - D->Type[1] = T_END; + D->Type[0].C = T_INT; + D->Type[1].C = T_END; break; case TOK_SIGNED: @@ -401,31 +863,31 @@ static void ParseTypeSpec (DeclSpec* D, int Default) case TOK_CHAR: NextToken (); - D->Type[0] = T_SCHAR; - D->Type[1] = T_END; + D->Type[0].C = T_SCHAR; + D->Type[1].C = T_END; break; case TOK_SHORT: NextToken (); - optionalint (); - D->Type[0] = T_SHORT; - D->Type[1] = T_END; + OptionalInt (); + D->Type[0].C = T_SHORT; + D->Type[1].C = T_END; break; case TOK_LONG: NextToken (); - optionalint (); - D->Type[0] = T_LONG; - D->Type[1] = T_END; - break; + OptionalInt (); + D->Type[0].C = T_LONG; + D->Type[1].C = T_END; + break; case TOK_INT: NextToken (); /* FALL THROUGH */ - default: - D->Type[0] = T_INT; - D->Type[1] = T_END; + default: + D->Type[0].C = T_INT; + D->Type[1].C = T_END; break; } break; @@ -436,90 +898,119 @@ static void ParseTypeSpec (DeclSpec* D, int Default) case TOK_CHAR: NextToken (); - D->Type[0] = T_UCHAR; - D->Type[1] = T_END; + D->Type[0].C = T_UCHAR; + D->Type[1].C = T_END; break; case TOK_SHORT: NextToken (); - optionalint (); - D->Type[0] = T_USHORT; - D->Type[1] = T_END; + OptionalInt (); + D->Type[0].C = T_USHORT; + D->Type[1].C = T_END; break; case TOK_LONG: NextToken (); - optionalint (); - D->Type[0] = T_ULONG; - D->Type[1] = T_END; + OptionalInt (); + D->Type[0].C = T_ULONG; + D->Type[1].C = T_END; break; - case TOK_INT: + case TOK_INT: NextToken (); /* FALL THROUGH */ default: - D->Type[0] = T_UINT; - D->Type[1] = T_END; + D->Type[0].C = T_UINT; + D->Type[1].C = T_END; break; } break; - case TOK_STRUCT: + case TOK_FLOAT: + NextToken (); + D->Type[0].C = T_FLOAT; + D->Type[1].C = T_END; + break; + + case TOK_DOUBLE: + NextToken (); + D->Type[0].C = T_DOUBLE; + D->Type[1].C = T_END; + break; + case TOK_UNION: - StructType = (CurTok.Tok == TOK_STRUCT)? T_STRUCT : T_UNION; NextToken (); /* */ if (CurTok.Tok == TOK_IDENT) { strcpy (Ident, CurTok.Ident); NextToken (); } else { - AnonName (Ident, (StructType == T_STRUCT)? "struct" : "union"); + AnonName (Ident, "union"); + } + /* Remember we have an extra type decl */ + D->Flags |= DS_EXTRA_TYPE; + /* Declare the union in the current scope */ + Entry = ParseUnionDecl (Ident); + /* Encode the union entry into the type */ + D->Type[0].C = T_UNION; + SetSymEntry (D->Type, Entry); + D->Type[1].C = T_END; + break; + + case TOK_STRUCT: + NextToken (); + /* */ + if (CurTok.Tok == TOK_IDENT) { + strcpy (Ident, CurTok.Ident); + NextToken (); + } else { + AnonName (Ident, "struct"); } /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Declare the struct in the current scope */ - Entry = ParseStructDecl (Ident, StructType); + Entry = ParseStructDecl (Ident); /* Encode the struct entry into the type */ - D->Type[0] = StructType; - EncodePtr (D->Type+1, Entry); - D->Type[DECODE_SIZE+1] = T_END; + D->Type[0].C = T_STRUCT; + SetSymEntry (D->Type, Entry); + D->Type[1].C = T_END; break; case TOK_ENUM: NextToken (); - if (CurTok.Tok != TOK_LCURLY) { + if (CurTok.Tok != TOK_LCURLY) { /* Named enum */ if (CurTok.Tok == TOK_IDENT) { /* Find an entry with this name */ - Entry = FindTagSym (CurTok.Ident); + Entry = FindTagSym (CurTok.Ident); if (Entry) { if (SymIsLocal (Entry) && (Entry->Flags & SC_ENUM) == 0) { Error ("Symbol `%s' is already different kind", Entry->Name); } - } else { + } else { /* Insert entry into table ### */ } /* Skip the identifier */ NextToken (); - } else { + } else { Error ("Identifier expected"); - } - } - /* Remember we have an extra type decl */ - D->Flags |= DS_EXTRA_TYPE; - /* Parse the enum decl */ + } + } + /* Remember we have an extra type decl */ + D->Flags |= DS_EXTRA_TYPE; + /* Parse the enum decl */ ParseEnumDecl (); - D->Type[0] = T_INT; - D->Type[1] = T_END; + D->Type[0].C = T_INT; + D->Type[1].C = T_END; break; case TOK_IDENT: - Entry = FindSym (CurTok.Ident); - if (Entry && IsTypeDef (Entry)) { + Entry = FindSym (CurTok.Ident); + if (Entry && SymIsTypeDef (Entry)) { /* It's a typedef */ NextToken (); - TypeCpy (D->Type, Entry->Type); + TypeCopy (D->Type, Entry->Type); break; } /* FALL THROUGH */ @@ -527,30 +1018,29 @@ static void ParseTypeSpec (DeclSpec* D, int Default) default: if (Default < 0) { Error ("Type expected"); - D->Type[0] = T_INT; - D->Type[1] = T_END; + D->Type[0].C = T_INT; + D->Type[1].C = T_END; } else { - D->Flags |= DS_DEF_TYPE; - D->Type[0] = (type) Default; - D->Type[1] = T_END; - } - break; + D->Flags |= DS_DEF_TYPE; + D->Type[0].C = (TypeCode) Default; + D->Type[1].C = T_END; + } + break; } /* There may also be qualifiers *after* the initial type */ - D->Type[0] |= OptionalQualifiers (Qualifiers); + D->Type[0].C |= (Qualifiers | OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE)); } -static type* ParamTypeCvt (type* T) +static Type* ParamTypeCvt (Type* T) /* If T is an array, convert it to a pointer else do nothing. Return the * resulting type. */ { if (IsTypeArray (T)) { - T += DECODE_SIZE; - T[0] = T_PTR; + T->C = T_PTR; } return T; } @@ -569,7 +1059,7 @@ static void ParseOldStyleParamList (FuncDesc* F) } /* Create a symbol table entry with type int */ - AddLocalSym (CurTok.Ident, type_int, SC_AUTO | SC_PARAM | SC_DEF, 0); + AddLocalSym (CurTok.Ident, type_int, SC_AUTO | SC_PARAM | SC_DEF | SC_DEFTYPE, 0); /* Count arguments */ ++F->ParamCount; @@ -602,31 +1092,41 @@ static void ParseOldStyleParamList (FuncDesc* F) * we ignore all this, since we use auto anyway. */ if ((Spec.StorageClass & SC_AUTO) == 0 && - (Spec.StorageClass & SC_REGISTER) == 0) { + (Spec.StorageClass & SC_REGISTER) == 0) { Error ("Illegal storage class"); } /* Parse a comma separated variable list */ while (1) { - Declaration Decl; + Declaration Decl; /* Read the parameter */ ParseDecl (&Spec, &Decl, DM_NEED_IDENT); if (Decl.Ident[0] != '\0') { - /* We have a name given. Search for the symbol */ + /* We have a name given. Search for the symbol */ SymEntry* Sym = FindLocalSym (Decl.Ident); if (Sym) { - /* Found it, change the default type to the one given */ - ChangeSymType (Sym, ParamTypeCvt (Decl.Type)); + /* Check if we already changed the type for this + * parameter + */ + if (Sym->Flags & SC_DEFTYPE) { + /* Found it, change the default type to the one given */ + ChangeSymType (Sym, ParamTypeCvt (Decl.Type)); + /* Reset the "default type" flag */ + Sym->Flags &= ~SC_DEFTYPE; + } else { + /* Type has already been changed */ + Error ("Redefinition for parameter `%s'", Sym->Name); + } } else { Error ("Unknown identifier: `%s'", Decl.Ident); } } if (CurTok.Tok == TOK_COMMA) { - NextToken (); + NextToken (); } else { break; } @@ -648,11 +1148,11 @@ static void ParseAnsiParamList (FuncDesc* F) DeclSpec Spec; Declaration Decl; - DeclAttr Attr; + SymEntry* Sym; /* Allow an ellipsis as last parameter */ if (CurTok.Tok == TOK_ELLIPSIS) { - NextToken (); + NextToken (); F->Flags |= FD_VARIADIC; break; } @@ -660,14 +1160,15 @@ static void ParseAnsiParamList (FuncDesc* F) /* Read the declaration specifier */ ParseDeclSpec (&Spec, SC_AUTO, T_INT); - /* We accept only auto and register as storage class specifiers, but - * we ignore all this and use auto. - */ - if ((Spec.StorageClass & SC_AUTO) == 0 && - (Spec.StorageClass & SC_REGISTER) == 0) { + /* We accept only auto and register as storage class specifiers */ + if ((Spec.StorageClass & SC_AUTO) == SC_AUTO) { + Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; + } else if ((Spec.StorageClass & SC_REGISTER) == SC_REGISTER) { + Spec.StorageClass = SC_REGISTER | SC_STATIC | SC_PARAM | SC_DEF; + } else { Error ("Illegal storage class"); + Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; } - Spec.StorageClass = SC_AUTO | SC_PARAM | SC_DEF; /* Allow parameters without a name, but remember if we had some to * eventually print an error message later. @@ -682,14 +1183,24 @@ static void ParseAnsiParamList (FuncDesc* F) F->Flags |= FD_UNNAMED_PARAMS; /* Clear defined bit on nonames */ - Spec.StorageClass &= ~SC_DEF; + Decl.StorageClass &= ~SC_DEF; } - /* Parse an attribute ### */ - ParseAttribute (&Decl, &Attr); + /* Parse attributes for this parameter */ + ParseAttribute (&Decl); /* Create a symbol table entry */ - AddLocalSym (Decl.Ident, ParamTypeCvt (Decl.Type), Spec.StorageClass, 0); + Sym = AddLocalSym (Decl.Ident, ParamTypeCvt (Decl.Type), Decl.StorageClass, 0); + + /* Add attributes if we have any */ + SymUseAttr (Sym, &Decl); + + /* If the parameter is a struct or union, emit a warning */ + if (IsClassStruct (Decl.Type)) { + if (IS_Get (&WarnStructParam)) { + Warning ("Passing struct by value for parameter `%s'", Decl.Ident); + } + } /* Count arguments */ ++F->ParamCount; @@ -706,16 +1217,6 @@ static void ParseAnsiParamList (FuncDesc* F) * the breaks above bail out without checking. */ ConsumeRParen (); - - /* Check if this is a function definition */ - if (CurTok.Tok == TOK_LCURLY) { - /* Print an error if in strict ANSI mode and we have unnamed - * parameters. - */ - if (ANSI && (F->Flags & FD_UNNAMED_PARAMS) != 0) { - Error ("Parameter name omitted"); - } - } } @@ -741,34 +1242,47 @@ static FuncDesc* ParseFuncDecl (void) NextToken (); F->Flags |= FD_VOID_PARAM; } else if (CurTok.Tok == TOK_IDENT && - (NextTok.Tok == TOK_COMMA || NextTok.Tok == TOK_RPAREN)) { - /* If the identifier is a typedef, we have a new style parameter list, - * if it's some other identifier, it's an old style parameter list. - */ - Sym = FindSym (CurTok.Ident); - if (Sym == 0 || !IsTypeDef (Sym)) { - /* Old style (K&R) function. Assume variable param list. */ - F->Flags |= (FD_OLDSTYLE | FD_VARIADIC); - } + (NextTok.Tok == TOK_COMMA || NextTok.Tok == TOK_RPAREN)) { + /* If the identifier is a typedef, we have a new style parameter list, + * if it's some other identifier, it's an old style parameter list. + */ + Sym = FindSym (CurTok.Ident); + if (Sym == 0 || !SymIsTypeDef (Sym)) { + /* Old style (K&R) function. */ + F->Flags |= FD_OLDSTYLE; + } } /* Parse params */ if ((F->Flags & FD_OLDSTYLE) == 0) { + /* New style function */ ParseAnsiParamList (F); + } else { /* Old style function */ ParseOldStyleParamList (F); } + /* Remember the last function parameter. We need it later for several + * purposes, for example when passing stuff to fastcall functions. Since + * more symbols are added to the table, it is easier if we remember it + * now, since it is currently the last entry in the symbol table. + */ + F->LastParam = GetSymTab()->SymTail; + /* Assign offsets. If the function has a variable parameter list, * there's one additional byte (the arg size). */ Offs = (F->Flags & FD_VARIADIC)? 1 : 0; - Sym = GetSymTab()->SymTail; + Sym = F->LastParam; while (Sym) { - unsigned Size = SizeOf (Sym->Type); - Sym->V.Offs = Offs; + unsigned Size = CheckedSizeOf (Sym->Type); + if (SymIsRegVar (Sym)) { + Sym->V.R.SaveOffs = Offs; + } else { + Sym->V.Offs = Offs; + } Offs += Size; F->ParamSize += Size; Sym = Sym->PrevSym; @@ -783,49 +1297,48 @@ static FuncDesc* ParseFuncDecl (void) -static void Decl (Declaration* D, unsigned Mode) +static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Recursively process declarators. Build a type array in reverse order. */ { + /* Read optional function or pointer qualifiers. These modify the + * identifier or token to the right. For convenience, we allow the fastcall + * qualifier also for pointers here. If it is a pointer-to-function, the + * qualifier will later be transfered to the function itself. If it's a + * pointer to something else, it will be flagged as an error. + */ + TypeCode Qualifiers = OptionalQualifiers (T_QUAL_ADDRSIZE | T_QUAL_FASTCALL); + /* Pointer to something */ if (CurTok.Tok == TOK_STAR) { - type T = T_PTR; + + /* Skip the star */ NextToken (); - /* Allow optional const or volatile qualifiers */ - T |= OptionalQualifiers (T_QUAL_NONE); - Decl (D, Mode); - *D->T++ = T; + + /* Allow const, restrict and volatile qualifiers */ + Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT); + + /* Parse the type, the pointer points to */ + Declarator (Spec, D, Mode); + + /* Add the type */ + AddTypeToDeclaration (D, T_PTR | Qualifiers); return; - } else if (CurTok.Tok == TOK_LPAREN) { + } + + if (CurTok.Tok == TOK_LPAREN) { NextToken (); - Decl (D, Mode); + Declarator (Spec, D, Mode); ConsumeRParen (); - } else if (CurTok.Tok == TOK_FASTCALL) { - /* Remember the current type pointer */ - type* T = D->T; - /* Skip the fastcall token */ - NextToken (); - /* Parse the function */ - Decl (D, Mode); - /* Set the fastcall flag */ - if (!IsTypeFunc (T) && !IsTypeFuncPtr (T)) { - Error ("__fastcall__ modifier applied to non function"); - } else if (IsVariadicFunc (T)) { - Error ("Cannot apply __fastcall__ to functions with variable parameter list"); - } else { - FuncDesc* F = GetFuncDesc (T); - F->Flags |= FD_FASTCALL; - } - return; } else { /* Things depend on Mode now: * - Mode == DM_NEED_IDENT means: * we *must* have a type and a variable identifer. * - Mode == DM_NO_IDENT means: - * we must have a type but no variable identifer + * we must have a type but no variable identifer * (if there is one, it's not read). * - Mode == DM_ACCEPT_IDENT means: - * we *may* have an identifier. If there is an identifier, - * it is read, but it is no error, if there is none. + * we *may* have an identifier. If there is an identifier, + * it is read, but it is no error, if there is none. */ if (Mode == DM_NO_IDENT) { D->Ident[0] = '\0'; @@ -837,36 +1350,88 @@ static void Decl (Declaration* D, unsigned Mode) Error ("Identifier expected"); } D->Ident[0] = '\0'; - return; } } while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) { if (CurTok.Tok == TOK_LPAREN) { + /* Function declaration */ - FuncDesc* F; + FuncDesc* F; + + /* Skip the opening paren */ NextToken (); - /* Parse the function declaration */ + + /* Parse the function declaration */ F = ParseFuncDecl (); - *D->T++ = T_FUNC; - EncodePtr (D->T, F); - D->T += DECODE_SIZE; + + /* We cannot specify fastcall for variadic functions */ + if ((F->Flags & FD_VARIADIC) && (Qualifiers & T_QUAL_FASTCALL)) { + Error ("Variadic functions cannot be `__fastcall'"); + Qualifiers &= ~T_QUAL_FASTCALL; + } + + /* Add the function type. Be sure to bounds check the type buffer */ + NeedTypeSpace (D, 1); + D->Type[D->Index].C = T_FUNC | Qualifiers; + D->Type[D->Index].A.P = F; + ++D->Index; + + /* Qualifiers now used */ + Qualifiers = T_QUAL_NONE; + } else { - /* Array declaration */ - unsigned long Size = 0; + /* Array declaration. */ + long Size = UNSPECIFIED; + + /* We cannot have any qualifiers for an array */ + if (Qualifiers != T_QUAL_NONE) { + Error ("Invalid qualifiers for array"); + Qualifiers = T_QUAL_NONE; + } + + /* Skip the left bracket */ NextToken (); - /* Read the size if it is given */ + + /* Read the size if it is given */ if (CurTok.Tok != TOK_RBRACK) { - ExprDesc lval; - constexpr (&lval); - Size = lval.ConstVal; + ExprDesc Expr; + ConstAbsIntExpr (hie1, &Expr); + if (Expr.IVal <= 0) { + if (D->Ident[0] != '\0') { + Error ("Size of array `%s' is invalid", D->Ident); + } else { + Error ("Size of array is invalid"); + } + Expr.IVal = 1; + } + Size = Expr.IVal; } + + /* Skip the right bracket */ ConsumeRBrack (); - *D->T++ = T_ARRAY; - Encode (D->T, Size); - D->T += DECODE_SIZE; + + /* Add the array type with the size to the type */ + NeedTypeSpace (D, 1); + D->Type[D->Index].C = T_ARRAY; + D->Type[D->Index].A.L = Size; + ++D->Index; } } + + /* If we have remaining qualifiers, flag them as invalid */ + if (Qualifiers & T_QUAL_NEAR) { + Error ("Invalid `__near__' qualifier"); + } + if (Qualifiers & T_QUAL_FAR) { + Error ("Invalid `__far__' qualifier"); + } + if (Qualifiers & T_QUAL_FASTCALL) { + Error ("Invalid `__fastcall__' qualifier"); + } + if (Qualifiers & T_QUAL_CDECL) { + Error ("Invalid `__cdecl__' qualifier"); + } } @@ -877,7 +1442,7 @@ static void Decl (Declaration* D, unsigned Mode) -type* ParseType (type* Type) +Type* ParseType (Type* T) /* Parse a complete type specification */ { DeclSpec Spec; @@ -885,56 +1450,132 @@ type* ParseType (type* Type) /* Get a type without a default */ InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1); + ParseTypeSpec (&Spec, -1, T_QUAL_NONE); /* Parse additional declarators */ - InitDeclaration (&Decl); ParseDecl (&Spec, &Decl, DM_NO_IDENT); /* Copy the type to the target buffer */ - TypeCpy (Type, Decl.Type); + TypeCopy (T, Decl.Type); /* Return a pointer to the target buffer */ - return Type; + return T; } -void ParseDecl (const DeclSpec* Spec, Declaration* D, unsigned Mode) +void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Parse a variable, type or function declaration */ { /* Initialize the Declaration struct */ InitDeclaration (D); /* Get additional declarators and the identifier */ - Decl (D, Mode); + Declarator (Spec, D, Mode); /* Add the base type. */ - TypeCpy (D->T, Spec->Type); + NeedTypeSpace (D, TypeLen (Spec->Type) + 1); /* Bounds check */ + TypeCopy (D->Type + D->Index, Spec->Type); + + /* Use the storage class from the declspec */ + D->StorageClass = Spec->StorageClass; + + /* Do several fixes on qualifiers */ + FixQualifiers (D->Type); + + /* If we have a function, add a special storage class */ + if (IsTypeFunc (D->Type)) { + D->StorageClass |= SC_FUNC; + } + + /* Parse attributes for this declaration */ + ParseAttribute (D); + + /* Check several things for function or function pointer types */ + if (IsTypeFunc (D->Type) || IsTypeFuncPtr (D->Type)) { + + /* A function. Check the return type */ + Type* RetType = GetFuncReturn (D->Type); + + /* Functions may not return functions or arrays */ + if (IsTypeFunc (RetType)) { + Error ("Functions are not allowed to return functions"); + } else if (IsTypeArray (RetType)) { + Error ("Functions are not allowed to return arrays"); + } + + /* The return type must not be qualified */ + if (GetQualifier (RetType) != T_QUAL_NONE && RetType[1].C == T_END) { + + if (GetType (RetType) == T_TYPE_VOID) { + /* A qualified void type is always an error */ + Error ("function definition has qualified void return type"); + } else { + /* For others, qualifiers are ignored */ + Warning ("type qualifiers ignored on function return type"); + RetType[0].C = UnqualifiedType (RetType[0].C); + } + } + + /* Warn about an implicit int return in the function */ + if ((Spec->Flags & DS_DEF_TYPE) != 0 && + RetType[0].C == T_INT && RetType[1].C == T_END) { + /* Function has an implicit int return. Output a warning if we don't + * have the C89 standard enabled explicitly. + */ + if (IS_Get (&Standard) >= STD_C99) { + Warning ("Implicit `int' return type is an obsolete feature"); + } + GetFuncDesc (D->Type)->Flags |= FD_OLDSTYLE_INTRET; + } + + } + + /* For anthing that is not a function or typedef, check for an implicit + * int declaration. + */ + if ((D->StorageClass & SC_FUNC) != SC_FUNC && + (D->StorageClass & SC_TYPEDEF) != SC_TYPEDEF) { + /* If the standard was not set explicitly to C89, print a warning + * for variables with implicit int type. + */ + if ((Spec->Flags & DS_DEF_TYPE) != 0 && IS_Get (&Standard) >= STD_C99) { + Warning ("Implicit `int' is an obsolete feature"); + } + } /* Check the size of the generated type */ - if (!IsTypeFunc (D->Type) && !IsTypeVoid (D->Type) && SizeOf (D->Type) >= 0x10000) { - if (D->Ident[0] != '\0') { - Error ("Size of `%s' is invalid", D->Ident); - } else { - Error ("Invalid size"); - } + if (!IsTypeFunc (D->Type) && !IsTypeVoid (D->Type)) { + unsigned Size = SizeOf (D->Type); + if (Size >= 0x10000) { + if (D->Ident[0] != '\0') { + Error ("Size of `%s' is invalid (0x%06X)", D->Ident, Size); + } else { + Error ("Invalid size in declaration (0x%06X)", Size); + } + } } + } -void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, int DefType) +void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, long DefType) /* Parse a declaration specification */ { + TypeCode Qualifiers; + /* Initialize the DeclSpec struct */ InitDeclSpec (D); - /* First, get the storage class specifier for this declaration */ + /* There may be qualifiers *before* the storage class specifier */ + Qualifiers = OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE); + + /* Now get the storage class specifier for this declaration */ ParseStorageClass (D, DefStorage); - /* Parse the type specifiers */ - ParseTypeSpec (D, DefType); + /* Parse the type specifiers passing any initial type qualifiers */ + ParseTypeSpec (D, DefType, Qualifiers); } @@ -952,206 +1593,573 @@ void CheckEmptyDecl (const DeclSpec* D) -static void ParseVoidInit (void) -/* Parse an initialization of a void variable (special cc65 extension) */ +static void SkipInitializer (unsigned BracesExpected) +/* Skip the remainder of an initializer in case of errors. Try to be somewhat + * smart so we don't have too many following errors. + */ { - ExprDesc lval; + while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected > 0) { + switch (CurTok.Tok) { + case TOK_RCURLY: --BracesExpected; break; + case TOK_LCURLY: ++BracesExpected; break; + default: break; + } + NextToken (); + } +} - /* Allow an arbitrary list of values */ - ConsumeLCurly (); - do { - constexpr (&lval); - switch (lval.Type[0]) { - case T_SCHAR: - case T_UCHAR: - if ((lval.Flags & E_MCTYPE) == E_TCONST) { - /* Make it byte sized */ - lval.ConstVal &= 0xFF; - } - DefineData (&lval); - break; - case T_SHORT: - case T_USHORT: - case T_INT: - case T_UINT: - case T_PTR: - case T_ARRAY: - if ((lval.Flags & E_MCTYPE) == E_TCONST) { - /* Make it word sized */ - lval.ConstVal &= 0xFFFF; - } - DefineData (&lval); - break; +static unsigned OpeningCurlyBraces (unsigned BracesNeeded) +/* Accept any number of opening curly braces around an initialization, skip + * them and return the number. If the number of curly braces is less than + * BracesNeeded, issue a warning. + */ +{ + unsigned BraceCount = 0; + while (CurTok.Tok == TOK_LCURLY) { + ++BraceCount; + NextToken (); + } + if (BraceCount < BracesNeeded) { + Error ("`{' expected"); + } + return BraceCount; +} - case T_LONG: - case T_ULONG: - DefineData (&lval); - break; - default: - Error ("Illegal type in initialization"); - break; - } +static void ClosingCurlyBraces (unsigned BracesExpected) +/* Accept and skip the given number of closing curly braces together with + * an optional comma. Output an error messages, if the input does not contain + * the expected number of braces. + */ +{ + while (BracesExpected) { + if (CurTok.Tok == TOK_RCURLY) { + NextToken (); + } else if (CurTok.Tok == TOK_COMMA && NextTok.Tok == TOK_RCURLY) { + NextToken (); + NextToken (); + } else { + Error ("`}' expected"); + return; + } + --BracesExpected; + } +} - if (CurTok.Tok != TOK_COMMA) { - break; - } - NextToken (); - } while (CurTok.Tok != TOK_RCURLY); - ConsumeRCurly (); +static void DefineData (ExprDesc* Expr) +/* Output a data definition for the given expression */ +{ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute: numeric address or const */ + g_defdata (TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0); + break; + + case E_LOC_GLOBAL: + /* Global variable */ + g_defdata (CF_EXTERNAL, Expr->Name, Expr->IVal); + break; + + case E_LOC_STATIC: + case E_LOC_LITERAL: + /* Static variable or literal in the literal pool */ + g_defdata (CF_STATIC, Expr->Name, Expr->IVal); + break; + + case E_LOC_REGISTER: + /* Register variable. Taking the address is usually not + * allowed. + */ + if (IS_Get (&AllowRegVarAddr) == 0) { + Error ("Cannot take the address of a register variable"); + } + g_defdata (CF_REGVAR, Expr->Name, Expr->IVal); + break; + + case E_LOC_STACK: + case E_LOC_PRIMARY: + case E_LOC_EXPR: + Error ("Non constant initializer"); + break; + + default: + Internal ("Unknown constant type: 0x%04X", ED_GetLoc (Expr)); + } } -static void ParseStructInit (type* Type) -/* Parse initialization of a struct or union */ +static void OutputBitFieldData (StructInitData* SI) +/* Output bit field data */ { - SymEntry* Entry; - SymTable* Tab; + /* Ignore if we have no data */ + if (SI->ValBits > 0) { + + /* Output the data */ + g_defdata (CF_INT | CF_UNSIGNED | CF_CONST, SI->BitVal, 0); + + /* Clear the data from SI and account for the size */ + SI->BitVal = 0; + SI->ValBits = 0; + SI->Offs += SIZEOF_INT; + } +} + + + +static void ParseScalarInitInternal (Type* T, ExprDesc* ED) +/* Parse initializaton for scalar data types. This function will not output the + * data but return it in ED. + */ +{ + /* Optional opening brace */ + unsigned BraceCount = OpeningCurlyBraces (0); + + /* We warn if an initializer for a scalar contains braces, because this is + * quite unusual and often a sign for some problem in the input. + */ + if (BraceCount > 0) { + Warning ("Braces around scalar initializer"); + } + + /* Get the expression and convert it to the target type */ + ConstExpr (hie1, ED); + TypeConversion (ED, T); + + /* Close eventually opening braces */ + ClosingCurlyBraces (BraceCount); +} + + + +static unsigned ParseScalarInit (Type* T) +/* Parse initializaton for scalar data types. Return the number of data bytes. */ +{ + ExprDesc ED; + + /* Parse initialization */ + ParseScalarInitInternal (T, &ED); + + /* Output the data */ + DefineData (&ED); + + /* Done */ + return SizeOf (T); +} + + + +static unsigned ParsePointerInit (Type* T) +/* Parse initializaton for pointer data types. Return the number of data bytes. */ +{ + /* Optional opening brace */ + unsigned BraceCount = OpeningCurlyBraces (0); + + /* Expression */ + ExprDesc ED; + ConstExpr (hie1, &ED); + TypeConversion (&ED, T); + + /* Output the data */ + DefineData (&ED); + + /* Close eventually opening braces */ + ClosingCurlyBraces (BraceCount); + + /* Done */ + return SIZEOF_PTR; +} + + + +static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) +/* Parse initializaton for arrays. Return the number of data bytes. */ +{ + int Count; + + /* Get the array data */ + Type* ElementType = GetElementType (T); + unsigned ElementSize = CheckedSizeOf (ElementType); + long ElementCount = GetElementCount (T); + + /* Special handling for a character array initialized by a literal */ + if (IsTypeChar (ElementType) && + (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST || + (CurTok.Tok == TOK_LCURLY && + (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) { + + /* Char array initialized by string constant */ + int NeedParen; + + /* If we initializer is enclosed in brackets, remember this fact and + * skip the opening bracket. + */ + NeedParen = (CurTok.Tok == TOK_LCURLY); + if (NeedParen) { + NextToken (); + } + + /* Translate into target charset */ + TranslateLiteral (CurTok.SVal); + + /* If the array is one too small for the string literal, omit the + * trailing zero. + */ + Count = GetLiteralSize (CurTok.SVal); + if (ElementCount != UNSPECIFIED && + ElementCount != FLEXIBLE && + Count == ElementCount + 1) { + /* Omit the trailing zero */ + --Count; + } + + /* Output the data */ + g_defbytes (GetLiteralStr (CurTok.SVal), Count); + + /* Skip the string */ + NextToken (); + + /* If the initializer was enclosed in curly braces, we need a closing + * one. + */ + if (NeedParen) { + ConsumeRCurly (); + } + + } else { + + /* Curly brace */ + ConsumeLCurly (); + + /* Initialize the array members */ + Count = 0; + while (CurTok.Tok != TOK_RCURLY) { + /* Flexible array members may not be initialized within + * an array (because the size of each element may differ + * otherwise). + */ + ParseInitInternal (ElementType, 0); + ++Count; + if (CurTok.Tok != TOK_COMMA) + break; + NextToken (); + } + + /* Closing curly braces */ + ConsumeRCurly (); + } + + if (ElementCount == UNSPECIFIED) { + /* Number of elements determined by initializer */ + SetElementCount (T, Count); + ElementCount = Count; + } else if (ElementCount == FLEXIBLE && AllowFlexibleMembers) { + /* In non ANSI mode, allow initialization of flexible array + * members. + */ + ElementCount = Count; + } else if (Count < ElementCount) { + g_zerobytes ((ElementCount - Count) * ElementSize); + } else if (Count > ElementCount) { + Error ("Too many initializers"); + } + return ElementCount * ElementSize; +} + + + +static unsigned ParseStructInit (Type* T, int AllowFlexibleMembers) +/* Parse initialization of a struct or union. Return the number of data bytes. */ +{ + SymEntry* Entry; + SymTable* Tab; + StructInitData SI; + /* Consume the opening curly brace */ ConsumeLCurly (); /* Get a pointer to the struct entry from the type */ - Entry = (SymEntry*) Decode (Type + 1); + Entry = GetSymEntry (T); + + /* Get the size of the struct from the symbol table entry */ + SI.Size = Entry->V.S.Size; /* Check if this struct definition has a field table. If it doesn't, it * is an incomplete definition. */ Tab = Entry->V.S.SymTab; if (Tab == 0) { - Error ("Cannot initialize variables with incomplete type"); - /* Returning here will cause lots of errors, but recovery is difficult */ - return; + Error ("Cannot initialize variables with incomplete type"); + /* Try error recovery */ + SkipInitializer (1); + /* Nothing initialized */ + return 0; } /* Get a pointer to the list of symbols */ Entry = Tab->SymHead; + + /* Initialize fields */ + SI.Offs = 0; + SI.BitVal = 0; + SI.ValBits = 0; while (CurTok.Tok != TOK_RCURLY) { - if (Entry == 0) { - Error ("Too many initializers"); - return; - } - ParseInit (Entry->Type); - Entry = Entry->NextSym; - if (CurTok.Tok != TOK_COMMA) - break; - NextToken (); + + /* */ + if (Entry == 0) { + Error ("Too many initializers"); + SkipInitializer (1); + return SI.Offs; + } + + /* Parse initialization of one field. Bit-fields need a special + * handling. + */ + if (SymIsBitField (Entry)) { + + ExprDesc ED; + unsigned Val; + unsigned Shift; + + /* Calculate the bitmask from the bit-field data */ + unsigned Mask = (1U << Entry->V.B.BitWidth) - 1U; + + /* Safety ... */ + CHECK (Entry->V.B.Offs * CHAR_BITS + Entry->V.B.BitOffs == + SI.Offs * CHAR_BITS + SI.ValBits); + + /* This may be an anonymous bit-field, in which case it doesn't + * have an initializer. + */ + if (IsAnonName (Entry->Name)) { + /* Account for the data and output it if we have a full word */ + SI.ValBits += Entry->V.B.BitWidth; + CHECK (SI.ValBits <= INT_BITS); + if (SI.ValBits == INT_BITS) { + OutputBitFieldData (&SI); + } + goto NextMember; + } else { + /* Read the data, check for a constant integer, do a range + * check. + */ + ParseScalarInitInternal (type_uint, &ED); + if (!ED_IsConstAbsInt (&ED)) { + Error ("Constant initializer expected"); + ED_MakeConstAbsInt (&ED, 1); + } + if (ED.IVal > (long) Mask) { + Warning ("Truncating value in bit-field initializer"); + ED.IVal &= (long) Mask; + } + Val = (unsigned) ED.IVal; + } + + /* Add the value to the currently stored bit-field value */ + Shift = (Entry->V.B.Offs - SI.Offs) * CHAR_BITS + Entry->V.B.BitOffs; + SI.BitVal |= (Val << Shift); + + /* Account for the data and output it if we have a full word */ + SI.ValBits += Entry->V.B.BitWidth; + CHECK (SI.ValBits <= INT_BITS); + if (SI.ValBits == INT_BITS) { + OutputBitFieldData (&SI); + } + + } else { + + /* Standard member. We should never have stuff from a + * bit-field left + */ + CHECK (SI.ValBits == 0); + + /* Flexible array members may only be initialized if they are + * the last field (or part of the last struct field). + */ + SI.Offs += ParseInitInternal (Entry->Type, AllowFlexibleMembers && Entry->NextSym == 0); + } + + /* More initializers? */ + if (CurTok.Tok != TOK_COMMA) { + break; + } + + /* Skip the comma */ + NextToken (); + +NextMember: + /* Next member. For unions, only the first one can be initialized */ + if (IsTypeUnion (T)) { + /* Union */ + Entry = 0; + } else { + /* Struct */ + Entry = Entry->NextSym; + } } /* Consume the closing curly brace */ ConsumeRCurly (); + /* If we have data from a bit-field left, output it now */ + OutputBitFieldData (&SI); + /* If there are struct fields left, reserve additional storage */ - while (Entry) { - g_zerobytes (SizeOf (Entry->Type)); - Entry = Entry->NextSym; + if (SI.Offs < SI.Size) { + g_zerobytes (SI.Size - SI.Offs); + SI.Offs = SI.Size; } + + /* Return the actual number of bytes initialized. This number may be + * larger than sizeof (Struct) if flexible array members are present and + * were initialized (possible in non ANSI mode). + */ + return SI.Offs; } -void ParseInit (type* T) -/* Parse initialization of variables. */ +static unsigned ParseVoidInit (void) +/* Parse an initialization of a void variable (special cc65 extension). + * Return the number of bytes initialized. + */ { - ExprDesc lval; - type* t; - const char* str; - int Count; - int Size; + ExprDesc Expr; + unsigned Size; + + /* Opening brace */ + ConsumeLCurly (); + + /* Allow an arbitrary list of values */ + Size = 0; + do { + ConstExpr (hie1, &Expr); + switch (UnqualifiedType (Expr.Type[0].C)) { + + case T_SCHAR: + case T_UCHAR: + if (ED_IsConstAbsInt (&Expr)) { + /* Make it byte sized */ + Expr.IVal &= 0xFF; + } + DefineData (&Expr); + Size += SIZEOF_CHAR; + break; + + case T_SHORT: + case T_USHORT: + case T_INT: + case T_UINT: + case T_PTR: + case T_ARRAY: + if (ED_IsConstAbsInt (&Expr)) { + /* Make it word sized */ + Expr.IVal &= 0xFFFF; + } + DefineData (&Expr); + Size += SIZEOF_INT; + break; + + case T_LONG: + case T_ULONG: + if (ED_IsConstAbsInt (&Expr)) { + /* Make it dword sized */ + Expr.IVal &= 0xFFFFFFFF; + } + DefineData (&Expr); + Size += SIZEOF_LONG; + break; + + default: + Error ("Illegal type in initialization"); + break; + + } + + if (CurTok.Tok != TOK_COMMA) { + break; + } + NextToken (); + + } while (CurTok.Tok != TOK_RCURLY); + + /* Closing brace */ + ConsumeRCurly (); + + /* Return the number of bytes initialized */ + return Size; +} - switch (UnqualifiedType (*T)) { + + +static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) +/* Parse initialization of variables. Return the number of data bytes. */ +{ + switch (UnqualifiedType (T->C)) { case T_SCHAR: case T_UCHAR: - constexpr (&lval); - if ((lval.Flags & E_MCTYPE) == E_TCONST) { - /* Make it byte sized */ - lval.ConstVal &= 0xFF; - } - assignadjust (T, &lval); - DefineData (&lval); - break; - case T_SHORT: case T_USHORT: case T_INT: case T_UINT: - case T_PTR: - constexpr (&lval); - if ((lval.Flags & E_MCTYPE) == E_TCONST) { - /* Make it word sized */ - lval.ConstVal &= 0xFFFF; - } - assignadjust (T, &lval); - DefineData (&lval); - break; - case T_LONG: case T_ULONG: - constexpr (&lval); - if ((lval.Flags & E_MCTYPE) == E_TCONST) { - /* Make it long sized */ - lval.ConstVal &= 0xFFFFFFFF; - } - assignadjust (T, &lval); - DefineData (&lval); - break; + case T_FLOAT: + case T_DOUBLE: + return ParseScalarInit (T); + + case T_PTR: + return ParsePointerInit (T); case T_ARRAY: - Size = Decode (T + 1); - t = T + DECODE_SIZE + 1; - if (IsTypeChar(t) && CurTok.Tok == TOK_SCONST) { - str = GetLiteral (CurTok.IVal); - Count = strlen (str) + 1; - TranslateLiteralPool (CurTok.IVal); /* Translate into target charset */ - g_defbytes (str, Count); - ResetLiteralPoolOffs (CurTok.IVal); /* Remove string from pool */ - NextToken (); - } else { - ConsumeLCurly (); - Count = 0; - while (CurTok.Tok != TOK_RCURLY) { - ParseInit (T + DECODE_SIZE + 1); - ++Count; - if (CurTok.Tok != TOK_COMMA) - break; - NextToken (); - } - ConsumeRCurly (); - } - if (Size == 0) { - Encode (T + 1, Count); - } else if (Count < Size) { - g_zerobytes ((Size - Count) * SizeOf (T + DECODE_SIZE + 1)); - } else if (Count > Size) { - Error ("Too many initializers"); - } - break; + return ParseArrayInit (T, AllowFlexibleMembers); case T_STRUCT: case T_UNION: - ParseStructInit (T); - break; + return ParseStructInit (T, AllowFlexibleMembers); case T_VOID: - if (!ANSI) { + if (IS_Get (&Standard) == STD_CC65) { /* Special cc65 extension in non ANSI mode */ - ParseVoidInit (); - break; + return ParseVoidInit (); } /* FALLTHROUGH */ default: Error ("Illegal type"); - break; + return SIZEOF_CHAR; + + } +} + + +unsigned ParseInit (Type* T) +/* Parse initialization of variables. Return the number of data bytes. */ +{ + /* Parse the initialization. Flexible array members can only be initialized + * in cc65 mode. + */ + unsigned Size = ParseInitInternal (T, IS_Get (&Standard) == STD_CC65); + + /* The initialization may not generate code on global level, because code + * outside function scope will never get executed. + */ + if (HaveGlobalCode ()) { + Error ("Non constant initializers"); + RemoveGlobalCode (); } + + /* Return the size needed for the initialization */ + return Size; }