File: CompilationContext.vb
Web Access
Project: src\src\ExpressionEvaluator\VisualBasic\Source\ExpressionCompiler\Microsoft.CodeAnalysis.VisualBasic.ExpressionCompiler.vbproj (Microsoft.CodeAnalysis.VisualBasic.ExpressionCompiler)
' 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
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.Debugging
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.ExpressionEvaluator
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.VisualStudio.Debugger.Clr
Imports Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation
Imports Roslyn.Utilities
 
Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
    Friend NotInheritable Class CompilationContext
 
        Private Shared ReadOnly s_fullNameFormat As New SymbolDisplayFormat(
            globalNamespaceStyle:=SymbolDisplayGlobalNamespaceStyle.Omitted,
            typeQualificationStyle:=SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
            genericsOptions:=SymbolDisplayGenericsOptions.IncludeTypeParameters,
            miscellaneousOptions:=SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers Or SymbolDisplayMiscellaneousOptions.UseSpecialTypes)
 
        Friend Shared ReadOnly BackstopBinder As Binder = New BackstopBinder()
 
        Friend ReadOnly Compilation As VisualBasicCompilation
        Friend ReadOnly NamespaceBinder As Binder ' Internal for test purposes.
 
        Private ReadOnly _currentFrame As MethodSymbol
        Private ReadOnly _locals As ImmutableArray(Of LocalSymbol)
        Private ReadOnly _displayClassVariables As ImmutableDictionary(Of String, DisplayClassVariable)
        Private ReadOnly _sourceMethodParametersInOrder As ImmutableArray(Of String)
        Private ReadOnly _localsForBinding As ImmutableArray(Of LocalSymbol)
        Private ReadOnly _methodNotType As Boolean
        Private ReadOnly _voidType As NamedTypeSymbol
 
        ''' <summary>
        ''' Create a context to compile expressions within a method scope.
        ''' </summary>
        Friend Sub New(
            compilation As VisualBasicCompilation,
            currentFrame As MethodSymbol,
            currentSourceMethod As MethodSymbol,
            locals As ImmutableArray(Of LocalSymbol),
            inScopeHoistedLocalSlots As ImmutableSortedSet(Of Integer),
            methodDebugInfo As MethodDebugInfo(Of TypeSymbol, LocalSymbol),
            withSyntax As Boolean)
 
            _currentFrame = currentFrame
 
            Debug.Assert(compilation.Options.RootNamespace = "") ' Default value.
            Debug.Assert(methodDebugInfo.ExternAliasRecords.IsDefaultOrEmpty)
 
            Dim originalCompilation = compilation
 
            If withSyntax AndAlso
                compilation.Options.SuppressEmbeddedDeclarations AndAlso
                compilation.SyntaxTrees.IsEmpty Then
                ' We need to add an empty tree to the compilation as 
                ' a workaround for https://github.com/dotnet/roslyn/issues/16885.
                compilation = compilation.AddSyntaxTrees(VisualBasicSyntaxTree.Dummy)
            End If
 
            Dim defaultNamespaceName As String = methodDebugInfo.DefaultNamespaceName
 
            ' Note We don't need to try to bind this string because this is analogous to passing
            ' a command-line argument - as long as the syntax is valid, an appropriate symbol will
            ' be created for us.
            If defaultNamespaceName IsNot Nothing AndAlso TryParseDottedName(defaultNamespaceName, Nothing) Then
                compilation = compilation.WithOptions(compilation.Options.WithRootNamespace(defaultNamespaceName))
            End If
 
            If compilation Is originalCompilation Then
                compilation = compilation.Clone()
            End If
 
            Me.Compilation = compilation
 
            ' Each expression compile should use a unique compilation
            ' to ensure expression-specific synthesized members can be
            ' added (anonymous types, for instance).
            Debug.Assert(Me.Compilation IsNot originalCompilation)
 
            NamespaceBinder = CreateBinderChain(
                Me.Compilation,
                currentFrame.ContainingNamespace,
                methodDebugInfo.ImportRecordGroups)
 
            _voidType = Me.Compilation.GetSpecialType(SpecialType.System_Void)
 
            _methodNotType = Not locals.IsDefault
 
            If _methodNotType Then
                _locals = locals
                _sourceMethodParametersInOrder = GetSourceMethodParametersInOrder(currentFrame, currentSourceMethod)
                Dim displayClassVariableNamesInOrder As ImmutableArray(Of String) = Nothing
                GetDisplayClassVariables(
                    currentFrame,
                    currentSourceMethod,
                    locals,
                    inScopeHoistedLocalSlots,
                    _sourceMethodParametersInOrder,
                    displayClassVariableNamesInOrder,
                    _displayClassVariables)
                Debug.Assert(displayClassVariableNamesInOrder.Length = _displayClassVariables.Count)
                _localsForBinding = GetLocalsForBinding(locals, displayClassVariableNamesInOrder, _displayClassVariables)
            Else
                _locals = ImmutableArray(Of LocalSymbol).Empty
                _displayClassVariables = ImmutableDictionary(Of String, DisplayClassVariable).Empty
                _localsForBinding = ImmutableArray(Of LocalSymbol).Empty
            End If
 
            ' Assert that the cheap check for "Me" is equivalent to the expensive check for "Me".
            Debug.Assert(
                _displayClassVariables.ContainsKey(GeneratedNameConstants.HoistedMeName) =
                _displayClassVariables.Values.Any(Function(v) v.Kind = DisplayClassVariableKind.Me))
        End Sub
 
        Friend Function Compile(
            syntax As ExecutableStatementSyntax,
            typeName As String,
            methodName As String,
            aliases As ImmutableArray(Of [Alias]),
            testData As Microsoft.CodeAnalysis.CodeGen.CompilationTestData,
            diagnostics As DiagnosticBag,
            <Out> ByRef synthesizedMethod As EEMethodSymbol) As CommonPEModuleBuilder
 
            Dim objectType = Me.Compilation.GetSpecialType(SpecialType.System_Object)
            Dim synthesizedType = New EENamedTypeSymbol(
                Me.Compilation.SourceAssembly.GlobalNamespace,
                objectType,
                syntax,
                _currentFrame,
                typeName,
                methodName,
                Me,
                Function(method As EEMethodSymbol, diags As DiagnosticBag, ByRef properties As ResultProperties)
                    Dim hasDisplayClassMe = _displayClassVariables.ContainsKey(GeneratedNameConstants.HoistedMeName)
                    Dim bindAsExpression = syntax.Kind = SyntaxKind.PrintStatement
                    Dim binder = ExtendBinderChain(
                        aliases,
                        method,
                        NamespaceBinder,
                        hasDisplayClassMe,
                        _methodNotType,
                        allowImplicitDeclarations:=Not bindAsExpression)
                    Return If(bindAsExpression,
                        BindExpression(binder, DirectCast(syntax, PrintStatementSyntax).Expression, diags, properties),
                        BindStatement(binder, syntax, diags, properties))
                End Function)
 
            Dim moduleBuilder = CreateModuleBuilder(
                Me.Compilation,
                synthesizedType.Methods,
                additionalTypes:=ImmutableArray.Create(DirectCast(synthesizedType, NamedTypeSymbol)),
                testData:=testData,
                diagnostics:=diagnostics)
 
            Debug.Assert(moduleBuilder IsNot Nothing)
 
            Me.Compilation.Compile(
                moduleBuilder,
                emittingPdb:=False,
                diagnostics:=diagnostics,
                filterOpt:=Nothing,
                cancellationToken:=CancellationToken.None)
 
            If diagnostics.HasAnyErrors() Then
                synthesizedMethod = Nothing
                Return Nothing
            End If
 
            synthesizedMethod = DirectCast(synthesizedType.Methods(0), EEMethodSymbol)
            Return moduleBuilder
        End Function
 
        Private Shared Function GetNextMethodName(builder As ArrayBuilder(Of MethodSymbol)) As String
            ' NOTE: These names are consumed by Concord, so there's no native precedent.
            Return String.Format("<>m{0}", builder.Count)
        End Function
 
        ''' <summary>
        ''' Generate a class containing methods that represent
        ''' the set of arguments and locals at the current scope.
        ''' </summary>
        Friend Function CompileGetLocals(
            typeName As String,
            localBuilder As ArrayBuilder(Of LocalAndMethod),
            argumentsOnly As Boolean,
            aliases As ImmutableArray(Of [Alias]),
            testData As Microsoft.CodeAnalysis.CodeGen.CompilationTestData,
            diagnostics As DiagnosticBag) As CommonPEModuleBuilder
 
            Dim objectType = Me.Compilation.GetSpecialType(SpecialType.System_Object)
            Dim allTypeParameters = GetAllTypeParameters(_currentFrame)
            Dim additionalTypes = ArrayBuilder(Of NamedTypeSymbol).GetInstance()
            Dim syntax = SyntaxFactory.IdentifierName(SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken))
 
            Dim typeVariablesType As EENamedTypeSymbol = Nothing
            If Not argumentsOnly AndAlso allTypeParameters.Length > 0 Then
                ' Generate a generic type with matching type parameters.
                ' A null instance of this type will be used to represent
                ' the "Type variables" local.
                typeVariablesType = New EENamedTypeSymbol(
                    Me.Compilation.SourceModule.GlobalNamespace,
                    objectType,
                    syntax,
                    _currentFrame,
                    ExpressionCompilerConstants.TypeVariablesClassName,
                    Function(m, t)
                        Dim constructor As New EEConstructorSymbol(t)
                        constructor.SetParameters(ImmutableArray(Of ParameterSymbol).Empty)
                        Return ImmutableArray.Create(Of MethodSymbol)(constructor)
                    End Function,
                    allTypeParameters,
                    Function(t1, t2) allTypeParameters.SelectAsArray(Function(tp, i, t) DirectCast(New SimpleTypeParameterSymbol(t, i, tp.GetUnmangledName()), TypeParameterSymbol), t2))
                additionalTypes.Add(typeVariablesType)
            End If
 
            Dim synthesizedType As New EENamedTypeSymbol(
                Me.Compilation.SourceModule.GlobalNamespace,
                objectType,
                syntax,
                _currentFrame,
                typeName,
                Function(m, container)
                    Dim methodBuilder = ArrayBuilder(Of MethodSymbol).GetInstance()
 
                    If Not argumentsOnly Then
                        If aliases.Length > 0 Then
                            ' Pseudo-variables: $exception, $ReturnValue, etc.
                            Dim typeNameDecoder = New EETypeNameDecoder(Me.Compilation, DirectCast(_currentFrame.ContainingModule, PEModuleSymbol))
                            For Each [alias] As [Alias] In aliases
                                If [alias].IsReturnValueWithoutIndex() Then
                                    Debug.Assert(aliases.Count(Function(a) a.Kind = DkmClrAliasKind.ReturnValue) > 1)
                                    Continue For
                                End If
 
                                Dim methodName = GetNextMethodName(methodBuilder)
                                Dim local = PlaceholderLocalSymbol.Create(typeNameDecoder, _currentFrame, [alias])
                                ' Skip pseudo-variables with errors.
                                If local.GetUseSiteInfo().DiagnosticInfo?.Severity = DiagnosticSeverity.Error Then
                                    Continue For
                                End If
                                Dim aliasMethod = Me.CreateMethod(
                                    container,
                                    methodName,
                                    syntax,
                                    Function(method As EEMethodSymbol, diags As DiagnosticBag, ByRef properties As ResultProperties)
                                        Dim expression = New BoundLocal(syntax, local, isLValue:=False, type:=local.Type)
                                        properties = Nothing
                                        Return New BoundReturnStatement(syntax, expression, Nothing, Nothing).MakeCompilerGenerated()
                                    End Function)
                                localBuilder.Add(MakeLocalAndMethod(local, aliasMethod, If(local.IsReadOnly, DkmClrCompilationResultFlags.ReadOnlyResult, DkmClrCompilationResultFlags.None)))
                                methodBuilder.Add(aliasMethod)
                            Next
                        End If
 
                        ' "Me" for non-shared methods that are not display class methods
                        ' or display class methods where the display class contains "$VB$Me".
                        If Not m.IsShared AndAlso (Not m.ContainingType.IsClosureOrStateMachineType() OrElse _displayClassVariables.ContainsKey(GeneratedNames.MakeStateMachineCapturedMeName())) Then
                            Dim methodName = GetNextMethodName(methodBuilder)
                            Dim method = Me.GetMeMethod(container, methodName)
                            localBuilder.Add(New VisualBasicLocalAndMethod("Me", "Me", method, DkmClrCompilationResultFlags.None)) ' NOTE: writable in Dev11.
                            methodBuilder.Add(method)
                        End If
                    End If
 
                    Dim itemsAdded = PooledHashSet(Of String).GetInstance()
 
                    ' Method parameters
                    Dim parameterIndex = If(m.IsShared, 0, 1)
                    For Each parameter In m.Parameters
                        Dim parameterName As String = parameter.Name
                        If GeneratedNameParser.GetKind(parameterName) = GeneratedNameKind.None Then
                            AppendParameterAndMethod(localBuilder, methodBuilder, parameter, container, parameterIndex)
                            itemsAdded.Add(parameterName)
                        End If
 
                        parameterIndex += 1
                    Next
 
                    ' In case of iterator Or async state machine, the 'm' method has no parameters
                    ' but the source method can have parameters to iterate over.
                    If itemsAdded.Count = 0 AndAlso _sourceMethodParametersInOrder.Length <> 0 Then
                        Dim localsDictionary = PooledDictionary(Of String, (LocalSymbol, Integer)).GetInstance()
                        Dim localIndex = 0
                        For Each local In _localsForBinding
                            localsDictionary.Add(local.Name, (local, localIndex))
                            localIndex += 1
                        Next
 
                        For Each argumentName In _sourceMethodParametersInOrder
 
                            Dim localSymbolAndIndex As (LocalSymbol, Integer) = Nothing
                            If localsDictionary.TryGetValue(argumentName, localSymbolAndIndex) Then
                                itemsAdded.Add(argumentName)
                                Dim local = localSymbolAndIndex.Item1
                                AppendLocalAndMethod(localBuilder, methodBuilder, local, container, localSymbolAndIndex.Item2, GetLocalResultFlags(local))
                            End If
                        Next
 
                        localsDictionary.Free()
                    End If
 
                    If Not argumentsOnly Then
                        ' Locals.
                        Dim localIndex As Integer = 0
                        For Each local In _localsForBinding
                            If Not itemsAdded.Contains(local.Name) Then
                                AppendLocalAndMethod(localBuilder, methodBuilder, local, container, localIndex, GetLocalResultFlags(local))
                            End If
 
                            localIndex += 1
                        Next
 
                        ' "Type variables".
                        If typeVariablesType IsNot Nothing Then
                            Dim methodName = GetNextMethodName(methodBuilder)
                            Dim returnType = typeVariablesType.Construct(ImmutableArrayExtensions.Cast(Of TypeParameterSymbol, TypeSymbol)(allTypeParameters))
                            Dim method = Me.GetTypeVariableMethod(container, methodName, returnType)
                            localBuilder.Add(New VisualBasicLocalAndMethod(
                                             ExpressionCompilerConstants.TypeVariablesLocalName,
                                             ExpressionCompilerConstants.TypeVariablesLocalName,
                                             method,
                                             DkmClrCompilationResultFlags.ReadOnlyResult))
                            methodBuilder.Add(method)
                        End If
                    End If
 
                    itemsAdded.Free()
                    Return methodBuilder.ToImmutableAndFree()
                End Function)
 
            additionalTypes.Add(synthesizedType)
 
            Dim moduleBuilder = CreateModuleBuilder(
                Me.Compilation,
                synthesizedType.Methods,
                additionalTypes:=additionalTypes.ToImmutableAndFree(),
                testData:=testData,
                diagnostics:=diagnostics)
 
            Debug.Assert(moduleBuilder IsNot Nothing)
 
            Me.Compilation.Compile(
                moduleBuilder,
                emittingPdb:=False,
                diagnostics:=diagnostics,
                filterOpt:=Nothing,
                cancellationToken:=CancellationToken.None)
 
            Return If(diagnostics.HasAnyErrors(), Nothing, moduleBuilder)
        End Function
 
        Private Sub AppendLocalAndMethod(
            localBuilder As ArrayBuilder(Of LocalAndMethod),
            methodBuilder As ArrayBuilder(Of MethodSymbol),
            local As LocalSymbol,
            container As EENamedTypeSymbol,
            localIndex As Integer,
            resultFlags As DkmClrCompilationResultFlags)
 
            Dim methodName = GetNextMethodName(methodBuilder)
            Dim method = Me.GetLocalMethod(container, methodName, local.Name, localIndex)
            localBuilder.Add(MakeLocalAndMethod(local, method, resultFlags))
            methodBuilder.Add(method)
        End Sub
 
        Private Sub AppendParameterAndMethod(
            localBuilder As ArrayBuilder(Of LocalAndMethod),
            methodBuilder As ArrayBuilder(Of MethodSymbol),
            parameter As ParameterSymbol,
            container As EENamedTypeSymbol,
            parameterIndex As Integer)
 
            ' Note: The native EE doesn't do this, but if we don't escape keyword identifiers,
            ' the ResultProvider needs to be able to disambiguate cases Like "Me" And "[Me]",
            ' which it can't do correctly without semantic information.
            Dim name = SyntaxHelpers.EscapeKeywordIdentifiers(parameter.Name)
            Dim methodName = GetNextMethodName(methodBuilder)
            Dim method = Me.GetParameterMethod(container, methodName, parameter.Name, parameterIndex)
            localBuilder.Add(New VisualBasicLocalAndMethod(name, name, method, DkmClrCompilationResultFlags.None))
            methodBuilder.Add(method)
        End Sub
 
        Private Shared Function MakeLocalAndMethod(local As LocalSymbol, method As MethodSymbol, flags As DkmClrCompilationResultFlags) As LocalAndMethod
            ' Note: The native EE doesn't do this, but if we don't escape keyword identifiers,
            ' the ResultProvider needs to be able to disambiguate cases Like "Me" And "[Me]",
            ' which it can't do correctly without semantic information.
            Dim escapedName = SyntaxHelpers.EscapeKeywordIdentifiers(local.Name)
            Dim displayName = If(TryCast(local, PlaceholderLocalSymbol)?.DisplayName, escapedName)
            Return New VisualBasicLocalAndMethod(escapedName, displayName, method, flags)
        End Function
 
        Private Shared Function CreateModuleBuilder(
            compilation As VisualBasicCompilation,
            methods As ImmutableArray(Of MethodSymbol),
            additionalTypes As ImmutableArray(Of NamedTypeSymbol),
            testData As Microsoft.CodeAnalysis.CodeGen.CompilationTestData,
            diagnostics As DiagnosticBag) As EEAssemblyBuilder
 
            ' Each assembly must have a unique name.
            Dim emitOptions = New EmitOptions(outputNameOverride:=ExpressionCompilerUtilities.GenerateUniqueName())
            Dim runtimeMetadataVersion = compilation.GetRuntimeMetadataVersion()
            Dim serializationProperties = compilation.ConstructModuleSerializationProperties(emitOptions, runtimeMetadataVersion)
            Return New EEAssemblyBuilder(compilation.SourceAssembly, emitOptions, methods, serializationProperties, additionalTypes, testData)
        End Function
 
        Friend Function CreateMethod(
            container As EENamedTypeSymbol,
            methodName As String,
            syntax As VisualBasicSyntaxNode,
            generateMethodBody As GenerateMethodBody) As EEMethodSymbol
 
            Return New EEMethodSymbol(
                Compilation,
                container,
                methodName,
                syntax.GetLocation(),
                _currentFrame,
                _locals,
                _localsForBinding,
                _displayClassVariables,
                _voidType,
                generateMethodBody)
        End Function
 
        Private Function GetLocalMethod(container As EENamedTypeSymbol, methodName As String, localName As String, localIndex As Integer) As EEMethodSymbol
            Dim syntax = SyntaxFactory.IdentifierName(localName)
            Return Me.CreateMethod(
                container,
                methodName,
                syntax,
                Function(method As EEMethodSymbol, diagnostics As DiagnosticBag, ByRef properties As ResultProperties)
                    Dim local = method.LocalsForBinding(localIndex)
                    Dim expression = New BoundLocal(syntax, local, isLValue:=False, type:=local.Type)
                    properties = Nothing
                    Return New BoundReturnStatement(syntax, expression, Nothing, Nothing).MakeCompilerGenerated()
                End Function)
        End Function
 
        Private Function GetParameterMethod(container As EENamedTypeSymbol, methodName As String, parameterName As String, parameterIndex As Integer) As EEMethodSymbol
            Dim syntax = SyntaxFactory.IdentifierName(parameterName)
            Return Me.CreateMethod(
                container,
                methodName,
                syntax,
                Function(method As EEMethodSymbol, diagnostics As DiagnosticBag, ByRef properties As ResultProperties)
                    Dim parameter = method.Parameters(parameterIndex)
                    Dim expression = New BoundParameter(syntax, parameter, isLValue:=False, type:=parameter.Type)
                    properties = Nothing
                    Return New BoundReturnStatement(syntax, expression, Nothing, Nothing).MakeCompilerGenerated()
                End Function)
        End Function
 
        Private Function GetMeMethod(container As EENamedTypeSymbol, methodName As String) As EEMethodSymbol
            Dim syntax = SyntaxFactory.MeExpression()
            Return Me.CreateMethod(
                container,
                methodName,
                syntax,
                Function(method As EEMethodSymbol, diagnostics As DiagnosticBag, ByRef properties As ResultProperties)
                    Dim expression = New BoundMeReference(syntax, GetNonClosureOrStateMachineContainer(container.SubstitutedSourceType))
                    properties = Nothing
                    Return New BoundReturnStatement(syntax, expression, Nothing, Nothing).MakeCompilerGenerated()
                End Function)
        End Function
 
        Private Function GetTypeVariableMethod(container As EENamedTypeSymbol, methodName As String, typeVariablesType As NamedTypeSymbol) As EEMethodSymbol
            Dim syntax = SyntaxFactory.IdentifierName(SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken))
            Return Me.CreateMethod(
                container,
                methodName,
                syntax,
                Function(method As EEMethodSymbol, diagnostics As DiagnosticBag, ByRef properties As ResultProperties)
                    Dim type = method.TypeMap.SubstituteNamedType(typeVariablesType)
                    Dim expression = New BoundObjectCreationExpression(syntax, type.InstanceConstructors(0), ImmutableArray(Of BoundExpression).Empty, Nothing, type)
                    properties = Nothing
                    Return New BoundReturnStatement(syntax, expression, Nothing, Nothing).MakeCompilerGenerated()
                End Function)
        End Function
 
        Private Shared Function BindExpression(binder As Binder, syntax As ExpressionSyntax, diagnostics As DiagnosticBag, <Out> ByRef resultProperties As ResultProperties) As BoundStatement
            Dim bindingDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=False)
            Dim expression = binder.BindExpression(syntax, bindingDiagnostics)
 
            Dim flags = DkmClrCompilationResultFlags.None
            If Not IsAssignableExpression(binder, expression) Then
                flags = flags Or DkmClrCompilationResultFlags.ReadOnlyResult
            End If
 
            Try
                If MayHaveSideEffectsVisitor.MayHaveSideEffects(expression) Then
                    flags = flags Or DkmClrCompilationResultFlags.PotentialSideEffect
                End If
            Catch ex As BoundTreeVisitor.CancelledByStackGuardException
                ex.AddAnError(diagnostics)
            End Try
 
            If IsStatement(expression) Then
                expression = binder.ReclassifyInvocationExpressionAsStatement(expression, bindingDiagnostics)
            Else
                expression = binder.MakeRValue(expression, bindingDiagnostics)
            End If
 
            diagnostics.AddRange(bindingDiagnostics.DiagnosticBag)
            bindingDiagnostics.Free()
 
            Select Case expression.Type.SpecialType
                Case SpecialType.System_Void
                    Debug.Assert(expression.ConstantValueOpt Is Nothing)
                    resultProperties = expression.ExpressionSymbol.GetResultProperties(flags, isConstant:=False)
                    Return New BoundExpressionStatement(syntax, expression).MakeCompilerGenerated()
                Case SpecialType.System_Boolean
                    flags = flags Or DkmClrCompilationResultFlags.BoolResult
            End Select
 
            resultProperties = expression.ExpressionSymbol.GetResultProperties(flags, expression.ConstantValueOpt IsNot Nothing)
            Return New BoundReturnStatement(syntax, expression, Nothing, Nothing).MakeCompilerGenerated()
        End Function
 
        Private Shared Function IsAssignableExpression(binder As Binder, expression As BoundExpression) As Boolean
            Dim diagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=False)
            Dim value = binder.ReclassifyAsValue(expression, diagnostics)
            Dim result = False
            If Binder.IsValidAssignmentTarget(value) AndAlso Not diagnostics.HasAnyErrors() Then
                Dim isError = False
                binder.AdjustAssignmentTarget(value.Syntax, value, diagnostics, isError)
                Debug.Assert(isError = diagnostics.HasAnyErrors())
                result = Not isError
            End If
            diagnostics.Free()
            Return result
        End Function
 
        Private Shared Function IsStatement(expression As BoundExpression) As Boolean
            Select Case expression.Kind
                Case BoundKind.Call
                    Return IsCallStatement(DirectCast(expression, BoundCall))
                Case BoundKind.ConditionalAccess
                    Dim [call] = TryCast(DirectCast(expression, BoundConditionalAccess).AccessExpression, BoundCall)
                    Return ([call] IsNot Nothing) AndAlso IsCallStatement([call])
                Case Else
                    Return False
            End Select
        End Function
 
        Private Shared Function IsCallStatement([call] As BoundCall) As Boolean
            Return [call].Method.IsSub
        End Function
 
        Private Shared Function BindStatement(binder As Binder, syntax As StatementSyntax, diagnostics As DiagnosticBag, <Out> ByRef resultProperties As ResultProperties) As BoundStatement
            resultProperties = New ResultProperties(DkmClrCompilationResultFlags.PotentialSideEffect Or DkmClrCompilationResultFlags.ReadOnlyResult)
            Dim builder = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=False)
            Dim result = binder.BindStatement(syntax, builder).MakeCompilerGenerated()
            diagnostics.AddRange(builder.DiagnosticBag)
            builder.Free()
            Return result
        End Function
 
        Private Shared Function CreateBinderChain(
            compilation As VisualBasicCompilation,
            [namespace] As NamespaceSymbol,
            importRecordGroups As ImmutableArray(Of ImmutableArray(Of ImportRecord))) As Binder
 
            Dim binder = BackstopBinder
            binder = New SuppressDiagnosticsBinder(binder)
            binder = New IgnoreAccessibilityBinder(binder)
            binder = New SourceModuleBinder(binder, DirectCast(compilation.Assembly.Modules(0), SourceModuleSymbol))
 
            If Not importRecordGroups.IsEmpty Then
                binder = BuildImportedSymbolsBinder(binder, New NamespaceBinder(binder, compilation.GlobalNamespace), importRecordGroups)
            End If
 
            Dim stack = ArrayBuilder(Of String).GetInstance()
            Dim containingNamespace = [namespace]
            While containingNamespace IsNot Nothing
                stack.Push(containingNamespace.Name)
                containingNamespace = containingNamespace.ContainingNamespace
            End While
 
            ' PERF: we used to call compilation.GetCompilationNamespace on every iteration,
            ' but that involved walking up to the global namespace, which we have to do
            ' anyway.  Instead, we'll inline the functionality into our own walk of the
            ' namespace chain.
            [namespace] = compilation.GlobalNamespace
 
            While stack.Count > 0
                Dim namespaceName = stack.Pop()
                If namespaceName.Length > 0 Then
                    ' We're re-getting the namespace, rather than using the one containing
                    ' the current frame method, because we want the merged namespace
                    [namespace] = [namespace].GetNestedNamespace(namespaceName)
                    Debug.Assert([namespace] IsNot Nothing,
                                 "We worked backwards from symbols to names, but no symbol exists for name '" + namespaceName + "'")
                Else
                    Debug.Assert([namespace] Is compilation.GlobalNamespace)
                End If
                binder = New NamespaceBinder(binder, [namespace])
            End While
 
            stack.Free()
 
            Return binder
        End Function
 
        Private Shared Function ExtendBinderChain(
            aliases As ImmutableArray(Of [Alias]),
            method As EEMethodSymbol,
            binder As Binder,
            hasDisplayClassMe As Boolean,
            methodNotType As Boolean,
            allowImplicitDeclarations As Boolean) As Binder
 
            Dim substitutedSourceMethod = GetSubstitutedSourceMethod(method.SubstitutedSourceMethod, hasDisplayClassMe)
            Dim substitutedSourceType = substitutedSourceMethod.ContainingType
 
            Dim stack = ArrayBuilder(Of NamedTypeSymbol).GetInstance()
            Dim type = substitutedSourceType
            While type IsNot Nothing
                stack.Push(type)
                type = type.ContainingType
            End While
 
            While stack.Count > 0
                substitutedSourceType = stack.Pop()
                binder = New EENamedTypeBinder(substitutedSourceType, binder)
            End While
 
            stack.Free()
 
            If substitutedSourceMethod.Arity > 0 Then
                binder = New MethodTypeParametersBinder(binder, substitutedSourceMethod.TypeArguments.SelectAsArray(Function(t) DirectCast(t, TypeParameterSymbol)))
            End If
 
            ' The "Type Context" is used when binding DebuggerDisplayAttribute expressions.
            ' We have chosen to explicitly disallow pseudo variables in that scenario.
            If methodNotType Then
                ' Method locals and parameters shadow pseudo-variables.
                Dim typeNameDecoder = New EETypeNameDecoder(binder.Compilation, DirectCast(substitutedSourceMethod.ContainingModule, PEModuleSymbol))
                binder = New PlaceholderLocalBinder(aliases, method, typeNameDecoder, allowImplicitDeclarations, binder)
            End If
 
            ' Even if there are no parameters or locals, this has the effect of setting
            ' the containing member to the substituted source method.
            binder = New ParametersAndLocalsBinder(binder, method, substitutedSourceMethod)
 
            Return binder
        End Function
 
        Private Shared Function BuildImportedSymbolsBinder(
            containingBinder As Binder,
            importBinder As Binder,
            importRecordGroups As ImmutableArray(Of ImmutableArray(Of ImportRecord))) As Binder
 
            Dim projectLevelImportsBuilder As ArrayBuilder(Of NamespaceOrTypeAndImportsClausePosition) = Nothing
            Dim fileLevelImportsBuilder As ArrayBuilder(Of NamespaceOrTypeAndImportsClausePosition) = Nothing
 
            Dim projectLevelAliases As Dictionary(Of String, AliasAndImportsClausePosition) = Nothing
            Dim fileLevelAliases As Dictionary(Of String, AliasAndImportsClausePosition) = Nothing
 
            Dim projectLevelXmlImports As Dictionary(Of String, XmlNamespaceAndImportsClausePosition) = Nothing
            Dim fileLevelXmlImports As Dictionary(Of String, XmlNamespaceAndImportsClausePosition) = Nothing
 
            Debug.Assert(importRecordGroups.Length = 2) ' First file-level, then project-level.
            Dim fileLevelImportRecords = importRecordGroups(0)
            Dim projectLevelImportRecords = importRecordGroups(1)
 
            ' Use this to give the imports different positions
            Dim position = 0
 
            For Each importRecord As ImportRecord In projectLevelImportRecords
                If AddImportForRecord(
                    importRecord,
                    importBinder,
                    position,
                    projectLevelImportsBuilder,
                    projectLevelAliases,
                    projectLevelXmlImports) Then
 
                    position += 1
                End If
            Next
 
            For Each importRecord As ImportRecord In fileLevelImportRecords
                If AddImportForRecord(
                    importRecord,
                    importBinder,
                    position,
                    fileLevelImportsBuilder,
                    fileLevelAliases,
                    fileLevelXmlImports) Then
 
                    position += 1
                End If
            Next
 
            ' BinderBuilder.CreateBinderForSourceFile creates separate binders for the project- and file-level
            ' imports.  We'd do the same, but we don't have a SourceFileBinder to put in between and that
            ' violates some (very specific) assertions about the shape of the binder chain.  Instead, we will
            ' manually resolve ties and then create a single set of binders.
 
            Dim binder = containingBinder
 
            Dim importsBuilder As ArrayBuilder(Of NamespaceOrTypeAndImportsClausePosition)
            If projectLevelImportsBuilder Is Nothing Then
                importsBuilder = fileLevelImportsBuilder
            ElseIf fileLevelImportsBuilder Is Nothing Then
                importsBuilder = projectLevelImportsBuilder
            Else
                importsBuilder = fileLevelImportsBuilder
                importsBuilder.AddRange(projectLevelImportsBuilder)
                projectLevelImportsBuilder.Free()
            End If
            If importsBuilder IsNot Nothing Then
                Dim [imports] As ImmutableArray(Of NamespaceOrTypeAndImportsClausePosition) = importsBuilder.ToImmutableAndFree()
                binder = New TypesOfImportedNamespacesMembersBinder(binder, [imports])
                binder = New ImportedTypesAndNamespacesMembersBinder(binder, [imports])
            End If
 
            Dim aliases = MergeAliases(projectLevelAliases, fileLevelAliases)
            If aliases IsNot Nothing Then
                binder = New ImportAliasesBinder(binder, aliases)
            End If
 
            Dim xmlImports = MergeAliases(projectLevelXmlImports, fileLevelXmlImports)
            If xmlImports IsNot Nothing Then
                binder = New XmlNamespaceImportsBinder(binder, xmlImports)
            End If
 
            Return binder
        End Function
 
        Private Shared Function AddImportForRecord(
            importRecord As ImportRecord,
            importBinder As Binder,
            position As Integer,
            ByRef importsBuilder As ArrayBuilder(Of NamespaceOrTypeAndImportsClausePosition),
            ByRef aliases As Dictionary(Of String, AliasAndImportsClausePosition),
            ByRef xmlImports As Dictionary(Of String, XmlNamespaceAndImportsClausePosition)) As Boolean
 
            Dim targetString = importRecord.TargetString
 
            ' NB: It appears that imports of generic types are not included in the PDB, so we never have to worry about parsing them.
            ' NB: Unlike in C# PDBs, the assembly name will not be present, so we have to just bind the string.
            Dim targetSyntax As NameSyntax = Nothing
            If Not String.IsNullOrEmpty(targetString) AndAlso ' CurrentNamespace may be an empty string, new-format types may be null.
                    importRecord.TargetKind <> ImportTargetKind.XmlNamespace AndAlso
                    Not TryParseDottedName(targetString, targetSyntax) Then
 
                Debug.WriteLine($"Import record '{importRecord}' has syntactically invalid target '{targetString}'")
                Return False
            End If
 
            ' Check for syntactically invalid aliases.
            Dim [alias] = importRecord.Alias
            If Not String.IsNullOrEmpty([alias]) Then
                Dim aliasNameSyntax As NameSyntax = Nothing
                If Not TryParseDottedName([alias], aliasNameSyntax) OrElse aliasNameSyntax.Kind <> SyntaxKind.IdentifierName Then
                    Debug.WriteLine($"Import record '{importRecord}' has syntactically invalid alias '{[alias]}'")
                    Return False
                End If
            End If
 
            Select Case importRecord.TargetKind
                Case ImportTargetKind.Type
                    Dim typeSymbol As TypeSymbol
                    If importRecord.TargetType IsNot Nothing Then
                        typeSymbol = DirectCast(importRecord.TargetType, TypeSymbol)
                    Else
                        Debug.Assert(importRecord.Alias Is Nothing) ' Represented as ImportTargetKind.NamespaceOrType in old-format PDBs.
 
                        typeSymbol = importBinder.BindTypeSyntax(targetSyntax, BindingDiagnosticBag.Discarded)
 
                        Debug.Assert(typeSymbol IsNot Nothing)
 
                        If typeSymbol.Kind = SymbolKind.ErrorType Then
                            ' Type is unrecognized.  The import may have been
                            ' valid in the original source but unnecessary.
                            Return False ' Don't add anything for this import.
                        End If
                    End If
 
                    If [alias] IsNot Nothing Then
                        Dim aliasSymbol As New AliasSymbol(importBinder.Compilation, importBinder.ContainingNamespaceOrType, [alias], typeSymbol, NoLocation.Singleton)
 
                        If aliases Is Nothing Then
                            aliases = New Dictionary(Of String, AliasAndImportsClausePosition)()
                        End If
 
                        ' There's no real syntax, so there's no real position.  We'll give them separate numbers though.
                        aliases([alias]) = New AliasAndImportsClausePosition(aliasSymbol, position, syntaxReference:=Nothing, ImmutableArray(Of AssemblySymbol).Empty)
                    Else
                        If importsBuilder Is Nothing Then
                            importsBuilder = ArrayBuilder(Of NamespaceOrTypeAndImportsClausePosition).GetInstance()
                        End If
 
                        ' There's no real syntax, so there's no real position.  We'll give them separate numbers though.
                        importsBuilder.Add(New NamespaceOrTypeAndImportsClausePosition(typeSymbol, position, syntaxReference:=Nothing, ImmutableArray(Of AssemblySymbol).Empty))
                    End If
 
                ' Dev12 treats the current namespace the same as any other namespace (see ProcedureContext::LoadImportsAndDefaultNamespaceNormal).
                ' It seems pointless to add an import for the namespace in which we are binding expressions, but the native source gives
                ' the impression that other namespaces may take the same form in Everett PDBs.
                Case ImportTargetKind.CurrentNamespace, ImportTargetKind.Namespace ' Unaliased namespace or type
                    If targetString = "" Then
                        Debug.Assert(importRecord.TargetKind = ImportTargetKind.CurrentNamespace) ' The current namespace can be empty.
                        Return False
                    End If
 
                    Dim namespaceOrTypeSymbol = importBinder.BindNamespaceOrTypeSyntax(targetSyntax, BindingDiagnosticBag.Discarded)
 
                    Debug.Assert(namespaceOrTypeSymbol IsNot Nothing)
 
                    If namespaceOrTypeSymbol.Kind = SymbolKind.ErrorType Then
                        ' Namespace is unrecognized.  The import may have been
                        ' valid in the original source but unnecessary.
                        Return False ' Don't add anything for this import.
                    End If
 
                    ' Native PDBs: aliased namespace is stored as NamespaceOrType
                    ' Portable PDBs: aliased namespace is stored as Namespace
                    If [alias] Is Nothing Then
                        If importsBuilder Is Nothing Then
                            importsBuilder = ArrayBuilder(Of NamespaceOrTypeAndImportsClausePosition).GetInstance()
                        End If
 
                        ' There's no real syntax, so there's no real position.  We'll give them separate numbers though.
                        importsBuilder.Add(New NamespaceOrTypeAndImportsClausePosition(namespaceOrTypeSymbol, position, syntaxReference:=Nothing, ImmutableArray(Of AssemblySymbol).Empty))
                    Else
                        Dim aliasSymbol As New AliasSymbol(importBinder.Compilation, importBinder.ContainingNamespaceOrType, [alias], namespaceOrTypeSymbol, NoLocation.Singleton)
 
                        If aliases Is Nothing Then
                            aliases = New Dictionary(Of String, AliasAndImportsClausePosition)()
                        End If
 
                        ' There's no real syntax, so there's no real position.  We'll give them separate numbers though.
                        aliases([alias]) = New AliasAndImportsClausePosition(aliasSymbol, position, syntaxReference:=Nothing, ImmutableArray(Of AssemblySymbol).Empty)
                    End If
 
                Case ImportTargetKind.NamespaceOrType ' Aliased namespace or type (native PDB only)
                    Dim namespaceOrTypeSymbol = importBinder.BindNamespaceOrTypeSyntax(targetSyntax, BindingDiagnosticBag.Discarded)
 
                    Debug.Assert(namespaceOrTypeSymbol IsNot Nothing)
 
                    If namespaceOrTypeSymbol.Kind = SymbolKind.ErrorType Then
                        ' Type is unrecognized.  The import may have been
                        ' valid in the original source but unnecessary.
                        Return False ' Don't add anything for this import.
                    End If
 
                    Debug.Assert([alias] IsNot Nothing) ' Implied by TargetKind
 
                    Dim aliasSymbol As New AliasSymbol(importBinder.Compilation, importBinder.ContainingNamespaceOrType, [alias], namespaceOrTypeSymbol, NoLocation.Singleton)
 
                    If aliases Is Nothing Then
                        aliases = New Dictionary(Of String, AliasAndImportsClausePosition)()
                    End If
 
                    ' There's no real syntax, so there's no real position.  We'll give them separate numbers though.
                    aliases([alias]) = New AliasAndImportsClausePosition(aliasSymbol, position, syntaxReference:=Nothing, ImmutableArray(Of AssemblySymbol).Empty)
 
                Case ImportTargetKind.XmlNamespace
                    If xmlImports Is Nothing Then
                        xmlImports = New Dictionary(Of String, XmlNamespaceAndImportsClausePosition)()
                    End If
 
                    ' There's no real syntax, so there's no real position.  We'll give them separate numbers though.
                    xmlImports(importRecord.Alias) = New XmlNamespaceAndImportsClausePosition(importRecord.TargetString, position, syntaxReference:=Nothing)
                Case ImportTargetKind.DefaultNamespace
                    ' Processed ahead of time so that it can be incorporated into the compilation before
                    ' constructing the binder chain.
                    Return False
                Case ImportTargetKind.MethodToken ' forwarding
                    ' One level of forwarding is pre-processed away, but invalid PDBs might contain
                    ' chains.  Just ignore them (as in Dev12).
                    Return False
                Case ImportTargetKind.Defunct
                    Return False
                Case ImportTargetKind.Assembly
                    ' VB doesn't have extern aliases.
                    Throw ExceptionUtilities.UnexpectedValue(importRecord.TargetKind)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(importRecord.TargetKind)
            End Select
 
            Return True
        End Function
 
        Private Shared Function MergeAliases(Of T)(projectLevel As Dictionary(Of String, T), fileLevel As Dictionary(Of String, T)) As Dictionary(Of String, T)
            If projectLevel Is Nothing Then
                Return fileLevel
            ElseIf fileLevel Is Nothing Then
                Return projectLevel
            End If
 
            ' File-level aliases win.
            For Each pair In projectLevel
                Dim [alias] As String = pair.Key
                If Not fileLevel.ContainsKey([alias]) Then
                    fileLevel.Add([alias], pair.Value)
                End If
            Next
 
            Return fileLevel
        End Function
 
        ''' <summary>
        ''' We don't want to use the real scanner because we want to treat keywords as identifiers.
        ''' Since the inputs are so simple, we'll just do the scanning ourselves.
        ''' </summary>
        Friend Shared Function TryParseDottedName(input As String, <Out> ByRef output As NameSyntax) As Boolean
            Dim pooled = PooledStringBuilder.GetInstance()
            Try
                Dim builder = pooled.Builder
 
                output = Nothing
                For Each ch In input
                    If builder.Length = 0 Then
 
                        If Not SyntaxFacts.IsIdentifierStartCharacter(ch) Then
                            output = Nothing
                            Return False
                        End If
 
                        builder.Append(ch)
                    ElseIf ch = "."c Then
                        Dim identifierName = SyntaxFactory.IdentifierName(builder.ToString())
 
                        builder.Clear()
 
                        output = If(output Is Nothing,
                            DirectCast(identifierName, NameSyntax),
                            SyntaxFactory.QualifiedName(output, identifierName))
                    ElseIf SyntaxFacts.IsIdentifierPartCharacter(ch) Then
                        builder.Append(ch)
                    Else
                        output = Nothing
                        Return False
                    End If
                Next
 
                ' There must be at least one character in the last identifier.
                If builder.Length = 0 Then
                    output = Nothing
                    Return False
                End If
 
                Dim finalIdentifierName = SyntaxFactory.IdentifierName(builder.ToString())
                output = If(output Is Nothing,
                    DirectCast(finalIdentifierName, NameSyntax),
                    SyntaxFactory.QualifiedName(output, finalIdentifierName))
 
                Return True
            Finally
                pooled.Free()
            End Try
        End Function
 
        Friend ReadOnly Property MessageProvider As CommonMessageProvider
            Get
                Return Me.Compilation.MessageProvider
            End Get
        End Property
 
        Private Shared Function GetLocalsForBinding(
            locals As ImmutableArray(Of LocalSymbol),
            displayClassVariableNamesInOrder As ImmutableArray(Of String),
            displayClassVariables As ImmutableDictionary(Of String, DisplayClassVariable)) As ImmutableArray(Of LocalSymbol)
 
            Dim builder = ArrayBuilder(Of LocalSymbol).GetInstance()
            For Each local In locals
                Dim name = local.Name
                If name IsNot Nothing AndAlso Not IsGeneratedLocalName(name) Then
                    builder.Add(local)
                End If
            Next
 
            For Each variableName In displayClassVariableNamesInOrder
                Dim variable = displayClassVariables(variableName)
                Select Case variable.Kind
                    Case DisplayClassVariableKind.Local,
                         DisplayClassVariableKind.Parameter
                        Debug.Assert(Not IsGeneratedLocalName(variable.Name)) ' Established by GetDisplayClassVariables.
                        builder.Add(New EEDisplayClassFieldLocalSymbol(variable))
                End Select
            Next
 
            Return builder.ToImmutableAndFree()
        End Function
 
        Private Shared Function GetSourceMethodParametersInOrder(
            method As MethodSymbol,
            sourceMethod As MethodSymbol) As ImmutableArray(Of String)
 
            Dim containingType = method.ContainingType
            Dim isIteratorOrAsyncMethod = containingType.IsClosureOrStateMachineType() AndAlso containingType.IsStateMachineType()
 
            Dim parameterNamesInOrder = ArrayBuilder(Of String).GetInstance()
 
            ' For version before .NET 4.5, we cannot find the sourceMethod properly:
            ' The source method coincides with the original method in the case.
            ' Here, we have to get parameters from the containingType.
            ' This does not guarantee the proper order of parameters.
            If isIteratorOrAsyncMethod AndAlso method = sourceMethod Then
                Debug.Assert(containingType.IsClosureOrStateMachineType())
 
                For Each member In containingType.GetMembers
                    ' All iterator and async state machine fields in VB have mangled names.
                    ' The ones beginning with "$VB$Local_" are the hoisted parameters.
                    If member.Kind <> SymbolKind.Field Then
                        Continue For
                    End If
 
                    Dim field = DirectCast(member, FieldSymbol)
                    Dim fieldName = field.Name
                    Dim parameterName As String = Nothing
                    If GeneratedNameParser.TryParseHoistedUserVariableName(fieldName, parameterName) Then
                        parameterNamesInOrder.Add(parameterName)
                    End If
                Next
            Else
                If sourceMethod <> Nothing Then
                    For Each parameter In sourceMethod.Parameters
                        parameterNamesInOrder.Add(parameter.Name)
                    Next
                End If
            End If
 
            Return parameterNamesInOrder.ToImmutableAndFree()
        End Function
 
        ''' <summary>
        ''' Return a mapping of captured variables (parameters, locals, and "Me") to locals.
        ''' The mapping is needed to expose the original local identifiers (those from source)
        ''' in the binder.
        ''' </summary>
        Private Shared Sub GetDisplayClassVariables(
            method As MethodSymbol,
            sourceMethod As MethodSymbol,
            locals As ImmutableArray(Of LocalSymbol),
            inScopeHoistedLocalSlots As ImmutableSortedSet(Of Integer),
            sourceMethodParametersInOrder As ImmutableArray(Of String),
            <Out> ByRef displayClassVariableNamesInOrder As ImmutableArray(Of String),
            <Out> ByRef displayClassVariables As ImmutableDictionary(Of String, DisplayClassVariable))
 
            ' Calculate the shortest paths from locals to instances of display classes.
            ' There should not be two instances of the same display class immediately
            ' within any particular method.
            Dim displayClassInstances = ArrayBuilder(Of DisplayClassInstanceAndFields).GetInstance()
 
            For Each parameter As ParameterSymbol In method.Parameters
                If GeneratedNameParser.GetKind(parameter.Name) = GeneratedNameKind.TransparentIdentifier Then
                    Dim instance As New DisplayClassInstanceFromParameter(parameter)
                    displayClassInstances.Add(New DisplayClassInstanceAndFields(instance))
                End If
            Next
 
            If method.ContainingType.IsClosureOrStateMachineType() Then
                If Not method.IsShared Then
                    ' Add "Me" display class instance.
                    Dim instance As New DisplayClassInstanceFromParameter(method.MeParameter)
                    displayClassInstances.Add(New DisplayClassInstanceAndFields(instance))
                End If
            End If
 
            Dim displayClassTypes = PooledHashSet(Of TypeSymbol).GetInstance()
            For Each instance In displayClassInstances
                displayClassTypes.Add(instance.Instance.Type)
            Next
 
            ' Find any additional display class instances.
            GetAdditionalDisplayClassInstances(displayClassTypes, displayClassInstances, startIndex:=0)
 
            ' Add any display class instances from locals (these will contain any hoisted locals).
            ' Locals are only added after finding all display class instances reachable from
            ' parameters because locals may be null (temporary locals in async state machine
            ' for instance) so we prefer parameters to locals.
            Dim startIndex = displayClassInstances.Count
            For Each local As LocalSymbol In locals
                Dim localName = local.Name
                If localName IsNot Nothing AndAlso IsDisplayClassInstanceLocalName(localName) Then
                    If displayClassTypes.Add(local.Type) Then
                        Dim instance As New DisplayClassInstanceFromLocal(DirectCast(local, EELocalSymbol))
                        displayClassInstances.Add(New DisplayClassInstanceAndFields(instance))
                    End If
                End If
            Next
            GetAdditionalDisplayClassInstances(displayClassTypes, displayClassInstances, startIndex)
 
            displayClassTypes.Free()
 
            If displayClassInstances.Any() Then
                ' The locals are the set of all fields from the display classes.
                Dim displayClassVariableNamesInOrderBuilder = ArrayBuilder(Of String).GetInstance()
                Dim displayClassVariablesBuilder = PooledDictionary(Of String, DisplayClassVariable).GetInstance()
 
                Dim parameterNames = PooledHashSet(Of String).GetInstance()
                For Each p In sourceMethodParametersInOrder
                    parameterNames.Add(p)
                Next
 
                For Each instance In displayClassInstances
                    GetDisplayClassVariables(
                        displayClassVariableNamesInOrderBuilder,
                        displayClassVariablesBuilder,
                        parameterNames,
                        inScopeHoistedLocalSlots,
                        instance)
                Next
 
                displayClassVariableNamesInOrder = displayClassVariableNamesInOrderBuilder.ToImmutableAndFree()
                displayClassVariables = displayClassVariablesBuilder.ToImmutableDictionary()
                displayClassVariablesBuilder.Free()
                parameterNames.Free()
            Else
                displayClassVariableNamesInOrder = ImmutableArray(Of String).Empty
                displayClassVariables = ImmutableDictionary(Of String, DisplayClassVariable).Empty
            End If
 
            displayClassInstances.Free()
        End Sub
 
        Private Shared Function IsHoistedMeFieldName(fieldName As String) As Boolean
            Return fieldName.Equals(GeneratedNameConstants.HoistedMeName, StringComparison.Ordinal)
        End Function
 
        Private Shared Function IsLambdaMethodName(methodName As String) As Boolean
            Return methodName.StartsWith(GeneratedNameConstants.LambdaMethodNamePrefix, StringComparison.Ordinal)
        End Function
 
        ''' <summary>
        ''' Test whether the name is for a local holding an instance of a display class.
        ''' </summary>
        Private Shared Function IsDisplayClassInstanceLocalName(name As String) As Boolean
            Debug.Assert(name IsNot Nothing) ' Verified by caller.
            Return name.StartsWith(GeneratedNameConstants.ClosureVariablePrefix, StringComparison.Ordinal)
        End Function
 
        ''' <summary>
        ''' Test whether the name is for a field holding an instance of a display class
        ''' (i.e. a hoisted display class instance local).
        ''' </summary>
        Private Shared Function IsDisplayClassInstanceFieldName(name As String) As Boolean
            Debug.Assert(name IsNot Nothing) ' Verified by caller.
            Return name.StartsWith(GeneratedNameConstants.HoistedSpecialVariablePrefix & GeneratedNameConstants.ClosureVariablePrefix, StringComparison.Ordinal) OrElse
                name.StartsWith(GeneratedNameConstants.StateMachineHoistedUserVariableOrDisplayClassPrefix & GeneratedNameConstants.ClosureVariablePrefix, StringComparison.Ordinal) OrElse
                name.StartsWith(GeneratedNameConstants.HoistedSpecialVariablePrefix & GeneratedNameConstants.DisplayClassPrefix, StringComparison.Ordinal) ' Async lambda case
        End Function
 
        Private Shared Function IsTransparentIdentifierField(field As FieldSymbol) As Boolean
            Dim fieldName = field.Name
 
            Dim unmangledName As String = Nothing
            If GeneratedNameParser.TryParseHoistedUserVariableName(fieldName, unmangledName) Then
                fieldName = unmangledName
            ElseIf field.IsAnonymousTypeField(unmangledName) Then
                fieldName = unmangledName
            End If
 
            Return GeneratedNameParser.GetKind(fieldName) = GeneratedNameKind.TransparentIdentifier
        End Function
 
        Private Shared Function IsGeneratedLocalName(name As String) As Boolean
            Debug.Assert(name IsNot Nothing) ' Verified by caller.
            ' If a local's name contains "$", then it is a generated local.
            Return name.IndexOf("$"c) >= 0
        End Function
 
        Private Shared Function GetLocalResultFlags(local As LocalSymbol) As DkmClrCompilationResultFlags
            Debug.Assert(local.IsConst OrElse Not local.IsReadOnly, "Didn't expect user-referenceable read-only local.")
            Return If(
                local.IsConst,
                DkmClrCompilationResultFlags.ReadOnlyResult,
                DkmClrCompilationResultFlags.None)
        End Function
 
        Private Shared Sub GetAdditionalDisplayClassInstances(
            displayClassTypes As HashSet(Of TypeSymbol),
            displayClassInstances As ArrayBuilder(Of DisplayClassInstanceAndFields),
            startIndex As Integer)
 
            ' Find any additional display class instances breadth first.
            Dim i = startIndex
            While i < displayClassInstances.Count()
                GetDisplayClassInstances(displayClassTypes, displayClassInstances, displayClassInstances(i))
                i += 1
            End While
        End Sub
 
        Private Shared Sub GetDisplayClassInstances(
            displayClassTypes As HashSet(Of TypeSymbol),
            displayClassInstances As ArrayBuilder(Of DisplayClassInstanceAndFields),
            instance As DisplayClassInstanceAndFields)
 
            ' Display class instance.  The display class fields are variables.
            For Each member In instance.Type.GetMembers()
                If member.Kind <> SymbolKind.Field Then
                    Continue For
                End If
                Dim field = DirectCast(member, FieldSymbol)
                Dim fieldType = field.Type
                Dim fieldName As String = field.Name
                If IsDisplayClassInstanceFieldName(fieldName) OrElse
                    IsTransparentIdentifierField(field) Then
                    Debug.Assert(Not field.IsShared)
                    ' A local that is itself a display class instance.
                    If displayClassTypes.Add(fieldType) Then
                        Dim other = instance.FromField(field)
                        displayClassInstances.Add(other)
                    End If
                End If
            Next
        End Sub
 
        Private Shared Sub GetDisplayClassVariables(
            displayClassVariableNamesInOrder As ArrayBuilder(Of String),
            displayClassVariablesBuilder As Dictionary(Of String, DisplayClassVariable),
            parameterNames As HashSet(Of String),
            inScopeHoistedLocalSlots As ImmutableSortedSet(Of Integer),
            instance As DisplayClassInstanceAndFields)
 
            ' Display class instance.  The display class fields are variables.
            For Each member In instance.Type.GetMembers()
                If member.Kind <> SymbolKind.Field Then
                    Continue For
                End If
 
                Dim field = DirectCast(member, FieldSymbol)
                Dim fieldName = field.Name
 
                Dim unmangledName As String = Nothing
                If field.IsAnonymousTypeField(unmangledName) Then
                    fieldName = unmangledName
                End If
 
                Dim variableKind As DisplayClassVariableKind
                Dim variableName As String
                Dim hoistedLocalName As String = Nothing
                Dim hoistedLocalSlotIndex As Integer = 0
 
                If fieldName.StartsWith(GeneratedNameConstants.HoistedUserVariablePrefix, StringComparison.Ordinal) Then
                    Debug.Assert(Not field.IsShared)
                    variableKind = DisplayClassVariableKind.Local
                    variableName = fieldName.Substring(GeneratedNameConstants.HoistedUserVariablePrefix.Length)
                ElseIf fieldName.StartsWith(GeneratedNameConstants.HoistedSpecialVariablePrefix, StringComparison.Ordinal) Then
                    Debug.Assert(Not field.IsShared)
                    variableKind = DisplayClassVariableKind.Local
                    variableName = fieldName.Substring(GeneratedNameConstants.HoistedSpecialVariablePrefix.Length)
                ElseIf GeneratedNameParser.TryParseStateMachineHoistedUserVariableOrDisplayClassName(fieldName, hoistedLocalName, hoistedLocalSlotIndex) Then
                    Debug.Assert(Not field.IsShared)
 
                    If Not inScopeHoistedLocalSlots.Contains(hoistedLocalSlotIndex) Then
                        Continue For
                    End If
 
                    variableKind = DisplayClassVariableKind.Local
                    variableName = hoistedLocalName
 
                ElseIf IsHoistedMeFieldName(fieldName) Then
                    Debug.Assert(Not field.IsShared)
                    ' A reference to "Me".
                    variableKind = DisplayClassVariableKind.Me
                    variableName = fieldName ' As in C#, we retain the mangled name.  It shouldn't be used, other than as a dictionary key.
                ElseIf fieldName.StartsWith(GeneratedNameConstants.LambdaCacheFieldPrefix, StringComparison.Ordinal) Then
                    Continue For
                ElseIf GeneratedNameParser.GetKind(fieldName) = GeneratedNameKind.TransparentIdentifier Then
                    ' A transparent identifier (field) in an anonymous type synthesized for a transparent identifier.
                    Debug.Assert(Not field.IsShared)
                    Continue For
                Else
                    variableKind = DisplayClassVariableKind.Local
                    variableName = fieldName
                End If
 
                If variableKind <> DisplayClassVariableKind.Me AndAlso IsGeneratedLocalName(variableName) Then
                    Continue For
                End If
 
                If variableKind = DisplayClassVariableKind.Local AndAlso parameterNames.Contains(variableName) Then
                    variableKind = DisplayClassVariableKind.Parameter
                End If
 
                Dim displayClassVariable As DisplayClassVariable = Nothing
                If displayClassVariablesBuilder.TryGetValue(variableName, displayClassVariable) Then
                    ' Only expecting duplicates for async state machine
                    ' fields (that should be at the top-level).
                    Debug.Assert(displayClassVariable.DisplayClassFields.Count() = 1)
 
                    If Not instance.Fields.Any() Then
                        ' Prefer parameters over locals.
                        Debug.Assert(TypeOf instance.Instance Is DisplayClassInstance)
                    Else
                        Debug.Assert(instance.Fields.Count() >= 1) ' greater depth
                        ' There are two ways names could collide:
                        '   1) hoisted state machine locals in different scopes
                        '   2) hoisted state machine parameters that are also captured by lambdas
                        ' The former should be impossible since we dropped out-of-scope hoisted
                        ' locals above.  We assert that we are seeing the latter.
                        Debug.Assert((variableKind = DisplayClassVariableKind.Parameter) OrElse
                        (variableKind = DisplayClassVariableKind.Me))
 
                        If variableKind = DisplayClassVariableKind.Parameter AndAlso GeneratedNameParser.GetKind(instance.Type.Name) = GeneratedNameKind.LambdaDisplayClass Then
                            displayClassVariablesBuilder(variableName) = instance.ToVariable(variableName, variableKind, field)
                        End If
                    End If
 
                Else
                    displayClassVariableNamesInOrder.Add(variableName)
                    displayClassVariablesBuilder.Add(variableName, instance.ToVariable(variableName, variableKind, field))
                End If
            Next
        End Sub
 
        ''' <summary>
        ''' Identifies the method in which binding should occur.
        ''' </summary>
        ''' <param name="candidateSubstitutedSourceMethod">
        ''' The symbol of the method that is currently on top of the callstack, with
        ''' EE type parameters substituted in place of the original type parameters.
        ''' </param>
        ''' <param name="sourceMethodMustBeInstance">
        ''' True if "Me" is available via a display class in the current context
        ''' </param>
        ''' <returns>
        ''' If <paramref name="candidateSubstitutedSourceMethod"/> is compiler-generated,
        ''' then we will attempt to determine which user-derived method caused it to be
        ''' generated.  For example, if <paramref name="candidateSubstitutedSourceMethod"/>
        ''' is a state machine MoveNext method, then we will try to find the iterator or
        ''' async method for which it was generated.  if we are able to find the original
        ''' method, then we will substitute in the EE type parameters.  Otherwise, we will
        ''' return <paramref name="candidateSubstitutedSourceMethod"/>.
        ''' </returns>
        ''' <remarks>
        ''' In the event that the original method is overloaded, we may not be able to determine
        ''' which overload actually corresponds to the state machine.  In particular, we do not
        ''' have information about the signature of the original method (i.e. number of parameters,
        ''' parameter types and ref-kinds, return type).  However, we conjecture that this
        ''' level of uncertainty is acceptable, since parameters are managed by a separate binder
        ''' in the synthesized binder chain and we have enough information to check the other method
        ''' properties that are used during binding (e.g. static-ness, generic arity, type parameter
        ''' constraints).
        ''' </remarks>
        Friend Shared Function GetSubstitutedSourceMethod(
            candidateSubstitutedSourceMethod As MethodSymbol,
            sourceMethodMustBeInstance As Boolean) As MethodSymbol
 
            Dim candidateSubstitutedSourceType = candidateSubstitutedSourceMethod.ContainingType
            Dim candidateSourceTypeName = candidateSubstitutedSourceType.Name
 
            Dim desiredMethodName As String = Nothing
            If IsLambdaMethodName(candidateSubstitutedSourceMethod.Name) OrElse
               GeneratedNameParser.TryParseStateMachineTypeName(candidateSourceTypeName, desiredMethodName) Then
 
                ' We could be in the MoveNext method of an async lambda.  If that is the case, we can't 
                ' figure out desiredMethodName by unmangling the name.
                If desiredMethodName IsNot Nothing AndAlso IsLambdaMethodName(desiredMethodName) Then
                    desiredMethodName = Nothing
                    Dim containing = candidateSubstitutedSourceType.ContainingType
                    Debug.Assert(containing IsNot Nothing)
                    If containing.IsClosureType() Then
                        candidateSubstitutedSourceType = containing
                        sourceMethodMustBeInstance = candidateSubstitutedSourceType.MemberNames.Contains(GeneratedNameConstants.HoistedMeName, StringComparer.Ordinal)
                    End If
                End If
 
                Dim desiredTypeParameters = candidateSubstitutedSourceType.OriginalDefinition.TypeParameters
 
                ' Type containing the original iterator, async, or lambda-containing method.
                Dim substitutedSourceType = GetNonClosureOrStateMachineContainer(candidateSubstitutedSourceType)
 
                For Each candidateMethod In substitutedSourceType.GetMembers().OfType(Of MethodSymbol)()
                    If IsViableSourceMethod(candidateMethod, desiredMethodName, desiredTypeParameters, sourceMethodMustBeInstance) Then
                        Return If(desiredTypeParameters.Length = 0,
                            candidateMethod,
                            candidateMethod.Construct(candidateSubstitutedSourceType.TypeArgumentsNoUseSiteDiagnostics))
                    End If
                Next
 
                Debug.Assert(False, String.Format("Why didn't we find a substituted source method for {0}?", candidateSubstitutedSourceMethod))
            End If
 
            Return candidateSubstitutedSourceMethod
        End Function
 
        Private Shared Function GetNonClosureOrStateMachineContainer(type As NamedTypeSymbol) As NamedTypeSymbol
            ' 1) Display class and state machine types are always nested within the types
            '    that use them so that they can access private members of those types).
            ' 2) The native compiler used to produce nested display classes for nested lambdas,
            '    so we may have to walk out more than one level.
            While type.IsClosureOrStateMachineType()
                type = type.ContainingType
            End While
            Debug.Assert(type IsNot Nothing)
 
            Return type
        End Function
 
        Private Shared Function IsViableSourceMethod(
            candidateMethod As MethodSymbol,
            desiredMethodName As String,
            desiredTypeParameters As ImmutableArray(Of TypeParameterSymbol),
            desiredMethodMustBeInstance As Boolean) As Boolean
 
            Return _
                Not candidateMethod.IsMustOverride AndAlso
                (Not (desiredMethodMustBeInstance AndAlso candidateMethod.IsShared)) AndAlso
                (desiredMethodName Is Nothing OrElse desiredMethodName = candidateMethod.Name) AndAlso
                HasDesiredConstraints(candidateMethod, desiredTypeParameters)
        End Function
 
        Private Shared Function HasDesiredConstraints(candidateMethod As MethodSymbol, desiredTypeParameters As ImmutableArray(Of TypeParameterSymbol)) As Boolean
            Dim arity = candidateMethod.Arity
            If arity <> desiredTypeParameters.Length Then
                Return False
            ElseIf arity = 0 Then
                Return True
            End If
 
            Dim indexedTypeParameters = IndexedTypeParameterSymbol.Take(arity).As(Of TypeSymbol)
 
            ' NOTE: Can't seem to construct a type map for just the method type parameters,
            ' so we also specify a trivial map for the type parameters of the (immediately)
            ' containing type.
            Dim candidateMethodDefinition As MethodSymbol = candidateMethod.OriginalDefinition
            Dim sourceTypeTypeParameters As ImmutableArray(Of TypeParameterSymbol) = candidateMethodDefinition.ContainingType.TypeParameters
            Dim candidateTypeMap = TypeSubstitution.Create(
                candidateMethodDefinition,
                sourceTypeTypeParameters.Concat(candidateMethodDefinition.TypeParameters),
                sourceTypeTypeParameters.As(Of TypeSymbol).Concat(indexedTypeParameters))
            Debug.Assert(candidateTypeMap.PairsIncludingParent.Length = arity)
 
            Dim desiredTypeMap = TypeSubstitution.Create(
                desiredTypeParameters(0).ContainingSymbol,
                desiredTypeParameters,
                indexedTypeParameters)
            Debug.Assert(desiredTypeMap.PairsIncludingParent.Length = arity)
 
            For i = 0 To arity - 1
                If Not MethodSignatureComparer.HaveSameConstraints(candidateMethodDefinition.TypeParameters(i), candidateTypeMap, desiredTypeParameters(i), desiredTypeMap) Then
                    Return False
                End If
            Next
 
            Return True
        End Function
 
        <DebuggerDisplay("{GetDebuggerDisplay(), nq}")>
        Private Structure DisplayClassInstanceAndFields
            Friend ReadOnly Instance As DisplayClassInstance
            Friend ReadOnly Fields As ConsList(Of FieldSymbol)
 
            Friend Sub New(instance As DisplayClassInstance)
                MyClass.New(instance, ConsList(Of FieldSymbol).Empty)
                Debug.Assert(instance.Type.IsClosureOrStateMachineType() OrElse
                             GeneratedNameParser.GetKind(instance.Type.Name) = GeneratedNameKind.AnonymousType)
            End Sub
 
            Private Sub New(instance As DisplayClassInstance, fields As ConsList(Of FieldSymbol))
                Me.Instance = instance
                Me.Fields = fields
            End Sub
 
            Friend ReadOnly Property Type As TypeSymbol
                Get
                    Return If(Me.Fields.Any(), Me.Fields.Head.Type, Me.Instance.Type)
                End Get
            End Property
 
            Friend ReadOnly Property Depth As Integer
                Get
                    Return Me.Fields.Count()
                End Get
            End Property
 
            Friend Function FromField(field As FieldSymbol) As DisplayClassInstanceAndFields
                Debug.Assert(field.Type.IsClosureOrStateMachineType() OrElse
                             GeneratedNameParser.GetKind(field.Type.Name) = GeneratedNameKind.AnonymousType)
                Return New DisplayClassInstanceAndFields(Me.Instance, Me.Fields.Prepend(field))
            End Function
 
            Friend Function ToVariable(name As String, kind As DisplayClassVariableKind, field As FieldSymbol) As DisplayClassVariable
                Return New DisplayClassVariable(name, kind, Me.Instance, Me.Fields.Prepend(field))
            End Function
 
            Private Function GetDebuggerDisplay() As String
                Return Instance.GetDebuggerDisplay(Fields)
            End Function
        End Structure
    End Class
End Namespace