Update: I have filed a bug report with Microsoft Connect, please vote for it!
Update 2: Microsoft have marked the bug report as fixed
That's not cool. I think you should file a bug. (connect.microsoft.com)
Also, this seems to work (I only tested for your fail case though):
public static bool IsGuid(object item)
{
return item.GetType() == typeof(Guid);
}
Interestingly enough this works correctly:
using System;
public class Program
{
public static bool IsGuid(object item)
{
return item is Guid;
}
public static void Main()
{
Guid s = Guid.NewGuid();
Console.Write(IsGuid(s));
}
}
Here is the difference in the il:
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 23 (0x17)
.maxstack 1
.locals init (valuetype [mscorlib]System.Guid V_0)
IL_0000: call valuetype [mscorlib]System.Guid [mscorlib]System.Guid::NewGuid()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: box [mscorlib]System.Guid
IL_000c: call bool Program::IsGuid(object)
IL_0011: call void [mscorlib]System.Console::Write(bool)
IL_0016: ret
} // end of method Program::Main
Other than a few nops, reflector indictes the only differences are in the IsGuid method:
DEBUG:
.method public hidebysig static bool IsGuid(object item) cil managed
{
.maxstack 2
.locals init (
[0] bool CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: isinst [mscorlib]System.Guid
L_0007: ldnull
L_0008: cgt.un
L_000a: stloc.0
L_000b: br.s L_000d
L_000d: ldloc.0
L_000e: ret
}
RELEASE:
.method public hidebysig static bool IsGuid(object item) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: isinst [mscorlib]System.Guid
L_0006: ldnull
L_0007: cgt.un
L_0009: ret
}
I'm not fluent enough to explain these differences but this is a community wiki answer so maybe somebody else can enlighten us?
Changing the method to make it generic works around this (possible bug?)
public static bool IsGuid<T>(T item)
{
return item is Guid;
}
As does forcing it into a local varible (but it must be used in the method to prevent the optimizations kicking in):
public static bool IsGuid(object item)
{
bool a = item is Guid;
a.ToString();
return a;
}
I worked up a similar example that fails the same way:
using System;
using System.Runtime.CompilerServices;
public class Program {
static void Main() {
Console.Write(Verify(Test.Create()));
Console.ReadLine();
}
//[MethodImpl(MethodImplOptions.NoInlining)]
static bool Verify(IDisposable item) {
return item is Test;
}
struct Test : IDisposable {
public void Dispose() { }
public static Test Create() { return new Test(); }
}
}
It is a JIT optimizer bug. Can't quite put the finger on it, it optimizes the code heavily. But it looks to me like it gets in trouble when it optimizes the boxing conversion away. Pretty serious bug, frankly.
This bug has been fixed, I can no longer repro it. My current version of clrjit.dll is 4.0.30319.237 dated May 17th 2011. I can't tell exactly what update repaired it. I got a security update on Aug 5th 2011 that updated clrjit.dll to revision 235 with a date of Apr 12, that would be the earliest.
To answer your last question you can add the MethodImpl
attribute with the option MethodImplOptions.NoInlining
to your IsGuid
method as an workaround to fix the problem.
I just made a simple test of switching between Debug and Release configuration for x86 on the .NET 4.0 and this seems to resolve the problem. I haven't yet run your runtests.bat though.
You should also submit an issue in Connect if one is not yet submitted and link it from your question.
Here's my results on XP SP3 (x86):
>runtest
Compiler: Framework\v4.0.30319\csc.exe
False => x86
False => x86 (Optimized)
True => x86 (Debug)
False => x86 (Debug + Optimized)
False => AnyCPU
False => AnyCPU (Optimized)
True => AnyCPU (Debug)
False => AnyCPU (Debug + Optimized)
>runtest v3.5
Compiler: Framework\v3.5\csc.exe
True => x86
True => x86 (Optimized)
True => x86 (Debug)
True => x86 (Debug + Optimized)
True => AnyCPU
True => AnyCPU (Optimized)
True => AnyCPU (Debug)
True => AnyCPU (Debug + Optimized)
The interesting point about this result is that the .NET 4 target fails on AnyCPU (running as x86) but works on AnyCPU (running as x64).