From 34cceea4208be1ee50ea5b9b2dfee0e5ce698aa1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 5 Sep 2026 10:12:43 +0200 Subject: [PATCH] TransformExpressionTrees: emit box for a boxing Expression.Convert A conversion from a value type to a reference type produced an opaque cast, so no box instruction appeared anywhere in the converted tree, while the same C# compiled as a plain lambda yields box T. The operand type is what box takes, and it is available from the converted operand. Assisted-by: Claude:claude-opus-5[1m]:Claude Code --- .../IL/Transforms/TransformExpressionTrees.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index a7ba49c56..6579b66b5 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -913,6 +913,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// The three-argument overload carries the user-defined conversion operator, which /// includes the decimal conversions; it is lifted when the operand is Nullable<T>. + /// A conversion from a value type to a reference type is a boxing conversion and + /// produces box T(expr), where T is the operand type. /// A conversion from a small integer type to Int32 produces the operand unchanged, /// because such values already occupy an I4 stack slot. /// @@ -948,8 +950,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms var exprInst = expr(); if (exprInst == null) return null; - if (exprInst.InferType(context.TypeSystem).IsSmallIntegerType() && targetType.IsKnownType(KnownTypeCode.Int32)) + var operandType = exprInst.InferType(context.TypeSystem); + if (operandType.IsSmallIntegerType() && targetType.IsKnownType(KnownTypeCode.Int32)) return exprInst; + if (operandType.IsReferenceType == false && targetType.IsReferenceType == true) + return new Box(exprInst, operandType); return new ExpressionTreeCast(targetType, exprInst, isChecked); }; }