Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected compile time error with dynamic

Clarification of question: I am not looking for answers on how to solve this issue (several are listed below), but as to why it is happening.

I expect the following code to compile:

struct Alice
{
    public string Alpha;
    public string Beta;
}

struct Bob
{
    public long Gamma;
}

static object Foo(dynamic alice)
{
    decimal alpha;
    long beta;

    if (!decimal.TryParse(alice.Alpha, out alpha) // *
        || !long.TryParse(alice.Beta, out beta)) // **
    {
        return alice;
    }

    var bob = new Bob { Gamma = beta }; // ***

    // do some stuff with alice and bob

    return alice;
}

However the following compile time error is thrown at // ***:

Use of unassigned local variable 'beta'

I can make the program compile under the following situations:

  1. If I change the signature to be

    static object Foo(Alice alice)

  2. Explicitly casting on the lines // * and // **, e.g.:

    !long.TryParse((string)alice.Beta, out beta).

  3. Removing the decimal.TryParse on line // *.

  4. Replacing the short circuit or || with |. Thanks to HansPassant

  5. Swapping the TryParses around

  6. Pulling the results of the TryParses into bools Thanks to Chris

  7. Assigning a default value to beta

Am I missing something obvious, or is there something subtle going on, or is this a bug?

like image 437
dav_i Avatar asked Jul 23 '14 09:07

dav_i


People also ask

How do I fix compile time error?

Mismatched brackets If the brackets don't all match up, the result is a compile time error. The fix to this compile error is to add a leading round bracket after the println to make the error go away: int x = 10; System.

What causes a compile time error?

A compile time error is an error that is detected by the compiler. Common causes for compile time errors include: Syntax errors such as missing semi-colon or use of a reserved keyword (such as 'class'). When you try and access a variable that is not in scope.

What are compile time errors explain with example?

The Errors that occur due to incorrect syntax are known as compile-time errors or sometimes also referred to as syntax errors in java. Examples of compile-time errors include: missing parenthesis, missing a semicolon, utilizing undeclared variables, etc.

What are the two types of compile time error?

The compile-time errors can be: Syntax errors. Semantic errors.


2 Answers

I don't know answer for sure, but for me it looks like compiler bug or "by design" issue.

I played with your sample a bit, reducing it bit by bit, and here is what left from it:

    private static bool Default<T>(out T result)
    {
        result = default(T);
        return true;
    }

    private static void Foo()
    {
        int result;

        if (true || Default(out result))
        {
            return;
        }

        Console.WriteLine(result);
    }

Which also fails with

error CS0165: Use of unassigned local variable 'result'

You can play with int result in Foo to check any type you want.

Please notice there's no dynamic usage, and also please notice true branch which should immediately return.

So for me it looks like VS.Net compiler is "not intelligent enough" here.

What is good with this piece of code - it can be compiled with compilers prior to .Net 4 (using csc.exe from appropriate frameworks), so here are the results:

  • .Net 2.0

Build ok, warnings:

warning CS0429: Unreachable expression code detected

warning CS0162: Unreachable code detected

  • .Net 3.5

Build failed:

error CS0165: Use of unassigned local variable 'result'

So if it is a bug, it appears somewhere in between .NET 2 and .NET 3.5

like image 134
Lanorkin Avatar answered Sep 24 '22 22:09

Lanorkin


It happens because the dynamic keyword causes a lot of changes in the generated code structure (generated by the C# compiler).

You can observe this using a tool such as .NET reflector (I suggest to select 'None' for C# inference so you can really see all the generated stuff). Basically, each time you access a dynamic object, the generated code adds at least an if case. These ifs can cause important code paths changes.

For example, this simple C# code

    static void MyFoo(dynamic dyn)
    {
        if (dyn == null)
            return;

        var x = dyn;
    }

is generated as:

private static void MyFoo([Dynamic] object dyn)
{
    object obj2;
    CSharpArgumentInfo[] infoArray;
    bool flag;
    if (<MyFoo>o__SiteContainer0.<>p__Site1 != null)
    {
        goto Label_0038;
    }
    <MyFoo>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, bool>>.Create(Binder.UnaryOperation(0, 0x53, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(0, null) }));
Label_0038:
    if (<MyFoo>o__SiteContainer0.<>p__Site2 != null)
    {
        goto Label_0088;
    }
    <MyFoo>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, object, object, object>>.Create(Binder.BinaryOperation(0, 13, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(0, null), CSharpArgumentInfo.Create(2, null) }));
Label_0088:
    if ((<MyFoo>o__SiteContainer0.<>p__Site1.Target(<MyFoo>o__SiteContainer0.<>p__Site1, <MyFoo>o__SiteContainer0.<>p__Site2.Target(<MyFoo>o__SiteContainer0.<>p__Site2, dyn, null)) == 0) == null)
    {
        goto Label_00AE;
    }
    obj2 = dyn;
Label_00AE:
    return;
}

Let's take a another simple example. This code:

    static void MyFoo1(dynamic dyn)
    {
        long value;

        if (long.TryParse(dyn, out value))
            return;

        var x = value;
    }

compiles fine. This one

    static void MyFoo2(dynamic dyn)
    {
        long value;

        if (true || long.TryParse(dyn, out value))
            return;

        var x = value;
    }

doesn't. If you look at the generated code (set value to 0 to make sure it compiles), it will simply contain some extra ifs and goto that change the whole code path quite drastically. I don't think it's a bug, maybe more a limitation of the dynamic keyword (that has quite a few, for example: Limitations of the dynamic type in C#)

like image 22
Simon Mourier Avatar answered Sep 20 '22 22:09

Simon Mourier