File: Lowering\UseTwiceRewriter.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.Collections
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.CodeGen
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Friend NotInheritable Class UseTwiceRewriter
 
        Public Structure Result
            Public Sub New(first As BoundExpression, second As BoundExpression)
                Me.First = first
                Me.Second = second
            End Sub
            Public ReadOnly First As BoundExpression
            Public ReadOnly Second As BoundExpression
        End Structure
 
        Private Sub New()
        End Sub
 
        ''' <summary>
        ''' Given an expression that produces some result and 
        ''' has some observable evaluation side effects, return two expressions:
        '''   1) First - produces the same result with the same observable side effects,
        '''   2) Second - produces the same result, but without observable side effects, whenever possible.
        ''' 
        ''' This is used for compound assignment, method call ByRef parameter copy back, etc.
        ''' </summary>
        Public Shared Function UseTwice(
            containingMember As Symbol,
            value As BoundExpression,
            isForRegularCompoundAssignment As Boolean,
            temporaries As ArrayBuilder(Of SynthesizedLocal)
        ) As Result
            Debug.Assert(value.IsValue())
 
            Select Case value.Kind
                Case BoundKind.XmlMemberAccess
                    Dim memberAccess = DirectCast(value, BoundXmlMemberAccess)
                    Dim result = UseTwice(containingMember, memberAccess.MemberAccess, isForRegularCompoundAssignment, temporaries)
                    Return New Result(memberAccess.Update(result.First), memberAccess.Update(result.Second))
 
                Case BoundKind.PropertyAccess
                    Return UseTwicePropertyAccess(containingMember, DirectCast(value, BoundPropertyAccess), isForRegularCompoundAssignment, temporaries)
 
                Case BoundKind.LateInvocation
                    Return UseTwiceLateInvocation(containingMember, DirectCast(value, BoundLateInvocation), temporaries)
 
                Case BoundKind.LateMemberAccess
                    Return UseTwiceLateMember(containingMember, DirectCast(value, BoundLateMemberAccess), temporaries)
 
                Case Else
                    Debug.Assert(value.Kind <> BoundKind.ByRefArgumentWithCopyBack AndAlso
                                 value.Kind <> BoundKind.LateBoundArgumentSupportingAssignmentWithCapture)
 
                    Dim result = UseTwiceExpression(containingMember, value, temporaries)
 
                    ' LValue-ness of expressions must be preserved
                    Debug.Assert(result.First.IsLValue = result.Second.IsLValue AndAlso result.Second.IsLValue = value.IsLValue)
                    Return result
 
            End Select
        End Function
 
        ' receiver of latebound operation
        Private Shared Function UseTwiceLateBoundReceiver(
            containingMember As Symbol,
            receiverOpt As BoundExpression,
            temporaries As ArrayBuilder(Of SynthesizedLocal)
        ) As Result
 
            Dim receiver As Result
 
            If receiverOpt Is Nothing Then
                receiver = New Result(Nothing, Nothing)
            ElseIf receiverOpt.IsLValue AndAlso receiverOpt.Type.IsReferenceType Then
                Dim boundTemp As BoundLocal = Nothing
                Dim capture As BoundAssignmentOperator = CaptureInATemp(containingMember, receiverOpt.MakeRValue(), temporaries, boundTemp)
                boundTemp = boundTemp.Update(boundTemp.LocalSymbol, isLValue:=True, type:=boundTemp.Type)
                receiver = New Result(New BoundSequence(capture.Syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(Of BoundExpression)(capture), boundTemp, boundTemp.Type),
                                      boundTemp)
            ElseIf Not receiverOpt.IsLValue AndAlso Not receiverOpt.Type.IsReferenceType AndAlso Not receiverOpt.Type.IsValueType Then
                Dim boundTemp As BoundLocal = Nothing
                Dim capture As BoundAssignmentOperator = CaptureInATemp(containingMember, receiverOpt.MakeRValue(), temporaries, boundTemp)
                boundTemp = boundTemp.Update(boundTemp.LocalSymbol, isLValue:=True, type:=boundTemp.Type)
 
                receiver = New Result(New BoundSequence(capture.Syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(Of BoundExpression)(capture), boundTemp, boundTemp.Type),
                                      boundTemp)
            Else
                receiver = UseTwiceExpression(containingMember, receiverOpt, temporaries)
            End If
 
            ' LValue-ness of a receiver should be preserved because it affects how helper method is called.
            Debug.Assert(receiver.Second Is Nothing OrElse (receiverOpt.IsLValue = receiver.First.IsLValue AndAlso receiverOpt.IsLValue = receiver.Second.IsLValue))
            Return receiver
        End Function
 
        Private Shared Function UseTwiceExpression(
            containingMember As Symbol,
            value As BoundExpression,
            temporaries As ArrayBuilder(Of SynthesizedLocal)
        ) As Result
 
            If Not value.IsLValue Then
                Return UseTwiceRValue(containingMember, value, temporaries)
            End If
 
            Select Case value.Kind
                Case BoundKind.Call
                    Return UseTwiceCall(containingMember, DirectCast(value, BoundCall), temporaries)
                Case BoundKind.ArrayAccess
                    Return UseTwiceArrayAccess(containingMember, DirectCast(value, BoundArrayAccess), temporaries)
                Case BoundKind.FieldAccess
                    Return UseTwiceFieldAccess(containingMember, DirectCast(value, BoundFieldAccess), temporaries)
                Case BoundKind.Local,
                     BoundKind.Parameter,
                     BoundKind.PseudoVariable,
                     BoundKind.WithLValueExpressionPlaceholder
                    Return New Result(value, value)
                Case Else
                    Debug.Assert(False) ' Add tests if this assert fires
                    Return UseTwiceRValue(containingMember, value, temporaries)
 
            End Select
        End Function
 
        Private Shared Function CaptureInATemp(
            containingMember As Symbol,
            value As BoundExpression,
            type As TypeSymbol,
            temporaries As ArrayBuilder(Of SynthesizedLocal),
            ByRef referToTemp As BoundLocal
        ) As BoundAssignmentOperator
            Debug.Assert(type IsNot Nothing AndAlso Not type.IsVoidType() AndAlso value.Type Is type)
 
            Dim temp = New SynthesizedLocal(containingMember, type, SynthesizedLocalKind.LoweringTemp)
            temporaries.Add(temp)
 
            referToTemp = New BoundLocal(value.Syntax, temp, type)
            referToTemp.SetWasCompilerGenerated()
            Dim capture = (New BoundAssignmentOperator(value.Syntax, referToTemp, value, suppressObjectClone:=True, type:=type)).MakeCompilerGenerated()
 
            ' Make sure we will not try to write to this local or pass it ByRef.
            referToTemp = referToTemp.MakeRValue()
            Return capture
        End Function
 
        Private Shared Function CaptureInATemp(
            containingMember As Symbol,
            value As BoundExpression,
            temporaries As ArrayBuilder(Of SynthesizedLocal),
            ByRef referToTemp As BoundLocal
        ) As BoundAssignmentOperator
            Return CaptureInATemp(containingMember, value, value.Type, temporaries, referToTemp)
        End Function
 
        Private Shared Function UseTwiceRValue(containingMember As Symbol, value As BoundExpression, arg As ArrayBuilder(Of SynthesizedLocal)) As Result
            Dim kind As BoundKind = value.Kind
 
            If kind = BoundKind.BadVariable OrElse
               kind = BoundKind.MeReference OrElse
               kind = BoundKind.MyBaseReference OrElse
               kind = BoundKind.MyClassReference OrElse
               kind = BoundKind.Literal Then
                Return New Result(value, value)
 
            ElseIf value.IsValue AndAlso value.Type IsNot Nothing AndAlso Not value.Type.IsVoidType() Then
 
                Debug.Assert(Not value.IsLValue)
                Debug.Assert(Not value.IsPropertyOrXmlPropertyAccess() OrElse (value.GetAccessKind() = PropertyAccessKind.Get))
                Debug.Assert(Not value.IsLateBound() OrElse (value.GetLateBoundAccessKind() = LateBoundAccessKind.Get))
 
                Dim constantValue As ConstantValue = value.ConstantValueOpt
 
                If constantValue IsNot Nothing Then
                    Debug.Assert(value.Kind <> BoundKind.Literal)
 
                    Dim second = New BoundLiteral(value.Syntax, constantValue, value.Type)
                    second.SetWasCompilerGenerated()
                    Return New Result(value, second)
                End If
 
                ' TODO: Might need to do some optimization for compiler generated locals.
                '       For example, no reason to recapture a local that is already a capture.
                '       Something to try when we implement WITH statement.
                Dim boundTemp As BoundLocal = Nothing
                Dim first = CaptureInATemp(containingMember, value, arg, boundTemp)
 
                Debug.Assert(Not first.IsLValue AndAlso Not boundTemp.IsLValue)
                Return New Result(first, boundTemp)
            End If
 
            Throw ExceptionUtilities.Unreachable
        End Function
 
        Private Shared Function UseTwiceCall(containingMember As Symbol, node As BoundCall, arg As ArrayBuilder(Of SynthesizedLocal)) As Result
            Debug.Assert(node.IsLValue)
            Return UseTwiceLValue(containingMember, node, arg)
        End Function
 
        Private Shared Function UseTwiceArrayAccess(containingMember As Symbol, node As BoundArrayAccess, arg As ArrayBuilder(Of SynthesizedLocal)) As Result
 
            Debug.Assert(node.IsLValue)
 
