File: Lowering\LambdaRewriter\LambdaRewriter.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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.
 
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    ''' <summary>
    ''' The rewriter for removing lambda expressions from method bodies and introducing closure classes
    ''' as containers for captured variables along the lines of the example in section 6.5.3 of the
    ''' C# language specification.
    ''' 
    ''' The entry point is the public method Rewrite.  It operates as follows:
    ''' 
    ''' First, an analysis of the whole method body is performed that determines which variables are
    ''' captured, what their scopes are, and what the nesting relationship is between scopes that
    ''' have captured variables.  The result of this analysis is left in LambdaRewriter.analysis.
    ''' 
    ''' Then we make frame, or compiler-generated class, represented by an instance of
    ''' LambdaRewriter.Frame for each scope with captured variables.  The generated frames are kept
    ''' in LambdaRewriter.frames.  Each frame is given a single field for each captured
    ''' variable in the corresponding scope.  These are maintained in LambdaRewriter.proxies.
    ''' 
    ''' Finally, we walk and rewrite the input bound tree, keeping track of the following:
    ''' (1) The current set of active frame pointers, in LambdaRewriter.framePointers
    ''' (2) The current method being processed (this changes within a lambda's body), in LambdaRewriter.currentMethod
    ''' (3) The "this" symbol for the current method in LambdaRewriter.currentFrameThis, and
    ''' (4) The symbol that is used to access the innermost frame pointer (it could be a local variable or "this" parameter)
    ''' 
    ''' There are a few key transformations done in the rewriting.
    ''' (1) Lambda expressions are turned into delegate creation expressions, and the body of the lambda is
    '''     moved into a new, compiler-generated method of a selected frame class.
    ''' (2) On entry to a scope with captured variables, we create a frame object and store it in a local variable.
    ''' (3) References to captured variables are transformed into references to fields of a frame class.
    ''' 
    ''' In addition, the rewriting deposits into the field LambdaRewriter.generatedMethods a (MethodSymbol, BoundStatement)
    ''' pair for each generated method.
    ''' 
    ''' LambdaRewriter.Rewrite produces its output in two forms.  First, it returns a new bound statement
    ''' for the caller to use for the body of the original method.  Second, it returns a collection of
    ''' (MethodSymbol, BoundStatement) pairs for additional method that the lambda rewriter produced.
    ''' These additional methods contain the bodies of the lambdas moved into ordinary methods of their
    ''' respective frame classes, and the caller is responsible for processing them just as it does with
    ''' the returned bound node.  For example, the caller will typically perform iterator method and
    ''' asynchronous method transformations, and emit IL instructions into an assembly.
    ''' </summary>
    Partial Friend NotInheritable Class LambdaRewriter
        Inherits MethodToClassRewriter(Of FieldSymbol)
 
        Private ReadOnly _analysis As Analysis
        Private ReadOnly _topLevelMethod As MethodSymbol
        Private ReadOnly _topLevelMethodOrdinal As Integer
 
        ' lambda frame for static lambdas. 
        ' initialized lazily and could be Nothing if there are no static lambdas
        Private _lazyStaticLambdaFrame As LambdaFrame
 
        ' for each block with lifted (captured) variables, the corresponding frame type
        Private ReadOnly _frames As Dictionary(Of BoundNode, LambdaFrame) = New Dictionary(Of BoundNode, LambdaFrame)()
 
        ' the current set of frame pointers in scope.  Each is either a local variable (where introduced),
        ' or the "this" parameter when at the top level. Keys in this map are never constructed types.
        Private ReadOnly _framePointers As Dictionary(Of NamedTypeSymbol, Symbol) = New Dictionary(Of NamedTypeSymbol, Symbol)()
 
        ' The method/lambda which is currently being rewritten.
        ' if we are rewriting a lambda, currentMethod is the new generated method.
        Private _currentMethod As MethodSymbol
 
        ' "This" in the context of current method.
        Private _currentFrameThis As ParameterSymbol
 
        Private ReadOnly _lambdaDebugInfoBuilder As ArrayBuilder(Of EncLambdaInfo)
        Private ReadOnly _lambdaRuntimeRudeEditsBuilder As ArrayBuilder(Of LambdaRuntimeRudeEditInfo)
 
        Private _delegateRelaxationIdDispenser As Integer
 
        ' ID dispenser for field names of frame references.
        Private _synthesizedFieldNameIdDispenser As Integer
 
        ' The symbol (field or local) holding the innermost frame. 
        ' Needed in case inner frame needs to reference outer frame.
        Private _innermostFramePointer As Symbol
 
        ' The mapping of type parameters for the current lambda body
        Private _currentLambdaBodyTypeSubstitution As TypeSubstitution
 
        ' The current set of type parameters (mapped from the enclosing method's type parameters)
        Private _currentTypeParameters As ImmutableArray(Of TypeParameterSymbol)
 
        'initialization for the proxy of the upper frame if it needs to be deferred 
        'such situation happens when lifting Me in a ctor.
        'CLR requires that the first use of "Me" must be a constructor call for which "Me" is a receiver
        'only after that we can proceed with lifting "Me"
        Private _meProxyDeferredInit As BoundExpression
        Private _meIsInitialized As Boolean
        Private _meProxyDeferredInitDone As Boolean
 
        ' Are we code that will be rewritten into an expression tree?
        Private _inExpressionLambda As Boolean
 
        Private _reported_ERR_CannotUseOnErrorGotoWithClosure As Boolean
 
#Disable Warning IDE0044 ' Add readonly modifier - The field is assigned in "#If DEBUG"
        ''' <summary> WARNING: used ONLY in DEBUG </summary>
        Private _rewrittenNodes As HashSet(Of BoundNode) = Nothing
