Search Unity

Why does the error message never tell which variable is null?

Discussion in 'General Discussion' started by DragonCoder, Oct 31, 2022.

  1. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,696
    There's one thing that always frustrated me.
    Let's say we have a code line like this:
    Code (CSharp):
    1.  
    2. effect.float_value.value = effect_ref.slider.value;
    3.  
    Now there can be four variables in this line that can be null and they all throw exactly the same error:
    Why is that so? Why can't the error handling tell me which variable exactly is null?
    Is that an inherent weakness of C# that this information is lost when compiling even in debug mode?
     
  2. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,433
    Names of variables are not stored in the call stack, and often not even available within the virtual machine interpreting the bytecompiled code, so there's no opportunity to add it. The data call stack is all that the exception handler has to go by to form an error message.

    Think about writing a function. The caller supplies arguments, not variables. The callee receives those argument values into entirely new and different names.

    Code (CSharp):
    1. public float GetSum(float a, float b)
    2. {
    3.     return a + b;
    4. }
    5.  
    6. // ...
    7.  
    8. public void DoSomethingInteresting()
    9. {
    10.     float total = GetSum(3 + Mathf.sin(1), 34.6f);
    11. }
    Note that DoSomethingInteresting() did not pass names of variables to GetSum(), it passed values. The same is true for the internally defined "dot operator" in C#. The dot operator takes a left argument which should be an object, and a right argument which should be a field name. The left argument might be null, which causes the exception, but what its name was in the code has been lost if it was ever known, by the time the dot operator is working at this level.

    This is not just a C# issue, it's pretty common across many languages.

    @Kurt-Dekker will probably follow up to add his macro-micro-rant about how any line of code with four dots is just being mean to yourself.
     
    Last edited: Oct 31, 2022
    DragonCoder and angrypenguin like this.
  3. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    Like halley said, variable name is a thing that frequently does not exist at runtime.

    If you want to track where the problem happened, well, that's what stack trace is for. It could happen seven levels deep within the assignment.

    Additional issue is that you're dealing with C# so you don't know which one of them is a variable.
    For example, effect_ref, effect_ref.slider, effect_ref.slider.value, effect, effect.float_value could all be properties returning a dynamic value.
     
  4. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    To be fair, though, debug stack traces already include a bunch of other data which doesn't exist at runtime, e.g. line numbers. So I think that taking the question one step further and wondering why variable names can't also be represented in or used by that additional data is fair.

    Thinking waaay back to when I made a primitive language as a part of a university assignment... a human readable stack trace was straightforward because it required only information which was already present in the stack*. But getting variable names in there would have introduced the significant complication and overhead of needing to generate extra data at runtime. Each time a function was called I wouldn't need to just copy relevant values onto the stack, I'd also have to store information about where they were being copied from. Furthermore, I think that my de-referencing would then become more complicated in order to be able to use that. All of which is a significant runtime overhead which offers no value until things break in a very specific (albeit common) way.

    I suspect that's the answer. Nobody wants that in their runtime all of the time when we can attach a debugger in just the particular instances where we need it.

    * This was an interpreted language. For a compiled language we'd have needed to first save out associations between compiled instructions and their positions in the source to look up when needed.
     
    Gekigengar, JoNax97 and DragonCoder like this.
  5. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    It's a limitation of C# itself, like other said. What you can do to improve debugging on your side is to break accesses into multiple lines by storing things in temporary local variables, AKA: reduce the number of dots in a single line as much as possible.
     
  6. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,566
    That depends on the language and build settings.

    In release C++ build you get nothing. Well, you get stack pointer and memory dump from nearby location, but that's it.

    To get more, you need debug build which will be noticeably (if not significantly) slower and debug information, which can be huge. It is larger than sizes of your exes and dlls by factor of 10 if not 100. For example, debug information for unreal engine takes something like 20 or 30 gigabytes. If you don't have it, you don't know where the editor has crashed.

    Every feature has a cost. "Name of the variable" also has it. What's more it is something you very rarely need. That's why nobody bothers with it, as "normal" procedure is to jump in with a debugger.
    ------
    I'd like to remind that in C# 8 C# introduced non-nullable references.
    Now, that requires a compile time flag, but the idea is that by default a reference cannot be null, unless marked as nullable explicitly. That's a good feature, and while I'm not /quite/ sure how well that interacts with unity, I do recommend to use this approach if you have an opportunity.
     
  7. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    Probably not too useful in Unity due to it being possible to have a valid C# object that points to a null/destroyed C++ Unity object.
     
    Kiwasi and JoNax97 like this.
  8. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,145
    Last edited: Oct 31, 2022