AsyncRewriter.AsyncIteratorRewriter.cs 39.3 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
4 5 6

using System.Collections.Immutable;
using System.Diagnostics;
7
using System.Diagnostics.CodeAnalysis;
8
using System.Linq;
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;

namespace Microsoft.CodeAnalysis.CSharp
{
    internal partial class AsyncRewriter : StateMachineRewriter
    {
        /// <summary>
        /// This rewriter rewrites an async-iterator method. See async-streams.md for design overview.
        /// </summary>
        private sealed class AsyncIteratorRewriter : AsyncRewriter
        {
            private FieldSymbol _promiseOfValueOrEndField; // this struct implements the IValueTaskSource logic
            private FieldSymbol _currentField; // stores the current/yielded value
24
            private FieldSymbol _disposeModeField; // whether the state machine is in dispose mode (ie. skipping all logic except that in `catch` and `finally`, yielding no new elements)
25
            private FieldSymbol _combinedTokensField; // CancellationTokenSource for combining tokens
26

27 28 29 30
            // true if the iterator implements IAsyncEnumerable<T>,
            // false if it implements IAsyncEnumerator<T>
            private readonly bool _isEnumerable;

31 32 33 34 35 36 37 38 39 40
            internal AsyncIteratorRewriter(
                BoundStatement body,
                MethodSymbol method,
                int methodOrdinal,
                AsyncStateMachine stateMachineType,
                VariableSlotAllocator slotAllocatorOpt,
                TypeCompilationState compilationState,
                DiagnosticBag diagnostics)
                : base(body, method, methodOrdinal, stateMachineType, slotAllocatorOpt, compilationState, diagnostics)
            {
41
                Debug.Assert(!TypeSymbol.Equals(method.IteratorElementTypeWithAnnotations.Type, null, TypeCompareKind.ConsiderEverything2));
42

43
                _isEnumerable = method.IsAsyncReturningIAsyncEnumerable(method.DeclaringCompilation);
44
                Debug.Assert(_isEnumerable != method.IsAsyncReturningIAsyncEnumerator(method.DeclaringCompilation));
45 46
            }

47 48 49
            protected override void VerifyPresenceOfRequiredAPIs(DiagnosticBag bag)
            {
                base.VerifyPresenceOfRequiredAPIs(bag);
50 51 52 53

                if (_isEnumerable)
                {
                    EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, bag);
54 55 56 57
                    EnsureWellKnownMember(WellKnownMember.System_Threading_CancellationToken__Equals, bag);
                    EnsureWellKnownMember(WellKnownMember.System_Threading_CancellationTokenSource__CreateLinkedTokenSource, bag);
                    EnsureWellKnownMember(WellKnownMember.System_Threading_CancellationTokenSource__Token, bag);
                    EnsureWellKnownMember(WellKnownMember.System_Threading_CancellationTokenSource__Dispose, bag);
58
                }
59

60 61
                EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__MoveNextAsync, bag);
                EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__get_Current, bag);
62

63
                EnsureWellKnownMember(WellKnownMember.System_IAsyncDisposable__DisposeAsync, bag);
64 65
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_ValueTask_T__ctorSourceAndToken, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_ValueTask_T__ctorValue, bag);
66
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_ValueTask__ctor, bag);
67

68 69 70 71 72 73 74
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__GetResult, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__GetStatus, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__get_Version, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__OnCompleted, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__Reset, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__SetException, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__SetResult, bag);
75 76 77 78

                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource_T__GetResult, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource_T__GetStatus, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource_T__OnCompleted, bag);
79 80 81 82

                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource__GetResult, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource__GetStatus, bag);
                EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource__OnCompleted, bag);
83 84
            }

85 86
            protected override void GenerateMethodImplementations()
            {
87
                // IAsyncStateMachine methods and constructor
88 89
                base.GenerateMethodImplementations();

90 91 92 93 94
                if (_isEnumerable)
                {
                    // IAsyncEnumerable
                    GenerateIAsyncEnumerableImplementation_GetAsyncEnumerator();
                }
95 96

                // IAsyncEnumerator
97 98
                GenerateIAsyncEnumeratorImplementation_MoveNextAsync();
                GenerateIAsyncEnumeratorImplementation_Current();
99 100

                // IValueTaskSource<bool>
101 102 103 104 105
                GenerateIValueTaskSourceBoolImplementation_GetResult();
                GenerateIValueTaskSourceBoolImplementation_GetStatus();
                GenerateIValueTaskSourceBoolImplementation_OnCompleted();

                // IValueTaskSource
106 107 108 109 110 111 112 113
                GenerateIValueTaskSourceImplementation_GetResult();
                GenerateIValueTaskSourceImplementation_GetStatus();
                GenerateIValueTaskSourceImplementation_OnCompleted();

                // IAsyncDisposable
                GenerateIAsyncDisposable_DisposeAsync();
            }

