File: Lowering\LocalRewriter\LocalRewriter_AsNewLocalDeclarations.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.Diagnostics
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
 
Namespace Microsoft.CodeAnalysis.VisualBasic
    Partial Friend NotInheritable Class LocalRewriter
        Public Overrides Function VisitAsNewLocalDeclarations(node As BoundAsNewLocalDeclarations) As BoundNode
            Dim builder = ArrayBuilder(Of BoundStatement).GetInstance()
 
            Dim localDeclarations = node.LocalDeclarations
            For declarationIndex = 0 To localDeclarations.Length - 1
                Dim localDeclaration = localDeclarations(declarationIndex)
                Debug.Assert(localDeclaration.InitializerOpt Is Nothing)
 
                Dim rewrittenInitializer As BoundNode
 
                Dim localSymbol = localDeclaration.LocalSymbol
                Dim staticLocalBackingFields As KeyValuePair(Of SynthesizedStaticLocalBackingField, SynthesizedStaticLocalBackingField) = Nothing
 
                If localSymbol.IsStatic Then
                    staticLocalBackingFields = CreateBackingFieldsForStaticLocal(localSymbol, hasInitializer:=True)
                End If
 
                Dim initializerToRewrite As BoundExpression
 
                If declarationIndex = 0 Then
                    initializerToRewrite = node.Initializer
                Else
                    ' For all variables except the first one we rebind the initializer
                    ' and throw away diagnostics, those are supposed to be reported in the first
                    ' binding and don't need to be duplicated as Dev10/11 does
                    '
                    ' Note that we have to rebind the initializers because current implementation of lambda 
                    ' rewriter does not handle correctly blocks and locals which are reused in bound tree. 
                    ' Actually it seems to heavily rely on an assumption that the bound tree is a tree, not 
                    ' a DAG, with just minor deviations. Thus, the natural way of just rewriting bound 
                    ' initializer stored in node.Initializer simply does not work because rewriting *may* 
                    ' keep many blocks and locals unchanged and reuse them in all rewritten initializers, 
                    ' in which case if we have a lambda inside the initializer lambda rewriter simply throws.
                    '
                    ' Another option to satisfy lambda rewriter would be to deep-clone bound tree, but 
                    ' in this case we will also have to clone all locals and all references to locals.
                    ' We might want to look into this option later.
 
                    Debug.Assert(node.Syntax IsNot Nothing)
                    Debug.Assert(node.Syntax.Kind = SyntaxKind.VariableDeclarator)
 
                    Dim varDecl = DirectCast(node.Syntax, VariableDeclaratorSyntax)
                    Dim asNew = DirectCast(varDecl.AsClause, AsNewClauseSyntax)
 
                    ' Rebind and discard diagnostics
                    initializerToRewrite = node.Binder.BindVariableDeclaration(varDecl, varDecl.Names(declarationIndex), asNew, Nothing, BindingDiagnosticBag.Discarded, skipAsNewInitializer:=False).InitializerOpt
                End If
 
                ' The initializer expression may contain placeholder values and typically they are replaced with bound locals that
                ' get created in the local rewriter when rewriting the expression. 
                ' 
                ' There is one case where the placeholder does not get replaced by a bound temporary and needs to be replaced by
                ' the currently declared local. This is the case for an object creation expression with a object initializer if it's
                ' used in an AsNew declaration and the type is a value type, e.g.:
                ' Dim x As New ValType() With {...} 
                '     or
                ' Dim x, y As New ValType() With {...} 
                ' Because only this method knows the bound local that the placeholder will be replaced with, we need to fill 
                ' the replacement map here, before rewriting the local declaration (there is a special case, because the first 
                ' example will be bound to a BoundLocalDeclaration, see LocalRewriter.VisitLocalDeclaration for the special case).
                '
                Dim objectInitializer As BoundObjectInitializerExpression = GetBoundObjectInitializerFromInitializer(initializerToRewrite)
 
                ' rewrite the initializer for each declared variable because the initializer may contain placeholders that need
                ' to be replaced with the current bound local
 
                If objectInitializer IsNot Nothing Then
                    Debug.Assert(objectInitializer.PlaceholderOpt IsNot Nothing)
 
                    Dim placeholder As BoundWithLValueExpressionPlaceholder = objectInitializer.PlaceholderOpt
 
                    If Not objectInitializer.CreateTemporaryLocalForInitialization Then
                        AddPlaceholderReplacement(placeholder,
                                                  VisitExpressionNode(New BoundLocal(localDeclaration.Syntax,
                                                                      localSymbol,
                                                                      localSymbol.Type)))
                    End If
 
                    rewrittenInitializer = Me.VisitAndGenerateObjectCloneIfNeeded(initializerToRewrite)
 
                    If Not objectInitializer.CreateTemporaryLocalForInitialization Then
                        RemovePlaceholderReplacement(placeholder)
                    End If
 
                Else
                    rewrittenInitializer = Me.VisitAndGenerateObjectCloneIfNeeded(initializerToRewrite)
                End If
 
                Dim initialization = RewriteLocalDeclarationAsInitializer(
                    localDeclaration,
                    DirectCast(rewrittenInitializer, BoundExpression),
                    staticLocalBackingFields,
                    objectInitializer Is Nothing OrElse objectInitializer.CreateTemporaryLocalForInitialization)
 
                builder.Add(initialization)
            Next
 
            Return New BoundStatementList(node.Syntax, builder.ToImmutableAndFree())
        End Function
 
        Private Shared Function GetBoundObjectInitializerFromInitializer(initializer As BoundExpression) As BoundObjectInitializerExpression
            If initializer IsNot Nothing AndAlso (initializer.Kind = BoundKind.ObjectCreationExpression OrElse initializer.Kind = BoundKind.NewT) Then
                Dim objectCreationExpression = DirectCast(initializer, BoundObjectCreationExpressionBase)
                Return TryCast(objectCreationExpression.InitializerOpt, BoundObjectInitializerExpression)
            End If
            Return Nothing
        End Function
 
    End Class
End Namespace