Browse Source

Fix two minor bugs in NullableLiftingTransform, and enable the LiftedOperators tests that are not affected by redundant casts.

pull/897/head
Daniel Grunwald 8 years ago
parent
commit
a4d4c1253e
  1. 8
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  2. 469
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs
  3. 20
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

8
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]

469
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<bool> 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)
@ -102,9 +82,6 @@ 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);
@ -123,15 +100,12 @@ 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);
@ -172,13 +146,10 @@ 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();
}
}
@ -205,13 +176,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine();
}
if (!(a == x())) {
if (!(a > x())) {
Console.WriteLine();
}
if (!(a != x())) {
Console.WriteLine();
}
if (!(a > x())) {
if (!(a <= x())) {
Console.WriteLine();
}
}
@ -245,9 +213,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);
@ -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<decimal> 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<decimal> 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<TS> 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<TS> x, Func<int> 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>(T? x) where T : struct
private static void Print<T>(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>(T x)
private static void Print<T>(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;
}
}
}

20
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).

Loading…
Cancel
Save