mirror of
https://github.com/cc65/cc65.git
synced 2025-02-28 20:29:46 +00:00
Merge pull request #2331 from acqn/FuncDeclFix
[cc65] Fixed calling convention parsing in type names and function parameter types
This commit is contained in:
commit
a71865f6ec
@ -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. */
|
||||
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;
|
||||
|
111
test/val/bug2327-cconv-type.c
Normal file
111
test/val/bug2327-cconv-type.c
Normal file
@ -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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user