Browse Source

Type system: add support for tuple conversions.

pull/1134/head
Daniel Grunwald 7 years ago
parent
commit
92b72c9570
  1. 12
      ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs
  2. 15
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs
  3. 70
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.opt.roslyn.il
  4. 73
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.roslyn.il
  5. 11
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  6. 4
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  7. 104
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  8. 58
      ICSharpCode.Decompiler/Semantics/Conversion.cs
  9. 11
      ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs
  10. 78
      ICSharpCode.Decompiler/TypeSystem/TupleType.cs

12
ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs

@ -103,6 +103,18 @@ namespace ICSharpCode.Decompiler.Tests.Semantics @@ -103,6 +103,18 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
new TupleType(compilation, ImmutableArray.Create(stringType, intType), ImmutableArray.Create("a", "b"))));
}
[Test]
public void TupleConversions()
{
Assert.AreEqual(
C.TupleConversion(ImmutableArray.Create(C.ImplicitNumericConversion, C.ImplicitReferenceConversion)),
ImplicitConversion(typeof((int, string)), typeof((long, object))));
Assert.AreEqual(
C.TupleConversion(ImmutableArray.Create(C.ImplicitNumericConversion)),
ImplicitConversion(typeof(ValueTuple<float>), typeof(ValueTuple<double>)));
}
[Test]
public void PrimitiveConversions()
{

15
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs

@ -24,6 +24,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -24,6 +24,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class TupleTests
{
private abstract class OverloadResolution
{
public abstract void M1((long, long) a);
public abstract void M1(object a);
public void UseM1((int, int) a)
{
// M1(a); TODO: tuple conversion transform
// Cast is required to avoid the overload usable via tuple conversion:
M1((object)a);
}
}
public ValueTuple VT0;
public ValueTuple<int> VT1;
public ValueTuple<int, int, int, int, int, int, int, ValueTuple> VT7EmptyRest;
@ -64,6 +77,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -64,6 +77,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int TupleHash => (1, 2, 3).GetHashCode();
public int TupleHash2 => Named2.GetHashCode();
public (int, int) AccessRest => (1, 2, 3, 4, 5, 6, 7, 8, 9).Rest;
public void UseDict()
{
if (TupleDict.Count > 10) {

70
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.opt.roslyn.il

@ -41,6 +41,43 @@ @@ -41,6 +41,43 @@
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests
extends [mscorlib]System.Object
{
.class abstract auto ansi nested private beforefieldinit OverloadResolution
extends [mscorlib]System.Object
{
.method public hidebysig newslot abstract virtual
instance void M1(valuetype [mscorlib]System.ValueTuple`2<int64,int64> a) cil managed
{
} // end of method OverloadResolution::M1
.method public hidebysig newslot abstract virtual
instance void M1(object a) cil managed
{
} // end of method OverloadResolution::M1
.method public hidebysig instance void
UseM1(valuetype [mscorlib]System.ValueTuple`2<int32,int32> a) cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: box valuetype [mscorlib]System.ValueTuple`2<int32,int32>
IL_0007: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests/OverloadResolution::M1(object)
IL_000c: ret
} // end of method OverloadResolution::UseM1
.method family hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method OverloadResolution::.ctor
} // end of class OverloadResolution
.field public valuetype [mscorlib]System.ValueTuple VT0
.field public valuetype [mscorlib]System.ValueTuple`1<int32> VT1
.field public valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple> VT7EmptyRest
@ -242,6 +279,34 @@ @@ -242,6 +279,34 @@
IL_0011: ret
} // end of method TupleTests::get_TupleHash2
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2<int32,int32>
get_AccessRest() cil managed
{
// Code size 26 (0x1a)
.maxstack 9
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: ldc.i4.3
IL_0003: ldc.i4.4
IL_0004: ldc.i4.5
IL_0005: ldc.i4.6
IL_0006: ldc.i4.7
IL_0007: ldc.i4.8
IL_0008: ldc.i4.s 9
IL_000a: newobj instance void valuetype [mscorlib]System.ValueTuple`2<int32,int32>::.ctor(!0,
!1)
IL_000f: newobj instance void valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`2<int32,int32>>::.ctor(!0,
!1,
!2,
!3,
!4,
!5,
!6,
!7)
IL_0014: ldfld !7 valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`2<int32,int32>>::Rest
IL_0019: ret
} // end of method TupleTests::get_AccessRest
.method public hidebysig instance void
UseDict() cil managed
{
@ -336,6 +401,11 @@ @@ -336,6 +401,11 @@
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_TupleHash2()
} // end of property TupleTests::TupleHash2
.property instance valuetype [mscorlib]System.ValueTuple`2<int32,int32>
AccessRest()
{
.get instance valuetype [mscorlib]System.ValueTuple`2<int32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_AccessRest()
} // end of property TupleTests::AccessRest
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests

73
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.roslyn.il

@ -41,6 +41,46 @@ @@ -41,6 +41,46 @@
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests
extends [mscorlib]System.Object
{
.class abstract auto ansi nested private beforefieldinit OverloadResolution
extends [mscorlib]System.Object
{
.method public hidebysig newslot abstract virtual
instance void M1(valuetype [mscorlib]System.ValueTuple`2<int64,int64> a) cil managed
{
} // end of method OverloadResolution::M1
.method public hidebysig newslot abstract virtual
instance void M1(object a) cil managed
{
} // end of method OverloadResolution::M1
.method public hidebysig instance void
UseM1(valuetype [mscorlib]System.ValueTuple`2<int32,int32> a) cil managed
{
// Code size 15 (0xf)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.1
IL_0003: box valuetype [mscorlib]System.ValueTuple`2<int32,int32>
IL_0008: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests/OverloadResolution::M1(object)
IL_000d: nop
IL_000e: ret
} // end of method OverloadResolution::UseM1
.method family hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method OverloadResolution::.ctor
} // end of class OverloadResolution
.field public valuetype [mscorlib]System.ValueTuple VT0
.field public valuetype [mscorlib]System.ValueTuple`1<int32> VT1
.field public valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple> VT7EmptyRest
@ -242,6 +282,34 @@ @@ -242,6 +282,34 @@
IL_0011: ret
} // end of method TupleTests::get_TupleHash2
.method public hidebysig specialname instance valuetype [mscorlib]System.ValueTuple`2<int32,int32>
get_AccessRest() cil managed
{
// Code size 26 (0x1a)
.maxstack 9
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: ldc.i4.3
IL_0003: ldc.i4.4
IL_0004: ldc.i4.5
IL_0005: ldc.i4.6
IL_0006: ldc.i4.7
IL_0007: ldc.i4.8
IL_0008: ldc.i4.s 9
IL_000a: newobj instance void valuetype [mscorlib]System.ValueTuple`2<int32,int32>::.ctor(!0,
!1)
IL_000f: newobj instance void valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`2<int32,int32>>::.ctor(!0,
!1,
!2,
!3,
!4,
!5,
!6,
!7)
IL_0014: ldfld !7 valuetype [mscorlib]System.ValueTuple`8<int32,int32,int32,int32,int32,int32,int32,valuetype [mscorlib]System.ValueTuple`2<int32,int32>>::Rest
IL_0019: ret
} // end of method TupleTests::get_AccessRest
.method public hidebysig instance void
UseDict() cil managed
{
@ -351,6 +419,11 @@ @@ -351,6 +419,11 @@
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_TupleHash2()
} // end of property TupleTests::TupleHash2
.property instance valuetype [mscorlib]System.ValueTuple`2<int32,int32>
AccessRest()
{
.get instance valuetype [mscorlib]System.ValueTuple`2<int32,int32> ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests::get_AccessRest()
} // end of property TupleTests::AccessRest
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.TupleTests

11
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -518,23 +518,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -518,23 +518,16 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
static readonly NormalizeTypeVisitor typeErasure = new NormalizeTypeVisitor {
ReplaceClassTypeParametersWithDummy = false,
ReplaceMethodTypeParametersWithDummy = false,
DynamicAndObject = true,
TupleToUnderlyingType = true
};
bool IsAppropriateCallTarget(ExpectedTargetDetails expectedTargetDetails, IMember expectedTarget, IMember actualTarget)
{
if (expectedTarget.Equals(actualTarget, typeErasure))
if (expectedTarget.Equals(actualTarget, NormalizeTypeVisitor.TypeErasure))
return true;
if (expectedTargetDetails.CallOpCode == OpCode.CallVirt && actualTarget.IsOverride) {
if (expectedTargetDetails.NeedsBoxingConversion && actualTarget.DeclaringType.IsReferenceType != true)
return false;
foreach (var possibleTarget in InheritanceHelper.GetBaseMembers(actualTarget, false)) {
if (expectedTarget.Equals(possibleTarget, typeErasure))
if (expectedTarget.Equals(possibleTarget, NormalizeTypeVisitor.TypeErasure))
return true;
if (!possibleTarget.IsOverride)
break;

4
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -207,11 +207,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -207,11 +207,11 @@ namespace ICSharpCode.Decompiler.CSharp
{
if (targetResolveResult == null) {
var result = resolver.ResolveSimpleName(field.Name, EmptyList<IType>.Instance, isInvocationTarget: false) as MemberResolveResult;
return !(result == null || result.IsError || !result.Member.Equals(field));
return !(result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure));
} else {
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var result = lookup.Lookup(target.ResolveResult, field.Name, EmptyList<IType>.Instance, false) as MemberResolveResult;
return !(result == null || result.IsError || !result.Member.Equals(field));
return !(result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure));
}
}

104
ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.Semantics;
@ -37,15 +38,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -37,15 +38,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
{
readonly ConcurrentDictionary<TypePair, Conversion> implicitConversionCache = new ConcurrentDictionary<TypePair, Conversion>();
readonly ICompilation compilation;
readonly IType objectType;
public CSharpConversions(ICompilation compilation)
{
if (compilation == null)
throw new ArgumentNullException("compilation");
this.compilation = compilation;
this.objectType = compilation.FindType(KnownTypeCode.Object);
this.dynamicErasure = new DynamicErasure(this);
}
/// <summary>
@ -97,7 +95,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -97,7 +95,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
#endregion
#region ImplicitConversion
private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType, bool allowUserDefined)
private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType, bool allowUserDefined, bool allowTuple)
{
Conversion c;
if (resolveResult.IsCompileTimeConstant) {
@ -105,18 +103,18 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -105,18 +103,18 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (c.IsValid) return c;
if (ImplicitConstantExpressionConversion(resolveResult, toType))
return Conversion.ImplicitConstantExpressionConversion;
c = StandardImplicitConversion(resolveResult.Type, toType);
c = StandardImplicitConversion(resolveResult.Type, toType, allowTuple);
if (c != Conversion.None) return c;
if (allowUserDefined) {
c = UserDefinedImplicitConversion(resolveResult, resolveResult.Type, toType);
if (c != Conversion.None) return c;
}
} else {
if (allowUserDefined) {
// if allowUserDefined is true, we might as well use the cache
if (allowUserDefined && allowTuple) {
// if allowUserDefined and allowTuple are true, we might as well use the cache
c = ImplicitConversion(resolveResult.Type, toType);
} else {
c = ImplicitConversion(resolveResult.Type, toType, allowUserDefined);
c = ImplicitConversion(resolveResult.Type, toType, allowUserDefined, allowTuple);
}
if (c != Conversion.None) return c;
}
@ -128,10 +126,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -128,10 +126,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return c;
}
private Conversion ImplicitConversion(IType fromType, IType toType, bool allowUserDefined)
private Conversion ImplicitConversion(IType fromType, IType toType, bool allowUserDefined, bool allowTuple)
{
// C# 4.0 spec: §6.1
var c = StandardImplicitConversion(fromType, toType);
var c = StandardImplicitConversion(fromType, toType, allowTuple);
if (c == Conversion.None && allowUserDefined) {
c = UserDefinedImplicitConversion(null, fromType, toType);
}
@ -142,7 +140,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -142,7 +140,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
{
if (resolveResult == null)
throw new ArgumentNullException("resolveResult");
return ImplicitConversion(resolveResult, toType, allowUserDefined: true);
return ImplicitConversion(resolveResult, toType, allowUserDefined: true, allowTuple: true);
}
public Conversion ImplicitConversion(IType fromType, IType toType)
@ -157,18 +155,23 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -157,18 +155,23 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (implicitConversionCache.TryGetValue(pair, out c))
return c;
c = ImplicitConversion(fromType, toType, allowUserDefined: true);
c = ImplicitConversion(fromType, toType, allowUserDefined: true, allowTuple: true);
implicitConversionCache[pair] = c;
return c;
}
public Conversion StandardImplicitConversion(IType fromType, IType toType)
{
if (fromType == null)
throw new ArgumentNullException("fromType");
if (toType == null)
throw new ArgumentNullException("toType");
return StandardImplicitConversion(fromType, toType, allowTupleConversion: true);
}
Conversion StandardImplicitConversion(IType fromType, IType toType, bool allowTupleConversion)
{
// C# 4.0 spec: §6.3.1
if (IdentityConversion(fromType, toType))
return Conversion.IdentityConversion;
@ -190,9 +193,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -190,9 +193,14 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
if (ImplicitPointerConversion(fromType, toType))
return Conversion.ImplicitPointerConversion;
if (allowTupleConversion) {
c = TupleConversion(fromType, toType, isExplicit: false);
if (c != Conversion.None)
return c;
}
return Conversion.None;
}
/// <summary>
/// Gets whether the type 'fromType' is convertible to 'toType'
/// using one of the conversions allowed when satisying constraints (§4.4.4)
@ -233,7 +241,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -233,7 +241,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (resolveResult.Type.Kind == TypeKind.Dynamic)
return Conversion.ExplicitDynamicConversion;
Conversion c = ImplicitConversion(resolveResult, toType, allowUserDefined: false);
Conversion c = ImplicitConversion(resolveResult, toType, allowUserDefined: false, allowTuple: false);
if (c != Conversion.None)
return c;
c = ExplicitConversionImpl(resolveResult.Type, toType);
@ -249,7 +257,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -249,7 +257,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (toType == null)
throw new ArgumentNullException("toType");
Conversion c = ImplicitConversion(fromType, toType, allowUserDefined: false);
Conversion c = ImplicitConversion(fromType, toType, allowUserDefined: false, allowTuple: false);
if (c != Conversion.None)
return c;
c = ExplicitConversionImpl(fromType, toType);
@ -278,10 +286,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -278,10 +286,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return c;
if (ExplicitPointerConversion(fromType, toType))
return Conversion.ExplicitPointerConversion;
return Conversion.None;
return TupleConversion(fromType, toType, isExplicit: true);
}
#endregion
#region Identity Conversion
/// <summary>
/// Gets whether there is an identity conversion from <paramref name="fromType"/> to <paramref name="toType"/>
@ -289,32 +297,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -289,32 +297,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
public 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(CSharpConversions conversions)
{
this.objectType = conversions.objectType;
}
public override IType VisitOtherType(IType type)
{
if (type.Kind == TypeKind.Dynamic)
return objectType;
else
return base.VisitOtherType(type);
}
public override IType VisitTupleType(TupleType type)
{
return type.UnderlyingType;
}
fromType = fromType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure);
toType = toType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure);
return fromType.Equals(toType);
}
#endregion
@ -499,7 +484,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -499,7 +484,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
bool IsSubtypeOf(IType s, IType t, int subtypeCheckNestingDepth)
{
// conversion to dynamic + object are always possible
if (t.Kind == TypeKind.Dynamic || t.Equals(objectType))
if (t.Kind == TypeKind.Dynamic || t.IsKnownType(KnownTypeCode.Object))
return true;
if (subtypeCheckNestingDepth > 10) {
// Subtyping in C# is undecidable
@ -861,7 +846,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -861,7 +846,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
Conversion UserDefinedExplicitConversion(ResolveResult fromResult, IType fromType, IType toType)
{
// C# 4.0 spec §6.4.5 User-defined implicit conversions
// C# 4.0 spec §6.4.5 User-defined explicit conversions
var operators = GetApplicableConversionOperators(fromResult, fromType, toType, true);
if (operators.Count > 0) {
IType mostSpecificSource;
@ -1140,7 +1125,32 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1140,7 +1125,32 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
|| IsImplicitReferenceConversion(m.ReturnType, invoke.ReturnType);
}
#endregion
#region Tuple Conversion
Conversion TupleConversion(IType fromType, IType toType, bool isExplicit)
{
var fromElements = TupleType.GetTupleElementTypes(fromType);
if (fromElements.IsDefaultOrEmpty)
return Conversion.None;
var toElements = TupleType.GetTupleElementTypes(toType);
if (toElements.IsDefault || fromElements.Length != toElements.Length)
return Conversion.None;
Conversion[] elementConversions = new Conversion[fromElements.Length];
for (int i = 0; i < elementConversions.Length; i++) {
Conversion c;
if (isExplicit) {
c = ExplicitConversion(fromElements[i], toElements[i]);
} else {
c = ImplicitConversion(fromElements[i], toElements[i]);
}
if (!c.IsValid)
return Conversion.None;
elementConversions[i] = c;
}
return Conversion.TupleConversion(elementConversions.ToImmutableArray());
}
#endregion
#region BetterConversion
/// <summary>
/// Gets the better conversion (C# 4.0 spec, §7.5.3.3)

58
ICSharpCode.Decompiler/Semantics/Conversion.cs

@ -17,6 +17,8 @@ @@ -17,6 +17,8 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Immutable;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Semantics
@ -94,6 +96,11 @@ namespace ICSharpCode.Decompiler.Semantics @@ -94,6 +96,11 @@ namespace ICSharpCode.Decompiler.Semantics
throw new ArgumentNullException("chosenMethod");
return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: false);
}
public static Conversion TupleConversion(ImmutableArray<Conversion> conversions)
{
return new TupleConv(conversions);
}
#endregion
#region Inner classes
@ -376,6 +383,43 @@ namespace ICSharpCode.Decompiler.Semantics @@ -376,6 +383,43 @@ namespace ICSharpCode.Decompiler.Semantics
return method.GetHashCode();
}
}
sealed class TupleConv : Conversion
{
public override bool IsImplicit { get; }
public override bool IsExplicit => !IsImplicit;
public override ImmutableArray<Conversion> ElementConversions { get; }
public override bool IsTupleConversion => true;
public TupleConv(ImmutableArray<Conversion> elementConversions)
{
this.ElementConversions = elementConversions;
this.IsImplicit = elementConversions.All(c => c.IsImplicit);
}
public override bool Equals(Conversion other)
{
return other is TupleConv o
&& ElementConversions.SequenceEqual(o.ElementConversions);
}
public override int GetHashCode()
{
unchecked {
int hash = 0;
foreach (var conv in ElementConversions) {
hash *= 31;
hash += conv.GetHashCode();
}
return hash;
}
}
public override string ToString()
{
return (IsImplicit ? "implicit " : "explicit ") + " tuple conversion";
}
}
#endregion
/// <summary>
@ -451,7 +495,7 @@ namespace ICSharpCode.Decompiler.Semantics @@ -451,7 +495,7 @@ namespace ICSharpCode.Decompiler.Semantics
public virtual bool IsNullableConversion {
get { return false; }
}
/// <summary>
/// Gets whether this conversion is user-defined (op_Implicit or op_Explicit).
/// </summary>
@ -533,7 +577,17 @@ namespace ICSharpCode.Decompiler.Semantics @@ -533,7 +577,17 @@ namespace ICSharpCode.Decompiler.Semantics
public virtual IMethod Method {
get { return null; }
}
/// <summary>
/// Gets whether this conversion is a tuple conversion.
/// </summary>
public virtual bool IsTupleConversion => false;
/// <summary>
/// For a tuple conversion, gets the individual tuple element conversions.
/// </summary>
public virtual ImmutableArray<Conversion> ElementConversions => default(ImmutableArray<Conversion>);
public override sealed bool Equals(object obj)
{
return Equals(obj as Conversion);

11
ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs

@ -7,6 +7,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -7,6 +7,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
sealed class NormalizeTypeVisitor : TypeVisitor
{
/// <summary>
/// NormalizeTypeVisitor that does not normalize type parameters,
/// but performs type erasure (object->dynamic; tuple->underlying type).
/// </summary>
internal static readonly NormalizeTypeVisitor TypeErasure = new NormalizeTypeVisitor {
ReplaceClassTypeParametersWithDummy = false,
ReplaceMethodTypeParametersWithDummy = false,
DynamicAndObject = true,
TupleToUnderlyingType = true
};
public bool ReplaceClassTypeParametersWithDummy = true;
public bool ReplaceMethodTypeParametersWithDummy = true;
public bool DynamicAndObject = true;

78
ICSharpCode.Decompiler/TypeSystem/TupleType.cs

@ -21,13 +21,13 @@ using System.Collections.Generic; @@ -21,13 +21,13 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.TypeSystem
{
[DebuggerDisplay("TupleType({ElementTypes}, {ElementNames})")]
public sealed class TupleType : AbstractType, ICompilationProvider
{
public const int RestPosition = 8;
@ -130,11 +130,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -130,11 +130,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
public static TupleType FromUnderlyingType(ICompilation compilation, IType type)
{
var elementTypes = new List<IType>();
if (CollectTupleElementTypes(type, elementTypes)) {
var elementTypes = GetTupleElementTypes(type);
if (elementTypes.Length > 0) {
return new TupleType(
compilation,
elementTypes.ToImmutableArray(),
elementTypes,
valueTupleAssembly: type.GetDefinition()?.ParentAssembly
);
} else {
@ -142,27 +142,44 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -142,27 +142,44 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
static bool CollectTupleElementTypes(IType type, List<IType> output)
/// <summary>
/// Gets the tuple element types from a tuple type or tuple underlying type.
/// </summary>
public static ImmutableArray<IType> GetTupleElementTypes(IType tupleType)
{
switch (type.Kind) {
case TypeKind.Tuple:
output.AddRange(((TupleType)type).ElementTypes);
return true;
case TypeKind.Class:
case TypeKind.Struct:
if (type.Namespace == "System" && type.Name == "ValueTuple") {
int tpc = type.TypeParameterCount;
if (tpc > 0 && tpc < RestPosition) {
output.AddRange(type.TypeArguments);
return true;
} else if (tpc == RestPosition) {
output.AddRange(type.TypeArguments.Take(RestPosition - 1));
return CollectTupleElementTypes(type.TypeArguments[RestIndex], output);
List<IType> output = null;
if (Collect(tupleType)) {
return output.ToImmutableArray();
} else {
return default(ImmutableArray<IType>);
}
bool Collect(IType type)
{
switch (type.Kind) {
case TypeKind.Tuple:
if (output == null)
output = new List<IType>();
output.AddRange(((TupleType)type).ElementTypes);
return true;
case TypeKind.Class:
case TypeKind.Struct:
if (type.Namespace == "System" && type.Name == "ValueTuple") {
if (output == null)
output = new List<IType>();
int tpc = type.TypeParameterCount;
if (tpc > 0 && tpc < RestPosition) {
output.AddRange(type.TypeArguments);
return true;
} else if (tpc == RestPosition) {
output.AddRange(type.TypeArguments.Take(RestPosition - 1));
return Collect(type.TypeArguments[RestIndex]);
}
}
}
break;
break;
}
return false;
}
return false;
}
public override TypeKind Kind => TypeKind.Tuple;
@ -199,6 +216,23 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -199,6 +216,23 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
public override string ToString()
{
StringBuilder b = new StringBuilder();
b.Append('(');
for (int i = 0; i < ElementTypes.Length; i++) {
if (i > 0)
b.Append(", ");
b.Append(ElementTypes[i]);
if (ElementNames[i] != null) {
b.Append(' ');
b.Append(ElementNames[i]);
}
}
b.Append(')');
return b.ToString();
}
public override IType AcceptVisitor(TypeVisitor visitor)
{
return visitor.VisitTupleType(this);

Loading…
Cancel
Save