Browse Source

Fix #991: explicit represent T->T? conversion in lifted operator calls.

pull/1012/head
Daniel Grunwald 8 years ago
parent
commit
d949d4e638
  1. 4
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  2. 52
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

4
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -282,6 +282,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
var parent = loadInst.Parent;
if (NullableLiftingTransform.MatchNullableCtor(parent, out _, out _)) {
// inline into nullable ctor call in lifted operator
parent = parent.Parent;
}
if (parent is ILiftableInstruction liftable && liftable.IsLifted) {
return true; // inline into lifted operators
}

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

@ -334,6 +334,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public ILInstruction Left;
public ILInstruction Right;
public IType LeftExpectedType {
get {
if (Instruction is Call call) {
return call.Method.Parameters[0].Type;
} else {
return SpecialType.UnknownType;
}
}
}
public IType RightExpectedType {
get {
if (Instruction is Call call) {
return call.Method.Parameters[1].Type;
} else {
return SpecialType.UnknownType;
}
}
}
internal ILInstruction MakeLifted(ComparisonKind newComparisonKind, ILInstruction left, ILInstruction right)
{
if (Instruction is Comp comp) {
@ -420,7 +440,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary>
ILInstruction LiftCSharpComparison(CompOrDecimal comp, ComparisonKind newComparisonKind)
{
var (left, right, bits) = DoLiftBinary(comp.Left, comp.Right);
var (left, right, bits) = DoLiftBinary(comp.Left, comp.Right, comp.LeftExpectedType, comp.RightExpectedType);
// due to the restrictions on side effects, we only allow instructions that are pure after lifting.
// (we can't check this before lifting due to the calls to GetValueOrDefault())
if (left != null && right != null && SemanticHelper.IsPure(left.Flags) && SemanticHelper.IsPure(right.Flags)) {
@ -541,7 +561,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
if (liftedOperator != null) {
context.Step("Lift user-defined comparison operator", trueInst);
var (left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1]);
var (left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1],
call.Method.Parameters[0].Type, call.Method.Parameters[1].Type);
if (left != null && right != null && bits.All(0, nullableVars.Count)) {
return new Call(liftedOperator) {
Arguments = { left, right },
@ -661,7 +682,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (newInst, bits);
}
} else if (inst is BinaryNumericInstruction binary) {
var (left, right, bits) = DoLiftBinary(binary.Left, binary.Right);
var (left, right, bits) = DoLiftBinary(binary.Left, binary.Right, SpecialType.UnknownType, SpecialType.UnknownType);
if (left != null && right != null) {
if (binary.HasDirectFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) {
// Cannot execute potentially-throwing instruction unless all
@ -704,7 +725,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
newArgs = new[] { arg };
newBits = bits;
} else if (call.Arguments.Count == 2) {
var (left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1]);
var (left, right, bits) = DoLiftBinary(call.Arguments[0], call.Arguments[1],
call.Method.Parameters[0].Type, call.Method.Parameters[1].Type);
newArgs = new[] { left, right };
newBits = bits;
} else {
@ -726,17 +748,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, null);
}
(ILInstruction, ILInstruction, BitSet) DoLiftBinary(ILInstruction lhs, ILInstruction rhs)
(ILInstruction, ILInstruction, BitSet) DoLiftBinary(ILInstruction lhs, ILInstruction rhs, IType leftExpectedType, IType rightExpectedType)
{
var (left, leftBits) = DoLift(lhs);
var (right, rightBits) = DoLift(rhs);
if (left != null && right == null && SemanticHelper.IsPure(rhs.Flags)) {
// Embed non-nullable pure expression in lifted expression.
right = rhs.Clone();
right = NewNullable(rhs.Clone(), rightExpectedType);
}
if (left == null && right != null && SemanticHelper.IsPure(lhs.Flags)) {
// Embed non-nullable pure expression in lifted expression.
left = lhs.Clone();
left = NewNullable(lhs.Clone(), leftExpectedType);
}
if (left != null && right != null) {
var bits = leftBits ?? rightBits;
@ -747,6 +769,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, null, null);
}
}
private ILInstruction NewNullable(ILInstruction inst, IType underlyingType)
{
if (underlyingType == SpecialType.UnknownType)
return inst;
var nullable = context.TypeSystem.Compilation.FindType(KnownTypeCode.NullableOfT).GetDefinition();
var ctor = nullable?.Methods.FirstOrDefault(m => m.IsConstructor && m.Parameters.Count == 1);
if (ctor != null) {
ctor = ctor.Specialize(new TypeParameterSubstitution(new[] { underlyingType }, null));
return new NewObj(ctor) { Arguments = { inst } };
} else {
return inst;
}
}
#endregion
#region Match...Call
@ -799,7 +835,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Matches 'newobj Nullable{underlyingType}.ctor(arg)'
/// </summary>
static bool MatchNullableCtor(ILInstruction inst, out IType underlyingType, out ILInstruction arg)
internal static bool MatchNullableCtor(ILInstruction inst, out IType underlyingType, out ILInstruction arg)
{
underlyingType = null;
arg = null;

Loading…
Cancel
Save