Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ public static bool IsConvertible(this Type type)
case TypeCode.Char:
return true;
default:
return false;
return type == typeof(IntPtr) || type == typeof(UIntPtr);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,21 @@ private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Ty
il.Emit(OpCodes.Conv_Ovf_U8_Un);
break;
default:
if (typeTo.IsEnum)
{
Type underlyingType = Enum.GetUnderlyingType(typeTo);
if (underlyingType == typeof(IntPtr))
{
il.Emit(OpCodes.Conv_Ovf_I_Un);
break;
}
else
{
Debug.Assert(underlyingType == typeof(UIntPtr));
il.Emit(OpCodes.Conv_Ovf_U_Un);
break;
}
}
throw Error.UnhandledConvert(typeTo);
}
}
Expand Down Expand Up @@ -900,6 +915,21 @@ private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Ty
il.Emit(OpCodes.Conv_Ovf_U8);
break;
default:
if (typeTo.IsEnum)
{
Type underlyingType = Enum.GetUnderlyingType(typeTo);
if (underlyingType == typeof(IntPtr))
{
il.Emit(OpCodes.Conv_Ovf_I);
break;
}
else
{
Debug.Assert(underlyingType == typeof(UIntPtr));
il.Emit(OpCodes.Conv_Ovf_U);
break;
}
}
throw Error.UnhandledConvert(typeTo);
}
}
Expand Down Expand Up @@ -952,6 +982,21 @@ private static void EmitNumericConversion(this ILGenerator il, Type typeFrom, Ty
il.Emit(OpCodes.Cgt_Un);
break;
default:
if (typeTo.IsEnum)
{
Type underlyingType = Enum.GetUnderlyingType(typeTo);
if (underlyingType == typeof(IntPtr))
{
il.Emit(OpCodes.Conv_I);
break;
}
else
{
Debug.Assert(underlyingType == typeof(UIntPtr));
il.Emit(OpCodes.Conv_U);
break;
}
}
throw Error.UnhandledConvert(typeTo);
}
}
Expand Down Expand Up @@ -1195,9 +1240,23 @@ internal static void EmitDefault(this ILGenerator il, Type type)
case TypeCode.DateTime:
if (type.GetTypeInfo().IsValueType)
{
// Type.GetTypeCode on an enum returns the underlying
// integer TypeCode, so we won't get here.
Debug.Assert(!type.GetTypeInfo().IsEnum);
if (type.GetTypeInfo().IsEnum)
{
Type underlyingType = Enum.GetUnderlyingType(type);
if (underlyingType == typeof(IntPtr))
{
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Conv_I);
break;
}
else
{
Debug.Assert(underlyingType == typeof(UIntPtr));
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Conv_U);
break;
}
}

// This is the IL for default(T) if T is a generic type
// parameter, so it should work for any type. It's also
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ namespace System.Linq.Expressions.Interpreter
internal abstract class EqualInstruction : Instruction
{
// Perf: EqualityComparer<T> but is 3/2 to 2 times slower.
private static Instruction s_reference, s_boolean, s_SByte, s_int16, s_char, s_int32, s_int64, s_byte, s_UInt16, s_UInt32, s_UInt64, s_single, s_double;
private static Instruction s_referenceLiftedToNull, s_booleanLiftedToNull, s_SByteLiftedToNull, s_int16LiftedToNull, s_charLiftedToNull, s_int32LiftedToNull, s_int64LiftedToNull, s_byteLiftedToNull, s_UInt16LiftedToNull, s_UInt32LiftedToNull, s_UInt64LiftedToNull, s_singleLiftedToNull, s_doubleLiftedToNull;
private static Instruction s_reference, s_boolean, s_SByte, s_int16, s_char, s_int32, s_int64, s_byte, s_UInt16, s_UInt32, s_UInt64, s_single, s_double, s_intPtr, s_uintPtr;
private static Instruction s_referenceLiftedToNull, s_booleanLiftedToNull, s_SByteLiftedToNull, s_int16LiftedToNull, s_charLiftedToNull, s_int32LiftedToNull, s_int64LiftedToNull, s_byteLiftedToNull, s_UInt16LiftedToNull, s_UInt32LiftedToNull, s_UInt64LiftedToNull, s_singleLiftedToNull, s_doubleLiftedToNull, s_intPtrLiftedToNull, s_uintPtrLiftedToNull;

public override int ConsumedStack => 2;
public override int ProducedStack => 1;
Expand Down Expand Up @@ -283,6 +283,50 @@ public override int Run(InterpretedFrame frame)
}
}

private sealed class EqualIntPtr : EqualInstruction
{
public override int Run(InterpretedFrame frame)
{
object right = frame.Pop();
object left = frame.Pop();
if (left == null)
{
frame.Push(right == null);
}
else if (right == null)
{
frame.Push(false);
}
else
{
frame.Push((IntPtr)left == (IntPtr)right);
}
return +1;
}
}

private sealed class EqualUIntPtr : EqualInstruction
{
public override int Run(InterpretedFrame frame)
{
object right = frame.Pop();
object left = frame.Pop();
if (left == null)
{
frame.Push(right == null);
}
else if (right == null)
{
frame.Push(false);
}
else
{
frame.Push((UIntPtr)left == (UIntPtr)right);
}
return +1;
}
}

private sealed class EqualReference : EqualInstruction
{
public override int Run(InterpretedFrame frame)
Expand Down Expand Up @@ -508,6 +552,41 @@ public override int Run(InterpretedFrame frame)
}
}

private sealed class EqualIntPtrLiftedToNull : EqualInstruction
{
public override int Run(InterpretedFrame frame)
{
object right = frame.Pop();
object left = frame.Pop();
if (left == null || right == null)
{
frame.Push(null);
}
else
{
frame.Push((IntPtr)left == (IntPtr)right);
}
return +1;
}
}

