I'm using a very simple ternary expression in my C# code:
helperClass.SomeData = helperClass.HasData ? GetSomeData() : GetSomeOtherData();
In both cases, the functions on each path of the expression return a non-null object, but if I look at the result in the debugger, it is null until I reference it in the code such as using an assert:
Debug.Assert(helperClass.SomeData != null);
This only appears to happen if I use an "x64" or "Any CPU" platform setting in Debug mode. It's fine in "x86" mode.
I try to be very cautious before assuming I've found a bug in the compiler or debugger, but I can't find any other explanation for this behavior.
Here's a full class to do a repro, just call SomeClass.SomeAction() in the debugger in x64 mode and step through to see it:
public class SomeClass {
public bool HasData;
public object SomeData;
private SomeClass() {
HasData = false;
}
public static void SomeAction() {
var helperClass = new SomeClass();
// Exhibits weird debugger behavior of having helperClass.SomeData = null after this line:
helperClass.SomeData = helperClass.HasData ? GetSomeData() : GetSomeOtherData();
// Note that trying helperClass.SomeData.ToString() returns a debugger error saying SomeData is null
// But this code is just fine
//if(helperClass.HasData) {
// helperClass.SomeData = GetSomeData();
//}
//else {
// helperClass.SomeData = GetSomeOtherData();
//}
// In both cases though, after this line things are fine:
Debug.Assert(helperClass.SomeData != null);
}
private static object GetSomeData() {
return new object();
}
private static object GetSomeOtherData() {
return new object();
}
}
Am I missing something or is this a bug in the x64 debugger? I'm using debug mode so no optimizations should be present.
Taking Eric Lippert's advice that this is probably a bug, I've filed an official Connect bug for this issue: https://connect.microsoft.com/VisualStudio/feedback/details/684202
Thanks everyone for your feedback!
UPDATE: They got back to me and said they've fixed this corner case in the next version of the compiler. Hooray! :)
to me this doesn't seem a bug in the debugger but possibly of the compiler...
when changing the code to
{ helperClass.SomeData = helperClass.HasData ? GetSomeData() : GetSomeOtherData(); }
the IL generated is different and the debugger works as expected...
Related
I have some code that behaves differently between a Release build and a Debug build. It behaves correctly in Debug but not in Release.
I have a function that returns a ReadOnlyCollection<MyCustomClass>. One section is this:
var result = new List<MyCustomClass>();
...
var list1 = this.returnEmptyList();
var list2 = this.returnListWithOneItem();
if (list1.Count == 0 && list2.Count == 0)
{
functionOutVariable = string.Empty;
return result.AsReadOnly();
}
For the purposes of troubleshooting I've simplified the code and named the variables in a generic fashion, and the methods returnEmptyList and returnListWithOneItem are shown here:
private List<string> returnEmptyList()
{
return new List<string>();
}
private List<string> returnListWithOneItem()
{
return new List<string> {"something"};
}
Clearly it should never enter the if block because list2.Count should always be 1, but when I execute this in a Release build, it does:
So there's clearly some optimization going on since you can see that list1 is inaccessible, and when stepping through it executed line 416 and then jumped immediately to line 421. I should state that all the assemblies in my solution use .NET Framework 4.6.2, and I'm running Visual Studio 2017 version 15.3.5.
When I change the build to Debug and execute this, it executes line 416, 417, and on line 418 it shows list1.Count is 0 and list2.Count is 1, and it correctly does not enter the if block.
I'm trying to make a test project to reproduce this but I can't. I'm looking for any way to get to the bottom of this. I don't just want a fix that makes it go away - I need to understand what I'm doing wrong.
Ok, I'm pretty sure it's a result of a subtle bug in the rest of my function that allowed the compiler to just optimize out the if block and return early. I can reproduce the behavior of the debugger in this test project, and it totally makes sense in this case:
class Program
{
static void Main(string[] args)
{
var test = new MyClass();
test.DoTest(out var result);
Console.WriteLine(result);
Console.ReadKey();
}
}
class MyClass
{
public ReadOnlyCollection<MyCustomClass> DoTest(out string functionOutVariable)
{
var result = new List<MyCustomClass>();
var list1 = this.returnEmptyList();
var list2 = this.returnListWithOneItem();
if (list1.Count == 0 && list2.Count == 0)
{
functionOutVariable = string.Empty;
return result.AsReadOnly();
}
functionOutVariable = string.Empty;
return result.AsReadOnly();
}
private List<string> returnEmptyList()
{
return new List<string>();
}
private List<string> returnListWithOneItem()
{
return new List<string> { "something" };
}
}
class MyCustomClass
{
}
When I execute in a Release build with the debugger, it appears to enter the if block, but in reality it just optimized out the if block completely and the debugger confusingly shows it executing the lines inside the if block instead of jumping over it:
Edit: I've confirmed there was a bug later in the function that was causing my problem, and the behavior of the debugger when looking at Release build code was just causing me confusion, due to compiler optimizations.
To be clear, my question is incorrect: the function was actually giving the same result in both Release and Debug builds but I was mistaken. That's because I followed this (flawed) sequence:
I had a failing test (against a Release build).
I ran the test with the debugger (still in Release build) and saw it apparently go into the if block incorrectly.
I then switched the build to Debug and ran the test with the debugger and saw it step over the if block. I assumed (incorrectly) that was the source of my problem.
That sent me on the wild goose chase seen above. Sorry for wasting your time, but I certainly found the exercise informative. Perhaps someone else will learn from my mistake in the future. :)
After updating Windows 10 to creators update with .net 4.7 I have a critical issue on starting very simple code.
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
class Program
{
private int? m_bool;
private bool prop {get{ return false;}}
void test()
{
//nothing
}
private object Test()
{
if (prop)
{
try
{
test();
}
catch (Exception) {}
m_bool = 1;
}
return null;
}
static void Main(string[] args)
{
new Program().Test();
}
}
Seems the similar issue is https://github.com/dotnet/coreclr/issues/10826
Anyone knows how to avoid that?
The issue is caused when an optimization is running on an unreachable basic block.
In your case the compiler inlines the get_prop method (which unconditionally returns false). This leads to JIT compiler to consider the region as unreachable. Typically the JIT compiler removes the unreachable code before we run the optimization, but adding a try/catch region causes the JIT not to delete these basic blocks.
If you want to prevent the issue you could disable optimizations, disable the inlining of get_prop or change the implementation of the get_prop method to be:
static bool s_alwaysFalse = false;
private bool prop {get{ return s_alwaysFalse;}}
We have had a couple of reports of this issue and we do have a fix ready and it will be provided to users in a upcoming update.
I am trying to identify usages of the "method group" syntax in my solution. The reason is that Resharper tries to help with the ConvertClosureToMethodGroup code inspection. The problem is, that the "optimized" code does not compile to the same IL code, which means that it breaks in my specific scenario. The below scenario does not break, but it illustrates what I am trying to find:
void Main()
{
var arr = new string[]{"foo"};
//This works
var bar = arr.Select(s=>MyMethod(s));
//Resharper suggests the below, which is different
//var bar = arr.Select(MyMethod);
}
string MyMethod(string s)
{
return "bar";
}
So: Is there a way, that I can identify all the places in my code, where any method is being passed as a method group?
I can disable the refactoring suggestion to prevent future usages of this, but how can I identify the places where this already happened?
EDIT 1: Example where this refactoring breaks runtime
void Main()
{
MyClass obj = null;
//This works
var lazy = new Lazy<bool>(()=> obj.MyMethod());
//This will break at runtime when obj is null
//var lazy = new Lazy<bool>(obj.MyMethod);
}
class MyClass
{
public bool MyMethod()
{
return false;
}
}
I don't have too much knowledge of compilers and how .Net optimizes the generated machine code but would like to understand the following scenario:
class AnyClass
{
public bool anyFlag;
AnyClass()
{
anyFlag = true;
}
public void Action()
{
if(anyFlag)
//Perform Certain Actions
}
}
anyFlag does not change throughout the scope of the program. Will the compiler generate MIL/machine code for Action method? If so will it have the if check in there?
In your case the compiler won't filter the if statement because your anyFlag is public (so it can be changed from multiple places) and because the value is only is set in the consctructor the compiler also doesn't now this value until runtime.
The following code does what you want:
static void MyFunc()
{
const bool flag = true;
string s = null;
if (flag)
{
s = "a";
}
else
{
s = "b";
}
}
If you insert this code in Visual Studio you will see a generated warning stating that s="b"; will never be reached and this code will be optimized out.
Yes, the compiler will generate the IL. Since you could later user reflection or Emit to call that method or alter the value of anyFlag, it retains it. You can show this by disassembling the produced executable file using ildasm, which comes with Visual Studio.
The following code works fine until I upgrade to .NET 4 (x64)
namespace CrashME
{
class Program
{
private static volatile bool testCrash = false;
private static void Crash()
{
try
{
}
finally
{
HttpRuntime.Cache.Insert("xxx", testCrash);
}
}
static void Main(string[] args)
{
Crash();
// Works on .NET 3.5 , crash on .NET 4
}
}
}
Did I just uncover a runtime bug, or is there some issue with my usage?
This would appear to be a bug in the CLR - you should report it to Microsoft.
Note that the StackOverflowException occurs as the CLR attempts to execute the Crash, not during the execution of the Crash method - the program in fact never enters the method. This would appear to indicate that this is some low-level failure in the CLR. (Also note that the thrown exception also has no stack trace).
This exception is incredibly specific to this situation - changing any one of a number of things fixes this, for example the following code works fine:
private static void Crash()
{
bool testCrash2 = testCrash;
try { }
finally
{
HttpRuntime.Cache.Insert("xxx", testCrash2);
}
}
I would recommend that you report this to Microsoft, but attempt to work around the issue by tweaking your code in the meantime.
I can reproduce it on an x86 machine. The following code also fails:
try
{
}
finally
{
var foo = new List<object>();
foo.Add(testCrash);
}
However, the following code succeeds:
try
{
}
finally
{
var foo = new List<bool>();
foo.Add(testCrash);
}
I thought it might have something to do with the boxing of volatile fields within the finally block, but then I tried the following (which also fails):
try
{
}
finally
{
bool[] foo = new bool[1];
foo[0] = testCrash;
}
Very interesting problem...