File: Lowering\LocalRewriter\LocalRewriter_LateBindingHelpers.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.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