Browse Source

[nullables] operator! on bool?

pull/870/head
Daniel Grunwald 8 years ago
parent
commit
96c6b03d9b
  1. 8
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 2
      ICSharpCode.Decompiler/IL/Instructions/Comp.cs
  3. 20
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  4. 20
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  5. 13
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

8
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -416,6 +416,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -416,6 +416,14 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitComp(Comp inst, TranslationContext context)
{
if (inst.LiftingKind == ComparisonLiftingKind.ThreeValuedLogic) {
if (inst.Kind == ComparisonKind.Equality && inst.Right.MatchLdcI4(0)) {
// lifted logic.not
var targetType = NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean));
var arg = Translate(inst.Left).ConvertTo(targetType, this);
return new UnaryOperatorExpression(UnaryOperatorType.Not, arg.Expression)
.WithRR(new OperatorResolveResult(targetType, ExpressionType.Not, arg.ResolveResult))
.WithILInstruction(inst);
}
return ErrorExpression("Nullable comparisons with three-valued-logic not supported in C#");
}
if (inst.Kind.IsEqualityOrInequality()) {

2
ICSharpCode.Decompiler/IL/Instructions/Comp.cs

@ -110,7 +110,7 @@ namespace ICSharpCode.Decompiler.IL @@ -110,7 +110,7 @@ namespace ICSharpCode.Decompiler.IL
/// that is, if any input operand is <c>null</c>, the comparison evaluates to <c>null</c>.
/// </summary>
/// <remarks>
/// This lifting kind is currently not used.
/// This lifting kind is currently only used for operator! on bool?.
/// </remarks>
ThreeValuedLogic
}

20
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -56,6 +56,8 @@ namespace ICSharpCode.Decompiler.IL @@ -56,6 +56,8 @@ namespace ICSharpCode.Decompiler.IL
if (inst == null)
throw new ArgumentNullException(nameof(inst));
Debug.Assert(!this.IsDescendantOf(inst), "ILAst must form a tree");
// If a call to ReplaceWith() triggers the "ILAst must form a tree" assertion,
// make sure to read the remarks on the ReplaceWith() method.
}
[Conditional("DEBUG")]
@ -368,13 +370,27 @@ namespace ICSharpCode.Decompiler.IL @@ -368,13 +370,27 @@ namespace ICSharpCode.Decompiler.IL
}
}
#endregion
/// <summary>
/// Replaces this ILInstruction with the given replacement instruction.
/// </summary>
/// <remarks>
/// It is temporarily possible for a node to be used in multiple places in the ILAst,
/// this method only replaces this node at its primary position (see remarks on <see cref="Parent"/>).
///
/// This means you cannot use ReplaceWith() to wrap an instruction in another node.
/// For example, <c>node.ReplaceWith(new BitNot(node))</c> will first call the BitNot constructor,
/// which sets <c>node.Parent</c> to the BitNot instance.
/// The ReplaceWith() call then attempts to set <c>BitNot.Argument</c> to the BitNot instance,
/// which creates a cyclic ILAst. Meanwhile, node's original parent remains unmodified.
///
/// The solution in this case is to avoid using <c>ReplaceWith</c>.
/// If the parent node is unknown, the following trick can be used:
/// <code>
/// node.Parent.Children[node.ChildIndex] = new BitNot(node);
/// </code>
/// Unlike the <c>ReplaceWith()</c> call, this will evaluate <c>node.Parent</c> and <c>node.ChildIndex</c>
/// before the <c>BitNot</c> constructor is called, thus modifying the expected position in the ILAst.
/// </remarks>
public void ReplaceWith(ILInstruction replacement)
{
@ -586,6 +602,8 @@ namespace ICSharpCode.Decompiler.IL @@ -586,6 +602,8 @@ namespace ICSharpCode.Decompiler.IL
{
Debug.Assert(GetChild(newChild.ChildIndex) == newChild);
Debug.Assert(!this.IsDescendantOf(newChild), "ILAst must form a tree");
// If a call to ReplaceWith() triggers the "ILAst must form a tree" assertion,
// make sure to read the remarks on the ReplaceWith() method.
newChild.parent = this;
if (refCount > 0)
newChild.AddRef();

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

@ -359,5 +359,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -359,5 +359,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
return inst as CallInstruction;
}
/// <summary>
/// Gets whether arg can be un-inlined out of stmt.
/// </summary>
internal static bool CanUninline(ILInstruction arg, ILInstruction stmt)
{
Debug.Assert(arg.IsDescendantOf(stmt));
for (ILInstruction inst = arg; inst != stmt; inst = inst.Parent) {
if (!inst.SlotInfo.CanInlineInto)
return false;
// Check whether re-ordering with predecessors is valid:
int childIndex = inst.ChildIndex;
for (int i = 0; i < childIndex; ++i) {
ILInstruction predecessor = inst.Parent.Children[i];
if (!SemanticHelper.MayReorder(arg, predecessor))
return false;
}
}
return true;
}
}
}

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

@ -451,6 +451,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -451,6 +451,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms
};
return (newInst, bits);
}
} else if (inst is Comp comp && !comp.IsLifted && comp.Kind == ComparisonKind.Equality
&& MatchGetValueOrDefault(comp.Left, out ILVariable v)
&& NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean)
&& comp.Right.MatchLdcI4(0)
) {
// C# doesn't support ComparisonLiftingKind.ThreeValuedLogic,
// except for operator! on bool?.
var (arg, bits) = DoLift(comp.Left);
Debug.Assert(arg != null);
var newInst = new Comp(comp.Kind, ComparisonLiftingKind.ThreeValuedLogic, comp.InputType, comp.Sign, arg, comp.Right.Clone()) {
ILRange = comp.ILRange
};
return (newInst, bits);
}
return (null, null);
}

Loading…
Cancel
Save