void EmitCastIfNeeded(IType expectedType, IType actualType)
{
if (actualType == null) // see NullLiteralExpression
return;
if (expectedType == actualType)
return;
if (expectedType.IsPointer || actualType.IsPointer) //no cast needed for addresses
return;
if (IsAssignableFrom(expectedType, actualType))
{
EmitBoxIfNeeded(expectedType, actualType);
return;
}
var method = TypeSystemServices.FindImplicitConversionOperator(actualType, expectedType)
?? TypeSystemServices.FindExplicitConversionOperator(actualType, expectedType);
if (method != null)
{
EmitBoxIfNeeded(method.GetParameters()[0].Type, actualType);
Call(GetMethodInfo(method));
return;
}
if (expectedType is IGenericParameter)
{
// Since expected type is a generic parameter, we don't know whether to emit
// an unbox opcode or a castclass opcode; so we emit an unbox.any opcode which
// works as either of those at runtime
_il.Emit(OpCodes.Unbox_Any, GetSystemType(expectedType));
return;
}
if (expectedType.IsValueType)
{
if (!actualType.IsValueType)
{
// To get a value type out of a reference type we emit an unbox opcode
EmitUnbox(expectedType);
return;
}
// numeric promotion
if (TypeSystemServices.DecimalType == expectedType)
{
Call(GetToDecimalConversionMethod(actualType));
}
else if (TypeSystemServices.DecimalType == actualType)
{
Call(GetFromDecimalConversionMethod(expectedType));
}
else
{
//we need to get the real underlying type here and no earlier
//(because cause enum casting from int can occur [e.g enums-13])
if (actualType.IsEnum)
actualType = TypeSystemServices.Map(GetEnumUnderlyingType(actualType));
if (expectedType.IsEnum)
expectedType = TypeSystemServices.Map(GetEnumUnderlyingType(expectedType));
if (actualType != expectedType) //do we really need conv?
_il.Emit(GetNumericPromotionOpCode(expectedType));
}
return;
}
EmitRuntimeCoercionIfNeeded(expectedType, actualType);
}