AsyncMethodToStateMachineRewriter.cs 26.7 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
2

3
using System;
4 5 6 7
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Runtime.CompilerServices;
8
using Microsoft.CodeAnalysis.CodeGen;
9 10
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
T
Tomas Matousek 已提交
11
using Microsoft.CodeAnalysis.PooledObjects;
12 13 14 15
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
16
    /// <summary>
17
    /// Produces a MoveNext() method for an async method.
18
    /// </summary>
19
    internal class AsyncMethodToStateMachineRewriter : MethodToStateMachineRewriter
20 21 22 23
    {
        /// <summary>
        /// The method being rewritten.
        /// </summary>
24
        protected readonly MethodSymbol _method;
25 26 27 28 29 30

        /// <summary>
        /// The field of the generated async class used to store the async method builder: an instance of
        /// <see cref="AsyncVoidMethodBuilder"/>, <see cref="AsyncTaskMethodBuilder"/>, or <see cref="AsyncTaskMethodBuilder{TResult}"/> depending on the
        /// return type of the async method.
        /// </summary>
31
        protected readonly FieldSymbol _asyncMethodBuilderField;
32 33 34 35

        /// <summary>
        /// A collection of well-known members for the current async method builder.
        /// </summary>
36
        protected readonly AsyncMethodBuilderMemberCollection _asyncMethodBuilderMemberCollection;
37 38 39 40 41

        /// <summary>
        /// The exprReturnLabel is used to label the return handling code at the end of the async state-machine
        /// method. Return expressions are rewritten as unconditional branches to exprReturnLabel.
        /// </summary>
42
        protected readonly LabelSymbol _exprReturnLabel;
43 44 45 46

        /// <summary>
        /// The label containing a return from the method when the async method has not completed.
        /// </summary>
47
        private readonly LabelSymbol _exitLabel;
48 49 50 51 52 53

        /// <summary>
        /// The field of the generated async class used in generic task returning async methods to store the value
        /// of rewritten return expressions. The return-handling code then uses <c>SetResult</c> on the async method builder
        /// to make the result available to the caller.
        /// </summary>
54
        private readonly LocalSymbol _exprRetValue;
55

56
        private readonly LoweredDynamicOperationFactory _dynamicFactory;
57

58 59
        private readonly Dictionary<TypeSymbol, FieldSymbol> _awaiterFields;
        private int _nextAwaiterId;
60

61 62
        private readonly Dictionary<BoundValuePlaceholderBase, BoundExpression> _placeholderMap;

63 64
        internal AsyncMethodToStateMachineRewriter(
            MethodSymbol method,
65
            int methodOrdinal,
66 67 68 69
            AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection,
            SyntheticBoundNodeFactory F,
            FieldSymbol state,
            FieldSymbol builder,
70
            IReadOnlySet<Symbol> hoistedVariables,
71
            IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies,
72 73 74
            SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals,
            VariableSlotAllocator slotAllocatorOpt,
            int nextFreeHoistedLocalSlot,
75
            DiagnosticBag diagnostics)
76
            : base(F, method, state, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics, useFinalizerBookkeeping: false)
77
        {
78 79 80 81 82
            _method = method;
            _asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection;
            _asyncMethodBuilderField = builder;
            _exprReturnLabel = F.GenerateLabel("exprReturn");
            _exitLabel = F.GenerateLabel("exitLabel");
83

84
            _exprRetValue = method.IsGenericTaskReturningAsync(F.Compilation)
85
                ? F.SynthesizedLocal(asyncMethodBuilderMemberCollection.ResultType, syntax: F.Syntax, kind: SynthesizedLocalKind.AsyncMethodReturnValue)
86 87
                : null;

88
            _dynamicFactory = new LoweredDynamicOperationFactory(F, methodOrdinal);
89
            _awaiterFields = new Dictionary<TypeSymbol, FieldSymbol>(TypeSymbol.EqualsIgnoringDynamicTupleNamesAndNullabilityComparer);
90
            _nextAwaiterId = slotAllocatorOpt?.PreviousAwaiterSlotCount ?? 0;
91 92

            _placeholderMap = new Dictionary<BoundValuePlaceholderBase, BoundExpression>();
93 94 95 96 97
        }

        private FieldSymbol GetAwaiterField(TypeSymbol awaiterType)
        {
            FieldSymbol result;
98 99 100

            // Awaiters of the same type always share the same slot, regardless of what await expressions they belong to.
            // Even in case of nested await expressions only one awaiter is active.
101
            // So we don't need to tie the awaiter variable to a particular await expression and only use its type
102
            // to find the previous awaiter field.
103
            if (!_awaiterFields.TryGetValue(awaiterType, out result))
104
            {
105
                int slotIndex;
106
                if (slotAllocatorOpt == null || !slotAllocatorOpt.TryGetPreviousAwaiterSlotIndex(F.ModuleBuilderOpt.Translate(awaiterType, F.Syntax, F.Diagnostics), F.Diagnostics, out slotIndex))
107
                {
108
                    slotIndex = _nextAwaiterId++;
109 110
                }

111 112
                string fieldName = GeneratedNames.AsyncAwaiterFieldName(slotIndex);
                result = F.StateMachineField(awaiterType, fieldName, SynthesizedLocalKind.AwaiterField, slotIndex);
113
                _awaiterFields.Add(awaiterType, result);
114
            }
115

116 117 118 119 120 121 122 123
            return result;
        }

        /// <summary>
        /// Generate the body for <c>MoveNext()</c>.
        /// </summary>
        internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod)
        {
124
            F.CurrentFunction = moveNextMethod;
125
            BoundStatement rewrittenBody = VisitBody(body);
126

127 128 129
            ImmutableArray<StateMachineFieldSymbol> rootScopeHoistedLocals;
            TryUnwrapBoundStateMachineScope(ref rewrittenBody, out rootScopeHoistedLocals);

130 131
            var bodyBuilder = ArrayBuilder<BoundStatement>.GetInstance();

T
TomasMatousek 已提交
132 133
            bodyBuilder.Add(F.HiddenSequencePoint());
            bodyBuilder.Add(F.Assignment(F.Local(cachedState), F.Field(F.This(), stateField)));
134
            bodyBuilder.Add(CacheThisIfNeeded());
135

136
            var exceptionLocal = F.SynthesizedLocal(F.WellKnownType(WellKnownType.System_Exception));
137
            bodyBuilder.Add(
138
                GenerateTopLevelTry(
T
TomasMatousek 已提交
139 140 141
                    F.Block(ImmutableArray<LocalSymbol>.Empty,
                        // switch (state) ...
                        F.HiddenSequencePoint(),
142
                        Dispatch(),
T
TomasMatousek 已提交
143 144
                        // [body]
                        rewrittenBody
145
                    ),
146
                    F.CatchBlocks(GenerateExceptionHandling(exceptionLocal)))
147
                );
148 149

            // ReturnLabel (for the rewritten return expressions in the user's method body)
150
            bodyBuilder.Add(F.Label(_exprReturnLabel));
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

            // this.state = finishedState
            var stateDone = F.Assignment(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine));
            var block = body.Syntax as BlockSyntax;
            if (block == null)
            {
                // this happens, for example, in (async () => await e) where there is no block syntax
                bodyBuilder.Add(stateDone);
            }
            else
            {
                bodyBuilder.Add(F.SequencePointWithSpan(block, block.CloseBraceToken.Span, stateDone));
                bodyBuilder.Add(F.HiddenSequencePoint());
                // The remaining code is hidden to hide the fact that it can run concurrently with the task's continuation
            }

