private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundStatement statement, bool substituteTemps)
        {
            if (builder == null)
            {
                // statement doesn't contain any await
                Debug.Assert(!substituteTemps || _tempSubstitution.Count == 0);
                Debug.Assert(statement != null);
                return(statement);
            }

            Debug.Assert(builder.Value == null);
            if (statement != null)
            {
                builder.AddStatement(statement);
            }

            var substituterOpt = (substituteTemps && _tempSubstitution.Count > 0) ? new LocalSubstituter(_tempSubstitution, RecursionDepth) : null;
            var result         = _F.Block(builder.GetLocals(), builder.GetStatements(substituterOpt));

            builder.Free();
            return(result);
        }
        public override BoundNode VisitSpillBlock(BoundSpillBlock node)
        {
            var newStatements = ArrayBuilder <BoundStatement> .GetInstance();

            spillFieldAllocator.AllocateFields(node.SpillTemps);

            newStatements.AddRange(VisitList(node.Statements));

            // Release references held by the spill temps:
            foreach (var spill in node.SpillTemps)
            {
                if (spill.Type.IsManagedType)
                {
                    var field = spillFieldAllocator.GetField(spill);
                    newStatements.Add(F.Assignment(F.Field(F.This(), field), F.NullOrDefault(field.Type)));
                }

                spillFieldAllocator.Free(spill);
            }

            return(F.Block(node.Locals, newStatements.ToReadOnlyAndFree()));
        }
        private BoundStatement InitializeFixedStatementStringLocal(
            LocalSymbol localSymbol,
            BoundFixedLocalCollectionInitializer fixedInitializer,
            SyntheticBoundNodeFactory factory,
            out LocalSymbol stringTemp,
            out LocalSymbol localToClear)
        {
            TypeSymbol localType = localSymbol.Type;
            BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression);
            TypeSymbol initializerType = initializerExpr.Type;

            // intervening parens may have been skipped by the binder; find the declarator
            VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf<VariableDeclaratorSyntax>();
            Debug.Assert(declarator != null);

            stringTemp = factory.SynthesizedLocal(initializerType, syntax: declarator, isPinned: true, kind: SynthesizedLocalKind.FixedString);

            // NOTE: we pin the string, not the pointer.
            Debug.Assert(stringTemp.IsPinned);
            Debug.Assert(!localSymbol.IsPinned);

            BoundStatement stringTempInit = factory.Assignment(factory.Local(stringTemp), initializerExpr);

            var convertedStringTemp = factory.Convert(
                localType,
                factory.Local(stringTemp),
                fixedInitializer.ElementPointerTypeConversion);

            BoundStatement localInit = AddLocalDeclarationSequencePointIfNecessary(declarator, localSymbol, 
                factory.Assignment(factory.Local(localSymbol), convertedStringTemp));

            BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, factory.Local(stringTemp), BinaryOperatorKind.NotEqual);
            BoundExpression helperCall;

            MethodSymbol offsetMethod;
            if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData, out offsetMethod))
            {
                helperCall = factory.Call(receiver: null, method: offsetMethod);
            }
            else
            {
                helperCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray<BoundNode>.Empty, ErrorTypeSymbol.UnknownResultType);
            }

            BoundExpression addition = factory.Binary(BinaryOperatorKind.PointerAndIntAddition, localType, factory.Local(localSymbol), helperCall);
            BoundStatement conditionalAdd = factory.If(notNullCheck, factory.Assignment(factory.Local(localSymbol), addition));

            localToClear = stringTemp;
            return factory.Block(stringTempInit, localInit, conditionalAdd);
        }
Beispiel #4
0
        /// <summary>
        /// Translate a statement that declares a given set of locals.  Also allocates and frees hoisted temps as
        /// required for the translation.
        /// </summary>
        /// <param name="locals">The set of locals declared in the original version of this statement</param>
        /// <param name="wrapped">A delegate to return the translation of the body of this statement</param>
        private BoundStatement PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped)
        {
            if (locals.IsDefaultOrEmpty)
            {
                return(wrapped());
            }

            var hoistedLocalsWithDebugScopes = ArrayBuilder <StateMachineFieldSymbol> .GetInstance();

            foreach (var local in locals)
            {
                if (!NeedsProxy(local))
                {
                    continue;
                }

                // Ref synthesized variables have proxies that are allocated in VisitAssignmentOperator.
                if (local.RefKind != RefKind.None)
                {
                    Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.Spill);
                    continue;
                }

                CapturedSymbolReplacement proxy;
                bool reused = false;
                if (!proxies.TryGetValue(local, out proxy))
                {
                    proxy = new CapturedToStateMachineFieldReplacement(GetOrAllocateReusableHoistedField(TypeMap.SubstituteType(local.Type).Type, out reused, local), isReusable: true);
                    proxies.Add(local, proxy);
                }

                // We need to produce hoisted local scope debug information for user locals as well as
                // lambda display classes, since Dev12 EE uses them to determine which variables are displayed
                // in Locals window.
                if ((local.SynthesizedKind == SynthesizedLocalKind.UserDefined && local.ScopeDesignatorOpt?.Kind() != SyntaxKind.SwitchSection) ||
                    local.SynthesizedKind == SynthesizedLocalKind.LambdaDisplayClass)
                {
                    // NB: This is the case when the local backed by recycled field will not be visible in debugger.
                    //     It may be possible in the future, but for now a backing field can be mapped only to a single local.
                    if (!reused)
                    {
                        hoistedLocalsWithDebugScopes.Add(((CapturedToStateMachineFieldReplacement)proxy).HoistedField);
                    }
                }
            }

            var translatedStatement = wrapped();
            var variableCleanup     = ArrayBuilder <BoundAssignmentOperator> .GetInstance();

            // produce cleanup code for all fields of locals defined by this block
            // as well as all proxies allocated by VisitAssignmentOperator within this block:
            foreach (var local in locals)
            {
                CapturedSymbolReplacement proxy;
                if (!proxies.TryGetValue(local, out proxy))
                {
                    continue;
                }

                var simpleProxy = proxy as CapturedToStateMachineFieldReplacement;
                if (simpleProxy != null)
                {
                    AddVariableCleanup(variableCleanup, simpleProxy.HoistedField);

                    if (proxy.IsReusable)
                    {
                        FreeReusableHoistedField(simpleProxy.HoistedField);
                    }
                }
                else
                {
                    foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedFields)
                    {
                        AddVariableCleanup(variableCleanup, field);

                        if (proxy.IsReusable)
                        {
                            FreeReusableHoistedField(field);
                        }
                    }
                }
            }

            if (variableCleanup.Count != 0)
            {
                translatedStatement = F.Block(
                    translatedStatement,
                    F.Block(variableCleanup.SelectAsArray((e, f) => (BoundStatement)f.ExpressionStatement(e), F)));
            }

            variableCleanup.Free();

            // wrap the node in an iterator scope for debugging
            if (hoistedLocalsWithDebugScopes.Count != 0)
            {
                translatedStatement = MakeStateMachineScope(hoistedLocalsWithDebugScopes.ToImmutable(), translatedStatement);
            }

            hoistedLocalsWithDebugScopes.Free();

            return(translatedStatement);
        }
        /// <summary>
        /// fixed(char* ptr = stringVar){ ... }    == becomes ===>
        ///
        /// pinned string pinnedTemp = stringVar;    // pinning managed ref
        /// char* ptr = (char*)pinnedTemp;           // unsafe cast to unmanaged ptr
        /// if (pinnedTemp != null) ptr += OffsetToStringData();
        ///   . . .
        /// </summary>
        private BoundStatement InitializeFixedStatementStringLocal(
            BoundLocalDeclaration localDecl,
            LocalSymbol localSymbol,
            BoundFixedLocalCollectionInitializer fixedInitializer,
            SyntheticBoundNodeFactory factory,
            out LocalSymbol pinnedTemp)
        {
            TypeSymbol      localType       = localSymbol.Type;
            BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression);
            TypeSymbol      initializerType = initializerExpr.Type;

            // intervening parens may have been skipped by the binder; find the declarator
            VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclaratorSyntax>();

            Debug.Assert(declarator != null);

            pinnedTemp = factory.SynthesizedLocal(
                initializerType,
                syntax: declarator,
                isPinned: true,
                kind: SynthesizedLocalKind.FixedReference);

            // NOTE: we pin the string, not the pointer.
            Debug.Assert(pinnedTemp.IsPinned);
            Debug.Assert(!localSymbol.IsPinned);

            BoundStatement stringTempInit = factory.Assignment(factory.Local(pinnedTemp), initializerExpr);

            // (char*)pinnedTemp;
            var addr = factory.Convert(
                fixedInitializer.ElementPointerType,
                factory.Local(pinnedTemp),
                Conversion.PinnedObjectToPointer);

            var convertedStringTemp = factory.Convert(
                localType,
                addr,
                fixedInitializer.ElementPointerTypeConversion);

            BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol,
                                                                             factory.Assignment(factory.Local(localSymbol), convertedStringTemp));

            BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, factory.Local(localSymbol), BinaryOperatorKind.NotEqual);
            BoundExpression helperCall;

            MethodSymbol offsetMethod;

            if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData, out offsetMethod))
            {
                helperCall = factory.Call(receiver: null, method: offsetMethod);
            }
            else
            {
                helperCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray <BoundExpression> .Empty, ErrorTypeSymbol.UnknownResultType);
            }

            BoundExpression addition       = factory.Binary(BinaryOperatorKind.PointerAndIntAddition, localType, factory.Local(localSymbol), helperCall);
            BoundStatement  conditionalAdd = factory.If(notNullCheck, factory.Assignment(factory.Local(localSymbol), addition));

            return(factory.Block(stringTempInit, localInit, conditionalAdd));
        }
            private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node)
            {
                var expression = _localRewriter.VisitExpression(node.Expression);
                var result     = ArrayBuilder <BoundStatement> .GetInstance();

                // EnC: We need to insert a hidden sequence point to handle function remapping in case
                // the containing method is edited while methods invoked in the expression are being executed.
                if (!node.WasCompilerGenerated && _localRewriter.Instrument)
                {
                    var instrumentedExpression = _localRewriter._instrumenter.InstrumentSwitchStatementExpression(node, expression, _factory);
                    if (expression.ConstantValue == null)
                    {
                        expression = instrumentedExpression;
                    }
                    else
                    {
                        // If the expression is a constant, we leave it alone (the decision tree lowering code needs
                        // to see that constant). But we add an additional leading statement with the instrumented expression.
                        result.Add(_factory.ExpressionStatement(instrumentedExpression));
                    }
                }

                // output the decision tree part
                LowerPatternSwitch(expression, node, result);

                // if the endpoint is reachable, we exit the switch
                if (!node.IsComplete)
                {
                    result.Add(_factory.Goto(node.BreakLabel));
                }
                // at this point the end of result is unreachable.

                _declaredTemps.AddRange(node.InnerLocals);

                // output the sections of code
                foreach (var section in node.SwitchSections)
                {
                    // Lifetime of these locals is expanded to the entire switch body.
                    _declaredTemps.AddRange(section.Locals);

                    // Start with the part of the decision tree that is in scope of the section variables.
                    // Its endpoint is not reachable (it jumps back into the decision tree code).
                    var sectionSyntax  = (SyntaxNode)section.Syntax;
                    var sectionBuilder = _switchSections[sectionSyntax];

                    // Add labels corresponding to the labels of the switch section.
                    foreach (var label in section.SwitchLabels)
                    {
                        sectionBuilder.Add(_factory.Label(label.Label));
                    }

                    // Add the translated body of the switch section
                    sectionBuilder.AddRange(_localRewriter.VisitList(section.Statements));

                    sectionBuilder.Add(_factory.Goto(node.BreakLabel));

                    ImmutableArray <BoundStatement> statements = sectionBuilder.ToImmutableAndFree();
                    if (section.Locals.IsEmpty)
                    {
                        result.Add(_factory.StatementList(statements));
                    }
                    else
                    {
                        result.Add(new BoundScope(section.Syntax, section.Locals, statements));
                    }
                    // at this point the end of result is unreachable.
                }

                result.Add(_factory.Label(node.BreakLabel));

                BoundStatement translatedSwitch = _factory.Block(_declaredTemps.ToImmutableArray(), node.InnerLocalFunctions, result.ToImmutableAndFree());

                // Only add instrumentation (such as a sequence point) if the node is not compiler-generated.
                if (!node.WasCompilerGenerated && _localRewriter.Instrument)
                {
                    translatedSwitch = _localRewriter._instrumenter.InstrumentPatternSwitchStatement(node, translatedSwitch);
                }

                return(translatedSwitch);
            }
        /// <summary>
        /// Translate a statement that declares a given set of locals.  Also allocates and frees hoisted temps as
        /// required for the translation.
        /// </summary>
        /// <param name="locals">The set of locals declared in the original version of this statement</param>
        /// <param name="wrapped">A delegate to return the translation of the body of this statement</param>
        private BoundNode PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped)
        {
            if (locals.IsDefaultOrEmpty)
            {
                return(wrapped());
            }
            var proxyFields = ArrayBuilder <SynthesizedFieldSymbolBase> .GetInstance();

            foreach (var local in locals)
            {
                if (!VariablesCaptured.Contains(local))
                {
                    continue;
                }
                CapturedSymbolReplacement proxy;
                if (proxies.TryGetValue(local, out proxy))
                {
                    // All of the user-declared variables have pre-allocated proxies
                    var field = proxy.HoistedField;
                    Debug.Assert((object)field != null);
                    Debug.Assert(local.DeclarationKind != LocalDeclarationKind.CompilerGenerated); // temps have lazily allocated proxies
                    if (local.DeclarationKind != LocalDeclarationKind.CompilerGenerated)
                    {
                        proxyFields.Add(field);
                    }
                }
                else
                {
                    if (local.RefKind == RefKind.None)
                    {
                        SynthesizedFieldSymbolBase field = MakeHoistedTemp(local, TypeMap.SubstituteType(local.Type));
                        proxy = new CapturedToFrameSymbolReplacement(field);
                        proxies.Add(local, proxy);
                    }
                    // ref temporary variables have proxies that are allocated on demand
                    // See VisitAssignmentOperator.
                }
            }

            var translatedStatement = wrapped();

            // produce code to free (e.g. mark as available for reuse) and clear (e.g. set to null) the proxies for any temps for these locals
            var clearTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance();

            foreach (var local in locals)
            {
                ArrayBuilder <SynthesizedFieldSymbolBase> frees;
                if (freeTempsMap.TryGetValue(local, out frees))
                {
                    Debug.Assert(local.DeclarationKind == LocalDeclarationKind.CompilerGenerated); // only temps are managed this way
                    freeTempsMap.Remove(local);
                    foreach (var field in frees)
                    {
                        if (MightContainReferences(field.Type))
                        {
                            clearTemps.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type)));
                        }
                        FreeTemp(field);
                    }
                    frees.Free();
                }
            }

            if (clearTemps.Count != 0)
            {
                translatedStatement = F.Block(
                    translatedStatement,
                    F.Block(clearTemps.Select(e => F.ExpressionStatement(e)).AsImmutable <BoundStatement>())
                    );
            }
            clearTemps.Free();

            // wrap the node in an iterator scope for debugging
            if (proxyFields.Count != 0)
            {
                translatedStatement = new BoundIteratorScope(F.Syntax, proxyFields.ToImmutable(), translatedStatement);
            }
            proxyFields.Free();

            return(translatedStatement);
        }
            private BoundStatement LowerNonprimitiveSwitch(BoundSwitchStatement switchStatement)
            {
                // Here we handle "other" types, such as float, double, and decimal, by
                // lowering the BoundSwitchStatement.
                // We compare the constant values using value.Equals(input), using ordinary
                // overload resolution. Note that we cannot and do not rely on switching
                // on the hash code, as it may not be consistent with the behavior of Equals;
                // see https://github.com/dotnet/coreclr/issues/6237. Also, the hash code is
                // not guaranteed to be the same on the compilation platform as the runtime
                // platform.
                // CONSIDER: can we improve the quality of the code using comparisons, like
                //           we do for other numeric types, by generating a series of tests
                //           that use divide-and-conquer to efficiently find a matching value?
                //           If so, we should use the BoundSwitchStatement and do that in emit.
                //           Moreover, we should be able to use `==` rather than `.Equals`
                //           for cases (such as non-NaN) where we know the result to be the same.
                var rewrittenSections = switchStatement.SwitchSections;
                var expression        = switchStatement.Expression;
                var noValueMatches    = switchStatement.BreakLabel;

                Debug.Assert(switchStatement.LoweredPreambleOpt == null);
                Debug.Assert(switchStatement.InnerLocals.IsDefaultOrEmpty);
                Debug.Assert(switchStatement.InnerLocalFunctions.IsDefaultOrEmpty);
                Debug.Assert(switchStatement.StringEquality == null);
                LabelSymbol nextLabel = null;
                var         builder   = ArrayBuilder <BoundStatement> .GetInstance();

                foreach (var section in rewrittenSections)
                {
                    foreach (var boundSwitchLabel in section.SwitchLabels)
                    {
                        if (nextLabel != null)
                        {
                            builder.Add(_factory.Label(nextLabel));
                        }
                        nextLabel = _factory.GenerateLabel("failcase+" + section.SwitchLabels[0].ConstantValueOpt.Value);

                        Debug.Assert(boundSwitchLabel.ConstantValueOpt != null);
                        // generate (if (value.Equals(input)) goto label;
                        var literal   = LocalRewriter.MakeLiteral(_factory.Syntax, boundSwitchLabel.ConstantValueOpt, expression.Type);
                        var condition = _factory.InstanceCall(literal, "Equals", expression);
                        if (!condition.HasErrors && condition.Type.SpecialType != SpecialType.System_Boolean)
                        {
                            var call = (BoundCall)condition;
                            // '{1} {0}' has the wrong return type
                            _factory.Diagnostics.Add(ErrorCode.ERR_BadRetType, boundSwitchLabel.Syntax.GetLocation(), call.Method, call.Type);
                        }

                        builder.Add(_factory.ConditionalGoto(condition, boundSwitchLabel.Label, true));
                        builder.Add(_factory.Goto(nextLabel));
                    }

                    foreach (var boundSwitchLabel in section.SwitchLabels)
                    {
                        builder.Add(_factory.Label(boundSwitchLabel.Label));
                    }

                    builder.Add(_factory.Block(section.Statements));
                    // this location should not be reachable.
                }

                Debug.Assert(nextLabel != null);
                builder.Add(_factory.Label(nextLabel));
                builder.Add(_factory.Label(noValueMatches));
                return(_factory.Block(builder.ToImmutableAndFree()));
            }
