Browse Source

Added implicit conversion logic.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
26cc23846c
  1. 2
      ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs
  2. 264
      ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
  3. 2
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  4. 11
      ICSharpCode.NRefactory/TypeSystem/ArrayType.cs
  5. 1
      ICSharpCode.NRefactory/TypeSystem/ByReferenceType.cs
  6. 3
      ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs
  7. 16
      ICSharpCode.NRefactory/TypeSystem/IType.cs
  8. 5
      ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs
  9. 5
      ICSharpCode.NRefactory/TypeSystem/Implementation/TypeWithElementType.cs
  10. 1
      ICSharpCode.NRefactory/TypeSystem/PointerType.cs
  11. 34
      ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs

2
ICSharpCode.NRefactory.Tests/TypeSystem/CecilLoaderTests.cs

@ -60,7 +60,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -60,7 +60,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
IMethod toPointer = c.Methods.Single(p => p.Name == "ToPointer");
Assert.AreEqual("System.Void*", toPointer.ReturnType.Resolve(ctx).DotNetName);
Assert.IsInstanceOf(typeof(PointerType), toPointer.ReturnType.Resolve(ctx));
Assert.AreEqual("System.Void", toPointer.ReturnType.Resolve(ctx).GetElementType().FullName);
Assert.AreEqual("System.Void", ((PointerType)toPointer.ReturnType.Resolve(ctx)).ElementType.FullName);
}
[Test]

264
ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs

@ -0,0 +1,264 @@ @@ -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
}
}

2
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -139,6 +139,7 @@ @@ -139,6 +139,7 @@
<Compile Include="CSharp\Parser\mcs\MonoSymbolWriter.cs" />
<Compile Include="CSharp\Parser\mcs\outline.cs" />
<Compile Include="CSharp\Parser\mcs\roottypes.cs" />
<Compile Include="CSharp\Resolver\Conversions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TypeSystem\Accessibility.cs" />
<Compile Include="TypeSystem\AmbiguousType.cs" />
@ -255,6 +256,7 @@ @@ -255,6 +256,7 @@
<Compile Include="Util\TreeTraversal.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="CSharp\Resolver" />
<Folder Include="Util" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />

11
ICSharpCode.NRefactory/TypeSystem/ArrayType.cs