114 115 116
            protected override bool PreserveInitialParameterValuesAndThreadId
                => _isEnumerable;

117 118 119 120 121 122 123 124 125
            protected override void GenerateControlFields()
            {
                // the fields are initialized from entry-point method (which replaces the async-iterator method), so they need to be public

                base.GenerateControlFields();
                NamedTypeSymbol boolType = F.SpecialType(SpecialType.System_Boolean);

                // Add a field: ManualResetValueTaskSourceLogic<bool> promiseOfValueOrEnd
                _promiseOfValueOrEndField = F.StateMachineField(
126
                    F.WellKnownType(WellKnownType.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T).Construct(boolType),
127 128
                    GeneratedNames.MakeAsyncIteratorPromiseOfValueOrEndFieldName(), isPublic: true);

129 130 131
                // the element type may contain method type parameters, which are now alpha-renamed into type parameters of the generated class
                TypeSymbol elementType = ((AsyncStateMachine)stateMachineType).IteratorElementType;

132
                // Add a field: T current
133
                _currentField = F.StateMachineField(elementType, GeneratedNames.MakeIteratorCurrentFieldName());
134 135 136

                // Add a field: bool disposeMode
                _disposeModeField = F.StateMachineField(boolType, GeneratedNames.MakeDisposeModeFieldName());
137 138 139 140 141 142 143 144

                if (_isEnumerable && this.method.Parameters.Any(p => p is SourceComplexParameterSymbol { HasEnumeratorCancellationAttribute: true }))
                {
                    // Add a field: CancellationTokenSource combinedTokens
                    _combinedTokensField = F.StateMachineField(
                        F.WellKnownType(WellKnownType.System_Threading_CancellationTokenSource),
                        GeneratedNames.MakeAsyncIteratorCombinedTokensFieldName());
                }
145 146
            }

147
            protected override void GenerateConstructor()
148
            {
149 150 151 152 153
                // Produces:
                // .ctor(int state)
                // {
                //     this.state = state;
                //     this.initialThreadId = {managedThreadId};
154
                //     this.builder = System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Create();
155 156 157 158 159 160
                // }
                Debug.Assert(stateMachineType.Constructor is IteratorConstructor);

                F.CurrentFunction = stateMachineType.Constructor;
                var bodyBuilder = ArrayBuilder<BoundStatement>.GetInstance();
                bodyBuilder.Add(F.BaseInitialization());
161
                bodyBuilder.Add(F.Assignment(F.InstanceField(stateField), F.Parameter(F.CurrentFunction.Parameters[0]))); // this.state = state;
162 163 164

                var managedThreadId = MakeCurrentThreadId();
                if (managedThreadId != null && (object)initialThreadIdField != null)
165
                {
166
                    // this.initialThreadId = {managedThreadId};
167
                    bodyBuilder.Add(F.Assignment(F.InstanceField(initialThreadIdField), managedThreadId));
168 169
                }

170 171
                // this.builder = System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Create();
                bodyBuilder.Add(GenerateCreateAndAssignBuilder());
172

173 174 175 176
                bodyBuilder.Add(F.Return());
                F.CloseMethod(F.Block(bodyBuilder.ToImmutableAndFree()));
                bodyBuilder = null;
            }
177

178 179 180 181 182 183 184 185 186 187 188
            private BoundExpressionStatement GenerateCreateAndAssignBuilder()
            {
                // Produce:
                // this.builder = System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Create();
                return F.Assignment(
                    F.InstanceField(_builderField),
                    F.StaticCall(
                        null,
                        _asyncMethodBuilderMemberCollection.CreateBuilder));
            }

189 190
            protected override void InitializeStateMachine(ArrayBuilder<BoundStatement> bodyBuilder, NamedTypeSymbol frameType, LocalSymbol stateMachineLocal)
            {
191
                // var stateMachineLocal = new {StateMachineType}({initialState})
192
                int initialState = _isEnumerable ? StateMachineStates.FinishedStateMachine : StateMachineStates.InitialAsyncIteratorStateMachine;
193 194 195 196 197 198
                bodyBuilder.Add(
                    F.Assignment(
                        F.Local(stateMachineLocal),
                        F.New(stateMachineType.Constructor.AsMember(frameType), F.Literal(initialState))));
            }