Beispiel #9
0
            private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node)
            {
                var expression = _localRewriter.VisitExpression(node.Expression);
                var result     = ArrayBuilder <BoundStatement> .GetInstance();

                // if the expression is "too complex", we copy it to a temp.
                LocalSymbol initialTemp = null;

                if (expression.ConstantValue == null)
                {
                    initialTemp = _factory.SynthesizedLocal(expression.Type, expression.Syntax);
                    result.Add(_factory.Assignment(_factory.Local(initialTemp), expression));
                    expression = _factory.Local(initialTemp);
                }

                // EnC: We need to insert a hidden sequence point to handle function remapping in case
                // the containing method is edited while methods invoked in the expression are being executed.
                if (!node.WasCompilerGenerated && _localRewriter.Instrument)
                {
                    expression = _localRewriter._instrumenter.InstrumentSwitchStatementExpression(node, expression, _factory);
                }

                // output the decision tree part
                LowerPatternSwitch(expression, node, result);

                // if the endpoint is reachable, we exit the switch
                if (!node.IsComplete)
                {
                    result.Add(_factory.Goto(node.BreakLabel));
                }
                // at this point the end of result is unreachable.

                // output the sections of code
                foreach (var section in node.SwitchSections)
                {
                    // Start with the part of the decision tree that is in scope of the section variables.
                    // Its endpoint is not reachable (it jumps back into the decision tree code).
                    var sectionSyntax  = (SyntaxNode)section.Syntax;
                    var sectionBuilder = _switchSections[sectionSyntax];

                    // Add labels corresponding to the labels of the switch section.
                    foreach (var label in section.SwitchLabels)
                    {
                        sectionBuilder.Add(_factory.Label(label.Label));
                    }

                    // Add the translated body of the switch section
                    sectionBuilder.AddRange(_localRewriter.VisitList(section.Statements));
                    sectionBuilder.Add(_factory.Goto(node.BreakLabel));
                    result.Add(_factory.Block(section.Locals, sectionBuilder.ToImmutableAndFree()));
                    // at this point the end of result is unreachable.
                }

                result.Add(_factory.Label(node.BreakLabel));
                _declaredTemps.AddOptional(initialTemp);
                _declaredTemps.AddRange(node.InnerLocals);
                BoundStatement translatedSwitch = _factory.Block(_declaredTemps.ToImmutableArray(), node.InnerLocalFunctions, result.ToImmutableAndFree());

                // Only add instrumentation (such as a sequence point) if the node is not compiler-generated.
                if (!node.WasCompilerGenerated && _localRewriter.Instrument)
                {
                    translatedSwitch = _localRewriter._instrumenter.InstrumentPatternSwitchStatement(node, translatedSwitch);
                }

                return(translatedSwitch);
            }
