Fixed function parameter checking.
Fixed function return type checking.
This commit is contained in:
@@ -410,8 +410,10 @@ fncls_t GetFuncInfo (const char* Name, unsigned short* Use, unsigned short* Chg)
|
|||||||
(AutoCDecl ?
|
(AutoCDecl ?
|
||||||
IsQualFastcall (E->Type) :
|
IsQualFastcall (E->Type) :
|
||||||
!IsQualCDecl (E->Type))) {
|
!IsQualCDecl (E->Type))) {
|
||||||
/* Will use registers depending on the last param. */
|
/* Will use registers depending on the last param. If the last
|
||||||
switch (CheckedSizeOf (D->LastParam->Type)) {
|
** param has incomplete type, just assume __EAX__.
|
||||||
|
*/
|
||||||
|
switch (SizeOf (D->LastParam->Type)) {
|
||||||
case 1u:
|
case 1u:
|
||||||
*Use = REG_A;
|
*Use = REG_A;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1704,7 +1704,6 @@ static void ParseAnsiParamList (FuncDesc* F)
|
|||||||
static FuncDesc* ParseFuncDecl (void)
|
static FuncDesc* ParseFuncDecl (void)
|
||||||
/* Parse the argument list of a function. */
|
/* Parse the argument list of a function. */
|
||||||
{
|
{
|
||||||
unsigned Offs;
|
|
||||||
SymEntry* Sym;
|
SymEntry* Sym;
|
||||||
SymEntry* WrappedCall;
|
SymEntry* WrappedCall;
|
||||||
unsigned char WrappedCallData;
|
unsigned char WrappedCallData;
|
||||||
@@ -1751,23 +1750,10 @@ static FuncDesc* ParseFuncDecl (void)
|
|||||||
*/
|
*/
|
||||||
F->LastParam = GetSymTab()->SymTail;
|
F->LastParam = GetSymTab()->SymTail;
|
||||||
|
|
||||||
/* Assign offsets. If the function has a variable parameter list,
|
/* It is allowed to use incomplete types in function prototypes, so we
|
||||||
** there's one additional byte (the arg size).
|
** 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 */
|
/* Leave the lexical level remembering the symbol tables */
|
||||||
RememberFunctionLevel (F);
|
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 FrameOffs = 0; /* Offset into parameter frame */
|
||||||
int Ellipsis = 0; /* Function is variadic */
|
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
|
/* 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,
|
** once instead of pushing into each parameter as it comes. We may do that,
|
||||||
** if...
|
** if...
|
||||||
@@ -359,7 +362,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall)
|
|||||||
** (instead of pushing) is enabled.
|
** (instead of pushing) is enabled.
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
if (IS_Get (&CodeSizeFactor) >= 200) {
|
if (ParamComplete && IS_Get (&CodeSizeFactor) >= 200) {
|
||||||
|
|
||||||
/* Calculate the number and size of the parameters */
|
/* Calculate the number and size of the parameters */
|
||||||
FrameParams = Func->ParamCount;
|
FrameParams = Func->ParamCount;
|
||||||
@@ -424,65 +427,68 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall)
|
|||||||
/* Evaluate the argument expression */
|
/* Evaluate the argument expression */
|
||||||
hie1 (&Expr);
|
hie1 (&Expr);
|
||||||
|
|
||||||
/* If we don't have a prototype, accept anything; otherwise, convert
|
/* Skip to the next parameter if there are any incomplete types */
|
||||||
** the actual argument to the parameter type needed.
|
if (ParamComplete) {
|
||||||
*/
|
/* If we don't have an argument spec., accept anything; otherwise,
|
||||||
Flags = CF_NONE;
|
** convert the actual argument to the type needed.
|
||||||
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".
|
|
||||||
*/
|
*/
|
||||||
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 we have a prototype, chars may be pushed as chars */
|
||||||
if (IsClassStruct (Expr.Type)) {
|
Flags |= CF_FORCECHAR;
|
||||||
/* 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 {
|
} 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 */
|
/* Handle struct/union specially */
|
||||||
PushedSize += ArgSize;
|
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 */
|
/* 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)
|
const char* F_GetFuncName (const Function* F)
|
||||||
/* Return the name of the current function */
|
/* Return the name of the current function */
|
||||||
{
|
{
|
||||||
@@ -378,9 +434,11 @@ static void F_EmitDebugInfo (void)
|
|||||||
void NewFunc (SymEntry* Func, FuncDesc* D)
|
void NewFunc (SymEntry* Func, FuncDesc* D)
|
||||||
/* Parse argument declarations and function body. */
|
/* Parse argument declarations and function body. */
|
||||||
{
|
{
|
||||||
|
int ParamComplete; /* If all paramemters have complete types */
|
||||||
int C99MainFunc = 0;/* Flag for C99 main function returning int */
|
int C99MainFunc = 0;/* Flag for C99 main function returning int */
|
||||||
SymEntry* Param;
|
SymEntry* Param;
|
||||||
const Type* RType; /* Real type used for struct parameters */
|
const Type* RType; /* Real type used for struct parameters */
|
||||||
|
const Type* ReturnType; /* Return type */
|
||||||
|
|
||||||
/* Remember this function descriptor used for definition */
|
/* Remember this function descriptor used for definition */
|
||||||
GetFuncDesc (Func->Type)->FuncDef = D;
|
GetFuncDesc (Func->Type)->FuncDef = D;
|
||||||
@@ -391,6 +449,21 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
|
|||||||
/* Reenter the lexical level */
|
/* Reenter the lexical level */
|
||||||
ReenterFunctionLevel (D);
|
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
|
/* Check if the function header contains unnamed parameters. These are
|
||||||
** only allowed in cc65 mode.
|
** 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
|
/* If cc65 extensions aren't enabled, don't allow a main function that
|
||||||
** doesn't return an int.
|
** 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");
|
Error ("'main' must always return an int");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,16 +545,11 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
|
|||||||
unsigned Flags;
|
unsigned Flags;
|
||||||
|
|
||||||
/* Generate the push */
|
/* Generate the push */
|
||||||
if (IsTypeFunc (D->LastParam->Type)) {
|
/* Handle struct/union specially */
|
||||||
/* Pointer to function */
|
if (IsClassStruct (D->LastParam->Type)) {
|
||||||
Flags = CF_PTR;
|
Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR;
|
||||||
} else {
|
} else {
|
||||||
/* Handle struct/union specially */
|
Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR;
|
||||||
if (IsClassStruct (D->LastParam->Type)) {
|
|
||||||
Flags = TypeOf (GetStructReplacementType (D->LastParam->Type)) | CF_FORCECHAR;
|
|
||||||
} else {
|
|
||||||
Flags = TypeOf (D->LastParam->Type) | CF_FORCECHAR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
g_push (Flags, 0);
|
g_push (Flags, 0);
|
||||||
}
|
}
|
||||||
@@ -497,48 +565,50 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
|
|||||||
/* Setup the stack */
|
/* Setup the stack */
|
||||||
StackPtr = 0;
|
StackPtr = 0;
|
||||||
|
|
||||||
/* Walk through the parameter list and allocate register variable space
|
/* Emit code to handle the parameters if all of them have complete types */
|
||||||
** for parameters declared as register. Generate code to swap the contents
|
if (ParamComplete) {
|
||||||
** of the register bank with the save area on the stack.
|
/* Walk through the parameter list and allocate register variable space
|
||||||
*/
|
** for parameters declared as register. Generate code to swap the contents
|
||||||
Param = D->SymTab->SymHead;
|
** of the register bank with the save area on the stack.
|
||||||
while (Param && (Param->Flags & SC_PARAM) != 0) {
|
*/
|
||||||
|
Param = D->SymTab->SymHead;
|
||||||
|
while (Param && (Param->Flags & SC_PARAM) != 0) {
|
||||||
|
|
||||||
/* Check if we need copy for struct/union type */
|
/* Check if we need copy for struct/union type */
|
||||||
RType = Param->Type;
|
RType = Param->Type;
|
||||||
if (IsClassStruct (RType)) {
|
if (IsClassStruct (RType)) {
|
||||||
RType = GetStructReplacementType (RType);
|
RType = GetStructReplacementType (RType);
|
||||||
|
|
||||||
/* If there is no replacement type, then it is just the address.
|
/* If there is no replacement type, then it is just the address.
|
||||||
** We don't currently support this case.
|
** We don't currently support this case.
|
||||||
*/
|
*/
|
||||||
if (RType == Param->Type) {
|
if (RType == Param->Type) {
|
||||||
Error ("Passing '%s' of this size by value is not supported", GetFullTypeName (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 */
|
/* Allocate space */
|
||||||
if (SymIsRegVar (Param)) {
|
int Reg = F_AllocRegVar (CurrentFunc, RType);
|
||||||
|
|
||||||
/* Allocate space */
|
/* Could we allocate a register? */
|
||||||
int Reg = F_AllocRegVar (CurrentFunc, RType);
|
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? */
|
/* Generate swap code */
|
||||||
if (Reg < 0) {
|
g_swap_regvars (Param->V.R.SaveOffs, Reg, CheckedSizeOf (RType));
|
||||||
/* 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));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Next parameter */
|
/* Next parameter */
|
||||||
Param = Param->NextSym;
|
Param = Param->NextSym;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Need a starting curly brace */
|
/* 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);
|
const char* F_GetFuncName (const Function* F);
|
||||||
/* Return the name of the current function */
|
/* Return the name of the current function */
|
||||||
|
|
||||||
|
|||||||
@@ -318,28 +318,38 @@ static void ReturnStatement (void)
|
|||||||
/* Evaluate the return expression */
|
/* Evaluate the return expression */
|
||||||
hie0 (&Expr);
|
hie0 (&Expr);
|
||||||
|
|
||||||
/* If we return something in a void function, print an error and
|
/* If we return something in a function with void or incomplete return
|
||||||
** ignore the value. Otherwise convert the value to the type of the
|
** type, print an error and ignore the value. Otherwise convert the
|
||||||
** return.
|
** value to the type of the return.
|
||||||
*/
|
*/
|
||||||
if (F_HasVoidReturn (CurrentFunc)) {
|
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 {
|
} else {
|
||||||
/* Convert the return value to the type of the function result */
|
|
||||||
TypeConversion (&Expr, F_GetReturnType (CurrentFunc));
|
|
||||||
|
|
||||||
/* Load the value into the primary */
|
/* Check the return type first */
|
||||||
if (IsClassStruct (Expr.Type)) {
|
ReturnType = F_GetReturnType (CurrentFunc);
|
||||||
/* Handle struct/union specially */
|
if (IsIncompleteESUType (ReturnType)) {
|
||||||
ReturnType = GetStructReplacementType (Expr.Type);
|
/* Avoid excess errors */
|
||||||
if (ReturnType == Expr.Type) {
|
if (ErrorCount == 0) {
|
||||||
Error ("Returning '%s' of this size by value is not supported", GetFullTypeName (Expr.Type));
|
Error ("Returning a value in function with incomplete return type");
|
||||||
}
|
}
|
||||||
LoadExpr (TypeOf (ReturnType), &Expr);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
/* Convert the return value to the type of the function result */
|
||||||
|
TypeConversion (&Expr, ReturnType);
|
||||||
|
|
||||||
/* Load the value into the primary */
|
/* 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user