From dd1c509651cc90aa951375108a230686097a8c80 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 31 Oct 2017 00:31:48 +0100 Subject: [PATCH] Fix #926: missing explicit casts for implicit operators --- .../TestCases/Correctness/Conversions.cs | 36 ++++++++++++++++- .../TestCases/Pretty/TypeAnalysisTests.cs | 10 +++++ .../TestCases/Pretty/TypeAnalysisTests.il | 40 +++++++++++++++++-- .../TestCases/Pretty/TypeAnalysisTests.opt.il | 28 +++++++++++-- .../Pretty/TypeAnalysisTests.opt.roslyn.il | 24 ++++++++++- .../Pretty/TypeAnalysisTests.roslyn.il | 36 ++++++++++++++++- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 17 ++++++++ .../ReplaceMethodCallsWithOperators.cs | 4 -- .../CSharp/TranslatedExpression.cs | 3 +- 9 files changed, 180 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Conversions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Conversions.cs index 3ed201d14..b8f4d0430 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Conversions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Conversions.cs @@ -37,7 +37,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness TypeCode.UInt64, TypeCode.Single, TypeCode.Double, - //TypeCode.Decimal + TypeCode.Decimal }; static object[] inputValues = { @@ -109,6 +109,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness RunTest(checkForOverflow: true); Console.WriteLine(ReadZeroTerminatedString("Hello World!".Length)); + C1.Test(); } static void RunTest(bool checkForOverflow) @@ -156,4 +157,37 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness return System.Text.Encoding.ASCII.GetBytes("Hello World!"); } } + + class C1 + { + public static implicit operator Type(C1 c) + { + return c.GetType(); + } + + public static void Test() + { + Console.WriteLine("op_Implicit tests"); + ExplicitUseOfImplicitConversion(new C1()); + Console.WriteLine(ChainedImplicitConversions(new C2()).Name); + } + + static void ExplicitUseOfImplicitConversion(C1 c) + { + Console.WriteLine(((Type)c).Name); + } + + static Type ChainedImplicitConversions(C2 c) + { + return (C1)c; + } + } + + class C2 + { + public static implicit operator C1(C2 c) + { + return new C1(); + } + } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs index 72db78dfb..2436ad5ef 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs @@ -214,5 +214,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return a == null; } + + public decimal ImplicitConversionToDecimal(byte v) + { + return v; + } + + public decimal ImplicitConversionToDecimal(ulong v) + { + return v; + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il index 0eab76d08..19e483a77 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly ph2u0axx +.assembly t2movxx2 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module ph2u0axx.dll -// MVID: {8F821F70-8053-4F18-BDF7-88A43880B754} +.module t2movxx2.dll +// MVID: {F28F6C6C-94AA-4D5E-B9EE-907881B3E7E4} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x00D70000 +// Image base: 0x00690000 // =============== CLASS MEMBERS DECLARATION =================== @@ -735,6 +735,38 @@ IL_0009: ret } // end of method TypeAnalysisTests::CompareDelegateWithNull + .method public hidebysig instance valuetype [mscorlib]System.Decimal + ImplicitConversionToDecimal(uint8 v) cil managed + { + // Code size 12 (0xc) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Decimal V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(uint8) + IL_0007: stloc.0 + IL_0008: br.s IL_000a + + IL_000a: ldloc.0 + IL_000b: ret + } // end of method TypeAnalysisTests::ImplicitConversionToDecimal + + .method public hidebysig instance valuetype [mscorlib]System.Decimal + ImplicitConversionToDecimal(uint64 v) cil managed + { + // Code size 12 (0xc) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Decimal V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(uint64) + IL_0007: stloc.0 + IL_0008: br.s IL_000a + + IL_000a: ldloc.0 + IL_000b: ret + } // end of method TypeAnalysisTests::ImplicitConversionToDecimal + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il index c5c37d26a..4ecf057c9 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly frnxitb5 +.assembly '0nmkvdgs' { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module frnxitb5.dll -// MVID: {DC6B1392-D29F-44A5-97D6-0C026DCD3A00} +.module '0nmkvdgs.dll' +// MVID: {E543BCF9-C441-4A6D-A25F-19C170B765C0} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x00D50000 +// Image base: 0x00C50000 // =============== CLASS MEMBERS DECLARATION =================== @@ -510,6 +510,26 @@ IL_0004: ret } // end of method TypeAnalysisTests::CompareDelegateWithNull + .method public hidebysig instance valuetype [mscorlib]System.Decimal + ImplicitConversionToDecimal(uint8 v) cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(uint8) + IL_0006: ret + } // end of method TypeAnalysisTests::ImplicitConversionToDecimal + + .method public hidebysig instance valuetype [mscorlib]System.Decimal + ImplicitConversionToDecimal(uint64 v) cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(uint64) + IL_0006: ret + } // end of method TypeAnalysisTests::ImplicitConversionToDecimal + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il index 0a09fe8f2..405008458 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module TypeAnalysisTests.dll -// MVID: {B6659F2C-8CAA-447B-9D1D-D59D8C40C6D7} +// MVID: {7D2FE161-359E-42EC-B423-26BEB23F890D} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x003E0000 +// Image base: 0x01730000 // =============== CLASS MEMBERS DECLARATION =================== @@ -512,6 +512,26 @@ IL_0004: ret } // end of method TypeAnalysisTests::CompareDelegateWithNull + .method public hidebysig instance valuetype [mscorlib]System.Decimal + ImplicitConversionToDecimal(uint8 v) cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(uint8) + IL_0006: ret + } // end of method TypeAnalysisTests::ImplicitConversionToDecimal + + .method public hidebysig instance valuetype [mscorlib]System.Decimal + ImplicitConversionToDecimal(uint64 v) cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(uint64) + IL_0006: ret + } // end of method TypeAnalysisTests::ImplicitConversionToDecimal + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il index f12c3a17a..66f4f274d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module TypeAnalysisTests.dll -// MVID: {E25A3961-A07D-4220-AB6C-FE5B1624514C} +// MVID: {6A008C4E-2BC5-45CA-93FB-93A12CD0BC69} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x00D80000 +// Image base: 0x01320000 // =============== CLASS MEMBERS DECLARATION =================== @@ -734,6 +734,38 @@ IL_0009: ret } // end of method TypeAnalysisTests::CompareDelegateWithNull + .method public hidebysig instance valuetype [mscorlib]System.Decimal + ImplicitConversionToDecimal(uint8 v) cil managed + { + // Code size 12 (0xc) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Decimal V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(uint8) + IL_0007: stloc.0 + IL_0008: br.s IL_000a + + IL_000a: ldloc.0 + IL_000b: ret + } // end of method TypeAnalysisTests::ImplicitConversionToDecimal + + .method public hidebysig instance valuetype [mscorlib]System.Decimal + ImplicitConversionToDecimal(uint64 v) cil managed + { + // Code size 12 (0xc) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Decimal V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(uint64) + IL_0007: stloc.0 + IL_0008: br.s IL_000a + + IL_000a: ldloc.0 + IL_000b: ret + } // end of method TypeAnalysisTests::ImplicitConversionToDecimal + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 525758a73..3dd0e777a 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -167,6 +167,8 @@ namespace ICSharpCode.Decompiler.CSharp } else if (IsDelegateEqualityComparison(method, arguments)) { return HandleDelegateEqualityComparison(method, arguments) .WithILInstruction(inst).WithRR(rr); + } else if (method.IsOperator && method.Name == "op_Implicit" && arguments.Count == 1) { + return HandleImplicitConversion(inst, arguments[0]); } else { bool requireTypeArguments = false; bool targetCasted = false; @@ -269,6 +271,21 @@ namespace ICSharpCode.Decompiler.CSharp ); } + private TranslatedExpression HandleImplicitConversion(CallInstruction call, TranslatedExpression argument) + { + var conversions = CSharpConversions.Get(expressionBuilder.compilation); + IType targetType = call.Method.ReturnType; + var conv = conversions.ImplicitConversion(argument.Type, targetType); + if (!(conv.IsUserDefined && conv.Method.Equals(call.Method))) { + // implicit conversion to targetType isn't directly possible, so first insert a cast to the argument type + argument = argument.ConvertTo(call.Method.Parameters[0].Type, expressionBuilder); + conv = conversions.ImplicitConversion(argument.Type, targetType); + } + return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression) + .WithILInstruction(call) + .WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, conv)); + } + OverloadResolutionErrors IsUnambiguousCall(ILInstruction inst, TranslatedExpression target, IMethod method, IType[] typeArguments, IList arguments) { var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index fa7001f30..635aa06cc 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -131,10 +131,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms ); return; } - if (method.Name == "op_Implicit" && arguments.Length == 1) { - invocationExpression.ReplaceWith(arguments[0]); - return; - } if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) { invocationExpression.ReplaceWith(arguments[0]); return; diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 34075fa46..2186a31b1 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -179,7 +179,8 @@ namespace ICSharpCode.Decompiler.CSharp if (Expression is CastExpression cast && (type.IsKnownType(KnownTypeCode.Object) && conversion.Conversion.IsBoxingConversion || type.Kind == TypeKind.Delegate && conversion.Conversion.IsAnonymousFunctionConversion - )) { + || (conversion.Conversion.IsImplicit && (conversion.Conversion.IsUserDefined || targetType.IsKnownType(KnownTypeCode.Decimal))) + )) { return this.UnwrapChild(cast.Expression); } else if (Expression is ObjectCreateExpression oce && conversion.Conversion.IsMethodGroupConversion && oce.Arguments.Count == 1 && expressionBuilder.settings.UseImplicitMethodGroupConversion) {