Fixed underlying types of enums.

Made enumerator diagnostics more sensible.
Fixed Issue #1048 as a natural result.
This commit is contained in:
acqn
2020-07-26 20:12:55 +08:00
committed by Oliver Schmidt
parent c272c73686
commit daa65199b3
9 changed files with 526 additions and 124 deletions

View File

@@ -83,14 +83,9 @@ const char* GetBasicTypeName (const Type* T)
** Return "type" for unknown basic types. ** Return "type" for unknown basic types.
*/ */
{ {
switch (GetType (T)) { switch (GetRawType (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";
case T_TYPE_ENUM: return "enum"; 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_DOUBLE: return "double";
case T_TYPE_VOID: return "void"; case T_TYPE_VOID: return "void";
case T_TYPE_STRUCT: return "struct"; 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_PTR: return "pointer";
case T_TYPE_FUNC: return "function"; case T_TYPE_FUNC: return "function";
case T_TYPE_NONE: /* FALLTHROUGH */ 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) Type* PointerTo (const Type* T)
/* Return a type string that is "pointer to T". The type string is allocated /* Return a type string that is "pointer to T". The type string is allocated
** on the heap and may be freed after use. ** on the heap and may be freed after use.
@@ -321,6 +393,9 @@ void PrintType (FILE* F, const Type* T)
case T_TYPE_LONGLONG: case T_TYPE_LONGLONG:
fprintf (F, "long long"); fprintf (F, "long long");
break; break;
case T_TYPE_ENUM:
fprintf (F, "enum");
break;
case T_TYPE_FLOAT: case T_TYPE_FLOAT:
fprintf (F, "float"); fprintf (F, "float");
break; 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) unsigned SizeOf (const Type* T)
/* Compute size of object represented by type array. */ /* Compute size of object represented by type array. */
{ {
switch (UnqualifiedType (T->C)) { switch (GetUnderlyingTypeCode (T)) {
case T_VOID: case T_VOID:
/* A void variable is a cc65 extension. /* A void variable is a cc65 extension.
@@ -470,9 +603,6 @@ unsigned SizeOf (const Type* T)
case T_ULONGLONG: case T_ULONGLONG:
return SIZEOF_LONGLONG; return SIZEOF_LONGLONG;
case T_ENUM:
return SIZEOF_INT;
case T_FLOAT: case T_FLOAT:
return SIZEOF_FLOAT; return SIZEOF_FLOAT;
@@ -491,7 +621,12 @@ unsigned SizeOf (const Type* T)
return T->A.U * SizeOf (T + 1); return T->A.U * SizeOf (T + 1);
} }
case T_ENUM:
/* Incomplete enum type */
return 0;
default: default:
Internal ("Unknown type in SizeOf: %04lX", T->C); Internal ("Unknown type in SizeOf: %04lX", T->C);
return 0; return 0;
@@ -547,7 +682,9 @@ unsigned CheckedPSizeOf (const Type* T)
unsigned TypeOf (const Type* T) unsigned TypeOf (const Type* T)
/* Get the code generator base type of the object */ /* Get the code generator base type of the object */
{ {
switch (UnqualifiedType (T->C)) { unsigned NewType;
switch (GetUnderlyingTypeCode (T)) {
case T_SCHAR: case T_SCHAR:
return CF_CHAR; return CF_CHAR;
@@ -557,7 +694,6 @@ unsigned TypeOf (const Type* T)
case T_SHORT: case T_SHORT:
case T_INT: case T_INT:
case T_ENUM:
return CF_INT; return CF_INT;
case T_USHORT: case T_USHORT:
@@ -582,6 +718,10 @@ unsigned TypeOf (const Type* T)
case T_STRUCT: case T_STRUCT:
case T_UNION: case T_UNION:
NewType = TypeOfBySize (T);
if (NewType != CF_NONE) {
return NewType;
}
/* Address of ... */ /* Address of ... */
return CF_INT | CF_UNSIGNED; return CF_INT | CF_UNSIGNED;
@@ -726,8 +866,8 @@ Type* GetBaseElementType (Type* T)
SymEntry* GetSymEntry (const Type* T) SymEntry* GetSymEntry (const Type* T)
/* Return a SymEntry pointer from a type */ /* Return a SymEntry pointer from a type */
{ {
/* Only structs or unions have a SymEntry attribute */ /* Only enums, structs or unions have a SymEntry attribute */
CHECK (IsClassStruct (T)); CHECK (IsClassStruct (T) || IsTypeEnum (T));
/* Return the attribute */ /* Return the attribute */
return T->A.P; return T->A.P;
@@ -738,8 +878,8 @@ SymEntry* GetSymEntry (const Type* T)
void SetSymEntry (Type* T, SymEntry* S) void SetSymEntry (Type* T, SymEntry* S)
/* Set the SymEntry pointer for a type */ /* Set the SymEntry pointer for a type */
{ {
/* Only structs or unions have a SymEntry attribute */ /* Only enums, structs or unions have a SymEntry attribute */
CHECK (IsClassStruct (T)); CHECK (IsClassStruct (T) || IsTypeEnum (T));
/* Set the attribute */ /* Set the attribute */
T->A.P = S; T->A.P = S;

View File

@@ -96,37 +96,39 @@ enum {
/* Type size modifiers */ /* Type size modifiers */
T_SIZE_NONE = 0x000000, T_SIZE_NONE = 0x000000,
T_SIZE_SHORT = 0x000200, T_SIZE_CHAR = 0x000200,
T_SIZE_LONG = 0x000400, T_SIZE_SHORT = 0x000400,
T_SIZE_LONGLONG = 0x000600, T_SIZE_INT = 0x000600,
T_MASK_SIZE = 0x000600, T_SIZE_LONG = 0x000800,
T_SIZE_LONGLONG = 0x000A00,
T_MASK_SIZE = 0x000E00,
/* Type qualifiers */ /* Type qualifiers */
T_QUAL_NONE = 0x000000, T_QUAL_NONE = 0x000000,
T_QUAL_CONST = 0x000800, T_QUAL_CONST = 0x001000,
T_QUAL_VOLATILE = 0x001000, T_QUAL_VOLATILE = 0x002000,
T_QUAL_RESTRICT = 0x002000, T_QUAL_RESTRICT = 0x004000,
T_QUAL_NEAR = 0x004000, T_QUAL_NEAR = 0x008000,
T_QUAL_FAR = 0x008000, T_QUAL_FAR = 0x010000,
T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR, T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR,
T_QUAL_FASTCALL = 0x010000, T_QUAL_FASTCALL = 0x020000,
T_QUAL_CDECL = 0x020000, T_QUAL_CDECL = 0x040000,
T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL, T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL,
T_MASK_QUAL = 0x03F800, T_MASK_QUAL = 0x07F000,
/* Types */ /* Types */
T_CHAR = 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_NONE, 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_NONE, 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_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_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_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_NONE, 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_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_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_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_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_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_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, 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); const Type* GetStructReplacementType (const Type* SType);
/* Get a replacement type for passing a struct/union in the primary register */ /* 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); Type* PointerTo (const Type* T);
/* Return a type string that is "pointer to T". The type string is allocated /* Return a type string that is "pointer to T". The type string is allocated
** on the heap and may be freed after use. ** 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) # define UnqualifiedType(T) ((T) & ~T_MASK_QUAL)
#endif #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); unsigned SizeOf (const Type* T);
/* Compute size of object represented by type array. */ /* 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 */ /* Convert an array to a pointer to it's first element */
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE TypeCode GetType (const Type* T) INLINE TypeCode GetRawType (const Type* T)
/* Get the raw type */ /* Get the raw type */
{ {
return (T->C & T_MASK_TYPE); return (T->C & T_MASK_TYPE);
} }
#else #else
# define GetType(T) ((T)->C & T_MASK_TYPE) # define GetRawType(T) ((T)->C & T_MASK_TYPE)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeChar (const Type* T) INLINE int IsTypeChar (const Type* T)
/* Return true if this is a character type */ /* Return true if this is a character type */
{ {
return (GetType (T) == T_TYPE_CHAR); return (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR);
} }
#else #else
# define IsTypeChar(T) (GetType (T) == T_TYPE_CHAR) # define IsTypeChar(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_CHAR)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeInt (const Type* T) INLINE int IsTypeInt (const Type* T)
/* Return true if this is an int type (signed or unsigned) */ /* 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 #else
# define IsTypeInt(T) (GetType (T) == T_TYPE_INT) # define IsTypeInt(T) (GetRawType (GetUnderlyingType (T)) == T_TYPE_INT)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeLong (const Type* T) INLINE int IsTypeLong (const Type* T)
/* Return true if this is a long type (signed or unsigned) */ /* 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 #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 #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeFloat (const Type* T) INLINE int IsTypeFloat (const Type* T)
/* Return true if this is a float type */ /* Return true if this is a float type */
{ {
return (GetType (T) == T_TYPE_FLOAT); return (GetRawType (T) == T_TYPE_FLOAT);
} }
#else #else
# define IsTypeFloat(T) (GetType (T) == T_TYPE_FLOAT) # define IsTypeFloat(T) (GetRawType (T) == T_TYPE_FLOAT)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeDouble (const Type* T) INLINE int IsTypeDouble (const Type* T)
/* Return true if this is a double type */ /* Return true if this is a double type */
{ {
return (GetType (T) == T_TYPE_DOUBLE); return (GetRawType (T) == T_TYPE_DOUBLE);
} }
#else #else
# define IsTypeDouble(T) (GetType (T) == T_TYPE_DOUBLE) # define IsTypeDouble(T) (GetRawType (T) == T_TYPE_DOUBLE)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypePtr (const Type* T) INLINE int IsTypePtr (const Type* T)
/* Return true if this is a pointer type */ /* Return true if this is a pointer type */
{ {
return (GetType (T) == T_TYPE_PTR); return (GetRawType (T) == T_TYPE_PTR);
} }
#else #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 #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeStruct (const Type* T) INLINE int IsTypeStruct (const Type* T)
/* Return true if this is a struct type */ /* Return true if this is a struct type */
{ {
return (GetType (T) == T_TYPE_STRUCT); return (GetRawType (T) == T_TYPE_STRUCT);
} }
#else #else
# define IsTypeStruct(T) (GetType (T) == T_TYPE_STRUCT) # define IsTypeStruct(T) (GetRawType (T) == T_TYPE_STRUCT)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeUnion (const Type* T) INLINE int IsTypeUnion (const Type* T)
/* Return true if this is a union type */ /* Return true if this is a union type */
{ {
return (GetType (T) == T_TYPE_UNION); return (GetRawType (T) == T_TYPE_UNION);
} }
#else #else
# define IsTypeUnion(T) (GetType (T) == T_TYPE_UNION) # define IsTypeUnion(T) (GetRawType (T) == T_TYPE_UNION)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeArray (const Type* T) INLINE int IsTypeArray (const Type* T)
/* Return true if this is an array type */ /* Return true if this is an array type */
{ {
return (GetType (T) == T_TYPE_ARRAY); return (GetRawType (T) == T_TYPE_ARRAY);
} }
#else #else
# define IsTypeArray(T) (GetType (T) == T_TYPE_ARRAY) # define IsTypeArray(T) (GetRawType (T) == T_TYPE_ARRAY)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeVoid (const Type* T) INLINE int IsTypeVoid (const Type* T)
/* Return true if this is a void type */ /* Return true if this is a void type */
{ {
return (GetType (T) == T_TYPE_VOID); return (GetRawType (T) == T_TYPE_VOID);
} }
#else #else
# define IsTypeVoid(T) (GetType (T) == T_TYPE_VOID) # define IsTypeVoid(T) (GetRawType (T) == T_TYPE_VOID)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE int IsTypeFunc (const Type* T) INLINE int IsTypeFunc (const Type* T)
/* Return true if this is a function class */ /* Return true if this is a function class */
{ {
return (GetType (T) == T_TYPE_FUNC); return (GetRawType (T) == T_TYPE_FUNC);
} }
#else #else
# define IsTypeFunc(T) (GetType (T) == T_TYPE_FUNC) # define IsTypeFunc(T) (GetRawType (T) == T_TYPE_FUNC)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
@@ -502,13 +558,23 @@ INLINE int IsClassFunc (const Type* T)
#endif #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)
INLINE TypeCode GetSignedness (const Type* T) INLINE TypeCode GetRawSignedness (const Type* T)
/* Get the sign of a type */ /* Get the raw signedness of a type */
{ {
return (T->C & T_MASK_SIGN); return ((T)->C & T_MASK_SIGN);
} }
#else #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 #endif
#if defined(HAVE_INLINE) #if defined(HAVE_INLINE)

View File

@@ -453,27 +453,99 @@ static void ParseStorageClass (DeclSpec* D, unsigned DefStorage)
static void ParseEnumDecl (void) static SymEntry* ESUForwardDecl (const char* Name, unsigned Type)
/* Process an enum declaration . */ /* Handle an enum, struct or union forward decl */
{ {
int EnumVal; /* Try to find an enum/struct/union with the given name. If there is none,
ident Ident; ** 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 */ /* Accept forward definitions */
if (CurTok.Tok != TOK_LCURLY) { if (CurTok.Tok != TOK_LCURLY) {
return; return ESUForwardDecl (Name, SC_ENUM);
} }
/* Add the enum tag */
AddEnumSym (Name, 0, 0);
/* Skip the opening curly brace */ /* Skip the opening curly brace */
NextToken (); NextToken ();
/* Read the enum tags */ /* Read the enum tags */
EnumVal = 0; EnumVal = -1L;
while (CurTok.Tok != TOK_RCURLY) { while (CurTok.Tok != TOK_RCURLY) {
/* We expect an identifier */ /* We expect an identifier */
if (CurTok.Tok != TOK_IDENT) { if (CurTok.Tok != TOK_IDENT) {
Error ("Identifier expected"); Error ("Identifier expected for enumerator declarator");
/* Avoid excessive errors */
NextToken ();
continue; continue;
} }
@@ -483,21 +555,116 @@ static void ParseEnumDecl (void)
/* Check for an assigned value */ /* Check for an assigned value */
if (CurTok.Tok == TOK_ASSIGN) { if (CurTok.Tok == TOK_ASSIGN) {
ExprDesc Expr; ExprDesc Expr;
NextToken (); NextToken ();
ConstAbsIntExpr (hie1, &Expr); 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 */ /* Track down the min/max values and evaluate the type of EnumVal
AddConstSym (Ident, type_int, SC_ENUMERATOR | SC_CONST, 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 */ /* Check for end of definition */
if (CurTok.Tok != TOK_COMMA) if (CurTok.Tok != TOK_COMMA) {
break; break;
}
NextToken (); NextToken ();
} }
ConsumeRCurly (); 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) static unsigned CopyAnonStructFields (const Declaration* Decl, int Offs)
/* Copy fields from an anon union/struct into the current lexical level. The /* Copy fields from an anon union/struct into the current lexical level. The
** function returns the size of the embedded struct/union. ** function returns the size of the embedded struct/union.
@@ -617,7 +766,7 @@ static SymEntry* ParseUnionDecl (const char* Name)
if (CurTok.Tok != TOK_LCURLY) { if (CurTok.Tok != TOK_LCURLY) {
/* Just a forward declaration. */ /* 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 */ /* 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) { if (CurTok.Tok != TOK_LCURLY) {
/* Just a forward declaration. */ /* 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 */ /* 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: case TOK_ENUM:
NextToken (); NextToken ();
if (CurTok.Tok != TOK_LCURLY) { /* Named enum */
/* Named enum */ if (CurTok.Tok == TOK_IDENT) {
if (CurTok.Tok == TOK_IDENT) { strcpy (Ident, CurTok.Ident);
/* Find an entry with this name */ NextToken ();
Entry = FindTagSym (CurTok.Ident); } else {
if (Entry) { if (CurTok.Tok != TOK_LCURLY) {
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 {
Error ("Identifier expected"); Error ("Identifier expected");
} else {
AnonName (Ident, "enum");
} }
} }
/* Remember we have an extra type decl */ /* Remember we have an extra type decl */
D->Flags |= DS_EXTRA_TYPE; D->Flags |= DS_EXTRA_TYPE;
/* Parse the enum decl */ /* Parse the enum decl */
ParseEnumDecl (); Entry = ParseEnumDecl (Ident);
D->Type[0].C = T_INT; D->Type[0].C |= T_ENUM;
SetSymEntry (D->Type, Entry);
D->Type[1].C = T_END; D->Type[1].C = T_END;
break; break;
@@ -1624,7 +1767,7 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
/* The return type must not be qualified */ /* The return type must not be qualified */
if (GetQualifier (RetType) != T_QUAL_NONE && RetType[1].C == T_END) { 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 */ /* A qualified void type is always an error */
Error ("function definition has qualified void return type"); Error ("function definition has qualified void return type");
} else { } else {
@@ -1916,7 +2059,7 @@ static unsigned ParseArrayInit (Type* T, int AllowFlexibleMembers)
long ElementCount = GetElementCount (T); long ElementCount = GetElementCount (T);
/* Special handling for a character array initialized by a literal */ /* 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_SCONST || CurTok.Tok == TOK_WCSCONST ||
(CurTok.Tok == TOK_LCURLY && (CurTok.Tok == TOK_LCURLY &&
(NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) { (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) {
@@ -2180,7 +2323,7 @@ static unsigned ParseVoidInit (Type* T)
Size = 0; Size = 0;
do { do {
ConstExpr (hie1, &Expr); ConstExpr (hie1, &Expr);
switch (UnqualifiedType (Expr.Type[0].C)) { switch (GetUnderlyingTypeCode (&Expr.Type[0])) {
case T_SCHAR: case T_SCHAR:
case T_UCHAR: case T_UCHAR:
@@ -2244,7 +2387,7 @@ static unsigned ParseVoidInit (Type* T)
static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers) static unsigned ParseInitInternal (Type* T, int AllowFlexibleMembers)
/* Parse initialization of variables. Return the number of data bytes. */ /* Parse initialization of variables. Return the number of data bytes. */
{ {
switch (UnqualifiedType (T->C)) { switch (GetUnderlyingTypeCode (T)) {
case T_SCHAR: case T_SCHAR:
case T_UCHAR: case T_UCHAR:

View File

@@ -452,7 +452,7 @@ void NewFunc (SymEntry* Func)
/* Determine if this is a main function in a C99 environment that /* Determine if this is a main function in a C99 environment that
** returns an int. ** returns an int.
*/ */
if (IsTypeInt (F_GetReturnType (CurrentFunc)) && if (IsRawTypeInt (F_GetReturnType (CurrentFunc)) &&
IS_Get (&Standard) == STD_C99) { IS_Get (&Standard) == STD_C99) {
C99MainFunc = 1; C99MainFunc = 1;
} }

View File

@@ -132,7 +132,7 @@ void SwitchStatement (void)
/* Setup the control structure, save the old and activate the new one */ /* Setup the control structure, save the old and activate the new one */
SwitchData.Nodes = NewCollection (); SwitchData.Nodes = NewCollection ();
SwitchData.ExprType = UnqualifiedType (SwitchExpr.Type[0].C); SwitchData.ExprType = GetUnderlyingTypeCode (&SwitchExpr.Type[0]);
SwitchData.Depth = SizeOf (SwitchExpr.Type); SwitchData.Depth = SizeOf (SwitchExpr.Type);
SwitchData.DefaultLabel = 0; SwitchData.DefaultLabel = 0;
OldSwitch = Switch; OldSwitch = Switch;

View File

@@ -165,6 +165,12 @@ struct SymEntry {
unsigned Size; /* Size of the union/struct */ unsigned Size; /* Size of the union/struct */
} S; } S;
/* Data for enums */
struct {
struct SymTable* SymTab; /* Member symbol table */
const Type* Type; /* Underlying type */
} E;
/* Data for bit fields */ /* Data for bit fields */
struct { struct {
unsigned Offs; /* Byte offset into struct */ unsigned Offs; /* Byte offset into struct */

View File

@@ -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) SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab)
/* Add a struct/union entry and return it */ /* 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 */ /* Create a new entry */
Entry = NewSymEntry (Name, Flags); Entry = NewSymEntry (Name, Flags);
/* Enum values are ints */ /* We only have integer constants for now */
Entry->Type = TypeDup (T); Entry->Type = TypeDup (T);
/* Set the enum data */ /* Set the constant data */
Entry->V.ConstVal = Val; Entry->V.ConstVal = Val;
/* Add the entry to the symbol table */ /* Add the entry to the symbol table */

View File

@@ -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); SymEntry* AddStructSym (const char* Name, unsigned Type, unsigned Size, SymTable* Tab);
/* Add a struct/union entry and return it */ /* Add a struct/union entry and return it */

View File

@@ -214,9 +214,9 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
return; return;
} }
/* Get the raw left and right types, signs and qualifiers */ /* Get the left and right types, signs and qualifiers */
LeftType = GetType (lhs); LeftType = GetUnderlyingTypeCode (lhs);
RightType = GetType (rhs); RightType = GetUnderlyingTypeCode (rhs);
LeftSign = GetSignedness (lhs); LeftSign = GetSignedness (lhs);
RightSign = GetSignedness (rhs); RightSign = GetSignedness (rhs);
LeftQual = GetQualifier (lhs); LeftQual = GetQualifier (lhs);
@@ -229,7 +229,7 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result)
RightType = T_TYPE_PTR; 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) { if (LeftType != RightType) {
SetResult (Result, TC_INCOMPATIBLE); SetResult (Result, TC_INCOMPATIBLE);
return; return;