167
            bodyBuilder.Add(GenerateSetResultCall());
168 169

            // this code is hidden behind a hidden sequence point.
170
            bodyBuilder.Add(F.Label(_exitLabel));
171 172
            bodyBuilder.Add(F.Return());

173
            var newStatements = bodyBuilder.ToImmutableAndFree();
174 175 176

            var locals = ArrayBuilder<LocalSymbol>.GetInstance();
            locals.Add(cachedState);
177
            if ((object)cachedThis != null) locals.Add(cachedThis);
178
            if ((object)_exprRetValue != null) locals.Add(_exprRetValue);
179

180
            var newBody =
181
                F.SequencePoint(
J
Jared Parsons 已提交
182
                    body.Syntax,
183
                    F.Block(
184
                        locals.ToImmutableAndFree(),
185 186 187 188 189 190
                        newStatements));

            if (rootScopeHoistedLocals.Length > 0)
            {
                newBody = MakeStateMachineScope(rootScopeHoistedLocals, newBody);
            }
J
Jared Parsons 已提交
191

192
            F.CloseMethod(newBody);
193 194
        }

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
        protected virtual BoundStatement GenerateTopLevelTry(BoundBlock tryBlock, ImmutableArray<BoundCatchBlock> catchBlocks)
            => F.Try(tryBlock, catchBlocks);

        protected virtual BoundStatement GenerateSetResultCall()
        {
            // builder.SetResult([RetVal])
            return F.ExpressionStatement(
                F.Call(
                    F.Field(F.This(), _asyncMethodBuilderField),
                    _asyncMethodBuilderMemberCollection.SetResult,
                    _method.IsGenericTaskReturningAsync(F.Compilation)
                        ? ImmutableArray.Create<BoundExpression>(F.Local(_exprRetValue))
                        : ImmutableArray<BoundExpression>.Empty));
        }

        protected BoundCatchBlock GenerateExceptionHandling(LocalSymbol exceptionLocal)
211
        {
212 213
            // catch (Exception ex)
            // {
214
            //     _state = finishedState;
215
            //     builder.SetException(ex);  OR  if (this.combinedTokens != null) this.combinedTokens.Dispose(); _promiseOfValueOrEnd.SetException(ex); /* for async-iterator method */
216 217 218
            //     return;
            // }

219 220 221
            // _state = finishedState;
            BoundStatement assignFinishedState =
                F.ExpressionStatement(F.AssignmentExpression(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)));
222

223
            // builder.SetException(ex);  OR  if (this.combinedTokens != null) this.combinedTokens.Dispose(); _promiseOfValueOrEnd.SetException(ex);
224
            BoundStatement callSetException = GenerateSetExceptionCall(exceptionLocal);
225 226 227 228 229

            return new BoundCatchBlock(
                F.Syntax,
                ImmutableArray.Create(exceptionLocal),
                F.Local(exceptionLocal),
230
                exceptionLocal.Type,
231 232
                exceptionFilterOpt: null,
                body: F.Block(
233 234 235
                    assignFinishedState, // _state = finishedState;
                    callSetException, // builder.SetException(ex);  OR  _promiseOfValueOrEnd.SetException(ex);
                    GenerateReturn(false)), // return;
236
                isSynthesizedAsyncCatchAll: true);
237 238
        }

239 240
        protected virtual BoundStatement GenerateSetExceptionCall(LocalSymbol exceptionLocal)
        {
241 242
            Debug.Assert(!CurrentMethod.IsIterator); // an override handles async-iterators

243 244 245 246 247 248 249 250 251
            // builder.SetException(ex);
            return F.ExpressionStatement(
                F.Call(
                    F.Field(F.This(), _asyncMethodBuilderField),
                    _asyncMethodBuilderMemberCollection.SetException,
                    F.Local(exceptionLocal)));
        }

        protected sealed override BoundStatement GenerateReturn(bool finished)
252
        {
253
            return F.Goto(_exitLabel);
254 255 256 257
        }

        #region Visitors

258
        protected virtual BoundStatement VisitBody(BoundStatement body)
259
            => (BoundStatement)Visit(body);
260

261
        public sealed override BoundNode VisitExpressionStatement(BoundExpressionStatement node)
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
        {
            if (node.Expression.Kind == BoundKind.AwaitExpression)
            {
                return VisitAwaitExpression((BoundAwaitExpression)node.Expression, resultPlace: null);
            }
            else if (node.Expression.Kind == BoundKind.AssignmentOperator)
            {
                var expression = (BoundAssignmentOperator)node.Expression;
                if (expression.Right.Kind == BoundKind.AwaitExpression)
                {
                    return VisitAwaitExpression((BoundAwaitExpression)expression.Right, resultPlace: expression.Left);
                }
            }

            BoundExpression expr = (BoundExpression)this.Visit(node.Expression);
V
VSadov 已提交
277
            return (expr != null) ? node.Update(expr) : (BoundStatement)F.StatementList();
278 279
        }

280
        public sealed override BoundNode VisitAwaitExpression(BoundAwaitExpression node)
281 282 283 284 285
        {
            // await expressions must, by now, have been moved to the top level.
            throw ExceptionUtilities.Unreachable;
        }

286
        public sealed override BoundNode VisitBadExpression(BoundBadExpression node)
287 288 289 290 291 292 293 294
        {
            // Cannot recurse into BadExpression
            return node;
        }

        private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace)
        {
            var expression = (BoundExpression)Visit(node.Expression);
295 296 297 298 299 300 301 302 303 304 305
            var awaitablePlaceholder = node.AwaitableInfo.AwaitableInstancePlaceholder;

            if (awaitablePlaceholder != null)
            {
                _placeholderMap.Add(awaitablePlaceholder, expression);
            }

            var getAwaiter = node.AwaitableInfo.IsDynamic ?
                MakeCallMaybeDynamic(expression, null, WellKnownMemberNames.GetAwaiter) :
                (BoundExpression)Visit(node.AwaitableInfo.GetAwaiter);

306
            resultPlace = (BoundExpression)Visit(resultPlace);
307 308
            MethodSymbol getResult = VisitMethodSymbol(node.AwaitableInfo.GetResult);
            MethodSymbol isCompletedMethod = ((object)node.AwaitableInfo.IsCompleted != null) ? VisitMethodSymbol(node.AwaitableInfo.IsCompleted.GetMethod) : null;
309 310
            TypeSymbol type = VisitType(node.Type);

311 312 313 314 315
            if (awaitablePlaceholder != null)
            {
                _placeholderMap.Remove(awaitablePlaceholder);
            }

316 317
            // The awaiter temp facilitates EnC method remapping and thus have to be long-lived.
            // It transfers the awaiter objects from the old version of the MoveNext method to the new one.
318
            Debug.Assert(node.Syntax.IsKind(SyntaxKind.AwaitExpression) || node.WasCompilerGenerated);
319 320

            var awaiterTemp = F.SynthesizedLocal(getAwaiter.Type, syntax: node.Syntax, kind: SynthesizedLocalKind.Awaiter);
321
            var awaitIfIncomplete = F.Block(
322 323
                    // temp $awaiterTemp = <expr>.GetAwaiter();
                    F.Assignment(
324
                        F.Local(awaiterTemp),
325
                        getAwaiter),
326

327 328 329
                    // hidden sequence point facilitates EnC method remapping, see explanation on SynthesizedLocalKind.Awaiter:
                    F.HiddenSequencePoint(),

330 331
                    // if(!($awaiterTemp.IsCompleted)) { ... }
                    F.If(
332 333
                        condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)),
                        thenClause: GenerateAwaitForIncompleteTask(awaiterTemp)));
334 335 336 337 338 339
            BoundExpression getResultCall = MakeCallMaybeDynamic(
                F.Local(awaiterTemp),
                getResult,
                WellKnownMemberNames.GetResult,
                resultsDiscarded: resultPlace == null);

340
            // [$resultPlace = ] $awaiterTemp.GetResult();
341
            BoundStatement getResultStatement = resultPlace != null && !type.IsVoidType() ?
342
                F.Assignment(resultPlace, getResultCall) :
343 344 345 346 347 348
                F.ExpressionStatement(getResultCall);

            return F.Block(
                ImmutableArray.Create(awaiterTemp),
                awaitIfIncomplete,
                getResultStatement);
349 350
        }

351 352 353 354 355
        public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node)
        {
            return _placeholderMap[node];
        }

356 357 358
        private BoundExpression MakeCallMaybeDynamic(
            BoundExpression receiver,
            MethodSymbol methodSymbol = null,
359
            string methodName = null,
360 361 362 363 364 365 366 367 368 369 370 371 372 373
            bool resultsDiscarded = false)
        {
            if ((object)methodSymbol != null)
            {
                // non-dynamic:
                Debug.Assert(receiver != null);

                return methodSymbol.IsStatic
                    ? F.StaticCall(methodSymbol.ContainingType, methodSymbol, receiver)
                    : F.Call(receiver, methodSymbol);
            }

            // dynamic:
            Debug.Assert(methodName != null);
374
            return _dynamicFactory.MakeDynamicMemberInvocation(
375 376
                methodName,
                receiver,
377
                typeArgumentsWithAnnotations: ImmutableArray<TypeWithAnnotations>.Empty,
378
                loweredArguments: ImmutableArray<BoundExpression>.Empty,
379
                argumentNames: ImmutableArray<string>.Empty,
380 381 382 383 384 385 386
                refKinds: ImmutableArray<RefKind>.Empty,
                hasImplicitReceiver: false,
                resultDiscarded: resultsDiscarded).ToExpression();
        }

        private BoundExpression GenerateGetIsCompleted(LocalSymbol awaiterTemp, MethodSymbol getIsCompletedMethod)
        {
387
            if (awaiterTemp.Type.IsDynamic())
388
            {
389 390
                return _dynamicFactory.MakeDynamicConversion(
                    _dynamicFactory.MakeDynamicGetMember(
391 392 393 394 395 396 397 398 399 400 401 402 403 404
                        F.Local(awaiterTemp),
                        WellKnownMemberNames.IsCompleted,
                        false).ToExpression(),
                    isExplicit: true,
                    isArrayIndex: false,
                    isChecked: false,
                    resultType: F.SpecialType(SpecialType.System_Boolean)).ToExpression();
            }

            return F.Call(F.Local(awaiterTemp), getIsCompletedMethod);
        }

        private BoundBlock GenerateAwaitForIncompleteTask(LocalSymbol awaiterTemp)
        {
405
            AddState(out int stateNumber, out GeneratedLabelSymbol resumeLabel);
406

407
            TypeSymbol awaiterFieldType = awaiterTemp.Type.IsVerifierReference()
408
                ? F.SpecialType(SpecialType.System_Object)
409
                : awaiterTemp.Type;
410 411 412 413 414 415

            FieldSymbol awaiterField = GetAwaiterField(awaiterFieldType);

            var blockBuilder = ArrayBuilder<BoundStatement>.GetInstance();

            blockBuilder.Add(
416 417
                // this.state = cachedState = stateForLabel
                GenerateSetBothStates(stateNumber));
418 419

            blockBuilder.Add(
420 421
                    // Emit await yield point to be injected into PDB
                    F.NoOp(NoOpStatementFlavor.AwaitYieldPoint));
422 423

            blockBuilder.Add(
424 425
                    // this.<>t__awaiter = $awaiterTemp
                    F.Assignment(
426
                    F.Field(F.This(), awaiterField),
427
                    (TypeSymbol.Equals(awaiterField.Type, awaiterTemp.Type, TypeCompareKind.ConsiderEverything2))
428 429 430
                        ? F.Local(awaiterTemp)
                        : F.Convert(awaiterFieldType, F.Local(awaiterTemp))));

431
            blockBuilder.Add(awaiterTemp.Type.IsDynamic()
432
                ? GenerateAwaitOnCompletedDynamic(awaiterTemp)
433
                : GenerateAwaitOnCompleted(awaiterTemp.Type, awaiterTemp));
434 435 436 437 438 439 440 441

            blockBuilder.Add(
                GenerateReturn(false));

            blockBuilder.Add(
                F.Label(resumeLabel));

            blockBuilder.Add(
442 443
                    // Emit await resume point to be injected into PDB
                    F.NoOp(NoOpStatementFlavor.AwaitResumePoint));
444 445

            blockBuilder.Add(
446 447 448
                    // $awaiterTemp = this.<>t__awaiter   or   $awaiterTemp = (AwaiterType)this.<>t__awaiter
                    // $this.<>t__awaiter = null;
                    F.Assignment(
449
                    F.Local(awaiterTemp),
450
                    TypeSymbol.Equals(awaiterTemp.Type, awaiterField.Type, TypeCompareKind.ConsiderEverything2)
451
                        ? F.Field(F.This(), awaiterField)
452
                        : F.Convert(awaiterTemp.Type, F.Field(F.This(), awaiterField))));
453 454

            blockBuilder.Add(
455
                F.Assignment(F.Field(F.This(), awaiterField), F.NullOrDefault(awaiterField.Type)));
456 457

            blockBuilder.Add(
458
                    // this.state = cachedState = NotStartedStateMachine
459
                    GenerateSetBothStates(StateMachineStates.NotStartedStateMachine));
460

V
VSadov 已提交
461
            return F.Block(blockBuilder.ToImmutableAndFree());
462 463 464 465 466
        }

        private BoundStatement GenerateAwaitOnCompletedDynamic(LocalSymbol awaiterTemp)
        {
            //  temp $criticalNotifyCompletedTemp = $awaiterTemp as ICriticalNotifyCompletion
467
            //  if ($criticalNotifyCompletedTemp != null)
468 469
            //  {
            //    this.builder.AwaitUnsafeOnCompleted<ICriticalNotifyCompletion,TSM>(
470
            //      ref $criticalNotifyCompletedTemp,
471
            //      ref this)
472 473
            //  }
            //  else
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
            //  {
            //    temp $notifyCompletionTemp = (INotifyCompletion)$awaiterTemp
            //    this.builder.AwaitOnCompleted<INotifyCompletion,TSM>(ref $notifyCompletionTemp, ref this)
            //    free $notifyCompletionTemp
            //  }
            //  free $criticalNotifyCompletedTemp

            var criticalNotifyCompletedTemp = F.SynthesizedLocal(
                F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_ICriticalNotifyCompletion),
                null);

            var notifyCompletionTemp = F.SynthesizedLocal(
                F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_INotifyCompletion),
                null);

489
            LocalSymbol thisTemp = (F.CurrentType.TypeKind == TypeKind.Class) ? F.SynthesizedLocal(F.CurrentType) : null;
490

491 492 493
            var blockBuilder = ArrayBuilder<BoundStatement>.GetInstance();

            blockBuilder.Add(
494 495
                F.Assignment(
                    F.Local(criticalNotifyCompletedTemp),
496
                        // Use reference conversion rather than dynamic conversion:
497
                        F.As(F.Local(awaiterTemp), criticalNotifyCompletedTemp.Type)));
498 499 500 501 502

            if (thisTemp != null)
            {
                blockBuilder.Add(F.Assignment(F.Local(thisTemp), F.This()));
            }
503

504
            blockBuilder.Add(
505
                F.If(
506
                    condition: F.ObjectEqual(F.Local(criticalNotifyCompletedTemp), F.Null(criticalNotifyCompletedTemp.Type)),
507 508 509 510 511

                    thenClause: F.Block(
                        ImmutableArray.Create(notifyCompletionTemp),
                        F.Assignment(
                            F.Local(notifyCompletionTemp),
512
                                // Use reference conversion rather than dynamic conversion:
513
                                F.Convert(notifyCompletionTemp.Type, F.Local(awaiterTemp), Conversion.ExplicitReference)),
514 515
                        F.ExpressionStatement(
                            F.Call(
516 517
                                F.Field(F.This(), _asyncMethodBuilderField),
                                _asyncMethodBuilderMemberCollection.AwaitOnCompleted.Construct(
518
                                    notifyCompletionTemp.Type,
519
                                    F.This().Type),
520
                                F.Local(notifyCompletionTemp), F.This(thisTemp))),
521 522
                        F.Assignment(
                            F.Local(notifyCompletionTemp),
523
                            F.NullOrDefault(notifyCompletionTemp.Type))),
524

525
                    elseClauseOpt: F.Block(
526 527
                        F.ExpressionStatement(
                            F.Call(
528 529
                                F.Field(F.This(), _asyncMethodBuilderField),
                                _asyncMethodBuilderMemberCollection.AwaitUnsafeOnCompleted.Construct(
530
                                    criticalNotifyCompletedTemp.Type,
531
                                    F.This().Type),
532
                                F.Local(criticalNotifyCompletedTemp), F.This(thisTemp))))));
