diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 8b7512730..2666a8d31 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -501,6 +501,31 @@ static void FixQualifiers (Type* DataType) T[0].C |= CodeAddrSizeQualifier (); } + } else { + + /* If we have remaining qualifiers, flag them as invalid */ + Q = T[0].C; + + if (Q & T_QUAL_NEAR) { + Error ("Invalid '__near__' qualifier"); + Q &= ~T_QUAL_NEAR; + } + if (Q & T_QUAL_FAR) { + Error ("Invalid '__far__' qualifier"); + Q &= ~T_QUAL_FAR; + } + if (Q & T_QUAL_FASTCALL) { + Error ("Invalid '__fastcall__' qualifier"); + Q &= ~T_QUAL_FASTCALL; + } + if (Q & T_QUAL_CDECL) { + Error ("Invalid '__cdecl__' qualifier"); + Q &= ~T_QUAL_CDECL; + } + + /* Clear the invalid qualifiers */ + T[0].C &= Q; + } ++T; } @@ -1990,7 +2015,7 @@ static void ParseAnsiParamList (FuncDesc* F) static void ParseFuncDecl (Declarator* D, declmode_t Mode, TypeCode Qualifiers) -/* Parse the argument list of a function with the enclosing parentheses */ +/* Parse the argument list of a function with the closing parenthesis */ { /* Create a new function descriptor */ FuncDesc* F = NewFuncDesc (); @@ -1998,9 +2023,6 @@ static void ParseFuncDecl (Declarator* D, declmode_t Mode, TypeCode Qualifiers) /* Enter a new lexical level */ EnterFunctionLevel (); - /* Skip the opening paren */ - NextToken (); - /* Check for several special parameter lists */ if (CurTok.Tok == TOK_RPAREN) { /* Parameter list is empty (K&R-style) */ @@ -2075,19 +2097,18 @@ static void ParseFuncDecl (Declarator* D, declmode_t Mode, TypeCode Qualifiers) -static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) +static void DirectDecl (DeclSpec* Spec, Declarator* D, TypeCode* RemQ, declmode_t Mode) /* Recursively process direct declarators. Build a type array in reverse order. */ { /* Read optional function or pointer qualifiers that modify the identifier - ** or token to the right. For convenience, we allow a calling convention - ** also for pointers here. If it's a pointer-to-function, the qualifier - ** later will be transfered to the function itself. If it's a pointer to - ** something else, it will be flagged as an error. + ** or token to the right. */ - TypeCode Qualifiers = OptionalQualifiers (T_QUAL_NONE, T_QUAL_ADDRSIZE | T_QUAL_CCONV); + TypeCode Qualifiers = *RemQ | OptionalQualifiers (*RemQ, T_QUAL_ADDRSIZE | T_QUAL_CCONV); /* Pointer to something */ if (CurTok.Tok == TOK_STAR) { + /* Qualifiers on the pointer itself */ + TypeCode Q = T_QUAL_NONE; /* Skip the star */ NextToken (); @@ -2098,17 +2119,30 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) } /* Allow const, restrict, and volatile qualifiers */ - Qualifiers |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); + Q |= OptionalQualifiers (Qualifiers, T_QUAL_CVR); - /* Parse the type that the pointer points to */ - DirectDecl (Spec, D, Mode); + /* For convenience, we allow a calling convention also for pointers + ** here. If it's a pointer-to-function, the qualifier later will be + ** transfered to the function itself. If it's a pointer to something + ** else, it will be flagged as an error. + */ + *RemQ = T_QUAL_NONE; + + /* Parse the type that derives from the pointer */ + DirectDecl (Spec, D, RemQ, Mode); /* Add the type */ - AddTypeCodeToDeclarator (D, T_PTR | Qualifiers); + AddTypeCodeToDeclarator (D, T_PTR | Q | *RemQ); + + /* Return the calling convention and address size specifiers on the + ** pointee type. + */ + *RemQ = Qualifiers; return; } if (CurTok.Tok == TOK_LPAREN) { + int Nested = 0; SymEntry* Entry; /* An empty declaration cannot contain parentheses where an identifier @@ -2118,19 +2152,33 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) Spec->Flags |= DS_NO_EMPTY_DECL; } + /* Skip the opening paren */ + NextToken (); + /* We have to disambiguate the meanings of 'type (identifier' when ** the identifier can be a typedef'ed parameter type specifier or ** a declarator enclosed in parentheses in some cases. */ if (Mode == DM_IDENT_OR_EMPTY || /* If we are in a declaration... */ - NextTok.Tok == TOK_LPAREN || /* or the next token is one more paren... */ - NextTok.Tok == TOK_STAR || /* or a '*' ... */ - (NextTok.Tok == TOK_IDENT && /* or an identifier that... */ - ((Entry = FindSym (NextTok.Ident)) == 0 || /* is not a typedef. */ - !SymIsTypeDef (Entry)))) { + CurTok.Tok == TOK_LPAREN || /* or the next token is one more paren... */ + CurTok.Tok == TOK_STAR || /* or a '*' ... */ + (CurTok.Tok == TOK_IDENT && /* or an identifier that... */ + ((Entry = FindSym (CurTok.Ident)) == 0 || /* is not a typedef. */ + !SymIsTypeDef (Entry)))) { + Nested = 1; + } else { + /* Check for qualifiers */ + TypeCode Q = OptionalQualifiers (T_QUAL_NONE, T_QUAL_ADDRSIZE | T_QUAL_CCONV); + + if (Q != T_QUAL_NONE) { + Qualifiers |= Q; + Nested = 1; + } + } + + if (Nested) { /* Parse the direct declarator in parentheses */ - NextToken (); - DirectDecl (Spec, D, Mode); + DirectDecl (Spec, D, &Qualifiers, Mode); ConsumeRParen (); } else { /* This is a parameter type list in parentheses */ @@ -2160,6 +2208,9 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN) { if (CurTok.Tok == TOK_LPAREN) { + /* Skip the opening paren */ + NextToken (); + /* Function declarator */ ParseFuncDecl (D, Mode, Qualifiers); @@ -2215,19 +2266,7 @@ static void DirectDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) } } - /* If we have remaining qualifiers, flag them as invalid */ - if (Qualifiers & T_QUAL_NEAR) { - Error ("Invalid '__near__' qualifier"); - } - if (Qualifiers & T_QUAL_FAR) { - Error ("Invalid '__far__' qualifier"); - } - if (Qualifiers & T_QUAL_FASTCALL) { - Error ("Invalid '__fastcall__' qualifier"); - } - if (Qualifiers & T_QUAL_CDECL) { - Error ("Invalid '__cdecl__' qualifier"); - } + *RemQ = Qualifiers; } @@ -2288,6 +2327,8 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) ** Return 1 otherwise. */ { + TypeCode Q = T_QUAL_NONE; + /* Used to check if we have any errors during parsing this */ unsigned PrevErrorCount = ErrorCount; @@ -2303,11 +2344,12 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode) InitDeclarator (D); /* Get additional derivation of the declarator and the identifier */ - DirectDecl (Spec, D, Mode); + DirectDecl (Spec, D, &Q, Mode); /* Add the base type */ NeedTypeSpace (D, TypeLen (Spec->Type) + 1); /* Bounds check */ TypeCopy (D->Type + D->Index, Spec->Type); + D->Type[D->Index].C |= Q; /* Use the storage class from the declspec */ D->StorageClass = Spec->StorageClass; diff --git a/test/val/bug2327-cconv-type.c b/test/val/bug2327-cconv-type.c new file mode 100644 index 000000000..678727278 --- /dev/null +++ b/test/val/bug2327-cconv-type.c @@ -0,0 +1,111 @@ +/* Bug #2327 - Calling conventions and address size specifiers of functions in type names and parameter types are not parsed correctly */ + +#include +#include + +unsigned failures; +int need_header = 1; +const char* test_header; + +/* Helpers */ +void set_header(const char* name) +{ + if (need_header == 0) + { + printf("\n"); + } + test_header = name; + need_header = 1; +} + +void print_header(void) +{ + if (need_header) + { + need_header = 0; + printf("<%s test>\n", test_header); + } +} + +#define CHECK(R, E) \ + do { \ + if ((R) != (E)) { \ + ++failures; \ + print_header(); \ + printf(" fail: %s = %d\n", #R, (R)); \ + } \ + } while (0); + +#define CHECK_RV() CHECK(rv, 42) +#define CHECK_SP() CHECK(x - (intptr_t)&x, 0) + +#define FUNC_QUAL __cdecl__ __near__ + +typedef int hoo_t(int __far__ __cdecl__ ()); +typedef int hoo_t(int __far__ (__cdecl__)()); /* Question: should this be rejected? */ +typedef int hoo_t(int __far__ (__cdecl__ ())); +typedef int hoo_t(int __far__ (__cdecl__ *)()); +typedef int hoo_t(int __far__ (__cdecl__ (*)())); +typedef int hoo_t(int __far__ ((__cdecl__ *)())); + +typedef int hoo_t(int __cdecl__ __far__ ()); +typedef int hoo_t(int (__cdecl__ __far__)()); /* Question: should this be rejected? */ +typedef int hoo_t(int (__cdecl__ __far__ ())); +typedef int hoo_t(int (__cdecl__ __far__ *)()); +typedef int hoo_t(int (__cdecl__ (__far__ *)())); +typedef int hoo_t(int ((__cdecl__ __far__ *)())); + +typedef int (FUNC_QUAL foo_t)(int, int); +typedef int (FUNC_QUAL *pfoo_t)(int, int); + +int FUNC_QUAL foo(int a, int b) +{ + return a * b; +} + +int (FUNC_QUAL * const pfoo)() = (int (FUNC_QUAL *)())foo; + +/* Incompatible and not working for cc65 if used as-is */ +int (*qfoo)(int, ...) = foo; + +int main(void) +{ + int rv; + intptr_t x; + + set_header("init"); + x = (intptr_t)&x; + CHECK_SP() + + set_header("foo"); + rv = foo((int8_t)-3, (int32_t)-14); + CHECK_RV() + CHECK_SP() + + set_header("pfoo"); +#if 0 + /* This would fail */ + rv = pfoo((int8_t)-6, (int32_t)-7); +#else + rv = ((pfoo_t)pfoo)((int8_t)-6, (int32_t)-7); +#endif + CHECK_RV() + CHECK_SP() + + set_header("qfoo"); +#if 0 + /* This would fail */ + rv = (qfoo)((int32_t)-6, (int8_t)-7); +#else + rv = ((foo_t *)qfoo)((int32_t)-6, (int8_t)-7); +#endif + CHECK_RV() + CHECK_SP() + + if (failures > 0) + { + printf("\nfailures: %u\n", failures); + } + + return failures; +}