Fixed function parameter checking.
Fixed function return type checking.
This commit is contained in:
parent
4e61ae5b36
commit
0a96ffc878
6 changed files with 210 additions and 130 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
112
src/cc65/expr.c
112
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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue