File: Lowering\LocalRewriter\LocalRewriter_LateBindingHelpers.vb
Web Access
Project: src\src\roslyn\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.

Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols

Namespace Microsoft.CodeAnalysis.VisualBasic
    Partial Friend NotInheritable Class LocalRewriter

        ' returns receiver, or Nothing literal otherwise
        Private Function LateMakeReceiverArgument(node As SyntaxNode,
                                                rewrittenReceiver As BoundExpression,
                                                objectType As TypeSymbol) As BoundExpression
            Debug.Assert(objectType.IsObjectType)

            If rewrittenReceiver Is Nothing Then
                Return MakeNullLiteral(node, objectType)
            Else
                If Not rewrittenReceiver.Type.IsObjectType Then
                    Dim useSiteInfo = GetNewCompoundUseSiteInfo()
                    Dim convKind = Conversions.ClassifyDirectCastConversion(rewrittenReceiver.Type, objectType, useSiteInfo)
                    _diagnostics.Add(node, useSiteInfo)
                    rewrittenReceiver = New BoundDirectCast(node, rewrittenReceiver, convKind, objectType)
                End If

                Return rewrittenReceiver
            End If
        End Function

        ' returns GetType(Type) if receiver is nothing, or Nothing literal otherwise
        Private Function LateMakeContainerArgument(node As SyntaxNode,
                                                       receiver As BoundExpression,
                                                       containerType As TypeSymbol,
                                                       typeType As TypeSymbol) As BoundExpression

            If receiver IsNot Nothing Then
                Return MakeNullLiteral(node, typeType)
            Else
                Return MakeGetTypeExpression(node, containerType, typeType)
            End If
        End Function

        ' returns "New Type(){GetType(Type1), GetType(Type2) ...} or Nothing literal
        Private Function LateMakeTypeArgumentArrayArgument(node As SyntaxNode, arguments As BoundTypeArguments, typeArrayType As TypeSymbol) As BoundExpression
            If arguments Is Nothing Then
                Return MakeNullLiteral(node, typeArrayType)
            Else
                Return MakeArrayOfGetTypeExpressions(node, arguments.Arguments, typeArrayType)
            End If
        End Function

        ' returns "New Boolean(length){}
        Private Function LateMakeCopyBackArray(node As SyntaxNode,
                                               flags As ImmutableArray(Of Boolean),
                                               booleanArrayType As TypeSymbol) As BoundExpression

            Dim arrayType = DirectCast(booleanArrayType, ArrayTypeSymbol)
            Dim booleanType = arrayType.ElementType

            Debug.Assert(arrayType.IsSZArray)
            Debug.Assert(booleanType.IsBooleanType)

            If flags.IsDefaultOrEmpty Then
                Return MakeNullLiteral(node, booleanArrayType)

            Else
                Dim intType = Me.GetSpecialType(SpecialType.System_Int32)
                Dim bounds As BoundExpression = New BoundLiteral(node, ConstantValue.Create(flags.Length), intType)

                Dim initializers = ArrayBuilder(Of BoundExpression).GetInstance
                For Each f In flags
                    initializers.Add(MakeBooleanLiteral(node, f, booleanType))
                Next

                Dim initializer = New BoundArrayInitialization(node, initializers.ToImmutableAndFree, Nothing)

                Return New BoundArrayCreation(node, ImmutableArray.Create(bounds), initializer, booleanArrayType)
            End If
        End Function

        Private Function LateMakeArgumentArrayArgument(node As SyntaxNode,
                                               rewrittenArguments As ImmutableArray(Of BoundExpression),
                                               argumentNames As ImmutableArray(Of String),
                                               objectArrayType As TypeSymbol) As BoundExpression

            If argumentNames.IsDefaultOrEmpty Then
                Return LateMakeArgumentArrayArgumentNoNamed(node, rewrittenArguments, objectArrayType)
            End If

            Dim namedArgNum As Integer = 0
            Dim regularArgNum As Integer

            For Each name In argumentNames
                If name IsNot Nothing Then
                    namedArgNum += 1
                End If
            Next
            regularArgNum = rewrittenArguments.Length - namedArgNum

            Dim arrayType = DirectCast(objectArrayType, ArrayTypeSymbol)
            Dim objectType = arrayType.ElementType

            Debug.Assert(arrayType.IsSZArray)
            Debug.Assert(objectType.IsObjectType)
            Debug.Assert(Not rewrittenArguments.IsDefaultOrEmpty)

            Dim intType = Me.GetSpecialType(SpecialType.System_Int32)
            Dim bounds As BoundExpression = New BoundLiteral(node, ConstantValue.Create(rewrittenArguments.Length), intType)

            Dim arrayCreation = New BoundArrayCreation(node, ImmutableArray.Create(bounds), Nothing, objectArrayType)
            Dim arrayTemp As LocalSymbol = New SynthesizedLocal(Me._currentMethodOrLambda, arrayCreation.Type, SynthesizedLocalKind.LoweringTemp)
            Dim arrayTempRef = New BoundLocal(node, arrayTemp, arrayTemp.Type)

            Dim arrayInit = New BoundAssignmentOperator(node, arrayTempRef, arrayCreation, suppressObjectClone:=True)

            Dim sideeffects = ArrayBuilder(Of BoundExpression).GetInstance
            sideeffects.Add(arrayInit)

            arrayTempRef = arrayTempRef.MakeRValue

            For i As Integer = 0 To rewrittenArguments.Length - 1
                Dim argument = rewrittenArguments(i)
                argument = argument.MakeRValue
                If Not argument.Type.IsObjectType Then
                    Dim useSiteInfo = GetNewCompoundUseSiteInfo()
                    Dim convKind = Conversions.ClassifyDirectCastConversion(argument.Type, objectType, useSiteInfo)
                    _diagnostics.Add(node, useSiteInfo)
                    argument = New BoundDirectCast(node, argument, convKind, objectType)
                End If

                ' named arguments are actually passed first in the array
                Dim indexVal = If(i < regularArgNum,
                               namedArgNum + i,
                               i - regularArgNum)

                Dim indexExpr As BoundExpression = New BoundLiteral(node, ConstantValue.Create(indexVal), intType)
                Dim indices = ImmutableArray.Create(indexExpr)

                Dim arrayElement As BoundExpression = New BoundArrayAccess(node,
                                                                    arrayTempRef,
                                                                    indices,
                                                                    objectType)

                Dim elementAssignment = New BoundAssignmentOperator(node, arrayElement, argument, suppressObjectClone:=True)
                sideeffects.Add(elementAssignment)
            Next

            Return New BoundSequence(node, ImmutableArray.Create(arrayTemp), sideeffects.ToImmutableAndFree, arrayTempRef, arrayTempRef.Type)
        End Function

        ' returns "New object(){Arg1, Arg2 ..., value}
        Private Function LateMakeSetArgumentArrayArgument(node As SyntaxNode,
                                            rewrittenValue As BoundExpression,
                                            rewrittenArguments As ImmutableArray(Of BoundExpression),
                                            argumentNames As ImmutableArray(Of String),
                                            objectArrayType As TypeSymbol) As BoundExpression

            Dim arrayType = DirectCast(objectArrayType, ArrayTypeSymbol)
            Dim objectType = arrayType.ElementType

            Debug.Assert(arrayType.IsSZArray)
            Debug.Assert(objectType.IsObjectType)

            Dim useSiteInfo = GetNewCompoundUseSiteInfo()

            If Not rewrittenValue.Type.IsObjectType Then
                Dim convKind = Conversions.ClassifyDirectCastConversion(rewrittenValue.Type, objectType, useSiteInfo)
                _diagnostics.Add(node, useSiteInfo)
                rewrittenValue = New BoundDirectCast(node, rewrittenValue, convKind, objectType)
            End If

            If argumentNames.IsDefaultOrEmpty Then
                If rewrittenArguments.IsDefaultOrEmpty Then
                    rewrittenArguments = ImmutableArray.Create(rewrittenValue)
                ElseIf argumentNames.IsDefaultOrEmpty Then
                    rewrittenArguments = rewrittenArguments.Add(rewrittenValue)
                End If

                Return LateMakeArgumentArrayArgumentNoNamed(node, rewrittenArguments, objectArrayType)
            End If

            ' have named arguments, need to reshuffle {named}{regular}{value}

            Dim namedArgNum As Integer = 0
            Dim regularArgNum As Integer

            For Each name In argumentNames
                If name IsNot Nothing Then
                    namedArgNum += 1
                End If
            Next
            regularArgNum = rewrittenArguments.Length - namedArgNum

            Debug.Assert(Not rewrittenArguments.IsDefaultOrEmpty)

            Dim intType = Me.GetSpecialType(SpecialType.System_Int32)
            Dim bounds As BoundExpression = New BoundLiteral(node, ConstantValue.Create(rewrittenArguments.Length + 1), intType)

            Dim arrayCreation = New BoundArrayCreation(node, ImmutableArray.Create(bounds), Nothing, objectArrayType)
            Dim arrayTemp As LocalSymbol = New SynthesizedLocal(Me._currentMethodOrLambda, arrayCreation.Type, SynthesizedLocalKind.LoweringTemp)
            Dim arrayTempRef = New BoundLocal(node, arrayTemp, arrayTemp.Type)

            Dim arrayInit = New BoundAssignmentOperator(node, arrayTempRef, arrayCreation, suppressObjectClone:=True)

            Dim sideeffects = ArrayBuilder(Of BoundExpression).GetInstance
            sideeffects.Add(arrayInit)

            arrayTempRef = arrayTempRef.MakeRValue

            For i As Integer = 0 To rewrittenArguments.Length - 1
                Dim argument = rewrittenArguments(i)
                argument = argument.MakeRValue
                If Not argument.Type.IsObjectType Then
                    Dim convKind = Conversions.ClassifyDirectCastConversion(argument.Type, objectType, useSiteInfo)
                    _diagnostics.Add(argument, useSiteInfo)
                    argument = New BoundDirectCast(node, argument, convKind, objectType)
                End If

                ' named arguments are actually passed first in the array, then regular, then assignment value
                Dim indexVal = If(i < regularArgNum,
                               namedArgNum + i,
                               i - regularArgNum)

                Dim elementAssignment = LateAssignToArrayElement(node, arrayTempRef, indexVal, argument, intType)
                sideeffects.Add(elementAssignment)
            Next

            ' value goes last
            If Not rewrittenValue.Type.IsObjectType Then
                Dim convKind = Conversions.ClassifyDirectCastConversion(rewrittenValue.Type, objectType, useSiteInfo)
                _diagnostics.Add(rewrittenValue, useSiteInfo)
                rewrittenValue = New BoundDirectCast(node, rewrittenValue, convKind, objectType)
            End If

            Dim valueElementAssignment = LateAssignToArrayElement(node, arrayTempRef, rewrittenArguments.Length, rewrittenValue, intType)
            sideeffects.Add(valueElementAssignment)

            Return New BoundSequence(node, ImmutableArray.Create(arrayTemp), sideeffects.ToImmutableAndFree, arrayTempRef, arrayTempRef.Type)
        End Function

        Private Shared Function LateAssignToArrayElement(node As SyntaxNode,
                                                  arrayRef As BoundExpression,
                                                  index As Integer,
                                                  value As BoundExpression,
                                                  intType As TypeSymbol) As BoundExpression

            Dim indexExpr As BoundExpression = New BoundLiteral(node, ConstantValue.Create(index), intType)
            Dim arrayElement As BoundExpression = New BoundArrayAccess(node,
                                                                arrayRef,
                                                                ImmutableArray.Create(indexExpr),
                                                                value.Type)

            Return New BoundAssignmentOperator(node, arrayElement, value, suppressObjectClone:=True)
        End Function

        ' returns "New object(){Arg1, Arg2 ...}
        Private Function LateMakeArgumentArrayArgumentNoNamed(node As SyntaxNode,
                                                       rewrittenArguments As ImmutableArray(Of BoundExpression),
                                                       objectArrayType As TypeSymbol) As BoundExpression

            Dim arrayType = DirectCast(objectArrayType, ArrayTypeSymbol)
            Dim objectType = arrayType.ElementType
            Dim intType = Me.GetSpecialType(SpecialType.System_Int32)

            Debug.Assert(arrayType.IsSZArray)
            Debug.Assert(objectType.IsObjectType)

            If rewrittenArguments.IsDefaultOrEmpty Then
                Dim bounds As BoundExpression = New BoundLiteral(node, ConstantValue.Default(ConstantValueTypeDiscriminator.Int32), intType)
                Return New BoundArrayCreation(node, ImmutableArray.Create(bounds), Nothing, objectArrayType)

            Else
                Dim bounds As BoundExpression = New BoundLiteral(node, ConstantValue.Create(rewrittenArguments.Length), intType)

                Dim initializers = ArrayBuilder(Of BoundExpression).GetInstance
                For Each argument In rewrittenArguments
                    argument = argument.MakeRValue
                    If Not argument.Type.IsObjectType Then
                        Dim useSiteInfo = GetNewCompoundUseSiteInfo()
                        Dim convKind = Conversions.ClassifyDirectCastConversion(argument.Type, objectType, useSiteInfo)
                        _diagnostics.Add(argument, useSiteInfo)
                        argument = New BoundDirectCast(node, argument, convKind, objectType)
                    End If

                    initializers.Add(argument)
                Next

                'TODO: Dev11 does GetobjectValue here is it needed? Should array initialization do it in general?
                Dim initializer = New BoundArrayInitialization(node, initializers.ToImmutableAndFree, Nothing)

                Return New BoundArrayCreation(node, ImmutableArray.Create(bounds), initializer, objectArrayType)
            End If
        End Function

        ' returns "New object(){name1, name2 ...} or Nothing literal
        Private Function LateMakeArgumentNameArrayArgument(node As SyntaxNode,
                                                       argumentNames As ImmutableArray(Of String),
                                                       stringArrayType As TypeSymbol) As BoundExpression

            Dim arrayType = DirectCast(stringArrayType, ArrayTypeSymbol)
            Dim stringType = arrayType.ElementType

            Debug.Assert(arrayType.IsSZArray)
            Debug.Assert(stringType.IsStringType)

            If argumentNames.IsDefaultOrEmpty Then
                Return MakeNullLiteral(node, stringArrayType)

            Else
                Dim initializers = ArrayBuilder(Of BoundExpression).GetInstance
                For Each name In argumentNames
                    If name IsNot Nothing Then
                        initializers.Add(MakeStringLiteral(node, name, stringType))
                    Else
                        Debug.Assert(initializers.Count = 0, "once we have named argument all following arguments must be named")
                    End If
                Next

                Dim initializer = New BoundArrayInitialization(node, initializers.ToImmutableAndFree, Nothing)

                Dim intType = Me.GetSpecialType(SpecialType.System_Int32)
                Dim bounds As BoundExpression = New BoundLiteral(node, ConstantValue.Create(initializer.Initializers.Length), intType)

                Return New BoundArrayCreation(node, ImmutableArray.Create(bounds), initializer, stringArrayType)
            End If
        End Function

        ' Makes expressions like
        '  if(copyBackArrayRef[argNum], assignmentTarget := valueArrayRef[argNum] , Nothing)
        '
        ' NOTE: assignmentTarget comes in not yet lowered.
        Private Function LateMakeConditionalCopyback(assignmentTarget As BoundExpression,
                                     valueArrayRef As BoundExpression,
                                     copyBackArrayRef As BoundExpression,
                                     argNum As Integer) As BoundExpression

            Dim syntax = assignmentTarget.Syntax
            Dim intType = Me.GetSpecialType(SpecialType.System_Int32)

            Dim index As BoundExpression = New BoundLiteral(syntax, ConstantValue.Create(argNum), intType)
            Dim indices = ImmutableArray.Create(index)

            Dim booleanType = DirectCast(copyBackArrayRef.Type, ArrayTypeSymbol).ElementType
            Dim condition As BoundExpression = New BoundArrayAccess(syntax,
                                                                    copyBackArrayRef,
                                                                    ImmutableArray.Create(index),
                                                                    booleanType).MakeRValue

            Dim objectType = DirectCast(valueArrayRef.Type, ArrayTypeSymbol).ElementType
            Dim value As BoundExpression = New BoundArrayAccess(syntax,
                                            valueArrayRef,
                                            indices,
                                            objectType).MakeRValue

            Dim targetType = assignmentTarget.Type

            If Not targetType.IsSameTypeIgnoringAll(objectType) Then
                ' // Call ChangeType to perform a latebound conversion
                Dim changeTypeMethod As MethodSymbol = Nothing
                If TryGetWellknownMember(changeTypeMethod,
                                         WellKnownMember.Microsoft_VisualBasic_CompilerServices_Conversions__ChangeType, syntax) Then

                    ' value = ChangeType(value, GetType(targetType))
                    Dim factory As New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, syntax, _compilationState, _diagnostics)
                    Dim getTypeExpr = factory.Typeof(targetType, changeTypeMethod.Parameters(1).Type)

                    'TODO: should we suppress object clone here? Dev11 does not.
                    value = New BoundCall(syntax,
                                          changeTypeMethod,
                                          Nothing,
                                          Nothing,
                                          ImmutableArray.Create(Of BoundExpression)(value, getTypeExpr),
                                          Nothing,
                                          suppressObjectClone:=False,
                                          type:=objectType)

                End If