199 200 201 202
            protected override BoundStatement InitializeParameterField(MethodSymbol getEnumeratorMethod, ParameterSymbol parameter, BoundExpression resultParameter, BoundExpression parameterProxy)
            {
                BoundStatement result;
                if (_combinedTokensField is object &&
203 204
                    parameter is SourceComplexParameterSymbol { HasEnumeratorCancellationAttribute: true } &&
                    parameter.Type.Equals(F.Compilation.GetWellKnownType(WellKnownType.System_Threading_CancellationToken), TypeCompareKind.ConsiderEverything))
205
                {
206
                    // For a parameter of type CancellationToken with [EnumeratorCancellation]
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
                    // if (this.parameterProxy.Equals(default))
                    // {
                    //     result.parameter = token;
                    // }
                    // else if (token.Equals(this.parameterProxy) || token.Equals(default))
                    // {
                    //     result.parameter = this.parameterProxy;
                    // }
                    // else
                    // {
                    //     result.combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(this.parameterProxy, token);
                    //     result.parameter = combinedTokens.Token;
                    // }

                    BoundParameter tokenParameter = F.Parameter(getEnumeratorMethod.Parameters[0]);
                    BoundFieldAccess combinedTokens = F.Field(F.This(), _combinedTokensField);
                    result = F.If(
                        // if (this.parameterProxy.Equals(default))
                        F.Call(parameterProxy, WellKnownMember.System_Threading_CancellationToken__Equals, F.Default(parameterProxy.Type)),
                        // result.parameter = token;
                        thenClause: F.Assignment(resultParameter, tokenParameter),
                        elseClauseOpt: F.If(
                            // else if (token.Equals(this.parameterProxy) || token.Equals(default))
                            F.LogicalOr(
                                F.Call(tokenParameter, WellKnownMember.System_Threading_CancellationToken__Equals, parameterProxy),
                                F.Call(tokenParameter, WellKnownMember.System_Threading_CancellationToken__Equals, F.Default(tokenParameter.Type))),
                            // result.parameter = this.parameterProxy;
                            thenClause: F.Assignment(resultParameter, parameterProxy),
                            elseClauseOpt: F.Block(
                                // result.combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(this.parameterProxy, token);
                                F.Assignment(combinedTokens, F.StaticCall(WellKnownMember.System_Threading_CancellationTokenSource__CreateLinkedTokenSource, parameterProxy, tokenParameter)),
                                // result.parameter = result.combinedTokens.Token;
                                F.Assignment(resultParameter, F.Property(combinedTokens, WellKnownMember.System_Threading_CancellationTokenSource__Token)))));
                }
                else
                {
                    // For parameters that don't have [EnumeratorCancellation], initialize their parameter fields
                    // result.parameter = this.parameterProxy;
                    result = F.Assignment(resultParameter, parameterProxy);
                }

                return result;
            }

251 252 253 254
            protected override BoundStatement GenerateStateMachineCreation(LocalSymbol stateMachineVariable, NamedTypeSymbol frameType)
            {
                // return local;
                return F.Block(F.Return(F.Local(stateMachineVariable)));
255 256 257
            }

            /// <summary>
J
tweaks  
Julien Couvreur 已提交
258
            /// Generates the `ValueTask&lt;bool> MoveNextAsync()` method.
259
            /// </summary>
260
            [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "Standard naming convention for generating 'IAsyncEnumerator.MoveNextAsync'")]
261
            private void GenerateIAsyncEnumeratorImplementation_MoveNextAsync()
262
            {
J
tweaks  
Julien Couvreur 已提交
263
                // Produce:
264
                //  if (state == StateMachineStates.FinishedStateMachine)
J
tweaks  
Julien Couvreur 已提交
265
                //  {
266
                //      return default;
J
tweaks  
Julien Couvreur 已提交
267 268 269 270
                //  }
                //  _valueOrEndPromise.Reset();
                //  var inst = this;
                //  _builder.Start(ref inst);
271 272 273 274 275 276
                //  var version = _valueOrEndPromise.Version;
                //  if (_valueOrEndPromise.GetStatus(version) == ValueTaskSourceStatus.Succeeded)
                //  {
                //      return new ValueTask<bool>(_valueOrEndPromise.GetResult(version));
                //  }
                //  return new ValueTask<bool>(this, version);
277 278 279

                NamedTypeSymbol IAsyncEnumeratorOfElementType =
                    F.WellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T)
