File: Lowering\LocalRewriter\LocalRewriter_ObjectCreation.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.Diagnostics
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic
    Partial Friend NotInheritable Class LocalRewriter
        Public Overrides Function VisitObjectCreationExpression(node As BoundObjectCreationExpression) As BoundNode
 
            ' save the object initializer away to rewrite them later on and set the initializers to nothing to not rewrite them
            ' two times.
            Dim objectInitializer = node.InitializerOpt
            node = node.Update(node.ConstructorOpt, node.Arguments, node.DefaultArguments, Nothing, node.Type)
 
            Dim ctor = node.ConstructorOpt
            Dim result As BoundExpression = node
 
            If ctor IsNot Nothing Then
                Dim temporaries As ImmutableArray(Of SynthesizedLocal) = Nothing
                Dim copyBack As ImmutableArray(Of BoundExpression) = Nothing
 
                result = node.Update(ctor,
                                     RewriteCallArguments(node.Arguments, ctor.Parameters, temporaries, copyBack, False),
                                     node.DefaultArguments,
                                     Nothing,
                                     ctor.ContainingType)
 
                If Not temporaries.IsDefault Then
                    result = GenerateSequenceValueSideEffects(_currentMethodOrLambda, result, StaticCast(Of LocalSymbol).From(temporaries), copyBack)
                End If
 
                ' If a coclass was instantiated, convert the class to the interface type.
                If node.Type.IsInterfaceType() Then
                    Debug.Assert(result.Type.Equals(DirectCast(node.Type, NamedTypeSymbol).CoClassType))
 
                    Dim useSiteInfo = GetNewCompoundUseSiteInfo()
                    Dim conv As ConversionKind = Conversions.ClassifyDirectCastConversion(result.Type, node.Type, useSiteInfo)
                    Debug.Assert(Conversions.ConversionExists(conv))
                    _diagnostics.Add(result, useSiteInfo)
                    result = New BoundDirectCast(node.Syntax, result, conv, node.Type, Nothing)
                Else
                    Debug.Assert(node.Type.IsSameTypeIgnoringAll(result.Type))
                End If
            End If
 
            If objectInitializer IsNot Nothing Then
                Return VisitObjectCreationInitializer(objectInitializer, node, result)
            End If
 
            Return result
        End Function
 
        Public Overrides Function VisitNoPiaObjectCreationExpression(node As BoundNoPiaObjectCreationExpression) As BoundNode
            ' For the NoPIA feature, we need to gather the GUID from the coclass, and 
            ' generate the following:
            ' DirectCast(System.Activator.CreateInstance(System.Runtime.InteropServices.Marshal.GetTypeFromCLSID(New Guid(GUID))), IPiaType)
            '
            ' If System.Runtime.InteropServices.Marshal.GetTypeFromCLSID is not available (older framework),
            ' System.Type.GetTypeFromCLSID() is used to get the type for the CLSID.
 
            Dim factory As New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, node.Syntax, _compilationState, _diagnostics)
 
            Dim ctor = factory.WellKnownMember(Of MethodSymbol)(WellKnownMember.System_Guid__ctor)
            Dim newGuid As BoundExpression
            If ctor IsNot Nothing Then
                newGuid = factory.[New](ctor, factory.Literal(node.GuidString))
            Else
                newGuid = New BoundBadExpression(node.Syntax, LookupResultKind.NotCreatable, ImmutableArray(Of Symbol).Empty, ImmutableArray(Of BoundExpression).Empty, ErrorTypeSymbol.UnknownResultType, hasErrors:=True)
            End If
 
            Dim getTypeFromCLSID = If(factory.WellKnownMember(Of MethodSymbol)(WellKnownMember.System_Runtime_InteropServices_Marshal__GetTypeFromCLSID, isOptional:=True),
                   factory.WellKnownMember(Of MethodSymbol)(WellKnownMember.System_Type__GetTypeFromCLSID))
            Dim callGetTypeFromCLSID As BoundExpression
            If getTypeFromCLSID IsNot Nothing Then
                callGetTypeFromCLSID = factory.Call(Nothing, getTypeFromCLSID, newGuid)
            Else
                callGetTypeFromCLSID = New BoundBadExpression(node.Syntax, LookupResultKind.OverloadResolutionFailure, ImmutableArray(Of Symbol).Empty, ImmutableArray(Of BoundExpression).Empty, ErrorTypeSymbol.UnknownResultType, hasErrors:=True)
            End If
 
            Dim createInstance = factory.WellKnownMember(Of MethodSymbol)(WellKnownMember.System_Activator__CreateInstance)
            Dim rewrittenObjectCreation As BoundExpression
            If createInstance IsNot Nothing AndAlso Not createInstance.ReturnType.IsErrorType() Then
                Dim useSiteInfo = GetNewCompoundUseSiteInfo()
                Dim conversion = Conversions.ClassifyDirectCastConversion(createInstance.ReturnType, node.Type, useSiteInfo)
                _diagnostics.Add(node, useSiteInfo)
                rewrittenObjectCreation = New BoundDirectCast(node.Syntax, factory.Call(Nothing, createInstance, callGetTypeFromCLSID), conversion, node.Type)
            Else
                rewrittenObjectCreation = New BoundBadExpression(node.Syntax, LookupResultKind.OverloadResolutionFailure, ImmutableArray(Of Symbol).Empty, ImmutableArray(Of BoundExpression).Empty, node.Type, hasErrors:=True)
            End If
 
            If node.InitializerOpt Is Nothing OrElse node.InitializerOpt.HasErrors Then
                Return rewrittenObjectCreation
            End If
 
            Return VisitObjectCreationInitializer(node.InitializerOpt, node, rewrittenObjectCreation)
        End Function
 
        Private Function VisitObjectCreationInitializer(
            objectInitializer As BoundObjectInitializerExpressionBase,
            objectCreationExpression As BoundExpression,
            rewrittenObjectCreationExpression As BoundExpression
        ) As BoundNode
            If objectInitializer.Kind = BoundKind.CollectionInitializerExpression Then
                Return RewriteCollectionInitializerExpression(DirectCast(objectInitializer, BoundCollectionInitializerExpression),
                                                              objectCreationExpression, rewrittenObjectCreationExpression)
            Else
                Return RewriteObjectInitializerExpression(DirectCast(objectInitializer, BoundObjectInitializerExpression),
                                                          objectCreationExpression, rewrittenObjectCreationExpression)
            End If
        End Function
 
        Public Overrides Function VisitNewT(node As BoundNewT) As BoundNode
            ' Unlike C#, "New T()" is always rewritten as "Activator.CreateInstance<T>()",
            ' even if T is known to be a value type or reference type. This matches Dev10 VB.
 
            If _inExpressionLambda Then
                ' NOTE: If we are in expression lambda, we want to keep BoundNewT 
                ' NOTE: node, but we need to rewrite initializers if any.
 
                If node.InitializerOpt IsNot Nothing Then
                    Return VisitObjectCreationInitializer(node.InitializerOpt, node, node)
                Else
                    Return node
                End If
            End If
 
            Dim syntax = node.Syntax
            Dim typeParameter = DirectCast(node.Type, TypeParameterSymbol)
 
            Dim result As BoundExpression
 
            Dim method As MethodSymbol = Nothing
            If TryGetWellknownMember(method, WellKnownMember.System_Activator__CreateInstance_T, syntax) Then
                Debug.Assert(method IsNot Nothing)
                method = method.Construct(ImmutableArray.Create(Of TypeSymbol)(typeParameter))
 
                result = New BoundCall(syntax,
                                       method,
                                       methodGroupOpt:=Nothing,
                                       receiverOpt:=Nothing,
                                       arguments:=ImmutableArray(Of BoundExpression).Empty,
                                       constantValueOpt:=Nothing,
                                       isLValue:=False,
                                       suppressObjectClone:=False,
                                       type:=typeParameter)
            Else
                result = New BoundBadExpression(syntax, LookupResultKind.NotReferencable, ImmutableArray(Of Symbol).Empty, ImmutableArray(Of BoundExpression).Empty, typeParameter, hasErrors:=True)
            End If
 
            If node.InitializerOpt IsNot Nothing Then
                Return VisitObjectCreationInitializer(node.InitializerOpt, result, result)
            End If
 
            Return result
        End Function
 
        ''' <summary>
        ''' Rewrites a CollectionInitializerExpression to a list of Add calls and returns the temporary.
        ''' E.g. the following code:
        '''     Dim x As New CollectionType(param1) From {1, {2, 3}, {4, {5, 6, 7}}}
        ''' gets rewritten to 
        '''     Dim temp as CollectionType 
        '''     temp = new CollectionType(param1)
        '''     temp.Add(1)
        '''     temp.Add(2, 3)
        '''     temp.Add(4, {5, 6, 7})
        '''     x = temp
        ''' where the last assignment is not part of this rewriting, because the BoundCollectionInitializerExpression
        ''' only represents the object creation expression with the initialization.
        ''' </summary>
        ''' <param name="node">The BoundCollectionInitializerExpression that should be rewritten.</param>
        ''' <returns>A bound sequence for the object creation expression containing the invocation expressions.</returns>
        Public Function RewriteCollectionInitializerExpression(
            node As BoundCollectionInitializerExpression,
            objectCreationExpression As BoundExpression,
            rewrittenObjectCreationExpression As BoundExpression
        ) As BoundNode
            Debug.Assert(node.PlaceholderOpt IsNot Nothing)
 
            Dim expressionType = node.Type
 
            Dim syntaxNode = node.Syntax
            Dim tempLocalSymbol As LocalSymbol
            Dim tempLocal As BoundLocal
            Dim expressions = ArrayBuilder(Of BoundExpression).GetInstance()
            Dim newPlaceholder As BoundWithLValueExpressionPlaceholder
 
            If _inExpressionLambda Then
                ' A temp is not needed for this case 
                tempLocalSymbol = Nothing
                tempLocal = Nothing
 
                ' Simply replace placeholder with a copy, it will be dropped by Expression Tree rewriter. The copy is needed to 
                ' keep the double rewrite tracking happy.
                newPlaceholder = New BoundWithLValueExpressionPlaceholder(node.PlaceholderOpt.Syntax, node.PlaceholderOpt.Type)
                AddPlaceholderReplacement(node.PlaceholderOpt, newPlaceholder)
            Else
                ' Create a temp symbol 
                '    Dim temp as CollectionType
 
                ' Create assignment for the rewritten object 
                ' creation expression to the temp
                '    temp = new CollectionType(param1)
                tempLocalSymbol = New SynthesizedLocal(Me._currentMethodOrLambda, expressionType, SynthesizedLocalKind.LoweringTemp)
                tempLocal = New BoundLocal(syntaxNode, tempLocalSymbol, expressionType)
                Dim temporaryAssignment = New BoundAssignmentOperator(syntaxNode,
                                                                  tempLocal,
                                                                  GenerateObjectCloneIfNeeded(objectCreationExpression, rewrittenObjectCreationExpression),
                                                                  suppressObjectClone:=True,
                                                                  type:=expressionType)
                expressions.Add(temporaryAssignment)
 
                newPlaceholder = Nothing
                AddPlaceholderReplacement(node.PlaceholderOpt, tempLocal)
            End If
 
            Dim initializerCount = node.Initializers.Length
 
            ' rewrite the invocation expressions and add them to the expression of the sequence
            '    temp.Add(...)
            For initializerIndex = 0 To initializerCount - 1
                ' NOTE: if the method Add(...) is omitted we build a local which
                '       seems to be redundant, this will optimized out later 
                '       by stack scheduler
                Dim initializer As BoundExpression = node.Initializers(initializerIndex)
                If Not IsOmittedBoundCall(initializer) Then
                    expressions.Add(VisitExpressionNode(initializer))
                End If
            Next
 
            RemovePlaceholderReplacement(node.PlaceholderOpt)
 
            If _inExpressionLambda Then
                Debug.Assert(tempLocalSymbol Is Nothing)
                Debug.Assert(tempLocal Is Nothing)
 
                ' NOTE: if inside expression lambda we rewrite the collection initializer 
                ' NOTE: node and attach it back to object creation expression, it will be 
                ' NOTE: rewritten later in ExpressionLambdaRewriter
 
                ' Rewrite object creation
                Return ReplaceObjectOrCollectionInitializer(
                            rewrittenObjectCreationExpression,
                            node.Update(newPlaceholder,
                                        expressions.ToImmutableAndFree(),
                                        node.Type))
            Else
                Debug.Assert(tempLocalSymbol IsNot Nothing)
                Debug.Assert(tempLocal IsNot Nothing)
 
                Return New BoundSequence(syntaxNode,
                                     ImmutableArray.Create(Of LocalSymbol)(tempLocalSymbol),
                                     expressions.ToImmutableAndFree(),
                                     tempLocal.MakeRValue(),
                                     expressionType)
            End If
        End Function
 
        ''' <summary>
        ''' Rewrites a ObjectInitializerExpression to either a statement list (in case the there is no temporary used) or a bound
        ''' sequence expression (in case there is a temporary used). The information whether to use a temporary or not is 
        ''' stored in the bound object member initializer node itself.
        ''' 
        ''' E.g. the following code:
        '''     Dim x = New RefTypeName(param1) With {.FieldName1 = 23, .FieldName2 = .FieldName3, .FieldName4 = x.FieldName1}
        ''' gets rewritten to 
        '''     Dim temp as RefTypeName 
        '''     temp = new RefTypeName(param1)
        '''     temp.FieldName1 = 23
        '''     temp.FieldName2 = temp.FieldName3
        '''     temp.FieldName4 = x.FieldName1
        '''     x = temp
        ''' where the last assignment is not part of this rewriting, because the BoundObjectInitializerExpression
        ''' only represents the object creation expression with the initialization.
        ''' 
        ''' In a case where no temporary is used the following code:
        '''     Dim x As New ValueTypeName(param1) With {.FieldName1 = 23, .FieldName2 = .FieldName3, .FieldName4 = x.FieldName1}
        ''' gets rewritten to 
        '''     x = new ValueTypeName(param1)
        '''     x.FieldName1 = 23
        '''     x.FieldName2 = x.FieldName3
        '''     x.FieldName4 = x.FieldName1
        ''' </summary>
        ''' <param name="node">The BoundObjectInitializerExpression that should be rewritten.</param>
        ''' <returns>A bound sequence for the object creation expression containing the invocation expressions, or a 
        ''' bound statement list if no temporary should be used.</returns>
        Public Function RewriteObjectInitializerExpression(
            node As BoundObjectInitializerExpression,
            objectCreationExpression As BoundExpression,
            rewrittenObjectCreationExpression As BoundExpression
        ) As BoundNode
            Dim targetObjectReference As BoundExpression
            Dim expressionType = node.Type
            Dim initializerCount = node.Initializers.Length
            Dim syntaxNode = node.Syntax
            Dim sequenceType As TypeSymbol
            Dim sequenceTemporaries As ImmutableArray(Of LocalSymbol)
            Dim sequenceValueExpression As BoundExpression
 
            Debug.Assert(node.PlaceholderOpt IsNot Nothing)
 
            ' NOTE: If we are in an expression lambda not all object initializers are allowed, essentially
            ' NOTE: everything requiring temp local creation is disabled; this rule is not applicable to 
            ' NOTE: locals that are created and ONLY used on left-hand-side of initializer assignments,
            ' NOTE: ExpressionLambdaRewriter will get rid of them
            ' NOTE: In order ExpressionLambdaRewriter to be able to detect such locals being used on the 
            ' NOTE: *right* side of initializer assignments we rewrite node.PlaceholderOpt into itself
 
            If node.CreateTemporaryLocalForInitialization Then
                ' create temporary
                '    Dim temp as RefTypeName 
                Dim tempLocalSymbol As LocalSymbol = New SynthesizedLocal(Me._currentMethodOrLambda, expressionType, SynthesizedLocalKind.LoweringTemp)
                sequenceType = expressionType
 
                sequenceTemporaries = ImmutableArray.Create(Of LocalSymbol)(tempLocalSymbol)
 
                targetObjectReference = If(_inExpressionLambda,
                                           DirectCast(node.PlaceholderOpt, BoundExpression),
                                           New BoundLocal(syntaxNode, tempLocalSymbol, expressionType))
                sequenceValueExpression = targetObjectReference.MakeRValue()
                AddPlaceholderReplacement(node.PlaceholderOpt, targetObjectReference)
            Else
                ' Get the receiver for the current initialized variable in case of an "AsNew" declaration
                ' this is the only case where there might be no temporary needed.
                ' The replacement for this placeholder was added in VisitAsNewLocalDeclarations.
                targetObjectReference = PlaceholderReplacement(node.PlaceholderOpt)
                sequenceType = GetSpecialType(SpecialType.System_Void)
 
                sequenceTemporaries = ImmutableArray(Of LocalSymbol).Empty
                sequenceValueExpression = Nothing
            End If
 
            Dim sequenceExpressions(initializerCount) As BoundExpression
 
            ' create assignment for object creation expression to temporary or variable declaration
            '    x = new TypeName(...)
            '       or
            '    temp = new TypeName(...)
            sequenceExpressions(0) = New BoundAssignmentOperator(syntaxNode,
                                                                 targetObjectReference,
                                                                 GenerateObjectCloneIfNeeded(objectCreationExpression, rewrittenObjectCreationExpression),
                                                                 suppressObjectClone:=True,
                                                                 type:=expressionType)
 
            ' rewrite the assignment expressions and add them to the statement list
            '    x.FieldName = value expression
            '        or
            '    temp.FieldName = value expression
            For initializerIndex = 0 To initializerCount - 1
                If _inExpressionLambda Then
                    ' NOTE: Inside expression lambda we rewrite only right-hand-side of the assignments, left part 
                    ' NOTE: will be kept unchanged to make sure we got proper symbol out of it 
                    Dim assignment = DirectCast(node.Initializers(initializerIndex), BoundAssignmentOperator)
                    Debug.Assert(assignment.LeftOnTheRightOpt Is Nothing)
                    sequenceExpressions(initializerIndex + 1) = assignment.Update(assignment.Left,
                                                                                  assignment.LeftOnTheRightOpt,
                                                                                  VisitExpressionNode(assignment.Right),
                                                                                  True,
                                                                                  assignment.Type)
                Else
                    sequenceExpressions(initializerIndex + 1) = VisitExpressionNode(node.Initializers(initializerIndex))
                End If
            Next
 
            If node.CreateTemporaryLocalForInitialization Then
                RemovePlaceholderReplacement(node.PlaceholderOpt)
            End If
 
            If _inExpressionLambda Then
                ' when converting object initializer inside expression lambdas we want to keep 
                ' object initializer in object creation expression; we just store visited initializers 
                ' back to the original object initializer and update the original object creation expression
 
                ' create new initializers
                Dim newInitializers(initializerCount - 1) As BoundExpression
                Dim errors As Boolean = False
                For index = 0 To initializerCount - 1
                    newInitializers(index) = sequenceExpressions(index + 1)
                Next
 
                ' Rewrite object creation
                Return ReplaceObjectOrCollectionInitializer(
                            rewrittenObjectCreationExpression,
                            node.Update(node.CreateTemporaryLocalForInitialization,
                                        node.PlaceholderOpt,
                                        newInitializers.AsImmutableOrNull(),
                                        node.Type))
            End If
 
            Return New BoundSequence(syntaxNode,
                                     sequenceTemporaries,
                                     sequenceExpressions.AsImmutableOrNull,
                                     sequenceValueExpression,
                                     sequenceType)
        End Function
 
        Private Function ReplaceObjectOrCollectionInitializer(rewrittenObjectCreationExpression As BoundExpression, rewrittenInitializer As BoundObjectInitializerExpressionBase) As BoundExpression
            Select Case rewrittenObjectCreationExpression.Kind
                Case BoundKind.ObjectCreationExpression
                    Dim objCreation = DirectCast(rewrittenObjectCreationExpression, BoundObjectCreationExpression)
                    Return objCreation.Update(objCreation.ConstructorOpt, objCreation.Arguments, objCreation.DefaultArguments, rewrittenInitializer, objCreation.Type)
 
                Case BoundKind.NewT
                    Dim newT = DirectCast(rewrittenObjectCreationExpression, BoundNewT)
                    Return newT.Update(rewrittenInitializer, newT.Type)
 
                Case BoundKind.Sequence
                    ' NOTE: is rewrittenObjectCreationExpression is not an object creation expression, it 
                    ' NOTE: was probably wrapped with sequence which means that this case is not supported 
                    ' NOTE: inside expression lambdas.
                    Dim sequence = DirectCast(rewrittenObjectCreationExpression, BoundSequence)
                    Debug.Assert(sequence.ValueOpt IsNot Nothing AndAlso sequence.ValueOpt.Kind = BoundKind.ObjectCreationExpression)
                    Return sequence.Update(sequence.Locals,
                                           sequence.SideEffects,
                                           ReplaceObjectOrCollectionInitializer(sequence.ValueOpt, rewrittenInitializer),
                                           sequence.Type)
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(rewrittenObjectCreationExpression.Kind)
            End Select
        End Function
 
    End Class
End Namespace