Fixed support for storage class specifiers after type specifiers.

This commit is contained in:
acqn
2022-11-03 21:46:42 +08:00
parent 991af3755b
commit 8a7f566387
7 changed files with 178 additions and 123 deletions

View File

@@ -123,7 +123,7 @@ static void Parse (void)
} }
/* Read variable defs and functions */ /* Read variable defs and functions */
ParseDeclSpec (&Spec, SC_EXTERN | SC_STATIC, T_INT); ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_EXTERN | SC_STATIC);
/* Don't accept illegal storage classes */ /* Don't accept illegal storage classes */
if ((Spec.StorageClass & SC_TYPEMASK) == 0) { if ((Spec.StorageClass & SC_TYPEMASK) == 0) {

View File

@@ -72,8 +72,7 @@
static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified);
int* SignednessSpecified);
/* Parse a type specifier */ /* Parse a type specifier */
@@ -84,6 +83,75 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
static unsigned ParseOneStorageClass (void)
/* Parse and return a storage class specifier */
{
unsigned StorageClass = 0;
/* Check the storage class given */
switch (CurTok.Tok) {
case TOK_EXTERN:
StorageClass = SC_EXTERN | SC_STATIC;
NextToken ();
break;
case TOK_STATIC:
StorageClass = SC_STATIC;
NextToken ();
break;
case TOK_REGISTER:
StorageClass = SC_REGISTER | SC_STATIC;
NextToken ();
break;
case TOK_AUTO:
StorageClass = SC_AUTO;
NextToken ();
break;
case TOK_TYPEDEF:
StorageClass = SC_TYPEDEF;
NextToken ();
break;
default:
break;
}
return StorageClass;
}
static int ParseStorageClass (DeclSpec* D)
/* Parse storage class specifiers. Return true if a specifier is read even if
** it was duplicated or disallowed. */
{
/* Check the storage class given */
unsigned StorageClass = ParseOneStorageClass ();
if (StorageClass == 0) {
return 0;
}
while (StorageClass != 0) {
if (D->StorageClass == 0) {
D->StorageClass = StorageClass;
} else if (D->StorageClass == StorageClass) {
Warning ("Duplicate storage class specifier");
} else {
Error ("Conflicting storage class specifier");
}
StorageClass = ParseOneStorageClass ();
}
return 1;
}
static void DuplicateQualifier (const char* Name) static void DuplicateQualifier (const char* Name)
/* Print an error message */ /* Print an error message */
{ {
@@ -92,9 +160,9 @@ static void DuplicateQualifier (const char* Name)
static TypeCode OptionalQualifiers (TypeCode Allowed) static TypeCode OptionalQualifiers (TypeCode Qualifiers, TypeCode Allowed)
/* Read type qualifiers if we have any. Allowed specifies the allowed /* Read type qualifiers if we have any. Allowed specifies the allowed
** qualifiers. ** qualifiers. Return any read qualifiers even if they caused errors.
*/ */
{ {
/* We start without any qualifiers */ /* We start without any qualifiers */
@@ -107,7 +175,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_CONST: case TOK_CONST:
if (Allowed & T_QUAL_CONST) { if (Allowed & T_QUAL_CONST) {
if (Q & T_QUAL_CONST) { if (Qualifiers & T_QUAL_CONST) {
DuplicateQualifier ("const"); DuplicateQualifier ("const");
} }
Q |= T_QUAL_CONST; Q |= T_QUAL_CONST;
@@ -118,7 +186,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_VOLATILE: case TOK_VOLATILE:
if (Allowed & T_QUAL_VOLATILE) { if (Allowed & T_QUAL_VOLATILE) {
if (Q & T_QUAL_VOLATILE) { if (Qualifiers & T_QUAL_VOLATILE) {
DuplicateQualifier ("volatile"); DuplicateQualifier ("volatile");
} }
Q |= T_QUAL_VOLATILE; Q |= T_QUAL_VOLATILE;
@@ -129,7 +197,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_RESTRICT: case TOK_RESTRICT:
if (Allowed & T_QUAL_RESTRICT) { if (Allowed & T_QUAL_RESTRICT) {
if (Q & T_QUAL_RESTRICT) { if (Qualifiers & T_QUAL_RESTRICT) {
DuplicateQualifier ("restrict"); DuplicateQualifier ("restrict");
} }
Q |= T_QUAL_RESTRICT; Q |= T_QUAL_RESTRICT;
@@ -140,7 +208,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_NEAR: case TOK_NEAR:
if (Allowed & T_QUAL_NEAR) { if (Allowed & T_QUAL_NEAR) {
if (Q & T_QUAL_NEAR) { if (Qualifiers & T_QUAL_NEAR) {
DuplicateQualifier ("near"); DuplicateQualifier ("near");
} }
Q |= T_QUAL_NEAR; Q |= T_QUAL_NEAR;
@@ -151,7 +219,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_FAR: case TOK_FAR:
if (Allowed & T_QUAL_FAR) { if (Allowed & T_QUAL_FAR) {
if (Q & T_QUAL_FAR) { if (Qualifiers & T_QUAL_FAR) {
DuplicateQualifier ("far"); DuplicateQualifier ("far");
} }
Q |= T_QUAL_FAR; Q |= T_QUAL_FAR;
@@ -162,7 +230,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_FASTCALL: case TOK_FASTCALL:
if (Allowed & T_QUAL_FASTCALL) { if (Allowed & T_QUAL_FASTCALL) {
if (Q & T_QUAL_FASTCALL) { if (Qualifiers & T_QUAL_FASTCALL) {
DuplicateQualifier ("fastcall"); DuplicateQualifier ("fastcall");
} }
Q |= T_QUAL_FASTCALL; Q |= T_QUAL_FASTCALL;
@@ -173,7 +241,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
case TOK_CDECL: case TOK_CDECL:
if (Allowed & T_QUAL_CDECL) { if (Allowed & T_QUAL_CDECL) {
if (Q & T_QUAL_CDECL) { if (Qualifiers & T_QUAL_CDECL) {
DuplicateQualifier ("cdecl"); DuplicateQualifier ("cdecl");
} }
Q |= T_QUAL_CDECL; Q |= T_QUAL_CDECL;
@@ -187,13 +255,16 @@ static TypeCode OptionalQualifiers (TypeCode Allowed)
} }
/* Combine with newly read qualifiers */
Qualifiers |= Q;
/* Skip the token */ /* Skip the token */
NextToken (); NextToken ();
} }
Done: Done:
/* We cannot have more than one address size far qualifier */ /* We cannot have more than one address size far qualifier */
switch (Q & T_QUAL_ADDRSIZE) { switch (Qualifiers & T_QUAL_ADDRSIZE) {
case T_QUAL_NONE: case T_QUAL_NONE:
case T_QUAL_NEAR: case T_QUAL_NEAR:
@@ -202,11 +273,11 @@ Done:
default: default:
Error ("Cannot specify more than one address size qualifier"); Error ("Cannot specify more than one address size qualifier");
Q &= ~T_QUAL_ADDRSIZE; Qualifiers &= ~T_QUAL_ADDRSIZE;
} }
/* We cannot have more than one calling convention specifier */ /* We cannot have more than one calling convention specifier */
switch (Q & T_QUAL_CCONV) { switch (Qualifiers & T_QUAL_CCONV) {
case T_QUAL_NONE: case T_QUAL_NONE:
case T_QUAL_FASTCALL: case T_QUAL_FASTCALL:
@@ -215,15 +286,41 @@ Done:
default: default:
Error ("Cannot specify more than one calling convention qualifier"); Error ("Cannot specify more than one calling convention qualifier");
Q &= ~T_QUAL_CCONV; Qualifiers &= ~T_QUAL_CCONV;
} }
/* Return the qualifiers read */ /* Return any qualifiers just read */
return Q; return Q;
} }
static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t TSFlags)
/* Read storage specifiers and/or type qualifiers if we have any. Storage class
** specifiers require the corresponding typespec_t flag set to be allowed, and
** only const and volatile type qualifiers are allowed under any circumstance.
** Read storage class specifiers are output in *Spec and type qualifiers are
** output in *Qualifiers with error checking.
*/
{
TypeCode Q = T_QUAL_NONE;
int Continue;
do {
/* There may be type qualifiers *before* any storage class specifiers */
Q = OptionalQualifiers (*Qualifiers, T_QUAL_CONST | T_QUAL_VOLATILE);
*Qualifiers |= Q;
/* Parse storage class specifiers anyway then check */
Continue = ParseStorageClass (Spec);
if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) {
Error ("Unexpected storage class specified");
}
} while (Continue || Q != T_QUAL_NONE);
}
static void OptionalInt (void) static void OptionalInt (void)
/* Eat an optional "int" token */ /* Eat an optional "int" token */
{ {
@@ -396,48 +493,6 @@ static void FixQualifiers (Type* DataType)
static unsigned ParseOneStorageClass (void)
/* Parse and return a storage class */
{
unsigned StorageClass = 0;
/* Check the storage class given */
switch (CurTok.Tok) {
case TOK_EXTERN:
StorageClass = SC_EXTERN | SC_STATIC;
NextToken ();
break;
case TOK_STATIC:
StorageClass = SC_STATIC;
NextToken ();
break;
case TOK_REGISTER:
StorageClass = SC_REGISTER | SC_STATIC;
NextToken ();
break;
case TOK_AUTO:
StorageClass = SC_AUTO;
NextToken ();
break;
case TOK_TYPEDEF:
StorageClass = SC_TYPEDEF;
NextToken ();
break;
default:
break;
}
return StorageClass;
}
static void CheckArrayElementType (Type* DataType) static void CheckArrayElementType (Type* DataType)
/* Check if data type consists of arrays of incomplete element types */ /* Check if data type consists of arrays of incomplete element types */
{ {
@@ -469,33 +524,6 @@ static void CheckArrayElementType (Type* DataType)
static void ParseStorageClass (DeclSpec* D, unsigned DefStorage)
/* Parse a storage class */
{
/* Assume we're using an explicit storage class */
D->Flags &= ~DS_DEF_STORAGE;
/* Check the storage class given */
D->StorageClass = ParseOneStorageClass ();
if (D->StorageClass == 0) {
/* No storage class given, use default */
D->Flags |= DS_DEF_STORAGE;
D->StorageClass = DefStorage;
} else {
unsigned StorageClass = ParseOneStorageClass ();
while (StorageClass != 0) {
if (D->StorageClass == StorageClass) {
Warning ("Duplicate storage class specifier");
} else {
Error ("Conflicting storage class specifier");
}
StorageClass = ParseOneStorageClass ();
}
}
}
static SymEntry* ESUForwardDecl (const char* Name, unsigned Flags, unsigned* DSFlags) static SymEntry* ESUForwardDecl (const char* Name, unsigned Flags, unsigned* DSFlags)
/* Handle an enum, struct or union forward decl */ /* Handle an enum, struct or union forward decl */
{ {
@@ -896,7 +924,7 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags)
} }
InitDeclSpec (&Spec); InitDeclSpec (&Spec);
ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified); ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified);
/* Read fields with this type */ /* Read fields with this type */
while (1) { while (1) {
@@ -1046,7 +1074,7 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags)
} }
InitDeclSpec (&Spec); InitDeclSpec (&Spec);
ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified); ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified);
/* Read fields with this type */ /* Read fields with this type */
while (1) { while (1) {
@@ -1212,8 +1240,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) {
static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified)
int* SignednessSpecified)
/* Parse a type specifier. Store whether one of "signed" or "unsigned" was /* Parse a type specifier. Store whether one of "signed" or "unsigned" was
** specified, so bit-fields of unspecified signedness can be treated as ** specified, so bit-fields of unspecified signedness can be treated as
** unsigned; without special handling, it would be treated as signed. ** unsigned; without special handling, it would be treated as signed.
@@ -1221,6 +1248,7 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
{ {
ident Ident; ident Ident;
SymEntry* TagEntry; SymEntry* TagEntry;
TypeCode Qualifiers = T_QUAL_NONE;
if (SignednessSpecified != NULL) { if (SignednessSpecified != NULL) {
*SignednessSpecified = 0; *SignednessSpecified = 0;
@@ -1229,8 +1257,8 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
/* Assume we have an explicit type */ /* Assume we have an explicit type */
D->Flags &= ~DS_DEF_TYPE; D->Flags &= ~DS_DEF_TYPE;
/* Read type qualifiers if we have any */ /* Read storage specifiers and/or type qualifiers if we have any */
Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE); OptionalSpecifiers (D, &Qualifiers, TSFlags);
/* Look at the data type */ /* Look at the data type */
switch (CurTok.Tok) { switch (CurTok.Tok) {
@@ -1477,20 +1505,21 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
/* FALL THROUGH */ /* FALL THROUGH */
default: default:
if (Default < 0) { if ((TSFlags & TS_MASK_DEFAULT_TYPE) != TS_DEFAULT_TYPE_INT) {
Error ("Type expected"); Error ("Type expected");
D->Type[0].C = T_INT; D->Type[0].C = T_INT;
D->Type[1].C = T_END; D->Type[1].C = T_END;
} else { } else {
D->Flags |= DS_DEF_TYPE; D->Flags |= DS_DEF_TYPE;
D->Type[0].C = (TypeCode) Default; D->Type[0].C = T_INT;
D->Type[1].C = T_END; D->Type[1].C = T_END;
} }
break; break;
} }
/* There may also be qualifiers *after* the initial type */ /* There may also be specifiers/qualifiers *after* the initial type */
D->Type[0].C |= (Qualifiers | OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE)); OptionalSpecifiers (D, &Qualifiers, TSFlags);
D->Type[0].C |= Qualifiers;
} }
@@ -1570,7 +1599,7 @@ static void ParseOldStyleParamList (FuncDesc* F)
DeclSpec Spec; DeclSpec Spec;
/* Read the declaration specifier */ /* Read the declaration specifier */
ParseDeclSpec (&Spec, SC_AUTO, T_INT); ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
/* We accept only auto and register as storage class specifiers, but /* We accept only auto and register as storage class specifiers, but
** we ignore all this, since we use auto anyway. ** we ignore all this, since we use auto anyway.
@@ -1649,7 +1678,7 @@ static void ParseAnsiParamList (FuncDesc* F)
} }
/* Read the declaration specifier */ /* Read the declaration specifier */
ParseDeclSpec (&Spec, SC_AUTO, T_INT); ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
/* We accept only auto and register as storage class specifiers */ /* We accept only auto and register as storage class specifiers */
if ((Spec.StorageClass & SC_AUTO) == SC_AUTO) { if ((Spec.StorageClass & SC_AUTO) == SC_AUTO) {
@@ -1797,7 +1826,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
** qualifier later will be transfered to the function itself. If it's a ** qualifier later will be transfered to the function itself. If it's a
** pointer to something else, it will be flagged as an error. ** pointer to something else, it will be flagged as an error.
*/ */
TypeCode Qualifiers = OptionalQualifiers (T_QUAL_ADDRSIZE | T_QUAL_CCONV); TypeCode Qualifiers = OptionalQualifiers (T_QUAL_NONE, T_QUAL_ADDRSIZE | T_QUAL_CCONV);
/* Pointer to something */ /* Pointer to something */
if (CurTok.Tok == TOK_STAR) { if (CurTok.Tok == TOK_STAR) {
@@ -1806,7 +1835,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
NextToken (); NextToken ();
/* Allow const, restrict, and volatile qualifiers */ /* Allow const, restrict, and volatile qualifiers */
Qualifiers |= OptionalQualifiers (T_QUAL_CVR); Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR);
/* Parse the type that the pointer points to */ /* Parse the type that the pointer points to */
Declarator (Spec, D, Mode); Declarator (Spec, D, Mode);
@@ -1951,7 +1980,7 @@ Type* ParseType (Type* T)
/* Get a type without a default */ /* Get a type without a default */
InitDeclSpec (&Spec); InitDeclSpec (&Spec);
ParseTypeSpec (&Spec, -1, T_QUAL_NONE, NULL); ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, NULL);
/* Parse additional declarators */ /* Parse additional declarators */
ParseDecl (&Spec, &Decl, DM_NO_IDENT); ParseDecl (&Spec, &Decl, DM_NO_IDENT);
@@ -2076,22 +2105,23 @@ void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode)
void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, long DefType) void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage)
/* Parse a declaration specification */ /* Parse a declaration specification */
{ {
TypeCode Qualifiers;
/* Initialize the DeclSpec struct */ /* Initialize the DeclSpec struct */
InitDeclSpec (D); InitDeclSpec (D);
/* There may be qualifiers *before* the storage class specifier */ /* Assume we're using an explicit storage class */
Qualifiers = OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE); D->Flags &= ~DS_DEF_STORAGE;
/* Now get the storage class specifier for this declaration */ /* Parse the type specifiers */
ParseStorageClass (D, DefStorage); ParseTypeSpec (D, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL);
/* Parse the type specifiers passing any initial type qualifiers */ /* If no explicit storage class is given, use the default */
ParseTypeSpec (D, DefType, Qualifiers, NULL); if (D->StorageClass == 0) {
D->Flags |= DS_DEF_STORAGE;
D->StorageClass = DefStorage;
}
} }

View File

@@ -53,6 +53,22 @@
/* Type specifier parser flags */
typedef enum typespec_t typespec_t;
enum typespec_t {
TS_NONE = 0x00,
/* Default type */
TS_MASK_DEFAULT_TYPE = 0x03,
TS_DEFAULT_TYPE_NONE = 0x00, /* No default type */
TS_DEFAULT_TYPE_INT = 0x01, /* Good old int */
TS_DEFAULT_TYPE_AUTO = 0x02, /* C23 type inference with auto */
/* Whether to allow certain kinds of specifiers */
TS_STORAGE_CLASS_SPEC = 0x04, /* Allow storage storage class specifiers */
TS_FUNCTION_SPEC = 0x08, /* Allow function specifiers */
};
/* Masks for the Flags field in DeclSpec */ /* Masks for the Flags field in DeclSpec */
#define DS_DEF_STORAGE 0x0001U /* Default storage class used */ #define DS_DEF_STORAGE 0x0001U /* Default storage class used */
#define DS_DEF_TYPE 0x0002U /* Default type used */ #define DS_DEF_TYPE 0x0002U /* Default type used */
@@ -105,7 +121,7 @@ Type* ParseType (Type* Type);
void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode); void ParseDecl (const DeclSpec* Spec, Declaration* D, declmode_t Mode);
/* Parse a variable, type or function declaration */ /* Parse a variable, type or function declaration */
void ParseDeclSpec (DeclSpec* D, unsigned DefStorage, long DefType); void ParseDeclSpec (DeclSpec* D, typespec_t TSFlags, unsigned DefStorage);
/* Parse a declaration specification */ /* Parse a declaration specification */
void CheckEmptyDecl (const DeclSpec* D); void CheckEmptyDecl (const DeclSpec* D);

View File

@@ -1301,7 +1301,7 @@ static void Primary (ExprDesc* E)
/* Let's see if this is a C99-style declaration */ /* Let's see if this is a C99-style declaration */
DeclSpec Spec; DeclSpec Spec;
InitDeclSpec (&Spec); InitDeclSpec (&Spec);
ParseDeclSpec (&Spec, -1, T_QUAL_NONE); ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO);
if (Spec.Type->C != T_END) { if (Spec.Type->C != T_END) {

View File

@@ -568,7 +568,7 @@ void DeclareLocals (void)
continue; continue;
} }
ParseDeclSpec (&Spec, SC_AUTO, T_INT); ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO);
if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */
(Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */ (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */
GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */ GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */

View File

@@ -1,10 +0,0 @@
/* bug #1888 - The compiler doesn't accept valid data declarations */
/* The following is a valid declaration but not accepted by the compiler */
int static a;
int main(void)
{
return 0;
}

View File

@@ -0,0 +1,19 @@
/* bug 1888 - cc65 fails with storage class specifiers after type specifiers */
#include <stdio.h>
int const typedef volatile x_type, * const volatile y_type;
int static failures = 0;
int extern main(void);
int main(void)
{
volatile static x_type const x = 42, * const volatile y[] = { 1 ? &x : (y_type)0 };
if (**y != 42) {
++failures;
printf("y = %d, Expected: 42\n", **y);
}
return failures;
}