280
                    .Construct(_currentField.Type);
281

282
                MethodSymbol IAsyncEnumerableOfElementType_MoveNextAsync = F.WellKnownMethod(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__MoveNextAsync)
283 284
                    .AsMember(IAsyncEnumeratorOfElementType);

285
                var promiseType = (NamedTypeSymbol)_promiseOfValueOrEndField.Type;
286 287 288 289 290 291 292

                MethodSymbol promise_GetStatus = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__GetStatus)
                    .AsMember(promiseType);

                MethodSymbol promise_GetResult = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__GetResult)
                    .AsMember(promiseType);

293
                var moveNextAsyncReturnType = (NamedTypeSymbol)IAsyncEnumerableOfElementType_MoveNextAsync.ReturnType;
294 295 296 297 298 299 300

                MethodSymbol valueTaskT_ctorValue = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_ValueTask_T__ctorValue)
                    .AsMember(moveNextAsyncReturnType);

                MethodSymbol valueTaskT_ctor = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_ValueTask_T__ctorSourceAndToken)
                    .AsMember(moveNextAsyncReturnType);

301
                // The implementation doesn't depend on the method body of the iterator method.
J
tweaks  
Julien Couvreur 已提交
302
                OpenMethodImplementation(IAsyncEnumerableOfElementType_MoveNextAsync, hasMethodBodyDependency: false);
303

304
                GetPartsForStartingMachine(out BoundExpressionStatement callReset,
305 306 307 308 309 310 311 312 313
                    out LocalSymbol instSymbol,
                    out BoundStatement instAssignment,
                    out BoundExpressionStatement startCall,
                    out MethodSymbol promise_get_Version);

                BoundStatement ifFinished = F.If(
                    // if (state == StateMachineStates.FinishedStateMachine)
                    F.IntEqual(F.InstanceField(stateField), F.Literal(StateMachineStates.FinishedStateMachine)),
                    // return default;
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
                    thenClause: F.Return(F.Default(moveNextAsyncReturnType)));

                // var version = _valueOrEndPromise.Version;
                var versionSymbol = F.SynthesizedLocal(F.SpecialType(SpecialType.System_Int16));
                var versionLocal = F.Local(versionSymbol);
                var versionInit = F.Assignment(versionLocal, F.Call(F.Field(F.This(), _promiseOfValueOrEndField), promise_get_Version));

                var ifPromiseReady = F.If(
                    // if (_valueOrEndPromise.GetStatus(version) == ValueTaskSourceStatus.Succeeded)
                    F.IntEqual(
                        F.Call(F.Field(F.This(), _promiseOfValueOrEndField), promise_GetStatus, versionLocal),
                        F.Literal(1)),
                    // return new ValueTask<bool>(_valueOrEndPromise.GetResult(version));
                    thenClause: F.Return(F.New(valueTaskT_ctorValue, F.Call(F.Field(F.This(), _promiseOfValueOrEndField), promise_GetResult, versionLocal))));

                // return new ValueTask<bool>(this, version);
                // Note: we fall back to this slower method of returning when the promise doesn't yet have a value.
                // This method of returning relies on two interface calls (`IValueTaskSource<bool>.GetStatus(version)` and `IValueTaskSource<bool>.GetResult(version)`).
                var returnStatement = F.Return(F.New(valueTaskT_ctor, F.This(), versionLocal));
333 334

                F.CloseMethod(F.Block(
335
                    ImmutableArray.Create(instSymbol, versionSymbol),
336 337 338 339
                    ifFinished,
                    callReset, // _promiseOfValueOrEnd.Reset();
                    instAssignment, // var inst = this;
                    startCall, // _builder.Start(ref inst);
340 341
                    versionInit,
                    ifPromiseReady,
342 343 344 345 346 347
                    returnStatement));
            }

            /// <summary>
            /// Prepares most of the parts for MoveNextAsync() and DisposeAsync() methods.
            /// </summary>
348 349
            private void GetPartsForStartingMachine(out BoundExpressionStatement callReset, out LocalSymbol instSymbol, out BoundStatement instAssignment,
                out BoundExpressionStatement startCall, out MethodSymbol promise_get_Version)
