13

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.

Jeff Moser
  • 19,727
  • 6
  • 65
  • 85
  • 2
    Note that *debug symbols generated* and *optimizations generated* are actually orthogonal. It is legal, though somewhat unusual, to turn on debug symbols and optimizations at the same time. Might want to double-check that you are not in that odd configuration. – Eric Lippert Aug 15 '11 at 16:02
  • The "Optimize code" checkbox under the "Build" tab is unchecked for my active (Debug/x64) configuration. Should I check somewhere else? – Jeff Moser Aug 15 '11 at 16:05
  • Nope; sounds like that isn't the problem. – Eric Lippert Aug 15 '11 at 16:13
  • just tried it (optimize code off, debug on) and can see the described behaviour - in x86 diractly after the line SomeData != null while in x64 it "becomes != null" when first accessed (not from debugger but in code)... could be interesting to compare what the generated IL looks like... – Yahia Aug 15 '11 at 16:15
  • I repro on VS2010 SP1. The debugger loses its marbles pretty badly at that statement. Adding a line and setting a breakpoint on it doesn't work either. You can report the bug at connect.microsoft.com. Debugging the x86 build is generally highly advisable. – Hans Passant Aug 15 '11 at 16:18
  • @Eric Lippert: Can you confirm this is a bug? If so, is there already a Connect case for it? – Jeff Moser Aug 15 '11 at 16:28
  • 3
    @Jeff: It certainly sounds like it is a bug, but I have not reproduced it and so I cannot confirm it. Sounds like a good candidate for posting on Connect. I do not know if there is already a case. I note that over the years there have been a number of odd bugs involving code generation and debugging for the conditional operator; the precise CLR rules for determining statically what the known type of a stack slot is can be a bit tricky and that leads to bugs. It would not surprise me if this were another one of those, but like I said, I haven't personally repro'd it. – Eric Lippert Aug 15 '11 at 16:47
  • @Eric: Thanks, I've filed a Connect bug for this: https://connect.microsoft.com/VisualStudio/feedback/details/684202 – Jeff Moser Aug 15 '11 at 17:01
  • @Eric I use that "pdb with opt on" all the time. If your app is in house you get nicer stack traces (line numbers, if occasionally taken with a pinch of salt) with them present at the cost of deploying more bits. – ShuggyCoUk Aug 16 '11 at 09:13

2 Answers2

9

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! :)

Jeff Moser
  • 19,727
  • 6
  • 65
  • 85
4

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...

Yahia
  • 69,653
  • 9
  • 115
  • 144
  • Why should you have to put {}'s around it? – Jeff Moser Aug 15 '11 at 16:41
  • I don't say you have to... just something that seemed working after Reflector gaved me some weird code when disassembling that line... – Yahia Aug 15 '11 at 16:42
  • It seems like a bug (perhaps just in the compiler as you suggest). I'm working around it by converting it to an if statement even though ReSharper thinks I'm an idiot for doing that :) – Jeff Moser Aug 15 '11 at 16:43
  • nevermind what ReSharper says in this case... except ReSharper has some brilliant idea to solve it :-) – Yahia Aug 15 '11 at 16:45