#Enable Warning IDE0044 ' Add readonly modifier
 
        Private Sub New(analysis As Analysis,
                        method As MethodSymbol,
                        methodOrdinal As Integer,
                        lambdaDebugInfoBuilder As ArrayBuilder(Of EncLambdaInfo),
                        lambdaRuntimeRudeEditsBuilder As ArrayBuilder(Of LambdaRuntimeRudeEditInfo),
                        delegateRelaxationIdDispenser As Integer,
                        slotAllocatorOpt As VariableSlotAllocator,
                        compilationState As TypeCompilationState,
                        diagnostics As BindingDiagnosticBag)
            MyBase.New(slotAllocatorOpt, compilationState, diagnostics, method.PreserveOriginalLocals)
 
            Me._topLevelMethod = method
            Me._topLevelMethodOrdinal = methodOrdinal
            Me._lambdaDebugInfoBuilder = lambdaDebugInfoBuilder
            Me._lambdaRuntimeRudeEditsBuilder = lambdaRuntimeRudeEditsBuilder
            Me._delegateRelaxationIdDispenser = delegateRelaxationIdDispenser
            Me._currentMethod = method
            Me._analysis = analysis
            Me._currentTypeParameters = Me._topLevelMethod.TypeParameters
            Me._inExpressionLambda = False
 
            If Not method.IsShared Then
                Me._innermostFramePointer = method.MeParameter
                _framePointers(method.ContainingType) = method.MeParameter
            End If
 
            Me._currentFrameThis = method.MeParameter
            Me._synthesizedFieldNameIdDispenser = 1
        End Sub
 
        ''' <summary>
        ''' Rewrite the given node to eliminate lambda expressions.  Also returned are the method symbols and their
        ''' bound bodies for the extracted lambda bodies. These would typically be emitted by the caller such as
        ''' MethodBodyCompiler.  See this class' documentation
        ''' for a more thorough explanation of the algorithm and its use by clients.
        ''' </summary>
        ''' <param name="node">The bound node to be rewritten</param>
        ''' <param name="method">The containing method of the node to be rewritten</param>
        ''' <param name="methodOrdinal">Index of the method symbol in its containing type member list.</param>
        ''' <param name="lambdaDebugInfoBuilder">Information on lambdas defined in <paramref name="method"/> needed for debugging.</param>
        ''' <param name="lambdaRuntimeRudeEditsBuilder">EnC rude edit information on lambdas defined in <paramref name="method"/>.</param>
        ''' <param name="closureDebugInfoBuilder">Information on closures defined in <paramref name="method"/> needed for debugging.</param>
        ''' <param name="compilationState">The caller's buffer into which we produce additional methods to be emitted by the caller</param>
        ''' <param name="symbolsCapturedWithoutCopyCtor">Set of symbols that should not be captured using a copy constructor</param>
        ''' <param name="diagnostics">The caller's buffer into which we place any diagnostics for problems encountered</param>
        Public Shared Function Rewrite(node As BoundBlock,
                                       method As MethodSymbol,
                                       methodOrdinal As Integer,
                                       lambdaDebugInfoBuilder As ArrayBuilder(Of EncLambdaInfo),
                                       lambdaRuntimeRudeEditsBuilder As ArrayBuilder(Of LambdaRuntimeRudeEditInfo),
                                       closureDebugInfoBuilder As ArrayBuilder(Of EncClosureInfo),
                                       ByRef delegateRelaxationIdDispenser As Integer,
                                       slotAllocatorOpt As VariableSlotAllocator,
                                       compilationState As TypeCompilationState,
                                       symbolsCapturedWithoutCopyCtor As ISet(Of Symbol),
                                       diagnostics As BindingDiagnosticBag,
                                       rewrittenNodes As HashSet(Of BoundNode)) As BoundBlock
 
            Dim analysis = LambdaRewriter.Analysis.AnalyzeMethodBody(node, method, symbolsCapturedWithoutCopyCtor, diagnostics)
            If Not analysis.seenLambda Then
                Return node
            End If
 
            Dim rewriter = New LambdaRewriter(analysis,
                                              method,
                                              methodOrdinal,
                                              lambdaDebugInfoBuilder,
                                              lambdaRuntimeRudeEditsBuilder,
                                              delegateRelaxationIdDispenser,
                                              slotAllocatorOpt,
                                              compilationState,
                                              diagnostics)
#If DEBUG Then
            Debug.Assert(rewrittenNodes IsNot Nothing)
            rewriter._rewrittenNodes = rewrittenNodes
#End If
 
            analysis.ComputeLambdaScopesAndFrameCaptures()
            rewriter.MakeFrames(closureDebugInfoBuilder)
 
            Dim body = DirectCast(rewriter.Visit(node), BoundBlock)
 
            Debug.Assert(rewriter._meProxyDeferredInitDone OrElse rewriter._meProxyDeferredInit Is Nothing)
 
            ' The dispenser could be updated during the lambda rewrite:
            delegateRelaxationIdDispenser = rewriter._delegateRelaxationIdDispenser
 
            Return body
        End Function
 
        Protected Overrides ReadOnly Property CurrentMethod As MethodSymbol
            Get
                Return Me._currentMethod
            End Get
        End Property
 
        Protected Overrides ReadOnly Property TopLevelMethod As MethodSymbol
            Get
                Return Me._topLevelMethod
            End Get
        End Property
 
        Protected Overrides ReadOnly Property TypeMap As TypeSubstitution
            Get
                Return Me._currentLambdaBodyTypeSubstitution
            End Get
        End Property
 
        Protected Overrides ReadOnly Property IsInExpressionLambda As Boolean
            Get
                Return Me._inExpressionLambda
            End Get
        End Property
 
        ''' <summary>
        ''' Create the frame types.
        ''' </summary>
        Private Sub MakeFrames(closureDebugInfo As ArrayBuilder(Of EncClosureInfo))
            ' There is a simple test to determine whether to do copy-construction:
            ' If method contains a backward branch, then all closures should attempt copy-construction.
            ' In the worst case, we will redundantly check if a previous version exists which is cheap
            ' compared to other costs associated with closure.
            '
            ' In some cases the closure should never be copy constructed, where the symbols are contained in 
            '  _analysis.symbolsCapturedWithoutCopyCtor.
            Dim copyConstructor = _analysis.seenBackBranches
 
            For Each captured In _analysis.capturedVariables
                Dim node As BoundNode = Nothing
                If Not _analysis.variableScope.TryGetValue(captured, node) OrElse _analysis.declaredInsideExpressionLambda.Contains(captured) Then
                    Continue For
                End If
 
                Dim frame As LambdaFrame = GetFrameForScope(copyConstructor, captured, node, closureDebugInfo, _delegateRelaxationIdDispenser)
 
                Dim proxy = LambdaCapturedVariable.Create(frame, captured, _synthesizedFieldNameIdDispenser)
                Proxies.Add(captured, proxy)
                frame.CapturedLocals.Add(proxy)
            Next
 
            For Each frame In _frames.Values
                CompilationState.AddSynthesizedMethod(frame.Constructor, MakeFrameCtor(frame, Diagnostics), stateMachineType:=Nothing, ImmutableArray(Of StateMachineStateDebugInfo).Empty)
            Next
        End Sub
 
        Private Function GetFrameForScope(copyConstructor As Boolean,
                                          captured As Symbol,
                                          scope As BoundNode,
                                          closureDebugInfo As ArrayBuilder(Of EncClosureInfo),
                                          ByRef delegateRelaxationIdDispenser As Integer) As LambdaFrame
            Dim frame As LambdaFrame = Nothing
 
            If Not _frames.TryGetValue(scope, frame) Then
                ' if the control variable of a for each is lifted, make sure it's using the copy constructor
                Debug.Assert(captured.Kind <> SymbolKind.Local OrElse
                             Not DirectCast(captured, LocalSymbol).IsForEach OrElse
                             copyConstructor)
 
                Dim syntax = scope.Syntax
                Debug.Assert(syntax IsNot Nothing)
 
                ' Frames created for delegate relaxations are just immutable wrappers of the delegate target object.
                ' They are not reused during EnC update and thus don't have a closure scope.
                Dim isDelegateRelaxationFrame = If(TryCast(captured, SynthesizedLocal)?.SynthesizedKind = SynthesizedLocalKind.DelegateRelaxationReceiver, False)
 
                Dim methodId, closureId As DebugId
                Dim rudeEdit As RuntimeRudeEdit? = Nothing
 
                If isDelegateRelaxationFrame Then
                    Dim currentGeneration = CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal
                    methodId = New DebugId(_topLevelMethodOrdinal, currentGeneration)
                    closureId = New DebugId(delegateRelaxationIdDispenser, currentGeneration)
                    delegateRelaxationIdDispenser += 1
                Else
                    methodId = GetTopLevelMethodId()
                    closureId = GetClosureId(scope, syntax, closureDebugInfo, rudeEdit)
                End If
 
                frame = New LambdaFrame(_topLevelMethod,
                                        syntax,
                                        methodId,
                                        closureId,
                                        rudeEdit,
                                        copyConstructor AndAlso Not _analysis.symbolsCapturedWithoutCopyCtor.Contains(captured),
                                        isStatic:=False,
                                        isDelegateRelaxationFrame:=isDelegateRelaxationFrame)
 
                _frames(scope) = frame
 
                CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(_topLevelMethod.ContainingType, frame.GetCciAdapter())
                ' NOTE: we will add this ctor to compilation state after we know all captured locals
                '       we need them to generate copy constructor, if needed
            End If
 
            Return frame
        End Function
 
        Private Function GetStaticFrame(lambda As BoundNode, diagnostics As BindingDiagnosticBag) As LambdaFrame
            If Me._lazyStaticLambdaFrame Is Nothing Then
                Dim isNonGeneric = Not TopLevelMethod.IsGenericMethod
                If isNonGeneric Then
                    Me._lazyStaticLambdaFrame = CompilationState.staticLambdaFrame
                End If
 
                If Me._lazyStaticLambdaFrame Is Nothing Then
                    Dim methodId As DebugId
                    If isNonGeneric Then
                        methodId = New DebugId(DebugId.UndefinedOrdinal, CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal)
                    Else
                        methodId = GetTopLevelMethodId()
                    End If
 
                    Dim closureId As DebugId = Nothing
                    _lazyStaticLambdaFrame = New LambdaFrame(
                        _topLevelMethod,
                        lambda.Syntax,
                        methodId,
                        closureId,
                        rudeEdit:=Nothing,
                        copyConstructor:=False,
                        isStatic:=True,
                        isDelegateRelaxationFrame:=False)
 
                    ' non-generic static lambdas can share the frame
                    If isNonGeneric Then
                        CompilationState.staticLambdaFrame = Me._lazyStaticLambdaFrame
                    End If
 
                    Dim frame = Me._lazyStaticLambdaFrame
 
                    ' add frame type
                    CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(_topLevelMethod.ContainingType, frame.GetCciAdapter())
 
                    ' associate the frame with the first lambda that caused it to exist. 
                    ' we need to associate this with some syntax.
                    ' unfortunately either containing method or containing class could be synthetic
                    ' therefore could have no syntax.
                    Dim syntax = lambda.Syntax
 
                    ' add its ctor
                    CompilationState.AddSynthesizedMethod(frame.Constructor, MakeFrameCtor(frame, diagnostics), stateMachineType:=Nothing, ImmutableArray(Of StateMachineStateDebugInfo).Empty)
 
                    ' add cctor
                    ' Frame.inst = New Frame()
                    Dim F = New SyntheticBoundNodeFactory(frame.SharedConstructor, frame.SharedConstructor, syntax, CompilationState, diagnostics)
                    Dim body = F.Block(
                            F.Assignment(
                                F.Field(Nothing, frame.SingletonCache, isLValue:=True),
                                F.[New](frame.Constructor)),
                            F.Return())
 
                    CompilationState.AddSynthesizedMethod(frame.SharedConstructor, body, stateMachineType:=Nothing, ImmutableArray(Of StateMachineStateDebugInfo).Empty)
                End If
            End If
 
            Return Me._lazyStaticLambdaFrame
        End Function
 
        ''' <summary>
        ''' Produces a bound expression representing a pointer to a frame of a particular frame type.
        ''' </summary>
        ''' <param name="syntax">The syntax to attach to the bound nodes produced</param>
        ''' <param name="frameType">The type of frame to be returned</param>
        ''' <returns>A bound node that computes the pointer to the required frame</returns>
        Private Function FrameOfType(syntax As SyntaxNode, frameType As NamedTypeSymbol) As BoundExpression
            Dim result As BoundExpression = FramePointer(syntax, frameType.OriginalDefinition)
            Debug.Assert(TypeSymbol.Equals(result.Type, frameType, TypeCompareKind.ConsiderEverything))
            Return result
        End Function
 
        ''' <summary>
        ''' Produce a bound expression representing a pointer to a frame of a particular frame class.
        ''' Note that for generic frames, the frameClass parameter is the generic definition, but
        ''' the resulting expression will be constructed with the current type parameters.
        ''' </summary>
        ''' <param name="syntax">The syntax to attach to the bound nodes produced</param>
        ''' <param name="frameClass">The class type of frame to be returned</param>
        ''' <returns>A bound node that computes the pointer to the required frame</returns>
        Friend Overrides Function FramePointer(syntax As SyntaxNode, frameClass As NamedTypeSymbol) As BoundExpression
            Debug.Assert(frameClass.IsDefinition)
            If _currentFrameThis IsNot Nothing AndAlso TypeSymbol.Equals(_currentFrameThis.Type, frameClass, TypeCompareKind.ConsiderEverything) Then
                Return New BoundMeReference(syntax, frameClass)
            End If
 
            ' Otherwise we need to return the value from a frame pointer local variable...
            Dim result As Symbol = _framePointers(frameClass)
            Dim proxyField As FieldSymbol = Nothing
            If Proxies.TryGetValue(result, proxyField) Then
                ' However, frame pointer local variables themselves can be "captured".  In that case
                ' the inner frames contain pointers to the enclosing frames.  That is, nested
                ' frame pointers are organized in a linked list.
                Dim innerFrame As BoundExpression = FramePointer(syntax, proxyField.ContainingType)
                Dim proxyFieldParented = proxyField.AsMember(DirectCast(innerFrame.Type, NamedTypeSymbol))
                Return New BoundFieldAccess(syntax, innerFrame, proxyFieldParented, False, proxyFieldParented.Type)
            End If
 
            Dim localFrame = DirectCast(result, LocalSymbol)
            Return New BoundLocal(syntax, localFrame, isLValue:=False, type:=localFrame.Type)
        End Function
 
        Protected Overrides Function MaterializeProxy(origExpression As BoundExpression, proxy As FieldSymbol) As BoundNode
            Dim frame As BoundExpression = FramePointer(origExpression.Syntax, proxy.ContainingType)
            Dim constructedProxyField = proxy.AsMember(DirectCast(frame.Type, NamedTypeSymbol))
            Return New BoundFieldAccess(origExpression.Syntax,
                                        frame,
                                        constructedProxyField,
                                        origExpression.IsLValue,
                                        constructedProxyField.Type)
        End Function
 
        Private Function MakeFrameCtor(frame As LambdaFrame, diagnostics As BindingDiagnosticBag) As BoundBlock
            Dim constructor = frame.Constructor
            Dim syntaxNode As SyntaxNode = constructor.Syntax
 
            Dim builder = ArrayBuilder(Of BoundStatement).GetInstance
            builder.Add(MethodCompiler.BindDefaultConstructorInitializer(constructor, diagnostics))
 
            ' add copy logic if ctor has parameters - 
            '
            ' Sub  New(arg as Frame)
            '   if arg is nothing goto Done
            '       Me.field0 = arg.field0                
            '       Me.field1 = arg.field1                
            '       . . .
            '       Me.fieldN = arg.fieldN                
            '   Done:
            '   Return
            ' End Sub
 
            If Not constructor.Parameters.IsEmpty Then
                Dim arg = constructor.Parameters(0)
                Debug.Assert(arg.Type Is frame)
 
                Dim parameterExpr = New BoundParameter(syntaxNode, arg, frame)
 
                Dim bool = frame.ContainingAssembly.GetSpecialType(SpecialType.System_Boolean)
                Dim useSiteError = bool.GetUseSiteInfo()
                diagnostics.Add(useSiteError, syntaxNode.GetLocation())
 
                Dim obj = frame.ContainingAssembly.GetSpecialType(SpecialType.System_Object)
                ' WARN: We assume that if System_Object was not found we would never reach 
                '       this point because the error should have been/processed generated earlier
                Debug.Assert(obj.GetUseSiteInfo().DiagnosticInfo Is Nothing)
 
                Dim condition = New BoundBinaryOperator(syntaxNode, BinaryOperatorKind.Is,
                                                        New BoundDirectCast(syntaxNode, parameterExpr.MakeRValue(), ConversionKind.WideningReference, obj, Nothing),
                                                        New BoundLiteral(syntaxNode, ConstantValue.Nothing, obj),
                                                        False,
                                                        bool)
 
                Dim doneLabel = New GeneratedLabelSymbol("Done")
                Dim condGoto = New BoundConditionalGoto(syntaxNode, condition, True, doneLabel)
 
                ' If arg is Nothing Then GoTo Done
                builder.Add(condGoto)
 
                Dim thisParam = constructor.MeParameter
                Debug.Assert(thisParam.Type Is frame)
 
                Dim this = New BoundParameter(syntaxNode, thisParam, frame)
 
                For Each field In frame.CapturedLocals
                    Dim type = field.Type
                    Dim left = New BoundFieldAccess(syntaxNode, this, field, True, field.Type)
                    Dim right = New BoundFieldAccess(syntaxNode, parameterExpr, field, False, field.Type)
                    Dim fieldInit = New BoundAssignmentOperator(syntaxNode, left, right, True, type)
 
                    ' me.FieldX = arg.FieldX
                    builder.Add(New BoundExpressionStatement(syntaxNode, fieldInit))
                Next
 
                ' Done:
                builder.Add(New BoundLabelStatement(syntaxNode, doneLabel))
            End If
 
            ' Return
            builder.Add(New BoundReturnStatement(syntaxNode, Nothing, Nothing, Nothing))
 
            Return New BoundBlock(syntaxNode,
                                Nothing,
                                ImmutableArray(Of LocalSymbol).Empty,
                                builder.ToImmutableAndFree())
        End Function
 
        ''' <summary>
        ''' Constructs a concrete frame type if needed.
        ''' </summary>
        Friend Shared Function ConstructFrameType(Of T As TypeSymbol)(type As LambdaFrame, typeArguments As ImmutableArray(Of T)) As NamedTypeSymbol
            If type.CanConstruct Then
                Return type.Construct(StaticCast(Of TypeSymbol).From(typeArguments))
            Else
                Debug.Assert(typeArguments.IsEmpty)
                Return type
            End If
        End Function
 
        ''' <summary>
        ''' Introduce a frame around the translation of the given node.
        ''' </summary>
        ''' <param name="node">The node whose translation should be translated to contain a frame</param>
        ''' <param name="frame">The frame for the translated node</param>
        ''' <param name="F">A function that computes the translation of the node.  It receives lists of added statements and added symbols</param>
        ''' <returns>The translated statement, as returned from F</returns>
        Private Function IntroduceFrame(node As BoundNode,
                                        frame As LambdaFrame,
                                        F As Func(Of ArrayBuilder(Of BoundExpression), ArrayBuilder(Of LocalSymbol), BoundNode),
                                        Optional origLambda As LambdaSymbol = Nothing) As BoundNode
 
            Dim frameType As NamedTypeSymbol = ConstructFrameType(frame, _currentTypeParameters)
            Dim framePointer = New SynthesizedLocal(Me._topLevelMethod, frameType, SynthesizedLocalKind.LambdaDisplayClass, frame.ScopeSyntax)
            Dim prologue = ArrayBuilder(Of BoundExpression).GetInstance()
            Dim constructor As MethodSymbol = frame.Constructor.AsMember(frameType)
            Debug.Assert(TypeSymbol.Equals(frameType, constructor.ContainingType, TypeCompareKind.ConsiderEverything))
 
            Dim syntaxNode As SyntaxNode = node.Syntax
 
            Dim frameAccess = New BoundLocal(syntaxNode, framePointer, frameType)
 
            ' if this is a copy-ctor, it should get previous frame value
            Dim args = If(constructor.Parameters.IsEmpty,
                          ImmutableArray(Of BoundExpression).Empty,
                          ImmutableArray.Create(Of BoundExpression)(frameAccess))
 
            prologue.Add(New BoundAssignmentOperator(
                syntaxNode,
                frameAccess,
                New BoundObjectCreationExpression(syntaxNode, constructor, args, Nothing, frameType),
                True,
                frameType))
 
            Dim oldInnermostFrameProxy As FieldSymbol = Nothing
 
            If _innermostFramePointer IsNot Nothing Then
                Proxies.TryGetValue(_innermostFramePointer, oldInnermostFrameProxy)
 
                If _analysis.needsParentFrame.Contains(node) Then
                    Dim capturedFrame = LambdaCapturedVariable.Create(frame, _innermostFramePointer, _synthesizedFieldNameIdDispenser)
                    Dim frameParent = capturedFrame.AsMember(frameType)
                    Dim left As BoundExpression = New BoundFieldAccess(syntaxNode,
                                                                       New BoundLocal(syntaxNode, framePointer, frameType),
                                                                       frameParent,
                                                                       True,
                                                                       frameParent.Type)
 
                    Dim right As BoundExpression = FrameOfType(syntaxNode, TryCast(frameParent.Type, NamedTypeSymbol))
                    Dim assignment = New BoundAssignmentOperator(syntaxNode, left, right, True, left.Type)
 
                    ' if we are capturing "Me" in a ctor, we should do it after "Me" is initialized
                    If _innermostFramePointer.Kind = SymbolKind.Parameter AndAlso _topLevelMethod.MethodKind = MethodKind.Constructor AndAlso
                       _topLevelMethod Is _currentMethod AndAlso Not _meIsInitialized Then
                        Debug.Assert(_meProxyDeferredInit Is Nothing, "we should be capturing 'Me' only once")
                        _meProxyDeferredInit = assignment
                    Else
                        prologue.Add(assignment)
                    End If
 
                    CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(frame, capturedFrame.GetCciAdapter())
 
                    Proxies(_innermostFramePointer) = capturedFrame
                End If
            End If
 
            If origLambda IsNot Nothing Then
                ' init proxies for lambda parameters
                For Each p In origLambda.Parameters
                    InitVariableProxy(syntaxNode, p, framePointer, frameType, prologue)
                Next
            Else
                ' init proxies for method parameters when seeing top block
                If Not _analysis.blockParent.ContainsKey(node) Then
                    Debug.Assert(_topLevelMethod = _currentMethod)
 
                    For Each p In _topLevelMethod.Parameters
                        InitVariableProxy(syntaxNode, p, framePointer, frameType, prologue)
                    Next
                End If
            End If
 
            ' init proxies for original locals if we're preserving them
            If Me.PreserveOriginalLocals Then
                For Each variable In _analysis.capturedVariables
                    If variable.Kind <> SymbolKind.Local Then
                        Continue For
                    End If
 
                    Dim variableNode As BoundNode = Nothing
                    If ((Not _analysis.variableScope.TryGetValue(variable, variableNode)) OrElse (variableNode IsNot node)) Then
                        Continue For
                    End If
 
                    InitVariableProxy(syntaxNode, variable, framePointer, frameType, prologue)
                Next
            End If
 
            Dim oldInnermostFramePointer As Symbol = _innermostFramePointer
            _innermostFramePointer = framePointer
            Dim addedLocals = ArrayBuilder(Of LocalSymbol).GetInstance()
            addedLocals.Add(framePointer)
            _framePointers.Add(frame, framePointer)
 
            Dim result = F(prologue, addedLocals)
 
            _framePointers.Remove(frame)
            _innermostFramePointer = oldInnermostFramePointer
 
            If _innermostFramePointer IsNot Nothing Then
                If oldInnermostFrameProxy IsNot Nothing Then
                    Proxies(_innermostFramePointer) = oldInnermostFrameProxy
                Else
                    Proxies.Remove(_innermostFramePointer)
                End If
            End If
 
            Return result
        End Function
 
        ''' <summary>
        ''' If parameter (or variable in the EE) is lifted, initialize its proxy.
        ''' </summary>
        Private Sub InitVariableProxy(syntaxNode As SyntaxNode,
                                      originalSymbol As Symbol,
                                      framePointer As LocalSymbol,
                                      frameType As NamedTypeSymbol,
                                      prologue As ArrayBuilder(Of BoundExpression))
 
            Debug.Assert(originalSymbol IsNot Nothing)
 
            Dim proxy As FieldSymbol = Nothing
            If Proxies.TryGetValue(originalSymbol, proxy) AndAlso Not _analysis.declaredInsideExpressionLambda.Contains(originalSymbol) Then
                Dim value As BoundExpression
                Select Case originalSymbol.Kind
                    Case SymbolKind.Parameter
                        ' parameter needs to be accessed in the context of current method
                        Dim originalParameter = DirectCast(originalSymbol, ParameterSymbol)
                        Dim actualParameter As ParameterSymbol = Nothing
                        If Not ParameterMap.TryGetValue(originalParameter, actualParameter) Then
                            actualParameter = originalParameter
                        End If
 
                        value = New BoundParameter(syntaxNode,
                                                   actualParameter,
                                                   isLValue:=False,
                                                   type:=actualParameter.Type)
                    Case SymbolKind.Local
                        Dim originalLocal = DirectCast(originalSymbol, LocalSymbol)
                        Dim actualLocal As LocalSymbol = Nothing
                        If Not LocalMap.TryGetValue(originalLocal, actualLocal) Then
                            actualLocal = originalLocal
                        End If
                        value = New BoundLocal(syntaxNode, actualLocal, isLValue:=False, type:=actualLocal.Type)
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(originalSymbol.Kind)
                End Select
 
                Dim constructedProxy = proxy.AsMember(frameType)
                Dim assignProxy As BoundExpression = New BoundAssignmentOperator(
                                                         syntaxNode,
                                                         New BoundFieldAccess(
                                                             syntaxNode,
                                                             New BoundLocal(syntaxNode,
                                                                            framePointer,
                                                                            frameType),
                                                             constructedProxy,
                                                             isLValue:=True,
                                                             type:=constructedProxy.Type),
                                                         value,
                                                         suppressObjectClone:=True,
                                                         type:=constructedProxy.Type)
                prologue.Add(assignProxy)
            End If
        End Sub
 
        Public Overrides Function VisitMeReference(node As BoundMeReference) As BoundNode
            If _currentMethod = _topLevelMethod Then
                Return node
            End If
 
            ' this can happen only in a case of errors
            ' we will not find a frame for "Me" in a shared method
            If _topLevelMethod.IsShared Then
                Return node
            End If
 
            Return FramePointer(node.Syntax, TryCast(node.Type, NamedTypeSymbol))
        End Function
 
        Public Overrides Function VisitMyClassReference(node As BoundMyClassReference) As BoundNode
            Return If(_currentMethod Is _topLevelMethod, node,
                      If(_currentMethod.ContainingType Is _topLevelMethod.ContainingType,
                         New BoundMyClassReference(node.Syntax, node.Type),
                         FramePointer(node.Syntax, TryCast(_topLevelMethod.ContainingType, NamedTypeSymbol))))
        End Function
 
        Public Overrides Function VisitMyBaseReference(node As BoundMyBaseReference) As BoundNode
            Return If(_currentMethod Is _topLevelMethod, node,
                      If(_currentMethod.ContainingType Is _topLevelMethod.ContainingType,
                         New BoundMyBaseReference(node.Syntax, node.Type),
                         FramePointer(node.Syntax, TryCast(_topLevelMethod.ContainingType, NamedTypeSymbol))))
        End Function
 
        Public Overrides Function VisitRangeVariable(node As BoundRangeVariable) As BoundNode
            Throw ExceptionUtilities.Unreachable
        End Function
 
        Private Function RewriteStatementList(node As BoundStatementList,
                               prologue As ArrayBuilder(Of BoundExpression),
                               newLocals As ArrayBuilder(Of LocalSymbol)) As BoundStatement
 
            Dim newStatements = ArrayBuilder(Of BoundStatement).GetInstance
 
            For Each expr In prologue
                newStatements.Add(New BoundExpressionStatement(expr.Syntax, expr))
            Next
 
            ' done with this
            prologue.Free()
 
            For Each s In node.Statements
                Dim replacement = DirectCast(Me.Visit(s), BoundStatement)
                If replacement IsNot Nothing Then
                    newStatements.Add(replacement)
                End If
            Next
 
            If newLocals.Count = 0 Then
                newLocals.Free()
                Return node.Update(newStatements.ToImmutableAndFree())
            Else
                Return New BoundBlock(node.Syntax,
                                      Nothing,
                                      newLocals.ToImmutableAndFree(),
                                      newStatements.ToImmutableAndFree())
            End If
        End Function
 
        Public Overrides Function VisitBlock(node As BoundBlock) As BoundNode
            Dim frame As LambdaFrame = Nothing
 
            ' Test if this frame has captured variables and requires the introduction of a closure class.
            If _frames.TryGetValue(node, frame) Then
                Return IntroduceFrame(node, frame,
                                      Function(prologue As ArrayBuilder(Of BoundExpression), newLocals As ArrayBuilder(Of LocalSymbol))
                                          Return RewriteBlock(node, prologue, newLocals)
                                      End Function)
            Else
                Return RewriteBlock(node)
            End If
        End Function
 
        Public Overrides Function VisitSequence(node As BoundSequence) As BoundNode
            Dim frame As LambdaFrame = Nothing
 
            ' Test if this frame has captured variables and requires the introduction of a closure class.
            If _frames.TryGetValue(node, frame) Then
                Return IntroduceFrame(node, frame,
                                      Function(prologue As ArrayBuilder(Of BoundExpression), newLocals As ArrayBuilder(Of LocalSymbol))
                                          Return RewriteSequence(node, prologue, newLocals)
                                      End Function)
            Else
                Return RewriteSequence(node)
            End If
        End Function
 
        Public Overrides Function VisitCatchBlock(node As BoundCatchBlock) As BoundNode
            Dim frame As LambdaFrame = Nothing
 
            ' Test if this frame has captured variables and requires the introduction of a closure class.
            If _frames.TryGetValue(node, frame) Then
                Return IntroduceFrame(node, frame,
                                      Function(prologue As ArrayBuilder(Of BoundExpression), newLocals As ArrayBuilder(Of LocalSymbol))
                                          Return RewriteCatch(node, prologue, newLocals)
                                      End Function)
            Else
                Return RewriteCatch(node, ArrayBuilder(Of BoundExpression).GetInstance, ArrayBuilder(Of LocalSymbol).GetInstance)
            End If
        End Function
 
        Private Function RewriteCatch(node As BoundCatchBlock,
                                      prologue As ArrayBuilder(Of BoundExpression),
                                      newLocals As ArrayBuilder(Of LocalSymbol)) As BoundCatchBlock
 
            ' Catch node contains 3 important pieces
            ' 1) LocalOpt - like BoundBlock, catch may own variables, but it happens so that it never needs more than one.
            ' 2) ExceptionVariable - presence of this variable indicates that caught exception needs to be stored. 
            '                        in such case ExceptionVariable is used as a target of a one-time assignment
            '                        when Catch is entered.
            ' 3) Code (Filter and Body)
            '
            ' It is important to note that all these 3 parts do not have any dependencies on each other 
            ' except that assignment must happen before any other Catch code is executed.
            '
            ' When LocalOpt is present, ExceptionVariable typically holds a reference to it, but it is not a requirement.
            ' There is nothing wrong with LocalOpt holding a reference to a closure frame and 
            ' ExceptionVariable pointing to a lifted variable.
            ' And we will do exactly that in a case if LocalOpt gets lifted.
 
            Dim rewrittenCatchLocal As LocalSymbol = Nothing
 
            If newLocals.Count <> 0 Then
                Debug.Assert(newLocals.Count = 1, "must be only one local that is the frame reference")
                Debug.Assert(Me.Proxies.ContainsKey(node.LocalOpt), "original local should be proxied")
 
                ' getting new locals means that our original local was lifted into a closure
                ' and instead of an actual local Catch will own frame reference.
                rewrittenCatchLocal = newLocals(0)
 
            ElseIf node.LocalOpt IsNot Nothing Then
                ' local was not lifted, but its type may need to be rewritten
                ' this happens when it has a generic type which needs to be rewritten
                ' when lambda body was moved to a separate method.
                Dim origLocal = node.LocalOpt
                Debug.Assert(Not Me.Proxies.ContainsKey(origLocal), "captured local should not need rewriting")
 
                Dim newType = VisitType(origLocal.Type)
                If TypeSymbol.Equals(newType, origLocal.Type, TypeCompareKind.ConsiderEverything) Then
                    ' keeping same local
                    rewrittenCatchLocal = origLocal
 
                Else
                    ' need a local of a different type
                    rewrittenCatchLocal = CreateReplacementLocalOrReturnSelf(origLocal, newType)
                    LocalMap.Add(origLocal, rewrittenCatchLocal)
                End If
            End If
 
            Dim rewrittenExceptionSource = DirectCast(Me.Visit(node.ExceptionSourceOpt), BoundExpression)
 
            ' If exception variable got lifted, IntroduceFrame will give us frame init prologue.
            ' It needs to run before the exception variable is accessed.
            ' To ensure that, we will make exception variable a sequence that performs prologue as its sideeffects.
            If prologue.Count <> 0 Then
                rewrittenExceptionSource = New BoundSequence(
                    rewrittenExceptionSource.Syntax,
                    ImmutableArray(Of LocalSymbol).Empty,
                    prologue.ToImmutable,
                    rewrittenExceptionSource,
                    rewrittenExceptionSource.Type)
            End If
 
            ' done with these.
            newLocals.Free()
            prologue.Free()
 
            ' rewrite filter and body
            ' NOTE: this will proxy all accesses to exception local if that got lifted.
            Dim rewrittenErrorLineNumberOpt = DirectCast(Me.Visit(node.ErrorLineNumberOpt), BoundExpression)
            Dim rewrittenFilter = DirectCast(Me.Visit(node.ExceptionFilterOpt), BoundExpression)
            Dim rewrittenBody = DirectCast(Me.Visit(node.Body), BoundBlock)
 
            ' rebuild the node.
            Return node.Update(rewrittenCatchLocal,
                               rewrittenExceptionSource,
                               rewrittenErrorLineNumberOpt,
                               rewrittenFilter,
                               rewrittenBody,
                               node.IsSynthesizedAsyncCatchAll)
        End Function
 
        Public Overrides Function VisitStatementList(node As BoundStatementList) As BoundNode
            Dim frame As LambdaFrame = Nothing
 
            ' Test if this frame has captured variables and requires the introduction of a closure class.
            ' That can occur for a BoundStatementList if it is the body of a method with captured parameters.
            If _frames.TryGetValue(node, frame) Then
                Return IntroduceFrame(node, frame,
                                      Function(prologue As ArrayBuilder(Of BoundExpression), newLocals As ArrayBuilder(Of LocalSymbol))
                                          Return RewriteStatementList(node, prologue, newLocals)
                                      End Function)
            Else
                Return MyBase.VisitStatementList(node)
            End If
 
        End Function
 
        ''' <summary>
        ''' Rewrites lambda body into a body of a method.
        ''' </summary>
        ''' <param name="method">Method symbol for the rewritten lambda body.</param>
        ''' <param name="lambda">Original lambda node.</param>
        ''' <returns>Lambda body rewritten as a body of the given method symbol.</returns>
        Public Function RewriteLambdaAsMethod(method As SynthesizedLambdaMethod,
                                              lambda As BoundLambda,
                                              stateMachineStateDebugInfosBuilder As ArrayBuilder(Of StateMachineStateDebugInfo),
                                              <Out> ByRef stateMachineType As StateMachineTypeSymbol) As BoundBlock
 
            ' report use site errors for attributes that are needed later on in the rewriter
            Dim lambdaSyntax = lambda.Syntax
 
            Dim node As BoundBlock = lambda.Body
            Dim frame As LambdaFrame = Nothing
            Dim loweredBody As BoundBlock = Nothing
 
            If _frames.TryGetValue(node, frame) Then
                loweredBody = DirectCast(
                                    IntroduceFrame(node, frame,
                                      Function(prologue As ArrayBuilder(Of BoundExpression), newLocals As ArrayBuilder(Of LocalSymbol))
                                          Return RewriteBlock(node, prologue, newLocals)
                                      End Function,
                                      lambda.LambdaSymbol),
                                    BoundBlock)
            Else
                loweredBody = RewriteBlock(node)
            End If
 
            ' In case of async/iterator lambdas, the method has already been uniquely named, so there is no need to
            ' produce a unique method ordinal for the corresponding state machine type, whose name includes the (unique) method name.
            Const methodOrdinal As Integer = -1
 
            Dim slotAllocatorOpt = CompilationState.ModuleBuilderOpt.TryCreateVariableSlotAllocator(method, method.TopLevelMethod, Diagnostics.DiagnosticBag)
            Return Rewriter.RewriteIteratorAndAsync(loweredBody, method, methodOrdinal, CompilationState, Diagnostics, stateMachineStateDebugInfosBuilder, slotAllocatorOpt, stateMachineType)
        End Function
 
        Public Overrides Function VisitTryCast(node As BoundTryCast) As BoundNode
            Debug.Assert(node.RelaxationLambdaOpt Is Nothing)
 
            Dim lambda As BoundLambda = TryCast(node.Operand, BoundLambda)
            If lambda Is Nothing Then
                Return MyBase.VisitTryCast(node)
            End If
 
            Dim result As BoundExpression = RewriteLambda(lambda, VisitType(node.Type), (node.ConversionKind And ConversionKind.ConvertedToExpressionTree) <> 0)
            If _inExpressionLambda Then
                result = node.Update(result, node.ConversionKind, node.ConstantValueOpt, node.RelaxationLambdaOpt, node.Type)
            End If
            Return result
        End Function
 
        Public Overrides Function VisitDirectCast(node As BoundDirectCast) As BoundNode
            Debug.Assert(node.RelaxationLambdaOpt Is Nothing)
 
            Dim lambda As BoundLambda = TryCast(node.Operand, BoundLambda)
            If lambda Is Nothing Then
                Return MyBase.VisitDirectCast(node)
            End If
 
            Dim result As BoundExpression = RewriteLambda(lambda, VisitType(node.Type), (node.ConversionKind And ConversionKind.ConvertedToExpressionTree) <> 0)
            If _inExpressionLambda Then
                result = node.Update(result, node.ConversionKind, node.SuppressVirtualCalls, node.ConstantValueOpt, node.RelaxationLambdaOpt, node.Type)
            End If
            Return result
        End Function
 
        Public Overrides Function VisitConversion(conversion As BoundConversion) As BoundNode
            Debug.Assert(conversion.ExtendedInfoOpt Is Nothing)
 
            Dim lambda As BoundLambda = TryCast(conversion.Operand, BoundLambda)
            If lambda Is Nothing Then
                Return MyBase.VisitConversion(conversion)
            End If
 
            Dim result As BoundExpression = RewriteLambda(lambda, VisitType(conversion.Type), (conversion.ConversionKind And ConversionKind.ConvertedToExpressionTree) <> 0)
            If _inExpressionLambda Then
                result = conversion.Update(result,
                                           conversion.ConversionKind,
                                           conversion.Checked,
                                           conversion.ExplicitCastInCode,
                                           conversion.ConstantValueOpt,
                                           conversion.ExtendedInfoOpt,
                                           conversion.Type)
            End If
            Return result
        End Function
 
        Private Function GetTopLevelMethodId() As DebugId
            Return If(SlotAllocatorOpt?.MethodId, New DebugId(_topLevelMethodOrdinal, CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal))
        End Function
 
        Private Function GetClosureId(scope As BoundNode, syntax As SyntaxNode, closureDebugInfo As ArrayBuilder(Of EncClosureInfo), <Out> ByRef rudeEdit As RuntimeRudeEdit?) As DebugId
            Debug.Assert(syntax IsNot Nothing)
 
            Dim parentScope As BoundNode = Nothing
            Dim parentFrame As LambdaFrame = Nothing
            Dim parentClosureId As DebugId? = Nothing
 
            If _analysis.needsParentFrame.Contains(scope) AndAlso
               _analysis.blockParent.TryGetValue(scope, parentScope) AndAlso
               _frames.TryGetValue(parentScope, parentFrame) Then
 
                rudeEdit = parentFrame.RudeEdit
                parentClosureId = parentFrame.ClosureId
            End If
 
            Dim closureId As DebugId
            Dim previousClosureId As DebugId
 
            If rudeEdit Is Nothing AndAlso
               SlotAllocatorOpt IsNot Nothing AndAlso
               SlotAllocatorOpt.TryGetPreviousClosure(syntax, parentClosureId, structCaptures:=Nothing, previousClosureId, rudeEdit) AndAlso
               rudeEdit Is Nothing Then
                closureId = previousClosureId
            Else
                closureId = New DebugId(closureDebugInfo.Count, CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal)
            End If
 
            Dim syntaxOffset As Integer = _topLevelMethod.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree)
            closureDebugInfo.Add(New EncClosureInfo(New ClosureDebugInfo(syntaxOffset, closureId), parentClosureId, structCaptures:=Nothing))
            Return closureId
        End Function
 
        Private Function GetLambdaId(syntax As SyntaxNode, closureKind As ClosureKind, closureOrdinal As Integer, closureRudeEdit As RuntimeRudeEdit?) As DebugId
            Debug.Assert(syntax IsNot Nothing)
 
            Dim lambdaOrLambdaBodySyntax As SyntaxNode
            Dim isLambdaBody As Boolean ' indicates whether a node needs mapping for lambda or its bodies
 
            Dim lambdaExpression = TryCast(syntax, LambdaExpressionSyntax)
            If lambdaExpression IsNot Nothing Then
                lambdaOrLambdaBodySyntax = LambdaUtilities.GetLambdaExpressionLambdaBody(lambdaExpression)
                isLambdaBody = True
            ElseIf syntax.IsKind(SyntaxKind.AddressOfExpression) Then
                ' Late-bound AddressOf operator creates a display class, unlike delegate relaxations.
                ' EnC is not supported in this case.
                lambdaOrLambdaBodySyntax = syntax
                isLambdaBody = False
            ElseIf LambdaUtilities.IsNonUserCodeQueryLambda(syntax) Then
                lambdaOrLambdaBodySyntax = syntax
                isLambdaBody = False
            Else
                ' query lambdas
                lambdaOrLambdaBodySyntax = syntax
                isLambdaBody = True
            End If
 
            Debug.Assert(Not isLambdaBody OrElse LambdaUtilities.IsLambdaBody(lambdaOrLambdaBodySyntax))
 
            ' determine lambda ordinal and calculate syntax offset
 
            Dim lambdaId As DebugId
            Dim previousLambdaId As DebugId = Nothing
            Dim lambdaRudeEdit As RuntimeRudeEdit? = Nothing
            If closureRudeEdit Is Nothing AndAlso
               SlotAllocatorOpt?.TryGetPreviousLambda(lambdaOrLambdaBodySyntax, isLambdaBody, closureOrdinal, structClosureIds:=ImmutableArray(Of DebugId).Empty, previousLambdaId, lambdaRudeEdit) = True AndAlso
               lambdaRudeEdit Is Nothing Then
                lambdaId = previousLambdaId
            Else
                lambdaId = New DebugId(_lambdaDebugInfoBuilder.Count, CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal)
 
                Dim rudeEdit = If(closureRudeEdit, lambdaRudeEdit)
                If rudeEdit IsNot Nothing Then
                    _lambdaRuntimeRudeEditsBuilder.Add(New LambdaRuntimeRudeEditInfo(previousLambdaId, rudeEdit.Value))
                End If
            End If
 
            Dim syntaxOffset As Integer = _topLevelMethod.CalculateLocalSyntaxOffset(lambdaOrLambdaBodySyntax.SpanStart, lambdaOrLambdaBodySyntax.SyntaxTree)
            _lambdaDebugInfoBuilder.Add(New EncLambdaInfo(New LambdaDebugInfo(syntaxOffset, lambdaId, closureOrdinal), structClosureIds:=ImmutableArray(Of DebugId).Empty))
            Return lambdaId
        End Function
 
        Private Function RewriteLambda(node As BoundLambda, type As TypeSymbol, convertToExpressionTree As Boolean) As BoundExpression
            If convertToExpressionTree Or _inExpressionLambda Then
                ' This lambda is being converted to an expression tree.
                Dim wasInExpressionLambda = _inExpressionLambda
                _inExpressionLambda = True
 
                Dim newBody = DirectCast(Visit(node.Body), BoundBlock)
                node = node.Update(node.LambdaSymbol, newBody, node.Diagnostics, node.LambdaBinderOpt, node.DelegateRelaxation, node.MethodConversionKind)
 
                Dim rewrittenNode As BoundExpression = node
                If Not wasInExpressionLambda Then
                    ' Rewritten outermost lambda as expression tree
                    Dim delegateType = type.ExpressionTargetDelegate(CompilationState.Compilation)
                    rewrittenNode = ExpressionLambdaRewriter.RewriteLambda(node, Me._currentMethod, delegateType, Me.CompilationState, Me.TypeMap, Me.Diagnostics, Me._rewrittenNodes, Me.RecursionDepth)
                End If
 
                _inExpressionLambda = wasInExpressionLambda
                Return rewrittenNode
            End If
 
            Dim translatedLambdaContainer As InstanceTypeSymbol
            Dim containerFrame As LambdaFrame
            Dim lambdaScope As BoundNode = Nothing
            Dim closureOrdinal As Integer
            Dim closureKind As ClosureKind
 
            If _analysis.lambdaScopes.TryGetValue(node.LambdaSymbol, lambdaScope) Then
                containerFrame = _frames(lambdaScope)
                translatedLambdaContainer = containerFrame
                closureKind = ClosureKind.General
                closureOrdinal = _frames(lambdaScope).ClosureId.Ordinal
            ElseIf _analysis.capturedVariablesByLambda(node.LambdaSymbol).Count = 0 Then
                containerFrame = GetStaticFrame(node, Diagnostics)
                translatedLambdaContainer = containerFrame
                closureKind = ClosureKind.Static
                closureOrdinal = LambdaDebugInfo.StaticClosureOrdinal
            Else
                containerFrame = Nothing
                translatedLambdaContainer = DirectCast(_topLevelMethod.ContainingType, InstanceTypeSymbol)
                closureKind = ClosureKind.ThisOnly
                closureOrdinal = LambdaDebugInfo.ThisOnlyClosureOrdinal
            End If
 
            Dim lambdaId, topLevelMethodId As DebugId
            If node.LambdaSymbol.SynthesizedKind = SynthesizedLambdaKind.DelegateRelaxationStub Then
                _delegateRelaxationIdDispenser += 1
                Dim generation = CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal
                lambdaId = New DebugId(_delegateRelaxationIdDispenser, generation)
                topLevelMethodId = New DebugId(_topLevelMethodOrdinal, generation)
            Else
                lambdaId = GetLambdaId(node.Syntax, closureKind, closureOrdinal, containerFrame?.RudeEdit)
                topLevelMethodId = GetTopLevelMethodId()
            End If
 
            ' Move the body of the lambda to a freshly generated synthetic method on its container.
            Dim synthesizedMethod = New SynthesizedLambdaMethod(translatedLambdaContainer, closureKind, _topLevelMethod, topLevelMethodId, node, lambdaId, Me.Diagnostics)
 
            CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(translatedLambdaContainer, synthesizedMethod.GetCciAdapter())
 
            For Each parameter In node.LambdaSymbol.Parameters
                ParameterMap.Add(parameter, synthesizedMethod.Parameters(parameter.Ordinal))
            Next
 
            Dim oldMethod = _currentMethod
            Dim oldFrameThis = _currentFrameThis
            Dim oldTypeParameters = _currentTypeParameters
            Dim oldInnermostFramePointer = _innermostFramePointer
            Dim oldTypeSubstitution = _currentLambdaBodyTypeSubstitution
 
            Dim containerAsFrame = TryCast(translatedLambdaContainer, LambdaFrame)
 
            Me._currentMethod = synthesizedMethod
 
            If closureKind = ClosureKind.Static Then
                ' no link from a static lambda to its container
                _innermostFramePointer = Nothing
                _currentFrameThis = Nothing
            Else
                _currentFrameThis = synthesizedMethod.MeParameter
                _innermostFramePointer = Nothing
                _framePointers.TryGetValue(translatedLambdaContainer, _innermostFramePointer)
            End If
 
            If containerAsFrame IsNot Nothing Then
                _currentTypeParameters = translatedLambdaContainer.TypeParameters
                _currentLambdaBodyTypeSubstitution = containerAsFrame.TypeMap
            Else
                _currentTypeParameters = synthesizedMethod.TypeParameters
                _currentLambdaBodyTypeSubstitution = TypeSubstitution.Create(_topLevelMethod, _topLevelMethod.TypeParameters, _currentMethod.TypeArguments)
            End If
 
            Dim stateMachineStateDebugInfosBuilder = ArrayBuilder(Of StateMachineStateDebugInfo).GetInstance()
            Dim stateMachineType As StateMachineTypeSymbol = Nothing
            Dim body = DirectCast(RewriteLambdaAsMethod(synthesizedMethod, node, stateMachineStateDebugInfosBuilder, stateMachineType), BoundStatement)
            CompilationState.AddSynthesizedMethod(synthesizedMethod, body, stateMachineType, stateMachineStateDebugInfosBuilder.ToImmutableAndFree())
 
            ' return to old method
 
            Me._currentMethod = oldMethod
            _currentFrameThis = oldFrameThis
            _currentTypeParameters = oldTypeParameters
            _innermostFramePointer = oldInnermostFramePointer
            _currentLambdaBodyTypeSubstitution = oldTypeSubstitution
 
            ' Rewrite the lambda expression as a delegate creation expression
            Dim constructedFrame As NamedTypeSymbol = translatedLambdaContainer
 
            ' If container is a frame, create a concrete type
            If containerAsFrame IsNot Nothing Then
                constructedFrame = ConstructFrameType(containerAsFrame, _currentTypeParameters)
            End If
 
            ' for instance lambdas, receiver is the frame
            ' for static lambdas, get the singleton receiver 
            Dim receiver As BoundExpression
            If closureKind <> ClosureKind.Static Then
                receiver = FrameOfType(node.Syntax, constructedFrame)
            Else
                Dim field = containerAsFrame.SingletonCache.AsMember(constructedFrame)
                receiver = New BoundFieldAccess(node.Syntax, Nothing, field, isLValue:=False, type:=field.Type)
            End If
 
            Dim referencedMethod As MethodSymbol = synthesizedMethod.AsMember(constructedFrame)
 
            If referencedMethod.IsGenericMethod Then
                referencedMethod = referencedMethod.Construct(StaticCast(Of TypeSymbol).From(_currentTypeParameters))
            End If
 
            ' static lambdas are emitted as instance methods on a singleton receiver
            ' delegates invoke dispatch is optimized for instance delegates so 
            ' it is preferable to emit lambdas as instance methods even when lambdas 
            ' do Not capture anything
            Dim result As BoundExpression = New BoundDelegateCreationExpression(
                         node.Syntax,
                         receiver,
                         referencedMethod,
                         relaxationLambdaOpt:=Nothing,
                         relaxationReceiverPlaceholderOpt:=Nothing,
                         methodGroupOpt:=Nothing,
                         type:=type)
 
            ' If the block containing the lambda is not the innermost block,
            ' or the lambda is static, then the lambda object should be cached in its frame.
            ' NOTE: we are not caching static lambdas in static ctors - cannot reuse such cache
            ' NOTE: we require lambdaScope IsNot Nothing. We do not want to introduce a field into a user's class (not a synthetic frame)
            Dim shouldCacheStaticlambda As Boolean =
                (closureKind = ClosureKind.Static AndAlso CurrentMethod.MethodKind <> MethodKind.SharedConstructor AndAlso Not referencedMethod.IsGenericMethod)
 
            Dim shouldCacheInLoop As Boolean =
                (lambdaScope IsNot Nothing AndAlso lambdaScope IsNot _analysis.blockParent(node.Body)) AndAlso
                InLoopOrLambda(node.Syntax, lambdaScope.Syntax)
 
            If shouldCacheStaticlambda OrElse shouldCacheInLoop Then
                ' replace the expression "new Delegate(frame.M)" with "(frame.cache == null) ? (frame.cache = new Delegate(frame.M)) : frame.cache"
 
                Dim cachedFieldType As TypeSymbol = If(containerAsFrame Is Nothing,
                                                       type,
                                                       type.InternalSubstituteTypeParameters(containerAsFrame.TypeMap).Type)
 
                ' If we are generating the field into a display class created exclusively for the lambda the lambdaOrdinal itself Is unique already, 
                ' no need to include the top-level method ordinal in the field name.
                Dim cacheFieldName As String = GeneratedNames.MakeLambdaCacheFieldName(
                    If(closureKind = ClosureKind.General, -1, topLevelMethodId.Ordinal),
                    topLevelMethodId.Generation,
                    lambdaId.Ordinal,
                    lambdaId.Generation,
                    node.LambdaSymbol.SynthesizedKind)
 
                Dim cacheField As FieldSymbol = New SynthesizedLambdaCacheFieldSymbol(translatedLambdaContainer,
                                                                           implicitlyDefinedBy:=node.LambdaSymbol,
                                                                           type:=cachedFieldType,
                                                                           name:=cacheFieldName,
                                                                           topLevelMethod:=Me._topLevelMethod,
                                                                           accessibility:=Accessibility.Public,
                                                                           isShared:=(closureKind = ClosureKind.Static))
 
                CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(translatedLambdaContainer, cacheField.GetCciAdapter())
 
                Dim F = New SyntheticBoundNodeFactory(Me._topLevelMethod, Me._currentMethod, node.Syntax, CompilationState, Diagnostics)
                Dim fieldToAccess As FieldSymbol = cacheField.AsMember(constructedFrame)
                Dim cacheVariableLeft = F.Field(receiver, fieldToAccess, isLValue:=True)
                Dim cacheVariableRight = F.Field(receiver, fieldToAccess, isLValue:=False)
                result = F.Conditional(
                    F.ObjectReferenceEqual(cacheVariableRight, F.Null(cacheVariableRight.Type)),
                    F.AssignmentExpression(cacheVariableLeft, result),
                    cacheVariableRight,
                    cacheVariableRight.Type)
            End If
 
            Return result
        End Function
 
        ' This helper checks syntactically whether there is a loop or lambda expression
        ' between given lambda syntax and the syntax that corresponds to its closure.
        ' we use this heuristic as a hint that the lambda delegate may be created 
        ' multiple times with same closure.
        ' In such cases it makes sense to cache the delegate.
        '
        ' Examples:
        '            dim x = 123
        '            for i as integer = 1 to 10 
        '                if (i < 2)
        '                    arr[i].Execute(Function(arg) arg + x)  // delegate should be cached
        '                end if
        '            Next
 
        '            for i as integer = 1 to 10 
        '                Dim val = i
        '                if (i< 2)
        '                    Dim y = i + i
        '                    System.Console.WriteLine(y)
        '                    arr[i].Execute(Function(arg) arg + val);  // delegate should Not be cached (closure created inside the loop)
        '                end if
        '            Next
        '
        Private Shared Function InLoopOrLambda(lambdaSyntax As SyntaxNode, scopeSyntax As SyntaxNode) As Boolean
            Dim curSyntax = lambdaSyntax.Parent
            While (curSyntax IsNot Nothing AndAlso curSyntax IsNot scopeSyntax)
                Select Case curSyntax.Kind
                    Case SyntaxKind.ForBlock,
                         SyntaxKind.ForEachBlock,
                         SyntaxKind.WhileBlock,
                         SyntaxKind.SimpleDoLoopBlock,
                         SyntaxKind.DoWhileLoopBlock,
                         SyntaxKind.DoUntilLoopBlock,
                         SyntaxKind.DoLoopWhileBlock,
                         SyntaxKind.DoLoopUntilBlock,
                         SyntaxKind.MultiLineFunctionLambdaExpression,
                         SyntaxKind.MultiLineSubLambdaExpression,
                         SyntaxKind.SingleLineFunctionLambdaExpression,
                         SyntaxKind.SingleLineSubLambdaExpression
                        Return True
                End Select
 
                curSyntax = curSyntax.Parent
            End While
 
            Return False
        End Function
 
        Public Overrides Function VisitLambda(node As BoundLambda) As BoundNode
            Throw ExceptionUtilities.Unreachable
        End Function
 
        Private Function LowestCommonAncestor(gotoBlock As BoundNode, labelBlock As BoundNode) As BoundNode
            Dim gotoPath As New HashSet(Of BoundNode)
 
            gotoPath.Add(gotoBlock)
            While _analysis.blockParent.TryGetValue(gotoBlock, gotoBlock)
                gotoPath.Add(gotoBlock)
            End While
 
            Dim lca = labelBlock
 
            ' in a worst case we will find the top scope
            While Not gotoPath.Contains(lca)
                lca = _analysis.blockParent(lca)
            End While
 
            Return lca
        End Function
 
        ''' <summary>
        ''' It is illegal to jump into blocks that reference lifted variable
        ''' as that could leave closure frames of the target block uninitialized.
        ''' 
        ''' The fact that closure could be created as high as the declaration level of the variable
        ''' and well above goto block (thus making the jump safe) is considered an optional optimization 
        ''' and ignored. 
        ''' For the purpose of this analysis just having lifting lambdas already means 
        ''' that block may require initialization and cannot be jumped into.
        ''' 
        ''' Note that when you are jumping into a block you are essentially jumping into ALL blocks
        ''' on the path from LowestCommonAncestor(goto, label) to the actual label block.
        ''' </summary>
        Private Function IsLegalBranch(gotoBlock As BoundNode, labelBlock As BoundNode) As Boolean
            ' if goto block or any of its parents are same as label block we can jump.
            ' jumps OUT or WITHIN goto block are ok
 
            Dim curBlock = gotoBlock
            Do
                If labelBlock Is curBlock Then
                    Return True
                End If
            Loop While curBlock IsNot Nothing AndAlso _analysis.blockParent.TryGetValue(curBlock, curBlock)
 
            ' so, we are not jumping OUT or WITHIN a block.
            ' it may still be a valid jump if there are not lifting lambdas between 
            ' LeastCommonAncestor(goto, label) and label
            Dim commonAncestor = LowestCommonAncestor(gotoBlock, labelBlock)
            curBlock = labelBlock
            Do
                If curBlock Is commonAncestor Then
                    ' reached common ancestor and found no blocks that might initialize closures 
                    Return True
                End If
 
                If _analysis.containsLiftingLambda.Contains(curBlock) Then
                    ' this block contains a lambda that lifts. 
                    ' we cannot guarantee closure initialization so this is not a valid jump
                    Return False
                End If
 
                _analysis.blockParent.TryGetValue(curBlock, curBlock)
            Loop
        End Function
 
        Public Overrides Function VisitGotoStatement(node As BoundGotoStatement) As BoundNode
            Dim label = node.Label
            Dim labelBlock As BoundNode = Nothing
 
            ' label may not exist or not recorded if there were errors
            If label IsNot Nothing AndAlso _analysis.labelBlock.TryGetValue(node.Label, labelBlock) Then
 
                Dim gotoBlock As BoundNode = Nothing
                If _analysis.gotoBlock.TryGetValue(node, gotoBlock) Then
                    Dim isLegal = IsLegalBranch(gotoBlock, labelBlock)
 
                    If Not isLegal Then
                        Dim resumeLabel = TryCast(label, GeneratedUnstructuredExceptionHandlingResumeLabel)
 
                        If resumeLabel IsNot Nothing Then
                            If Not _reported_ERR_CannotUseOnErrorGotoWithClosure Then
                                _reported_ERR_CannotUseOnErrorGotoWithClosure = True
                                Me.Diagnostics.Add(ERRID.ERR_CannotUseOnErrorGotoWithClosure, resumeLabel.ResumeStatement.GetLocation(),
                                                   resumeLabel.ResumeStatement.ToString())
                            End If
                        Else
                            Select Case node.Syntax.Kind
                                Case SyntaxKind.ResumeLabelStatement, SyntaxKind.OnErrorGoToLabelStatement
                                    Me.Diagnostics.Add(ERRID.ERR_CannotGotoNonScopeBlocksWithClosure, node.Syntax.GetLocation(),
                                                       node.Syntax.ToString(), String.Empty, label.Name)
                                Case Else
                                    Me.Diagnostics.Add(ERRID.ERR_CannotGotoNonScopeBlocksWithClosure, node.Syntax.GetLocation(), "Goto ", label.Name, label.Name)
                            End Select
                        End If
 
                        node = New BoundGotoStatement(node.Syntax, node.Label, node.LabelExpressionOpt, True)
                    End If
                End If
            End If
 
            Return MyBase.VisitGotoStatement(node)
        End Function
 
        Public Overrides Function VisitCall(node As BoundCall) As BoundNode
            Dim rewritten As BoundNode = MyBase.VisitCall(node)
 
            If rewritten.Kind = BoundKind.Call Then
                Dim rewrittenCall As BoundCall = DirectCast(rewritten, BoundCall)
                rewrittenCall = OptimizeMethodCallForDelegateInvoke(rewrittenCall)
 
                ' Check if we need to init Me proxy and this is a ctor call
                If _currentMethod Is _topLevelMethod Then
                    Dim receiver As BoundExpression = node.ReceiverOpt
                    ' are we calling a ctor on Me or MyBase?
                    If node.Method.MethodKind = MethodKind.Constructor AndAlso receiver IsNot Nothing AndAlso receiver.IsInstanceReference Then
                        Debug.Assert(Not _meProxyDeferredInitDone)
                        _meIsInitialized = True
 
                        If _meProxyDeferredInit IsNot Nothing Then
                            _meProxyDeferredInitDone = True
                            Return LocalRewriter.GenerateSequenceValueSideEffects(Me._currentMethod,
                                                                              rewrittenCall,
                                                                              ImmutableArray(Of LocalSymbol).Empty,
                                                                              ImmutableArray.Create(Of BoundExpression)(_meProxyDeferredInit))
                        End If
                    End If
                End If
 
                Return rewrittenCall
            End If
 
            Return rewritten
        End Function
 
        Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
            Dim result = DirectCast(MyBase.VisitLoweredConditionalAccess(node), BoundLoweredConditionalAccess)
 
            If Not result.CaptureReceiver AndAlso Not node.ReceiverOrCondition.Type.IsBooleanType() AndAlso
               node.ReceiverOrCondition.Kind <> result.ReceiverOrCondition.Kind Then
                ' It looks like the receiver got lifted into a closure, we cannot assume that it will not change between null check and the following access.
                Return result.Update(result.ReceiverOrCondition, True, result.PlaceholderId, result.WhenNotNull, result.WhenNullOpt, result.Type)
            Else
                Return result
            End If
        End Function
 
        ''' <summary>
        ''' Optimize the case where we create an instance of a delegate and invoke it right away.
        ''' Skip the delegate creation and invoke the method directly. Specifically, we are targeting 
        ''' lambda relaxation scenario that requires a stub, which invokes original lambda by instantiating
        ''' an Anonymous Delegate and calling its Invoke method. That is why this optimization should be done
        ''' after lambdas are rewritten.
        ''' CONSIDER: Should we expand this optimization to all delegate types and all explicitly written code?
        '''           If we decide to do this, we should be careful with extension methods because they have
        '''           special treatment of 'this' parameter. 
        ''' </summary>
        Private Function OptimizeMethodCallForDelegateInvoke(node As BoundCall) As BoundCall
            Dim method = node.Method
            Dim receiver = node.ReceiverOpt
            Dim useSiteInfo As New CompoundUseSiteInfo(Of AssemblySymbol)(Diagnostics, CompilationState.Compilation.Assembly)
 
            If method.MethodKind = MethodKind.DelegateInvoke AndAlso
                    method.ContainingType.IsAnonymousType AndAlso
                    receiver.Kind = BoundKind.DelegateCreationExpression AndAlso
                    Conversions.ClassifyMethodConversionForLambdaOrAnonymousDelegate(
                        method, DirectCast(receiver, BoundDelegateCreationExpression).Method, useSiteInfo) = MethodConversionKind.Identity Then
                Diagnostics.Add(node, useSiteInfo)
 
                Dim delegateCreation = DirectCast(receiver, BoundDelegateCreationExpression)
                Debug.Assert(delegateCreation.RelaxationLambdaOpt Is Nothing AndAlso delegateCreation.RelaxationReceiverPlaceholderOpt Is Nothing)
 
                If Not delegateCreation.Method.IsReducedExtensionMethod Then
                    method = delegateCreation.Method
                    receiver = delegateCreation.ReceiverOpt
                    node = node.Update(
                        method,
                        Nothing,
                        receiver,
                        node.Arguments,
                        node.DefaultArguments,
                        Nothing,
                        isLValue:=False,
                        suppressObjectClone:=node.SuppressObjectClone,
                        type:=node.Type)
                End If
            End If
 
            Return node
        End Function
 
    End Class
End Namespace