11 changed files with 312 additions and 32 deletions
@ -0,0 +1,264 @@ |
|||||||
|
// 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; |
||||||
|
} |
||||||
|
|
||||||
|
#region ImplicitConversion
|
||||||
|
public bool ImplicitConversion(IType fromType, IType toType) |
||||||
|
{ |
||||||
|
// C# 4.0 spec: §6.1
|
||||||
|
return IdentityConversion(fromType, toType) |
||||||
|
|| ImplicitNumericConversion(fromType, toType) |
||||||
|
|| ImplicitEnumerationConversion(fromType, toType) |
||||||
|
|| ImplicitNullableConversion(fromType, toType) |
||||||
|
|| NullLiteralConversion(fromType, toType) |
||||||
|
|| ImplicitReferenceConversion(fromType, toType) |
||||||
|
|| BoxingConversion(fromType, toType) |
||||||
|
|| ImplicitConstantExpressionConversion(fromType, toType); |
||||||
|
} |
||||||
|
#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
|
||||||
|
DynamicErasure erasure = new DynamicErasure(this); |
||||||
|
return fromType.AcceptVisitor(erasure).Equals(toType.AcceptVisitor(erasure)); |
||||||
|
} |
||||||
|
|
||||||
|
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(IType fromType, IType toType) |
||||||
|
{ |
||||||
|
// C# 4.0 spec: §6.1.3
|
||||||
|
// TODO: implement ImplicitEnumerationConversion and ImplicitConstantExpressionConversion
|
||||||
|
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 == "List" || toPT.Name == "Collection" || 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; |
||||||
|
|
||||||
|
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) |
||||||
|
{ |
||||||
|
// §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; |
||||||
|
} |
||||||
|
} |
||||||
|
} 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(IType fromType, IType toType) |
||||||
|
{ |
||||||
|
// C# 4.0 spec: §6.1.9
|
||||||
|
// TODO: implement ImplicitEnumerationConversion and ImplicitConstantExpressionConversion
|
||||||
|
return false; |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
// TODO: add support for user-defined conversions
|
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue