AsyncMethodToStateMachineRewriter.cs 24.6 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 4 5 6 7

using System;
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;
11
using Microsoft.CodeAnalysis.Symbols;
12 13 14 15
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
16
    internal sealed class AsyncMethodToStateMachineRewriter : MethodToStateMachineRewriter
17 18 19 20
    {
        /// <summary>
        /// The method being rewritten.
        /// </summary>
21
        private readonly MethodSymbol _method;
22 23 24 25 26 27

        /// <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>
28
        private readonly FieldSymbol _asyncMethodBuilderField;
29 30 31 32

        /// <summary>
        /// A collection of well-known members for the current async method builder.
        /// </summary>
33
        private readonly AsyncMethodBuilderMemberCollection _asyncMethodBuilderMemberCollection;
34 35 36 37 38

        /// <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>
39
        private readonly LabelSymbol _exprReturnLabel;
40 41 42 43

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

        /// <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>
51
        private readonly LocalSymbol _exprRetValue;
52

53
        private readonly LoweredDynamicOperationFactory _dynamicFactory;
54

55 56
        private readonly Dictionary<TypeSymbol, FieldSymbol> _awaiterFields;
        private int _nextAwaiterId;
57 58 59

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

79
            _exprRetValue = method.IsGenericTaskReturningAsync(F.Compilation)
80
                ? F.SynthesizedLocal(asyncMethodBuilderMemberCollection.ResultType, syntax: F.Syntax, kind: SynthesizedLocalKind.AsyncMethodReturnValue)
81 82
                : null;

83
            _dynamicFactory = new LoweredDynamicOperationFactory(F, methodOrdinal);
84
            _awaiterFields = new Dictionary<TypeSymbol, FieldSymbol>(TypeSymbol.EqualsIgnoringDynamicAndTupleNamesComparer);
85
            _nextAwaiterId = slotAllocatorOpt?.PreviousAwaiterSlotCount ?? 0;
86 87 88 89 90
        }

        private FieldSymbol GetAwaiterField(TypeSymbol awaiterType)
        {
            FieldSymbol result;
91 92 93 94 95

            // 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.
            // So we don't need to tie the awaiter variable to a particular await expression and only use its type 
            // to find the previous awaiter field.
96
            if (!_awaiterFields.TryGetValue(awaiterType, out result))
97
            {
98
                int slotIndex;
99
                if (slotAllocatorOpt == null || !slotAllocatorOpt.TryGetPreviousAwaiterSlotIndex(F.ModuleBuilderOpt.Translate(awaiterType, F.Syntax, F.Diagnostics), F.Diagnostics, out slotIndex))
100
                {
101
                    slotIndex = _nextAwaiterId++;
102 103
                }

104 105
                string fieldName = GeneratedNames.AsyncAwaiterFieldName(slotIndex);
                result = F.StateMachineField(awaiterType, fieldName, SynthesizedLocalKind.AwaiterField, slotIndex);
106
                _awaiterFields.Add(awaiterType, result);
107
            }
108

109 110 111 112 113 114 115 116 117 118
            return result;
        }

        /// <summary>
        /// Generate the body for <c>MoveNext()</c>.
        /// </summary>
        internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod)
        {
            F.CurrentMethod = moveNextMethod;

119
            BoundStatement rewrittenBody = (BoundStatement)Visit(body);
120

121 122 123
            ImmutableArray<StateMachineFieldSymbol> rootScopeHoistedLocals;
            TryUnwrapBoundStateMachineScope(ref rewrittenBody, out rootScopeHoistedLocals);

124 125
            var bodyBuilder = ArrayBuilder<BoundStatement>.GetInstance();

T
TomasMatousek 已提交
126 127
            bodyBuilder.Add(F.HiddenSequencePoint());
            bodyBuilder.Add(F.Assignment(F.Local(cachedState), F.Field(F.This(), stateField)));
128
            bodyBuilder.Add(CacheThisIfNeeded());
129

130
            var exceptionLocal = F.SynthesizedLocal(F.WellKnownType(WellKnownType.System_Exception));
131 132
            bodyBuilder.Add(
                F.Try(
T
TomasMatousek 已提交
133 134 135
                    F.Block(ImmutableArray<LocalSymbol>.Empty,
                        // switch (state) ...
                        F.HiddenSequencePoint(),
136
                        Dispatch(),
T
TomasMatousek 已提交
137 138
                        // [body]
                        rewrittenBody
139 140
                    ),
                    F.CatchBlocks(
141 142
                        new BoundCatchBlock(
                            F.Syntax,
143
                            ImmutableArray.Create(exceptionLocal),
144
                            F.Local(exceptionLocal),
145
                            exceptionLocal.Type,
146
                            exceptionFilterOpt: null,
147
                            body: F.Block(
T
TomasMatousek 已提交
148 149 150 151
                                // this.state = finishedState
                                F.Assignment(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)),
                                // builder.SetException(ex)
                                F.ExpressionStatement(
152
                                    F.Call(
153 154
                                        F.Field(F.This(), _asyncMethodBuilderField),
                                        _asyncMethodBuilderMemberCollection.SetException,
155
                                        F.Local(exceptionLocal))),
156 157
                                GenerateReturn(false)),
                            isSynthesizedAsyncCatchAll: true)
158 159
                        )
                    )
160
                );
