1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-22 17:29:34 +00:00

Fixed function parameter checking.

Fixed function return type checking.
This commit is contained in:
acqn 2020-08-15 06:27:11 +08:00 committed by Oliver Schmidt
parent 4e61ae5b36
commit 0a96ffc878
6 changed files with 210 additions and 130 deletions

View File

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

View File

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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

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