@ -40,7 +40,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -40,7 +40,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
public override int GetHashCode()
{
return unchecked(GetElementType().GetHashCode() * 71681 + dimensions);
return unchecked(elementType.GetHashCode() * 71681 + dimensions);
}
public override bool Equals(IType other)
@ -55,9 +55,11 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -55,9 +55,11 @@ namespace ICSharpCode.NRefactory.TypeSystem
IType t = context.GetClass(typeof(Array));
if (t != null)
baseTypes.Add(t);
t = context.GetClass(typeof(List<>));
if (t != null)
baseTypes.Add(t);
if (dimensions == 1) { // single-dimensional arrays implement IList<T>
t = context.GetClass(typeof(IList<>));
if (t != null)
baseTypes.Add(t);
}
return baseTypes;
}
@ -68,7 +70,6 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -68,7 +70,6 @@ namespace ICSharpCode.NRefactory.TypeSystem
public override IType VisitChildren(TypeVisitor visitor)
{
IType elementType = GetElementType();
IType e = elementType.AcceptVisitor(visitor);
if (e == elementType)
return this;

1
ICSharpCode.NRefactory/TypeSystem/ByReferenceType.cs

@ -38,7 +38,6 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -38,7 +38,6 @@ namespace ICSharpCode.NRefactory.TypeSystem
public override IType VisitChildren(TypeVisitor visitor)
{
IType elementType = GetElementType();
IType e = elementType.AcceptVisitor(visitor);
if (e == elementType)
return this;

3
ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs

@ -17,7 +17,8 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -17,7 +17,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Gets all base types.
/// </summary>
/// <remarks>This is the reflexive and transitive closure of <see cref="IType.GetBaseTypes"/>.
/// Note that this method does not return all supertypes - doing so is impossible due to contravariance.
/// Note that this method does not return all supertypes - doing so is impossible due to contravariance
/// (and underisable for covariance and the list could become very large).
/// This method may return an infinite list for certain (invalid) class declarations like <c>class C{T} : C{C{T}}</c>
/// TODO: we could ensure a finite list by filtering out cyclic inheritance
/// </remarks>

16
ICSharpCode.NRefactory/TypeSystem/IType.cs

@ -20,17 +20,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -20,17 +20,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </returns>
bool? IsReferenceType { get; }
/// <summary>
/// Gets the element type of array or pointer types.
/// </summary>
/// <exception cref="InvalidOperationException">
/// This type is not an array or pointer type.
/// </exception>
IType GetElementType();
/// <summary>
/// Gets the underlying type definition.
/// Can return null for types which do not have a type definition (for example type parameters)
/// Can return null for types which do not have a type definition (for example arrays, pointers, type parameters)
/// </summary>
ITypeDefinition GetDefinition();
@ -193,12 +185,6 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -193,12 +185,6 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
}
IType IType.GetElementType()
{
Contract.Ensures(Contract.Result<IType>() != null);
return null;
}
ITypeDefinition IType.GetDefinition()
{
return null;

5
ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractType.cs

@ -46,11 +46,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -46,11 +46,6 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
get { return null; }
}
public virtual IType GetElementType()
{
throw new InvalidOperationException();
}
public virtual ITypeDefinition GetDefinition()
{
return null;

5
ICSharpCode.NRefactory/TypeSystem/Implementation/TypeWithElementType.cs

@ -34,9 +34,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation @@ -34,9 +34,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation
public abstract string NameSuffix { get; }
public override IType GetElementType()
{
return elementType;
public IType ElementType {
get { return elementType; }
}
// Force concrete implementations to override VisitChildren - the base implementation

1
ICSharpCode.NRefactory/TypeSystem/PointerType.cs

@ -40,7 +40,6 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -40,7 +40,6 @@ namespace ICSharpCode.NRefactory.TypeSystem
public override IType VisitChildren(TypeVisitor visitor)
{
IType elementType = GetElementType();
IType e = elementType.AcceptVisitor(visitor);
if (e == elementType)
return this;

34
ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.TypeSystem
@ -131,5 +132,38 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -131,5 +132,38 @@ namespace ICSharpCode.NRefactory.TypeSystem
return reflectionName;
}
}
static readonly Dictionary<string, TypeCode> typeNameToCodeDict = new Dictionary<string, TypeCode> {
{ typeof(Boolean).FullName, TypeCode.Boolean },
{ typeof(Byte).FullName, TypeCode.Byte },
{ typeof(Char).FullName, TypeCode.Char },
{ typeof(DateTime).FullName, TypeCode.DateTime },
{ typeof(DBNull).FullName, TypeCode.DBNull },
{ typeof(Decimal).FullName, TypeCode.Decimal },
{ typeof(Double).FullName, TypeCode.Double },
{ typeof(Int16).FullName, TypeCode.Int16 },
{ typeof(Int32).FullName, TypeCode.Int32 },
{ typeof(Int64).FullName, TypeCode.Int64 },
{ typeof(Object).FullName, TypeCode.Object },
{ typeof(SByte).FullName, TypeCode.SByte },
{ typeof(Single).FullName, TypeCode.Single },
{ typeof(String).FullName, TypeCode.String },
{ typeof(UInt16).FullName, TypeCode.UInt16 },
{ typeof(UInt32).FullName, TypeCode.UInt32 },
{ typeof(UInt64).FullName, TypeCode.UInt64 }
};
/// <summary>
/// Gets the type code for the specified type, or TypeCode.Empty if none of the other type codes matches.
/// </summary>
public static TypeCode GetTypeCode(IType type)
{
ITypeDefinition def = type as ITypeDefinition;
TypeCode typeCode;
if (def != null && def.TypeParameterCount == 0 && typeNameToCodeDict.TryGetValue(def.FullName, out typeCode))
return typeCode;
else
return TypeCode.Empty;
}
}
}

Loading…
Cancel
Save