Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the order of both a return and a throws statement cause different warnings about unreachable code [duplicate]

Why do the following lines of code not create a compiler warning?

void Main()
{
  throw new Exception();
  throw new Exception();
}

As I see it, the compiler should inform you that the second throw exception cannot be reached.

like image 792
DaveShaw Avatar asked Jun 16 '11 12:06

DaveShaw


People also ask

What is unreachable code in c#?

Unreachable code is a program code fragment which is never executed. Don't mix it up with dead code which is, unlike unreachable code, a code fragment that can be executed but whose result is never used in any other computation. Presence of unreachable code in a program is determined by a number of factors.

When unreachable code?

In computer programming, unreachable code is part of the source code of a program which can never be executed because there exists no control flow path to the code from the rest of the program.

Does unreachable code compile?

If any code can not be executable in any of the possible flows, then it is called unreachable code. Unreachable code in java is a compile time error. Look at the following example.


3 Answers

It is clearly a compiler bug, and it was introduced in C# 3.0 -- right around the time that I heavily refactored the reachability checker. This is probably my bad, sorry.

The bug is completely benign; basically, we just forgot a case in the warning reporter. We generate the reachability information correctly; as others have noted, we correctly trim out the unreachable code before codegen.

The bug is nothing more than a missing case in the warning generator. We have some tricky code in there that ensures that we do not report a zillion warnings when you make some large section of code unreachable. The compiler has code for specifically reporting warnings on unconditional gotos ("goto", "break", "continue"), conditional gotos ("if", "while" and so on), try-catch-finally (which includes forms equivalent to try-catch-finally, like lock and using), blocks, returns (yield return and regular return), local declarations, labelled statements, switches, and expression statements.

Do you see "throw statements" on that list? Me neither. That's because we forgot it.

Apologies for the inconvenience. I'll send a note to QA and we'll get a fix for this into a future version of the language.

Thanks for bringing this to my attention.

like image 132
Eric Lippert Avatar answered Oct 04 '22 17:10

Eric Lippert


It could give a compiler warning / error but sadly it does not. But if you look at IL code only first exception is regarded. You can log in to connect.microsoft.com and raise this as something you would like to see.

if you ILDasm the code below

static void Main(string[] args)
        {
            Console.Write("Line 1");
           throw new Exception(); 
           throw new Exception();
           Console.Write("Line 4");
        }

You will get this

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       18 (0x12)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "Line 1"
  IL_0006:  call       void [mscorlib]System.Console::Write(string)
  IL_000b:  nop
  IL_000c:  newobj     instance void [mscorlib]System.Exception::.ctor()
  IL_0011:  throw
} // end of method Program::Main

After the first Exception object nothing else is converted to IL.

like image 29
Ash Avatar answered Oct 01 '22 17:10

Ash


This bug (as Lippert calls it above) has some strange consequences. Of course code like this also gives no compile-time warnings:

static int Main()
{
  return 0;

  throw new Exception("Can you reach me?");
}

If you're creative, you can still make the throw statement induce (unrelated) warnings. In this curious example, the code generates a warning only because "green" is unreachable:

static int Main()
{
  return 0;

  throw new Exception(((Func<string>)(() => { if (2 == 2) { return "yellow"; } return "green"; }))());
}

(code just creates a delegate instance from a lambda and invokes the delegate).

But this example is simpler and seems worse:

static int Main()
{
  int neverAssigned;

  return 0;

  throw new Exception(neverAssigned.ToString());
}

This last code sample also compiles with no warning! There's no problem in "using" neverAssigned because the "usage" is unreachable. But you also get no warning about a local variable never assigned to (and never "really" read). So to repeat, no warning at all, which seems very wrong.

I wonder if this behavior will be changed in future versions of Visual C#? Changing it will give people warnings they didn't have before (which in my opinion they deserve).

Addition: This behavior appears to be unchanged with the Roslyn-based C# 6.0 compiler of Visual Studio 2015.

like image 25
Jeppe Stig Nielsen Avatar answered Oct 03 '22 17:10

Jeppe Stig Nielsen