question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Roslyn fails to enforce definite assignment rule for a variable assigned in a local iterator function or local async function

See original GitHub issue

Roslyn fails to enforce definite assignment rules for variables which are assigned in local iterator functions or local async functions which are called before the variables in question are used.

Since these types of local functions don’t execute all statements at the first call it can’t be proven that assignment was executed.

here are some examples:

  1. Since I don’t iterate the result of Local function it’s body won’t execute at all and I will end up using unassigned local variable i;
using System;
using System.Collections.Generic;

public class C
{
  static void Main()
  {
    int i;

    Local();

    Console.WriteLine(i);

    IEnumerable<int> Local()
    {
      yield return 0;

      i = 10;
    }
  }
}
  1. This case even marks statement ‘i = 10;’ as unreachable but still allows to use the variable in the enclosing method.
using System;
using System.Collections.Generic;

public class C
{
  static void Main()
  {
    int i;

    Local();

    Console.WriteLine(i);

    IEnumerable<int> Local()
    {
      yield break;

      i = 10;
    }
  }
}
  1. It’s almost guaranteed that local variable i won’t be assigned by the time it’s used.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class C
{
  static void Main()
  {
    int i;

    Local();

    Console.WriteLine(i);

    async Task Local()
    {
      await Task.Delay(1000*60*60);
      i = 10;
    }
  }
}

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
agockecommented, Sep 19, 2016

I’m going to disallow async and iterator functions from definitely assigning variables. The behavior is just too subtle.

Compiler data flow will be fixed to recognize these branches, though.


From: TessenR notifications@github.com Sent: Monday, September 19, 2016 5:20:36 AM To: dotnet/roslyn Cc: Andy Gocke; Mention Subject: Re: [dotnet/roslyn] Roslyn fails to enforce definite assignment rule for a variable assigned in a local iterator function or local async function (#13762)

@agockehttps://na01.safelinks.protection.outlook.com/?url=https%3a%2f%2fgithub.com%2fagocke&data=02%7c01%7cangocke%40microsoft.com%7ce4b40b9577cd43bd24d608d3e0875c9b%7c72f988bf86f141af91ab2d7cd011db47%7c1%7c0%7c636098844388392240&sdata=R5kRtLRZ4tu2pEx1gc582382uQ%2fuWoXPThf9iNB42Eg%3d Is there any decision how this should be implemented? I will appreciate any clarification about expected behavior.

I personally think that compiler should not propagate writes from iterator/async local functions at all.

However if you are planning to propagate writes from iterator/async local functions I’d like to know expected results for some other cases which also don’t have compiler errors currently.

  1. enumerating local iterator via foreach:
void M()
{
    int x;

    foreach(var y in Local()) { }

    System.Console.WriteLine(x);

    System.Collections.Generic.IEnumerable<int> Local()
    {
        yield return 5;
        x = 5;
    }
}
  1. enumerating local iterator via foreach with breaks in the loop body:
void M()
{
    int x, y;

    foreach(var z in Local()) { break; }

    System.Console.WriteLine(x);
    System.Console.WriteLine(y);

    System.Collections.Generic.IEnumerable<int> Local()
    {
        x = 5; // executed
        yield return 5;
        y = 10; // not executed
    }
}
  1. Enumerating part of the collection e.g. with linq Take method

using System.Linq; class C { void M() { int x,y;

    foreach(var z in Local().Take(2)) { }

    System.Console.WriteLine(x);
    System.Console.WriteLine(y);

    System.Collections.Generic.IEnumerable<int> Local()
    {
        yield return 5;
        x = 5;
        yield return 5;
        y = 5;
    }
}

}

  1. Enumerating part of the collection when it has unknown number of elements before assignment
void M()
{
    int x;

    foreach(var z in Local().Take(int.MaxValue)) { } // is int.MaxValue enough?

    System.Console.WriteLine(x);

    System.Collections.Generic.IEnumerable<int> Local()
    {
        var random = new System.Random();
        int counter = 0;
        while(counter++ < random.Next())
            yield return 5;
        x = 5;
    }
}
  1. assignment before/after first await in local async function with non-awaited call
void M()
{
    int x, y;

    Local();

    System.Console.WriteLine(x);
    System.Console.WriteLine(y);

    async System.Threading.Tasks.Task Local()
    {
        x = 5;
        await System.Threading.Tasks.Task.Delay(1000*60*60*24);
        y = 5;
    }
}
  1. assignment if method is awaited:
async void M()
{
    int x;

    await Local();

    System.Console.WriteLine(x);

    async System.Threading.Tasks.Task Local()
    {
        await System.Threading.Tasks.Task.Delay(1000*60*60*24);
        x = 5;
    }
}

You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://na01.safelinks.protection.outlook.com/?url=https%3a%2f%2fgithub.com%2fdotnet%2froslyn%2fissues%2f13762%23issuecomment-247978035&data=02%7c01%7cangocke%40microsoft.com%7ce4b40b9577cd43bd24d608d3e0875c9b%7c72f988bf86f141af91ab2d7cd011db47%7c1%7c0%7c636098844388402249&sdata=%2fFkJ2tCJJU5%2bKwl%2fTb9pqCL6IXxte6udiLa5Bb3isiU%3d, or mute the threadhttps://na01.safelinks.protection.outlook.com/?url=https%3a%2f%2fgithub.com%2fnotifications%2funsubscribe-auth%2fAAfevv0JVwiyv8cS5J-nv9fIDOS3jfN9ks5qrn4UgaJpZM4J8AnU&data=02%7c01%7cangocke%40microsoft.com%7ce4b40b9577cd43bd24d608d3e0875c9b%7c72f988bf86f141af91ab2d7cd011db47%7c1%7c0%7c636098844388402249&sdata=d800diVw339lAANdLRpvTWPD5eF8i7qMoBZB3EJVYsM%3d.

0reactions
xieguigangcommented, Nov 17, 2016

81761b83014db0e6b95031e35c2bda65c0167ee1

Probably the LINQ operator in VisualBasic combine with lambda can be more simplify

Read more comments on GitHub >

github_iconTop Results From Across the Web

Definite assignment bug when not awaiting an async closure
The local function doesn't contain any await s itself, so it is guaranteed to execute fully even if you don't await it. If...
Read more >
Exploring Roslyn, part 3: Breaking Changes - SLaks.Blog
Local variables in your method become fields in the iterator class, so they can be persisted across calls to MoveNext() .
Read more >
c# - Cannot assign to item because it is a foreach iteration ...
c# - Cannot assign to item because it is a foreach iteration variable - Stack Overflow.
Read more >
Lambda expressions
I'll cover ways to avoid mutating variables and data structures, passing functions as data, how LINQ is functional, and some speculation on ...
Read more >
11 Efficiency and readability tweaks - C# in Depth, Fourth ...
The rules of definite assignment in C# are complicated, and local methods complicate them further. The simplest way to think about it is...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found