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; 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) { if (parent is ILiftableInstruction liftable && liftable.IsLifted) {
return true; // inline into lifted operators 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 Left;
public ILInstruction Right; 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) internal ILInstruction MakeLifted(ComparisonKind newComparisonKind, ILInstruction left, ILInstruction right)
{ {
if (Instruction is Comp comp) { if (Instruction is Comp comp) {
@ -420,7 +440,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary> /// </summary>
ILInstruction LiftCSharpComparison(CompOrDecimal comp, ComparisonKind newComparisonKind) 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. // 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()) // (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)) { 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) { if (liftedOperator != null) {
context.Step("Lift user-defined comparison operator", trueInst); 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)) { if (left != null && right != null && bits.All(0, nullableVars.Count)) {
return new Call(liftedOperator) { return new Call(liftedOperator) {
Arguments = { left, right }, Arguments = { left, right },
@ -661,7 +682,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (newInst, bits); return (newInst, bits);
} }
} else if (inst is BinaryNumericInstruction binary) { } 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 (left != null && right != null) {
if (binary.HasDirectFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { if (binary.HasDirectFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) {
// Cannot execute potentially-throwing instruction unless all // Cannot execute potentially-throwing instruction unless all
@ -704,7 +725,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
newArgs = new[] { arg }; newArgs = new[] { arg };
newBits = bits; newBits = bits;
} else if (call.Arguments.Count == 2) { } 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 }; newArgs = new[] { left, right };
newBits = bits; newBits = bits;
} else { } else {
@ -726,17 +748,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, null); 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 (left, leftBits) = DoLift(lhs);
var (right, rightBits) = DoLift(rhs); var (right, rightBits) = DoLift(rhs);
if (left != null && right == null && SemanticHelper.IsPure(rhs.Flags)) { if (left != null && right == null && SemanticHelper.IsPure(rhs.Flags)) {
// Embed non-nullable pure expression in lifted expression. // 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)) { if (left == null && right != null && SemanticHelper.IsPure(lhs.Flags)) {
// Embed non-nullable pure expression in lifted expression. // Embed non-nullable pure expression in lifted expression.
left = lhs.Clone(); left = NewNullable(lhs.Clone(), leftExpectedType);
} }
if (left != null && right != null) { if (left != null && right != null) {
var bits = leftBits ?? rightBits; var bits = leftBits ?? rightBits;
@ -747,6 +769,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (null, null, null); 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 #endregion
#region Match...Call #region Match...Call
@ -799,7 +835,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary> /// <summary>
/// Matches 'newobj Nullable{underlyingType}.ctor(arg)' /// Matches 'newobj Nullable{underlyingType}.ctor(arg)'
/// </summary> /// </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; underlyingType = null;
arg = null; arg = null;

Loading…
Cancel
Save