Browse Source

Fix #1924: don't undo the compiler's &&-to-& optimization

ExpressionBuilder printed bitwise & / | on booleans as && / || whenever
the right-hand side was pure, but Roslyn lowers && / || to & / | only
when the right operand is a bare local or parameter read
(LocalRewriter.MakeBinaryOperator, unchanged since 2014). Shapes like
(c == 'a') | (c == 'b') can therefore only originate from a bitwise
source operator, yet were shown as short-circuiting. Per the discussion
in #1545, show the operator the IL actually uses instead of guessing the
source form: the reversal is dropped entirely, so Roslyn-compiled
"a && b" now decompiles to "a & b", which recompiles to the same IL.

Assisted-by: Claude:claude-fable-5:Claude Code
pull/3911/head
Siegfried Pammer 2 months ago committed by Siegfried Pammer
parent
commit
753f1d2481
  1. 18
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs
  2. 8
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs
  3. 16
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

18
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs

@ -326,13 +326,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -326,13 +326,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
#endif
public void PreferLogicalToBitwise(bool a, bool b, int i, float f)
public void BitwiseBooleanOperators(bool a, bool b, int i, float f)
{
B(a && b);
B(a & b);
B(a & (i == 1));
B((i == 1) & a);
B((i > i - 3) & a);
B((f < 0.1f) & a);
B(a | b);
B(a && i == 1);
B(i == 1 && a);
B(i > i - 3 && a);
B(f < 0.1f && a);
B(a || i == 1);
}
public bool BitwiseOrWithComparisons(char c)
{
return (c == 'a') | (c == 'b');
}
}
}

8
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs

@ -1282,7 +1282,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1282,7 +1282,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#if ROSLYN || OPT
public static void SingleIf1(int i, bool a)
{
if (i == 1 || (i == 2 && a))
if (i == 1 || ((i == 2) & a))
{
Console.WriteLine(1);
}
@ -1292,7 +1292,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1292,7 +1292,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void SingleIf2(int i, bool a, bool b)
{
if (i == 1 || (i == 2 && a) || (i == 3 && b))
if (i == 1 || ((i == 2) & a) || ((i == 3) & b))
{
Console.WriteLine(1);
}
@ -1301,7 +1301,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1301,7 +1301,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void SingleIf3(int i, bool a, bool b)
{
if (a || i == 1 || (i == 2 && b))
if (a || i == 1 || ((i == 2) & b))
{
Console.WriteLine(1);
}
@ -1310,7 +1310,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1310,7 +1310,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void SingleIf4(int i, bool a)
{
if (i == 1 || i == 2 || (i != 3 && a) || i != 4)
if (i == 1 || i == 2 || ((i != 3) & a) || i != 4)
{
Console.WriteLine(1);
}

16
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1650,22 +1650,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1650,22 +1650,6 @@ namespace ICSharpCode.Decompiler.CSharp
right.ResolveResult));
}
}
if (op.IsBitwise()
&& left.Type.IsKnownType(KnownTypeCode.Boolean)
&& right.Type.IsKnownType(KnownTypeCode.Boolean)
&& SemanticHelper.IsPure(inst.Right.Flags))
{
// Undo the C# compiler's optimization of "a && b" to "a & b".
if (op == BinaryOperatorType.BitwiseAnd)
{
op = BinaryOperatorType.ConditionalAnd;
}
else if (op == BinaryOperatorType.BitwiseOr)
{
op = BinaryOperatorType.ConditionalOr;
}
}
if (op.IsBitwise() && (left.Type.Kind == TypeKind.Enum || right.Type.Kind == TypeKind.Enum))
{
left = AdjustConstantExpressionToType(left, right.Type);

Loading…
Cancel
Save