// the sections mentioned in comments of this method are from C# specification v1.2 public static Conversion GetImplicit(Operand op, Type to, bool onlyStandard, ITypeMapper typeMapper) { Type from = Operand.GetType(op, typeMapper); Type toUnderlying = Helpers.GetNullableUnderlyingType(to); if (to.Equals(from)) { return(new Direct(typeMapper)); } Type fromUnderlying = Helpers.GetNullableUnderlyingType(@from); if (toUnderlying != null) { if (fromUnderlying != null) { Conversion c = GetImplicit(new FakeTypedOperand(fromUnderlying), toUnderlying, onlyStandard, typeMapper); if (c.IsValid) { return(new ConvertNullable(typeMapper, c)); } } else { Conversion c = GetImplicit(op, toUnderlying, onlyStandard, typeMapper); if (c.IsValid) { return(new WrapNullable(typeMapper, c)); } } } // required for arrays created from TypeBuilder-s if (from != null && to.IsArray && from.IsArray) { if (to.GetArrayRank() == from.GetArrayRank()) { if (to.GetElementType().Equals(from.GetElementType())) { return(new Direct(typeMapper)); } } } TypeCode tcFrom = Type.GetTypeCode(from); TypeCode tcTo = Type.GetTypeCode(to); byte ct = _convTable[(int)tcFrom][(int)tcTo]; // section 6.1.2 - Implicit numeric conversions if (from != null && (from.IsPrimitive || Helpers.AreTypesEqual(from, typeof(decimal), typeMapper)) && (to.IsPrimitive || Helpers.AreTypesEqual(to, typeof(decimal), typeMapper))) { if (ct <= I) { if (Helpers.AreTypesEqual(from, typeof(decimal), typeMapper) || Helpers.AreTypesEqual(to, typeof(decimal), typeMapper)) { // decimal is handled as user-defined conversion, but as it is a standard one, always enable UDC processing onlyStandard = false; } else { return(new Primitive(typeMapper)); } } } IntLiteral intLit = op as IntLiteral; // section 6.1.3 - Implicit enumeration conversions if (!onlyStandard && to.IsEnum && (object)intLit != null && intLit.Value == 0) { return(new Primitive(typeMapper)); } // section 6.1.4 - Implicit reference conversions if ((from == null || !from.IsValueType) && !to.IsValueType) { if (from == null) // from the null type to any reference type { return(new Direct(typeMapper)); } if (to.IsAssignableFrom(from)) // the rest { return(new Direct(typeMapper)); } } if (from == null) // no other conversion from null type is possible { return(new Invalid(typeMapper)); } // section 6.1.5 - Boxing conversions if (from.IsValueType) { if (to.IsAssignableFrom(from)) { return(new Boxing(typeMapper)); } } // section 6.1.6 - Implicit constant expression conversions if ((object)intLit != null && Helpers.AreTypesEqual(from, typeof(int), typeMapper) && to.IsPrimitive) { int val = intLit.Value; switch (tcTo) { case TypeCode.SByte: if (val >= sbyte.MinValue && val <= sbyte.MaxValue) { return(new Direct(typeMapper)); } break; case TypeCode.Byte: if (val >= byte.MinValue && val <= byte.MaxValue) { return(new Direct(typeMapper)); } break; case TypeCode.Int16: if (val >= short.MinValue && val <= short.MaxValue) { return(new Direct(typeMapper)); } break; case TypeCode.UInt16: if (val >= ushort.MinValue && val <= ushort.MaxValue) { return(new Direct(typeMapper)); } break; case TypeCode.UInt32: if (val >= 0) { return(new Direct(typeMapper)); } break; case TypeCode.UInt64: if (val >= 0) { return(new Direct(typeMapper)); } break; } } // section 6.1.7 - User-defined implicit conversions (details in section 6.4.3) if (onlyStandard || Helpers.AreTypesEqual(from, typeof(object), typeMapper) || Helpers.AreTypesEqual(to, typeof(object), typeMapper) || from.IsInterface || to.IsInterface || to.IsSubclassOf(from) || from.IsSubclassOf(to)) { return(new Invalid(typeMapper)); // skip not-permitted conversion attempts (section 6.4.1) } List <UserDefined> candidates = null; FindCandidates(ref candidates, FindImplicitMethods(from, to, typeMapper), op, to, GetImplicit, typeMapper); if (candidates == null) { return(new Invalid(typeMapper)); } if (candidates.Count == 1) { return(candidates[0]); } return(UserDefined.FindImplicit(candidates, @from, to, typeMapper)); }
// the sections mentioned in comments of this method are from C# specification v1.2 public static Conversion GetImplicit(Operand op, Type to, bool onlyStandard) { try { Type from = Operand.GetType(op); if (to.Equals(from) || to.FullName == from.FullName || to.IsGenericParameter) { return(Direct.Instance); } // required for arrays created from TypeBuilder-s if (from != null && to.IsArray && from.IsArray) { if (to.GetArrayRank() == from.GetArrayRank()) { if (to.GetElementType().Equals(from.GetElementType())) { return(Direct.Instance); } } } TypeCode tcFrom = Type.GetTypeCode(from); TypeCode tcTo = Type.GetTypeCode(to); byte ct = convTable[(int)tcFrom][(int)tcTo]; // section 6.1.2 - Implicit numeric conversions if ((from != null && (from.IsPrimitive || from == typeof(decimal))) && (to.IsPrimitive || to == typeof(decimal))) { if (ct <= I) { if (from == typeof(decimal) || to == typeof(decimal)) { // decimal is handled as user-defined conversion, but as it is a standard one, always enable UDC processing onlyStandard = false; } else { return(Primitive.Instance); } } } IntLiteral intLit = op as IntLiteral; // section 6.1.3 - Implicit enumeration conversions if (!onlyStandard && !to.IsGenericType && to.IsEnum && (object)intLit != null && intLit.Value == 0) { return(Primitive.Instance); } // section 6.1.4 - Implicit reference conversions if ((from == null || !from.IsValueType) && !to.IsValueType) { if (from == null) // from the null type to any reference type { return(Direct.Instance); } if (to.IsAssignableFrom(from) || to.IsDerivedFrom(from)) // the rest { return(Direct.Instance); } if (from.Name.StartsWith("_Frame")) { return(Direct.Instance); } if (to == typeof(Operand)) { return(UserDefined.Cast.Instance); } } if (from == null) // no other conversion from null type is possible { return(Invalid.Instance); } // section 6.1.5 - Boxing conversions if (from.IsValueType) { if (to.IsAssignableFrom(from)) { return(Boxing.Instance); } } // section 6.1.6 - Implicit constant expression conversions if ((object)intLit != null && from == typeof(int) && to.IsPrimitive) { int val = intLit.Value; switch (tcTo) { case TypeCode.SByte: if (val >= sbyte.MinValue && val <= sbyte.MaxValue) { return(Direct.Instance); } break; case TypeCode.Byte: if (val >= byte.MinValue && val <= byte.MaxValue) { return(Direct.Instance); } break; case TypeCode.Int16: if (val >= short.MinValue && val <= short.MaxValue) { return(Direct.Instance); } break; case TypeCode.UInt16: if (val >= ushort.MinValue && val <= ushort.MaxValue) { return(Direct.Instance); } break; case TypeCode.UInt32: if (val >= 0) { return(Direct.Instance); } break; case TypeCode.UInt64: if (val >= 0) { return(Primitive.Instance); } break; } } if (from == typeof(long)) { LongLiteral longLit = op as LongLiteral; if ((object)longLit != null && longLit.Value > 0) { return(Direct.Instance); } } // section 6.1.7 - User-defined implicit conversions (details in section 6.4.3) if (onlyStandard || from == typeof(object) || to == typeof(object) || from.IsInterface || to.IsInterface || to.IsSubclassOf(from) || from.IsSubclassOf(to)) { return(Invalid.Instance); // skip not-permitted conversion attempts (section 6.4.1) } List <UserDefined> candidates = null; FindCandidates(ref candidates, FindImplicitMethods(from, to), op, to, GetImplicit); if (candidates == null) { return(Invalid.Instance); } if (candidates.Count == 1) { return(candidates[0]); } return(UserDefined.FindImplicit(candidates, from, to)); } catch (NotSupportedException e) { return(Direct.Instance); // fallback to success :) } }