161 162

            // ReturnLabel (for the rewritten return expressions in the user's method body)
163
            bodyBuilder.Add(F.Label(_exprReturnLabel));
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

            // 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
            }

            // builder.SetResult([RetVal])
            bodyBuilder.Add(
                F.ExpressionStatement(
                    F.Call(
184 185 186 187
                        F.Field(F.This(), _asyncMethodBuilderField),
                        _asyncMethodBuilderMemberCollection.SetResult,
                        _method.IsGenericTaskReturningAsync(F.Compilation)
                            ? ImmutableArray.Create<BoundExpression>(F.Local(_exprRetValue))
188 189 190
                            : ImmutableArray<BoundExpression>.Empty)));

            // this code is hidden behind a hidden sequence point.
191
            bodyBuilder.Add(F.Label(_exitLabel));
192 193
            bodyBuilder.Add(F.Return());

194
            var newStatements = bodyBuilder.ToImmutableAndFree();
195 196 197

            var locals = ArrayBuilder<LocalSymbol>.GetInstance();
            locals.Add(cachedState);
198 199 200 201 202
            if ((object)cachedThis != null)
            {
                locals.Add(cachedThis);
            }

203
            if ((object)_exprRetValue != null) locals.Add(_exprRetValue);
204

205
            var newBody =
206
                F.SequencePoint(
J
Jared Parsons 已提交
207
                    body.Syntax,
208
                    F.Block(
209
                        locals.ToImmutableAndFree(),
210 211 212 213 214 215
                        newStatements));

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

217
            F.CloseMethod(newBody);
218 219 220 221
        }

        protected override BoundStatement GenerateReturn(bool finished)
        {
222
            return F.Goto(_exitLabel);
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
        }

        #region Visitors

        private enum AwaitableDynamism
        {
            None,
            DynamicTask,
            FullDynamic
        }

        public override BoundNode VisitExpressionStatement(BoundExpressionStatement node)
        {
            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 已提交
251
            return (expr != null) ? node.Update(expr) : (BoundStatement)F.StatementList();
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
        }

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

        public override BoundNode VisitBadExpression(BoundBadExpression node)
        {
            // Cannot recurse into BadExpression
            return node;
        }

        private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace)
        {
            var expression = (BoundExpression)Visit(node.Expression);
            resultPlace = (BoundExpression)Visit(resultPlace);
            MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter);
            MethodSymbol getResult = VisitMethodSymbol(node.GetResult);
            MethodSymbol isCompletedMethod = ((object)node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null;
            TypeSymbol type = VisitType(node.Type);

275 276 277
            // 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.
            Debug.Assert(node.Syntax.IsKind(SyntaxKind.AwaitExpression));
278
            TypeSymbol awaiterType = node.IsDynamic ? DynamicTypeSymbol.Instance : getAwaiter.ReturnType;
279
            var awaiterTemp = F.SynthesizedLocal(awaiterType, syntax: node.Syntax, kind: SynthesizedLocalKind.Awaiter);
280 281

            var awaitIfIncomplete = F.Block(
282 283
                    // temp $awaiterTemp = <expr>.GetAwaiter();
                    F.Assignment(
284 285
                        F.Local(awaiterTemp),
                        MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)),
286

287 288 289
                    // hidden sequence point facilitates EnC method remapping, see explanation on SynthesizedLocalKind.Awaiter:
                    F.HiddenSequencePoint(),

290 291
                    // if(!($awaiterTemp.IsCompleted)) { ... }
                    F.If(
292 293
                        condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)),
                        thenClause: GenerateAwaitForIncompleteTask(awaiterTemp)));
294 295 296 297 298 299 300

            BoundExpression getResultCall = MakeCallMaybeDynamic(
                F.Local(awaiterTemp),
                getResult,
                WellKnownMemberNames.GetResult,
                resultsDiscarded: resultPlace == null);

301 302 303 304 305 306 307 308 309
            // [$resultPlace = ] $awaiterTemp.GetResult();
            BoundStatement getResultStatement = resultPlace != null && type.SpecialType != SpecialType.System_Void ?
                F.Assignment(resultPlace, getResultCall):
                F.ExpressionStatement(getResultCall);

            return F.Block(
                ImmutableArray.Create(awaiterTemp),
                awaitIfIncomplete,
                getResultStatement);
