Search code examples
c#warningsunreachable-code

"Unreachable code detected" in MSIL or Native code


Does compiler compile "Unreachable Codes" in MSIL or Native code in run time?


Solution

  • The question is a bit unclear but I'll take a shot at it.

    First off, Adam's answer is correct insofar as there is a difference in the IL that the compiler emits based on whether the "optimize" switch is on or off. The compiler is much more aggressive about removing unreachable code with the optimize switch on.

    There are two kinds of unreachable code that are relevant. First there is de jure unreachable code; that is, code that the C# language specification calls out as unreachable. Second, there is de facto unreachable code; that is code that the C# specification does not call out as unreachable, but nevertheless, cannot be reached. Of the latter kind of unreachable code, there is code that is known to the optimizer to be unreachable, and there is code not known to the optimizer to be unreachable.

    The compiler typically always removes de jure unreachable code, but only removes de facto unreachable code if the optimizer is turned on.

    Here's an example of each:

    int x = 123;
    int y = 0;
    if (false) Console.WriteLine(1);
    if (x * 0 != 0) Console.WriteLine(2);
    if (x * y != 0) Console.WriteLine(3);
    

    All three Console.WriteLines are unreachable. The first is de jure unreachable; the C# compiler states that this code must be treated as unreachable for the purposes of definite assignment checking.

    The second two are de jure reachable but de facto unreachable. They must be checked for definite assignment errors, but the optimizer is permitted to remove them.

    Of the two, the optimizer detects the (2) case but not the (3) case. The optimizer knows that an integer multiplied by zero is always zero, and that therefore the condition is always false, so it removes the entire statement.

    In the (3) case the optimizer does not track the possible values assigned to y and determine that y is always zero at the point of the multiplication. Even though you and I know that the consequence is unreachable, the optimizer does not know that.

    The bit about definite assignment checking goes like this: if you have an unreachable statement then all local variables are considered to be assigned in that statement, and all assignments are considered to not happen:

    int z;
    if (false) z = 123;
    Console.WriteLine(z); // Error
    if (false) Console.WriteLine(z); // Legal
    

    The first usage is illegal because z has not been definitely assigned when it is used. The second usage is not illegal because the code isn't even reachable; z can't be used before it is assigned because control never gets there!

    C# 2 had some bugs where it confused the two kinds of reachability. In C# 2 you could do this:

    int x = 123;
    int z;
    if (x * 0 != 0) Console.WriteLine(z);
    

    And the compiler would not complain, even though de jure the call to Console.WriteLine is reachable. I fixed that in C# 3.0 and we took the breaking change.

    Note that we reserve the right to change up how the unreachable code detector and code generator work at any time; we might decide to always emit the unreachable code or never emit it or whatever.