diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 2a989e9f8..3f2725659 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -83,14 +83,9 @@ const char* GetBasicTypeName (const Type* T) ** Return "type" for unknown basic types. */ { - switch (GetType (T)) { - case T_TYPE_CHAR: return "char"; - case T_TYPE_SHORT: return "short"; - case T_TYPE_INT: return "integer"; - case T_TYPE_LONG: return "long"; - case T_TYPE_LONGLONG: return "long long"; + switch (GetRawType (T)) { case T_TYPE_ENUM: return "enum"; - case T_TYPE_FLOAT: return "poinfloatter"; + case T_TYPE_FLOAT: return "float"; case T_TYPE_DOUBLE: return "double"; case T_TYPE_VOID: return "void"; case T_TYPE_STRUCT: return "struct"; @@ -99,8 +94,42 @@ const char* GetBasicTypeName (const Type* T) case T_TYPE_PTR: return "pointer"; case T_TYPE_FUNC: return "function"; case T_TYPE_NONE: /* FALLTHROUGH */ - default: return "type"; + default: break; } + if (IsClassInt (T)) { + if (IsSignSigned (T)) { + switch (GetRawType (T)) { + case T_TYPE_CHAR: return "signed char"; + case T_TYPE_SHORT: return "short"; + case T_TYPE_INT: return "int"; + case T_TYPE_LONG: return "long"; + case T_TYPE_LONGLONG: return "long long"; + default: + return "signed integer"; + } + } else if (IsSignUnsigned (T)) { + switch (GetRawType (T)) { + case T_TYPE_CHAR: return "unsigned char"; + case T_TYPE_SHORT: return "unsigned short"; + case T_TYPE_INT: return "unsigned int"; + case T_TYPE_LONG: return "unsigned long"; + case T_TYPE_LONGLONG: return "unsigned long long"; + default: + return "unsigned integer"; + } + } else { + switch (GetRawType (T)) { + case T_TYPE_CHAR: return "char"; + case T_TYPE_SHORT: return "short"; + case T_TYPE_INT: return "int"; + case T_TYPE_LONG: return "long"; + case T_TYPE_LONGLONG: return "long long"; + default: + return "integer"; + } + } + } + return "type"; } @@ -245,6 +274,49 @@ const Type* GetStructReplacementType (const Type* SType) +long GetIntegerTypeMin (const Type* Type) +/* Get the smallest possible value of the integer type */ +{ + if (IsSignSigned (Type)) { + return (long)(0xFFFFFFFF << (CHAR_BITS * SizeOf (Type) - 1U)); + } else { + return 0; + } +} + + + +unsigned long GetIntegerTypeMax (const Type* Type) +/* Get the largest possible value of the integer type */ +{ + if (IsSignSigned (Type)) { + return (1UL << (CHAR_BITS * SizeOf (Type) - 1U)) - 1UL; + } else { + return (1UL << (CHAR_BITS * SizeOf (Type))) - 1UL; + } +} + + + +static unsigned TypeOfBySize (const Type* Type) +/* Get the code generator replacement type of the object by its size */ +{ + unsigned NewType; + /* If the size is less than or equal to that of a a long, we will copy + ** the struct using the primary register, otherwise we use memcpy. + */ + switch (SizeOf (Type)) { + case 1: NewType = CF_CHAR; break; + case 2: NewType = CF_INT; break; + case 3: /* FALLTHROUGH */ + case 4: NewType = CF_LONG; break; + default: NewType = CF_NONE; break; + } + + return NewType; +} + + Type* PointerTo (const Type* T) /* Return a type string that is "pointer to T". The type string is allocated ** on the heap and may be freed after use. @@ -321,6 +393,9 @@ void PrintType (FILE* F, const Type* T) case T_TYPE_LONGLONG: fprintf (F, "long long"); break; + case T_TYPE_ENUM: + fprintf (F, "enum"); + break; case T_TYPE_FLOAT: fprintf (F, "float"); break; @@ -430,10 +505,68 @@ int TypeHasAttr (const Type* T) +const Type* GetUnderlyingType (const Type* Type) +/* Get the underlying type of an enum or other integer class type */ +{ + if (IsTypeEnum (Type)) { + + /* This should not happen, but just in case */ + if (Type->A.P == 0) { + Internal ("Enum tag type error in GetUnderlyingTypeCode"); + } + + return ((SymEntry*)Type->A.P)->V.E.Type; + } + + return Type; +} + + + +TypeCode GetUnderlyingTypeCode (const Type* Type) +/* Get the type code of the unqualified underlying type of TCode. +** Return UnqualifiedType (TCode) if TCode is not scalar. +*/ +{ + TypeCode Underlying = UnqualifiedType (Type->C); + TypeCode TCode; + + /* We could also support other T_CLASS_INT types, but just enums for now */ + if (IsTypeEnum (Type)) { + + /* This should not happen, but just in case */ + if (Type->A.P == 0) { + Internal ("Enum tag type error in GetUnderlyingTypeCode"); + } + + /* Inspect the underlying type of the enum */ + if (((SymEntry*)Type->A.P)->V.E.Type == 0) { + /* Incomplete enum type is used */ + return Underlying; + } + TCode = UnqualifiedType (((SymEntry*)Type->A.P)->V.E.Type->C); + + /* Replace the type code with integer */ + Underlying = (TCode & ~T_MASK_TYPE); + switch (TCode & T_MASK_SIZE) { + case T_SIZE_INT: Underlying |= T_TYPE_INT; break; + case T_SIZE_LONG: Underlying |= T_TYPE_LONG; break; + case T_SIZE_SHORT: Underlying |= T_TYPE_SHORT; break; + case T_SIZE_CHAR: Underlying |= T_TYPE_CHAR; break; + case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break; + default: Underlying |= T_TYPE_INT; break; + } + } + + return Underlying; +} + + + unsigned SizeOf (const Type* T) /* Compute size of object represented by type array. */ { - switch (UnqualifiedType (T->C)) { + switch (GetUnderlyingTypeCode (T)) { case T_VOID: /* A void variable is a cc65 extension. @@ -470,9 +603,6 @@ unsigned SizeOf (const Type* T) case T_ULONGLONG: return SIZEOF_LONGLONG; - case T_ENUM: - return SIZEOF_INT; - case T_FLOAT: return SIZEOF_FLOAT; @@ -491,7 +621,12 @@ unsigned SizeOf (const Type* T) return T->A.U * SizeOf (T + 1); } + case T_ENUM: + /* Incomplete enum type */ + return 0; + default: + Internal ("Unknown type in SizeOf: %04lX", T->C); return 0; @@ -547,7 +682,9 @@ unsigned CheckedPSizeOf (const Type* T) unsigned TypeOf (const Type* T) /* Get the code generator base type of the object */ { - switch (UnqualifiedType (T->C)) { + unsigned NewType; + + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: return CF_CHAR; @@ -557,7 +694,6 @@ unsigned TypeOf (const Type* T) case T_SHORT: case T_INT: - case T_ENUM: return CF_INT; case T_USHORT: @@ -582,6 +718,10 @@ unsigned TypeOf (const Type* T) case T_STRUCT: case T_UNION: + NewType = TypeOfBySize (T); + if (NewType != CF_NONE) { + return NewType; + } /* Address of ... */ return CF_INT | CF_UNSIGNED; @@ -726,8 +866,8 @@ Type* GetBaseElementType (Type* T) SymEntry* GetSymEntry (const Type* T) /* Return a SymEntry pointer from a type */ { - /* Only structs or unions have a SymEntry attribute */ - CHECK (IsClassStruct (T)); + /* Only enums, structs or unions have a SymEntry attribute */ + CHECK (IsClassStruct (T) || IsTypeEnum (T)); /* Return the attribute */ return T->A.P; @@ -738,8 +878,8 @@ SymEntry* GetSymEntry (const Type* T) void SetSymEntry (Type* T, SymEntry* S) /* Set the SymEntry pointer for a type */ { - /* Only structs or unions have a SymEntry attribute */ - CHECK (IsClassStruct (T)); + /* Only enums, structs or unions have a SymEntry attribute */ + CHECK (IsClassStruct (T) || IsTypeEnum (T)); /* Set the attribute */ T->A.P = S; diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 3464e8a16..361f5e0a6 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -96,37 +96,39 @@ enum { /* Type size modifiers */ T_SIZE_NONE = 0x000000, - T_SIZE_SHORT = 0x000200, - T_SIZE_LONG = 0x000400, - T_SIZE_LONGLONG = 0x000600, - T_MASK_SIZE = 0x000600, + T_SIZE_CHAR = 0x000200, + T_SIZE_SHORT = 0x000400, + T_SIZE_INT = 0x000600, + T_SIZE_LONG = 0x000800, + T_SIZE_LONGLONG = 0x000A00, + T_MASK_SIZE = 0x000E00, /* Type qualifiers */ T_QUAL_NONE = 0x000000, - T_QUAL_CONST = 0x000800, - T_QUAL_VOLATILE = 0x001000, - T_QUAL_RESTRICT = 0x002000, - T_QUAL_NEAR = 0x004000, - T_QUAL_FAR = 0x008000, + T_QUAL_CONST = 0x001000, + T_QUAL_VOLATILE = 0x002000, + T_QUAL_RESTRICT = 0x004000, + T_QUAL_NEAR = 0x008000, + T_QUAL_FAR = 0x010000, T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR, - T_QUAL_FASTCALL = 0x010000, - T_QUAL_CDECL = 0x020000, + T_QUAL_FASTCALL = 0x020000, + T_QUAL_CDECL = 0x040000, T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL, - T_MASK_QUAL = 0x03F800, + T_MASK_QUAL = 0x07F000, /* Types */ - T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, - T_SCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, - T_UCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, + T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, + T_SCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_CHAR, + T_UCHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_CHAR, T_SHORT = T_TYPE_SHORT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_SHORT, T_USHORT = T_TYPE_SHORT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_SHORT, - T_INT = T_TYPE_INT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, - T_UINT = T_TYPE_INT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, + T_INT = T_TYPE_INT | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_INT, + T_UINT = T_TYPE_INT | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_INT, T_LONG = T_TYPE_LONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONG, T_ULONG = T_TYPE_LONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONG, T_LONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONGLONG, T_ULONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONGLONG, - T_ENUM = T_TYPE_ENUM | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, + T_ENUM = T_TYPE_ENUM | T_CLASS_INT | T_SIGN_NONE | T_SIZE_NONE, T_FLOAT = T_TYPE_FLOAT | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_DOUBLE = T_TYPE_DOUBLE | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_VOID = T_TYPE_VOID | T_CLASS_NONE | T_SIGN_NONE | T_SIZE_NONE, @@ -246,6 +248,12 @@ Type* GetImplicitFuncType (void); const Type* GetStructReplacementType (const Type* SType); /* Get a replacement type for passing a struct/union in the primary register */ +long GetIntegerTypeMin (const Type* Type); +/* Get the smallest possible value of the integer type */ + +unsigned long GetIntegerTypeMax (const Type* Type); +/* Get the largest possible value of the integer type */ + Type* PointerTo (const Type* T); /* Return a type string that is "pointer to T". The type string is allocated ** on the heap and may be freed after use. @@ -283,6 +291,14 @@ INLINE TypeCode UnqualifiedType (TypeCode T) # define UnqualifiedType(T) ((T) & ~T_MASK_QUAL) #endif +const Type* GetUnderlyingType (const Type* Type); +/* Get the underlying type of an enum or other integer class type */ + +TypeCode GetUnderlyingTypeCode (const Type* Type); +/* Get the type code of the unqualified underlying type of TCode. +** Return TCode if it is not scalar. +*/ + unsigned SizeOf (const Type* T); /* Compute size of object represented by type array. */ @@ -312,123 +328,163 @@ Type* ArrayToPtr (Type* T); /* Convert an array to a pointer to it's first element */ #if defined(HAVE_INLINE) -INLINE TypeCode GetType (const Type* T) +INLINE TypeCode GetRawType (const Type* T) /* Get the raw type */ { return (T->C & T_MASK_TYPE); } #else -# define GetType(T) ((T)->C & T_MASK_TYPE) +# define GetRawType(T) ((T)->C & T_MASK_TYPE) #endif #if defined(HAVE_INLINE) INLINE int IsTypeChar (const Type* T) /* Return true if this is a character type */ { - return (GetType (T) == T_TYPE_CHAR); + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR); } #else -# define IsTypeChar(T) (GetType (T) == T_TYPE_CHAR) +# define IsTypeChar(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR) #endif #if defined(HAVE_INLINE) INLINE int IsTypeInt (const Type* T) /* Return true if this is an int type (signed or unsigned) */ { - return (GetType (T) == T_TYPE_INT); + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT); } #else -# define IsTypeInt(T) (GetType (T) == T_TYPE_INT) +# define IsTypeInt(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeLong (const Type* T) /* Return true if this is a long type (signed or unsigned) */ { - return (GetType (T) == T_TYPE_LONG); + return (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG); } #else -# define IsTypeLong(T) (GetType (T) == T_TYPE_LONG) +# define IsTypeLong(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_LONG) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsRawTypeChar (const Type* T) +/* Return true if this is a character raw type */ +{ + return (GetRawType (T) == T_TYPE_CHAR); +} +#else +# define IsRawTypeChar(T) (GetRawType (T) == T_TYPE_CHAR) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsRawTypeInt (const Type* T) +/* Return true if this is an int raw type (signed or unsigned) */ +{ + return (GetRawType (T) == T_TYPE_INT); +} +#else +# define IsRawTypeInt(T) (GetRawType (T) == T_TYPE_INT) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsRawTypeLong (const Type* T) +/* Return true if this is a long raw type (signed or unsigned) */ +{ + return (GetRawType (T) == T_TYPE_LONG); +} +#else +# define IsRawTypeLong(T) (GetRawType (T) == T_TYPE_LONG) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFloat (const Type* T) /* Return true if this is a float type */ { - return (GetType (T) == T_TYPE_FLOAT); + return (GetRawType (T) == T_TYPE_FLOAT); } #else -# define IsTypeFloat(T) (GetType (T) == T_TYPE_FLOAT) +# define IsTypeFloat(T) (GetRawType (T) == T_TYPE_FLOAT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeDouble (const Type* T) /* Return true if this is a double type */ { - return (GetType (T) == T_TYPE_DOUBLE); + return (GetRawType (T) == T_TYPE_DOUBLE); } #else -# define IsTypeDouble(T) (GetType (T) == T_TYPE_DOUBLE) +# define IsTypeDouble(T) (GetRawType (T) == T_TYPE_DOUBLE) #endif #if defined(HAVE_INLINE) INLINE int IsTypePtr (const Type* T) /* Return true if this is a pointer type */ { - return (GetType (T) == T_TYPE_PTR); + return (GetRawType (T) == T_TYPE_PTR); } #else -# define IsTypePtr(T) (GetType (T) == T_TYPE_PTR) +# define IsTypePtr(T) (GetRawType (T) == T_TYPE_PTR) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeEnum (const Type* T) +/* Return true if this is an enum type */ +{ + return (GetRawType (T) == T_TYPE_ENUM); +} +#else +# define IsTypeEnum(T) (GetRawType (T) == T_TYPE_ENUM) #endif #if defined(HAVE_INLINE) INLINE int IsTypeStruct (const Type* T) /* Return true if this is a struct type */ { - return (GetType (T) == T_TYPE_STRUCT); + return (GetRawType (T) == T_TYPE_STRUCT); } #else -# define IsTypeStruct(T) (GetType (T) == T_TYPE_STRUCT) +# define IsTypeStruct(T) (GetRawType (T) == T_TYPE_STRUCT) #endif #if defined(HAVE_INLINE) INLINE int IsTypeUnion (const Type* T) /* Return true if this is a union type */ { - return (GetType (T) == T_TYPE_UNION); + return (GetRawType (T) == T_TYPE_UNION); } #else -# define IsTypeUnion(T) (GetType (T) == T_TYPE_UNION) +# define IsTypeUnion(T) (GetRawType (T) == T_TYPE_UNION) #endif #if defined(HAVE_INLINE) INLINE int IsTypeArray (const Type* T) /* Return true if this is an array type */ { - return (GetType (T) == T_TYPE_ARRAY); + return (GetRawType (T) == T_TYPE_ARRAY); } #else -# define IsTypeArray(T) (GetType (T) == T_TYPE_ARRAY) +# define IsTypeArray(T) (GetRawType (T) == T_TYPE_ARRAY) #endif #if defined(HAVE_INLINE) INLINE int IsTypeVoid (const Type* T) /* Return true if this is a void type */ { - return (GetType (T) == T_TYPE_VOID); + return (GetRawType (T) == T_TYPE_VOID); } #else -# define IsTypeVoid(T) (GetType (T) == T_TYPE_VOID) +# define IsTypeVoid(T) (GetRawType (T) == T_TYPE_VOID) #endif #if defined(HAVE_INLINE) INLINE int IsTypeFunc (const Type* T) /* Return true if this is a function class */ { - return (GetType (T) == T_TYPE_FUNC); + return (GetRawType (T) == T_TYPE_FUNC); } #else -# define IsTypeFunc(T) (GetType (T) == T_TYPE_FUNC) +# define IsTypeFunc(T) (GetRawType (T) == T_TYPE_FUNC) #endif #if defined(HAVE_INLINE) @@ -502,13 +558,23 @@ INLINE int IsClassFunc (const Type* T) #endif #if defined(HAVE_INLINE) -INLINE TypeCode GetSignedness (const Type* T) -/* Get the sign of a type */ +INLINE TypeCode GetRawSignedness (const Type* T) +/* Get the raw signedness of a type */ { - return (T->C & T_MASK_SIGN); + return ((T)->C & T_MASK_SIGN); } #else -# define GetSignedness(T) ((T)->C & T_MASK_SIGN) +# define GetRawSignedness(T) ((T)->C & T_MASK_SIGN) +#endif + +#if defined(HAVE_INLINE) +INLINE TypeCode GetSignedness (const Type* T) +/* Get the signedness of a type */ +{ + return (GetUnderlyingTypeCode (T) & T_MASK_SIGN); +} +#else +# define GetSignedness(T) (GetUnderlyingTypeCode (T) & T_MASK_SIGN) #endif #if defined(HAVE_INLINE) diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 323e9af14..734256b97 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -453,27 +453,99 @@ static void ParseStorageClass (DeclSpec* D, unsigned DefStorage) -static void ParseEnumDecl (void) -/* Process an enum declaration . */ +static SymEntry* ESUForwardDecl (const char* Name, unsigned Type) +/* Handle an enum, struct or union forward decl */ { - int EnumVal; - ident Ident; + /* Try to find an enum/struct/union with the given name. If there is none, + ** insert a forward declaration into the current lexical level. + */ + SymEntry* Entry = FindTagSym (Name); + if (Entry == 0) { + if (Type != SC_ENUM) { + Entry = AddStructSym (Name, Type, 0, 0); + } else { + Entry = AddEnumSym (Name, 0, 0); + } + } else if ((Entry->Flags & SC_TYPEMASK) != Type) { + /* Already defined, but not the same type class */ + Error ("Symbol '%s' is already different kind", Name); + } + return Entry; +} + + + +static const Type* GetEnumeratorType (long Min, unsigned long Max, int Signed) +/* GitHub #1093 - We use unsigned types to save spaces whenever possible. +** If both the signed and unsigned integer types of the same minimum size +** capable of representing all values of the enum, we prefer the unsigned +** one. +** Return 0 if impossible to represent Min and Max as the same integer type. +*/ +{ + const Type* Underlying = type_int; /* default type */ + + /* Change the underlying type if necessary */ + if (Min < 0 || Signed) { + /* We can't use unsigned types if there are any negative values */ + if (Max > (unsigned long)INT32_MAX) { + /* No way to represent both Min and Max as the same integer type */ + Underlying = 0; + } else if (Min < INT16_MIN || Max > (unsigned long)INT16_MAX) { + Underlying = type_long; + } else if (Min < INT8_MIN || Max > (unsigned long)INT8_MAX) { + Underlying = type_int; + } else { + Underlying = type_schar; + } + } else { + if (Max > UINT16_MAX) { + Underlying = type_ulong; + } else if (Max > UINT8_MAX) { + Underlying = type_uint; + } else { + Underlying = type_uchar; + } + } + + return Underlying; +} + + + +static SymEntry* ParseEnumDecl (const char* Name) +/* Process an enum declaration */ +{ + SymTable* FieldTab; + long EnumVal; + int IsSigned; + int IsIncremented; + ident Ident; + long MinConstant = 0; + unsigned long MaxConstant = 0; + const Type* NewType = type_int; /* new enumerator type */ + const Type* MemberType = type_int; /* default enumerator type */ /* Accept forward definitions */ if (CurTok.Tok != TOK_LCURLY) { - return; + return ESUForwardDecl (Name, SC_ENUM); } + /* Add the enum tag */ + AddEnumSym (Name, 0, 0); + /* Skip the opening curly brace */ NextToken (); /* Read the enum tags */ - EnumVal = 0; + EnumVal = -1L; while (CurTok.Tok != TOK_RCURLY) { /* We expect an identifier */ if (CurTok.Tok != TOK_IDENT) { - Error ("Identifier expected"); + Error ("Identifier expected for enumerator declarator"); + /* Avoid excessive errors */ + NextToken (); continue; } @@ -483,21 +555,116 @@ static void ParseEnumDecl (void) /* Check for an assigned value */ if (CurTok.Tok == TOK_ASSIGN) { + ExprDesc Expr; NextToken (); ConstAbsIntExpr (hie1, &Expr); - EnumVal = Expr.IVal; + EnumVal = Expr.IVal; + MemberType = Expr.Type; + IsSigned = IsSignSigned (MemberType); + IsIncremented = 0; + + } else { + + /* Defaulted with the same signedness as the previous member's */ + IsSigned = IsSignSigned (MemberType) && + (unsigned long)EnumVal != GetIntegerTypeMax (MemberType); + + /* Enumerate. Signed integer overflow is UB but unsigned integers + ** are guaranteed to wrap around. + */ + EnumVal = (long)((unsigned long)EnumVal + 1UL); + + if (UnqualifiedType (MemberType->C) == T_ULONG && EnumVal == 0) { + /* Warn on 'unsigned long' overflow in enumeration */ + Warning ("Enumerator '%s' overflows the range of '%s'", + Ident, + GetBasicTypeName (type_ulong)); + } + + IsIncremented = 1; } - /* Add an entry to the symbol table */ - AddConstSym (Ident, type_int, SC_ENUMERATOR | SC_CONST, EnumVal++); + /* Track down the min/max values and evaluate the type of EnumVal + ** using GetEnumeratorType in a tricky way. + */ + if (!IsSigned || EnumVal >= 0) { + if ((unsigned long)EnumVal > MaxConstant) { + MaxConstant = (unsigned long)EnumVal; + } + NewType = GetEnumeratorType (0, EnumVal, IsSigned); + } else { + if (EnumVal < MinConstant) { + MinConstant = EnumVal; + } + NewType = GetEnumeratorType (EnumVal, 0, 1); + } + + /* GetEnumeratorType above should never fail, but just in case */ + if (NewType == 0) { + Internal ("Unexpected failure with GetEnumeratorType: %lx", EnumVal); + NewType = type_ulong; + } else if (SizeOf (NewType) < SizeOf (type_int)) { + /* Integer constants are not shorter than int */ + NewType = type_int; + } + + /* Warn if the incremented value exceeds the range of the previous + ** type. + */ + if (IsIncremented && + EnumVal >= 0 && + NewType->C != UnqualifiedType (MemberType->C)) { + /* The possible overflow here can only be when EnumVal > 0 */ + Warning ("Enumerator '%s' (value = %lu) is of type '%s'", + Ident, + (unsigned long)EnumVal, + GetBasicTypeName (NewType)); + } + + /* Warn if the value exceeds range of 'int' in standard mode */ + if (IS_Get (&Standard) != STD_CC65 && NewType->C != T_INT) { + if (!IsSigned || EnumVal >= 0) { + Warning ("ISO C restricts enumerator values to range of 'int'\n" + "\tEnumerator '%s' (value = %lu) is too large", + Ident, + (unsigned long)EnumVal); + } else { + Warning ("ISO C restricts enumerator values to range of 'int'\n" + "\tEnumerator '%s' (value = %ld) is too small", + Ident, + EnumVal); + } + } + + /* Add an entry of the enumerator to the symbol table */ + AddConstSym (Ident, NewType, SC_ENUMERATOR | SC_CONST, EnumVal); + + /* Use this type for following members */ + MemberType = NewType; /* Check for end of definition */ - if (CurTok.Tok != TOK_COMMA) + if (CurTok.Tok != TOK_COMMA) { break; + } NextToken (); } ConsumeRCurly (); + + /* This evaluates the underlying type of the whole enum */ + MemberType = GetEnumeratorType (MinConstant, MaxConstant, 0); + if (MemberType == 0) { + /* It is very likely that the program is wrong */ + Error ("Enumeration values cannot be represented all as 'long'\n" + "\tMin enumerator value = %ld, Max enumerator value = %lu", + MinConstant, MaxConstant); + + /* Avoid more errors */ + MemberType = type_long; + } + + FieldTab = GetSymTab (); + return AddEnumSym (Name, MemberType, FieldTab); } @@ -541,24 +708,6 @@ static int ParseFieldWidth (Declaration* Decl) -static SymEntry* StructOrUnionForwardDecl (const char* Name, unsigned Type) -/* Handle a struct or union forward decl */ -{ - /* Try to find a struct/union 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, Type, 0, 0); - } else if ((Entry->Flags & SC_TYPEMASK) != Type) { - /* Already defined, but no struct */ - Error ("Symbol '%s' is already different kind", Name); - } - return Entry; -} - - - static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs) /* Copy fields from an anon union/struct into the current lexical level. The ** function returns the size of the embedded struct/union. @@ -617,7 +766,7 @@ static SymEntry* ParseUnionDecl (const char* Name) if (CurTok.Tok != TOK_LCURLY) { /* Just a forward declaration. */ - return StructOrUnionForwardDecl (Name, SC_UNION); + return ESUForwardDecl (Name, SC_UNION); } /* Add a forward declaration for the struct in the current lexical level */ @@ -722,7 +871,7 @@ static SymEntry* ParseStructDecl (const char* Name) if (CurTok.Tok != TOK_LCURLY) { /* Just a forward declaration. */ - return StructOrUnionForwardDecl (Name, SC_STRUCT); + return ESUForwardDecl (Name, SC_STRUCT); } /* Add a forward declaration for the struct in the current lexical level */ @@ -1069,29 +1218,23 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers) case TOK_ENUM: NextToken (); - if (CurTok.Tok != TOK_LCURLY) { - /* Named enum */ - if (CurTok.Tok == TOK_IDENT) { - /* Find an entry with this name */ - Entry = FindTagSym (CurTok.Ident); - if (Entry) { - if (SymIsLocal (Entry) && (Entry->Flags & SC_ENUM) == 0) { - Error ("Symbol '%s' is already different kind", Entry->Name); - } - } else { - /* Insert entry into table ### */ - } - /* Skip the identifier */ - NextToken (); - } else { + /* Named enum */ + if (CurTok.Tok == TOK_IDENT) { + strcpy (Ident, CurTok.Ident); + NextToken (); + } else { + if (CurTok.Tok != TOK_LCURLY) { Error ("Identifier expected"); + } else { + AnonName (Ident, "enum"); } } /* Remember we have an extra type decl */ D->Flags |= DS_EXTRA_TYPE; /* Parse the enum decl */ - ParseEnumDecl (); - D->Type[0].C = T_INT; + Entry = ParseEnumDecl (Ident); + D->Type[0].C |= T_ENUM; + SetSymEntry (D->Type, Entry); D->Type[1].C = T_END; break; @@ -1624,7 +1767,7 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* The return type must not be qualified */ if (GetQualifier (RetType) != T_QUAL_NONE && RetType[1].C == T_END) { - if (GetType (RetType) == T_TYPE_VOID) { + if (GetRawType (RetType) == T_TYPE_VOID) { /* A qualified void type is always an error */ Error ("function definition has qualified void return type"); } else { @@ -1916,7 +2059,7 @@ static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers) long ElementCount = GetElementCount (T); /* Special handling for a character array initialized by a literal */ - if (IsTypeChar (ElementType) && + if (IsRawTypeChar (ElementType) && (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST || (CurTok.Tok == TOK_LCURLY && (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) { @@ -2180,7 +2323,7 @@ static unsigned ParseVoidInit (Type* T) Size = 0; do { ConstExpr (hie1, &Expr); - switch (UnqualifiedType (Expr.Type[0].C)) { + switch (GetUnderlyingTypeCode (&Expr.Type[0])) { case T_SCHAR: case T_UCHAR: @@ -2244,7 +2387,7 @@ static unsigned ParseVoidInit (Type* T) static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) /* Parse initialization of variables. Return the number of data bytes. */ { - switch (UnqualifiedType (T->C)) { + switch (GetUnderlyingTypeCode (T)) { case T_SCHAR: case T_UCHAR: diff --git a/src/cc65/function.c b/src/cc65/function.c index ff4c23656..d6e11fea3 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -452,7 +452,7 @@ void NewFunc (SymEntry* Func) /* Determine if this is a main function in a C99 environment that ** returns an int. */ - if (IsTypeInt (F_GetReturnType (CurrentFunc)) && + if (IsRawTypeInt (F_GetReturnType (CurrentFunc)) && IS_Get (&Standard) == STD_C99) { C99MainFunc = 1; } diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 4a3730283..8cab93bba 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -132,7 +132,7 @@ void SwitchStatement (void) /* Setup the control structure, save the old and activate the new one */ SwitchData.Nodes = NewCollection (); - SwitchData.ExprType = UnqualifiedType (SwitchExpr.Type[0].C); + SwitchData.ExprType = GetUnderlyingTypeCode (&SwitchExpr.Type[0]); SwitchData.Depth = SizeOf (SwitchExpr.Type); SwitchData.DefaultLabel = 0; OldSwitch = Switch; diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 13829e958..b030d8b8b 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -165,6 +165,12 @@ struct SymEntry { unsigned Size; /* Size of the union/struct */ } S; + /* Data for enums */ + struct { + struct SymTable* SymTab; /* Member symbol table */ + const Type* Type; /* Underlying type */ + } E; + /* Data for bit fields */ struct { unsigned Offs; /* Byte offset into struct */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 26a09cd87..87a33760b 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -551,6 +551,50 @@ static void AddSymEntry (SymTable* T, SymEntry* S) +SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab) +/* Add an enum entry and return it */ +{ + /* Do we have an entry with this name already? */ + SymEntry* Entry = FindSymInTable (TagTab, Name, HashStr (Name)); + if (Entry) { + + /* We do have an entry. This may be a forward, so check it. */ + if ((Entry->Flags & SC_TYPEMASK) != SC_ENUM) { + /* Existing symbol is not an enum */ + Error ("Symbol '%s' is already different kind", Name); + } else { + /* Define the struct size if the underlying type is given. */ + if (Type != 0) { + if (Type !=0 && Entry->V.E.Type != 0) { + /* Both are definitions. */ + Error ("Multiple definition for enum '%s'", Name); + } + Entry->Type = 0; + Entry->V.E.SymTab = Tab; + Entry->V.E.Type = Type; + } + } + + } else { + + /* Create a new entry */ + Entry = NewSymEntry (Name, SC_ENUM); + + /* Set the enum type data */ + Entry->Type = 0; + Entry->V.E.SymTab = Tab; + Entry->V.E.Type = Type; + + /* Add it to the current table */ + AddSymEntry (TagTab, Entry); + } + + /* Return the entry */ + return Entry; +} + + + SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab) /* Add a struct/union entry and return it */ { @@ -649,10 +693,10 @@ SymEntry* AddConstSym (const char* Name, const Type* T, unsigned Flags, long Val /* Create a new entry */ Entry = NewSymEntry (Name, Flags); - /* Enum values are ints */ + /* We only have integer constants for now */ Entry->Type = TypeDup (T); - /* Set the enum data */ + /* Set the constant data */ Entry->V.ConstVal = Val; /* Add the entry to the symbol table */ diff --git a/src/cc65/symtab.h b/src/cc65/symtab.h index 94e5f2d9b..dd4b3aa37 100644 --- a/src/cc65/symtab.h +++ b/src/cc65/symtab.h @@ -146,6 +146,9 @@ unsigned short FindSPAdjustment (const char* Name); +SymEntry* AddEnumSym (const char* Name, const Type* Type, SymTable* Tab); +/* Add an enum entry and return it */ + SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab); /* Add a struct/union entry and return it */ diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 673dfa163..af04d8232 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -214,9 +214,9 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) return; } - /* Get the raw left and right types, signs and qualifiers */ - LeftType = GetType (lhs); - RightType = GetType (rhs); + /* Get the left and right types, signs and qualifiers */ + LeftType = GetUnderlyingTypeCode (lhs); + RightType = GetUnderlyingTypeCode (rhs); LeftSign = GetSignedness (lhs); RightSign = GetSignedness (rhs); LeftQual = GetQualifier (lhs); @@ -229,7 +229,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) RightType = T_TYPE_PTR; } - /* If the raw types are not identical, the types are incompatible */ + /* If the underlying types are not identical, the types are incompatible */ if (LeftType != RightType) { SetResult (Result, TC_INCOMPATIBLE); return;