#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

316 lines
11 KiB

// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
public class Conversions
{
readonly ITypeResolveContext context;
readonly IType objectType;
public Conversions(ITypeResolveContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
this.objectType = context.GetClass(typeof(object)) ?? SharedTypes.UnknownType;
this.dynamicErasure = new DynamicErasure(this);
}
#region ImplicitConversion
public bool ImplicitConversion(ConstantResolveResult resolveResult, IType toType)
{
if (resolveResult == null)
throw new ArgumentNullException("resolveResult");
ConstantResolveResult crr = resolveResult as ConstantResolveResult;
if (crr != null && (ImplicitEnumerationConversion(crr, toType) || ImplicitConstantExpressionConversion(crr, toType)))
return true;
return ImplicitConversion(resolveResult.Type, toType);
}
public bool ImplicitConversion(IType fromType, IType toType)
{
if (fromType == null)
throw new ArgumentNullException("fromType");
if (toType == null)
throw new ArgumentNullException("toType");
// C# 4.0 spec: §6.1
if (IdentityConversion(fromType, toType))
return true;
if (ImplicitNumericConversion(fromType, toType))
return true;
if (ImplicitReferenceConversion(fromType, toType))
return true;
if (ImplicitNullableConversion(fromType, toType))
return true;
if (NullLiteralConversion(fromType, toType))
return true;
if (BoxingConversion(fromType, toType))
return true;
if (ImplicitDynamicConversion(fromType, toType))
return true;
return false;
}
#endregion
#region IdentityConversion
/// <summary>
/// Gets whether there is an identity conversion from <paramref name="fromType"/> to <paramref name="toType"/>
/// </summary>
bool IdentityConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.1
return fromType.AcceptVisitor(dynamicErasure).Equals(toType.AcceptVisitor(dynamicErasure));
}
readonly DynamicErasure dynamicErasure;
sealed class DynamicErasure : TypeVisitor
{
readonly IType objectType;
public DynamicErasure(Conversions conversions)
{
this.objectType = conversions.objectType;
}
public override IType VisitOtherType(IType type)
{
if (type == SharedTypes.Dynamic)
return objectType;
else
return base.VisitOtherType(type);
}
}
#endregion
#region ImplicitNumericConversion
static readonly bool[,] implicitNumericConversionLookup = {
// to: short ushort int uint long ulong
// from:
/* char */ { false, true , true , true , true , true },
/* sbyte */ { true , false, true , false, true , false },
/* byte */ { true , true , true , true , true , true },
/* short */ { false, false, true , false, true , false },
/* ushort */ { false, false, true , true , true , true },
/* int */ { false, false, false, false, true , false },
/* uint */ { false, false, false, false, true , true },
};
bool ImplicitNumericConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.2
TypeCode from = ReflectionHelper.GetTypeCode(fromType);
TypeCode to = ReflectionHelper.GetTypeCode(toType);
if (to >= TypeCode.Single && to <= TypeCode.Decimal) {
// Conversions to float/double/decimal exist from all integral types,
// and there's a conversion from float to double.
return from >= TypeCode.Char && from <= TypeCode.UInt64
|| from == TypeCode.Single && to == TypeCode.Double;
} else {
// Conversions to integral types: look at the table
return from >= TypeCode.Char && from <= TypeCode.UInt32
&& to >= TypeCode.Int16 && to <= TypeCode.UInt64
&& implicitNumericConversionLookup[to - TypeCode.Int16, from - TypeCode.Char];
}
}
#endregion
#region ImplicitEnumerationConversion
bool ImplicitEnumerationConversion(ConstantResolveResult rr, IType toType)
{
// C# 4.0 spec: §6.1.3
TypeCode constantType = ReflectionHelper.GetTypeCode(rr.Type);
if (constantType >= TypeCode.SByte && constantType <= TypeCode.Decimal && Convert.ToDecimal(rr.Value) == 0) {
toType = UnpackNullable(toType) ?? toType;
ITypeDefinition toDef = toType.GetDefinition();
return toDef.ClassType == ClassType.Enum;
}
return false;
}
#endregion
#region ImplicitNullableConversion
bool ImplicitNullableConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.4
IType t = UnpackNullable(toType);
if (t != null) {
IType s = UnpackNullable(fromType) ?? fromType;
return IdentityConversion(s, t) || ImplicitNumericConversion(s, t);
} else {
return false;
}
}
static IType UnpackNullable(IType type)
{
ParameterizedType pt = type as ParameterizedType;
if (pt != null && pt.TypeArguments.Count == 1 && pt.FullName == "System.Nullable")
return pt.TypeArguments[0];
else
return null;
}
#endregion
#region NullLiteralConversion
bool NullLiteralConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.5
return fromType == SharedTypes.Null && (UnpackNullable(toType) != null);
// This function only handles the conversion from the null literal to nullable value types,
// reference types are handled by ImplicitReferenceConversion instead.
}
#endregion
#region ImplicitReferenceConversion
bool ImplicitReferenceConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.6
// reference conversions are possible only if both types are known to be reference types
if (fromType.IsReferenceType != true || toType.IsReferenceType != true)
return false;
// conversion from null literal is always possible
if (fromType == SharedTypes.Null)
return true;
ArrayType fromArray = fromType as ArrayType;
if (fromArray != null) {
ArrayType toArray = toType as ArrayType;
if (toArray != null) {
// array covariance (the broken kind)
return fromArray.Dimensions == toArray.Dimensions
&& ImplicitReferenceConversion(fromArray.ElementType, toArray.ElementType);
}
// conversion from single-dimensional array S[] to IList<T>:
ParameterizedType toPT = toType as ParameterizedType;
if (fromArray.Dimensions == 1 && toPT != null && toPT.TypeArguments.Count == 1
&& toPT.Namespace == "System.Collections.Generic"
&& (toPT.Name == "IList" || toPT.Name == "ICollection" || toPT.Name == "IEnumerable"))
{
// array covariance plays a part here as well (string[] is IList<object>)
return IdentityConversion(fromArray.ElementType, toPT.TypeArguments[0])
|| ImplicitReferenceConversion(fromArray.ElementType, toPT.TypeArguments[0]);
}
// conversion from any array to System.Array and the interfaces it implements:
ITypeDefinition systemArray = context.GetClass("System.Array", 0, StringComparer.Ordinal);
return systemArray != null && (systemArray.Equals(toType) || ImplicitReferenceConversion(systemArray, toType));
}
// now comes the hard part: traverse the inheritance chain and figure out generics+variance
return IsSubtypeOf(fromType, toType);
}
// Determines whether s is a subtype of t.
// Helper method used for ImplicitReferenceConversion and BoxingConversion
bool IsSubtypeOf(IType s, IType t)
{
// conversion to dynamic + object are always possible
if (t == SharedTypes.Dynamic || t.Equals(objectType))
return true;
// let GetAllBaseTypes do the work for us
foreach (IType baseType in s.GetAllBaseTypes(context)) {
if (IdentityOrVarianceConversion(baseType, t))
return true;
}
return false;
}
bool IdentityOrVarianceConversion(IType s, IType t)
{
ITypeDefinition def = s.GetDefinition();
if (def != null && def.Equals(t.GetDefinition())) {
ParameterizedType ps = s as ParameterizedType;
ParameterizedType pt = t as ParameterizedType;
if (ps != null && pt != null
&& ps.TypeArguments.Count == pt.TypeArguments.Count
&& ps.TypeArguments.Count == def.TypeParameters.Count)
{
// C# 4.0 spec: §13.1.3.2 Variance Conversion
for (int i = 0; i < def.TypeParameters.Count; i++) {
IType si = ps.TypeArguments[i];
IType ti = pt.TypeArguments[i];
if (IdentityConversion(si, ti))
continue;
ITypeParameter xi = def.TypeParameters[i];
switch (xi.Variance) {
case VarianceModifier.Covariant:
if (!ImplicitReferenceConversion(si, ti))
return false;
break;
case VarianceModifier.Contravariant:
if (!ImplicitReferenceConversion(ti, si))
return false;
break;
default:
return false;
}
}
} else if (ps != null || pt != null) {
return false; // only of of them is parameterized, or counts don't match? -> not valid conversion
}
return true;
}
return false;
}
#endregion
#region BoxingConversion
bool BoxingConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.7
fromType = UnpackNullable(fromType) ?? fromType;
return fromType.IsReferenceType == false && toType.IsReferenceType == true && IsSubtypeOf(fromType, toType);
}
#endregion
#region ImplicitDynamicConversion
bool ImplicitDynamicConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.8
return fromType == SharedTypes.Dynamic;
}
#endregion
#region ImplicitConstantExpressionConversion
bool ImplicitConstantExpressionConversion(ConstantResolveResult rr, IType toType)
{
// C# 4.0 spec: §6.1.9
TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type);
TypeCode toTypeCode = ReflectionHelper.GetTypeCode(UnpackNullable(toType) ?? toType);
if (fromTypeCode == TypeCode.Int64) {
long val = (long)rr.Value;
return val >= 0 && toTypeCode == TypeCode.UInt64;
} else if (fromTypeCode == TypeCode.Int32) {
int val = (int)rr.Value;
switch (toTypeCode) {
case TypeCode.SByte:
return val >= SByte.MinValue && val <= SByte.MaxValue;
case TypeCode.Byte:
return val >= Byte.MinValue && val <= Byte.MaxValue;
case TypeCode.Int16:
return val >= Int16.MinValue && val <= Int16.MaxValue;
case TypeCode.UInt16:
return val >= UInt16.MinValue && val <= UInt16.MaxValue;
case TypeCode.UInt32:
return val >= 0;
case TypeCode.UInt64:
return val >= 0;
}
}
return false;
}
#endregion
// TODO: add support for user-defined conversions
}
}