Browse Source

[nullables] Lift user-defined equality operator calls.

pull/892/head
Daniel Grunwald 8 years ago
parent
commit
87a979b549
  1. 70
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

70
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -138,11 +138,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (falseInst.MatchLdcI4(0)) { if (falseInst.MatchLdcI4(0)) {
// (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue == b.HasValue) : false // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue == b.HasValue) : false
// => a == b // => a == b
return LiftCSharpEqualityComparison(comp, ComparisonKind.Equality, trueInst);
return LiftCSharpEqualityComparison(comp, ComparisonKind.Equality, trueInst)
?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Equality, trueInst);
} else if (falseInst.MatchLdcI4(1)) { } else if (falseInst.MatchLdcI4(1)) {
// (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue != b.HasValue) : true // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue != b.HasValue) : true
// => a != b // => a != b
return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst);
return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst)
?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Inequality, trueInst);
} else if (IsGenericNewPattern(condition, trueInst, falseInst)) { } else if (IsGenericNewPattern(condition, trueInst, falseInst)) {
// (default(T) == null) ? Activator.CreateInstance<T>() : default(T) // (default(T) == null) ? Activator.CreateInstance<T>() : default(T)
// => Activator.CreateInstance<T>() // => Activator.CreateInstance<T>()
@ -347,6 +349,70 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null; return null;
} }
ILInstruction LiftCSharpUserEqualityComparison(Comp hasValueComp, ComparisonKind newComparisonKind, ILInstruction nestedIfInst)
{
// User-defined equality operator:
// if (comp(call get_HasValue(ldloca nullable1) == call get_HasValue(ldloca nullable2)))
// if (logic.not(call get_HasValue(ldloca nullable)))
// ldc.i4 1
// else
// call op_Equality(call GetValueOrDefault(ldloca nullable1), call GetValueOrDefault(ldloca nullable2)
// else
// ldc.i4 0
// User-defined inequality operator:
// if (comp(call get_HasValue(ldloca nullable1) != call get_HasValue(ldloca nullable2)))
// ldc.i4 1
// else
// if (call get_HasValue(ldloca nullable))
// call op_Inequality(call GetValueOrDefault(ldloca nullable1), call GetValueOrDefault(ldloca nullable2))
// else
// ldc.i4 0
if (!MatchHasValueCall(hasValueComp.Left, out var nullable1))
return null;
if (!MatchHasValueCall(hasValueComp.Right, out var nullable2))
return null;
if (!nestedIfInst.MatchIfInstruction(out var condition, out var trueInst, out var falseInst))
return null;
while (condition.MatchLogicNot(out var arg)) {
condition = arg;
Swap(ref trueInst, ref falseInst);
}
if (!MatchHasValueCall(condition, out var nullable))
return null;
if (nullable != nullable1 && nullable != nullable2)
return null;
if (!falseInst.MatchLdcI4(newComparisonKind == ComparisonKind.Equality ? 1 : 0))
return null;
if (!(trueInst is Call call))
return null;
if (!(call.Method.IsOperator && call.Arguments.Count == 2))
return null;
if (call.Method.Name != (newComparisonKind == ComparisonKind.Equality ? "op_Equality" : "op_Inequality"))
return null;
var liftedOperator = CSharp.Resolver.CSharpOperators.LiftUserDefinedOperator(call.Method);
if (liftedOperator == null)
return null;
nullableVars = new List<ILVariable> { nullable1 };
var (left, leftBits) = DoLift(call.Arguments[0]);
nullableVars[0] = nullable2;
var (right, rightBits) = DoLift(call.Arguments[1]);
if (left != null && right != null && leftBits[0] && rightBits[0]
&& SemanticHelper.IsPure(left.Flags) && SemanticHelper.IsPure(right.Flags)
) {
context.Step("NullableLiftingTransform: C# user-defined (in)equality comparison", nestedIfInst);
return new Call(liftedOperator) {
Arguments = { left, right },
ConstrainedTo = call.ConstrainedTo,
ILRange = call.ILRange,
ILStackWasEmpty = call.ILStackWasEmpty,
IsTail = call.IsTail,
};
}
return null;
}
/// <summary> /// <summary>
/// Performs nullable lifting. /// Performs nullable lifting.
/// ///

Loading…
Cancel
Save