using System;
using System.Diagnostics;
using CodeGeneration;
namespace ABT {
public enum TypeCastType {
NOP,
INT8_TO_INT16,
INT8_TO_INT32,
INT16_TO_INT32,
INT32_TO_FLOAT,
INT32_TO_DOUBLE,
PRESERVE_INT8,
PRESERVE_INT16,
UINT8_TO_UINT16,
UINT8_TO_UINT32,
UINT16_TO_UINT32,
FLOAT_TO_INT32,
FLOAT_TO_DOUBLE,
DOUBLE_TO_INT32,
DOUBLE_TO_FLOAT,
INT32_TO_INT64,
INT64_TO_INT32,
PTR_TO_INT32,
INT32_TO_PTR,
PTR_TO_PTR,
ARRAY_TO_PTR,
FUNC_TO_PTR,
COM_INTERFACE,
VARIANT_TO_COM_INTERFACE,
RUNTIME_IMPLEMENTED,
COPY_CONSTRUCTOR
}
public sealed partial class TypeCast : Expr {
private TypeCast(TypeCastType kind, Expr expr, ExprType type, Env env) {
this.Expr = expr;
this.Kind = kind;
this.Type = type;
this.Env = env;
}
public TypeCast(TypeCastType kind, Expr expr, ExprType type)
: this(kind, expr, type, expr.Env) { }
public readonly Expr Expr;
public readonly TypeCastType Kind;
// Note: typecast might introduce environment changes.
public override Env Env { get; }
// A typecast cannot be an lvalue.
// int a;
// (char)a = 'a'; // error: an lvalue is required.
public override Boolean IsLValue => false;
public override ExprType Type { get; }
public static Boolean EqualType(ExprType t1, ExprType t2) {
return t1.EqualType(t2);
}
///
/// From:
/// char, short, long
/// To:
/// char, uchar, short, ushort, long, ulong, float double
///
public static Expr SignedIntegralToArith(Expr expr, ExprType type) {
ExprTypeKind from = expr.Type.Kind;
ExprTypeKind to = type.Kind;
Env env = expr.Env;
switch (from) {
case ExprTypeKind.CHAR:
switch (to) {
case ExprTypeKind.SHORT:
case ExprTypeKind.USHORT:
return new TypeCast(TypeCastType.INT8_TO_INT16, expr, type);
case ExprTypeKind.LONG:
case ExprTypeKind.ULONG:
return new TypeCast(TypeCastType.INT8_TO_INT32, expr, type);
case ExprTypeKind.S64:
case ExprTypeKind.U64:
return new TypeCast(TypeCastType.INT32_TO_INT64, new TypeCast(TypeCastType.INT8_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.UCHAR:
return new TypeCast(TypeCastType.NOP, expr, type);
case ExprTypeKind.FLOAT:
// char -> long -> float
return new TypeCast(TypeCastType.INT32_TO_FLOAT, new TypeCast(TypeCastType.INT8_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.DOUBLE:
// char -> long -> double
return new TypeCast(TypeCastType.INT32_TO_DOUBLE, new TypeCast(TypeCastType.INT8_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.VOID:
case ExprTypeKind.POINTER:
case ExprTypeKind.FUNCTION:
case ExprTypeKind.ARRAY:
case ExprTypeKind.INCOMPLETE_ARRAY:
case ExprTypeKind.STRUCT_OR_UNION:
case ExprTypeKind.CHAR:
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
case ExprTypeKind.SHORT:
switch (to) {
case ExprTypeKind.CHAR:
case ExprTypeKind.UCHAR:
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.USHORT:
return new TypeCast(TypeCastType.NOP, expr, type);
case ExprTypeKind.LONG:
case ExprTypeKind.ULONG:
return new TypeCast(TypeCastType.INT16_TO_INT32, expr, type);
case ExprTypeKind.S64:
case ExprTypeKind.U64:
return new TypeCast(TypeCastType.INT32_TO_INT64, new TypeCast(TypeCastType.INT16_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.FLOAT:
// short -> long -> float
return new TypeCast(TypeCastType.INT32_TO_FLOAT, new TypeCast(TypeCastType.INT16_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.DOUBLE:
// short -> long -> double
return new TypeCast(TypeCastType.INT32_TO_DOUBLE, new TypeCast(TypeCastType.INT16_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.VOID:
case ExprTypeKind.SHORT:
case ExprTypeKind.POINTER:
case ExprTypeKind.FUNCTION:
case ExprTypeKind.ARRAY:
case ExprTypeKind.INCOMPLETE_ARRAY:
case ExprTypeKind.STRUCT_OR_UNION:
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
case ExprTypeKind.LONG:
switch (to) {
case ExprTypeKind.CHAR:
if (expr.IsConstExpr) {
return new ConstChar((SByte)((ConstLong)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.UCHAR:
if (expr.IsConstExpr) {
return new ConstUChar((Byte)((ConstLong)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.SHORT:
if (expr.IsConstExpr) {
return new ConstShort((Int16)((ConstLong)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, expr, type);
case ExprTypeKind.USHORT:
if (expr.IsConstExpr) {
return new ConstUShort((UInt16)((ConstLong)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, expr, type);
case ExprTypeKind.ULONG:
if (expr.IsConstExpr) {
return new ConstULong((UInt32)((ConstLong)expr).Value, env);
}
return new TypeCast(TypeCastType.NOP, expr, type);
case ExprTypeKind.S64:
if (expr.IsConstExpr)
{
return new ConstS64((UInt32)((ConstLong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_INT64, expr, type);
case ExprTypeKind.U64:
if (expr.IsConstExpr)
{
return new ConstU64((UInt32)((ConstLong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_INT64, expr, type);
case ExprTypeKind.FLOAT:
if (expr.IsConstExpr) {
return new ConstFloat(((ConstLong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_FLOAT, expr, type);
case ExprTypeKind.DOUBLE:
if (expr.IsConstExpr) {
return new ConstDouble(((ConstLong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_DOUBLE, expr, type);
case ExprTypeKind.VOID:
case ExprTypeKind.LONG:
case ExprTypeKind.POINTER:
case ExprTypeKind.FUNCTION:
case ExprTypeKind.ARRAY:
case ExprTypeKind.INCOMPLETE_ARRAY:
case ExprTypeKind.STRUCT_OR_UNION:
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
case ExprTypeKind.S64:
switch (to)
{
case ExprTypeKind.CHAR:
if (expr.IsConstExpr)
{
return new ConstChar((SByte)((ConstS64)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.UCHAR:
if (expr.IsConstExpr)
{
return new ConstUChar((Byte)((ConstS64)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.SHORT:
if (expr.IsConstExpr)
{
return new ConstShort((Int16)((ConstS64)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, expr, type);
case ExprTypeKind.USHORT:
if (expr.IsConstExpr)
{
return new ConstUShort((UInt16)((ConstS64)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, expr, type);
case ExprTypeKind.ULONG:
if (expr.IsConstExpr)
{
return new ConstULong((UInt32)((ConstS64)expr).Value, env);
}
return new TypeCast(TypeCastType.INT64_TO_INT32, expr, type);
case ExprTypeKind.U64:
if (expr.IsConstExpr)
{
return new ConstU64((UInt32)((ConstS64)expr).Value, env);
}
return new TypeCast(TypeCastType.NOP, expr, type);
case ExprTypeKind.FLOAT:
if (expr.IsConstExpr)
{
return new ConstFloat(((ConstS64)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_FLOAT, expr, type);
case ExprTypeKind.DOUBLE:
if (expr.IsConstExpr)
{
return new ConstDouble(((ConstS64)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_DOUBLE, expr, type);
case ExprTypeKind.VOID:
case ExprTypeKind.LONG:
case ExprTypeKind.POINTER:
case ExprTypeKind.FUNCTION:
case ExprTypeKind.ARRAY:
case ExprTypeKind.INCOMPLETE_ARRAY:
case ExprTypeKind.STRUCT_OR_UNION:
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
default:
throw new InvalidProgramException().Attach(expr);
}
}
///
/// From:
/// uchar, ushort, ulong
/// To:
/// char, uchar, short, ushort, long, ulong, float, double
///
///
/// Aaccording to MSDN "Conversions from Unsigned Integral Types",
/// unsigned long converts directly to double.
/// However, I just treat unsigned long as long.
///
public static Expr UnsignedIntegralToArith(Expr expr, ExprType type) {
ExprTypeKind from = expr.Type.Kind;
ExprTypeKind to = type.Kind;
Env env = expr.Env;
switch (from) {
case ExprTypeKind.UCHAR:
switch (to) {
case ExprTypeKind.CHAR:
return new TypeCast(TypeCastType.NOP, expr, type);
case ExprTypeKind.SHORT:
case ExprTypeKind.USHORT:
return new TypeCast(TypeCastType.UINT8_TO_UINT16, expr, type);
case ExprTypeKind.LONG:
case ExprTypeKind.ULONG:
return new TypeCast(TypeCastType.UINT8_TO_UINT32, expr, type);
case ExprTypeKind.S64:
case ExprTypeKind.U64:
return new TypeCast(TypeCastType.INT32_TO_INT64, new TypeCast(TypeCastType.UINT8_TO_UINT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.FLOAT:
// uchar -> ulong -> long -> float
return new TypeCast(TypeCastType.INT32_TO_FLOAT, new TypeCast(TypeCastType.UINT8_TO_UINT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.DOUBLE:
// uchar -> ulong -> long -> double
return new TypeCast(TypeCastType.INT32_TO_DOUBLE, new TypeCast(TypeCastType.UINT8_TO_UINT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
case ExprTypeKind.USHORT:
switch (to) {
case ExprTypeKind.CHAR:
case ExprTypeKind.UCHAR:
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.USHORT:
return new TypeCast(TypeCastType.NOP, expr, type);
case ExprTypeKind.LONG:
case ExprTypeKind.ULONG:
return new TypeCast(TypeCastType.UINT16_TO_UINT32, expr, type);
case ExprTypeKind.S64:
case ExprTypeKind.U64:
return new TypeCast(TypeCastType.INT32_TO_INT64, new TypeCast(TypeCastType.UINT8_TO_UINT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.FLOAT:
// ushort -> ulong -> long -> float
return new TypeCast(TypeCastType.INT32_TO_FLOAT, new TypeCast(TypeCastType.UINT16_TO_UINT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.DOUBLE:
// ushort -> ulong -> long -> double
return new TypeCast(TypeCastType.INT32_TO_DOUBLE, new TypeCast(TypeCastType.UINT16_TO_UINT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
case ExprTypeKind.ULONG:
switch (to) {
case ExprTypeKind.CHAR:
if (expr.IsConstExpr) {
return new ConstLong((SByte)((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.UCHAR:
if (expr.IsConstExpr) {
return new ConstULong((Byte)((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.SHORT:
if (expr.IsConstExpr) {
return new ConstLong((Int16)((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, expr, type);
case ExprTypeKind.USHORT:
if (expr.IsConstExpr) {
return new ConstULong((UInt16)((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, expr, type);
case ExprTypeKind.LONG:
if (expr.IsConstExpr) {
return new ConstLong((Int32)((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.NOP, expr, type);
case ExprTypeKind.ULONG:
if (expr.IsConstExpr)
return expr;
return new TypeCast(TypeCastType.NOP, expr, type);
case ExprTypeKind.S64:
if (expr.IsConstExpr)
{
return new ConstS64((UInt32)((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_INT64, expr, type);
case ExprTypeKind.U64:
if (expr.IsConstExpr)
{
return new ConstU64((UInt32)((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_INT64, expr, type);
case ExprTypeKind.FLOAT:
if (expr.IsConstExpr) {
return new ConstFloat(((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_FLOAT, expr, type);
case ExprTypeKind.DOUBLE:
if (expr.IsConstExpr) {
return new ConstDouble(((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_DOUBLE, expr, type);
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
case ExprTypeKind.U64:
switch (to)
{
case ExprTypeKind.CHAR:
if (expr.IsConstExpr)
{
return new ConstLong((SByte)((ConstU64)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.UCHAR:
if (expr.IsConstExpr)
{
return new ConstULong((Byte)((ConstU64)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT8, expr, type);
case ExprTypeKind.SHORT:
if (expr.IsConstExpr)
{
return new ConstLong((Int16)((ConstU64)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, expr, type);
case ExprTypeKind.USHORT:
if (expr.IsConstExpr)
{
return new ConstULong((UInt16)((ConstU64)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, expr, type);
case ExprTypeKind.LONG:
if (expr.IsConstExpr)
{
return new ConstLong((Int32)((ConstU64)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_INT64, expr, type);
case ExprTypeKind.S64:
if (expr.IsConstExpr)
{
return new ConstS64((UInt32)((ConstU64)expr).Value, env);
}
return new TypeCast(TypeCastType.NOP, expr, type);
case ExprTypeKind.FLOAT:
if (expr.IsConstExpr)
{
return new ConstFloat(((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_FLOAT, expr, type);
case ExprTypeKind.DOUBLE:
if (expr.IsConstExpr)
{
return new ConstDouble(((ConstULong)expr).Value, env);
}
return new TypeCast(TypeCastType.INT32_TO_DOUBLE, expr, type);
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
}
///
/// From:
/// float, double
/// To:
/// char, uchar, short, ushort, long, ulong, float, double
///
///
/// According to MSDN "Conversions from Floating-Point Types",
/// float cannot convert to unsigned char.
/// I don't know why, but I follow it.
///
public static Expr FloatToArith(Expr expr, ExprType type) {
ExprTypeKind from = expr.Type.Kind;
ExprTypeKind to = type.Kind;
Env env = expr.Env;
switch (from) {
case ExprTypeKind.FLOAT:
switch (to) {
case ExprTypeKind.CHAR:
if (expr.IsConstExpr) {
return new ConstLong((SByte)((ConstFloat)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT8, new TypeCast(TypeCastType.FLOAT_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.SHORT:
if (expr.IsConstExpr) {
return new ConstLong((Int16)((ConstFloat)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, new TypeCast(TypeCastType.FLOAT_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.USHORT:
if (expr.IsConstExpr) {
return new ConstULong((UInt16)((ConstFloat)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, new TypeCast(TypeCastType.FLOAT_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.LONG:
if (expr.IsConstExpr) {
return new ConstLong((Int32)((ConstFloat)expr).Value, env);
}
return new TypeCast(TypeCastType.FLOAT_TO_INT32, expr, type);
case ExprTypeKind.ULONG:
if (expr.IsConstExpr) {
return new ConstULong((UInt32)((ConstFloat)expr).Value, env);
}
return new TypeCast(TypeCastType.FLOAT_TO_INT32, expr, type);
case ExprTypeKind.S64:
if (expr.IsConstExpr)
{
return new ConstS64((Int32)((ConstFloat)expr).Value, env);
}
return new TypeCast(TypeCastType.FLOAT_TO_INT32, expr, type);
case ExprTypeKind.U64:
if (expr.IsConstExpr)
{
return new ConstU64((UInt32)((ConstFloat)expr).Value, env);
}
return new TypeCast(TypeCastType.FLOAT_TO_INT32, expr, type);
case ExprTypeKind.DOUBLE:
if (expr.IsConstExpr) {
return new ConstDouble(((ConstFloat)expr).Value, env);
}
return new TypeCast(TypeCastType.FLOAT_TO_DOUBLE, expr, type);
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
case ExprTypeKind.DOUBLE:
switch (to) {
case ExprTypeKind.CHAR:
// double -> float -> char
if (expr.IsConstExpr) {
return new ConstLong((SByte)((ConstDouble)expr).Value, env);
}
return FloatToArith(FloatToArith(expr, new FloatType(type.IsConst, type.IsVolatile)), new CharType(type.IsConst, type.IsVolatile));
case ExprTypeKind.SHORT:
// double -> float -> short
if (expr.IsConstExpr) {
return new ConstLong((Int16)((ConstDouble)expr).Value, env);
}
return FloatToArith(FloatToArith(expr, new FloatType(type.IsConst, type.IsVolatile)), new ShortType(type.IsConst, type.IsVolatile));
case ExprTypeKind.LONG:
// double -> float -> short
if (expr.IsConstExpr) {
return new ConstLong((Int32)((ConstDouble)expr).Value, env);
}
return new TypeCast(TypeCastType.DOUBLE_TO_INT32, expr, type);
case ExprTypeKind.ULONG:
if (expr.IsConstExpr) {
return new ConstULong((UInt32)((ConstDouble)expr).Value, env);
}
return new TypeCast(TypeCastType.DOUBLE_TO_INT32, expr, type);
case ExprTypeKind.S64:
// double -> float -> short
if (expr.IsConstExpr)
{
return new ConstS64((Int32)((ConstDouble)expr).Value, env);
}
return new TypeCast(TypeCastType.DOUBLE_TO_INT32, expr, type);
case ExprTypeKind.U64:
if (expr.IsConstExpr)
{
return new ConstU64((UInt32)((ConstDouble)expr).Value, env);
}
return new TypeCast(TypeCastType.DOUBLE_TO_INT32, expr, type);
case ExprTypeKind.USHORT:
// double -> long -> ushort
if (expr.IsConstExpr) {
return new ConstULong((UInt16)((ConstDouble)expr).Value, env);
}
return new TypeCast(TypeCastType.PRESERVE_INT16, new TypeCast(TypeCastType.DOUBLE_TO_INT32, expr, new LongType(type.IsConst, type.IsVolatile)), type);
case ExprTypeKind.FLOAT:
if (expr.IsConstExpr) {
return new ConstFloat((Single)((ConstDouble)expr).Value, env);
}
return new TypeCast(TypeCastType.DOUBLE_TO_FLOAT, expr, type);
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
default:
throw new InvalidProgramException($"Cannot cast from {from} to {to}").Attach(expr);
}
}
///
/// From:
/// pointer
/// To:
/// pointer, integral
///
public static Expr FromPointer(Expr expr, ExprType type, Env env) {
ExprTypeKind from = expr.Type.Kind;
ExprTypeKind to = type.Kind;
if (from != ExprTypeKind.POINTER) {
throw new InvalidOperationException("Expected a pointer.").Attach(expr);
}
bool fromVoid = !((PointerType)expr.Type).IsRef;
// if we are casting to another pointer, do a nop
if (to == ExprTypeKind.POINTER) {
if (expr.IsConstExpr) {
return new ConstPtr(((ConstPtr)expr).Value, type, env);
}
bool toVoid = !((PointerType)type).IsRef;
if (fromVoid && toVoid)
return new TypeCast(TypeCastType.NOP, expr, new ULongType(type.IsConst, type.IsVolatile), env);
if (fromVoid)
return new TypeCast(TypeCastType.INT32_TO_PTR, expr, type, env);
if (toVoid)
return new TypeCast(TypeCastType.PTR_TO_INT32, expr, new ULongType(type.IsConst, type.IsVolatile), env);
return new TypeCast(TypeCastType.PTR_TO_PTR, expr, type, env);
}
// if we are casting to an integral
if (type.IsIntegral) {
// pointer -> ulong -> whatever integral
if (expr.IsConstExpr) {
expr = new ConstULong(((ConstPtr)expr).Value, env);
} else {
if (!((PointerType)expr.Type).IsRef)
expr = new TypeCast(TypeCastType.NOP, expr, new ULongType(type.IsConst, type.IsVolatile), env);
else
expr = new TypeCast(TypeCastType.PTR_TO_INT32, expr, new ULongType(type.IsConst, type.IsVolatile), env);
}
return MakeCast(expr, type, env);
}
throw new InvalidOperationException("Casting from a pointer to an unsupported Type.").Attach(expr);
}
///
/// From:
/// pointer, integral
/// To:
/// pointer
///
public static Expr ToPointer(Expr expr, ExprType type, Env env) {
ExprTypeKind from = expr.Type.Kind;
ExprTypeKind to = type.Kind;
if (to != ExprTypeKind.POINTER) {
throw new InvalidOperationException("Error: expected casting to pointer.").Attach(expr);
}
var toPtr = (PointerType)type;
if (toPtr.IsForOpenArray)
{
// This is for an open array, so cpval will be used here.
// In theory everything is allowed here, but function pointer isn't actually a pointer, so:
if (expr.Type is FunctionType)
{
throw new InvalidOperationException("Function pointer cannot be used in OpenArrayOfConst").Attach(expr);
}
return new TypeCast(TypeCastType.COPY_CONSTRUCTOR, expr, type, env);
}
bool toVoid = !toPtr.IsRef;
if (from == ExprTypeKind.POINTER) {
if (expr.IsConstExpr) {
return new ConstPtr(((ConstPtr)expr).Value, type, env);
}
bool fromVoid = !((PointerType)expr.Type).IsRef;
if (fromVoid && toVoid)
return new TypeCast(TypeCastType.NOP, expr, new ULongType(type.IsConst, type.IsVolatile), env);
if (fromVoid)
return new TypeCast(TypeCastType.INT32_TO_PTR, expr, type, env);
if (toVoid)
return new TypeCast(TypeCastType.PTR_TO_INT32, expr, new ULongType(type.IsConst, type.IsVolatile), env);
return new TypeCast(TypeCastType.PTR_TO_PTR, expr, type, env);
}
if (expr.Type.IsIntegral) {
// if we are casting from an integral
// whatever integral -> ulong
switch (expr.Type.Kind) {
case ExprTypeKind.CHAR:
case ExprTypeKind.SHORT:
case ExprTypeKind.LONG:
expr = SignedIntegralToArith(expr, new ULongType(type.IsConst, type.IsVolatile));
break;
case ExprTypeKind.UCHAR:
case ExprTypeKind.USHORT:
case ExprTypeKind.ULONG:
expr = UnsignedIntegralToArith(expr, new ULongType(type.IsConst, type.IsVolatile));
break;
default:
break;
}
// ulong -> pointer
if (expr.IsConstExpr) {
return new ConstPtr(((ConstULong)expr).Value, type, env);
}
if (!((PointerType)type).IsRef)
return new TypeCast(TypeCastType.NOP, expr, type, env);
return new TypeCast(TypeCastType.INT32_TO_PTR, expr, type, env);
}
if (expr.Type is FunctionType) {
if (!expr.Type.EqualType(((PointerType)type).RefType)) {
throw new InvalidOperationException("Casting from an incompatible function.").Attach(expr);
}
// TODO: only allow compatible Type?
return new TypeCast(TypeCastType.FUNC_TO_PTR, expr, type, env);
}
if (expr.Type is ArrayType) {
// TODO: allow any pointer Type to cast to?
if (!toPtr.IsRef)
return new TypeCast(TypeCastType.PTR_TO_INT32, expr, type, env);
return new TypeCast(TypeCastType.ARRAY_TO_PTR, expr, type, env);
}
throw new InvalidOperationException("Error: casting from an unsupported Type to pointer.").Attach(expr);
}
// MakeCast
// ========
// input: Expr, Type
// output: TypeCast
// converts Expr to Type
//
public static Expr MakeCast(Expr expr, ExprType type, Env env) {
// for an init list, if this is the first cast (will always happen instantly for an rvalue), coerce it to the "correct" type and return
var initList = expr as ExprInitList;
if (initList != null && initList.Type == null)
{
initList.CoerceType(type);
return expr;
}
// if two types are equal, return Expr
if (EqualType(expr.Type, type)) {
return expr;
}
// from pointer
if (expr.Type.Kind == ExprTypeKind.POINTER) {
return FromPointer(expr, type, env);
}
// to pointer
if (type.Kind == ExprTypeKind.POINTER) {
return ToPointer(expr, type, env);
}
// if VARIANT, then we can basically just do a cast to anything and runtime will error if needed
if (expr.Type.Kind == ExprTypeKind.COM_VARIANT || type.Kind == ExprTypeKind.COM_VARIANT)
{
// if one side is interface, then we might need to do a cast.
// cast from variant to idispatch-implemented type?
if (CGenState.TypeImplementsIDispatch(type))
{
var castToDispatch = new TypeCast(TypeCastType.RUNTIME_IMPLEMENTED, expr, CGenState.ExprTypeIDispatch, env);
return new TypeCast(TypeCastType.COM_INTERFACE, castToDispatch, type, env);
}
// cast from idispatch-implemented type to variant?
if (CGenState.TypeImplementsIDispatch(expr.Type))
{
expr = new TypeCast(TypeCastType.COM_INTERFACE, expr, CGenState.ExprTypeIDispatch, env);
}
return new TypeCast(TypeCastType.RUNTIME_IMPLEMENTED, expr, type, env);
}
// if both types are interface, then the runtime function needs to be called for this, which turns into QueryInterface
if (expr.Type.Kind == ExprTypeKind.COM_INTERFACE && type.Kind == ExprTypeKind.COM_INTERFACE)
{
return new TypeCast(TypeCastType.COM_INTERFACE, expr, type, env);
}
switch (expr.Type.Kind) {
// from signed integral
case ExprTypeKind.CHAR:
case ExprTypeKind.SHORT:
case ExprTypeKind.LONG:
case ExprTypeKind.S64:
return SignedIntegralToArith(expr, type);
// from unsigned integral
case ExprTypeKind.UCHAR:
case ExprTypeKind.USHORT:
case ExprTypeKind.ULONG:
case ExprTypeKind.U64:
return UnsignedIntegralToArith(expr, type);
// from float
case ExprTypeKind.FLOAT:
case ExprTypeKind.DOUBLE:
return FloatToArith(expr, type);
case ExprTypeKind.VOID:
case ExprTypeKind.POINTER:
case ExprTypeKind.FUNCTION:
case ExprTypeKind.ARRAY:
case ExprTypeKind.INCOMPLETE_ARRAY:
case ExprTypeKind.STRUCT_OR_UNION:
default:
throw new InvalidOperationException("Error: expression Type not supported for casting from.").Attach(expr);
}
}
public static Expr MakeCast(Expr expr, ExprType type) {
var ret = MakeCast(expr, type, expr.Env);
ret.Copy(expr);
return ret;
}
///
/// Perform the usual arithmetic conversion on two expressions.
/// If either expression is , then both are converted to .
/// Else, if either expression is , then both are converted to .
/// Else, if either expression is , then both are converted to .
/// Else, both are converted to .
///
/// The first expression to be casted. Must have .
/// The second expression to be casted. Must have .
///
/// The two casted expressions, and an .
///
public static Tuple UsualArithmeticConversion(Expr e1, Expr e2) {
ExprType t1 = e1.Type;
ExprType t2 = e2.Type;
Boolean c1 = t1.IsConst;
Boolean v1 = t1.IsVolatile;
Boolean c2 = t2.IsConst;
Boolean v2 = t2.IsVolatile;
// ifps: if either Expr is unicodestring or ansistring, the other must be the same
if (t1.Kind == ExprTypeKind.ANSI_STRING || t1.Kind == ExprTypeKind.UNICODE_STRING)
{
if (t1.Kind != t2.Kind) throw new InvalidOperationException("strings must be of same type");
return new Tuple(e1, e2, t1.Kind);
}
// 1. if either Expr is double: both are converted to double
if (t1.Kind == ExprTypeKind.DOUBLE || t2.Kind == ExprTypeKind.DOUBLE) {
return new Tuple(MakeCast(e1, new DoubleType(c1, v1)), MakeCast(e2, new DoubleType(c2, v2)), ExprTypeKind.DOUBLE);
}
// 2. if either Expr is float: both are converted to float
if (t1.Kind == ExprTypeKind.FLOAT || t2.Kind == ExprTypeKind.FLOAT) {
return new Tuple(MakeCast(e1, new FloatType(c1, v1)), MakeCast(e2, new FloatType(c2, v2)), ExprTypeKind.FLOAT);
}
// 3. if either Expr is unsigned long: both are converted to unsigned long
if (t1.Kind == ExprTypeKind.ULONG || t2.Kind == ExprTypeKind.ULONG) {
return new Tuple(MakeCast(e1, new ULongType(c1, v1)), MakeCast(e2, new ULongType(c2, v2)), ExprTypeKind.ULONG);
}
// if either Expr is u64, convert both to u64
if (t1.Kind == ExprTypeKind.U64 || t2.Kind == ExprTypeKind.U64)
{
return new Tuple(MakeCast(e1, new U64Type(c1, v1)), MakeCast(e2, new U64Type(c2, v2)), ExprTypeKind.U64);
}
// if either Expr is s64, convert both to s64
if (t1.Kind == ExprTypeKind.S64 || t2.Kind == ExprTypeKind.S64)
{
return new Tuple(MakeCast(e1, new S64Type(c1, v1)), MakeCast(e2, new S64Type(c2, v2)), ExprTypeKind.S64);
}
// 4. both are converted to long
return new Tuple(MakeCast(e1, new LongType(c1, v1)), MakeCast(e2, new LongType(c2, v2)), ExprTypeKind.LONG);
}
///
/// First, convert pointers to 's, then do .
///
///
/// The first expression to be casted.
///
///
/// The second expression to be casted.
///
///
/// The two converted expressions and an .
/// Possible return types: , , , .
///
public static Tuple UsualScalarConversion(Expr e1, Expr e2) {
if (e1.Type.Kind == ExprTypeKind.POINTER) {
e1 = FromPointer(e1, new ULongType(e1.Type.IsConst, e1.Type.IsVolatile), e2.Env);
}
if (e2.Type.Kind == ExprTypeKind.POINTER) {
e2 = FromPointer(e2, new ULongType(e2.Type.IsConst, e2.Type.IsVolatile), e2.Env);
}
return UsualArithmeticConversion(e1, e2);
}
///
/// All integrals are converted to or .
///
///
/// The integral expression to be casted.
///
///
/// The casted expression and an .
/// Possible return types: , .
///
public static Tuple IntegralPromotion(Expr expr) {
if (!expr.Type.IsIntegral) {
throw new InvalidProgramException().Attach(expr);
}
switch (expr.Type.Kind) {
case ExprTypeKind.CHAR:
case ExprTypeKind.SHORT:
case ExprTypeKind.LONG:
return Tuple.Create(MakeCast(expr, new LongType(expr.Type.IsConst, expr.Type.IsVolatile)), ExprTypeKind.LONG);
case ExprTypeKind.UCHAR:
case ExprTypeKind.USHORT:
case ExprTypeKind.ULONG:
return Tuple.Create(MakeCast(expr, new ULongType(expr.Type.IsConst, expr.Type.IsVolatile)), ExprTypeKind.ULONG);
case ExprTypeKind.S64:
return Tuple.Create(MakeCast(expr, new S64Type(expr.Type.IsConst, expr.Type.IsVolatile)), ExprTypeKind.S64);
case ExprTypeKind.U64:
return Tuple.Create(MakeCast(expr, new U64Type(expr.Type.IsConst, expr.Type.IsVolatile)), ExprTypeKind.U64);
default:
throw new InvalidProgramException().Attach(expr);
}
}
}
}