350 351 352 353 354 355
            {
                // Produce the following parts:
                // - _promiseOfValueOrEnd.Reset();
                // - var inst = this;
                // - _builder.Start(ref inst);
                // - _valueOrEndPromise.Version
356

J
Julien Couvreur 已提交
357
                // _promiseOfValueOrEnd.Reset();
358
                BoundFieldAccess promiseField = F.InstanceField(_promiseOfValueOrEndField);
359
                var resetMethod = (MethodSymbol)F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__Reset, isOptional: true)
360
                    .SymbolAsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
J
Julien Couvreur 已提交
361

362
                callReset = F.ExpressionStatement(F.Call(promiseField, resetMethod));
J
Julien Couvreur 已提交
363 364 365 366

                // _builder.Start(ref inst);
                Debug.Assert(!_asyncMethodBuilderMemberCollection.CheckGenericMethodConstraints);
                MethodSymbol startMethod = _asyncMethodBuilderMemberCollection.Start.Construct(this.stateMachineType);
367
                instSymbol = F.SynthesizedLocal(this.stateMachineType);
J
Julien Couvreur 已提交
368

369 370 371
                // var inst = this;
                var instLocal = F.Local(instSymbol);
                instAssignment = F.Assignment(instLocal, F.This());
372

373 374 375 376 377 378 379 380 381
                // _builder.Start(ref inst);
                startCall = F.ExpressionStatement(
                    F.Call(
                        F.InstanceField(_builderField),
                        startMethod,
                        ImmutableArray.Create<BoundExpression>(instLocal)));

                //  _valueOrEndPromise.Version
                promise_get_Version = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__get_Version)
382
                    .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
383
            }
384

385 386
            /// <summary>
            /// Generates the `ValueTask IAsyncDisposable.DisposeAsync()` method.
387
            /// The DisposeAsync method should not be called from states -1 (running) or 0-and-up (awaits).
388
            /// </summary>
389
            [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "Standard naming convention for generating 'IAsyncDisposable.DisposeAsync'")]
390 391 392
            private void GenerateIAsyncDisposable_DisposeAsync()
            {
                // Produce:
393 394 395 396 397
                //  if (state >= StateMachineStates.NotStartedStateMachine /* -3 */)
                //  {
                //      throw new NotSupportedException();
                //  }
                //  if (state == StateMachineStates.FinishedStateMachine /* -2 */)
398 399 400
                //  {
                //      return default;
                //  }
401
                //  disposeMode = true;
402 403 404 405 406 407 408 409 410 411
                //  _valueOrEndPromise.Reset();
                //  var inst = this;
                //  _builder.Start(ref inst);
                //  return new ValueTask(this, _valueOrEndPromise.Version);

                MethodSymbol IAsyncDisposable_DisposeAsync = F.WellKnownMethod(WellKnownMember.System_IAsyncDisposable__DisposeAsync);

                // The implementation doesn't depend on the method body of the iterator method.
                OpenMethodImplementation(IAsyncDisposable_DisposeAsync, hasMethodBodyDependency: false);

412
                TypeSymbol returnType = IAsyncDisposable_DisposeAsync.ReturnType;
413

414
                GetPartsForStartingMachine(out BoundExpressionStatement callReset,
415 416 417 418 419
                    out LocalSymbol instSymbol,
                    out BoundStatement instAssignment,
                    out BoundExpressionStatement startCall,
                    out MethodSymbol promise_get_Version);

420 421 422 423 424 425
                BoundStatement ifInvalidState = F.If(
                    // if (state >= StateMachineStates.NotStartedStateMachine /* -1 */)
                    F.IntGreaterThanOrEqual(F.InstanceField(stateField), F.Literal(StateMachineStates.NotStartedStateMachine)),
                    // throw new NotSupportedException();
                    thenClause: F.Throw(F.New(F.WellKnownType(WellKnownType.System_NotSupportedException))));

426
                BoundStatement ifFinished = F.If(
427 428
                    // if (state == StateMachineStates.FinishedStateMachine)
                    F.IntEqual(F.InstanceField(stateField), F.Literal(StateMachineStates.FinishedStateMachine)),
429 430 431 432 433
                    // return default;
                    thenClause: F.Return(F.Default(returnType)));

                MethodSymbol valueTask_ctor =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_ValueTask__ctor)
