Fixed calling convention parsing in type names and function parameter types.

This commit is contained in:
acqn 2024-01-01 16:11:30 +08:00
parent 4343eebe67
commit 7aab84628d
2 changed files with 188 additions and 35 deletions

View File

@ -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;

View 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;
}