File: Symbols\MergedNamespaceSymbol.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.Generic
Imports System.Collections.Immutable
Imports System.Collections.ObjectModel
Imports System.Threading
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
    ''' <summary>
    ''' A MergedNamespaceSymbol represents a namespace that merges the contents of two or more other
    ''' namespaces. Any sub-namespaces with the same names are also merged if they have two or more
    ''' instances.
    ''' 
    ''' Merged namespaces are used to merged the symbols from multiple metadata modules and the source "module"
    ''' into a single symbol tree that represents all the available symbols. The compiler resolves names
    ''' against Me merged set of symbols.
    ''' 
    ''' Typically there will not be very many merged namespaces in a Compilation: only the root namespaces and
    ''' namespaces that are used in multiple referenced modules. (Microsoft, System, System.Xml,
    ''' System.Diagnostics, System.Threading, ...)
    ''' </summary>
    Friend MustInherit Class MergedNamespaceSymbol
        Inherits PEOrSourceOrMergedNamespaceSymbol
 
        Protected ReadOnly _namespacesToMerge As ImmutableArray(Of NamespaceSymbol)
        Protected ReadOnly _containingNamespace As MergedNamespaceSymbol
 
        ' The cachedLookup caches results of lookups on the constituent namespaces so
        ' that subsequent lookups for the same name are much faster than having to ask
        ' each of the constituent namespaces.
        Private ReadOnly _cachedLookup As CachingDictionary(Of String, Symbol)
 
        ' This caches results of GetModuleMembers()
        Private _lazyModuleMembers As ImmutableArray(Of NamedTypeSymbol)
 
        ' This caches results of GetMembers()
        Private _lazyMembers As ImmutableArray(Of Symbol)
 
        Private _lazyEmbeddedKind As Integer = EmbeddedSymbolKind.Unset
 
        ''' <summary>
        ''' Create a possibly merged namespace symbol representing global namespace on an assembly level.
        ''' </summary>
        Public Shared Function CreateGlobalNamespace(extent As AssemblySymbol) As NamespaceSymbol
            ' Get the root namespace from each module, and merge them all together. If there is only one, 
            ' then MergedNamespaceSymbol.Create will just return that one.
            Return MergedNamespaceSymbol.Create(extent, Nothing, ConstituentGlobalNamespaces(extent).AsImmutable())
        End Function
 
        Private Shared Iterator Function ConstituentGlobalNamespaces(extent As AssemblySymbol) As IEnumerable(Of NamespaceSymbol)
            For Each m In extent.Modules
                Yield m.GlobalNamespace
            Next
        End Function
 
        ''' <summary>
        ''' Create a possibly merged namespace symbol. If only a single namespace is passed it, it is just returned directly.
        ''' If two or more namespaces are passed in, then a new merged namespace is created with the given extent and container.
        ''' </summary>
        ''' <param name="extent">The namespace extent to use, IF a merged namespace is created.</param>
        ''' <param name="containingNamespace">The containing namespace to used, IF a merged namespace is created.</param>
        ''' <param name="namespacesToMerge">One or more namespaces to merged. If just one, then it is returned.
        ''' The merged namespace symbol may hold onto the array.</param>
        ''' <returns></returns>A namespace symbol representing the merged namespace.(of /returns)
        Private Shared Function Create(extent As AssemblySymbol, containingNamespace As AssemblyMergedNamespaceSymbol, namespacesToMerge As ImmutableArray(Of NamespaceSymbol)) As NamespaceSymbol
            ' Currently, if we are just merging 1 namespace, we just return the namespace itself. This is
            ' by far the most efficient, because it means that we don't create merged namespaces (which have a fair amount
            ' of memory overhead) unless there is actual merging going on. However, it means that
            ' the child namespace of a Compilation extent namespace may be a Module extent namespace, and the containing of that
            ' module extent namespace will be another module extent namespace. This is basically no different than type members of namespaces,
            ' so it shouldn't be TOO unexpected.
 
            Debug.Assert(namespacesToMerge.Length <> 0)
 
            If namespacesToMerge.Length = 1 Then
                Dim result = namespacesToMerge(0)
                Return result
            Else
                Return New AssemblyMergedNamespaceSymbol(extent, containingNamespace, namespacesToMerge)
            End If
        End Function
 
        Friend Shared Function CreateForTestPurposes(extent As AssemblySymbol, namespacesToMerge As IEnumerable(Of NamespaceSymbol)) As NamespaceSymbol
            Return Create(extent, Nothing, namespacesToMerge.AsImmutable())
        End Function
 
        ''' <summary>
        ''' Create a possibly merged namespace symbol representing global namespace on a compilation level.
        ''' </summary>
        Public Shared Function CreateGlobalNamespace(extent As VisualBasicCompilation) As NamespaceSymbol
            ' Get the root namespace from each module, and merge them all together.
            Return MergedNamespaceSymbol.Create(extent, Nothing, ConstituentGlobalNamespaces(extent))
        End Function
 
        Private Shared Iterator Function ConstituentGlobalNamespaces(extent As VisualBasicCompilation) As IEnumerable(Of NamespaceSymbol)
            For Each m In extent.Assembly.Modules
                Yield m.GlobalNamespace
            Next
 
            For Each reference In extent.SourceModule.GetReferencedAssemblySymbols()
                For Each m In reference.Modules
                    Yield m.GlobalNamespace
                Next
            Next
        End Function
 
        Private Shared Function Create(extent As VisualBasicCompilation, containingNamespace As CompilationMergedNamespaceSymbol, namespacesToMerge As IEnumerable(Of NamespaceSymbol)) As NamespaceSymbol
            Dim namespaceArray = ArrayBuilder(Of NamespaceSymbol).GetInstance()
            namespaceArray.AddRange(namespacesToMerge)
 
            ' Currently, if we are just merging 1 namespace, we just return the namespace itself. This is
            ' by far the most efficient, because it means that we don't create merged namespaces (which have a fair amount
            ' of memory overhead) unless there is actual merging going on. However, it means that
            ' the child namespace of a Compilation extent namespace may be a Module extent namespace, and the containing of that
            ' module extent namespace will be another module extent namespace. This is basically no different than type members of namespaces,
            ' so it shouldn't be TOO unexpected.
 
            Debug.Assert(namespaceArray.Count <> 0)
 
            If (namespaceArray.Count = 1) Then
                Dim result = namespaceArray(0)
                namespaceArray.Free()
                Return result
            Else
                Return New CompilationMergedNamespaceSymbol(extent, containingNamespace, namespaceArray.ToImmutableAndFree())
            End If
        End Function
 
        ''' <summary>
        ''' Create a possibly merged namespace symbol (namespace group). If only a single namespace is passed it, it is just returned directly.
        ''' If two or more namespaces are passed in, then a new merged namespace is created
        ''' </summary>
        ''' <param name="namespacesToMerge">One or more namespaces to merged. If just one, then it is returned.
        ''' The merged namespace symbol may hold onto the array.</param>
        ''' <returns></returns>A namespace symbol representing the merged namespace.(of /returns)
        Public Shared Function CreateNamespaceGroup(namespacesToMerge As IEnumerable(Of NamespaceSymbol)) As NamespaceSymbol
            Return Create(NamespaceGroupSymbol.GlobalNamespace, namespacesToMerge)
        End Function
 
        Public Overridable Function Shrink(namespacesToMerge As IEnumerable(Of NamespaceSymbol)) As NamespaceSymbol
            Throw ExceptionUtilities.Unreachable
        End Function
 
        ''' <summary>
        ''' Create a possibly merged namespace symbol (namespace group). If only a single namespace is passed it, it is just returned directly.
        ''' If two or more namespaces are passed in, then a new merged namespace is created with the given extent and container.
        ''' </summary>
        ''' <param name="containingNamespace">The containing namespace to used, IF a merged namespace is created.</param>
        ''' <param name="namespacesToMerge">One or more namespaces to merged. If just one, then it is returned.
        ''' The merged namespace symbol may hold onto the array.</param>
        ''' <returns></returns>A namespace symbol representing the merged namespace.(of /returns)
        Private Shared Function Create(containingNamespace As NamespaceGroupSymbol, namespacesToMerge As IEnumerable(Of NamespaceSymbol)) As NamespaceSymbol
            Dim namespaceArray = ArrayBuilder(Of NamespaceSymbol).GetInstance()
            namespaceArray.AddRange(namespacesToMerge)
 
            ' Currently, if we are just merging 1 namespace, we just return the namespace itself. This is
            ' by far the most efficient, because it means that we don't create merged namespaces (which have a fair amount
            ' of memory overhead) unless there is actual merging going on. However, it means that
            ' the child namespace of a Compilation extent namespace may be a Module extent namespace, and the containing of that
            ' module extent namespace will be another module extent namespace. This is basically no different than type members of namespaces,
            ' so it shouldn't be TOO unexpected.
 
            Debug.Assert(namespaceArray.Count <> 0)
 
            If (namespaceArray.Count = 1) Then
                Dim result = namespaceArray(0)
                namespaceArray.Free()
                Return result
            Else
                Return New NamespaceGroupSymbol(containingNamespace, namespaceArray.ToImmutableAndFree())
            End If
        End Function
 
        ' Constructor. Use static Create method to create instances.
        Private Sub New(containingNamespace As MergedNamespaceSymbol, namespacesToMerge As ImmutableArray(Of NamespaceSymbol))
            Debug.Assert(namespacesToMerge.Distinct().Length = namespacesToMerge.Length)
            Me._namespacesToMerge = namespacesToMerge
            Me._containingNamespace = containingNamespace
 
            Me._cachedLookup = New CachingDictionary(Of String, Symbol)(AddressOf SlowGetChildrenOfName, AddressOf SlowGetChildNames, IdentifierComparison.Comparer)
        End Sub
 
        Friend Function GetConstituentForCompilation(compilation As VisualBasicCompilation) As NamespaceSymbol
            For Each constituent In _namespacesToMerge
                If constituent.IsFromCompilation(compilation) Then
                    Return constituent
                End If
            Next
            Return Nothing
        End Function
 
        Public Overrides ReadOnly Property ConstituentNamespaces As ImmutableArray(Of NamespaceSymbol)
            Get
                Return _namespacesToMerge
            End Get
        End Property
 
        ''' <summary>
        ''' Method that is called from the CachingLookup to lookup the children of a given name. Looks
        ''' in all the constituent namespaces.
        ''' </summary>
        Private Function SlowGetChildrenOfName(name As String) As ImmutableArray(Of Symbol)
            Dim nsSymbols As ArrayBuilder(Of NamespaceSymbol) = Nothing
            Dim otherSymbols = ArrayBuilder(Of Symbol).GetInstance()
 
            ' Accumulate all the child namespaces and types.
            For Each nsSym As NamespaceSymbol In _namespacesToMerge
                For Each childSym As Symbol In nsSym.GetMembers(name)
                    If (childSym.Kind = SymbolKind.Namespace) Then
                        nsSymbols = If(nsSymbols, ArrayBuilder(Of NamespaceSymbol).GetInstance())
                        nsSymbols.Add(DirectCast(childSym, NamespaceSymbol))
                    Else
                        otherSymbols.Add(childSym)
                    End If
                Next
            Next
 
            If nsSymbols IsNot Nothing Then
                otherSymbols.Add(CreateChildMergedNamespaceSymbol(nsSymbols.ToImmutableAndFree()))
            End If
 
            Return otherSymbols.ToImmutableAndFree()
        End Function
 
        Protected MustOverride Function CreateChildMergedNamespaceSymbol(nsSymbols As ImmutableArray(Of NamespaceSymbol)) As NamespaceSymbol
 
        ''' <summary>
        ''' Method that is called from the CachingLookup to get all child names. Looks
        ''' in all constituent namespaces.
        ''' </summary>
        Private Function SlowGetChildNames(comparer As IEqualityComparer(Of String)) As SegmentedHashSet(Of String)
            ' compute an upper bound for the final capacity of the set we'll return, to reduce heap churn
            Dim childCount As Integer = 0
 
            For Each nsSym As NamespaceSymbol In _namespacesToMerge
                childCount += nsSym.GetMembersUnordered().Length
            Next
 
            Dim childNames As New SegmentedHashSet(Of String)(childCount, comparer)
 
            For Each nsSym As NamespaceSymbol In _namespacesToMerge
                For Each childSym As NamespaceOrTypeSymbol In nsSym.GetMembersUnordered()
                    childNames.Add(childSym.Name)
                Next
            Next
 
            Return childNames
        End Function
 
        Public Overrides ReadOnly Property Name As String
            Get
                Return _namespacesToMerge(0).Name
            End Get
        End Property
 
        Friend Overrides ReadOnly Property EmbeddedSymbolKind As EmbeddedSymbolKind
            Get
                If _lazyEmbeddedKind = EmbeddedSymbolKind.Unset Then
                    Dim value As Integer = EmbeddedSymbolKind.None
                    For Each ns In _namespacesToMerge
                        value = value Or ns.EmbeddedSymbolKind
                    Next
                    Interlocked.CompareExchange(_lazyEmbeddedKind, value, EmbeddedSymbolKind.Unset)
                End If
 
                Return CType(_lazyEmbeddedKind, EmbeddedSymbolKind)
            End Get
        End Property
 
        ' This is very performance critical for type lookup.
        ' It's important that this NOT enumerable and instantiate all the members. Instead, we just want to return
        ' all the modules in all constituent namespaces, and cache that.
        Public Overrides Function GetModuleMembers() As ImmutableArray(Of NamedTypeSymbol)
            If _lazyModuleMembers.IsDefault Then
                Dim moduleMembers = ArrayBuilder(Of NamedTypeSymbol).GetInstance()
 
                ' Accumulate all the child modules.
                For Each nsSym As NamespaceSymbol In _namespacesToMerge
                    moduleMembers.AddRange(nsSym.GetModuleMembers())
                Next
 
                ImmutableInterlocked.InterlockedCompareExchange(_lazyModuleMembers,
                                                    moduleMembers.ToImmutableAndFree,
                                                    Nothing)
            End If
 
            Return _lazyModuleMembers
        End Function
 
        Public Overrides Function GetMembers() As ImmutableArray(Of Symbol)
            If _lazyMembers.IsDefault Then
                Dim builder = ArrayBuilder(Of Symbol).GetInstance()
                _cachedLookup.AddValues(builder)
                _lazyMembers = builder.ToImmutableAndFree()
            End If
 
            Return _lazyMembers
        End Function
 
        Public Overrides Function GetMembers(name As String) As ImmutableArray(Of Symbol)
            Return _cachedLookup(name)
        End Function
 
        Friend Overrides Function GetTypeMembersUnordered() As ImmutableArray(Of NamedTypeSymbol)
            Return ImmutableArray.CreateRange(Of NamedTypeSymbol)(GetMembersUnordered().OfType(Of NamedTypeSymbol))
        End Function
 
        Public Overrides Function GetTypeMembers() As ImmutableArray(Of NamedTypeSymbol)
            Return ImmutableArray.CreateRange(Of NamedTypeSymbol)(GetMembers().OfType(Of NamedTypeSymbol))
        End Function
 
        Public Overrides Function GetTypeMembers(name As String) As ImmutableArray(Of NamedTypeSymbol)
            'TODO - Perf
            Return ImmutableArray.CreateRange(Of NamedTypeSymbol)(GetMembers(name).OfType(Of NamedTypeSymbol))
 
        End Function
 
        Public Overrides ReadOnly Property ContainingSymbol As Symbol
            Get
                Return _containingNamespace
            End Get
        End Property
 
        Public Overrides ReadOnly Property ContainingAssembly As AssemblySymbol
            Get
                If Me.Extent.Kind = NamespaceKind.Module Then
                    Return Me.Extent.Module.ContainingAssembly
 
                ElseIf Me.Extent.Kind = NamespaceKind.Assembly Then
                    Return Me.Extent.Assembly
 
                Else
                    Return Nothing
                End If
            End Get
        End Property
 
        Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location)
            Get
                Return ImmutableArray.CreateRange(Of Location)((From ns In _namespacesToMerge, loc In ns.Locations Select loc))
            End Get
        End Property
 
        Public Overrides ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)
            Get
                Return ImmutableArray.CreateRange(Of SyntaxReference)(From ns In _namespacesToMerge, reference In ns.DeclaringSyntaxReferences Select reference)
            End Get
        End Property
 
        ''' <summary>
        ''' Calculate declared accessibility of most accessible type within this namespace or within a containing namespace recursively.
        ''' Expected to be called at most once per namespace symbol, unless there is a race condition.
        ''' 
        ''' Valid return values:
        '''     Friend,
        '''     Public,
        '''     NotApplicable - if there are no types.
        ''' </summary>
        Protected NotOverridable Overrides Function GetDeclaredAccessibilityOfMostAccessibleDescendantType() As Accessibility
 
            Dim result As Accessibility = Accessibility.NotApplicable
 
            For Each nsSym As NamespaceSymbol In _namespacesToMerge
                Dim current As Accessibility = nsSym.DeclaredAccessibilityOfMostAccessibleDescendantType
 
                If current > result Then
                    If current = Accessibility.Public Then
                        Return Accessibility.Public
                    End If
 
                    result = current
                End If
            Next
 
            Return result
        End Function
 
        Friend Overrides Function IsDeclaredInSourceModule([module] As ModuleSymbol) As Boolean
            For Each nsSym As NamespaceSymbol In _namespacesToMerge
                If nsSym.IsDeclaredInSourceModule([module]) Then
                    Return True
                End If
            Next
 
            Return False
        End Function
 
        ''' <summary>
        ''' For test purposes only.
        ''' </summary>
        Friend MustOverride ReadOnly Property RawContainsAccessibleTypes As ThreeState
 
        Private NotInheritable Class AssemblyMergedNamespaceSymbol
            Inherits MergedNamespaceSymbol
 
            Private ReadOnly _assembly As AssemblySymbol
 
            Public Sub New(assembly As AssemblySymbol, containingNamespace As AssemblyMergedNamespaceSymbol, namespacesToMerge As ImmutableArray(Of NamespaceSymbol))
                MyBase.New(containingNamespace, namespacesToMerge)
#If DEBUG Then
                ' We shouldn't merge merged namespaces.
                For Each ns In namespacesToMerge
                    Debug.Assert(ns.ConstituentNamespaces.Length = 1)
                Next
#End If
 
                Debug.Assert(namespacesToMerge.Length > 0)
                Debug.Assert(containingNamespace Is Nothing OrElse containingNamespace._assembly Is assembly)
                Me._assembly = assembly
            End Sub
 
            Friend Overrides ReadOnly Property Extent As NamespaceExtent
                Get
                    Return New NamespaceExtent(_assembly)
                End Get
            End Property
 
            Protected Overrides Function CreateChildMergedNamespaceSymbol(nsSymbols As ImmutableArray(Of NamespaceSymbol)) As NamespaceSymbol
                Return MergedNamespaceSymbol.Create(_assembly, Me, nsSymbols)
            End Function
 
            Friend Overrides ReadOnly Property RawContainsAccessibleTypes As ThreeState
                Get
                    Throw ExceptionUtilities.Unreachable
                End Get
            End Property
 
            Friend Overrides Sub BuildExtensionMethodsMap(map As Dictionary(Of String, ArrayBuilder(Of MethodSymbol)))
                ' Assembly merged namespace symbols aren't used by binders.
                Throw ExceptionUtilities.Unreachable
            End Sub
 
            Friend Overrides ReadOnly Property TypesToCheckForExtensionMethods As ImmutableArray(Of NamedTypeSymbol)
                Get
                    ' Assembly merged namespace symbols aren't used by binders.
                    Throw ExceptionUtilities.Unreachable
                End Get
            End Property
        End Class
 
        Private NotInheritable Class CompilationMergedNamespaceSymbol
            Inherits MergedNamespaceSymbol
 
            Private ReadOnly _compilation As VisualBasicCompilation
            Private _containsAccessibleTypes As ThreeState = ThreeState.Unknown
            Private _isDeclaredInSourceModule As ThreeState = ThreeState.Unknown
 
            Friend Overrides ReadOnly Property RawContainsAccessibleTypes As ThreeState
                Get
                    Return _containsAccessibleTypes
                End Get
            End Property
 
            Public Sub New(compilation As VisualBasicCompilation, containingNamespace As CompilationMergedNamespaceSymbol, namespacesToMerge As ImmutableArray(Of NamespaceSymbol))
                MyBase.New(containingNamespace, namespacesToMerge)
#If DEBUG Then
                ' We shouldn't merge merged namespaces.
                For Each ns In namespacesToMerge
                    Debug.Assert(ns.ConstituentNamespaces.Length = 1)
                Next
#End If
                Debug.Assert(namespacesToMerge.Length > 0)
                Debug.Assert(containingNamespace Is Nothing OrElse containingNamespace._compilation Is compilation)
 
                Me._compilation = compilation
            End Sub
 
            Friend Overrides ReadOnly Property Extent As NamespaceExtent
                Get
                    Return New NamespaceExtent(_compilation)
                End Get
            End Property
 
            Protected Overrides Function CreateChildMergedNamespaceSymbol(nsSymbols As ImmutableArray(Of NamespaceSymbol)) As NamespaceSymbol
                Return MergedNamespaceSymbol.Create(_compilation, Me, nsSymbols)
            End Function
 
            ''' <summary>
            ''' Returns true if namespace contains types accessible from the target assembly.
            ''' </summary>
            Friend Overrides Function ContainsTypesAccessibleFrom(fromAssembly As AssemblySymbol) As Boolean
                ' For a compilation merged namespace we only need to support scenario when 
                ' [fromAssembly] is compilation's assembly, because this namespace symbol will never be 
                ' "imported" namespace in context of other compilation. 
                ' This allows us to do efficient caching of the result.
                Debug.Assert(fromAssembly Is _compilation.Assembly)
 
                If _containsAccessibleTypes.HasValue Then
                    Return _containsAccessibleTypes = ThreeState.True
                End If
 
                If Me.RawLazyDeclaredAccessibilityOfMostAccessibleDescendantType = Accessibility.Public Then
                    Return True
                End If
 
                Dim result As Boolean = False
 
                For Each nsSym As NamespaceSymbol In _namespacesToMerge
                    If nsSym.ContainsTypesAccessibleFrom(fromAssembly) Then
                        result = True
                        Exit For
                    End If
                Next
 
                If result Then
                    _containsAccessibleTypes = ThreeState.True
 
                    ' Bubble up the value.
                    Dim parent = TryCast(Me.ContainingSymbol, CompilationMergedNamespaceSymbol)
 
                    While parent IsNot Nothing AndAlso
                          parent._containsAccessibleTypes = ThreeState.Unknown
 
                        parent._containsAccessibleTypes = ThreeState.True
                        parent = TryCast(parent.ContainingSymbol, CompilationMergedNamespaceSymbol)
                    End While
                Else
                    _containsAccessibleTypes = ThreeState.False
                End If
 
                Return result
            End Function
 
            Friend Overrides Function IsDeclaredInSourceModule([module] As ModuleSymbol) As Boolean
                ' For a compilation merged namespace we only need to support scenario when 
                ' [module] is compilation's source module, because this namespace symbol will never be 
                ' "imported" namespace in context of other compilation. 
                ' This allows us to do efficient caching of the result.
                Debug.Assert([module] Is _compilation.SourceModule)
 
                If _isDeclaredInSourceModule.HasValue Then
                    Return _isDeclaredInSourceModule = ThreeState.True
                End If
 
                Dim result = MyBase.IsDeclaredInSourceModule([module])
 
                If result Then
                    _isDeclaredInSourceModule = ThreeState.True
 
                    ' Bubble up the value.
                    Dim parent = TryCast(Me.ContainingSymbol, CompilationMergedNamespaceSymbol)
 
                    While parent IsNot Nothing AndAlso
                          parent._isDeclaredInSourceModule = ThreeState.Unknown
 
                        parent._isDeclaredInSourceModule = ThreeState.True
                        parent = TryCast(parent.ContainingSymbol, CompilationMergedNamespaceSymbol)
                    End While
                Else
                    _isDeclaredInSourceModule = ThreeState.False
                End If
 
                Return result
            End Function
 
            ''' <summary>
            ''' Populate the map with all extension methods declared within this namespace, so that methods from
            ''' the same type are grouped together within each bucket. 
            ''' </summary>
            Friend Overrides Sub BuildExtensionMethodsMap(map As Dictionary(Of String, ArrayBuilder(Of MethodSymbol)))
                For Each nsSym As NamespaceSymbol In _namespacesToMerge
                    ' CONSIDER: Is it worth using m_lazyExtensionMethodsMap for nsSym if we've built it already for a 
                    '           different compilation?
                    nsSym.BuildExtensionMethodsMap(map)
                Next
            End Sub
 
            Friend Overrides Sub GetExtensionMethods(methods As ArrayBuilder(Of MethodSymbol), name As String)
                For Each nsSym As NamespaceSymbol In _namespacesToMerge
                    nsSym.GetExtensionMethods(methods, name)
                Next
            End Sub
 
            Friend Overrides ReadOnly Property TypesToCheckForExtensionMethods As ImmutableArray(Of NamedTypeSymbol)
                Get
                    ' We should override all callers of this function and go through implementation
                    ' provided by each individual namespace symbol.
                    Throw ExceptionUtilities.Unreachable
                End Get
            End Property
        End Class
 
        Private NotInheritable Class NamespaceGroupSymbol
            Inherits MergedNamespaceSymbol
 
            Public Shared ReadOnly GlobalNamespace As New NamespaceGroupSymbol(Nothing, ImmutableArray(Of NamespaceSymbol).Empty)
 
            Public Sub New(containingNamespace As NamespaceGroupSymbol, namespacesToMerge As ImmutableArray(Of NamespaceSymbol))
                MyBase.New(containingNamespace, namespacesToMerge)
#If DEBUG Then
                Dim name As String = If(namespacesToMerge.Length > 0, namespacesToMerge(0).Name, Nothing)
 
                For Each ns In namespacesToMerge
                    Debug.Assert(ns.NamespaceKind = NamespaceKind.Module OrElse ns.NamespaceKind = NamespaceKind.Compilation)
                    Debug.Assert(Not ns.IsGlobalNamespace)
                    Debug.Assert(IdentifierComparison.Equals(name, ns.Name))
                Next
#End If
                Debug.Assert(containingNamespace Is Nothing OrElse namespacesToMerge.Length > 0)
            End Sub
 
            Public Overrides ReadOnly Property Name As String
                Get
                    Return If(_namespacesToMerge.Length > 0, _namespacesToMerge(0).Name, "")
                End Get
            End Property
 
#If DEBUG Then
            Shared Sub New()
                ' Below is a set of compile time asserts, they break build if violated.
 
                ' Assert(SymbolExtensions.NamespaceKindNamespaceGroup = 0) 
                ' This is needed because we use default NamespaceExtent constructor in Extent property.
                Const assert01_1 As Integer = SymbolExtensions.NamespaceKindNamespaceGroup + Int32.MaxValue
                Const assert01_2 As Integer = SymbolExtensions.NamespaceKindNamespaceGroup + Int32.MinValue
 
                Dim dummy = assert01_1 + assert01_2
            End Sub
#End If
 
            Friend Overrides ReadOnly Property Extent As NamespaceExtent
                Get
                    Return New NamespaceExtent()
                End Get
            End Property
 
            Friend Overrides ReadOnly Property RawContainsAccessibleTypes As ThreeState
                Get
                    Throw ExceptionUtilities.Unreachable
                End Get
            End Property
 
            Friend Overrides ReadOnly Property TypesToCheckForExtensionMethods As ImmutableArray(Of NamedTypeSymbol)
                Get
                    Throw ExceptionUtilities.Unreachable
                End Get
            End Property
 
            Friend Overrides Sub BuildExtensionMethodsMap(map As Dictionary(Of String, ArrayBuilder(Of MethodSymbol)))
                Throw ExceptionUtilities.Unreachable
            End Sub
 
            Protected Overrides Function CreateChildMergedNamespaceSymbol(nsSymbols As ImmutableArray(Of NamespaceSymbol)) As NamespaceSymbol
                Return MergedNamespaceSymbol.Create(Me, nsSymbols)
            End Function
 
            Public Overrides Function Shrink(namespacesToMerge As IEnumerable(Of NamespaceSymbol)) As NamespaceSymbol
                Dim namespaceArray = ArrayBuilder(Of NamespaceSymbol).GetInstance()
                namespaceArray.AddRange(namespacesToMerge)
 
                ' Currently, if we are just merging 1 namespace, we just return the namespace itself. This is
                ' by far the most efficient, because it means that we don't create merged namespaces (which have a fair amount
                ' of memory overhead) unless there is actual merging going on. 
                Debug.Assert(namespaceArray.Count <> 0)
                If namespaceArray.Count = 0 Then
                    namespaceArray.Free()
                    Return Me
                End If
 
                ' New set of parts should be a subset of what we already have
 
                If namespaceArray.Count = 1 Then
                    Dim result = namespaceArray(0)
                    namespaceArray.Free()
 
                    If _namespacesToMerge.Contains(result) Then
                        Return result
                    End If
 
                    Throw ExceptionUtilities.Unreachable
                End If
 
                Dim lookup = New SmallDictionary(Of NamespaceSymbol, Boolean)()
 
                For Each item In _namespacesToMerge
                    lookup(item) = False
                Next
 
                For Each item In namespaceArray
                    If Not lookup.TryGetValue(item, Nothing) Then
                        Debug.Assert(False)
                        namespaceArray.Free()
                        Return Me
                    End If
                Next
 
                Return Shrink(namespaceArray.ToImmutableAndFree())
            End Function
 
            Private Overloads Function Shrink(namespaceArray As ImmutableArray(Of NamespaceSymbol)) As NamespaceGroupSymbol
                Debug.Assert(namespaceArray.Length < _namespacesToMerge.Length)
                If namespaceArray.Length >= _namespacesToMerge.Length Then
                    Debug.Assert(False)
                    Return Me
                End If
 
                If _containingNamespace Is GlobalNamespace Then
                    Return New NamespaceGroupSymbol(GlobalNamespace, namespaceArray)
                End If
 
                Dim parentsArray = ArrayBuilder(Of NamespaceSymbol).GetInstance(namespaceArray.Length)
                For Each item In namespaceArray
                    parentsArray.Add(item.ContainingNamespace)
                Next
 
                Return New NamespaceGroupSymbol(DirectCast(_containingNamespace, NamespaceGroupSymbol).Shrink(parentsArray.ToImmutableAndFree()), namespaceArray)
            End Function
 
        End Class
    End Class
End Namespace