File: Emit\EditAndContinue\VisualBasicSymbolMatcher.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.Concurrent
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
    Friend NotInheritable Class VisualBasicSymbolMatcher
        Inherits SymbolMatcher
 
        Private Shared ReadOnly s_nameComparer As StringComparer = IdentifierComparison.Comparer
 
        Private ReadOnly _visitor As Visitor
 
        Public Sub New(sourceAssembly As SourceAssemblySymbol,
                       otherAssembly As SourceAssemblySymbol,
                       synthesizedTypes As SynthesizedTypeMaps,
                       otherSynthesizedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)),
                       otherDeletedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)))
 
            _visitor = New Visitor(sourceAssembly, otherAssembly, synthesizedTypes, otherSynthesizedMembersOpt, otherDeletedMembersOpt, New DeepTranslator(otherAssembly.GetSpecialType(SpecialType.System_Object)))
        End Sub
 
        Public Sub New(synthesizedTypes As SynthesizedTypeMaps,
                       sourceAssembly As SourceAssemblySymbol,
                       otherAssembly As PEAssemblySymbol)
 
            _visitor = New Visitor(sourceAssembly, otherAssembly, synthesizedTypes, otherSynthesizedMembersOpt:=Nothing, otherDeletedMembers:=Nothing, deepTranslatorOpt:=Nothing)
        End Sub
 
        Public Overrides Function MapDefinition(definition As Cci.IDefinition) As Cci.IDefinition
            Dim symbol As Symbol = TryCast(definition.GetInternalSymbol(), Symbol)
            If symbol IsNot Nothing Then
                Return DirectCast(_visitor.Visit(symbol)?.GetCciAdapter(), Cci.IDefinition)
            End If
 
            ' For simplicity, PID helpers and no-PIA embedded definitions are not reused across generations, so we don't map them here.
            ' Instead, new ones are regenerated as needed.
            Debug.Assert(TypeOf definition Is PrivateImplementationDetails OrElse TypeOf definition Is Cci.IEmbeddedDefinition)
 
            Return Nothing
        End Function
 
        Public Overrides Function MapNamespace([namespace] As Cci.INamespace) As Cci.INamespace
            Debug.Assert(TypeOf [namespace].GetInternalSymbol() Is NamespaceSymbol)
            Return DirectCast(_visitor.Visit(DirectCast([namespace]?.GetInternalSymbol(), NamespaceSymbol))?.GetCciAdapter(), Cci.INamespace)
        End Function
 
        Public Overrides Function MapReference(reference As Cci.ITypeReference) As Cci.ITypeReference
            Dim symbol As Symbol = TryCast(reference.GetInternalSymbol(), Symbol)
            If symbol IsNot Nothing Then
                Return DirectCast(_visitor.Visit(symbol)?.GetCciAdapter(), Cci.ITypeReference)
            End If
            Return Nothing
        End Function
 
        Friend Function TryGetAnonymousTypeName(template As AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol, <Out> ByRef name As String, <Out> ByRef index As Integer) As Boolean
            Return _visitor.TryGetAnonymousTypeName(template, name, index)
        End Function
 
        Private NotInheritable Class Visitor
            Inherits VisualBasicSymbolVisitor(Of Symbol)
 
            Private ReadOnly _synthesizedTypes As SynthesizedTypeMaps
            Private ReadOnly _comparer As SymbolComparer
            Private ReadOnly _matches As ConcurrentDictionary(Of Symbol, Symbol)
 
            Private ReadOnly _sourceAssembly As SourceAssemblySymbol
            Private ReadOnly _otherAssembly As AssemblySymbol
            Private ReadOnly _otherSynthesizedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal))
            Private ReadOnly _otherDeletedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal))
 
            ' A cache of members per type, populated when the first member for a given
            ' type Is needed. Within each type, members are indexed by name. The reason
            ' for caching, And indexing by name, Is to avoid searching sequentially
            ' through all members of a given kind each time a member Is matched.
            Private ReadOnly _otherMembers As ConcurrentDictionary(Of ISymbolInternal, IReadOnlyDictionary(Of String, ImmutableArray(Of ISymbolInternal)))
 
            Public Sub New(sourceAssembly As SourceAssemblySymbol,
                           otherAssembly As AssemblySymbol,
                           synthesizedTypes As SynthesizedTypeMaps,
                           otherSynthesizedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)),
                           otherDeletedMembers As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)),
                           deepTranslatorOpt As DeepTranslator)
 
                _synthesizedTypes = synthesizedTypes
                _sourceAssembly = sourceAssembly
                _otherAssembly = otherAssembly
                _otherSynthesizedMembersOpt = otherSynthesizedMembersOpt
                _otherDeletedMembersOpt = otherDeletedMembers
                _comparer = New SymbolComparer(Me, deepTranslatorOpt)
                _matches = New ConcurrentDictionary(Of Symbol, Symbol)(ReferenceEqualityComparer.Instance)
                _otherMembers = New ConcurrentDictionary(Of ISymbolInternal, IReadOnlyDictionary(Of String, ImmutableArray(Of ISymbolInternal)))(ReferenceEqualityComparer.Instance)
            End Sub
 
            Friend Function TryGetAnonymousTypeName(type As AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol, <Out> ByRef name As String, <Out> ByRef index As Integer) As Boolean
                Dim otherType As AnonymousTypeValue = Nothing
                If TryFindAnonymousType(type, otherType) Then
                    name = otherType.Name
                    index = otherType.UniqueIndex
                    Return True
                End If
                name = Nothing
                index = -1
                Return False
            End Function
 
            Public Overrides Function DefaultVisit(symbol As Symbol) As Symbol
                ' Symbol should have been handled elsewhere.
                Throw ExceptionUtilities.Unreachable
            End Function
 
            Public Overrides Function Visit(symbol As Symbol) As Symbol
                Debug.Assert(symbol.ContainingAssembly IsNot Me._otherAssembly)
 
                ' Add an entry for the match, even if there Is no match, to avoid
                ' matching the same symbol unsuccessfully multiple times.
                Return Me._matches.GetOrAdd(symbol, AddressOf MyBase.Visit)
            End Function
 
            Public Overrides Function VisitArrayType(symbol As ArrayTypeSymbol) As Symbol
                Dim otherElementType As TypeSymbol = DirectCast(Me.Visit(symbol.ElementType), TypeSymbol)
                If otherElementType Is Nothing Then
                    ' For a newly added type, there is no match in the previous generation, so it could be Nothing.
                    Return Nothing
                End If
                Dim otherModifiers = VisitCustomModifiers(symbol.CustomModifiers)
 
                If symbol.IsSZArray Then
                    Return ArrayTypeSymbol.CreateSZArray(otherElementType, otherModifiers, Me._otherAssembly)
                End If
 
                Return ArrayTypeSymbol.CreateMDArray(otherElementType, otherModifiers, symbol.Rank, symbol.Sizes, symbol.LowerBounds, Me._otherAssembly)
            End Function
 
            Public Overrides Function VisitEvent(symbol As EventSymbol) As Symbol
                Return Me.VisitNamedTypeMember(symbol, AddressOf Me.AreEventsEqual)
            End Function
 
            Public Overrides Function VisitField(symbol As FieldSymbol) As Symbol
                Return Me.VisitNamedTypeMember(symbol, AddressOf Me.AreFieldsEqual)
            End Function
 
            Public Overrides Function VisitMethod(symbol As MethodSymbol) As Symbol
                ' Not expecting constructed method.
                Debug.Assert(symbol.IsDefinition)
                Return Me.VisitNamedTypeMember(symbol, AddressOf Me.AreMethodsEqual)
            End Function
 
            Public Overrides Function VisitModule([module] As ModuleSymbol) As Symbol
                Dim otherAssembly = DirectCast(Visit([module].ContainingAssembly), AssemblySymbol)
                If otherAssembly Is Nothing Then
                    Return Nothing
                End If
 
                ' manifest module:
                If [module].Ordinal = 0 Then
                    Return otherAssembly.Modules(0)
                End If
 
                ' match non-manifest module by name:
                For i = 1 To otherAssembly.Modules.Length - 1
                    Dim otherModule = otherAssembly.Modules(i)
 
                    ' use case sensitive comparison -- modules whose names differ in casing are considered distinct
                    If StringComparer.Ordinal.Equals(otherModule.Name, [module].Name) Then
                        Return otherModule
                    End If
                Next
 
                Return Nothing
            End Function
 
            Public Overrides Function VisitAssembly(assembly As AssemblySymbol) As Symbol
                If assembly.IsLinked Then
                    Return assembly
                End If
 
                ' When we map synthesized symbols from previous generations to the latest compilation
                ' we might encounter a symbol that is defined in arbitrary preceding generation,
                ' not just the immediately preceding generation. If the source assembly uses time-based
                ' versioning assemblies of preceding generations might differ in their version number.
                If IdentityEqualIgnoringVersionWildcard(assembly, _sourceAssembly) Then
                    Return _otherAssembly
                End If
 
                ' find a referenced assembly with the exactly same source identity:
                For Each otherReferencedAssembly In _otherAssembly.Modules(0).ReferencedAssemblySymbols
                    If IdentityEqualIgnoringVersionWildcard(assembly, otherReferencedAssembly) Then
                        Return otherReferencedAssembly
                    End If
                Next
 
                Return Nothing
            End Function
 
            Private Shared Function IdentityEqualIgnoringVersionWildcard(left As AssemblySymbol, right As AssemblySymbol) As Boolean
                Dim leftIdentity = left.Identity
                Dim rightIdentity = right.Identity
                Return AssemblyIdentityComparer.SimpleNameComparer.Equals(leftIdentity.Name, rightIdentity.Name) AndAlso
                       If(left.AssemblyVersionPattern, leftIdentity.Version).Equals(If(right.AssemblyVersionPattern, rightIdentity.Version)) AndAlso
                       AssemblyIdentity.EqualIgnoringNameAndVersion(leftIdentity, rightIdentity)
            End Function
 
            Public Overrides Function VisitNamespace([namespace] As NamespaceSymbol) As Symbol
                Dim otherContainer As Symbol = Visit([namespace].ContainingSymbol)
 
                ' Containing namespace will be missing from other assembly
                ' if its was added in the (newer) source assembly.
                If otherContainer Is Nothing Then
                    Return Nothing
                End If
 
                Select Case otherContainer.Kind
                    Case SymbolKind.NetModule
                        Return DirectCast(otherContainer, ModuleSymbol).GlobalNamespace
 
                    Case SymbolKind.Namespace
                        Return FindMatchingMember(otherContainer, [namespace], AddressOf AreNamespacesEqual)
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(otherContainer.Kind)
 
                End Select
            End Function
 
            Public Overrides Function VisitNamedType(type As NamedTypeSymbol) As Symbol
                Dim originalDef As NamedTypeSymbol = type.OriginalDefinition
                If originalDef IsNot type Then
                    Dim otherDef As NamedTypeSymbol = DirectCast(Me.Visit(originalDef), NamedTypeSymbol)
 
                    ' For anonymous delegates the rewriter generates a _ClosureCache$_N field
                    ' of the constructed delegate type. For those cases, the matched result will
                    ' be Nothing if the anonymous delegate is new to this compilation.
                    If otherDef Is Nothing Then
                        Return Nothing
                    End If
 
                    Dim otherTypeParameters As ImmutableArray(Of TypeParameterSymbol) = otherDef.GetAllTypeParameters()
                    Dim translationFailed As Boolean = False
                    Dim otherTypeArguments = type.GetAllTypeArgumentsWithModifiers().SelectAsArray(Function(t, v)
                                                                                                       Dim newType = DirectCast(v.Visit(t.Type), TypeSymbol)
                                                                                                       If newType Is Nothing Then
                                                                                                           ' For a newly added type, there is no match in the previous generation, so it could be Nothing.
                                                                                                           translationFailed = True
                                                                                                           newType = t.Type
                                                                                                       End If
 
                                                                                                       Return New TypeWithModifiers(newType, v.VisitCustomModifiers(t.CustomModifiers))
                                                                                                   End Function, Me)
                    If translationFailed Then
                        ' There is no match in the previous generation.
                        Return Nothing
                    End If
 
                    Dim typeMap = TypeSubstitution.Create(otherDef, otherTypeParameters, otherTypeArguments, False)
                    Return otherDef.Construct(typeMap)
                ElseIf type.IsTupleType Then
                    Dim otherDef = DirectCast(Me.Visit(type.TupleUnderlyingType), NamedTypeSymbol)
                    If otherDef Is Nothing OrElse Not otherDef.IsTupleOrCompatibleWithTupleOfCardinality(type.TupleElementTypes.Length) Then
                        Return Nothing
                    End If
 
                    Return otherDef
                End If
 
                Debug.Assert(type.IsDefinition)
 
                Dim otherContainer As Symbol = Me.Visit(type.ContainingSymbol)
                ' Containing type will be missing from other assembly
                ' if the type was added in the (newer) source assembly.
                If otherContainer Is Nothing Then
                    Return Nothing
                End If
 
                Select Case otherContainer.Kind
                    Case SymbolKind.Namespace
                        Dim template = TryCast(type, AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol)
                        If template IsNot Nothing Then
                            Debug.Assert(otherContainer Is _otherAssembly.GlobalNamespace)
                            Dim value As AnonymousTypeValue = Nothing
                            TryFindAnonymousType(template, value)
                            Return DirectCast(value.Type?.GetInternalSymbol(), NamedTypeSymbol)
                        End If
 
                        If type.IsAnonymousType Then
                            Return Visit(AnonymousTypeManager.TranslateAnonymousTypeSymbol(type))
                        End If
 
                        Return FindMatchingMember(otherContainer, type, AddressOf AreNamedTypesEqual)
 
                    Case SymbolKind.NamedType
                        Return FindMatchingMember(otherContainer, type, AddressOf AreNamedTypesEqual)
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(otherContainer.Kind)
                End Select
            End Function
 
            Public Overrides Function VisitParameter(parameter As ParameterSymbol) As Symbol
                Throw ExceptionUtilities.Unreachable
            End Function
 
            Public Overrides Function VisitProperty(symbol As PropertySymbol) As Symbol
                Return Me.VisitNamedTypeMember(symbol, AddressOf Me.ArePropertiesEqual)
            End Function
 
            Public Overrides Function VisitTypeParameter(symbol As TypeParameterSymbol) As Symbol
                Dim indexed = TryCast(symbol, IndexedTypeParameterSymbol)
                If indexed IsNot Nothing Then
                    Return indexed
                End If
 
                Dim otherContainer As Symbol = Me.Visit(symbol.ContainingSymbol)
                Debug.Assert(otherContainer IsNot Nothing)
 
                Dim otherTypeParameters As ImmutableArray(Of TypeParameterSymbol)
 
                Select Case otherContainer.Kind
                    Case SymbolKind.NamedType,
                         SymbolKind.ErrorType
                        otherTypeParameters = DirectCast(otherContainer, NamedTypeSymbol).TypeParameters
 
                    Case SymbolKind.Method
                        otherTypeParameters = DirectCast(otherContainer, MethodSymbol).TypeParameters
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(otherContainer.Kind)
                End Select
 
                Return otherTypeParameters(symbol.Ordinal)
            End Function
 
            Private Function VisitCustomModifiers(modifiers As ImmutableArray(Of CustomModifier)) As ImmutableArray(Of CustomModifier)
                Return modifiers.SelectAsArray(AddressOf VisitCustomModifier)
            End Function
 
            Private Function VisitCustomModifier(modifier As CustomModifier) As CustomModifier
                Dim type = DirectCast(Me.Visit(DirectCast(modifier.Modifier, Symbol)), NamedTypeSymbol)
                Debug.Assert(type IsNot Nothing)
                Return If(modifier.IsOptional,
                    VisualBasicCustomModifier.CreateOptional(type),
                    VisualBasicCustomModifier.CreateRequired(type))
            End Function
 
            Friend Function TryFindAnonymousType(type As AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol, <Out> ByRef otherType As AnonymousTypeValue) As Boolean
                Debug.Assert(type.ContainingSymbol Is _sourceAssembly.GlobalNamespace)
 
                Return _synthesizedTypes.AnonymousTypes.TryGetValue(type.GetAnonymousTypeKey(), otherType)
            End Function
 
            Private Function VisitNamedTypeMember(Of T As Symbol)(member As T, predicate As Func(Of T, T, Boolean)) As Symbol
                If member.ContainingType Is Nothing Then
                    ' ContainingType is null for synthesized PrivateImplementationDetails helpers.
                    ' For simplicity, these helpers are not reused across generations.
                    ' Instead new ones are regenerated as needed.
                    Debug.Assert(TypeOf member Is ISynthesizedGlobalMethodSymbol)
 
                    Return Nothing
                End If
 
                Dim otherType As NamedTypeSymbol = DirectCast(Visit(member.ContainingType), NamedTypeSymbol)
                If otherType Is Nothing Then
                    Return Nothing
                End If
                Return FindMatchingMember(otherType, member, predicate)
            End Function
 
            Private Function FindMatchingMember(Of T As Symbol)(otherTypeOrNamespace As ISymbolInternal, sourceMember As T, predicate As Func(Of T, T, Boolean)) As T
                Dim otherMembersByName = _otherMembers.GetOrAdd(otherTypeOrNamespace, AddressOf GetAllEmittedMembers)
 
                Dim otherMembers As ImmutableArray(Of ISymbolInternal) = Nothing
                If otherMembersByName.TryGetValue(sourceMember.Name, otherMembers) Then
                    For Each otherMember In otherMembers
                        Dim other = TryCast(otherMember, T)
                        If other IsNot Nothing AndAlso predicate(sourceMember, other) Then
                            Return other
                        End If
                    Next
                End If
 
                Return Nothing
            End Function
 
            Private Function AreArrayTypesEqual(type As ArrayTypeSymbol, other As ArrayTypeSymbol) As Boolean
                Debug.Assert(type.CustomModifiers.IsEmpty)
                Debug.Assert(other.CustomModifiers.IsEmpty)
                Return type.HasSameShapeAs(other) AndAlso Me.AreTypesEqual(type.ElementType, other.ElementType)
            End Function
 
            Private Function AreEventsEqual([event] As EventSymbol, other As EventSymbol) As Boolean
                Debug.Assert(s_nameComparer.Equals([event].Name, other.Name))
                ' Events can't be overloaded on type.
                ' ECMA: Within the rows owned by a given row in the TypeDef table, there shall be no duplicates based upon Name [ERROR]
                Return True
            End Function
 
            Private Function AreFieldsEqual(field As FieldSymbol, other As FieldSymbol) As Boolean
                Debug.Assert(s_nameComparer.Equals(field.Name, other.Name))
                Return Me._comparer.Equals(field.Type, other.Type)
            End Function
 
            Private Function AreMethodsEqual(method As MethodSymbol, other As MethodSymbol) As Boolean
                Debug.Assert(s_nameComparer.Equals(method.Name, other.Name))
 
                Debug.Assert(method.IsDefinition)
                Debug.Assert(other.IsDefinition)
 
                method = SubstituteTypeParameters(method)
                other = SubstituteTypeParameters(other)
 
                Return _comparer.Equals(method.ReturnType, other.ReturnType) AndAlso
                    method.Parameters.SequenceEqual(other.Parameters, AddressOf Me.AreParametersEqual) AndAlso
                    method.TypeParameters.SequenceEqual(other.TypeParameters, AddressOf Me.AreTypesEqual)
            End Function
 
            Private Shared Function SubstituteTypeParameters(method As MethodSymbol) As MethodSymbol
                Debug.Assert(method.IsDefinition)
 
                Dim i As Integer = method.TypeParameters.Length
                If i = 0 Then
                    Return method
                End If
                Return method.Construct(ImmutableArrayExtensions.Cast(Of TypeParameterSymbol, TypeSymbol)(IndexedTypeParameterSymbol.Take(i)))
            End Function
 
            Private Function AreNamedTypesEqual(type As NamedTypeSymbol, other As NamedTypeSymbol) As Boolean
                Debug.Assert(s_nameComparer.Equals(type.Name, other.Name))
                Debug.Assert(Not type.HasTypeArgumentsCustomModifiers)
                Debug.Assert(Not other.HasTypeArgumentsCustomModifiers)
 
                ' Tuple types should be unwrapped to their underlying type before getting here (see MatchSymbols.VisitNamedType)
                Debug.Assert(Not type.IsTupleType)
                Debug.Assert(Not other.IsTupleType)
 
                Return type.TypeArgumentsNoUseSiteDiagnostics.SequenceEqual(other.TypeArgumentsNoUseSiteDiagnostics, AddressOf Me.AreTypesEqual)
            End Function
 
            Private Function AreNamespacesEqual([namespace] As NamespaceSymbol, other As NamespaceSymbol) As Boolean
                Debug.Assert(s_nameComparer.Equals([namespace].Name, other.Name))
                Return True
            End Function
 
            Private Function AreParametersEqual(parameter As ParameterSymbol, other As ParameterSymbol) As Boolean
                Debug.Assert(parameter.Ordinal = other.Ordinal)
 
                ' allow a different ref-kind as long as the runtime type is the same:
                Return parameter.IsByRef = other.IsByRef AndAlso Me._comparer.Equals(parameter.Type, other.Type)
            End Function
 
            Private Function ArePropertiesEqual([property] As PropertySymbol, other As PropertySymbol) As Boolean
                Debug.Assert(s_nameComparer.Equals([property].Name, other.Name))
 
                ' Properties may be overloaded on their signature.
                ' ECMA: Within the rows owned by a given row in the TypeDef table, there shall be no duplicates based upon Name+Type [ERROR]
                Return Me._comparer.Equals([property].Type, other.Type) AndAlso
                    [property].Parameters.SequenceEqual(other.Parameters, AddressOf Me.AreParametersEqual)
            End Function
 
            Private Shared Function AreTypeParametersEqual(type As TypeParameterSymbol, other As TypeParameterSymbol) As Boolean
                Debug.Assert(type.Ordinal = other.Ordinal)
                Debug.Assert(s_nameComparer.Equals(type.Name, other.Name))
                ' Comparing constraints is unnecessary: two methods cannot differ by
                ' constraints alone and changing the signature of a method is a rude
                ' edit. Furthermore, comparing constraint types might lead to a cycle.
                Debug.Assert(type.HasConstructorConstraint = other.HasConstructorConstraint)
                Debug.Assert(type.HasValueTypeConstraint = other.HasValueTypeConstraint)
                Debug.Assert(type.AllowsRefLikeType = other.AllowsRefLikeType)
                Debug.Assert(type.HasReferenceTypeConstraint = other.HasReferenceTypeConstraint)
                Debug.Assert(type.ConstraintTypesNoUseSiteDiagnostics.Length = other.ConstraintTypesNoUseSiteDiagnostics.Length)
                Debug.Assert(type.HasUnmanagedTypeConstraint = other.HasUnmanagedTypeConstraint)
                Return True
            End Function
 
            Private Function AreTypesEqual(type As TypeSymbol, other As TypeSymbol) As Boolean
                If type.Kind <> other.Kind Then
                    Return False
                End If
                Select Case type.Kind
                    Case SymbolKind.ArrayType
                        Return AreArrayTypesEqual(DirectCast(type, ArrayTypeSymbol), DirectCast(other, ArrayTypeSymbol))
 
                    Case SymbolKind.NamedType,
                         SymbolKind.ErrorType
                        Return AreNamedTypesEqual(DirectCast(type, NamedTypeSymbol), DirectCast(other, NamedTypeSymbol))
 
                    Case SymbolKind.TypeParameter
                        Return AreTypeParametersEqual(DirectCast(type, TypeParameterSymbol), DirectCast(other, TypeParameterSymbol))
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(type.Kind)
                End Select
            End Function
 
            Private Function GetAllEmittedMembers(symbol As ISymbolInternal) As IReadOnlyDictionary(Of String, ImmutableArray(Of ISymbolInternal))
                Dim members = ArrayBuilder(Of ISymbolInternal).GetInstance()
 
                If symbol.Kind = SymbolKind.NamedType Then
                    Dim type = CType(symbol, NamedTypeSymbol)
                    members.AddRange(type.GetEventsToEmit())
                    members.AddRange(type.GetFieldsToEmit())
                    members.AddRange(type.GetMethodsToEmit())
                    members.AddRange(type.GetTypeMembers())
                    members.AddRange(type.GetPropertiesToEmit())
                Else
                    members.AddRange(CType(symbol, NamespaceSymbol).GetMembers())
                End If
 
                Dim synthesizedMembers As ImmutableArray(Of ISymbolInternal) = Nothing
                If _otherSynthesizedMembersOpt IsNot Nothing AndAlso _otherSynthesizedMembersOpt.TryGetValue(symbol, synthesizedMembers) Then
                    members.AddRange(synthesizedMembers)
                End If
 
                Dim deletedMembers As ImmutableArray(Of ISymbolInternal) = Nothing
                If _otherDeletedMembersOpt IsNot Nothing AndAlso _otherDeletedMembersOpt.TryGetValue(symbol, deletedMembers) Then
                    members.AddRange(deletedMembers)
                End If
 
                Dim result = members.ToDictionary(Function(s) s.Name, s_nameComparer)
                members.Free()
                Return result
            End Function
 
            Private Class SymbolComparer
                Private ReadOnly _matcher As Visitor
                Private ReadOnly _deepTranslatorOpt As DeepTranslator
 
                Public Sub New(matcher As Visitor, deepTranslatorOpt As DeepTranslator)
                    Debug.Assert(matcher IsNot Nothing)
                    _matcher = matcher
                    _deepTranslatorOpt = deepTranslatorOpt
                End Sub
 
                Public Overloads Function Equals(source As TypeSymbol, other As TypeSymbol) As Boolean
                    If ReferenceEquals(source, other) Then
                        Return True
                    End If
 
                    Dim visitedSource = DirectCast(_matcher.Visit(source), TypeSymbol)
                    Dim visitedOther = If(_deepTranslatorOpt IsNot Nothing, DirectCast(_deepTranslatorOpt.Visit(other), TypeSymbol), other)
 
                    ' If both visitedSource and visitedOther are Nothing, return false meaning that the method was not able to verify the equality.
                    Return visitedSource IsNot Nothing AndAlso visitedOther IsNot Nothing AndAlso visitedSource.IsSameType(visitedOther, TypeCompareKind.IgnoreTupleNames)
                End Function
            End Class
        End Class
 
        Friend NotInheritable Class DeepTranslator
            Inherits VisualBasicSymbolVisitor(Of Symbol)
 
            Private ReadOnly _matches As ConcurrentDictionary(Of Symbol, Symbol)
            Private ReadOnly _systemObject As NamedTypeSymbol
 
            Public Sub New(systemObject As NamedTypeSymbol)
                _matches = New ConcurrentDictionary(Of Symbol, Symbol)(ReferenceEqualityComparer.Instance)
                _systemObject = systemObject
            End Sub
 
            Public Overrides Function DefaultVisit(symbol As Symbol) As Symbol
                ' Symbol should have been handled elsewhere.
                Throw ExceptionUtilities.Unreachable
            End Function
 
            Public Overrides Function Visit(symbol As Symbol) As Symbol
                Return _matches.GetOrAdd(symbol, AddressOf MyBase.Visit)
            End Function
 
            Public Overrides Function VisitArrayType(symbol As ArrayTypeSymbol) As Symbol
                Dim translatedElementType As TypeSymbol = DirectCast(Me.Visit(symbol.ElementType), TypeSymbol)
                Dim translatedModifiers = VisitCustomModifiers(symbol.CustomModifiers)
 
                If symbol.IsSZArray Then
                    Return ArrayTypeSymbol.CreateSZArray(translatedElementType, translatedModifiers, symbol.BaseTypeNoUseSiteDiagnostics.ContainingAssembly)
                End If
 
                Return ArrayTypeSymbol.CreateMDArray(translatedElementType, translatedModifiers, symbol.Rank, symbol.Sizes, symbol.LowerBounds, symbol.BaseTypeNoUseSiteDiagnostics.ContainingAssembly)
            End Function
 
            Public Overrides Function VisitNamedType(type As NamedTypeSymbol) As Symbol
                If type.IsTupleType Then
                    type = type.TupleUnderlyingType
                    Debug.Assert(Not type.IsTupleType)
                End If
 
                Dim originalDef As NamedTypeSymbol = type.OriginalDefinition
                If originalDef IsNot type Then
                    Dim translatedTypeArguments = type.GetAllTypeArgumentsWithModifiers().SelectAsArray(Function(t, v) New TypeWithModifiers(DirectCast(v.Visit(t.Type), TypeSymbol),
                                                                                                                                             v.VisitCustomModifiers(t.CustomModifiers)), Me)
 
                    Dim translatedOriginalDef = DirectCast(Me.Visit(originalDef), NamedTypeSymbol)
                    Dim typeMap = TypeSubstitution.Create(translatedOriginalDef, translatedOriginalDef.GetAllTypeParameters(), translatedTypeArguments, False)
                    Return translatedOriginalDef.Construct(typeMap)
                End If
 
                Debug.Assert(type.IsDefinition)
 
                If type.IsAnonymousType Then
                    Return Me.Visit(AnonymousTypeManager.TranslateAnonymousTypeSymbol(type))
                End If
 
                Return type
            End Function
 
            Public Overrides Function VisitTypeParameter(symbol As TypeParameterSymbol) As Symbol
                Return symbol
            End Function
 
            Private Function VisitCustomModifiers(modifiers As ImmutableArray(Of CustomModifier)) As ImmutableArray(Of CustomModifier)
                Return modifiers.SelectAsArray(AddressOf VisitCustomModifier)
            End Function
 
            Private Function VisitCustomModifier(modifier As CustomModifier) As CustomModifier
                Dim translatedType = DirectCast(Me.Visit(DirectCast(modifier.Modifier, Symbol)), NamedTypeSymbol)
                Debug.Assert(translatedType IsNot Nothing)
                Return If(modifier.IsOptional,
                    VisualBasicCustomModifier.CreateOptional(translatedType),
                    VisualBasicCustomModifier.CreateRequired(translatedType))
            End Function
        End Class
    End Class
End Namespace