#If DONT_USE_BYREF_LOCALS_FOR_USE_TWICE Then
#Else
            If IsInvariantArray(node.Expression.Type) Then
                Return UseTwiceLValue(containingMember, node, arg)
            End If
#End If
 
            ' Note, as an alternative we could capture reference to the array element in a ByRef temp.
            ' However, without an introduction of an indirect assignment node, IL-gen is unable to distinguish 
            ' when it should assign indirect or should assign a reference. For now, decided to not introduce 
            ' special bound nodes for this purpose. Besides, it is not clear whether ByRef temps will make Async 
            ' easier to implement.
            Dim boundArrayTemp As BoundLocal = Nothing
            Dim storeArray = CaptureInATemp(containingMember, node.Expression, arg, boundArrayTemp)
 
            Dim n = node.Indices.Length
            Dim indicesFirst(n - 1) As BoundExpression
            Dim indicesSecond(n - 1) As BoundExpression
 
            For i = 0 To n - 1
                Dim result = UseTwiceRValue(containingMember, node.Indices(i), arg)
                indicesFirst(i) = result.First
                indicesSecond(i) = result.Second
            Next
 
            Dim second = node.Update(boundArrayTemp, indicesSecond.AsImmutableOrNull(), node.IsLValue, node.Type)
            Dim first = node.Update(storeArray, indicesFirst.AsImmutableOrNull(), node.IsLValue, node.Type)
 
            Debug.Assert(first.IsLValue AndAlso second.IsLValue)
            Return New Result(first, second)
 
        End Function
 
        Private Shared Function IsInvariantArray(type As TypeSymbol) As Boolean
            Dim value = TryCast(type, ArrayTypeSymbol)?.ElementType.IsNotInheritable
            Return value.GetValueOrDefault()
        End Function
 
        Private Shared Function UseTwiceLValue(containingMember As Symbol, lvalue As BoundExpression, temporaries As ArrayBuilder(Of SynthesizedLocal)) As Result
            Debug.Assert(lvalue.IsLValue)
            Dim temp = New SynthesizedLocal(containingMember, lvalue.Type, SynthesizedLocalKind.LoweringTemp, isByRef:=True)
            Dim first = New BoundReferenceAssignment(lvalue.Syntax,
                                                  New BoundLocal(lvalue.Syntax, temp, temp.Type).MakeCompilerGenerated(),
                                                  lvalue, isLValue:=True, type:=lvalue.Type).MakeCompilerGenerated()
            temporaries.Add(temp)
            Dim second = New BoundLocal(lvalue.Syntax, temp, isLValue:=True, type:=lvalue.Type).MakeCompilerGenerated()
            Return New Result(first, second)
        End Function
 
        Private Shared Function UseTwiceFieldAccess(containingMember As Symbol, node As BoundFieldAccess, arg As ArrayBuilder(Of SynthesizedLocal)) As Result
 
            Debug.Assert(node.IsLValue)
 
            Dim fieldSymbol = node.FieldSymbol
 
            If fieldSymbol.IsShared AndAlso node.ReceiverOpt IsNot Nothing Then
                ' Get rid of the receiver on second use of the shared field.
                ' It could be an expression that we don't want to visit twice.
                Dim second = node.Update(Nothing, fieldSymbol, node.IsLValue, node.SuppressVirtualCalls, constantsInProgressOpt:=Nothing, node.Type)
 
                Debug.Assert(second.IsLValue)
                Return New Result(node, second)
 
            ElseIf node.ReceiverOpt Is Nothing Then
                Return New Result(node, node)
 
            Else
