From cea8ff2dbbed9db1e81afc336f0511c0c23ce382 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 13 Sep 2026 15:22:41 +0200 Subject: [PATCH] Use type `bool` for `logic.and`/`logic.or`. --- .../IL/Instructions/IfInstruction.cs | 27 ++++++++++++------- .../IL/Transforms/ExpressionTransforms.cs | 27 ++++++++++++++----- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs index ceafb59e7..32b135440 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs @@ -39,7 +39,15 @@ namespace ICSharpCode.Decompiler.IL partial class IfInstruction : ILInstruction { // null means void - readonly IType? resultType; + IType? csharpType; + + public IType? CSharpType { + get => csharpType; + set { + Debug.Assert(this.ResultType == (value?.GetStackType() ?? StackType.Void)); + csharpType = value; + } + } public IfInstruction(ILInstruction condition, ILInstruction trueInst, ILInstruction? falseInst = null, IType? resultType = null) : base(OpCode.IfInstruction) @@ -48,7 +56,7 @@ namespace ICSharpCode.Decompiler.IL this.TrueInst = trueInst; falseInst ??= new Nop(); this.FalseInst = falseInst; - this.resultType = resultType; + this.csharpType = resultType; Debug.Assert(condition.ResultType == StackType.I4); Debug.Assert(trueInst.ResultType == this.ResultType || trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)); @@ -80,15 +88,15 @@ namespace ICSharpCode.Decompiler.IL public override StackType ResultType { get { - if (resultType != null) - return resultType.GetStackType(); + if (csharpType != null) + return csharpType.GetStackType(); return StackType.Void; } } public override IType InferType(ICompilation compilation) { - if (resultType != null) - return resultType; + if (csharpType != null) + return csharpType; return compilation.FindType(KnownTypeCode.Void); } public override InstructionFlags DirectFlags => InstructionFlags.ControlFlow; @@ -101,7 +109,8 @@ namespace ICSharpCode.Decompiler.IL protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); - if (options.UseLogicOperationSugar) + if (options.UseLogicOperationSugar + && csharpType != null && csharpType.IsKnownType(KnownTypeCode.Boolean)) { if (MatchLogicAnd(out var lhs, out var rhs)) { @@ -123,10 +132,10 @@ namespace ICSharpCode.Decompiler.IL } } output.Write(OpCode); - if (resultType != null) + if (csharpType != null) { output.Write(" ["); - output.Write(resultType.ReflectionName); + output.Write(csharpType.ReflectionName); output.Write(']'); } output.Write(" ("); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index d317578ab..9638eaa99 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -588,18 +588,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms IfInstruction HandleConditionalOperator(IfInstruction inst) { // if (cond) stloc A(V1) else stloc A(V2) --> stloc A(if (cond) V1 else V2) - Block trueInst = inst.TrueInst as Block; - if (trueInst == null || trueInst.Instructions.Count != 1) + if (inst.TrueInst is not Block trueInst || trueInst.Instructions.Count != 1) return inst; - Block falseInst = inst.FalseInst as Block; - if (falseInst == null || falseInst.Instructions.Count != 1) + if (inst.FalseInst is not Block falseInst || falseInst.Instructions.Count != 1) return inst; ILVariable v; ILInstruction value1, value2; - if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2)) + if (trueInst.Instructions[0].MatchStLoc(out v, out value1) + && falseInst.Instructions[0].MatchStLoc(v, out value2)) { context.Step("conditional operator", inst); - var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1, v.Type); + IType type = v.Type; + // Try to tighten the type; this matters esp. for logic.and/logic.or: + IType type1 = value1.InferType(context.TypeSystem); + IType type2 = value2.InferType(context.TypeSystem); + if (type1.IsKnownType(KnownTypeCode.Boolean) && ( + type2.IsKnownType(KnownTypeCode.Boolean) + || value2 is LdcI4 { Value: 0 or 1 } + )) + { + type = type1; + } + else if (type2.IsKnownType(KnownTypeCode.Boolean) + && value1 is LdcI4 { Value: 0 or 1 }) + { + type = type2; + } + var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1, type); newIf.AddILRange(inst); var stLoc = new StLoc(v, newIf); inst.ReplaceWith(stLoc);