533

534
            blockBuilder.Add(
535 536
                F.Assignment(
                    F.Local(criticalNotifyCompletedTemp),
537
                    F.NullOrDefault(criticalNotifyCompletedTemp.Type)));
538 539

            return F.Block(
540
                SingletonOrPair(criticalNotifyCompletedTemp, thisTemp),
541
                blockBuilder.ToImmutableAndFree());
542 543
        }

544
        private BoundStatement GenerateAwaitOnCompleted(TypeSymbol loweredAwaiterType, LocalSymbol awaiterTemp)
545 546 547 548 549
        {
            // this.builder.AwaitOnCompleted<TAwaiter,TSM>(ref $awaiterTemp, ref this)
            //    or
            // this.builder.AwaitOnCompleted<TAwaiter,TSM>(ref $awaiterArrayTemp[0], ref this)

550
            LocalSymbol thisTemp = (F.CurrentType.TypeKind == TypeKind.Class) ? F.SynthesizedLocal(F.CurrentType) : null;
551

552
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
553
            var useUnsafeOnCompleted = F.Compilation.Conversions.ClassifyImplicitConversionFromType(
554 555 556
                loweredAwaiterType,
                F.Compilation.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_ICriticalNotifyCompletion),
                ref useSiteDiagnostics).IsImplicit;
557

558
            var onCompleted = (useUnsafeOnCompleted ?
559 560
                _asyncMethodBuilderMemberCollection.AwaitUnsafeOnCompleted :
                _asyncMethodBuilderMemberCollection.AwaitOnCompleted).Construct(loweredAwaiterType, F.This().Type);
561 562 563 564
            if (_asyncMethodBuilderMemberCollection.CheckGenericMethodConstraints)
            {
                onCompleted.CheckConstraints(F.Compilation.Conversions, F.Syntax, F.Compilation, this.Diagnostics);
            }
565

566
            BoundExpression result =
567
                F.Call(
568
                    F.Field(F.This(), _asyncMethodBuilderField),
569
                    onCompleted,
570 571 572 573 574
                    F.Local(awaiterTemp), F.This(thisTemp));

            if (thisTemp != null)
            {
                result = F.Sequence(
575 576 577
                    ImmutableArray.Create(thisTemp),
                    ImmutableArray.Create<BoundExpression>(F.AssignmentExpression(F.Local(thisTemp), F.This())),
                    result);
578 579 580 581 582 583 584 585
            }

            return F.ExpressionStatement(result);
        }

        private static ImmutableArray<LocalSymbol> SingletonOrPair(LocalSymbol first, LocalSymbol secondOpt)
        {
            return (secondOpt == null) ? ImmutableArray.Create(first) : ImmutableArray.Create(first, secondOpt);
586 587
        }

588
        public sealed override BoundNode VisitReturnStatement(BoundReturnStatement node)
589 590 591
        {
            if (node.ExpressionOpt != null)
            {
592
                Debug.Assert(_method.IsGenericTaskReturningAsync(F.Compilation));
593
                return F.Block(
594 595
                    F.Assignment(F.Local(_exprRetValue), (BoundExpression)Visit(node.ExpressionOpt)),
                    F.Goto(_exprReturnLabel));
596 597
            }

598
            return F.Goto(_exprReturnLabel);
599 600 601
        }
        #endregion Visitors
    }
602
}