#If DEBUG Then
                Dim useSiteInfo As New CompoundUseSiteInfo(Of AssemblySymbol)(Me.Compilation.Assembly)
#Else
                Dim useSiteInfo = CompoundUseSiteInfo(Of AssemblySymbol).Discarded
#End If
                Dim conversionKind = Conversions.ClassifyDirectCastConversion(objectType, targetType, useSiteInfo)
#If DEBUG Then
                Debug.Assert(useSiteInfo.Diagnostics.IsNullOrEmpty)
                Debug.Assert(useSiteInfo.Dependencies.IsNullOrEmpty)
#End If
                value = New BoundDirectCast(syntax, value, conversionKind, targetType)
            End If

            Dim voidNoOp As BoundExpression = New BoundSequence(syntax,
                                                            ImmutableArray(Of LocalSymbol).Empty,
                                                            ImmutableArray(Of BoundExpression).Empty,
                                                            Nothing,
                                                            Me.GetSpecialType(SpecialType.System_Void))

            Dim voidAssignment As BoundExpression = LateMakeCopyback(syntax,
                                                                assignmentTarget,
                                                                value)

            Dim result = MakeTernaryConditionalExpression(syntax,
                                                          condition,
                                                          voidAssignment,
                                                          voidNoOp)

            Return VisitExpressionNode(result)
        End Function

        Private Function LateMakeCopyback(syntax As SyntaxNode,
                                          assignmentTarget As BoundExpression,
                                          convertedValue As BoundExpression) As BoundExpression

            If assignmentTarget.Kind = BoundKind.LateMemberAccess Then
                ' objExpr.goo = bar
                Dim memberAccess = DirectCast(assignmentTarget, BoundLateMemberAccess)
                Return LateSet(syntax,
                               memberAccess,
                               convertedValue,
                               argExpressions:=Nothing,
                               argNames:=Nothing,
                               isCopyBack:=True)

            ElseIf assignmentTarget.Kind = BoundKind.LateInvocation Then
                Dim invocation = DirectCast(assignmentTarget, BoundLateInvocation)

                If invocation.Member.Kind = BoundKind.LateMemberAccess Then
                    Dim memberAccess = DirectCast(invocation.Member, BoundLateMemberAccess)
                    ' objExpr.goo(args) = bar
                    Return LateSet(syntax,
                               memberAccess,
                               convertedValue,
                               argExpressions:=invocation.ArgumentsOpt,
                               argNames:=invocation.ArgumentNamesOpt,
                               isCopyBack:=True)
                Else
                    ' objExpr(args) = bar
                    Return LateIndexSet(syntax,
                                        invocation,
                                        convertedValue,
                                        isCopyBack:=True)
                End If
            End If

            ' TODO: should we suppress object clone here? Dev11 does not suppress.
            Dim assignment As BoundExpression = New BoundAssignmentOperator(syntax,
                                                                            assignmentTarget,
                                                                            GenerateObjectCloneIfNeeded(convertedValue),
                                                                            suppressObjectClone:=True)

            Dim voidAssignment = New BoundSequence(syntax,
                                                    ImmutableArray(Of LocalSymbol).Empty,
                                                    ImmutableArray.Create(assignment),
                                                    Nothing,
                                                    Me.GetSpecialType(SpecialType.System_Void))

            Return voidAssignment
        End Function

        Private Function LateIndexGet(node As BoundLateInvocation,
                            receiverExpr As BoundExpression,
                            argExpressions As ImmutableArray(Of BoundExpression)) As BoundExpression

            Debug.Assert(node.Member.Kind <> BoundKind.LateMemberAccess)

            ' We have 
            '   objExpr(arg0, ..., argN) 

            Dim syntax = node.Syntax

            ' expr = o.Goo<TypeParam>
            ' emit as:
            '     LateGet(invocation.Member, 
            '         invocation.Arguments, 
            '         invocation.ArgumentNames)
            '
            '
            Dim lateIndexGetMethod As MethodSymbol = Nothing
            If Not Me.TryGetWellknownMember(lateIndexGetMethod, WellKnownMember.Microsoft_VisualBasic_CompilerServices_NewLateBinding__LateIndexGet, syntax) Then
                Return node
            End If

            ' arg0  "object Instance"
            Dim receiver As BoundExpression = LateMakeReceiverArgument(syntax, receiverExpr.MakeRValue, lateIndexGetMethod.Parameters(0).Type)
            ' arg1  "object[] Arguments"
            Dim arguments As BoundExpression = LateMakeArgumentArrayArgument(node.Syntax, argExpressions, node.ArgumentNamesOpt, lateIndexGetMethod.Parameters(1).Type)
            ' arg2  "string[] ArgumentNames"
            Dim argumentNames As BoundExpression = LateMakeArgumentNameArrayArgument(syntax, node.ArgumentNamesOpt, lateIndexGetMethod.Parameters(2).Type)

            Dim callArgs = ImmutableArray.Create(Of BoundExpression)(receiver,
                                                                    arguments,
                                                                    argumentNames)

            Dim callerInvocation As BoundExpression = New BoundCall(
                syntax,
                lateIndexGetMethod,
                Nothing,
                Nothing,
                callArgs,
                Nothing,
                suppressObjectClone:=True,
                type:=lateIndexGetMethod.ReturnType)

            Return callerInvocation
        End Function

        Private Function LateSet(syntax As SyntaxNode,
                                memberAccess As BoundLateMemberAccess,
                                assignmentValue As BoundExpression,
                                argExpressions As ImmutableArray(Of BoundExpression),
                                argNames As ImmutableArray(Of String),
                                isCopyBack As Boolean) As BoundExpression

            Debug.Assert(memberAccess.AccessKind = LateBoundAccessKind.Set)

            ' TODO: May need a special case for parameters. 
            '       A readonly parameter (in queries) may be not IsLValue, but Dev11 may treat it as an LValue still.

            ' NOTE: Dev11 passes "false" when access is static. We will do the same. 
            '       It makes no difference at runtime since there is no base.
            Dim baseIsNotLValue As Boolean = memberAccess.ReceiverOpt IsNot Nothing AndAlso Not memberAccess.ReceiverOpt.IsLValue

            Dim lateSetMethod As MethodSymbol = Nothing
            Dim isComplex As Boolean = isCopyBack OrElse baseIsNotLValue

            If isComplex Then
                If Not Me.TryGetWellknownMember(lateSetMethod, WellKnownMember.Microsoft_VisualBasic_CompilerServices_NewLateBinding__LateSetComplex, syntax) Then
                    ' need to return something void
                    Return New BoundSequence(syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(Of BoundExpression)(memberAccess), Nothing, Me.GetSpecialType(SpecialType.System_Void))
                End If
            Else
                If Not Me.TryGetWellknownMember(lateSetMethod, WellKnownMember.Microsoft_VisualBasic_CompilerServices_NewLateBinding__LateSet, syntax) Then
                    ' need to return something void
                    Return New BoundSequence(syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(Of BoundExpression)(memberAccess), Nothing, Me.GetSpecialType(SpecialType.System_Void))
                End If
            End If

            ' arg0  "object Instance"
            Dim receiver As BoundExpression = LateMakeReceiverArgument(syntax,
                                                                       If(memberAccess.ReceiverOpt IsNot Nothing, memberAccess.ReceiverOpt.MakeRValue, Nothing),
                                                                       lateSetMethod.Parameters(0).Type)
            ' arg1  "Type Type"
            Dim containerType As BoundExpression = LateMakeContainerArgument(syntax, memberAccess.ReceiverOpt, memberAccess.ContainerTypeOpt, lateSetMethod.Parameters(1).Type)
            ' arg2  "string MemberName"
            Dim name As BoundLiteral = MakeStringLiteral(syntax, memberAccess.NameOpt, lateSetMethod.Parameters(2).Type)
            ' arg3  "object[] Arguments"
            Dim arguments As BoundExpression = LateMakeSetArgumentArrayArgument(syntax, assignmentValue, argExpressions, argNames, lateSetMethod.Parameters(3).Type)
            ' arg4  "string[] ArgumentNames"
            Dim argumentNames As BoundExpression = LateMakeArgumentNameArrayArgument(syntax, argNames, lateSetMethod.Parameters(4).Type)
            ' arg5  "Type[] TypeArguments"
            Dim typeArguments As BoundExpression = LateMakeTypeArgumentArrayArgument(syntax, memberAccess.TypeArgumentsOpt, lateSetMethod.Parameters(5).Type)

            Dim callArgs As ImmutableArray(Of BoundExpression)
            If Not isComplex Then
                callArgs = ImmutableArray.Create(Of BoundExpression)(receiver,
                                                                    containerType,
                                                                    name,
                                                                    arguments,
                                                                    argumentNames,
                                                                    typeArguments)
            Else
                ' arg6  "bool OptimisticSet"
                Dim optimisticSet As BoundExpression = MakeBooleanLiteral(syntax, isCopyBack, lateSetMethod.Parameters(6).Type)
                ' arg7  "bool RValueBase"
                Dim rValueBase As BoundExpression = MakeBooleanLiteral(syntax, baseIsNotLValue, lateSetMethod.Parameters(7).Type)

                callArgs = ImmutableArray.Create(Of BoundExpression)(receiver,
                                                                    containerType,
                                                                    name,
                                                                    arguments,
                                                                    argumentNames,
                                                                    typeArguments,
                                                                    optimisticSet,
                                                                    rValueBase)
            End If

            Return New BoundCall(
                syntax,
                lateSetMethod,
                Nothing,
                Nothing,
                callArgs,
                Nothing,
                suppressObjectClone:=True,
                type:=lateSetMethod.ReturnType)
        End Function

        Private Function LateIndexSet(syntax As SyntaxNode,
                                      invocation As BoundLateInvocation,
                                      assignmentValue As BoundExpression,
                                      isCopyBack As Boolean) As BoundExpression

            Debug.Assert(invocation.AccessKind = LateBoundAccessKind.Set)

            ' TODO: May need a special case for parameters. 
            '       A readonly parameter (in queries) may be not IsLValue, but Dev11 may treat it as an LValue still.

            ' NOTE: Dev11 passes "false" when access is static. We will do the same. 
            '       It makes no difference at runtime since there is no base.
            Dim baseIsNotLValue As Boolean = invocation.Member IsNot Nothing AndAlso Not invocation.Member.IsLValue

            Dim lateIndexSetMethod As MethodSymbol = Nothing
            Dim isComplex As Boolean = isCopyBack OrElse baseIsNotLValue

            If isComplex Then
                If Not Me.TryGetWellknownMember(lateIndexSetMethod, WellKnownMember.Microsoft_VisualBasic_CompilerServices_NewLateBinding__LateIndexSetComplex, syntax) Then
                    ' need to return something void
                    Return New BoundSequence(syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(Of BoundExpression)(invocation), Nothing, Me.GetSpecialType(SpecialType.System_Void))
                End If
            Else
                If Not Me.TryGetWellknownMember(lateIndexSetMethod, WellKnownMember.Microsoft_VisualBasic_CompilerServices_NewLateBinding__LateIndexSet, syntax) Then
                    ' need to return something void
                    Return New BoundSequence(syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(Of BoundExpression)(invocation), Nothing, Me.GetSpecialType(SpecialType.System_Void))
                End If
            End If

            ' arg0  "object Instance"
            Dim receiver As BoundExpression = LateMakeReceiverArgument(syntax, invocation.Member.MakeRValue, lateIndexSetMethod.Parameters(0).Type)
            ' arg1  "object[] Arguments"
            Dim arguments As BoundExpression = LateMakeSetArgumentArrayArgument(syntax, assignmentValue.MakeRValue, invocation.ArgumentsOpt, invocation.ArgumentNamesOpt, lateIndexSetMethod.Parameters(1).Type)
            ' arg2  "string[] ArgumentNames"
            Dim argumentNames As BoundExpression = LateMakeArgumentNameArrayArgument(syntax, invocation.ArgumentNamesOpt, lateIndexSetMethod.Parameters(2).Type)

            Dim callArgs As ImmutableArray(Of BoundExpression)
            If Not isComplex Then
                callArgs = ImmutableArray.Create(Of BoundExpression)(receiver,
                                                                    arguments,
                                                                    argumentNames)
            Else
                ' arg3  "bool OptimisticSet"
                Dim optimisticSet As BoundExpression = MakeBooleanLiteral(syntax, isCopyBack, lateIndexSetMethod.Parameters(3).Type)
                ' arg4  "bool RValueBase"
                Dim rValueBase As BoundExpression = MakeBooleanLiteral(syntax, baseIsNotLValue, lateIndexSetMethod.Parameters(4).Type)

                callArgs = ImmutableArray.Create(Of BoundExpression)(receiver,
                                             arguments,
                                             argumentNames,
                                             optimisticSet,
                                             rValueBase)
            End If

            Return New BoundCall(
                syntax,
                lateIndexSetMethod,
                Nothing,
                Nothing,
                callArgs,
                Nothing,
                suppressObjectClone:=True,
                type:=lateIndexSetMethod.ReturnType)
        End Function

        ' NOTE: assignmentArguments are no-side-effects expressions representing
        '       corresponding arguments if those need to be used as target of assignments.
        Private Function LateCallOrGet(memberAccess As BoundLateMemberAccess,
                                    receiverExpression As BoundExpression,
                                    argExpressions As ImmutableArray(Of BoundExpression),
                                    assignmentArguments As ImmutableArray(Of BoundExpression),
                                    argNames As ImmutableArray(Of String),
                                    useLateCall As Boolean) As BoundExpression

            Debug.Assert(memberAccess.AccessKind = LateBoundAccessKind.Call OrElse memberAccess.AccessKind = LateBoundAccessKind.Get)

            Debug.Assert((assignmentArguments.IsDefaultOrEmpty AndAlso argExpressions.IsDefaultOrEmpty) OrElse
              (assignmentArguments.Length = argExpressions.Length),
              "number of readable and writable arguments must match")

            Debug.Assert(argNames.IsDefaultOrEmpty OrElse argNames.Length = argExpressions.Length,
                         "should not have argument names or should have name for every argument")

            Dim syntax = memberAccess.Syntax

            ' We have 
            '   objExpr.Member()
            ' emit as:
            '     LateGet(memberAccess.ReceiverOpt, 
            '         memberAccess.MemberContainerOpt, 
            '         memberAccess.MemberNameOpt, 
            '         Arguments:=Nothing, 
            '         ArgumentNames:= Nothing, 
            '         TypeArguments = memberAccess.TypeArguments)
            '
            '
            Dim lateCallOrGetMethod As MethodSymbol = Nothing

            If useLateCall Then
                If Not Me.TryGetWellknownMember(lateCallOrGetMethod, WellKnownMember.Microsoft_VisualBasic_CompilerServices_NewLateBinding__LateCall, syntax) Then
                    Return memberAccess
                End If
            Else
                If Not Me.TryGetWellknownMember(lateCallOrGetMethod, WellKnownMember.Microsoft_VisualBasic_CompilerServices_NewLateBinding__LateGet, syntax) Then
                    Return memberAccess
                End If
            End If

            ' temp will be used only if we have copybacks
            Dim valueArrayTemp As SynthesizedLocal = Nothing
            Dim valueArrayRef As BoundLocal = Nothing

            Dim copyBackFlagArrayTemp As SynthesizedLocal = Nothing
            Dim copyBackFlagArrayRef As BoundLocal = Nothing

            ' passed as an array that represents "arguments of the call" 
            ' initially just rewrittenArgExpressions
            Dim argumentsArray As BoundExpression = LateMakeArgumentArrayArgument(syntax, argExpressions, argNames, lateCallOrGetMethod.Parameters(3).Type)

            ' passed as an array of flags indicating copyback status of corresponding elements of arguments array.
            ' initially Nothing
            Dim copyBackFlagArray As BoundExpression = LateMakeCopyBackArray(syntax, Nothing, lateCallOrGetMethod.Parameters(6).Type)

            '== process copybacks if needed
            Dim copyBackBuilder As ArrayBuilder(Of BoundExpression) = Nothing
            If Not assignmentArguments.IsDefaultOrEmpty Then
                Dim namedArgNum As Integer = 0
                Dim regularArgNum As Integer

                If Not argNames.IsDefaultOrEmpty Then
                    For Each n In argNames
                        If n IsNot Nothing Then
                            namedArgNum += 1
                        End If
                    Next
                End If

                regularArgNum = assignmentArguments.Length - namedArgNum

                ' This is a Late Call/Get with arguments. 
                ' All LValue arguments are presumed to be passed ByRef until rewriter assures us 
                ' the other way (we do not know the refness of parameters at the callsite).

                ' if have any copybacks, need an array of bool the size of assignmentArguments.Count
                ' "True" conveys to the latebinder that the argument can accept copyback
                ' "False" means to not bother.
                ' During the actual call latebinder will erase True for arguments that
                ' happened to be passed to a ByVal parameter (so that we do not need to do copyback assignment on our side).
                Dim copyBackFlagValues As Boolean() = Nothing

                For i As Integer = 0 To assignmentArguments.Length - 1
                    Dim assignmentTarget As BoundExpression = assignmentArguments(i)

                    If Not IsSupportingAssignment(assignmentTarget) Then
                        Continue For
                    End If

                    If copyBackFlagArrayTemp Is Nothing Then
                        ' since we may have copybacks, we need a temp for the flags array to examine its content after the call
                        copyBackFlagArrayTemp = New SynthesizedLocal(Me._currentMethodOrLambda, copyBackFlagArray.Type, SynthesizedLocalKind.LoweringTemp)
                        copyBackFlagArrayRef = (New BoundLocal(syntax, copyBackFlagArrayTemp, copyBackFlagArrayTemp.Type)).MakeRValue

                        ' since we may have copybacks, we need a temp for the arguments array to access it after the call
                        valueArrayTemp = New SynthesizedLocal(Me._currentMethodOrLambda, argumentsArray.Type, SynthesizedLocalKind.LoweringTemp)
                        valueArrayRef = New BoundLocal(syntax, valueArrayTemp, valueArrayTemp.Type)
                        argumentsArray = (New BoundAssignmentOperator(syntax, valueArrayRef, argumentsArray, suppressObjectClone:=True)).MakeRValue
                        valueArrayRef = valueArrayRef.MakeRValue

                        copyBackBuilder = ArrayBuilder(Of BoundExpression).GetInstance(assignmentArguments.Length)
                        copyBackFlagValues = (New Boolean(assignmentArguments.Length - 1) {})
                    End If

                    ' named arguments are actually passed first in the array
                    Dim indexVal = If(i < regularArgNum,
                                   namedArgNum + i,
                                   i - regularArgNum)

                    copyBackFlagValues(indexVal) = True
                    copyBackBuilder.Add(LateMakeConditionalCopyback(assignmentTarget, valueArrayRef, copyBackFlagArrayRef, indexVal))
                Next

                If copyBackFlagArrayTemp IsNot Nothing Then
                    copyBackFlagArray = (New BoundAssignmentOperator(syntax,
                                                                    New BoundLocal(syntax, copyBackFlagArrayTemp, copyBackFlagArrayTemp.Type),
                                                                    LateMakeCopyBackArray(syntax,
                                                                                          copyBackFlagValues.AsImmutableOrNull,
                                                                                          copyBackFlagArrayTemp.Type),
                                                                    suppressObjectClone:=True)).MakeRValue
                End If
            End If

            Dim receiverValue As BoundExpression = If(receiverExpression Is Nothing, Nothing, receiverExpression.MakeRValue)

            ' arg0  "object Instance"
            Dim receiver As BoundExpression = LateMakeReceiverArgument(syntax, receiverValue, lateCallOrGetMethod.Parameters(0).Type)
            ' arg1  "Type Type"
            Dim containerType As BoundExpression = LateMakeContainerArgument(syntax, receiverExpression, memberAccess.ContainerTypeOpt, lateCallOrGetMethod.Parameters(1).Type)
            ' arg2  "string MemberName"
            Dim name As BoundLiteral = MakeStringLiteral(syntax, memberAccess.NameOpt, lateCallOrGetMethod.Parameters(2).Type)
            ' arg3  "object[] Arguments"
            Dim arguments As BoundExpression = argumentsArray
            ' arg4  "string[] ArgumentNames"
            Dim argumentNames As BoundExpression = LateMakeArgumentNameArrayArgument(syntax, argNames, lateCallOrGetMethod.Parameters(4).Type)
            ' arg5  "Type[] TypeArguments"
            Dim typeArguments As BoundExpression = LateMakeTypeArgumentArrayArgument(syntax, memberAccess.TypeArgumentsOpt, lateCallOrGetMethod.Parameters(5).Type)
            ' arg6  "bool[] CopyBack"
            Dim copyBack As BoundExpression = copyBackFlagArray

            Dim callArgs = ImmutableArray.Create(Of BoundExpression)(receiver,
                                                                    containerType,
                                                                    name,
                                                                    arguments,
                                                                    argumentNames,
                                                                    typeArguments,
                                                                    copyBack)

            ' 
            ' It appears that IgnoreReturn is always set when LateCall is called from compiled code.
            '
            '           @ Expressions.cpp/CodeGenerator::GenerateLate [line:3314]
            '           ...          
            '           if (rtHelper == LateCallMember)
            '           {
            '               GenerateLiteralInt(COMPLUS_TRUE);
            '           }
            '           ...
            '
            If useLateCall Then
                ' arg7  "bool IgnoreReturn"
                Dim ignoreReturn As BoundExpression = MakeBooleanLiteral(syntax, True, lateCallOrGetMethod.Parameters(7).Type)
                callArgs = callArgs.Add(ignoreReturn)
            End If

            Dim callerInvocation As BoundExpression = New BoundCall(
                syntax,
                lateCallOrGetMethod,
                Nothing,
                Nothing,
                callArgs,
                Nothing,
                suppressObjectClone:=True,
                type:=lateCallOrGetMethod.ReturnType)

            ' process copybacks
            If copyBackFlagArrayTemp IsNot Nothing Then
                Dim valueTemp = New SynthesizedLocal(Me._currentMethodOrLambda, callerInvocation.Type, SynthesizedLocalKind.LoweringTemp)
                Dim valueRef = New BoundLocal(syntax, valueTemp, valueTemp.Type)
                Dim store = New BoundAssignmentOperator(syntax, valueRef, callerInvocation, suppressObjectClone:=True)

                ' Seq{all temps; valueTemp = callerInvocation; copyBacks, valueTemp}
                callerInvocation = New BoundSequence(syntax,
                                                     ImmutableArray.Create(Of LocalSymbol)(valueArrayTemp, copyBackFlagArrayTemp, valueTemp),
                                                     ImmutableArray.Create(Of BoundExpression)(store).Concat(copyBackBuilder.ToImmutableAndFree),
                                                     valueRef.MakeRValue,
                                                     valueRef.Type)
            End If

            Return callerInvocation
        End Function

        ' same as LateCaptureReceiverAndArgsComplex, but without a receiver
        ' and does not produce reReadable arguments - just 
        ' argument (that includes initialization of captures if needed) and a no-side-effect writable.
        ' NOTE: writables are not rewritten. They will be rewritten when they are combined with values into assignments.
        Private Sub LateCaptureArgsComplex(ByRef temps As ArrayBuilder(Of SynthesizedLocal),
                           ByRef arguments As ImmutableArray(Of BoundExpression),
                           <Out> ByRef writeTargets As ImmutableArray(Of BoundExpression))

            Dim container = Me._currentMethodOrLambda

            If temps Is Nothing Then
                temps = ArrayBuilder(Of SynthesizedLocal).GetInstance
            End If

            If Not arguments.IsDefaultOrEmpty Then
                Dim argumentBuilder = ArrayBuilder(Of BoundExpression).GetInstance
                Dim writeTargetsBuilder = ArrayBuilder(Of BoundExpression).GetInstance
                For Each argument In arguments
                    Dim writeTarget As BoundExpression

                    If Not argument.IsSupportingAssignment() Then
                        ' in this case writeTarget will not be used for assignment
                        writeTarget = Nothing

                    Else
                        Dim argumentWithCapture As BoundLateBoundArgumentSupportingAssignmentWithCapture = Nothing

                        If argument.Kind = BoundKind.LateBoundArgumentSupportingAssignmentWithCapture Then
                            argumentWithCapture = DirectCast(argument, BoundLateBoundArgumentSupportingAssignmentWithCapture)
                            argument = argumentWithCapture.OriginalArgument
                        End If

                        Dim useTwice = UseTwiceRewriter.UseTwice(container, argument, isForRegularCompoundAssignment:=False, temps)

                        If argument.IsPropertyOrXmlPropertyAccess Then
                            argument = useTwice.First.SetAccessKind(PropertyAccessKind.Get)
                            writeTarget = useTwice.Second.SetAccessKind(PropertyAccessKind.Set)
                        ElseIf argument.IsLateBound() Then
                            argument = useTwice.First.SetLateBoundAccessKind(LateBoundAccessKind.Get)
                            writeTarget = useTwice.Second.SetLateBoundAccessKind(LateBoundAccessKind.Set)
                        Else
                            argument = useTwice.First.MakeRValue()
                            writeTarget = useTwice.Second
                        End If

                        If argumentWithCapture IsNot Nothing Then
                            argument = New BoundAssignmentOperator(argumentWithCapture.Syntax,
                                                                   New BoundLocal(argumentWithCapture.Syntax,
                                                                                  argumentWithCapture.LocalSymbol,
                                                                                  argumentWithCapture.LocalSymbol.Type),
                                                                   argument,
                                                                   suppressObjectClone:=True,
                                                                   type:=argumentWithCapture.Type)
                        End If
                    End If

                    argumentBuilder.Add(VisitExpressionNode(argument))
                    writeTargetsBuilder.Add(writeTarget)
                Next

                arguments = argumentBuilder.ToImmutableAndFree
                writeTargets = writeTargetsBuilder.ToImmutableAndFree
            End If
        End Sub

        ' TODO: 
        ' ================= GENERAL PURPOSE, MOVE TO COMMON FILE

        Private Shared Function MakeStringLiteral(node As SyntaxNode,
                                           value As String,
                                           stringType As TypeSymbol) As BoundLiteral

            If value Is Nothing Then
                Return MakeNullLiteral(node, stringType)
            Else
                Return New BoundLiteral(node, ConstantValue.Create(value), stringType)
            End If
        End Function

        Private Shared Function MakeBooleanLiteral(node As SyntaxNode,
                                   value As Boolean,
                                   booleanType As TypeSymbol) As BoundLiteral

            Return New BoundLiteral(node, ConstantValue.Create(value), booleanType)
        End Function

        Private Function MakeGetTypeExpression(node As SyntaxNode,
                                               type As TypeSymbol,
                                               typeType As TypeSymbol) As BoundExpression

            Dim factory As New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, node, _compilationState, _diagnostics)
            Return factory.Typeof(type, typeType)
        End Function

        Private Function MakeArrayOfGetTypeExpressions(node As SyntaxNode,
                                       types As ImmutableArray(Of TypeSymbol),
                                       typeArrayType As TypeSymbol) As BoundArrayCreation

            Dim intType = Me.GetSpecialType(SpecialType.System_Int32)
            Dim bounds As BoundExpression = New BoundLiteral(node, ConstantValue.Create(types.Length), intType)

            Dim typeType = DirectCast(typeArrayType, ArrayTypeSymbol).ElementType
            Dim initializers = ArrayBuilder(Of BoundExpression).GetInstance
            For Each t In types
                initializers.Add(MakeGetTypeExpression(node, t, typeType))
            Next

            Dim initializer = New BoundArrayInitialization(node, initializers.ToImmutableAndFree, Nothing)

            Return New BoundArrayCreation(node, ImmutableArray.Create(bounds), initializer, typeArrayType)
        End Function

        Private Function TryGetWellknownMember(Of T As Symbol)(<Out> ByRef result As T,
                                                               memberId As WellKnownMember,
                                                               syntax As SyntaxNode,
                                                               Optional isOptional As Boolean = False) As Boolean

            result = Nothing
            Dim useSiteInfo As UseSiteInfo(Of AssemblySymbol) = Nothing
            Dim memberSymbol = Binder.GetWellKnownTypeMember(Me.Compilation, memberId, useSiteInfo)

            If useSiteInfo.DiagnosticInfo IsNot Nothing Then
                If Not isOptional Then
                    Binder.ReportUseSite(_diagnostics, syntax.GetLocation(), useSiteInfo)
                End If

                Return False
            End If

            _diagnostics.AddDependencies(useSiteInfo)
            result = DirectCast(memberSymbol, T)
            Return True
        End Function

        ''' <summary>
        ''' Attempt to retrieve the specified special member, reporting a use-site diagnostic if the member is not found.
        ''' </summary>
        Private Function TryGetSpecialMember(Of T As Symbol)(<Out> ByRef result As T,
                                                       memberId As SpecialMember,
                                                       syntax As SyntaxNode,
                                                       Optional isOptional As Boolean = False) As Boolean

            result = Nothing
            Dim useSiteInfo As UseSiteInfo(Of AssemblySymbol) = Nothing
            Dim memberSymbol = Binder.GetSpecialTypeMember(Me._topMethod.ContainingAssembly, memberId, useSiteInfo)

            If useSiteInfo.DiagnosticInfo IsNot Nothing Then
                If Not isOptional Then
                    Binder.ReportUseSite(_diagnostics, syntax.GetLocation(), useSiteInfo)
                End If

                Return False
            End If

            _diagnostics.AddDependencies(useSiteInfo)
            result = DirectCast(memberSymbol, T)
            Return True
        End Function

    End Class
End Namespace