Skip to content

Commit

Permalink
Type system: add support for tuple conversions.
Browse files Browse the repository at this point in the history
  • Loading branch information
dgrunwald committed May 14, 2018
1 parent 4695012 commit 92b72c9
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 82 deletions.
12 changes: 12 additions & 0 deletions ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ public void TupleIdentityConversions()
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()
{
Expand Down
15 changes: 15 additions & 0 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -64,6 +77,8 @@ public class TupleTests
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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


Expand Down
73 changes: 73 additions & 0 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.roslyn.il
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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


Expand Down
11 changes: 2 additions & 9 deletions ICSharpCode.Decompiler/CSharp/CallBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -518,23 +518,16 @@ ExpressionWithResolveResult HandleAccessorCall(ExpectedTargetDetails expectedTar
}
}

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;
Expand Down
4 changes: 2 additions & 2 deletions ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,11 @@ bool IsUnambiguousAccess()
{
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));
}
}

Expand Down
Loading

0 comments on commit 92b72c9

Please sign in to comment.