Beispiel #10
0
        /// <summary>
        /// Translate a statement that declares a given set of locals.  Also allocates and frees hoisted temps as
        /// required for the translation.
        /// </summary>
        /// <param name="locals">The set of locals declared in the original version of this statement</param>
        /// <param name="wrapped">A delegate to return the translation of the body of this statement</param>
        private BoundStatement PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped)
        {
            if (locals.IsDefaultOrEmpty)
            {
                return(wrapped());
            }

            var hoistedUserDefinedLocals = ArrayBuilder <SynthesizedFieldSymbolBase> .GetInstance();

            foreach (var local in locals)
            {
                if (!NeedsProxy(local))
                {
                    continue;
                }

                // Ref synthesized variables have proxies that are allocated in VisitAssignmentOperator.
                if (local.RefKind != RefKind.None)
                {
                    Debug.Assert(local.SynthesizedLocalKind == SynthesizedLocalKind.AwaitSpill);
                    continue;
                }

                Debug.Assert(local.SynthesizedLocalKind == SynthesizedLocalKind.None ||
                             local.SynthesizedLocalKind.IsLongLived());

                CapturedSymbolReplacement proxy;
                if (!proxies.TryGetValue(local, out proxy))
                {
                    proxy = new CapturedToFrameSymbolReplacement(GetOrAllocateHoistedField(TypeMap.SubstituteType(local.Type)), isReusable: true);
                    proxies.Add(local, proxy);
                }

                if (local.SynthesizedLocalKind == SynthesizedLocalKind.None)
                {
                    hoistedUserDefinedLocals.Add(((CapturedToFrameSymbolReplacement)proxy).HoistedField);
                }
            }

            var translatedStatement = wrapped();
            var variableCleanup     = ArrayBuilder <BoundAssignmentOperator> .GetInstance();

            // produce cleanup code for all fields of locals defined by this block
            // as well as all proxies allocated by VisitAssignmentOperator within this block:
            foreach (var local in locals)
            {
                CapturedSymbolReplacement proxy;
                if (!proxies.TryGetValue(local, out proxy))
                {
                    continue;
                }

                var simpleProxy = proxy as CapturedToFrameSymbolReplacement;
                if (simpleProxy != null)
                {
                    AddVariableCleanup(variableCleanup, simpleProxy.HoistedField);

                    if (proxy.IsReusable)
                    {
                        FreeHoistedField(simpleProxy.HoistedField);
                    }
                }
                else
                {
                    foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedFields)
                    {
                        AddVariableCleanup(variableCleanup, field);

                        if (proxy.IsReusable)
                        {
                            FreeHoistedField(field);
                        }
                    }
                }
            }

            if (variableCleanup.Count != 0)
            {
                translatedStatement = F.Block(
                    translatedStatement,
                    F.Block(variableCleanup.SelectAsArray((e, f) => (BoundStatement)f.ExpressionStatement(e), F)));
            }

            variableCleanup.Free();

            // wrap the node in an iterator scope for debugging
            if (hoistedUserDefinedLocals.Count != 0)
            {
                translatedStatement = F.Block(new BoundIteratorScope(F.Syntax, hoistedUserDefinedLocals.ToImmutable(), translatedStatement));
            }

            hoistedUserDefinedLocals.Free();

            return(translatedStatement);
        }