James Michael Hare's answer gives the de jure explanation: the local variable is definitely assigned, because the code is unreachable and all local variables are definitely assigned in unreachable code. Put another way: the program is only an error if there is a way to observe the state of the uninitialized local variable. In your program there is no way to observe the local, and therefore it is not an error.
Now, I note that the compiler is not required to be infinitely clever. For example:
void M()
{
int x = 0;
int y;
if (x + 0 == x) return;
Console.WriteLine(y);
}
You know and I know that the last line of the method is unreachable, but the compiler does not know that because the reachability analyzer does not know that zero is the additive identity of integers. The compiler thinks the last line might be reachable, and so gives an error.
For more information on aspects of designing reachability and definite assignment analyzers in programming languages, see my articles on the subject:
http://blogs.msdn.com/b/ericlippert/archive/tags/reachability/
http://blogs.msdn.com/b/ericlippert/archive/tags/definite+assignment/
I note though that no one has answered the deeper question, which is why should the error be suppressed in unreachable code? As you note, we give other semantic analysis errors in unreachable code.
To consider the pros and cons of that decision, you have to think about why someone would have unreachable code in the first place. Either it is intentionally unreachable, or unintentionally unreachable.
If it is unintentionally unreachable then the program contains a bug. The warning already calls attention to the primary problem: the code is unreachable. There is something majorly wrong with the control flow of the method if there is unreachable code. Odds are good that the developer is going to have to make a serious change to the control flow of the method; any local variable analysis we do on the unreachable code is likely to be misleading noise. Let the developer fix the code so that everything is reachable, and then we'll do an analysis of the now-reachable code for control-flow-related errors.
If the unreachable code is unreachable because the developer intended it to be unreachable, then odds are good they are doing something like this:
// If we can Blah, then Frob. However, if we cannot Blah and we can Baz, then Foo.
void M()
{
int y;
// TODO: The Blah method has a bug and always throws right now; fix it later.
if (false /* Blah(out y) */ )
{
Frob(y);
}
else if (Baz(out y))
{
Foo(y);
}
}
Should Frob(y)
be an error in this program?