File: Lowering\AsyncRewriter\AsyncRewriter.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.Generic
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Partial Friend NotInheritable Class AsyncRewriter
        Inherits StateMachineRewriter(Of CapturedSymbolOrExpression)
 
        Private ReadOnly _binder As Binder
        Private ReadOnly _lookupOptions As LookupOptions
        Private ReadOnly _asyncMethodKind As AsyncMethodKind
        Private ReadOnly _builderType As NamedTypeSymbol
        Private ReadOnly _resultType As TypeSymbol
 
        Private _builderField As FieldSymbol
        Private _lastExpressionCaptureNumber As Integer
 
        Public Sub New(body As BoundStatement,
                       method As MethodSymbol,
                       stateMachineType As AsyncStateMachine,
                       stateMachineStateDebugInfoBuilder As ArrayBuilder(Of StateMachineStateDebugInfo),
                       slotAllocatorOpt As VariableSlotAllocator,
                       asyncKind As AsyncMethodKind,
                       compilationState As TypeCompilationState,
                       diagnostics As BindingDiagnosticBag)
 
            MyBase.New(body, method, stateMachineType, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, compilationState, diagnostics)
 
            Me._binder = CreateMethodBinder(method)
            Me._lookupOptions = LookupOptions.AllMethodsOfAnyArity Or LookupOptions.IgnoreExtensionMethods Or LookupOptions.NoBaseClassLookup
 
            If compilationState.ModuleBuilderOpt.IgnoreAccessibility Then
                Me._binder = New IgnoreAccessibilityBinder(Me._binder)
                Me._lookupOptions = Me._lookupOptions Or LookupOptions.IgnoreAccessibility
            End If
 
            Debug.Assert(asyncKind <> AsyncMethodKind.None)
            Debug.Assert(asyncKind = GetAsyncMethodKind(method))
            Me._asyncMethodKind = asyncKind
 
            Select Case Me._asyncMethodKind
                Case AsyncMethodKind.Sub
                    Me._resultType = Me.F.SpecialType(SpecialType.System_Void)
                    Me._builderType = Me.F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncVoidMethodBuilder)
 
                Case AsyncMethodKind.TaskFunction
                    Me._resultType = Me.F.SpecialType(SpecialType.System_Void)
                    Me._builderType = Me.F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder)
 
                Case AsyncMethodKind.GenericTaskFunction
                    Me._resultType = DirectCast(Me.Method.ReturnType, NamedTypeSymbol).TypeArgumentsNoUseSiteDiagnostics().Single().InternalSubstituteTypeParameters(Me.TypeMap).Type
                    Me._builderType = Me.F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T).Construct(Me._resultType)
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(Me._asyncMethodKind)
            End Select
        End Sub
 
        ''' <summary>
        ''' Rewrite an async method into a state machine class.
        ''' </summary>
        Friend Overloads Shared Function Rewrite(body As BoundBlock,
                                                 method As MethodSymbol,
                                                 methodOrdinal As Integer,
                                                 stateMachineStateDebugInfoBuilder As ArrayBuilder(Of StateMachineStateDebugInfo),
                                                 slotAllocatorOpt As VariableSlotAllocator,
                                                 compilationState As TypeCompilationState,
                                                 diagnostics As BindingDiagnosticBag,
                                                 <Out> ByRef stateMachineType As AsyncStateMachine) As BoundBlock
 
            If body.HasErrors Then
                Return body
            End If
 
            Dim asyncMethodKind As AsyncMethodKind = GetAsyncMethodKind(method)
            If asyncMethodKind = AsyncMethodKind.None Then
                Return body
            End If
 
            ' The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class.
            Dim kind = If(compilationState.Compilation.Options.EnableEditAndContinue, TypeKind.Class, TypeKind.Struct)
 
            stateMachineType = New AsyncStateMachine(slotAllocatorOpt, compilationState, method, methodOrdinal, kind)
 
            compilationState.ModuleBuilderOpt.CompilationState.SetStateMachineType(method, stateMachineType)
 
            Dim rewriter As New AsyncRewriter(body, method, stateMachineType, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, asyncMethodKind, compilationState, diagnostics)
 
            ' check if we have all the types we need
            If rewriter.EnsureAllSymbolsAndSignature() Then
                Return body
            End If
 
            Return rewriter.Rewrite()
        End Function
 
        Private Shared Function CreateMethodBinder(method As MethodSymbol) As Binder
            ' For source method symbol create a binder
            Dim sourceMethod = TryCast(method, SourceMethodSymbol)
            If sourceMethod IsNot Nothing Then
                Return BinderBuilder.CreateBinderForMethodBody(DirectCast(sourceMethod.ContainingModule, SourceModuleSymbol),
                                                               sourceMethod.ContainingType.Locations(0).PossiblyEmbeddedOrMySourceTree(),
                                                               sourceMethod)
            End If
 
            ' For all other symbols we assume that it should be a synthesized method 
            ' placed inside source named type or in one of its nested types
            Debug.Assert((TypeOf method Is SynthesizedLambdaMethod) OrElse (TypeOf method Is SynthesizedInteractiveInitializerMethod))
            Dim containingType As NamedTypeSymbol = method.ContainingType
            While containingType IsNot Nothing
                Dim syntaxTree = containingType.Locations.FirstOrDefault()?.SourceTree
                If syntaxTree IsNot Nothing Then
                    Return BinderBuilder.CreateBinderForType(
                        DirectCast(containingType.ContainingModule, SourceModuleSymbol),
                        syntaxTree,
                        containingType)
                End If
                containingType = containingType.ContainingType
            End While
 
            Throw ExceptionUtilities.Unreachable
        End Function
 
        Protected Overrides Sub GenerateControlFields()
            ' The fields are initialized from async method, so they need to be public:
            Me.StateField = Me.F.StateMachineField(Me.F.SpecialType(SpecialType.System_Int32), Me.Method, GeneratedNames.MakeStateMachineStateFieldName(), Accessibility.Public)
            Me._builderField = Me.F.StateMachineField(Me._builderType, Me.Method, GeneratedNames.MakeStateMachineBuilderFieldName(), Accessibility.Public)
        End Sub
 
        Protected Overrides Sub InitializeStateMachine(bodyBuilder As ArrayBuilder(Of BoundStatement), frameType As NamedTypeSymbol, stateMachineLocal As LocalSymbol)
            If frameType.TypeKind = TypeKind.Class Then
                ' Dim stateMachineLocal = new AsyncImplementationClass()
                bodyBuilder.Add(
                    Me.F.Assignment(
                        Me.F.Local(stateMachineLocal, True),
                        Me.F.[New](StateMachineType.Constructor.AsMember(frameType))))
            Else
                ' STAT:   localStateMachine = Nothing ' Initialization
                bodyBuilder.Add(
                    Me.F.Assignment(
                        Me.F.Local(stateMachineLocal, True),
                        Me.F.Null(stateMachineLocal.Type)))
            End If
        End Sub
 
        Protected Overrides ReadOnly Property PreserveInitialParameterValues As Boolean
            Get
                Return False
            End Get
        End Property
 
        Friend Overrides ReadOnly Property TypeMap As TypeSubstitution
            Get
                Return Me.StateMachineType.TypeSubstitution
            End Get
        End Property
 
        Protected Overrides Sub GenerateMethodImplementations()
            ' Add IAsyncStateMachine.MoveNext()
            GenerateMoveNext(
                Me.OpenMoveNextMethodImplementation(
                    WellKnownMember.System_Runtime_CompilerServices_IAsyncStateMachine_MoveNext,
                    Accessibility.Friend))
 
            'Add IAsyncStateMachine.SetStateMachine()
            Me.OpenMethodImplementation(
                    WellKnownMember.System_Runtime_CompilerServices_IAsyncStateMachine_SetStateMachine,
                    "System.Runtime.CompilerServices.IAsyncStateMachine.SetStateMachine",
                    Accessibility.Private,
                    hasMethodBodyDependency:=False)
 
            ' SetStateMachine is used to initialize the underlying AsyncMethodBuilder's reference to the boxed copy of the state machine.
            ' If the state machine is a class there is no copy made and thus the initialization is not necessary. 
            ' In fact it is an error to reinitialize the builder since it already is initialized.
            If Me.F.CurrentType.TypeKind = TypeKind.Class Then
                Me.F.CloseMethod(F.Return())
            Else
                Me.CloseMethod(
                Me.F.Block(
                    Me.F.ExpressionStatement(
                        Me.GenerateMethodCall(
                            Me.F.Field(Me.F.Me(), Me._builderField, False),
                            Me._builderType,
                            "SetStateMachine",
                            {Me.F.Parameter(Me.F.CurrentMethod.Parameters(0))})),
                    Me.F.Return()))
            End If
 
            ' Constructor
            If StateMachineType.TypeKind = TypeKind.Class Then
                Me.F.CurrentMethod = StateMachineType.Constructor
                Me.F.CloseMethod(F.Block(ImmutableArray.Create(F.BaseInitialization(), F.Return())))
            End If
 
        End Sub
 
        Protected Overrides Function GenerateStateMachineCreation(stateMachineVariable As LocalSymbol, frameType As NamedTypeSymbol) As BoundStatement
            Dim bodyBuilder = ArrayBuilder(Of BoundStatement).GetInstance()
 
            ' STAT:   localStateMachine.$stateField = NotStartedStateMachine
            Dim stateFieldAsLValue As BoundExpression =
                Me.F.Field(
                    Me.F.Local(stateMachineVariable, True),
                    Me.StateField.AsMember(frameType), True)
 
            bodyBuilder.Add(
                Me.F.Assignment(
                    stateFieldAsLValue,
                    Me.F.Literal(StateMachineState.NotStartedOrRunningState)))
 
            ' STAT:   localStateMachine.$builder = System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of typeArgs).Create()
            Dim constructedBuilderField As FieldSymbol = Me._builderField.AsMember(frameType)
            Dim builderFieldAsLValue As BoundExpression = Me.F.Field(Me.F.Local(stateMachineVariable, True), constructedBuilderField, True)
 
            ' All properties and methods will be searched in this type
            Dim builderType As TypeSymbol = constructedBuilderField.Type
 
            bodyBuilder.Add(
                Me.F.Assignment(
                    builderFieldAsLValue,
                    Me.GenerateMethodCall(Nothing, builderType, "Create")))
 
            ' STAT:   localStateMachine.$builder.Start(ref localStateMachine) -- binding to the method AsyncTaskMethodBuilder<typeArgs>.Start(...)
            bodyBuilder.Add(
                Me.F.ExpressionStatement(
                    Me.GenerateMethodCall(
                        builderFieldAsLValue,
                        builderType,
                        "Start",
                        ImmutableArray.Create(Of TypeSymbol)(frameType),
                        Me.F.Local(stateMachineVariable, True))))
 
            ' STAT:   Return
            '  or
            ' STAT:   Return localStateMachine.$builder.Task
            bodyBuilder.Add(
                If(Me._asyncMethodKind = AsyncMethodKind.[Sub],
                   Me.F.Return(),
                   Me.F.Return(Me.GeneratePropertyGet(builderFieldAsLValue, builderType, "Task"))))
 
            Return RewriteBodyIfNeeded(Me.F.Block(ImmutableArray(Of LocalSymbol).Empty, bodyBuilder.ToImmutableAndFree()), Me.F.TopLevelMethod, Me.Method)
        End Function
 
        Private Sub GenerateMoveNext(moveNextMethod As MethodSymbol)
            Dim rewriter = New AsyncMethodToClassRewriter(
                method:=Method,
                F:=F,
                state:=StateField,
                builder:=_builderField,
                hoistedVariables:=hoistedVariables,
                nonReusableLocalProxies:=nonReusableLocalProxies,
                StateDebugInfoBuilder,
                slotAllocatorOpt:=SlotAllocatorOpt,
                owner:=Me,
                diagnostics:=Diagnostics)
 
            rewriter.GenerateMoveNext(Body, moveNextMethod)
        End Sub
 
        Friend Overrides Function RewriteBodyIfNeeded(body As BoundStatement, topMethod As MethodSymbol, currentMethod As MethodSymbol) As BoundStatement
            If body.HasErrors Then
                Return body
            End If
 
            Dim rewrittenNodes As HashSet(Of BoundNode) = Nothing
            Dim hasLambdas As Boolean = False
            Dim symbolsCapturedWithoutCtor As ISet(Of Symbol) = Nothing
 
            If body.Kind <> BoundKind.Block Then
                body = Me.F.Block(body)
            End If
 
            Const rewritingFlags As LocalRewriter.RewritingFlags =
                LocalRewriter.RewritingFlags.AllowSequencePoints Or
                LocalRewriter.RewritingFlags.AllowEndOfMethodReturnWithExpression Or
                LocalRewriter.RewritingFlags.AllowCatchWithErrorLineNumberReference
 
            Return LocalRewriter.Rewrite(DirectCast(body, BoundBlock),
                                         topMethod,
                                         F.CompilationState,
                                         previousSubmissionFields:=Nothing,
                                         diagnostics:=Me.Diagnostics,
                                         rewrittenNodes:=rewrittenNodes,
                                         hasLambdas:=hasLambdas,
                                         symbolsCapturedWithoutCopyCtor:=symbolsCapturedWithoutCtor,
                                         flags:=rewritingFlags,
                                         instrumenterOpt:=Nothing, ' Do not instrument anything during this rewrite 
                                         currentMethod:=currentMethod)
        End Function
 
        ''' <returns>
        ''' Returns true if any members that we need are missing or have use-site errors.
        ''' </returns>
        Friend Overrides Function EnsureAllSymbolsAndSignature() As Boolean
            If MyBase.EnsureAllSymbolsAndSignature Then
                Return True
            End If
 
            Dim bag = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=Me.Diagnostics.AccumulatesDependencies)
 
            EnsureSpecialType(SpecialType.System_Object, bag)
            EnsureSpecialType(SpecialType.System_Void, bag)
            EnsureSpecialType(SpecialType.System_ValueType, bag)
 
            EnsureWellKnownType(WellKnownType.System_Runtime_CompilerServices_IAsyncStateMachine, bag)
            EnsureWellKnownMember(WellKnownMember.System_Runtime_CompilerServices_IAsyncStateMachine_MoveNext, bag)
            EnsureWellKnownMember(WellKnownMember.System_Runtime_CompilerServices_IAsyncStateMachine_SetStateMachine, bag)
 
            ' We don't ensure those types because we don't know if they will be actually used, 
            ' use-site errors will be reported later if any of those types is actually requested
            '
            ' EnsureSpecialType(SpecialType.System_Boolean, hasErrors)
            ' EnsureWellKnownType(WellKnownType.System_Runtime_CompilerServices_ICriticalNotifyCompletion, hasErrors)
            ' EnsureWellKnownType(WellKnownType.System_Runtime_CompilerServices_INotifyCompletion, hasErrors)
 
            ' NOTE: We don't ensure DebuggerStepThroughAttribute, it is just not emitted if not found
            ' EnsureWellKnownMember(Of MethodSymbol)(WellKnownMember.System_Diagnostics_DebuggerStepThroughAttribute__ctor, hasErrors)
 
            Select Case Me._asyncMethodKind
                Case AsyncMethodKind.GenericTaskFunction
                    EnsureWellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T, bag)
                Case AsyncMethodKind.TaskFunction
                    EnsureWellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder, bag)
                Case AsyncMethodKind.[Sub]
                    EnsureWellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncVoidMethodBuilder, bag)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(Me._asyncMethodKind)
            End Select
 
            Dim hasErrors As Boolean = bag.HasAnyErrors
            If hasErrors Then
                Me.Diagnostics.AddRange(bag)
            End If
 
            bag.Free()
            Return hasErrors
        End Function
 
        ''' <summary>
        ''' Specifies a kind of an Async method
        ''' 
        ''' None is returned for non-Async methods or methods with wrong return type
        ''' </summary>
        Friend Enum AsyncMethodKind
            None
            [Sub]
            TaskFunction
            GenericTaskFunction
        End Enum
 
        ''' <summary>
        ''' Returns method's async kind
        ''' </summary>
        Friend Shared Function GetAsyncMethodKind(method As MethodSymbol) As AsyncMethodKind
            Debug.Assert(Not method.IsPartial)
            If Not method.IsAsync Then
                Return AsyncMethodKind.None
            End If
 
            If method.IsSub Then
                Return AsyncMethodKind.[Sub]
            End If
 
            Dim compilation As VisualBasicCompilation = method.DeclaringCompilation
            Debug.Assert(compilation IsNot Nothing)
 
            Dim returnType As TypeSymbol = method.ReturnType
            If TypeSymbol.Equals(returnType, compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task), TypeCompareKind.ConsiderEverything) Then
                Return AsyncMethodKind.TaskFunction
            End If
 
            If returnType.Kind = SymbolKind.NamedType AndAlso
                    TypeSymbol.Equals(returnType.OriginalDefinition, compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T), TypeCompareKind.ConsiderEverything) Then
                Return AsyncMethodKind.GenericTaskFunction
            End If
 
            ' Wrong return type
            Return AsyncMethodKind.None
        End Function
 
        Protected Overrides Function CreateByRefLocalCapture(typeMap As TypeSubstitution,
                                                             local As LocalSymbol,
                                                             initializers As Dictionary(Of LocalSymbol, BoundExpression)) As CapturedSymbolOrExpression
 
            Debug.Assert(local.IsByRef)
            Return CaptureExpression(typeMap, initializers(local), initializers)
        End Function
 
        Private Function CaptureExpression(typeMap As TypeSubstitution,
                                           expression As BoundExpression,
                                           initializers As Dictionary(Of LocalSymbol, BoundExpression)) As CapturedSymbolOrExpression
 
            If expression Is Nothing Then
                Return Nothing
            End If
 
            Select Case expression.Kind
                Case BoundKind.Literal
                    Debug.Assert(Not expression.IsLValue)
                    ' TODO: Do we want to extend support of this constant 
                    '       folding to non-literal expressions?
                    Return New CapturedConstantExpression(expression.ConstantValueOpt,
                                                          expression.Type.InternalSubstituteTypeParameters(typeMap).Type)
 
                Case BoundKind.Local
                    Return CaptureLocalSymbol(typeMap, DirectCast(expression, BoundLocal).LocalSymbol, initializers)
 
                Case BoundKind.Parameter
                    Return CaptureParameterSymbol(typeMap, DirectCast(expression, BoundParameter).ParameterSymbol)
 
                Case BoundKind.MeReference
                    Return CaptureParameterSymbol(typeMap, Me.Method.MeParameter)
 
                Case BoundKind.MyClassReference
                    Return CaptureParameterSymbol(typeMap, Me.Method.MeParameter)
 
                Case BoundKind.MyBaseReference
                    Return CaptureParameterSymbol(typeMap, Me.Method.MeParameter)
 
                Case BoundKind.FieldAccess
                    If Not expression.IsLValue Then
                        GoTo lCaptureRValue
                    End If
 
                    Dim fieldAccess = DirectCast(expression, BoundFieldAccess)
                    Return New CapturedFieldAccessExpression(CaptureExpression(typeMap, fieldAccess.ReceiverOpt, initializers), fieldAccess.FieldSymbol)
 
                Case BoundKind.ArrayAccess
                    If Not expression.IsLValue Then
                        GoTo lCaptureRValue
                    End If
 
                    Dim arrayAccess = DirectCast(expression, BoundArrayAccess)
 
                    Dim capturedArrayPointer As CapturedSymbolOrExpression =
                        CaptureExpression(typeMap, arrayAccess.Expression, initializers)
 
                    Dim indices As ImmutableArray(Of BoundExpression) = arrayAccess.Indices
                    Dim indicesCount As Integer = indices.Length
 
                    Dim capturedIndices(indicesCount - 1) As CapturedSymbolOrExpression
                    For i = 0 To indicesCount - 1
                        capturedIndices(i) = CaptureExpression(typeMap, indices(i), initializers)
                    Next
 
                    Return New CapturedArrayAccessExpression(capturedArrayPointer, capturedIndices.AsImmutableOrNull)
 
                Case Else
lCaptureRValue:
                    Debug.Assert(Not expression.IsLValue, "Need to support LValues of type " + expression.GetType.Name)
                    Me._lastExpressionCaptureNumber += 1
                    Return New CapturedRValueExpression(
                                    Me.F.StateMachineField(
                                        expression.Type,
                                        Me.Method,
                                        GeneratedNameConstants.StateMachineExpressionCapturePrefix & Me._lastExpressionCaptureNumber,
                                        Accessibility.Friend),
                                    expression)
            End Select
 
            Throw ExceptionUtilities.UnexpectedValue(expression.Kind)
        End Function
 
        Protected Overrides Sub InitializeParameterWithProxy(parameter As ParameterSymbol, proxy As CapturedSymbolOrExpression, stateMachineVariable As LocalSymbol, initializers As ArrayBuilder(Of BoundExpression))
            Debug.Assert(TypeOf proxy Is CapturedParameterSymbol)
            Dim field As FieldSymbol = DirectCast(proxy, CapturedParameterSymbol).Field
 
            Dim frameType As NamedTypeSymbol = If(Me.Method.IsGenericMethod,
                                                  Me.StateMachineType.Construct(Me.Method.TypeArguments),
                                                  Me.StateMachineType)
 
            Dim expression As BoundExpression = If(parameter.IsMe,
                                                   DirectCast(Me.F.[Me](), BoundExpression),
                                                   Me.F.Parameter(parameter).MakeRValue())
            initializers.Add(
                Me.F.AssignmentExpression(
                    Me.F.Field(
                        Me.F.Local(stateMachineVariable, True),
                        field.AsMember(frameType),
                        True),
                    expression))
        End Sub
 
        Protected Overrides Function CreateByValLocalCapture(field As FieldSymbol, local As LocalSymbol) As CapturedSymbolOrExpression
            Return New CapturedLocalSymbol(field, local)
        End Function
 
        Protected Overrides Function CreateParameterCapture(field As FieldSymbol, parameter As ParameterSymbol) As CapturedSymbolOrExpression
            Return New CapturedParameterSymbol(field)
        End Function
 
#Region "Method call and property access generation"
 
        Private Function GenerateMethodCall(receiver As BoundExpression,
                                            type As TypeSymbol,
                                            methodName As String,
                                            ParamArray arguments As BoundExpression()) As BoundExpression
 
            Return GenerateMethodCall(receiver, type, methodName, ImmutableArray(Of TypeSymbol).Empty, arguments)
        End Function
 
        Private Function GenerateMethodCall(receiver As BoundExpression,
                                            type As TypeSymbol,
                                            methodName As String,
                                            typeArgs As ImmutableArray(Of TypeSymbol),
                                            ParamArray arguments As BoundExpression()) As BoundExpression
 
            ' Get the method group
            Dim methodGroup = FindMethodAndReturnMethodGroup(receiver, type, methodName, typeArgs)
 
            If methodGroup Is Nothing OrElse
                arguments.Any(Function(a) a.HasErrors) OrElse
                (receiver IsNot Nothing AndAlso receiver.HasErrors) Then
                Return Me.F.BadExpression(arguments)
            End If
 
            ' Do overload resolution and bind an invocation of the method.
            Dim result = _binder.BindInvocationExpression(Me.F.Syntax,
                                                          Me.F.Syntax,
                                                          TypeCharacter.None,
                                                          methodGroup,
                                                          ImmutableArray.Create(Of BoundExpression)(arguments),
                                                          argumentNames:=Nothing,
                                                          diagnostics:=Me.Diagnostics,
                                                          callerInfoOpt:=Nothing)
            Return result
        End Function
 
        Private Function FindMethodAndReturnMethodGroup(receiver As BoundExpression,
                                                        type As TypeSymbol,
                                                        methodName As String,
                                                        typeArgs As ImmutableArray(Of TypeSymbol)) As BoundMethodGroup
 
            Dim group As BoundMethodGroup = Nothing
            Dim result = LookupResult.GetInstance()
 
            Dim useSiteInfo = Me._binder.GetNewCompoundUseSiteInfo(Me.Diagnostics)
            Me._binder.LookupMember(result, type, methodName, arity:=0, options:=_lookupOptions, useSiteInfo:=useSiteInfo)
            Me.Diagnostics.Add(Me.F.Syntax, useSiteInfo)
 
            If result.IsGood Then
                Debug.Assert(result.Symbols.Count > 0)
                Dim symbol0 = result.Symbols(0)
                If result.Symbols(0).Kind = SymbolKind.Method Then
                    group = New BoundMethodGroup(Me.F.Syntax,
                                                 Me.F.TypeArguments(typeArgs),
                                                 result.Symbols.ToDowncastedImmutable(Of MethodSymbol),
                                                 result.Kind,
                                                 receiver,
                                                 QualificationKind.QualifiedViaValue)
                End If
            End If
 
            If group Is Nothing Then
                Me.Diagnostics.Add(If(result.HasDiagnostic,
                                      result.Diagnostic,
                                      ErrorFactory.ErrorInfo(ERRID.ERR_NameNotMember2, methodName, type)),
                                   Me.F.Syntax.GetLocation())
            End If
 
            result.Free()
            Return group
        End Function
 
        Private Function GeneratePropertyGet(receiver As BoundExpression, type As TypeSymbol, propertyName As String) As BoundExpression
            ' Get the property group
            Dim propertyGroup = FindPropertyAndReturnPropertyGroup(receiver, type, propertyName)
 
            If propertyGroup Is Nothing OrElse (receiver IsNot Nothing AndAlso receiver.HasErrors) Then
                Return Me.F.BadExpression()
            End If
 
            ' Do overload resolution and bind an invocation of the property.
            Dim result = _binder.BindInvocationExpression(Me.F.Syntax,
                                                          Me.F.Syntax,
                                                          TypeCharacter.None,
                                                          propertyGroup,
                                                          Nothing,
                                                          argumentNames:=Nothing,
                                                          diagnostics:=Me.Diagnostics,
                                                          callerInfoOpt:=Nothing)
 
            ' reclassify property access as Get
            If result.Kind = BoundKind.PropertyAccess Then
                result = DirectCast(result, BoundPropertyAccess).SetAccessKind(PropertyAccessKind.Get)
            End If
 
            Debug.Assert(Not result.IsLValue)
            Return result
        End Function
 
        Private Function FindPropertyAndReturnPropertyGroup(receiver As BoundExpression, type As TypeSymbol, propertyName As String) As BoundPropertyGroup
            Dim group As BoundPropertyGroup = Nothing
            Dim result = LookupResult.GetInstance()
 
            Dim useSiteInfo = Me._binder.GetNewCompoundUseSiteInfo(Me.Diagnostics)
            Me._binder.LookupMember(result, type, propertyName, arity:=0, options:=_lookupOptions, useSiteInfo:=useSiteInfo)
            Me.Diagnostics.Add(Me.F.Syntax, useSiteInfo)
 
            If result.IsGood Then
                Debug.Assert(result.Symbols.Count > 0)
                Dim symbol0 = result.Symbols(0)
                If result.Symbols(0).Kind = SymbolKind.Property Then
                    group = New BoundPropertyGroup(Me.F.Syntax,
                                                   result.Symbols.ToDowncastedImmutable(Of PropertySymbol),
                                                   result.Kind,
                                                   receiver,
                                                   QualificationKind.QualifiedViaValue)
                End If
            End If
 
            If group Is Nothing Then
                Me.Diagnostics.Add(If(result.HasDiagnostic,
                                      result.Diagnostic,
                                      ErrorFactory.ErrorInfo(ERRID.ERR_NameNotMember2, propertyName, type)),
                                   Me.F.Syntax.GetLocation())
            End If
 
            result.Free()
            Return group
        End Function
 
#End Region
 
    End Class
 
End Namespace