Browse Source

Make NullableLiftingTransform handle the case where ExpressionTransforms.VisitComp already lifted a nullable comparison with constant.

pull/3243/head
Siegfried Pammer 12 months ago
parent
commit
28aa88bfb8
  1. 3
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  2. 31
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

3
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -106,7 +106,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
inst.Left.AcceptVisitor(this); inst.Left.AcceptVisitor(this);
return; return;
} }
if (context.Settings.LiftNullables)
{
new NullableLiftingTransform(context).Run(inst); new NullableLiftingTransform(context).Run(inst);
}
base.VisitComp(inst); base.VisitComp(inst);
if (inst.IsLifted) if (inst.IsLifted)

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

@ -225,14 +225,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst) return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst)
?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Inequality, trueInst); ?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Inequality, trueInst);
} }
else if (IsGenericNewPattern(comp.Left, comp.Right, trueInst, falseInst)) else if (!comp.IsLifted && IsGenericNewPattern(comp.Left, comp.Right, trueInst, falseInst))
{ {
// (default(T) == null) ? Activator.CreateInstance<T>() : default(T) // (default(T) == null) ? Activator.CreateInstance<T>() : default(T)
// => Activator.CreateInstance<T>() // => Activator.CreateInstance<T>()
return trueInst; return trueInst;
} }
} }
else else if (!comp.IsLifted)
{ {
// Not (in)equality, but one of < <= > >=. // Not (in)equality, but one of < <= > >=.
// Returns false unless all HasValue bits are true. // Returns false unless all HasValue bits are true.
@ -401,11 +401,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
result = default(CompOrDecimal); result = default(CompOrDecimal);
result.Instruction = inst; result.Instruction = inst;
if (inst is Comp comp && !comp.IsLifted) if (inst is Comp comp)
{ {
result.Kind = comp.Kind; result.Kind = comp.Kind;
result.Left = comp.Left; result.Left = comp.Left;
result.Right = comp.Right; result.Right = comp.Right;
result.IsLifted = comp.IsLifted;
return true; return true;
} }
else if (inst is Call call && call.Method.IsOperator && call.Arguments.Count == 2 && !call.IsLifted) else if (inst is Call call && call.Method.IsOperator && call.Arguments.Count == 2 && !call.IsLifted)
@ -449,6 +450,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public ComparisonKind Kind; public ComparisonKind Kind;
public ILInstruction Left; public ILInstruction Left;
public ILInstruction Right; public ILInstruction Right;
public bool IsLifted;
public IType LeftExpectedType { public IType LeftExpectedType {
get { get {
@ -528,6 +530,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// The HasValue comparison must be the same operator as the Value comparison. // The HasValue comparison must be the same operator as the Value comparison.
if (hasValueTest is Comp hasValueComp) if (hasValueTest is Comp hasValueComp)
{ {
if (valueComp.IsLifted || hasValueComp.IsLifted)
return null;
// Comparing two nullables: HasValue comparison must be the same operator as the Value comparison // Comparing two nullables: HasValue comparison must be the same operator as the Value comparison
if ((hasValueTestNegated ? hasValueComp.Kind.Negate() : hasValueComp.Kind) != newComparisonKind) if ((hasValueTestNegated ? hasValueComp.Kind.Negate() : hasValueComp.Kind) != newComparisonKind)
return null; return null;
@ -576,6 +580,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary> /// </summary>
ILInstruction LiftCSharpComparison(CompOrDecimal comp, ComparisonKind newComparisonKind) ILInstruction LiftCSharpComparison(CompOrDecimal comp, ComparisonKind newComparisonKind)
{ {
if (comp.IsLifted)
{
// Happens when legacy csc generates 'num.GetValueOrDefault() == const && num.HasValue',
// checking HasValue is redundant here, modern Roslyn versions optimize it and our
// NullableLifting transform on Comp will already lift the lhs of the logic.and.
// Treat this case as if the transform had not undone the optimization yet.
if (nullableVars.Count != 1)
{
return null;
}
if (comp.Left.MatchLdLoc(nullableVars[0]) || comp.Right.MatchLdLoc(nullableVars[0]))
{
return comp.MakeLifted(newComparisonKind, comp.Left.Clone(), comp.Right.Clone());
}
return null;
}
var (left, right, bits) = DoLiftBinary(comp.Left, comp.Right, comp.LeftExpectedType, comp.RightExpectedType); 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())
@ -611,7 +633,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// call op_Inequality(call GetValueOrDefault(ldloca nullable1), call GetValueOrDefault(ldloca nullable2)) // call op_Inequality(call GetValueOrDefault(ldloca nullable1), call GetValueOrDefault(ldloca nullable2))
// else // else
// ldc.i4 0 // ldc.i4 0
if (hasValueComp.IsLifted)
return null;
if (!MatchHasValueCall(hasValueComp.Left, out ILVariable nullable1)) if (!MatchHasValueCall(hasValueComp.Left, out ILVariable nullable1))
return null; return null;
if (!MatchHasValueCall(hasValueComp.Right, out ILVariable nullable2)) if (!MatchHasValueCall(hasValueComp.Right, out ILVariable nullable2))

Loading…
Cancel
Save