310 311 312 313 314
        }

        private BoundExpression MakeCallMaybeDynamic(
            BoundExpression receiver,
            MethodSymbol methodSymbol = null,
315
            string methodName = null,
316 317 318 319 320 321 322 323 324 325 326 327 328 329
            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);
330
            return _dynamicFactory.MakeDynamicMemberInvocation(
331 332 333 334
                methodName,
                receiver,
                typeArguments: ImmutableArray<TypeSymbol>.Empty,
                loweredArguments: ImmutableArray<BoundExpression>.Empty,
335
                argumentNames: ImmutableArray<string>.Empty,
336 337 338 339 340 341 342 343 344
                refKinds: ImmutableArray<RefKind>.Empty,
                hasImplicitReceiver: false,
                resultDiscarded: resultsDiscarded).ToExpression();
        }

        private BoundExpression GenerateGetIsCompleted(LocalSymbol awaiterTemp, MethodSymbol getIsCompletedMethod)
        {
            if (awaiterTemp.Type.IsDynamic())
            {
345 346
                return _dynamicFactory.MakeDynamicConversion(
                    _dynamicFactory.MakeDynamicGetMember(
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
                        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)
        {
            int stateNumber;
            GeneratedLabelSymbol resumeLabel;
            AddState(out stateNumber, out resumeLabel);

            TypeSymbol awaiterFieldType = awaiterTemp.Type.IsVerifierReference()
                ? F.SpecialType(SpecialType.System_Object)
                : awaiterTemp.Type;

            FieldSymbol awaiterField = GetAwaiterField(awaiterFieldType);

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

            blockBuilder.Add(
374 375
                    // this.state = cachedState = stateForLabel
                    F.Assignment(F.Field(F.This(), stateField), F.AssignmentExpression(F.Local(cachedState), F.Literal(stateNumber))));
376 377

            blockBuilder.Add(
378 379
                    // Emit await yield point to be injected into PDB
                    F.NoOp(NoOpStatementFlavor.AwaitYieldPoint));
380 381

            blockBuilder.Add(
382 383
                    // this.<>t__awaiter = $awaiterTemp
                    F.Assignment(
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
                    F.Field(F.This(), awaiterField),
                    (awaiterField.Type == awaiterTemp.Type)
                        ? F.Local(awaiterTemp)
                        : F.Convert(awaiterFieldType, F.Local(awaiterTemp))));

            blockBuilder.Add(awaiterTemp.Type.IsDynamic()
                ? GenerateAwaitOnCompletedDynamic(awaiterTemp)
                : GenerateAwaitOnCompleted(awaiterTemp.Type, awaiterTemp));

            blockBuilder.Add(
                GenerateReturn(false));

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

            blockBuilder.Add(
400 401
                    // Emit await resume point to be injected into PDB
                    F.NoOp(NoOpStatementFlavor.AwaitResumePoint));
402 403

            blockBuilder.Add(
404 405 406
                    // $awaiterTemp = this.<>t__awaiter   or   $awaiterTemp = (AwaiterType)this.<>t__awaiter
                    // $this.<>t__awaiter = null;
                    F.Assignment(
407 408 409 410 411 412 413 414 415
                    F.Local(awaiterTemp),
                    awaiterTemp.Type == awaiterField.Type
                        ? F.Field(F.This(), awaiterField)
                        : F.Convert(awaiterTemp.Type, F.Field(F.This(), awaiterField))));

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

            blockBuilder.Add(
416 417
                    // this.state = cachedState = NotStartedStateMachine
                    F.Assignment(F.Field(F.This(), stateField), F.AssignmentExpression(F.Local(cachedState), F.Literal(StateMachineStates.NotStartedStateMachine))));
418

V
VSadov 已提交
419
            return F.Block(blockBuilder.ToImmutableAndFree());
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
        }

        private BoundStatement GenerateAwaitOnCompletedDynamic(LocalSymbol awaiterTemp)
        {
            //  temp $criticalNotifyCompletedTemp = $awaiterTemp as ICriticalNotifyCompletion
            //  if ($criticalNotifyCompletedTemp != null) 
            //  {
            //    this.builder.AwaitUnsafeOnCompleted<ICriticalNotifyCompletion,TSM>(
            //      ref $criticalNotifyCompletedTemp, 
            //      ref this)
            //  } 
            //  else 
            //  {
            //    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);

447
            LocalSymbol thisTemp = (F.CurrentType.TypeKind == TypeKind.Class) ? F.SynthesizedLocal(F.CurrentType) : null;
448

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

            blockBuilder.Add(
452 453
                F.Assignment(
                    F.Local(criticalNotifyCompletedTemp),
454 455 456 457 458 459 460
                        // Use reference conversion rather than dynamic conversion:
                        F.As(F.Local(awaiterTemp), criticalNotifyCompletedTemp.Type)));

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

462
            blockBuilder.Add(
463 464 465 466 467 468 469
                F.If(
                    condition: F.ObjectEqual(F.Local(criticalNotifyCompletedTemp), F.Null(criticalNotifyCompletedTemp.Type)),

                    thenClause: F.Block(
                        ImmutableArray.Create(notifyCompletionTemp),
                        F.Assignment(
                            F.Local(notifyCompletionTemp),
470
                                // Use reference conversion rather than dynamic conversion:
V
VSadov 已提交
471
                                F.Convert(notifyCompletionTemp.Type, F.Local(awaiterTemp), Conversion.ExplicitReference)),
472 473
                        F.ExpressionStatement(
                            F.Call(
474 475
                                F.Field(F.This(), _asyncMethodBuilderField),
                                _asyncMethodBuilderMemberCollection.AwaitOnCompleted.Construct(
476 477
                                    notifyCompletionTemp.Type,
                                    F.This().Type),
478
                                F.Local(notifyCompletionTemp), F.This(thisTemp))),
479 480 481 482
                        F.Assignment(
                            F.Local(notifyCompletionTemp),
                            F.NullOrDefault(notifyCompletionTemp.Type))),

483
                    elseClauseOpt: F.Block(
484 485
                        F.ExpressionStatement(
                            F.Call(
486 487
                                F.Field(F.This(), _asyncMethodBuilderField),
                                _asyncMethodBuilderMemberCollection.AwaitUnsafeOnCompleted.Construct(
488 489
                                    criticalNotifyCompletedTemp.Type,
                                    F.This().Type),
490
                                F.Local(criticalNotifyCompletedTemp), F.This(thisTemp))))));
491

492
            blockBuilder.Add(
493 494 495
                F.Assignment(
                    F.Local(criticalNotifyCompletedTemp),
                    F.NullOrDefault(criticalNotifyCompletedTemp.Type)));
496 497

            return F.Block(
498
                SingletonOrPair(criticalNotifyCompletedTemp, thisTemp),
499
                blockBuilder.ToImmutableAndFree());
500 501
        }

502
        private BoundStatement GenerateAwaitOnCompleted(TypeSymbol loweredAwaiterType, LocalSymbol awaiterTemp)
503 504 505 506 507
        {
            // this.builder.AwaitOnCompleted<TAwaiter,TSM>(ref $awaiterTemp, ref this)
            //    or
            // this.builder.AwaitOnCompleted<TAwaiter,TSM>(ref $awaiterArrayTemp[0], ref this)

508
            LocalSymbol thisTemp = (F.CurrentType.TypeKind == TypeKind.Class) ? F.SynthesizedLocal(F.CurrentType) : null;
509

510
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
511
            var useUnsafeOnCompleted = F.Compilation.Conversions.ClassifyImplicitConversionFromType(
512 513 514
                loweredAwaiterType,
                F.Compilation.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_ICriticalNotifyCompletion),
                ref useSiteDiagnostics).IsImplicit;
515

516
            var onCompleted = (useUnsafeOnCompleted ?
517 518
                _asyncMethodBuilderMemberCollection.AwaitUnsafeOnCompleted :
                _asyncMethodBuilderMemberCollection.AwaitOnCompleted).Construct(loweredAwaiterType, F.This().Type);
519

520
            BoundExpression result =
521
                F.Call(
522
                    F.Field(F.This(), _asyncMethodBuilderField),
523
                    onCompleted,
524 525 526 527 528
                    F.Local(awaiterTemp), F.This(thisTemp));

            if (thisTemp != null)
            {
                result = F.Sequence(
529 530 531
                    ImmutableArray.Create(thisTemp),
                    ImmutableArray.Create<BoundExpression>(F.AssignmentExpression(F.Local(thisTemp), F.This())),
                    result);
532 533 534 535 536 537 538 539
            }

            return F.ExpressionStatement(result);
        }

        private static ImmutableArray<LocalSymbol> SingletonOrPair(LocalSymbol first, LocalSymbol secondOpt)
        {
            return (secondOpt == null) ? ImmutableArray.Create(first) : ImmutableArray.Create(first, secondOpt);
540 541 542 543 544 545
        }

        public override BoundNode VisitReturnStatement(BoundReturnStatement node)
        {
            if (node.ExpressionOpt != null)
            {
546
                Debug.Assert(_method.IsGenericTaskReturningAsync(F.Compilation));
547
                return F.Block(
548 549
                    F.Assignment(F.Local(_exprRetValue), (BoundExpression)Visit(node.ExpressionOpt)),
                    F.Goto(_exprReturnLabel));
550 551
            }

552
            return F.Goto(_exprReturnLabel);
553 554 555 556
        }

        #endregion Visitors
    }
557
}