Bigbob556677
Bigbob556677

Reputation: 2158

How does the compiler handle statements that are always true or false?

Whenever I need to temporarily disable a section of code in a block, I will wrap it in a statement that is always false, e.g.:

if (1 == 2)
{
    // disabled code
}

and when I need to re-enable it, I set the statement to evaluate to true.

if (1 == 1)
{
    // enabled code
}

Which obviously always returns false/true respectively.

How does the compiler handle an instance like this where it will obviously always fail/pass? Does it get optimized away or evaluated every single time?

Upvotes: 1

Views: 354

Answers (2)

Joe Sewell
Joe Sewell

Reputation: 6620

The C# language specification describes constant expressions, which are expressions that can be evaluated at compile-time. The compiler will calculate constant expressions and act as if the program stated the appropriate values. So if I say 1 + 2 then the compiler will treat that as 3.

The specification also describes the concept of statement reachability. Essentially, the spec includes rules for how the compiler determines whether statements can ever possibly be reached. The compiler can use constant expressions to determine whether certain paths through the code are impossible to reach.

Your case involves an if statement:

if(1==2){
    /* disabled code */
}

The rules for the if statement states:

The first embedded statement of an if statement is reachable if the if statement is reachable and the boolean expression does not have the constant value false.

(The phrase "first embedded statement" refers to the code or code block following the if, in your case the { /* disabled code */ } section. If you had an else there would be a "second embedded statement".)

To rephrase the rule, the interior of an if statement is unreachable ("dead code") if the condition for the if is something the compiler can guarantee is false. If the compiler can guarantee it's true, or if the compiler cannot make a guarantee one way or the other, then the code is reachable (assuming, of course, that wherever you put the if statement is itself reachable).


Now I don't believe the spec explicitly says what a compiler should do when it encounters unreachable code, besides requiring the compiler to emit a warning and prohibiting the compiler from emitting an error. In other words, a compiler could choose to generate IL (the code stored in .NET .dlls and .exes) for unreachable code, even though it knows that code wouldn't be used in normal execution.

I thought maybe the official C# compiler that ships with Visual Studio might choose to generate IL for unreachable code during debug builds, so you could explicitly jump into the block with a debugger. That does not appear to be the case; the following C#:

static void CallMe()
{
    if (1 == 2)
    {
        Console.WriteLine("LOL");
    }
}

compiles into the following IL when "optimize code" is off:

.method private hidebysig static void  CallMe() cil managed
{
  // Code size       6 (0x6)
  .maxstack  1
  .locals init ([0] bool V_0)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  br.s       IL_0005
  IL_0005:  ret
} // end of method Program::CallMe

This IL stores the value 0 (for false) into a temporary boolean variable - so at least the IL is "evaluating" the constant value 1 == 2 in some sense - and then there's a branch instruction that causes the code to jump... to the very next line. So it looks like the C# compiler still acts like there's an if block, just that (1) that block is empty and (2) the code jumps over that block unconditionally.

Upvotes: 1

grooveplex
grooveplex

Reputation: 2539

I tried a very simple example of your hypothetical code on .NET Fiddle and inspected the IL, and lo: the compiler detected that 1 == 2 is always false, and replaced it with... a single instruction: nop nothing. Hooray!

Then, I tried another condition, Math.E == 2, and that also worked. For good measure, I tried Math.Pow(3, 2) == 10, but that didn't get optimized away, probably because it's a method call.

TL;DR: The C# compiler seems to be kinda smart about this, but only with things that can be proven at compile time.

For a more idiomatic approach to your original question, see Henk's answer.

Upvotes: 2

Related Questions