434
                    .AsMember((NamedTypeSymbol)IAsyncDisposable_DisposeAsync.ReturnType);
435 436 437

                // return new ValueTask(this, _valueOrEndPromise.Version);
                var returnStatement = F.Return(F.New(valueTask_ctor, F.This(), F.Call(F.InstanceField(_promiseOfValueOrEndField), promise_get_Version)));
438

439
                F.CloseMethod(F.Block(
J
Julien Couvreur 已提交
440
                    ImmutableArray.Create(instSymbol),
441
                    ifInvalidState,
J
Julien Couvreur 已提交
442
                    ifFinished,
443
                    F.Assignment(F.InstanceField(_disposeModeField), F.Literal(true)), // disposeMode = true;
J
Julien Couvreur 已提交
444
                    callReset, // _promiseOfValueOrEnd.Reset();
445
                    instAssignment, // var inst = this;
J
Julien Couvreur 已提交
446
                    startCall, // _builder.Start(ref inst);
447
                    returnStatement));
448 449 450
            }

            /// <summary>
451
            /// Generates the Current property.
452
            /// </summary>
453
            private void GenerateIAsyncEnumeratorImplementation_Current()
454
            {
455
                // Produce the implementation for `T Current { get; }`:
456 457 458 459
                // return _current;

                NamedTypeSymbol IAsyncEnumeratorOfElementType =
                    F.WellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T)
460
                    .Construct(_currentField.Type);
461

462 463
                MethodSymbol IAsyncEnumerableOfElementType_get_Current =
                    F.WellKnownMethod(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__get_Current)
464 465
                    .AsMember(IAsyncEnumeratorOfElementType);

466
                OpenPropertyImplementation(IAsyncEnumerableOfElementType_get_Current);
467

468
                F.CloseMethod(F.Block(F.Return(F.InstanceField(_currentField))));
469 470
            }

471
            private void GenerateIValueTaskSourceBoolImplementation_GetResult()
472 473
            {
                // Produce the implementation for `bool IValueTaskSource<bool>.GetResult(short token)`:
J
Julien Couvreur 已提交
474
                // return _valueOrEndPromise.GetResult(token);
475 476 477 478 479 480 481 482 483 484

                NamedTypeSymbol IValueTaskSourceOfBool =
                    F.WellKnownType(WellKnownType.System_Threading_Tasks_Sources_IValueTaskSource_T)
                    .Construct(F.SpecialType(SpecialType.System_Boolean));

                MethodSymbol IValueTaskSourceOfBool_GetResult =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource_T__GetResult)
                    .AsMember(IValueTaskSourceOfBool);

                MethodSymbol promise_GetResult =
485
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__GetResult)
486
                    .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
487 488 489 490 491 492

                // The implementation doesn't depend on the method body of the iterator method.
                OpenMethodImplementation(IValueTaskSourceOfBool_GetResult, hasMethodBodyDependency: false);

                // return this._valueOrEndPromise.GetResult(token);
                F.CloseMethod(F.Return(
493
                    F.Call(F.InstanceField(_promiseOfValueOrEndField), promise_GetResult, F.Parameter(IValueTaskSourceOfBool_GetResult.Parameters[0]))));
494 495
            }

496
            private void GenerateIValueTaskSourceBoolImplementation_GetStatus()
497 498 499 500 501 502 503 504 505 506 507 508 509
            {
                // Produce the implementation for `ValueTaskSourceStatus IValueTaskSource<bool>.GetStatus(short token)`:
                // return this._valueOrEndPromise.GetStatus(token);

                NamedTypeSymbol IValueTaskSourceOfBool =
                    F.WellKnownType(WellKnownType.System_Threading_Tasks_Sources_IValueTaskSource_T)
                    .Construct(F.SpecialType(SpecialType.System_Boolean));

                MethodSymbol IValueTaskSourceOfBool_GetStatus =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource_T__GetStatus)
                    .AsMember(IValueTaskSourceOfBool);

                MethodSymbol promise_GetStatus =
510
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__GetStatus)
511
                    .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
512 513 514 515 516 517

                // The implementation doesn't depend on the method body of the iterator method.
                OpenMethodImplementation(IValueTaskSourceOfBool_GetStatus, hasMethodBodyDependency: false);

                // return this._valueOrEndPromise.GetStatus(token);
                F.CloseMethod(F.Return(
518
                    F.Call(F.InstanceField(_promiseOfValueOrEndField), promise_GetStatus, F.Parameter(IValueTaskSourceOfBool_GetStatus.Parameters[0]))));
