{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/4135","labels_url":"https://api.github.com/repos/dotnet/roslyn/issues/4135/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/4135/comments","events_url":"https://api.github.com/repos/dotnet/roslyn/issues/4135/events","html_url":"https://github.com/dotnet/roslyn/issues/4135","id":97454591,"number":4135,"title":"Async method on struct mutating copy","user":{"login":"YuvalItzchakov","id":3448320,"avatar_url":"https://avatars.githubusercontent.com/u/3448320?v=3","gravatar_id":"","url":"https://api.github.com/users/YuvalItzchakov","html_url":"https://github.com/YuvalItzchakov","followers_url":"https://api.github.com/users/YuvalItzchakov/followers","following_url":"https://api.github.com/users/YuvalItzchakov/following{/other_user}","gists_url":"https://api.github.com/users/YuvalItzchakov/gists{/gist_id}","starred_url":"https://api.github.com/users/YuvalItzchakov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/YuvalItzchakov/subscriptions","organizations_url":"https://api.github.com/users/YuvalItzchakov/orgs","repos_url":"https://api.github.com/users/YuvalItzchakov/repos","events_url":"https://api.github.com/users/YuvalItzchakov/events{/privacy}","received_events_url":"https://api.github.com/users/YuvalItzchakov/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-07-27T12:43:40Z","updated_at":"2015-07-27T13:46:22Z","closed_at":null,"body":"Following this question on SO: http://stackoverflow.com/questions/31642535/structs-private-field-value-is-not-updated-using-an-async-method/31646624#31646624\r\n\r\nThe following program will print 35, and not 45. This happens because the async generated state machine lifts a copy of the struct by copying the `this` parameter. \r\n\r\nI know that using mutable structs are evil, but still, this can lead to some confusion. Was this intentional behavior?\r\n\r\n    public static void Main(string[] args)\r\n    {\r\n         var sInstance = new Structure(25);\r\n         sInstance.Change(35);\r\n         await sInstance.ChangeAsync(45);\r\n    }\r\n\r\n    public struct Structure \r\n    {\r\n\t   private int _Value;\r\n\r\n\t   public Structure(int iValue) \r\n\t   {\r\n\t\t  _Value = iValue;\r\n\t   }\r\n\r\n\t   public void Change(int iValue)\r\n\t   {\r\n\t\t  _Value = iValue;\r\n\t   }\r\n\r\n\t   public async Task ChangeAsync(int iValue)\r\n\t   {\r\n\t\t  await Task.Delay(1);\r\n\t  \t  _Value = iValue;\r\n\t   }\r\n    }\r\n\r\nThis is the decompiled method:\r\n\r\n    [DebuggerStepThrough, AsyncStateMachine(typeof(Program.Structure.<ChangeAsync>d__4))]\r\n    public Task ChangeAsync(int iValue)\r\n    {\r\n\t   Program.Structure.<ChangeAsync>d__4 <ChangeAsync>d__;\r\n       // Lifting the copy here.\r\n\t   <ChangeAsync>d__.<>4__this = this;\r\n\t   <ChangeAsync>d__.iValue = iValue;\r\n\t   <ChangeAsync>d__.<>t__builder = AsyncTaskMethodBuilder.Create();\r\n\t   <ChangeAsync>d__.<>1__state = -1;\r\n\t   AsyncTaskMethodBuilder <>t__builder = <ChangeAsync>d__.<>t__builder;\r\n\t   <>t__builder.Start<Program.Structure.<ChangeAsync>d__4>(ref <ChangeAsync>d__);\r\n\t   return <ChangeAsync>d__.<>t__builder.Task;\r\n    }"},"comment":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/comments/125209853","html_url":"https://github.com/dotnet/roslyn/issues/4135#issuecomment-125209853","issue_url":"https://api.github.com/repos/dotnet/roslyn/issues/4135","id":125209853,"user":{"login":"controlflow","id":194765,"avatar_url":"https://avatars.githubusercontent.com/u/194765?v=3","gravatar_id":"","url":"https://api.github.com/users/controlflow","html_url":"https://github.com/controlflow","followers_url":"https://api.github.com/users/controlflow/followers","following_url":"https://api.github.com/users/controlflow/following{/other_user}","gists_url":"https://api.github.com/users/controlflow/gists{/gist_id}","starred_url":"https://api.github.com/users/controlflow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/controlflow/subscriptions","organizations_url":"https://api.github.com/users/controlflow/orgs","repos_url":"https://api.github.com/users/controlflow/repos","events_url":"https://api.github.com/users/controlflow/events{/privacy}","received_events_url":"https://api.github.com/users/controlflow/received_events","type":"User","site_admin":false},"created_at":"2015-07-27T13:46:22Z","updated_at":"2015-07-27T13:46:22Z","body":"In short: unfortunately, it is impossible to implement this mutation in CLR :)\r\n\r\nIn C#, we have some protection from this kind of struct mutability confusion when using closures inside struct declaration members:\r\n\r\n```c#\r\nstruct S {\r\n  public int Value;\r\n    \r\n  void M() {\r\n    Action f = () => {\r\n      Value ++; // CS1673\r\n    };\r\n  }\r\n}\r\n```\r\n\r\n> Error CS1673: Anonymous methods, lambda expressions, and query expressions inside structs cannot access instance members of 'this'. Consider copying 'this' to a local variable outside the anonymous method, lambda expression or query expression and using the local instead\r\n\r\nSince execution of every closure can be arbitrarily delayed, we need some way to also delay the lifetime of all the members captured into closure. There is no way to do it in general for `this` of value type, since value type can be allocated on stack (local variables of value types) and stack space will be reused on method execution exit.\r\n\r\nIn theory, when value type is stored as a field of some managed object/element of array, C# can emit closure code to do struct mutation inplace. Unfortunately, there is no knowledge on where `this` value is located when emitting struct member code, so C# decided simply to force users handle this situation manually (by copying the `this` value most of the time, as error message suggested).\r\n\r\nUnfortunately, different decision was made for iterator methods declared on `struct` declarations:\r\n\r\n```c#\r\nusing System.Collections;\r\n\r\nstruct S {\r\n    int Value;\r\n    \r\n    IEnumerable I() {\r\n        Value++;\r\n        yield break;\r\n    }\r\n}\r\n```\r\n\r\nThis code compiles fine and always operates on `S` value copy, since there is no way to put reference to arbitrary `S` value inside compiler-generated iterator class that implements `IEnumerable` interface. I was unable to find mentions of this behavior in C# 4.0 spec...\r\n\r\nAbsolutely the same story happens with `async` methods. There are ways to reference arbitrary values in CLR via managed references types (they are used for `ref`/`out`-parameters in CLR), but storing such references inside closure fields produce unverifiable MSIL, because of unpredictable-in-general lifetime of the value I described above."}}