#If DONT_USE_BYREF_LOCALS_FOR_USE_TWICE Then
                ' Note, as an alternative we could capture reference to the field in a ByRef temp.
                ' However, without an introduction of an indirect assignment node, IL-gen is unable to distinguish 
                ' when it should assign indirect or should assign a reference. For now, decided to not introduce 
                ' special bound nodes for this purpose. Besides, it is not clear whether ByRef temps will make Async 
                ' easier to implement.
 
                Dim receiver As Result = UseTwiceReceiver(containingMember, node.ReceiverOpt, arg)
                Dim first = node.Update(receiver.First, fieldSymbol, node.IsLValue, suppressVirtualCalls:=False, node.ConstantsInProgressOpt, node.Type)
                Dim second = node.Update(receiver.Second, fieldSymbol, node.IsLValue, suppressVirtualCalls:=False, node.ConstantsInProgressOpt, node.Type)
 
                Debug.Assert(first.IsLValue AndAlso second.IsLValue)
                Return New Result(first, second)
#Else
                Return UseTwiceLValue(containingMember, node, arg)
#End If
            End If
 
        End Function
 
        ' We only want to rewrite property access at the top of the expression, not within
        ' the expression. (For instance, P1 should not be rewritten in M(x.P1.P2).)
        Private Shared Function UseTwicePropertyAccess(containingMember As Symbol, node As BoundPropertyAccess, isForRegularCompoundAssignment As Boolean, arg As ArrayBuilder(Of SynthesizedLocal)) As Result
            Dim propertySymbol = node.PropertySymbol
 
            ' Visit receiver.
            Dim receiverOpt As BoundExpression = node.ReceiverOpt
            Dim receiver As Result
 
            If receiverOpt Is Nothing Then
                receiver = New Result(Nothing, Nothing)
            ElseIf node.PropertySymbol.IsShared Then
                receiver = New Result(receiverOpt, Nothing)
            ElseIf receiverOpt.IsLValue AndAlso receiverOpt.Type.IsReferenceType Then
                Dim boundTemp As BoundLocal = Nothing
                receiver = New Result(CaptureInATemp(containingMember, receiverOpt.MakeRValue(), arg, boundTemp), boundTemp)
            ElseIf Not receiverOpt.IsLValue AndAlso Not receiverOpt.Type.IsReferenceType AndAlso Not receiverOpt.Type.IsValueType Then
                Dim boundTemp As BoundLocal = Nothing
                Dim capture As BoundAssignmentOperator = CaptureInATemp(containingMember, receiverOpt.MakeRValue(), arg, boundTemp)
                boundTemp = boundTemp.Update(boundTemp.LocalSymbol, isLValue:=True, type:=boundTemp.Type)
 
                receiver = New Result(New BoundSequence(capture.Syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(Of BoundExpression)(capture), boundTemp, boundTemp.Type),
                                      boundTemp)
            ElseIf receiverOpt.IsLValue AndAlso
                   CodeGenerator.IsPossibleReferenceTypeReceiverOfConstrainedCall(receiverOpt) AndAlso
                   Not CodeGenerator.ReceiverIsKnownToReferToTempIfReferenceType(receiverOpt) AndAlso
                   Not (isForRegularCompoundAssignment AndAlso CodeGenerator.IsSafeToDereferenceReceiverRefAfterEvaluatingArguments(node.Arguments)) Then
 
                Debug.Assert(Not receiverOpt.Type.IsReferenceType)
 
                ' A case where T is actually a class must be handled specially.
                ' Taking a reference to a class instance is fragile because the value behind the 
                ' reference might change while arguments are evaluated. However, the call should be
                ' performed on the instance that is behind reference at the time we push the
                ' reference to the stack. So, for a class we need to emit a reference to a temporary
                ' location, rather than to the original location
 
                receiver = UseTwiceExpression(containingMember, receiverOpt, arg)
 
                Dim cloneTemp As BoundLocal = Nothing
                Dim clone As BoundAssignmentOperator = CaptureInATemp(containingMember, receiver.Second.MakeRValue(), arg, cloneTemp)
 
                Dim complexReceiverFirst = New BoundComplexConditionalAccessReceiver(receiverOpt.Syntax,
                                                                                     receiver.Second,
                                                                                     New BoundSequence(receiverOpt.Syntax, ImmutableArray(Of LocalSymbol).Empty,
                                                                                                       ImmutableArray.Create(Of BoundExpression)(clone),
                                                                                                       cloneTemp.Update(cloneTemp.LocalSymbol, isLValue:=True, cloneTemp.Type),
                                                                                                       cloneTemp.Type).MakeCompilerGenerated(),
                                                                                     receiverOpt.Type).MakeCompilerGenerated()
 
                Dim complexReceiverSecond = New BoundComplexConditionalAccessReceiver(receiverOpt.Syntax,
                                                                                      receiver.Second,
                                                                                      cloneTemp.Update(cloneTemp.LocalSymbol, isLValue:=True, cloneTemp.Type),
                                                                                      receiverOpt.Type).MakeCompilerGenerated()
 
                receiver = New Result(New BoundSequence(receiverOpt.Syntax, ImmutableArray(Of LocalSymbol).Empty,
                                                        ImmutableArray.Create(Of BoundExpression)(receiver.First.MakeRValue()),
                                                        complexReceiverFirst, receiverOpt.Type).MakeCompilerGenerated(),
                                      complexReceiverSecond)
            Else
                receiver = UseTwiceExpression(containingMember, receiverOpt, arg)
            End If
 
            ' Visit args.
            Dim firstArgs As ImmutableArray(Of BoundExpression)
            Dim secondArgs As ImmutableArray(Of BoundExpression)
            If node.Arguments.IsEmpty Then
                firstArgs = ImmutableArray(Of BoundExpression).Empty
                secondArgs = ImmutableArray(Of BoundExpression).Empty
            Else
                Dim nArgs = node.Arguments.Length
                Dim firstArgsArray(nArgs - 1) As BoundExpression
                Dim secondArgsArray(nArgs - 1) As BoundExpression
 
                For i = 0 To nArgs - 1
                    Dim boundArgument As BoundExpression = node.Arguments(i)
                    If boundArgument.Kind = BoundKind.ArrayCreation AndAlso DirectCast(boundArgument, BoundArrayCreation).IsParamArrayArgument Then
                        ' ParamArray argument
                        UseTwiceParamArrayArgument(containingMember, DirectCast(boundArgument, BoundArrayCreation), arg, firstArgsArray(i), secondArgsArray(i))
                    Else
                        ' Regular argument
                        UseTwiceRegularArgument(containingMember, boundArgument, arg, firstArgsArray(i), secondArgsArray(i))
                    End If
                Next
 
                firstArgs = firstArgsArray.AsImmutableOrNull()
                secondArgs = secondArgsArray.AsImmutableOrNull()
            End If
 
            ' Generate PropertyAccess nodes.
            Dim first = node.Update(
                            propertySymbol,
                            node.PropertyGroupOpt,
                            node.AccessKind,
                            isWriteable:=node.IsWriteable,
                            isLValue:=node.IsLValue,
                            receiverOpt:=receiver.First,
                            arguments:=firstArgs,
                            defaultArguments:=node.DefaultArguments,
                            type:=node.Type)
 
            Dim second = node.Update(
                            propertySymbol,
                            node.PropertyGroupOpt,
                            node.AccessKind,
                            isWriteable:=node.IsWriteable,
                            isLValue:=node.IsLValue,
                            receiverOpt:=receiver.Second,
                            arguments:=secondArgs,
                            defaultArguments:=node.DefaultArguments,
                            type:=node.Type)
 
            Return New Result(first, second)
        End Function
 
        Private Shared Function UseTwiceLateInvocation(containingMember As Symbol, node As BoundLateInvocation, arg As ArrayBuilder(Of SynthesizedLocal)) As Result
            ' Visit receiver.
            Dim receiver As Result
            If node.Member.Kind = BoundKind.LateMemberAccess Then
                receiver = UseTwiceLateMember(containingMember, DirectCast(node.Member, BoundLateMemberAccess), arg)
            Else
                receiver = UseTwiceLateBoundReceiver(containingMember, node.Member, arg)
            End If
 
            ' Visit args.
            Dim firstArgs As ImmutableArray(Of BoundExpression)
            Dim secondArgs As ImmutableArray(Of BoundExpression)
            If node.ArgumentsOpt.IsEmpty Then
                firstArgs = ImmutableArray(Of BoundExpression).Empty
                secondArgs = ImmutableArray(Of BoundExpression).Empty
            Else
                Dim nArgs = node.ArgumentsOpt.Length
                Dim firstArgsArray(nArgs - 1) As BoundExpression
                Dim secondArgsArray(nArgs - 1) As BoundExpression
 
                For i = 0 To nArgs - 1
                    Dim boundArgument As BoundExpression = node.ArgumentsOpt(i)
                    ' LateBound argument
                    If Not boundArgument.IsSupportingAssignment() Then
                        UseTwiceRegularArgument(containingMember, boundArgument, arg, firstArgsArray(i), secondArgsArray(i))
                    Else
                        Dim temp = New SynthesizedLocal(containingMember, boundArgument.Type, SynthesizedLocalKind.LoweringTemp)
                        arg.Add(temp)
 
                        firstArgsArray(i) = New BoundLateBoundArgumentSupportingAssignmentWithCapture(boundArgument.Syntax,
                                                                                                      boundArgument,
                                                                                                      temp,
                                                                                                      boundArgument.Type)
                        secondArgsArray(i) = New BoundLocal(boundArgument.Syntax, temp, isLValue:=False, type:=temp.Type)
                    End If
                Next
 
                firstArgs = firstArgsArray.AsImmutableOrNull()
                secondArgs = secondArgsArray.AsImmutableOrNull()
            End If
 
            ' Generate nodes.
            Dim first = node.Update(
                            receiver.First,
                            firstArgs,
                            node.ArgumentNamesOpt,
                            node.AccessKind,
                            node.MethodOrPropertyGroupOpt,
                            node.Type)
 
            Dim second = node.Update(
                            receiver.Second,
                            secondArgs,
                            node.ArgumentNamesOpt,
                            node.AccessKind,
                            node.MethodOrPropertyGroupOpt,
                            node.Type)
 
            Return New Result(first, second)
        End Function
 
        Private Shared Function UseTwiceLateMember(containingMember As Symbol, node As BoundLateMemberAccess, arg As ArrayBuilder(Of SynthesizedLocal)) As Result
            ' Visit receiver.
            Dim receiver As Result = UseTwiceLateBoundReceiver(containingMember, node.ReceiverOpt, arg)
 
            ' Generate nodes.
            Dim first = node.Update(node.NameOpt,
                                    node.ContainerTypeOpt,
                                    receiver.First,
                                    node.TypeArgumentsOpt,
                                    node.AccessKind,
                                    node.Type)
 
            Dim second = node.Update(node.NameOpt,
                                    node.ContainerTypeOpt,
                                    receiver.Second,
                                    node.TypeArgumentsOpt,
                                    node.AccessKind,
                                    node.Type)
 
            Return New Result(first, second)
        End Function
 
        Private Shared Sub UseTwiceRegularArgument(containingMember As Symbol, boundArgument As BoundExpression, arg As ArrayBuilder(Of SynthesizedLocal),
                                                   ByRef first As BoundExpression, ByRef second As BoundExpression)
 
            Debug.Assert(Not boundArgument.IsLValue)
            Dim result = UseTwiceRValue(containingMember, boundArgument, arg)
 
            Debug.Assert(Not result.First.IsLValue AndAlso Not result.Second.IsLValue)
            Debug.Assert(result.First.HasErrors = result.Second.HasErrors AndAlso boundArgument.HasErrors = result.First.HasErrors)
            Debug.Assert(result.First.HasErrors OrElse
                         (result.Second.Kind = BoundKind.Literal AndAlso result.First.ConstantValueOpt IsNot Nothing) OrElse
                         (result.Second.Kind = BoundKind.Local AndAlso result.Second.WasCompilerGenerated AndAlso
                          DirectCast(result.Second, BoundLocal).LocalSymbol.IsCompilerGenerated AndAlso
                          result.First.Kind = BoundKind.AssignmentOperator AndAlso result.First.WasCompilerGenerated AndAlso
                          DirectCast(result.First, BoundAssignmentOperator).Left.Kind = BoundKind.Local AndAlso
                          DirectCast(result.Second, BoundLocal).LocalSymbol Is DirectCast(DirectCast(result.First, BoundAssignmentOperator).Left, BoundLocal).LocalSymbol))
 
            first = result.First
            second = result.Second
        End Sub
 
        Private Shared Sub UseTwiceParamArrayArgument(containingMember As Symbol, boundArray As BoundArrayCreation, arg As ArrayBuilder(Of SynthesizedLocal),
                                                      ByRef first As BoundExpression, ByRef second As BoundExpression)
 
            Debug.Assert(Not boundArray.IsLValue)
            Debug.Assert(boundArray.InitializerOpt IsNot Nothing)
            Debug.Assert(boundArray.ArrayLiteralOpt Is Nothing)
 
            Dim initializer As BoundArrayInitialization = boundArray.InitializerOpt
            Dim initializerSize As Integer = initializer.Initializers.Length
 
            Dim firstArgsArray(initializerSize - 1) As BoundExpression
            Dim secondArgsArray(initializerSize - 1) As BoundExpression
 
            ' Process arguments from bound array creation initializer
            For index = 0 To initializerSize - 1
                UseTwiceRegularArgument(containingMember, initializer.Initializers(index), arg, firstArgsArray(index), secondArgsArray(index))
            Next
 
            ' query will be removed in Production 
            Debug.Assert(boundArray.Bounds.All(Function(expr) expr.Kind = BoundKind.Literal))
 
            ' Finally, duplicate array creation expression with updated initializers
            first = boundArray.Update(boundArray.IsParamArrayArgument, boundArray.Bounds,
                                      initializer.Update(firstArgsArray.AsImmutableOrNull(), initializer.Type), Nothing, Nothing, boundArray.Type)
            second = boundArray.Update(boundArray.IsParamArrayArgument, boundArray.Bounds,
                                       initializer.Update(secondArgsArray.AsImmutableOrNull(), initializer.Type), Nothing, Nothing, boundArray.Type)
        End Sub
 
#If DONT_USE_BYREF_LOCALS_FOR_USE_TWICE Then
        Private Shared Function UseTwiceReceiver(containingMember As Symbol, receiverOpt As BoundExpression, arg As ArrayBuilder(Of SynthesizedLocal)) As Result
            If receiverOpt Is Nothing Then
                Return New Result(Nothing, Nothing)
            ElseIf receiverOpt.IsLValue AndAlso receiverOpt.Type.IsReferenceType Then
                Dim boundTemp As BoundLocal = Nothing
                Dim first = CaptureInATemp(containingMember, receiverOpt.MakeRValue(), arg, boundTemp)
                Return New Result(first, boundTemp)
            Else
                Return UseTwiceExpression(containingMember, receiverOpt, arg)
            End If
        End Function
#End If
    End Class
 
End Namespace