519 520
            }

521
            private void GenerateIValueTaskSourceBoolImplementation_OnCompleted()
522 523 524 525 526 527 528 529 530 531 532 533 534 535
            {
                // Produce the implementation for `void IValueTaskSource<bool>.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)`:
                // this._valueOrEndPromise.OnCompleted(continuation, state, token, flags);
                // return;

                NamedTypeSymbol IValueTaskSourceOfBool =
                    F.WellKnownType(WellKnownType.System_Threading_Tasks_Sources_IValueTaskSource_T)
                    .Construct(F.SpecialType(SpecialType.System_Boolean));

                MethodSymbol IValueTaskSourceOfBool_OnCompleted =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource_T__OnCompleted)
                    .AsMember(IValueTaskSourceOfBool);

                MethodSymbol promise_OnCompleted =
536
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__OnCompleted)
537
                    .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
538 539 540 541 542 543 544

                // The implementation doesn't depend on the method body of the iterator method.
                OpenMethodImplementation(IValueTaskSourceOfBool_OnCompleted, hasMethodBodyDependency: false);

                F.CloseMethod(F.Block(
                    // this._valueOrEndPromise.OnCompleted(continuation, state, token, flags);
                    F.ExpressionStatement(
545 546 547 548 549
                        F.Call(F.InstanceField(_promiseOfValueOrEndField), promise_OnCompleted,
                            F.Parameter(IValueTaskSourceOfBool_OnCompleted.Parameters[0]),
                            F.Parameter(IValueTaskSourceOfBool_OnCompleted.Parameters[1]),
                            F.Parameter(IValueTaskSourceOfBool_OnCompleted.Parameters[2]),
                            F.Parameter(IValueTaskSourceOfBool_OnCompleted.Parameters[3]))),
550 551 552
                    F.Return())); // return;
            }

553
            private void GenerateIValueTaskSourceImplementation_GetResult()
554
            {
555 556 557
                // Produce an implementation for `void IValueTaskSource.GetResult(short token)`:
                //  this._valueOrEndPromise.GetResult(token);
                //  return;
558

559 560
                MethodSymbol IValueTaskSource_GetResult =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource__GetResult);
561

562 563
                MethodSymbol promise_GetResult =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__GetResult)
564
                    .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
565 566

                // The implementation doesn't depend on the method body of the iterator method.
567
                OpenMethodImplementation(IValueTaskSource_GetResult, hasMethodBodyDependency: false);
568

569 570 571 572 573 574
                F.CloseMethod(F.Block(
                    // this._valueOrEndPromise.GetResult(token);
                    F.ExpressionStatement(F.Call(F.InstanceField(_promiseOfValueOrEndField), promise_GetResult, F.Parameter(IValueTaskSource_GetResult.Parameters[0]))),
                    // return;
                    F.Return()));
            }
575

576 577 578 579 580 581
            // Consider factoring with IValueTaskSource<bool> implementation
            // See https://github.com/dotnet/roslyn/issues/31517
            private void GenerateIValueTaskSourceImplementation_GetStatus()
            {
                // Produce the implementation for `ValueTaskSourceStatus IValueTaskSource.GetStatus(short token)`:
                // return this._valueOrEndPromise.GetStatus(token);
582

583 584
                MethodSymbol IValueTaskSource_GetStatus =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource__GetStatus);
585

586 587
                MethodSymbol promise_GetStatus =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__GetStatus)
588
                    .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
589

590 591
                // The implementation doesn't depend on the method body of the iterator method.
                OpenMethodImplementation(IValueTaskSource_GetStatus, hasMethodBodyDependency: false);
592

593 594 595 596
                // return this._valueOrEndPromise.GetStatus(token);
                F.CloseMethod(F.Return(
                    F.Call(F.InstanceField(_promiseOfValueOrEndField), promise_GetStatus, F.Parameter(IValueTaskSource_GetStatus.Parameters[0]))));
            }
597

598 599 600 601 602 603 604
            // Consider factoring with IValueTaskSource<bool> implementation
            // See https://github.com/dotnet/roslyn/issues/31517
            private void GenerateIValueTaskSourceImplementation_OnCompleted()
            {
                // Produce the implementation for `void IValueTaskSource.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)`:
                // this._valueOrEndPromise.OnCompleted(continuation, state, token, flags);
                // return;
605

606
                MethodSymbol IValueTaskSource_OnCompleted = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_IValueTaskSource__OnCompleted);
