Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving incorrect function at compile time BUG?

Tags:

c#

delegates

I'm using Visual Studio 2012 Update 1 and .NET 4.5 here is the code.

void Test(Action a) { }
void Test(Func<int> a) { }
void TestError()
{
    bool throwException = true;
    //Resolves to Test(Action a)
    Test(() =>
    {
    });
    //Resolves to Test(Action a)
    Test(() =>
    {
        if (throwException) throw new Exception();
    });
    //Resolves to Test(Func<int> a)
    //(This seems like a bug since there is no return value)
    Test(() =>
    {
        throw new Exception();
    });
    //Resolves to Test(Action a)
    //(With warning unreachable code detected)
    Test(() =>
    {
        throw new Exception();
        return; //unreachable code detected
    });
}

It appears the last function call is incorrectly resolving to Func instead of Action and it has something to do with unconditionally throwing an exception.

Is this a BUG? Thanks.

like image 236
Aaron Stainback Avatar asked Feb 21 '13 13:02

Aaron Stainback


1 Answers

Well, it seems reasonable to me that both are valid candidates. In other words, I think it's fine to have a conversion of () => { throw new Exception(); } to both Func<int> and Action. In both cases the end of the method is unreachable - it would be entirely valid to put that into a normal named method like this:

public int GoBang()
{
    throw new Exception();
}

and then write:

Func<int> foo = GoBang;

So, the conversions from the lambda expression to both Func<int> and Action are valid. We now need to pick which method to use. This is specified in spec section 7.5.3.2 (better function member), which includes this:

For at least one argument, the conversion from Ex to Px is better than the conversion from from Ex to Qx

At this point, section 7.5.3.3 (better conversion from expression) kicks in:

C1 is a better conversion than C2 if at least one of the following holds:

  • [...]
  • E is an anonymous function, T1 is either a delegate type D1 or an expression tree type Expression<D1>, T2 is either a delegate type D2 or an expression tree type Expression<D2>, and one of the following holds:

    • [...]
    • D1 and D2 have identical parameter lists, and one of the following holds:
    • D1 has a return type Y, and D2 is void returning.

So that's why Func<int> is preferred over Action... the compiler's doing the right thing.

like image 97
Jon Skeet Avatar answered Oct 15 '22 10:10

Jon Skeet