Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not all code paths return, but compiler treats it as if all paths return

Tags:

c#

compilation

I can't think of a good title, but my question is not as naive as it appears.

Consider this:

public static void ExitApp(string message)
{
    // Do stuff
    throw new Exception(...);
}

OR

public static void ExitApp(string message)
{
    // Do stuff
    System.Environment.Exit(-1);
}

Neither of these methods will ever return. But when you invoke these methods elsewhere:

public int DoStuff()
{
    // Do stuff
    if (foo == 0)
    {
        throw new Exception(...);
    }
    else if (foo == 1)
    {
        // Do other stuff
        return ...;
    }
    else
    {
        ExitApp("Something borked");
    }
}

Try to compile that and you will get a "not all code paths return a value" in DoStuff. It seems silly to trail the call to ExitApp with an Exception just to satisfy the compiler even though I know that it's good. There seems to be nothing in ExitApp() that I can do to indicate it will never return.

How can I indicate to the compiler that ExitApp never returns and, thus, that DoStuff's else block will never return either? It seems like a rather simple bug that it's path checking fails to account for.

Even if I only use the first ExitApp (throws the exception) and that method returns an int the path checker is smart enough to realize that it will never return so it doesn't complain about the int type. This compiles file:

public static int ExitApp(string message)
{
    // Do stuff
    throw new Exception(...);
}

However, given that it knows this ExitApp will never return an int it does not extrapolate that to DoStuff() so I'm inclined to believe there is no solution to my question. My only choice is to throw an exception after calling ExitApp.

public int DoStuff()
{
    ...
    else
    {
        ExitApp("Something borked");
        throw new NotImplementedException("Should not reach this");
    }
}

Is there a reason for this behavior by the compiler?

like image 815
Colin Burnett Avatar asked Nov 29 '22 05:11

Colin Burnett


1 Answers

I have an exception defined for this purpose: UnreachableException. It might seem superfluous, but it's an easy way to say "Hey, person reading this, this line should never be executed!". I usually use it for the default case of some switch statements, but it applies here as well.

Just throw one after the ExitApp line.

public void int DoStuff()
{
    // Do stuff
    if (foo == 0)
    {
        throw new Exception(...);
    }
    else if (foo == 1)
    {
        // Do other stuff
        return ...;
    }
    else
    {
        ExitApp("Something borked");
        throw new UnreachableException();
    }
}

The actual reason the language doesn't support declaring a method which always throws is just: it's not worth it. The language developers don't have unlimited time to apply every feature we can think of. They have to prioritize.

I'm betting this is the first time you've run into this situation, and look: explicitly throwing an exception deals with the issue. Why would they bother dealing with such a rare, easy to bypass case? They could be spending that time implementing optional parameters, dynamic, or a bunch of other things that will be more useful and used more often than being able to say a function always throws an exception.

That's not to say it will never be implemented. This type of method information is exactly the type of thing contracts are great at specifying. So maybe it will be included with code contracts.

like image 64
Craig Gidney Avatar answered Dec 15 '22 14:12

Craig Gidney