From a4d4c1253eba0fafb5a7a3d026b163f00a368ce5 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 3 Oct 2017 14:07:22 +0200 Subject: [PATCH] Fix two minor bugs in NullableLiftingTransform, and enable the LiftedOperators tests that are not affected by redundant casts. --- .../PrettyTestRunner.cs | 8 +- .../TestCases/Pretty/LiftedOperators.cs | 477 +++++++++--------- .../IL/Transforms/NullableLiftingTransform.cs | 20 +- 3 files changed, 247 insertions(+), 258 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 880297416..f0033e239 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -121,14 +121,10 @@ namespace ICSharpCode.Decompiler.Tests Run(cscOptions: cscOptions); } - [Test, Ignore("Not implemented")] + [Test] public void LiftedOperators([ValueSource("defaultOptions")] CompilerOptions cscOptions) { - try { - Run(cscOptions: cscOptions); - } catch (AssertionException) { - Assert.Ignore("Not implemented"); - } + Run(cscOptions: cscOptions); } [Test] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs index b2a682b26..96aaa38a6 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { // C# uses 4 different patterns of IL for lifted operators: bool, other primitive types, decimal, other structs. // Different patterns are used depending on whether both of the operands are nullable or only the left/right operand is nullable. - // Negation must not be pushed through such comparisons because it would change the semantics. + // Negation must not be pushed through such comparisons because it would change the semantics (except for equality/inequality). // A comparison used in a condition differs somewhat from a comparison used as a simple value. public static void BoolBasic(bool? a, bool? b) @@ -36,13 +36,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty if (a != b) { Console.WriteLine(); } - - if (!(a == b)) { - Console.WriteLine(); - } - if (!(a != b)) { - Console.WriteLine(); - } } public static void BoolComplex(bool? a, Func x) @@ -60,19 +53,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty if (x() != a) { Console.WriteLine(); } - - if (!(a == x())) { - Console.WriteLine(); - } - if (!(a != x())) { - Console.WriteLine(); - } - if (!(x() == a)) { - Console.WriteLine(); - } - if (!(x() != a)) { - Console.WriteLine(); - } } public static void BoolConst(bool? a) @@ -101,10 +81,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { Console.WriteLine(a == b); Console.WriteLine(a != b); - - Console.WriteLine(!(a == b)); - Console.WriteLine(!(a != b)); - + Console.WriteLine(a & b); Console.WriteLine(a | b); Console.WriteLine(a ^ b); @@ -122,16 +99,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine(x() == a); Console.WriteLine(x() != a); - - Console.WriteLine(!(a == x())); - Console.WriteLine(!(a != x())); - - Console.WriteLine(a & x()); - Console.WriteLine(a | x()); + + //Console.WriteLine(a & x()); // we currently can't tell the order + //Console.WriteLine(a | x()); // of the operands in bool [&|] bool? Console.WriteLine(a ^ x()); Console.WriteLine(a ?? x()); - a &= x(); - a |= x(); + //a &= x(); -- also affected by order of operand problem + //a |= x(); a ^= x(); Console.WriteLine(x() & a); @@ -171,14 +145,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty if (a <= b) { Console.WriteLine(); } - - if (!(a == b)) { - Console.WriteLine(); - } - if (!(a != b)) { + + if (!(a > b)) { Console.WriteLine(); } - if (!(a > b)) { + if (!(a <= b)) { Console.WriteLine(); } } @@ -205,13 +176,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine(); } - if (!(a == x())) { - Console.WriteLine(); - } - if (!(a != x())) { + if (!(a > x())) { Console.WriteLine(); } - if (!(a > x())) { + if (!(a <= x())) { Console.WriteLine(); } } @@ -244,10 +212,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine(a == b); Console.WriteLine(a != b); Console.WriteLine(a > b); - - Console.WriteLine(!(a == b)); - Console.WriteLine(!(a != b)); + Console.WriteLine(!(a > b)); + Console.WriteLine(!(a >= b)); Console.WriteLine(a + b); Console.WriteLine(a - b); @@ -371,61 +338,62 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine(); } - if (!(a == b)) { - Console.WriteLine(); - } - if (!(a != b)) { + if (!(a > b)) { Console.WriteLine(); } - if (!(a > b)) { + if (!(a < b)) { Console.WriteLine(); } } public static void NumberComplex(decimal? a, Func x) { - if (a == x()) { - Console.WriteLine(); - } - if (a != x()) { - Console.WriteLine(); - } - if (a > x()) { - Console.WriteLine(); - } - - if (x() == a) { - Console.WriteLine(); - } - if (x() != a) { - Console.WriteLine(); - } - if (x() > a) { - Console.WriteLine(); - } + // Tests deactivated because we insert redundant casts; + // TODO: revisit after decision has been made regarding the type system. + //if (a == x()) { + // Console.WriteLine(); + //} + //if (a != x()) { + // Console.WriteLine(); + //} + //if (a > x()) { + // Console.WriteLine(); + //} + + //if (x() == a) { + // Console.WriteLine(); + //} + //if (x() != a) { + // Console.WriteLine(); + //} + //if (x() > a) { + // Console.WriteLine(); + //} } public static void NumberConst(decimal? a) { - if (a == 2m) { - Console.WriteLine(); - } - if (a != 2m) { - Console.WriteLine(); - } - if (a > 2m) { - Console.WriteLine(); - } - - if (2m == a) { - Console.WriteLine(); - } - if (2m != a) { - Console.WriteLine(); - } - if (2m > a) { - Console.WriteLine(); - } + // Tests deactivated because we insert redundant casts; + // TODO: revisit after decision has been made regarding the type system. + //if (a == 2m) { + // Console.WriteLine(); + //} + //if (a != 2m) { + // Console.WriteLine(); + //} + //if (a > 2m) { + // Console.WriteLine(); + //} + + //if (2m == a) { + // Console.WriteLine(); + //} + //if (2m != a) { + // Console.WriteLine(); + //} + //if (2m > a) { + // Console.WriteLine(); + //} } public static void NumberValueBasic(decimal? a, decimal? b) @@ -434,9 +402,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine(a != b); Console.WriteLine(a > b); - Console.WriteLine(!(a == b)); - Console.WriteLine(!(a != b)); Console.WriteLine(!(a > b)); + Console.WriteLine(!(a <= b)); Console.WriteLine(a + b); Console.WriteLine(a - b); @@ -459,53 +426,57 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void NumberValueComplex(decimal? a, Func x) { - Console.WriteLine(a == x()); - Console.WriteLine(a != x()); - Console.WriteLine(a > x()); + // Tests deactivated because we insert redundant casts; + // TODO: revisit after decision has been made regarding the type system. + //Console.WriteLine(a == x()); + //Console.WriteLine(a != x()); + //Console.WriteLine(a > x()); - Console.WriteLine(x() == a); - Console.WriteLine(x() != a); - Console.WriteLine(x() > a); + //Console.WriteLine(x() == a); + //Console.WriteLine(x() != a); + //Console.WriteLine(x() > a); - Console.WriteLine(a + x()); - Console.WriteLine(a - x()); - Console.WriteLine(a * x()); - Console.WriteLine(a / x()); - Console.WriteLine(a % x()); - Console.WriteLine(a ?? x()); - a += x(); - a -= x(); - a *= x(); - a /= x(); - a %= x(); + //Console.WriteLine(a + x()); + //Console.WriteLine(a - x()); + //Console.WriteLine(a * x()); + //Console.WriteLine(a / x()); + //Console.WriteLine(a % x()); + //Console.WriteLine(a ?? x()); + //a += x(); + //a -= x(); + //a *= x(); + //a /= x(); + //a %= x(); - Console.WriteLine(x() + a); - (new decimal?[0])[0] += x(); + //Console.WriteLine(x() + a); + //(new decimal?[0])[0] += x(); } public static void NumberValueConst(decimal? a) { - Console.WriteLine(a == 2m); - Console.WriteLine(a != 2m); - Console.WriteLine(a > 2m); + // Tests deactivated because we insert redundant casts; + // TODO: revisit after decision has been made regarding the type system. + //Console.WriteLine(a == 2m); + //Console.WriteLine(a != 2m); + //Console.WriteLine(a > 2m); - Console.WriteLine(2m == a); - Console.WriteLine(2m != a); - Console.WriteLine(2m > a); + //Console.WriteLine(2m == a); + //Console.WriteLine(2m != a); + //Console.WriteLine(2m > a); - Console.WriteLine(a + 2m); - Console.WriteLine(a - 2m); - Console.WriteLine(a * 2m); - Console.WriteLine(a / 2m); - Console.WriteLine(a % 2m); - Console.WriteLine(a ?? 2m); - a += 2m; - a -= 2m; - a *= 2m; - a /= 2m; - a %= 2m; + //Console.WriteLine(a + 2m); + //Console.WriteLine(a - 2m); + //Console.WriteLine(a * 2m); + //Console.WriteLine(a / 2m); + //Console.WriteLine(a % 2m); + //Console.WriteLine(a ?? 2m); + //a += 2m; + //a -= 2m; + //a *= 2m; + //a /= 2m; + //a %= 2m; - Console.WriteLine(2m + a); + //Console.WriteLine(2m + a); } public static void CompareWithImplictCast(int? a, long? b) @@ -516,12 +487,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty if (a == b) { Console.WriteLine(); } - if (a < 10L) { - Console.WriteLine(); - } - if (a == 10L) { - Console.WriteLine(); - } + // TODO: unnecessary cast + //if (a < 10L) { + // Console.WriteLine(); + //} + //if (a == 10L) { + // Console.WriteLine(); + //} } public static void CompareWithSignChange(int? a, int? b) @@ -529,9 +501,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty if ((uint?)a < (uint?)b) { Console.WriteLine(); } - if ((uint?)a < 10UL) { - Console.WriteLine(); - } + // TODO: unnecessary cast + //if ((uint?)a < 10) { + // Console.WriteLine(); + //} } public static void StructBasic(TS? a, TS? b) @@ -568,25 +541,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void StructComplex(TS? a, Func x) { - if (a == x()) { - Console.WriteLine(); - } - if (a != x()) { - Console.WriteLine(); - } - if (a > x()) { - Console.WriteLine(); - } - - if (x() == a) { - Console.WriteLine(); - } - if (x() != a) { - Console.WriteLine(); - } - if (x() > a) { - Console.WriteLine(); - } + // Tests deactivated because we insert redundant casts; + // TODO: revisit after decision has been made regarding the type system. + //if (a == x()) { + // Console.WriteLine(); + //} + //if (a != x()) { + // Console.WriteLine(); + //} + //if (a > x()) { + // Console.WriteLine(); + //} + + //if (x() == a) { + // Console.WriteLine(); + //} + //if (x() != a) { + // Console.WriteLine(); + //} + //if (x() > a) { + // Console.WriteLine(); + //} } public static void StructValueBasic(TS? a, TS? b, int? i) @@ -634,38 +609,40 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void StructValueComplex(TS? a, Func x, Func i) { - Console.WriteLine(a == x()); - Console.WriteLine(a != x()); - Console.WriteLine(a > x()); - - Console.WriteLine(x() == a); - Console.WriteLine(x() != a); - Console.WriteLine(x() > a); - - Console.WriteLine(a + x()); - Console.WriteLine(a - x()); - Console.WriteLine(a * x()); - Console.WriteLine(a / x()); - Console.WriteLine(a % x()); - Console.WriteLine(a & x()); - Console.WriteLine(a | x()); - Console.WriteLine(a ^ x()); - Console.WriteLine(a << i()); - Console.WriteLine(a >> i()); - Console.WriteLine(a ?? x()); - a += x(); - a -= x(); - a *= x(); - a /= x(); - a %= x(); - a &= x(); - a |= x(); - a ^= x(); - a <<= i(); - a >>= i(); - - Console.WriteLine(x() + a); - (new TS?[0])[0] += x(); + // Tests deactivated because we insert redundant casts; + // TODO: revisit after decision has been made regarding the type system. + //Console.WriteLine(a == x()); + //Console.WriteLine(a != x()); + //Console.WriteLine(a > x()); + + //Console.WriteLine(x() == a); + //Console.WriteLine(x() != a); + //Console.WriteLine(x() > a); + + //Console.WriteLine(a + x()); + //Console.WriteLine(a - x()); + //Console.WriteLine(a * x()); + //Console.WriteLine(a / x()); + //Console.WriteLine(a % x()); + //Console.WriteLine(a & x()); + //Console.WriteLine(a | x()); + //Console.WriteLine(a ^ x()); + //Console.WriteLine(a << i()); + //Console.WriteLine(a >> i()); + //Console.WriteLine(a ?? x()); + //a += x(); + //a -= x(); + //a *= x(); + //a /= x(); + //a %= x(); + //a &= x(); + //a |= x(); + //a ^= x(); + //a <<= i(); + //a >>= i(); + + //Console.WriteLine(x() + a); + //(new TS?[0])[0] += x(); } public static bool RetEq(int? a, int? b) @@ -822,7 +799,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - class LiftedImplicitConversions + internal class LiftedImplicitConversions { public int? ExtendI4(byte? b) { @@ -854,140 +831,144 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return b; } - public double? ToFloat(int? b) - { - return b; - } + // TODO: unnecessary cast + //public double? ToFloat(int? b) + //{ + // return b; + //} - public long? InArithmetic(uint? b) - { - return 100L + b; - } + //public long? InArithmetic(uint? b) + //{ + // return 100L + b; + //} public long? AfterArithmetic(uint? b) { return 100 + b; } - static double? InArithmetic2(float? nf, double? nd, float f) - { - return nf + nd + f; - } + // TODO: unnecessary cast + //public static double? InArithmetic2(float? nf, double? nd, float f) + //{ + // return nf + nd + f; + //} - static long? InArithmetic3(int? a, long? b, int? c, long d) + public static long? InArithmetic3(int? a, long? b, int? c, long d) { return a + b + c + d; } - - long? InReturnAfterArithmetic(int? a) - { - return a * a; - } } - class LiftedExplicitConversions + internal class LiftedExplicitConversions { - static void Print(T? x) where T : struct + private static void Print(T? x) where T : struct { Console.WriteLine(x); } - static void UncheckedCasts(int? i4, long? i8, float? f) + public static void UncheckedCasts(int? i4, long? i8, float? f) { - Print((byte?)i4); - Print((short?)i4); - Print((uint?)i4); - Print((uint?)i8); - Print((uint?)f); + LiftedExplicitConversions.Print((byte?)i4); + LiftedExplicitConversions.Print((short?)i4); + LiftedExplicitConversions.Print((uint?)i4); + LiftedExplicitConversions.Print((uint?)i8); + LiftedExplicitConversions.Print((uint?)f); } - static void CheckedCasts(int? i4, long? i8, float? f) + public static void CheckedCasts(int? i4, long? i8, float? f) { checked { - Print((byte?)i4); - Print((short?)i4); - Print((uint?)i4); - Print((uint?)i8); - Print((uint?)f); + LiftedExplicitConversions.Print((byte?)i4); + LiftedExplicitConversions.Print((short?)i4); + LiftedExplicitConversions.Print((uint?)i4); + LiftedExplicitConversions.Print((uint?)i8); + //LiftedExplicitConversions.Print((uint?)f); TODO } } } - class NullCoalescingTests + internal class NullCoalescingTests { - static void Print(T x) + private static void Print(T x) { Console.WriteLine(x); } - static void Objects(object a, object b) + public static void Objects(object a, object b) { - Print(a ?? b); + NullCoalescingTests.Print(a ?? b); } - static void Nullables(int? a, int? b) + public static void Nullables(int? a, int? b) { - Print(a ?? b); + NullCoalescingTests.Print(a ?? b); } - static void NullableWithNonNullableFallback(int? a, int b) + public static void NullableWithNonNullableFallback(int? a, int b) { - Print(a ?? b); + NullCoalescingTests.Print(a ?? b); } - static void NullableWithImplicitConversion(short? a, int? b) + public static void NullableWithImplicitConversion(short? a, int? b) { - Print(a ?? b); + NullCoalescingTests.Print(a ?? b); } - static void NullableWithImplicitConversionAndNonNullableFallback(short? a, int b) + public static void NullableWithImplicitConversionAndNonNullableFallback(short? a, int b) { - Print(a ?? b); + // TODO: unnecessary cast + //NullCoalescingTests.Print(a ?? b); } - static void Chain(int? a, int? b, int? c, int d) + public static void Chain(int? a, int? b, int? c, int d) { - Print(a ?? b ?? c ?? d); + NullCoalescingTests.Print(a ?? b ?? c ?? d); } - static void ChainWithImplicitConversions(int? a, short? b, long? c, byte d) + public static void ChainWithImplicitConversions(int? a, short? b, long? c, byte d) { - Print(a ?? b ?? c ?? d); + // TODO: unnecessary casts + //NullCoalescingTests.Print(a ?? b ?? c ?? d); } - static void ChainWithComputation(int? a, short? b, long? c, byte d) + public static void ChainWithComputation(int? a, short? b, long? c, byte d) { - Print((a + 1) ?? (b + 2) ?? (c + 3) ?? (d + 4)); + // TODO: unnecessary casts + //NullCoalescingTests.Print((a + 1) ?? (b + 2) ?? (c + 3) ?? (d + 4)); } - static object ReturnObjects(object a, object b) + public static object ReturnObjects(object a, object b) { return a ?? b; } - static int? ReturnNullables(int? a, int? b) + public static int? ReturnNullables(int? a, int? b) { return a ?? b; } - static int ReturnNullableWithNonNullableFallback(int? a, int b) + public static int ReturnNullableWithNonNullableFallback(int? a, int b) { return a ?? b; } - static int ReturnChain(int? a, int? b, int? c, int d) + public static int ReturnChain(int? a, int? b, int? c, int d) { return a ?? b ?? c ?? d; } - static long ReturnChainWithImplicitConversions(int? a, short? b, long? c, byte d) + public static long ReturnChainWithImplicitConversions(int? a, short? b, long? c, byte d) { - return a ?? b ?? c ?? d; + //TODO: unnecessary casts + //return a ?? b ?? c ?? d; + return 0L; } - static long ReturnChainWithComputation(int? a, short? b, long? c, byte d) + public static long ReturnChainWithComputation(int? a, short? b, long? c, byte d) { - return (a + 1) ?? (b + 2) ?? (c + 3) ?? (d + 4); + //TODO: unnecessary casts + //return (a + 1) ?? (b + 2) ?? (c + 3) ?? (d + 4); + return 0L; } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index 0ee050f4a..8d11720be 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -346,8 +347,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms return new Comp(newComparisonKind, ComparisonLiftingKind.CSharp, comp.InputType, comp.Sign, left, right) { ILRange = Instruction.ILRange }; - } else if (Instruction is Call call && newComparisonKind == Kind) { - return new Call(CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method)) { + } else if (Instruction is Call call) { + IMethod method; + if (newComparisonKind == Kind) { + method = call.Method; + } else if (newComparisonKind == ComparisonKind.Inequality && call.Method.Name == "op_Equality") { + method = call.Method.DeclaringType.GetMethods(m => m.Name == "op_Inequality") + .FirstOrDefault(m => ParameterListComparer.Instance.Equals(m.Parameters, call.Method.Parameters)); + if (method == null) + return null; + } else { + return null; + } + return new Call(CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(method)) { Arguments = { left, right }, ConstrainedTo = call.ConstrainedTo, ILRange = call.ILRange, @@ -636,7 +648,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else if (inst is Conv conv) { var (arg, bits) = DoLift(conv.Argument); if (arg != null) { - if (conv.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { + if (conv.HasDirectFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { // Cannot execute potentially-throwing instruction unless all // the nullableVars are arguments to the instruction // (thus causing it not to throw when any of them is null). @@ -658,7 +670,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else if (inst is BinaryNumericInstruction binary) { var (left, right, bits) = DoLiftBinary(binary.Left, binary.Right); if (left != null && right != null) { - if (binary.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { + if (binary.HasDirectFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { // Cannot execute potentially-throwing instruction unless all // the nullableVars are arguments to the instruction // (thus causing it not to throw when any of them is null).