Browse Source

TransformExpressionTrees: keep a constant's declared type

A constant narrower than its stack type builds as a plain ldc.i4, which
infers as int, so a conditional whose other branch really is a char saw
two different types and the whole tree was left as the Expression calls
that built it - EF Core's StringCharConverter.ToChar is one. Bool and
enum constants were already wrapped for this reason; the wrap now
applies wherever the built value does not infer as the declared type.

Assisted-by: Claude:claude-opus-5:Claude Code
pull/4091/head
Siegfried Pammer 1 week ago
parent
commit
03410c4455
  1. 7
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  2. 13
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

7
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs

@ -416,6 +416,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -416,6 +416,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
ToCode(X(), () => "abc"[1] == 'b');
}
public void ConditionalWithNarrowConstant(string s)
{
ToCode(X(), () => (s.Length < 1) ? '\0' : s[0]);
ToCode(X(), () => (s.Length < 1) ? ((byte)0) : ((byte)s[0]));
ToCode(X(), () => (s.Length < 1) ? ((short)0) : ((short)s[0]));
}
public void StringsImplicitCast()
{
int i = 1;

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

@ -1156,9 +1156,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -1156,9 +1156,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null;
if (value.MatchBox(out var arg, out var boxType))
{
if (boxType.Kind == TypeKind.Enum || boxType.IsKnownType(KnownTypeCode.Boolean))
return () => new ExpressionTreeCast(boxType, ConvertValue(arg, invocation), false);
return () => ConvertValue(arg, invocation);
return () => {
// A constant narrower than its stack type - a bool, a char, an enum, one of
// the small integers - builds as a plain ldc.i4 that infers as int, and
// consumers compare inferred types. The cast keeps the type the tree
// declared for it.
var constantValue = ConvertValue(arg, invocation);
if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(constantValue.InferType(context.TypeSystem), boxType))
return new ExpressionTreeCast(boxType, constantValue, false);
return constantValue;
};
}
return () => ConvertValue(value, invocation);

Loading…
Cancel
Save