File: BoundTree\BoundAssignmentOperator.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 Microsoft.CodeAnalysis.VisualBasic.Symbols
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Partial Friend NotInheritable Class BoundAssignmentOperator
        Inherits BoundExpression
 
        Public Sub New(syntax As SyntaxNode, left As BoundExpression, right As BoundExpression, suppressObjectClone As Boolean, type As TypeSymbol, Optional hasErrors As Boolean = False)
            Me.New(syntax, left, leftOnTheRightOpt:=Nothing, right:=right, suppressObjectClone:=suppressObjectClone, type:=type, hasErrors:=hasErrors)
        End Sub
 
        Public Sub New(syntax As SyntaxNode, left As BoundExpression, right As BoundExpression, suppressObjectClone As Boolean, Optional hasErrors As Boolean = False)
            Me.New(syntax, left, leftOnTheRightOpt:=Nothing, right:=right, suppressObjectClone:=suppressObjectClone, hasErrors:=hasErrors)
        End Sub
 
        Public Sub New(
            syntax As SyntaxNode,
            left As BoundExpression,
            leftOnTheRightOpt As BoundCompoundAssignmentTargetPlaceholder,
            right As BoundExpression,
            suppressObjectClone As Boolean,
            Optional hasErrors As Boolean = False
        )
            'NOTE: even though in general assignment returns the value of the Right,
            '      the type of the operator is the type of the Left as that is the type of the location 
            '      into which the Right is stored.
            '
            '       Dim x as Long
            '       Dim y as byte = 1
            '       Dim z := x := y       ' if this was legal assignment of an assignment, z would have type Long
            '
            '      properties and latebound assignments are special cased since they are semantically statements
            '
            'TODO: it could make sense to have BoundAssignmentStatement just for the purpose of 
            '      binding assignments. It would make invariants for both BoundAssignmentExpression and BoundAssignmentStatement simpler.
            Me.New(syntax, left, leftOnTheRightOpt, right:=right, suppressObjectClone:=suppressObjectClone,
                   type:=If(left.IsPropertyOrXmlPropertyAccess(),
                            left.GetPropertyOrXmlProperty().ContainingAssembly.GetSpecialType(SpecialType.System_Void),
                            If(left.IsLateBound,
                               left.Type.ContainingAssembly.GetSpecialType(SpecialType.System_Void),
                               left.Type)),
                   hasErrors:=hasErrors)
        End Sub
 
