Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this is an ExpressionTrees bug?

using System;
using System.Linq.Expressions;

class Program
{
  static void Main()
  {
    Expression<Func<float, uint>> expr = x => (uint) x;

    Func<float,uint> converter1 = expr.Compile();
    Func<float,uint> converter2 = x => (uint) x;

    var aa = converter1(float.MaxValue); // == 2147483648
    var bb = converter2(float.MaxValue); // == 0
  }
}

Same different behavior can be founded when compiling Expression.Convert for this conversions:

Single -> UInt32 Single -> UInt64

Double -> UInt32 Double -> UInt64

Looks strange, isn't it?

<=== Added some my research ===>

I'm looked at the compiled DynamicMethod MSIL code using DynamicMethod Visualizer and some reflection hack to get DynamicMethod from the compiled Expression<TDelegate>:

Expression<Func<float, uint>> expr = x => (uint) x;

Func<float,uint> converter1 = expr.Compile();
Func<float,uint> converter2 = x => (uint) x;

// get RTDynamicMethod - compiled MethodInfo
var rtMethodInfo = converter1.Method.GetType();

// get the field with the reference
var ownerField = rtMethodInfo.GetField(
  "m_owner", BindingFlags.NonPublic | BindingFlags.Instance);

// get the reference to the original DynamicMethod
var dynMethod = (DynamicMethod) ownerField.GetValue(converter1.Method);

// show me the MSIL
DynamicMethodVisualizer.Visualizer.Show(dynMethod);

And what I get is this MSIL code:

IL_0000: ldarg.1
IL_0001: conv.i4
IL_0002: ret

And the equal C#-compiled method has this body:

IL_0000: ldarg.0
IL_0001: conv.u4
IL_0002: ret

Do anybody see now that ExpressionTrees compiles not valid code for this conversion?

like image 713
controlflow Avatar asked Nov 04 '09 16:11

controlflow


2 Answers

This is clearly a bug, and it reproduces in today's build of C# 4.0. Thank you for bringing it to our attention. Odds are good that this issue will not make the bar for fixing before the final release; at this late stage we are taking only very high-priority fixes that we have confidence will not destabilize the release. What's more likely is that a fix will make it into a future service release; but of course, no promises.

like image 142
Eric Lippert Avatar answered Oct 21 '22 12:10

Eric Lippert


I don't see a problem here. In the ideal case, you should get a compilation error in both situations. Becuase the result is actually a silent overflow. For example, the following simply would not compile:

var test = (uint)(float.MaxValue);

Does it really matter that you get different values when doing a wrong thing in the first place? If you modify your code to use checked conversion ( x => checked((uint)x) ), you'll get the same result in both scenarios - a run time exception.

like image 21
Alexandra Rusina Avatar answered Oct 21 '22 13:10

Alexandra Rusina