From 0a96ffc8786726ca6d492fd70ceaf5dfdae7b3a7 Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 15 Aug 2020 06:27:11 +0800 Subject: [PATCH] Fixed function parameter checking. Fixed function return type checking. --- src/cc65/codeinfo.c | 6 +- src/cc65/declare.c | 20 +----- src/cc65/expr.c | 112 ++++++++++++++++--------------- src/cc65/function.c | 156 ++++++++++++++++++++++++++++++++------------ src/cc65/function.h | 6 ++ src/cc65/stmt.c | 40 +++++++----- 6 files changed, 210 insertions(+), 130 deletions(-) diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 3e1d58709..d7d85a12d 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -410,8 +410,10 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg) (AutoCDecl ? IsQualFastcall (E->Type) : !IsQualCDecl (E->Type))) { - /* Will use registers depending on the last param. */ - switch (CheckedSizeOf (D->LastParam->Type)) { + /* Will use registers depending on the last param. If the last + ** param has incomplete type, just assume __EAX__. + */ + switch (SizeOf (D->LastParam->Type)) { case 1u: *Use = REG_A; break; diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 4e8446176..218ba6017 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -1704,7 +1704,6 @@ static void ParseAnsiParamList (FuncDesc* F) static FuncDesc* ParseFuncDecl (void) /* Parse the argument list of a function. */ { - unsigned Offs; SymEntry* Sym; SymEntry* WrappedCall; unsigned char WrappedCallData; @@ -1751,23 +1750,10 @@ static FuncDesc* ParseFuncDecl (void) */ F->LastParam = GetSymTab()->SymTail; - /* Assign offsets. If the function has a variable parameter list, - ** there's one additional byte (the arg size). + /* It is allowed to use incomplete types in function prototypes, so we + ** won't always get to know the parameter sizes here and may do that later. */ - Offs = (F->Flags & FD_VARIADIC)? 1 : 0; - Sym = F->LastParam; - while (Sym) { - unsigned Size = CheckedSizeOf (Sym->Type); - if (SymIsRegVar (Sym)) { - Sym->V.R.SaveOffs = Offs; - } else { - Sym->V.Offs = Offs; - } - Offs += Size; - F->ParamSize += Size; - Sym = Sym->PrevSym; - } - + /* Leave the lexical level remembering the symbol tables */ RememberFunctionLevel (F); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 9f7902284..89c7ff108 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -346,6 +346,9 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) int FrameOffs = 0; /* Offset into parameter frame */ int Ellipsis = 0; /* Function is variadic */ + /* Make sure the size of all parameters are known */ + int ParamComplete = F_CheckParamList (Func, 1); + /* As an optimization, we may allocate the complete parameter frame at ** once instead of pushing into each parameter as it comes. We may do that, ** if... @@ -359,7 +362,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) ** (instead of pushing) is enabled. ** */ - if (IS_Get (&CodeSizeFactor) >= 200) { + if (ParamComplete && IS_Get (&CodeSizeFactor) >= 200) { /* Calculate the number and size of the parameters */ FrameParams = Func->ParamCount; @@ -424,65 +427,68 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) /* Evaluate the argument expression */ hie1 (&Expr); - /* If we don't have a prototype, accept anything; otherwise, convert - ** the actual argument to the parameter type needed. - */ - Flags = CF_NONE; - if (!Ellipsis) { - - /* Convert the argument to the parameter type if needed */ - TypeConversion (&Expr, Param->Type); - - /* If we have a prototype, chars may be pushed as chars */ - Flags |= CF_FORCECHAR; - - } else { - - /* No prototype available. Convert array to "pointer to first - ** element", and function to "pointer to function". + /* Skip to the next parameter if there are any incomplete types */ + if (ParamComplete) { + /* If we don't have an argument spec., accept anything; otherwise, + ** convert the actual argument to the type needed. */ - Expr.Type = PtrConversion (Expr.Type); + Flags = CF_NONE; + if (!Ellipsis) { - } + /* Convert the argument to the parameter type if needed */ + TypeConversion (&Expr, Param->Type); - /* Handle struct/union specially */ - if (IsClassStruct (Expr.Type)) { - /* Use the replacement type */ - Flags |= TypeOf (GetStructReplacementType (Expr.Type)); - } else { - /* Use the type of the argument for the push */ - Flags |= TypeOf (Expr.Type); - } + /* If we have a prototype, chars may be pushed as chars */ + Flags |= CF_FORCECHAR; - /* Load the value into the primary if it is not already there */ - LoadExpr (Flags, &Expr); - - /* If this is a fastcall function, don't push the last argument */ - if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) { - unsigned ArgSize = sizeofarg (Flags); - - if (FrameSize > 0) { - /* We have the space already allocated, store in the frame. - ** Because of invalid type conversions (that have produced an - ** error before), we can end up here with a non-aligned stack - ** frame. Since no output will be generated anyway, handle - ** these cases gracefully instead of doing a CHECK. - */ - if (FrameSize >= ArgSize) { - FrameSize -= ArgSize; - } else { - FrameSize = 0; - } - FrameOffs -= ArgSize; - /* Store */ - g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal); } else { - /* Push the argument */ - g_push (Flags, Expr.IVal); + + /* No prototype available. Convert array to "pointer to first + ** element", and function to "pointer to function". + */ + Expr.Type = PtrConversion (Expr.Type); + } - /* Calculate total parameter size */ - PushedSize += ArgSize; + /* Handle struct/union specially */ + if (IsClassStruct (Expr.Type)) { + /* Use the replacement type */ + Flags |= TypeOf (GetStructReplacementType (Expr.Type)); + } else { + /* Use the type of the argument for the push */ + Flags |= TypeOf (Expr.Type); + } + + /* Load the value into the primary if it is not already there */ + LoadExpr (Flags, &Expr); + + /* If this is a fastcall function, don't push the last argument */ + if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) { + unsigned ArgSize = sizeofarg (Flags); + + if (FrameSize > 0) { + /* We have the space already allocated, store in the frame. + ** Because of invalid type conversions (that have produced an + ** error before), we can end up here with a non-aligned stack + ** frame. Since no output will be generated anyway, handle + ** these cases gracefully instead of doing a CHECK. + */ + if (FrameSize >= ArgSize) { + FrameSize -= ArgSize; + } else { + FrameSize = 0; + } + FrameOffs -= ArgSize; + /* Store */ + g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal); + } else { + /* Push the argument */ + g_push (Flags, Expr.IVal); + } + + /* Calculate total parameter size */ + PushedSize += ArgSize; + } } /* Check for end of argument list */ diff --git a/src/cc65/function.c b/src/cc65/function.c index 290916cd2..451efc54d 100644 --- a/src/cc65/function.c +++ b/src/cc65/function.c @@ -105,6 +105,62 @@ static void FreeFunction (Function* F) +int F_CheckParamList (FuncDesc* D, int RequireAll) +/* Check and set the parameter sizes. +** If RequireAll is true, emit errors on parameters of incomplete types. +** Return true if all parameters have complete types. +*/ +{ + unsigned I = 0; + unsigned Offs; + SymEntry* Param; + unsigned ParamSize = 0; + unsigned IncompleteCount = 0; + + /* Assign offsets. If the function has a variable parameter list, + ** there's one additional byte (the arg size). + */ + Offs = (D->Flags & FD_VARIADIC) ? 1 : 0; + Param = D->LastParam; + while (Param) { + unsigned Size = SizeOf (Param->Type); + if (RequireAll && IsIncompleteESUType (Param->Type)) { + if (D->Flags & FD_UNNAMED_PARAMS) { + Error ("Parameter %u has incomplete type '%s'", + D->ParamCount - I, + GetFullTypeName (Param->Type)); + } else { + Error ("Parameter '%s' has incomplete type '%s'", + Param->Name, + GetFullTypeName (Param->Type)); + } + ++IncompleteCount; + } + if (SymIsRegVar (Param)) { + Param->V.R.SaveOffs = Offs; + } else { + Param->V.Offs = Offs; + } + Offs += Size; + ParamSize += Size; + Param = Param->PrevSym; + ++I; + } + + /* If all parameters have complete types, set the total size description + ** and return true. + */ + if (IncompleteCount == 0) { + D->ParamSize = ParamSize; + return 1; + } + + /* Otherwise return false */ + return 0; +} + + + const char* F_GetFuncName (const Function* F) /* Return the name of the current function */ { @@ -378,9 +434,11 @@ static void F_EmitDebugInfo (void) void NewFunc (SymEntry* Func, FuncDesc* D) /* Parse argument declarations and function body. */ { + int ParamComplete; /* If all paramemters have complete types */ int C99MainFunc = 0;/* Flag for C99 main function returning int */ SymEntry* Param; const Type* RType; /* Real type used for struct parameters */ + const Type* ReturnType; /* Return type */ /* Remember this function descriptor used for definition */ GetFuncDesc (Func->Type)->FuncDef = D; @@ -391,6 +449,21 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Reenter the lexical level */ ReenterFunctionLevel (D); + /* Check return type */ + ReturnType = F_GetReturnType (CurrentFunc); + if (IsIncompleteESUType (ReturnType)) { + /* There are already diagnostics on returning arrays or functions */ + if (!IsTypeArray (ReturnType) && !IsTypeFunc (ReturnType)) { + Error ("Function has incomplete return type '%s'", + GetFullTypeName (ReturnType)); + } + } + + /* Check and set the parameter sizes. All parameter must have complete + ** types now. + */ + ParamComplete = F_CheckParamList (D, 1); + /* Check if the function header contains unnamed parameters. These are ** only allowed in cc65 mode. */ @@ -429,7 +502,7 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* If cc65 extensions aren't enabled, don't allow a main function that ** doesn't return an int. */ - if (IS_Get (&Standard) != STD_CC65 && CurrentFunc->ReturnType[0].C != T_INT) { + if (IS_Get (&Standard) != STD_CC65 && ReturnType[0].C != T_INT) { Error ("'main' must always return an int"); } @@ -472,16 +545,11 @@ void NewFunc (SymEntry* Func, FuncDesc* D) unsigned Flags; /* Generate the push */ - if (IsTypeFunc (D->LastParam->Type)) { - /* Pointer to function */ - Flags = CF_PTR; + /* Handle struct/union specially */ + if (IsClassStruct (D->LastParam->Type)) { + Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; } else { - /* Handle struct/union specially */ - if (IsClassStruct (D->LastParam->Type)) { - Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR; - } else { - Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; - } + Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR; } g_push (Flags, 0); } @@ -497,48 +565,50 @@ void NewFunc (SymEntry* Func, FuncDesc* D) /* Setup the stack */ StackPtr = 0; - /* Walk through the parameter list and allocate register variable space - ** for parameters declared as register. Generate code to swap the contents - ** of the register bank with the save area on the stack. - */ - Param = D->SymTab->SymHead; - while (Param && (Param->Flags & SC_PARAM) != 0) { + /* Emit code to handle the parameters if all of them have complete types */ + if (ParamComplete) { + /* Walk through the parameter list and allocate register variable space + ** for parameters declared as register. Generate code to swap the contents + ** of the register bank with the save area on the stack. + */ + Param = D->SymTab->SymHead; + while (Param && (Param->Flags & SC_PARAM) != 0) { - /* Check if we need copy for struct/union type */ - RType = Param->Type; - if (IsClassStruct (RType)) { - RType = GetStructReplacementType (RType); + /* Check if we need copy for struct/union type */ + RType = Param->Type; + if (IsClassStruct (RType)) { + RType = GetStructReplacementType (RType); - /* If there is no replacement type, then it is just the address. - ** We don't currently support this case. - */ - if (RType == Param->Type) { - Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (Param->Type)); + /* If there is no replacement type, then it is just the address. + ** We don't currently support this case. + */ + if (RType == Param->Type) { + Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (Param->Type)); + } } - } + /* Check for a register variable */ + if (SymIsRegVar (Param)) { - /* Check for a register variable */ - if (SymIsRegVar (Param)) { + /* Allocate space */ + int Reg = F_AllocRegVar (CurrentFunc, RType); - /* Allocate space */ - int Reg = F_AllocRegVar (CurrentFunc, RType); + /* Could we allocate a register? */ + if (Reg < 0) { + /* No register available: Convert parameter to auto */ + CvtRegVarToAuto (Param); + } else { + /* Remember the register offset */ + Param->V.R.RegOffs = Reg; - /* Could we allocate a register? */ - if (Reg < 0) { - /* No register available: Convert parameter to auto */ - CvtRegVarToAuto (Param); - } else { - /* Remember the register offset */ - Param->V.R.RegOffs = Reg; - - /* Generate swap code */ - g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (RType)); + /* Generate swap code */ + g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (RType)); + } } - } - /* Next parameter */ - Param = Param->NextSym; + /* Next parameter */ + Param = Param->NextSym; + } } /* Need a starting curly brace */ diff --git a/src/cc65/function.h b/src/cc65/function.h index 8231a1970..e0b7ef0a2 100644 --- a/src/cc65/function.h +++ b/src/cc65/function.h @@ -81,6 +81,12 @@ extern Function* CurrentFunc; +int F_CheckParamList (FuncDesc* D, int RequireAll); +/* Check and set the parameter sizes. +** If RequireAll is true, emit errors on parameters of incomplete types. +** Return true if all parameters have complete types. +*/ + const char* F_GetFuncName (const Function* F); /* Return the name of the current function */ diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 036cc2d89..0925c6d3d 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -318,28 +318,38 @@ static void ReturnStatement (void) /* Evaluate the return expression */ hie0 (&Expr); - /* If we return something in a void function, print an error and - ** ignore the value. Otherwise convert the value to the type of the - ** return. + /* If we return something in a function with void or incomplete return + ** type, print an error and ignore the value. Otherwise convert the + ** value to the type of the return. */ if (F_HasVoidReturn (CurrentFunc)) { - Error ("Returning a value in function with return type void"); + Error ("Returning a value in function with return type 'void'"); } else { - /* Convert the return value to the type of the function result */ - TypeConversion (&Expr, F_GetReturnType (CurrentFunc)); - /* Load the value into the primary */ - if (IsClassStruct (Expr.Type)) { - /* Handle struct/union specially */ - ReturnType = GetStructReplacementType (Expr.Type); - if (ReturnType == Expr.Type) { - Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); + /* Check the return type first */ + ReturnType = F_GetReturnType (CurrentFunc); + if (IsIncompleteESUType (ReturnType)) { + /* Avoid excess errors */ + if (ErrorCount == 0) { + Error ("Returning a value in function with incomplete return type"); } - LoadExpr (TypeOf (ReturnType), &Expr); - } else { + /* Convert the return value to the type of the function result */ + TypeConversion (&Expr, ReturnType); + /* Load the value into the primary */ - LoadExpr (CF_NONE, &Expr); + if (IsClassStruct (Expr.Type)) { + /* Handle struct/union specially */ + ReturnType = GetStructReplacementType (Expr.Type); + if (ReturnType == Expr.Type) { + Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type)); + } + LoadExpr (TypeOf (ReturnType), &Expr); + + } else { + /* Load the value into the primary */ + LoadExpr (CF_NONE, &Expr); + } } }