#If DEBUG Then
        Private Sub Validate()
            Debug.Assert(Left.Type IsNot Nothing)
 
            If Not HasErrors Then
                Debug.Assert(Left.IsLValue OrElse Left.IsPropertyOrXmlPropertyAccess() OrElse Left.IsLateBound)
 
                Select Case Left.Kind
                    Case BoundKind.PropertyAccess
                        Dim propertyAccess = DirectCast(Left, BoundPropertyAccess)
                        Debug.Assert(propertyAccess.AccessKind = If(DirectCast(Left, BoundPropertyAccess).PropertySymbol.ReturnsByRef,
                                     PropertyAccessKind.Get,
                                     If(LeftOnTheRightOpt Is Nothing, PropertyAccessKind.Set, PropertyAccessKind.Set Or PropertyAccessKind.Get)))
                        Debug.Assert(Type.IsVoidType())
 
                    Case BoundKind.XmlMemberAccess
                        Debug.Assert(Left.GetAccessKind() = If(LeftOnTheRightOpt Is Nothing, PropertyAccessKind.Set, PropertyAccessKind.Set Or PropertyAccessKind.Get))
                        Debug.Assert(Type.IsVoidType())
 
                    Case BoundKind.LateMemberAccess
                        Debug.Assert(Left.GetLateBoundAccessKind() = If(LeftOnTheRightOpt Is Nothing, LateBoundAccessKind.Set, LateBoundAccessKind.Set Or LateBoundAccessKind.Get))
 
                    Case BoundKind.LateInvocation
                        Dim invocation = DirectCast(Left, BoundLateInvocation)
                        Debug.Assert(invocation.AccessKind = If(LeftOnTheRightOpt Is Nothing, LateBoundAccessKind.Set, LateBoundAccessKind.Set Or LateBoundAccessKind.Get))
 
                        If Not invocation.ArgumentsOpt.IsDefault Then
                            For Each arg In invocation.ArgumentsOpt
                                Debug.Assert(Not arg.IsSupportingAssignment())
                            Next
                        End If
 
                    Case Else
                        Debug.Assert(Not Left.IsLateBound)
                End Select
 
                Debug.Assert(Left.Type.IsSameTypeIgnoringAll(Right.Type))
            End If
 
            Right.AssertRValue()
            Debug.Assert(Left.IsPropertyOrXmlPropertyAccess() OrElse
                         Left.IsLateBound OrElse
                         IsByRefPropertyGet(Left) OrElse
                         Left.Type.IsSameTypeIgnoringAll(Type) OrElse
                         (Type.IsVoidType() AndAlso Syntax.Kind = SyntaxKind.MidAssignmentStatement) OrElse
                         (Left.Kind = BoundKind.FieldAccess AndAlso
                                DirectCast(Left, BoundFieldAccess).FieldSymbol.AssociatedSymbol.Kind = SymbolKind.Property AndAlso
                                Type.IsVoidType()))
 
            ' If LeftOnTheRightOpt is not nothing and this isn't a mid statement, then we assert the shape that IOperation is expecting
            ' compound expressions to have
            If LeftOnTheRightOpt IsNot Nothing Then
                ' The right node is either a BoundUserDefinedBinaryOperator or a BoundBinaryOperator, or a conversion on top of them
                Dim rightNode = Right
 
                If rightNode.Kind = BoundKind.Conversion Then
                    rightNode = DirectCast(rightNode, BoundConversion).Operand
 
                    If rightNode.Kind = BoundKind.UserDefinedConversion Then
                        rightNode = DirectCast(rightNode, BoundUserDefinedConversion).Operand
                    End If
                End If
 
                If rightNode.Kind <> BoundKind.MidResult Then
                    Debug.Assert(rightNode.Kind = BoundKind.BinaryOperator OrElse
                             rightNode.Kind = BoundKind.UserDefinedBinaryOperator)
 
                    Dim leftNode As BoundNode = Nothing
                    If rightNode.Kind = BoundKind.BinaryOperator Then
                        leftNode = DirectCast(rightNode, BoundBinaryOperator).Left
                    Else
                        Dim boundUserDefinedOperator = DirectCast(rightNode, BoundUserDefinedBinaryOperator)
                        If boundUserDefinedOperator.UnderlyingExpression.Kind = BoundKind.Call Then
                            leftNode = boundUserDefinedOperator.Left
                        Else
                            leftNode = DirectCast(boundUserDefinedOperator.UnderlyingExpression, BoundBadExpression).ChildBoundNodes(0)
                        End If
                    End If
 
                    ' The left node of the binary operation is either the same node as in LeftOnTheRightOpt, or is a conversion on top of that node
                    If leftNode.Kind = BoundKind.Conversion Then
                        leftNode = DirectCast(leftNode, BoundConversion).Operand
 
                        If leftNode.Kind = BoundKind.UserDefinedConversion Then
                            leftNode = DirectCast(leftNode, BoundUserDefinedConversion).Operand
                        End If
                    End If
 
                    Debug.Assert(leftNode Is LeftOnTheRightOpt)
                End If
            End If
        End Sub
 
        Private Shared Function IsByRefPropertyGet(node As BoundExpression) As Boolean
            Dim value = TryCast(TryCast(node, BoundCall)?.Method?.AssociatedSymbol, PropertySymbol)?.ReturnsByRef
            Return value.HasValue AndAlso value.Value = True
        End Function
#End If
 
    End Class
End Namespace