607

608 609
                MethodSymbol promise_OnCompleted =
                    F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__OnCompleted)
610
                    .AsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
611 612 613 614 615 616 617 618 619 620 621 622 623

                // The implementation doesn't depend on the method body of the iterator method.
                OpenMethodImplementation(IValueTaskSource_OnCompleted, hasMethodBodyDependency: false);

                F.CloseMethod(F.Block(
                    // this._valueOrEndPromise.OnCompleted(continuation, state, token, flags);
                    F.ExpressionStatement(
                        F.Call(F.InstanceField(_promiseOfValueOrEndField), promise_OnCompleted,
                            F.Parameter(IValueTaskSource_OnCompleted.Parameters[0]),
                            F.Parameter(IValueTaskSource_OnCompleted.Parameters[1]),
                            F.Parameter(IValueTaskSource_OnCompleted.Parameters[2]),
                            F.Parameter(IValueTaskSource_OnCompleted.Parameters[3]))),
                    F.Return())); // return;
624 625 626 627 628 629 630 631 632
            }

            /// <summary>
            /// Generates the GetAsyncEnumerator method.
            /// </summary>
            private void GenerateIAsyncEnumerableImplementation_GetAsyncEnumerator()
            {
                NamedTypeSymbol IAsyncEnumerableOfElementType =
                    F.WellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T)
633
                    .Construct(_currentField.Type);
634 635 636 637 638

                MethodSymbol IAsyncEnumerableOfElementType_GetEnumerator =
                    F.WellKnownMethod(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator)
                    .AsMember(IAsyncEnumerableOfElementType);

639
                BoundExpression managedThreadId = null;
640 641 642
                GenerateIteratorGetEnumerator(IAsyncEnumerableOfElementType_GetEnumerator, ref managedThreadId, initialState: StateMachineStates.InitialAsyncIteratorStateMachine);
            }

643
            protected override void GenerateResetInstance(ArrayBuilder<BoundStatement> builder, int initialState)
644
            {
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
                // this.state = {initialState};
                // this.builder = System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Create();
                // this.disposeMode = false;

                builder.Add(
                    // this.state = {initialState};
                    F.Assignment(F.Field(F.This(), stateField), F.Literal(initialState)));

                builder.Add(
                    // this.builder = System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Create();
                    GenerateCreateAndAssignBuilder());

                builder.Add(
                    // disposeMode = false;
                    F.Assignment(F.InstanceField(_disposeModeField), F.Literal(false)));
660 661 662 663
            }

            protected override void GenerateMoveNext(SynthesizedImplementationMethod moveNextMethod)
            {
664
                MethodSymbol setResultMethod = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__SetResult, isOptional: true);
665 666
                if ((object)setResultMethod != null)
                {
667
                    setResultMethod = (MethodSymbol)setResultMethod.SymbolAsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
668 669
                }

670
                MethodSymbol setExceptionMethod = F.WellKnownMethod(WellKnownMember.System_Threading_Tasks_Sources_ManualResetValueTaskSourceCore_T__SetException, isOptional: true);
671 672
                if ((object)setExceptionMethod != null)
                {
673
                    setExceptionMethod = (MethodSymbol)setExceptionMethod.SymbolAsMember((NamedTypeSymbol)_promiseOfValueOrEndField.Type);
674 675
                }

676
                var rewriter = new AsyncIteratorMethodToStateMachineRewriter(
677 678 679
                    method: method,
                    methodOrdinal: _methodOrdinal,
                    asyncMethodBuilderMemberCollection: _asyncMethodBuilderMemberCollection,
680
                    asyncIteratorInfo: new AsyncIteratorInfo(_promiseOfValueOrEndField, _combinedTokensField, _currentField, _disposeModeField, setResultMethod, setExceptionMethod),
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
                    F: F,
                    state: stateField,
                    builder: _builderField,
                    hoistedVariables: hoistedVariables,
                    nonReusableLocalProxies: nonReusableLocalProxies,
                    synthesizedLocalOrdinals: synthesizedLocalOrdinals,
                    slotAllocatorOpt: slotAllocatorOpt,
                    nextFreeHoistedLocalSlot: nextFreeHoistedLocalSlot,
                    diagnostics: diagnostics);

                rewriter.GenerateMoveNext(body, moveNextMethod);
            }
        }
    }
}