"Fossies" - the Fresh Open Source Software archive 
Member "pnet-0.8.0/cscc/c/c_coerce.c" of archive pnet-0.8.0.tar.gz:
/*
* c_coerce.c - Test for casts and coercions between C types.
*
* Copyright (C) 2002 Southern Storm Software, Pty Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <cscc/c/c_internal.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Coercion rules that may be returned from "GetCoerceRules".
*/
#define C_COERCE_INVALID 0
#define C_COERCE_OK (1<<0)
#define C_COERCE_LOSES_CONST (1<<1)
#define C_COERCE_PTR_TO_INT (1<<2)
#define C_COERCE_INT_TO_PTR (1<<3)
#define C_COERCE_PTR_TO_PTR (1<<4)
#define C_COERCE_NULL_PTR (1<<5)
#define C_COERCE_SIMPLE (1<<6)
#define C_COERCE_C_TO_CS_STRING (1<<7)
#define C_COERCE_CSHARP (1<<8)
#define C_COERCE_CSHARP_EXPLICIT (1<<9)
#define C_COERCE_NULL_REF (1<<10)
/*
* C# conversion rules that we can use within C. Basically, everything
* except numeric and user-defined.
*/
#define IL_CONVERT_C_RULES_IMPLICIT (IL_CONVERT_ENUM | \
IL_CONVERT_REFERENCE | \
IL_CONVERT_CONSTANT)
#define IL_CONVERT_C_RULES_EXPLICIT (IL_CONVERT_ENUM | \
IL_CONVERT_REFERENCE | \
IL_CONVERT_UNBOXING | \
IL_CONVERT_CONSTANT)
/*
* Determine if a primitive type is numeric or boolean.
*/
static int TypeIsNumericOrBoolean(int elemType)
{
switch(elemType)
{
case IL_META_ELEMTYPE_BOOLEAN:
case IL_META_ELEMTYPE_I1:
case IL_META_ELEMTYPE_U1:
case IL_META_ELEMTYPE_I2:
case IL_META_ELEMTYPE_U2:
case IL_META_ELEMTYPE_CHAR:
case IL_META_ELEMTYPE_I4:
case IL_META_ELEMTYPE_U4:
case IL_META_ELEMTYPE_I8:
case IL_META_ELEMTYPE_U8:
case IL_META_ELEMTYPE_I:
case IL_META_ELEMTYPE_U:
case IL_META_ELEMTYPE_R4:
case IL_META_ELEMTYPE_R8:
case IL_META_ELEMTYPE_R: return 1;
}
return 0;
}
/*
* Determine if a primitive type is integer.
*/
static int TypeIsInteger(int elemType)
{
switch(elemType)
{
case IL_META_ELEMTYPE_I1:
case IL_META_ELEMTYPE_U1:
case IL_META_ELEMTYPE_I2:
case IL_META_ELEMTYPE_U2:
case IL_META_ELEMTYPE_CHAR:
case IL_META_ELEMTYPE_I4:
case IL_META_ELEMTYPE_U4:
case IL_META_ELEMTYPE_I8:
case IL_META_ELEMTYPE_U8:
case IL_META_ELEMTYPE_I:
case IL_META_ELEMTYPE_U: return 1;
}
return 0;
}
/*
* Determine if a constant value is integer zero, which we can
* implicitly coerce into the NULL pointer value.
*/
static int IsZero(ILEvalValue *constValue)
{
switch(constValue->valueType)
{
case ILMachineType_Int8:
case ILMachineType_UInt8:
case ILMachineType_Int16:
case ILMachineType_UInt16:
case ILMachineType_Char:
case ILMachineType_Int32:
case ILMachineType_UInt32:
case ILMachineType_NativeInt:
case ILMachineType_NativeUInt:
return (constValue->un.i4Value == 0);
case ILMachineType_Int64:
case ILMachineType_UInt64:
return (constValue->un.i8Value == 0);
default: break;
}
return 0;
}
/*
* Determine if a type is "void *".
*/
static int IsVoidPtr(ILType *type)
{
if(type != 0 && ILType_IsComplex(type) &&
ILType_Kind(type) == IL_TYPE_COMPLEX_PTR)
{
return (ILTypeStripPrefixes(ILType_Ref(type)) == ILType_Void);
}
return 0;
}
/*
* Determine if a pointer type is pointing at a "const" element type.
*/
static int IsConstPtr(ILType *type)
{
if(type != 0 && ILType_IsComplex(type) &&
ILType_Kind(type) == IL_TYPE_COMPLEX_PTR)
{
return CTypeIsConst(ILType_Ref(type));
}
else
{
return 0;
}
}
/*
* Determine if an integer type is unsigned.
*/
static int IsUnsigned(ILType *type)
{
type = ILTypeGetEnumType(ILTypeStripPrefixes(type));
if(ILType_IsPrimitive(type))
{
switch(ILType_ToElement(type))
{
case IL_META_ELEMTYPE_U1:
case IL_META_ELEMTYPE_U2:
case IL_META_ELEMTYPE_CHAR:
case IL_META_ELEMTYPE_U4:
case IL_META_ELEMTYPE_U8:
case IL_META_ELEMTYPE_U: return 1;
}
}
return 0;
}
/*
* Determine if two function signatures have the same "shape".
* This is needed to handle cases like "qsort", where the prototype
* of the comparison function may not be identical to that of the
* parameter prototype, but it is compatible enough to use in an
* indirect function call.
*/
static int SameShape(ILType *type1, ILType *type2)
{
unsigned long numParams;
unsigned long param;
type1 = ILTypeStripPrefixes(type1);
type2 = ILTypeStripPrefixes(type2);
if(type1 && type2 && ILType_IsComplex(type1) && ILType_IsComplex(type2) &&
ILType_Kind(type1) == ILType_Kind(type2))
{
if(ILType_Kind(type1) == IL_TYPE_COMPLEX_PTR)
{
/* Pointer types always have the same shape */
return 1;
}
else if((ILType_Kind(type1) & IL_TYPE_COMPLEX_METHOD) != 0)
{
/* Check the return type and parameters in method signatures */
if(!SameShape(ILTypeGetReturn(type1), ILTypeGetReturn(type2)))
{
return 0;
}
if(ILType_CallConv(type1) != ILType_CallConv(type2))
{
return 0;
}
numParams = ILTypeNumParams(type1);
if(ILTypeNumParams(type2) != numParams)
{
return 0;
}
for(param = 1; param <= numParams; ++param)
{
if(!SameShape(ILTypeGetParam(type1, param),
ILTypeGetParam(type2, param)))
{
return 0;
}
}
return 1;
}
}
return ILTypeIdentical(type1, type2);
}
/*
* Determine the coercion rules for coercing "fromType" to "toType".
*/
static int GetCoerceRules(ILType *fromType, ILType *toType,
ILEvalValue *constValue)
{
int constFlags;
/* Strip the outer prefixes from the types, as they apply to
the l-value containing the type, not the type itself */
if(!CTypeIsFunctionPtr(fromType))
{
fromType = ILTypeGetEnumType(ILTypeStripPrefixes(fromType));
}
if(!CTypeIsFunctionPtr(toType))
{
toType = ILTypeGetEnumType(ILTypeStripPrefixes(toType));
}
/* Determine how to perform the coercion, starting at "fromType" */
if(ILType_IsPrimitive(fromType))
{
if(ILType_IsPrimitive(toType))
{
if(TypeIsNumericOrBoolean(ILType_ToElement(fromType)) &&
TypeIsNumericOrBoolean(ILType_ToElement(toType)))
{
/* Can coerce any numeric type or bool to any other
numeric type or bool */
return C_COERCE_SIMPLE;
}
}
else if(CTypeIsPointer(toType) || CTypeIsFunctionPtr(toType))
{
if(TypeIsInteger(ILType_ToElement(fromType)))
{
if(constValue != 0 && IsZero(constValue))
{
/* Implicit conversion of 0 into a NULL pointer value.
This is always OK, as ANSI C defines this behaviour */
return C_COERCE_NULL_PTR;
}
else
{
/* Coercing an integer type to a pointer */
return C_COERCE_INT_TO_PTR;
}
}
}
}
else if(ILType_IsValueType(fromType))
{
/* Coercing a struct, union, or array type: must be identical */
if(ILType_IsValueType(toType))
{
if(ILClassResolve(ILType_ToValueType(fromType)) ==
ILClassResolve(ILType_ToValueType(toType)))
{
return C_COERCE_OK;
}
}
}
else if(CTypeIsPointer(fromType) || CTypeIsFunctionPtr(fromType))
{
/* Coercing a pointer type */
if(IsConstPtr(fromType) && !IsConstPtr(toType))
{
constFlags = C_COERCE_LOSES_CONST;
}
else
{
constFlags = 0;
}
if(CTypeIsIdentical(fromType, toType))
{
/* Coercing a pointer type to itself */
return C_COERCE_OK | constFlags;
}
else if(CTypeIsPointer(toType) || CTypeIsFunctionPtr(toType))
{
if(IsVoidPtr(fromType) || IsVoidPtr(toType))
{
/* Coercing to or from "void *" is always ok */
return C_COERCE_OK | constFlags;
}
else
{
/* Coercing between pointers of different types */
return C_COERCE_OK | C_COERCE_PTR_TO_PTR | constFlags;
}
}
else if(ILType_IsPrimitive(toType) &&
TypeIsInteger(ILType_ToElement(toType)))
{
/* Coercing a pointer to an integer type */
return C_COERCE_PTR_TO_INT | constFlags;
}
else if(toType == ILType_Boolean)
{
/* Coercing a pointer to "_Bool" */
return C_COERCE_SIMPLE | constFlags;
}
}
else if(CTypeIsFunction(fromType) && CTypeIsFunctionPtr(toType))
{
/* We are coecing a function reference to a pointer destination */
if(CTypeIsIdentical(fromType, ILTypeStripPrefixes(toType)))
{
return C_COERCE_OK;
}
/* We can coerce with a warning if the two function pointers
have identical "shape" */
if(SameShape(fromType, toType))
{
return C_COERCE_OK | C_COERCE_PTR_TO_PTR;
}
}
/* Deal with coercions between C and C# strings */
if(CTypeIsPointer(fromType) &&
ILTypeStripPrefixes(CTypeGetPtrRef(fromType)) == ILType_Int8 &&
ILTypeIsStringClass(toType))
{
return C_COERCE_C_TO_CS_STRING;
}
/* We allow identical C# types to be coerced */
if(ILTypeIdentical(fromType, toType))
{
return C_COERCE_OK;
}
/* Test for implicit coercion of zero to a C# reference type.
In this case, we replace the zero with "null" */
if(ILTypeIsReference(toType))
{
if(TypeIsInteger(ILType_ToElement(fromType)))
{
if(constValue != 0 && IsZero(constValue))
{
/* Implicit conversion of 0 into a NULL reference value */
return C_COERCE_NULL_REF;
}
}
}
/* Determine if we can perform a C# type coercion */
if(ILCanCoerceKind(&CCCodeGen, fromType, toType,
IL_CONVERT_C_RULES_IMPLICIT, 0))
{
return C_COERCE_CSHARP;
}
if(ILCanCastKind(&CCCodeGen, fromType, toType,
IL_CONVERT_C_RULES_EXPLICIT, 0))
{
return C_COERCE_CSHARP_EXPLICIT;
}
/* We will get here if we don't have a C type or we have
a completely invalid combination of C types */
return C_COERCE_INVALID;
}
int CCanCoerce(ILType *fromType, ILType *toType)
{
return (GetCoerceRules(fromType, toType, 0) != C_COERCE_INVALID);
}
int CCanCoerceValue(CSemValue fromValue, ILType *toType)
{
if(!CSemIsRValue(fromValue))
{
return 0;
}
else
{
return (GetCoerceRules(CSemGetType(fromValue), toType,
CSemGetConstant(fromValue)) != C_COERCE_INVALID);
}
}
/*
* Create a simple cast node and fix up its line number information.
*/
static ILNode *SimpleCast(ILNode *node, ILMachineType type)
{
ILNode *newNode = ILNode_CastSimple_create(node, type);
CGenCloneLine(newNode, node);
return newNode;
}
/*
* Apply a coercion or cast to a specific node.
*/
static CSemValue ApplyCoercion(ILGenInfo *info, ILNode *node, ILNode **parent,
CSemValue fromValue, ILType *toType,
int explicit)
{
int rules;
ILEvalValue value;
/* Get the coercion rules to apply */
rules = GetCoerceRules(CSemGetType(fromValue), toType,
CSemGetConstant(fromValue));
if(rules == C_COERCE_INVALID)
{
return fromValue;
}
/* Handle C# coercions and casts */
if(rules == C_COERCE_CSHARP_EXPLICIT && !explicit)
{
CCErrorOnLine(yygetfilename(node), yygetlinenum(node),
_("cannot cast from `%s' to `%s'"),
CTypeToName(info, CSemGetType(fromValue)),
CTypeToName(info, toType));
}
if(rules == C_COERCE_CSHARP)
{
ILEvalValue *constValue = CSemGetConstant(fromValue);
if(constValue && constValue->valueType == ILMachineType_ObjectRef &&
constValue->un.oValue == 0)
{
/* We are coercing null to itself, so preserve the constant */
value.valueType = ILMachineType_ObjectRef;
value.un.oValue = 0;
CSemSetConstant(fromValue, toType, value);
return fromValue;
}
ILCoerceKind(info, node, parent, CSemGetType(fromValue), toType,
IL_CONVERT_C_RULES_IMPLICIT, 0);
CSemSetRValue(fromValue, toType);
return fromValue;
}
if(rules == C_COERCE_CSHARP_EXPLICIT)
{
ILCastKind(info, node, parent, CSemGetType(fromValue), toType,
IL_CONVERT_C_RULES_EXPLICIT, 0);
CSemSetRValue(fromValue, toType);
return fromValue;
}
/* Print warnings if necessary */
if(!explicit)
{
if((rules & C_COERCE_LOSES_CONST) != 0)
{
CCWarningOnLine(yygetfilename(node), yygetlinenum(node),
_("discarding `const' qualifier from pointer target type"));
}
if((rules & C_COERCE_PTR_TO_INT) != 0)
{
CCWarningOnLine(yygetfilename(node), yygetlinenum(node),
_("makes integer from pointer without a cast"));
}
if((rules & C_COERCE_INT_TO_PTR) != 0)
{
CCWarningOnLine(yygetfilename(node), yygetlinenum(node),
_("makes pointer from integer without a cast"));
}
if((rules & C_COERCE_PTR_TO_PTR) != 0)
{
CCWarningOnLine(yygetfilename(node), yygetlinenum(node),
_("incompatible pointer types"));
}
}
/* Determine the kind of cast to apply */
if((rules & C_COERCE_PTR_TO_INT) != 0)
{
if(IsUnsigned(toType))
{
/* Coerce to "native unsigned int" first */
node = *parent = SimpleCast(node, ILMachineType_NativeUInt);
}
else
{
/* Coerce to "native int" first */
node = *parent = SimpleCast(node, ILMachineType_NativeInt);
}
*parent = SimpleCast(node, ILTypeToMachineType(toType));
CSemSetRValue(fromValue, toType);
}
else if((rules & C_COERCE_INT_TO_PTR) != 0)
{
if(IsUnsigned(CSemGetType(fromValue)))
{
/* Coerce to "native unsigned int" first */
node = *parent = SimpleCast(node, ILMachineType_NativeUInt);
}
else
{
/* Coerce to "native int" first */
node = *parent = SimpleCast(node, ILMachineType_NativeInt);
}
*parent = SimpleCast(node, ILMachineType_UnmanagedPtr);
CSemSetRValue(fromValue, toType);
}
else if((rules & C_COERCE_NULL_PTR) != 0)
{
/* Replace the current node with a constant "null" pointer value */
*parent = ILNode_NullPtr_create();
CGenCloneLine(*parent, node);
value.valueType = ILMachineType_UnmanagedPtr;
value.un.i4Value = 0;
CSemSetConstant(fromValue, toType, value);
}
else if((rules & C_COERCE_NULL_REF) != 0)
{
/* Replace the current node with a constant "null" reference value */
*parent = ILNode_Null_create();
CGenCloneLine(*parent, node);
value.valueType = ILMachineType_ObjectRef;
value.un.i4Value = 0;
CSemSetConstant(fromValue, toType, value);
}
else if((rules & C_COERCE_SIMPLE) != 0)
{
/* Apply a simple numeric cast if "from" and "to" are different */
ILMachineType ftype = ILTypeToMachineType(CSemGetType(fromValue));
ILMachineType ttype = ILTypeToMachineType(toType);
if(ftype != ttype)
{
*parent = SimpleCast(node, ttype);
if(CSemGetConstant(fromValue) != 0)
{
/* Cast the constant value within the compiler if possible */
value = *(CSemGetConstant(fromValue));
if(ILGenCastConst(info, &value, ftype, ttype))
{
/* Return the coerced constant as a new semantic value */
CSemSetConstant(fromValue, toType, value);
}
else
{
/* We may get here if performing a cast that we cannot
know the value of until runtime. e.g. converting
"int64" into "native int" may or may not lose bits */
CSemSetRValue(fromValue, toType);
}
}
else if(CSemIsDynConstant(fromValue))
{
/* Dynamic constant conversion: result is still a constant */
CSemSetDynConstant(fromValue, toType);
}
else
{
CSemSetRValue(fromValue, toType);
}
}
else if(CSemGetConstant(fromValue) != 0)
{
/* Constant conversion that doesn't change the machine type */
CSemLToRValue(fromValue);
fromValue.type__ = toType;
}
else if(CSemIsDynConstant(fromValue))
{
/* Dynamic constant conversion that doesn't change machine type */
fromValue.type__ = toType;
}
else
{
CSemSetRValue(fromValue, toType);
}
}
else if((rules & C_COERCE_C_TO_CS_STRING) != 0)
{
/* Convert a C string into a C# string */
if(yyisa(node, ILNode_CString))
{
/* The argument is a constant, so merely change its node type */
*parent = ILNode_String_create
(((ILNode_CString *)node)->str, ((ILNode_CString *)node)->len);
CGenCloneLine(*parent, node);
}
else
{
/* We need to call "PtrToStringAnsi" to convert the string */
*parent = ILNode_CToCSharpString_create(node);
CGenCloneLine(*parent, node);
}
CSemSetRValue(fromValue, toType);
}
else
{
CSemSetRValue(fromValue, toType);
}
/* Return the modified semantic value to the caller */
return fromValue;
}
CSemValue CCoerceNode(ILGenInfo *info, ILNode *node, ILNode **parent,
CSemValue fromValue, ILType *toType)
{
return ApplyCoercion(info, node, parent, fromValue, toType, 0);
}
CSemValue CCastNode(ILGenInfo *info, ILNode *node, ILNode **parent,
CSemValue fromValue, ILType *toType)
{
return ApplyCoercion(info, node, parent, fromValue, toType, 1);
}
#ifdef __cplusplus
};
#endif