private sealed class EqualUIntPtrLiftedToNull : EqualInstruction
{
public override int Run(InterpretedFrame frame)
{
object right = frame.Pop();
object left = frame.Pop();
if (left == null || right == null)
{
frame.Push(null);
}
else
{
frame.Push((UIntPtr)left == (UIntPtr)right);
}
return +1;
}
}

private sealed class EqualReferenceLiftedToNull : EqualInstruction
{
Expand Down Expand Up @@ -559,6 +638,15 @@ public static Instruction Create(Type type, bool liftedToNull)
{
return s_referenceLiftedToNull ?? (s_referenceLiftedToNull = new EqualReferenceLiftedToNull());
}
else if (underlyingType == typeof(IntPtr))
{
return s_intPtrLiftedToNull ?? (s_intPtrLiftedToNull = new EqualIntPtrLiftedToNull());
}
else if (underlyingType == typeof(UIntPtr))
{
return s_uintPtrLiftedToNull ?? (s_uintPtrLiftedToNull = new EqualUIntPtrLiftedToNull());
}

// TODO: Nullable<T>
throw Error.ExpressionNotSupportedForNullableType("Equal", type);

Expand Down Expand Up @@ -591,6 +679,15 @@ public static Instruction Create(Type type, bool liftedToNull)
{
return s_reference ?? (s_reference = new EqualReference());
}
else if (underlyingType == typeof(IntPtr))
{
return s_intPtr ?? (s_intPtr = new EqualIntPtr());
}
else if(underlyingType == typeof(UIntPtr))
{
return s_uintPtr ?? (s_uintPtr = new EqualUIntPtr());
}

// TODO: Nullable<T>
throw Error.ExpressionNotSupportedForNullableType("Equal", type);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,12 +493,22 @@ public override int Run(InterpretedFrame frame)
// Disallowed in C#, but allowed in CIL
frame.Push(Enum.ToObject(_t, (char)from));
}
else if (underlying == typeof(bool))
{
// Disallowed in C#, but allowed in CIL
frame.Push(Enum.ToObject(_t, (bool)from));
}
else if (underlying == typeof(IntPtr))
{
// Disallowed in C#, but allowed in CIL
frame.Push(Enum.ToObject(_t, ((IntPtr)from).ToInt64()));
}
else
{
// Only remaining possible type.
// Disallowed in C#, but allowed in CIL
Debug.Assert(underlying == typeof(bool));
frame.Push(Enum.ToObject(_t, (bool)from));
Debug.Assert(underlying == typeof(UIntPtr));
frame.Push(Enum.ToObject(_t, ((UIntPtr)from).ToUInt64()));
}

return 1;
Expand Down
2 changes: 2 additions & 0 deletions src/System.Linq.Expressions/tests/Cast/CastTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2376,6 +2376,8 @@ public static IEnumerable<Type> EnumerableTypes()
yield return typeof(UInt64Enum);
yield return NonCSharpTypes.CharEnumType;
yield return NonCSharpTypes.BoolEnumType;
yield return NonCSharpTypes.IntPtrEnumType;
yield return NonCSharpTypes.UIntPtrEnumType;
}

public static IEnumerable<object[]> EnumerableTypeArgs() => EnumerableTypes().Select(t => new object[] {t});
Expand Down
46 changes: 38 additions & 8 deletions src/System.Linq.Expressions/tests/HelperTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -338,19 +338,21 @@ IEnumerator IEnumerable.GetEnumerator()
}
}

public enum ByteEnum : byte { A = Byte.MaxValue }
public enum SByteEnum : sbyte { A = SByte.MaxValue }
public enum Int16Enum : short { A = Int16.MaxValue }
public enum UInt16Enum : ushort { A = UInt16.MaxValue }
public enum Int32Enum : int { A = Int32.MaxValue }
public enum UInt32Enum : uint { A = UInt32.MaxValue }
public enum Int64Enum : long { A = Int64.MaxValue }
public enum UInt64Enum : ulong { A = UInt64.MaxValue }
public enum ByteEnum : byte { A = byte.MaxValue }
public enum SByteEnum : sbyte { A = sbyte.MaxValue }
public enum Int16Enum : short { A = short.MaxValue }
public enum UInt16Enum : ushort { A = ushort.MaxValue }
public enum Int32Enum : int { A = int.MaxValue }
public enum UInt32Enum : uint { A = uint.MaxValue }
public enum Int64Enum : long { A = long.MaxValue }
public enum UInt64Enum : ulong { A = ulong.MaxValue }

public static class NonCSharpTypes
{
private static Type _charEnumType;
private static Type _boolEnumType;
private static Type _intPtrEnumType;
private static Type _uintPtrEnumType;

private static ModuleBuilder GetModuleBuilder()
{
Expand Down Expand Up @@ -391,6 +393,34 @@ public static Type BoolEnumType
return _boolEnumType;
}
}

public static Type IntPtrEnumType
{
get
{
if (_intPtrEnumType == null)
{
EnumBuilder eb = GetModuleBuilder().DefineEnum("IntPtrEnumType", TypeAttributes.Public, typeof(IntPtr));
_intPtrEnumType = eb.CreateTypeInfo().AsType();
}

return _intPtrEnumType;
}
}

public static Type UIntPtrEnumType
{
get
{
if (_uintPtrEnumType == null)
{
EnumBuilder eb = GetModuleBuilder().DefineEnum("UIntPtrEnumType", TypeAttributes.Public, typeof(UIntPtr));
_uintPtrEnumType = eb.CreateTypeInfo().AsType();
}

return _uintPtrEnumType;
}
}
}

public class FakeExpression : Expression
Expand Down