diff --git a/src/cc65/compile.c b/src/cc65/compile.c index a8ef56af6..f15c5bc54 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -123,7 +123,7 @@ static void Parse (void) } /* 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 */ if ((Spec.StorageClass & SC_TYPEMASK) == 0) { diff --git a/src/cc65/declare.c b/src/cc65/declare.c index b141a15ef..f3674d711 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -72,8 +72,7 @@ -static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, - int* SignednessSpecified); +static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified); /* 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) /* 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 -** qualifiers. +** qualifiers. Return any read qualifiers even if they caused errors. */ { /* We start without any qualifiers */ @@ -107,7 +175,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_CONST: if (Allowed & T_QUAL_CONST) { - if (Q & T_QUAL_CONST) { + if (Qualifiers & T_QUAL_CONST) { DuplicateQualifier ("const"); } Q |= T_QUAL_CONST; @@ -118,7 +186,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_VOLATILE: if (Allowed & T_QUAL_VOLATILE) { - if (Q & T_QUAL_VOLATILE) { + if (Qualifiers & T_QUAL_VOLATILE) { DuplicateQualifier ("volatile"); } Q |= T_QUAL_VOLATILE; @@ -129,7 +197,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_RESTRICT: if (Allowed & T_QUAL_RESTRICT) { - if (Q & T_QUAL_RESTRICT) { + if (Qualifiers & T_QUAL_RESTRICT) { DuplicateQualifier ("restrict"); } Q |= T_QUAL_RESTRICT; @@ -140,7 +208,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_NEAR: if (Allowed & T_QUAL_NEAR) { - if (Q & T_QUAL_NEAR) { + if (Qualifiers & T_QUAL_NEAR) { DuplicateQualifier ("near"); } Q |= T_QUAL_NEAR; @@ -151,7 +219,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_FAR: if (Allowed & T_QUAL_FAR) { - if (Q & T_QUAL_FAR) { + if (Qualifiers & T_QUAL_FAR) { DuplicateQualifier ("far"); } Q |= T_QUAL_FAR; @@ -162,7 +230,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_FASTCALL: if (Allowed & T_QUAL_FASTCALL) { - if (Q & T_QUAL_FASTCALL) { + if (Qualifiers & T_QUAL_FASTCALL) { DuplicateQualifier ("fastcall"); } Q |= T_QUAL_FASTCALL; @@ -173,7 +241,7 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) case TOK_CDECL: if (Allowed & T_QUAL_CDECL) { - if (Q & T_QUAL_CDECL) { + if (Qualifiers & T_QUAL_CDECL) { DuplicateQualifier ("cdecl"); } Q |= T_QUAL_CDECL; @@ -187,13 +255,16 @@ static TypeCode OptionalQualifiers (TypeCode Allowed) } + /* Combine with newly read qualifiers */ + Qualifiers |= Q; + /* Skip the token */ NextToken (); } Done: /* 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_NEAR: @@ -202,11 +273,11 @@ Done: default: 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 */ - switch (Q & T_QUAL_CCONV) { + switch (Qualifiers & T_QUAL_CCONV) { case T_QUAL_NONE: case T_QUAL_FASTCALL: @@ -215,15 +286,41 @@ Done: default: 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; } +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) /* 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) /* 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) /* Handle an enum, struct or union forward decl */ { @@ -882,14 +910,21 @@ static SymEntry* ParseUnionDecl (const char* Name, unsigned* DSFlags) EnterStructLevel (); /* Parse union fields */ - UnionSize = 0; + UnionSize = 0; while (CurTok.Tok != TOK_RCURLY) { /* Get the type of the entry */ DeclSpec Spec; int SignednessSpecified = 0; + + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified); /* Read fields with this type */ while (1) { @@ -1030,6 +1065,7 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) /* Get the type of the entry */ DeclSpec Spec; + int SignednessSpecified = 0; /* Check for a _Static_assert */ if (CurTok.Tok == TOK_STATIC_ASSERT) { @@ -1037,9 +1073,8 @@ static SymEntry* ParseStructDecl (const char* Name, unsigned* DSFlags) continue; } - int SignednessSpecified = 0; InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE, &SignednessSpecified); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, &SignednessSpecified); /* Read fields with this type */ while (1) { @@ -1205,8 +1240,7 @@ NextMember: if (CurTok.Tok != TOK_COMMA) { -static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, - int* SignednessSpecified) +static void ParseTypeSpec (DeclSpec* D, typespec_t TSFlags, int* SignednessSpecified) /* Parse a type specifier. Store whether one of "signed" or "unsigned" was ** specified, so bit-fields of unspecified signedness can be treated as ** unsigned; without special handling, it would be treated as signed. @@ -1214,6 +1248,7 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, { ident Ident; SymEntry* TagEntry; + TypeCode Qualifiers = T_QUAL_NONE; if (SignednessSpecified != NULL) { *SignednessSpecified = 0; @@ -1222,8 +1257,8 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* Assume we have an explicit type */ D->Flags &= ~DS_DEF_TYPE; - /* Read type qualifiers if we have any */ - Qualifiers |= OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE); + /* Read storage specifiers and/or type qualifiers if we have any */ + OptionalSpecifiers (D, &Qualifiers, TSFlags); /* Look at the data type */ switch (CurTok.Tok) { @@ -1470,20 +1505,21 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers, /* FALL THROUGH */ default: - if (Default < 0) { + if ((TSFlags & TS_MASK_DEFAULT_TYPE) != TS_DEFAULT_TYPE_INT) { Error ("Type expected"); D->Type[0].C = T_INT; D->Type[1].C = T_END; } else { D->Flags |= DS_DEF_TYPE; - D->Type[0].C = (TypeCode) Default; + D->Type[0].C = T_INT; D->Type[1].C = T_END; } break; } - /* There may also be qualifiers *after* the initial type */ - D->Type[0].C |= (Qualifiers | OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE)); + /* There may also be specifiers/qualifiers *after* the initial type */ + OptionalSpecifiers (D, &Qualifiers, TSFlags); + D->Type[0].C |= Qualifiers; } @@ -1563,7 +1599,7 @@ static void ParseOldStyleParamList (FuncDesc* F) DeclSpec Spec; /* 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 ignore all this, since we use auto anyway. @@ -1642,7 +1678,7 @@ static void ParseAnsiParamList (FuncDesc* F) } /* 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 */ if ((Spec.StorageClass & SC_AUTO) == SC_AUTO) { @@ -1790,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 ** 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 */ if (CurTok.Tok == TOK_STAR) { @@ -1799,7 +1835,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) NextToken (); /* 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 */ Declarator (Spec, D, Mode); @@ -1944,7 +1980,7 @@ Type* ParseType (Type* T) /* Get a type without a default */ InitDeclSpec (&Spec); - ParseTypeSpec (&Spec, -1, T_QUAL_NONE, NULL); + ParseTypeSpec (&Spec, TS_DEFAULT_TYPE_NONE, NULL); /* Parse additional declarators */ ParseDecl (&Spec, &Decl, DM_NO_IDENT); @@ -2069,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 */ { - TypeCode Qualifiers; - /* Initialize the DeclSpec struct */ InitDeclSpec (D); - /* There may be qualifiers *before* the storage class specifier */ - Qualifiers = OptionalQualifiers (T_QUAL_CONST | T_QUAL_VOLATILE); + /* Assume we're using an explicit storage class */ + D->Flags &= ~DS_DEF_STORAGE; - /* Now get the storage class specifier for this declaration */ - ParseStorageClass (D, DefStorage); + /* Parse the type specifiers */ + ParseTypeSpec (D, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC, NULL); - /* Parse the type specifiers passing any initial type qualifiers */ - ParseTypeSpec (D, DefType, Qualifiers, NULL); + /* If no explicit storage class is given, use the default */ + if (D->StorageClass == 0) { + D->Flags |= DS_DEF_STORAGE; + D->StorageClass = DefStorage; + } } diff --git a/src/cc65/declare.h b/src/cc65/declare.h index 2b8b36f1c..474a848a9 100644 --- a/src/cc65/declare.h +++ b/src/cc65/declare.h @@ -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 */ #define DS_DEF_STORAGE 0x0001U /* Default storage class 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); /* 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 */ void CheckEmptyDecl (const DeclSpec* D); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 7a22b47b2..c95a5a401 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1301,7 +1301,7 @@ static void Primary (ExprDesc* E) /* Let's see if this is a C99-style declaration */ DeclSpec Spec; InitDeclSpec (&Spec); - ParseDeclSpec (&Spec, -1, T_QUAL_NONE); + ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO); if (Spec.Type->C != T_END) { diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 5f182f061..fb5bc2563 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -568,7 +568,7 @@ void DeclareLocals (void) 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 */ (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */ GetQualifier (Spec.Type) == T_QUAL_NONE) { /* No type qualifier */ diff --git a/test/misc/int-static-1888.c b/test/misc/int-static-1888.c deleted file mode 100644 index e5234ab2c..000000000 --- a/test/misc/int-static-1888.c +++ /dev/null @@ -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; -} diff --git a/test/val/decl-mixed-specifiers.c b/test/val/decl-mixed-specifiers.c new file mode 100644 index 000000000..a0fb1596b --- /dev/null +++ b/test/val/decl-mixed-specifiers.c @@ -0,0 +1,19 @@ +/* bug 1888 - cc65 fails with storage class specifiers after type specifiers */ + +#include + +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; +} diff --git a/test/val/staticassert.c b/test/val/staticassert.c index e43eeec8d..3338f7a4a 100644 --- a/test/val/staticassert.c +++ b/test/val/staticassert.c @@ -65,6 +65,13 @@ struct S { int b; }; +/* _Static_assert can also appear in unions. */ +union U { + int a; + _Static_assert (1, "1 should still be true."); + int b; +}; + int main (void) {