' 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.Runtime.CompilerServices
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace Microsoft.CodeAnalysis.VisualBasic

    ' Utilities for decoding syntax for use in binding.
    Partial Friend Class Binder
        ''' <summary>
        ''' If the identifier has a type character, report an error on it.
        ''' </summary>
        Public Shared Sub DisallowTypeCharacter(identifier As SyntaxToken,
                                         diagBag As BindingDiagnosticBag,
                                         Optional errid As ERRID = ERRID.ERR_TypecharNotallowed)
            If (identifier.GetTypeCharacter() <> TypeCharacter.None) Then
                ReportDiagnostic(diagBag, identifier, errid)
            End If
        End Sub

        Public Shared Function DecodeVariance(varianceKeywordOpt As SyntaxToken) As VarianceKind
            Select Case varianceKeywordOpt.Kind
                Case SyntaxKind.None
                    Return VarianceKind.None
                Case SyntaxKind.InKeyword
                    Return VarianceKind.In
                Case SyntaxKind.OutKeyword
                    Return VarianceKind.Out
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(varianceKeywordOpt.Kind)
            End Select
        End Function

        ''' <summary>
        ''' Given a list of keywords and a set of keywords kinds to search, return the first keyword
        ''' in the list, if any, that matches one of the keyword kinds.
        ''' </summary>
        Public Shared Function FindFirstKeyword(syntax As SyntaxTokenList,
                                                ParamArray keywordKinds As SyntaxKind()) As SyntaxToken
            For Each keywordSyntax In syntax
                If Array.IndexOf(keywordKinds, keywordSyntax.Kind) >= 0 Then
                    Return keywordSyntax
                End If
            Next

            Return Nothing
        End Function

        ' An array consisting of just the Friend keyword.
        Private Shared ReadOnly s_friendKeyword As SyntaxKind() = {SyntaxKind.FriendKeyword}

        ' Report an error on the first keyword to match one of the given kinds.
        Public Sub ReportModifierError(modifiers As SyntaxTokenList,
                                              errid As ERRID,
                                              diagBag As DiagnosticBag,
                                              ParamArray keywordKinds As SyntaxKind())
            Dim badKeyword = FindFirstKeyword(modifiers, keywordKinds)
            ' Special case: Report "Protected Friend" as error combination if 
            ' Protected is bad and both Protected and Friend found inside modifiers.
            If badKeyword.Kind = SyntaxKind.ProtectedKeyword Then
                Dim friendToken = FindFirstKeyword(modifiers, s_friendKeyword)
                If friendToken.Kind <> SyntaxKind.None Then
                    Dim startLoc As Integer = Math.Min(badKeyword.SpanStart, friendToken.SpanStart)
                    Dim endLoc As Integer = Math.Max(badKeyword.Span.End, friendToken.Span.End)
                    Dim location = Me.SyntaxTree.GetLocation(New TextSpan(startLoc, endLoc - startLoc))
                    ReportDiagnostic(diagBag, location, errid, badKeyword.ToString() & " " & friendToken.ToString())
                    Return
                End If
            End If

            ' Normal case.
            ReportDiagnostic(diagBag, badKeyword, errid, badKeyword.ToString())
        End Sub

        ''' <summary>
        ''' Map syntax kind of a modifier keyword to SourceMemberFlags value
        ''' </summary>
        Friend Shared Function MapKeywordToFlag(syntax As SyntaxToken) As SourceMemberFlags
            Select Case syntax.Kind
                Case SyntaxKind.PrivateKeyword : Return SourceMemberFlags.Private
                Case SyntaxKind.FriendKeyword : Return SourceMemberFlags.Friend
                Case SyntaxKind.ProtectedKeyword : Return SourceMemberFlags.Protected
                Case SyntaxKind.PublicKeyword : Return SourceMemberFlags.Public
                Case SyntaxKind.SharedKeyword : Return SourceMemberFlags.Shared
                Case SyntaxKind.ReadOnlyKeyword : Return SourceMemberFlags.ReadOnly
                Case SyntaxKind.WriteOnlyKeyword : Return SourceMemberFlags.WriteOnly
                Case SyntaxKind.OverloadsKeyword : Return SourceMemberFlags.Overloads
                Case SyntaxKind.OverridableKeyword : Return SourceMemberFlags.Overridable
                Case SyntaxKind.MustOverrideKeyword : Return SourceMemberFlags.MustOverride
                Case SyntaxKind.NotOverridableKeyword : Return SourceMemberFlags.NotOverridable
                Case SyntaxKind.OverridesKeyword : Return SourceMemberFlags.Overrides
                Case SyntaxKind.ShadowsKeyword : Return SourceMemberFlags.Shadows
                Case SyntaxKind.ConstKeyword : Return SourceMemberFlags.Const
                Case SyntaxKind.StaticKeyword : Return SourceMemberFlags.Static
                Case SyntaxKind.DefaultKeyword : Return SourceMemberFlags.Default
                Case SyntaxKind.WithEventsKeyword : Return SourceMemberFlags.WithEvents
                Case SyntaxKind.WideningKeyword : Return SourceMemberFlags.Widening
                Case SyntaxKind.NarrowingKeyword : Return SourceMemberFlags.Narrowing
                Case SyntaxKind.PartialKeyword : Return SourceMemberFlags.Partial
                Case SyntaxKind.DimKeyword : Return SourceMemberFlags.Dim
                Case SyntaxKind.MustInheritKeyword : Return SourceMemberFlags.MustInherit
                Case SyntaxKind.NotInheritableKeyword : Return SourceMemberFlags.NotInheritable
                Case SyntaxKind.AsyncKeyword : Return SourceMemberFlags.Async
                Case SyntaxKind.IteratorKeyword : Return SourceMemberFlags.Iterator

                Case Else
#If DEBUG Then
                    ' this case should only be reached for invalid code (invalid == error, not just warnings)
                    ' all possible modifiers should have been handled above.

                    Debug.Assert(syntax.ContainsDiagnostics)

                    Dim hasAtLeastOneSyntaxError As Boolean = False
                    For Each diag In syntax.Errors
                        If diag.Severity = DiagnosticSeverity.Error Then
                            hasAtLeastOneSyntaxError = True
                            Exit For
                        End If
                    Next
                    Debug.Assert(hasAtLeastOneSyntaxError)
#End If

                    Return SourceMemberFlags.None
            End Select
        End Function

        ''' <summary>
        ''' Decodes a set of modifier flags, reported any errors with the flags.
        ''' </summary>
        ''' <param name="syntax">The syntax list of the modifiers.</param>
        ''' <param name="allowableModifiers">A bit-flag of the allowable modifiers. If a bit isn't set, an error occurs.</param>
        ''' <param name="errIdBadModifier">Error ID to report if a bad modifier is found.</param>
        ''' <param name="defaultAccessibility">The default accessibility. </param>
        ''' <returns>Flags for the modifiers.</returns>
        Public Function DecodeModifiers(syntax As SyntaxTokenList,
                                        allowableModifiers As SourceMemberFlags,
                                        errIdBadModifier As ERRID,
                                        defaultAccessibility As Accessibility,
                                        diagBag As DiagnosticBag) As MemberModifiers
            Dim foundModifiers As SourceMemberFlags = Nothing
            Dim privateProtectedToken As SyntaxToken = Nothing
            Dim privateOverridableModifier As SyntaxToken = Nothing
            Dim privateMustOverrideModifier As SyntaxToken = Nothing
            Dim privateNotOverridableModifier As SyntaxToken = Nothing

            ' Go through each modifiers, accumulating flags of what we've seen and reporting errors.
            For Each keywordSyntax In syntax
                Dim currentModifier As SourceMemberFlags = MapKeywordToFlag(keywordSyntax)

                If currentModifier = SourceMemberFlags.None Then
                    Continue For
                End If

                ' Report errors with the modifier
                If (currentModifier And allowableModifiers) = 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, errIdBadModifier, keywordSyntax.ToString())
                ElseIf (currentModifier And foundModifiers) <> 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_DuplicateSpecifier)
                ElseIf (currentModifier And SourceMemberFlags.AllAccessibilityModifiers) <> 0 AndAlso
                       (foundModifiers And SourceMemberFlags.AllAccessibilityModifiers) <> 0 AndAlso
                       Not ((foundModifiers Or currentModifier) And SourceMemberFlags.AllAccessibilityModifiers) = (SourceMemberFlags.Protected Or SourceMemberFlags.Friend) AndAlso
                       Not (((foundModifiers Or currentModifier) And SourceMemberFlags.AllAccessibilityModifiers) = (SourceMemberFlags.Protected Or SourceMemberFlags.Private)) Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_DuplicateAccessCategoryUsed)
                ElseIf (currentModifier And SourceMemberFlags.AllOverrideModifiers) <> 0 AndAlso
                       (foundModifiers And SourceMemberFlags.AllOverrideModifiers) <> 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_DuplicateModifierCategoryUsed)
                ElseIf (currentModifier And SourceMemberFlags.AllWriteabilityModifiers) <> 0 AndAlso
                       (foundModifiers And SourceMemberFlags.AllWriteabilityModifiers) <> 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_DuplicateWriteabilityCategoryUsed)
                ElseIf (currentModifier And SourceMemberFlags.AllConversionModifiers) <> 0 AndAlso
                       (foundModifiers And SourceMemberFlags.AllConversionModifiers) <> 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_DuplicateConversionCategoryUsed)
                ElseIf (currentModifier And SourceMemberFlags.AllShadowingModifiers) <> 0 AndAlso
                       (foundModifiers And SourceMemberFlags.AllShadowingModifiers) <> 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_BadSpecifierCombo2, "Overloads", "Shadows")
                ElseIf (currentModifier And (SourceMemberFlags.Overrides Or SourceMemberFlags.Overridable)) <> 0 AndAlso
                       (foundModifiers And (SourceMemberFlags.Overrides Or SourceMemberFlags.Overridable)) <> 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_OverridesImpliesOverridable)
                ElseIf (currentModifier And SourceMemberFlags.PrivateOverridableModifiers) <> 0 AndAlso
                       (foundModifiers And SourceMemberFlags.PrivateOverridableModifiers) <> 0 Then
                    privateOverridableModifier = keywordSyntax
                    foundModifiers = foundModifiers Or currentModifier
                ElseIf (currentModifier And SourceMemberFlags.ShadowsAndOverrides) <> 0 AndAlso
                    (foundModifiers And SourceMemberFlags.ShadowsAndOverrides) <> 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_BadSpecifierCombo2, "Overrides", "Shadows")
                ElseIf (currentModifier And SourceMemberFlags.PrivateMustOverrideModifiers) <> 0 AndAlso
                       (foundModifiers And SourceMemberFlags.PrivateMustOverrideModifiers) <> 0 Then
                    privateMustOverrideModifier = keywordSyntax
                    foundModifiers = foundModifiers Or currentModifier
                ElseIf (currentModifier And SourceMemberFlags.PrivateNotOverridableModifiers) <> 0 AndAlso
                       (foundModifiers And SourceMemberFlags.PrivateNotOverridableModifiers) <> 0 Then
                    privateNotOverridableModifier = keywordSyntax
                    foundModifiers = foundModifiers Or currentModifier
                ElseIf (currentModifier And (SourceMemberFlags.Iterator Or SourceMemberFlags.WriteOnly)) <> 0 AndAlso
                       (foundModifiers And (SourceMemberFlags.Iterator Or SourceMemberFlags.WriteOnly)) <> 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_BadSpecifierCombo2, "Iterator", "WriteOnly")
                ElseIf (currentModifier And SourceMemberFlags.TypeInheritModifiers) <> 0 AndAlso
                       (foundModifiers And SourceMemberFlags.TypeInheritModifiers) <> 0 Then
                    ReportDiagnostic(diagBag, keywordSyntax, ERRID.ERR_BadSpecifierCombo2, "MustInherit", "NotInheritable")
                    '  Note: Need to be present both MustInherit & NotInheritable for properly reporting #30926
                    foundModifiers = foundModifiers Or currentModifier
                Else
                    If currentModifier = SourceMemberFlags.Private OrElse currentModifier = SourceMemberFlags.Protected Then
                        privateProtectedToken = keywordSyntax
                    End If
                    foundModifiers = foundModifiers Or currentModifier
                End If
            Next

            ' Convert the accessibility modifiers into accessibility.
            Dim access As Accessibility
            If (foundModifiers And SourceMemberFlags.Public) <> 0 Then
                access = Accessibility.Public
            ElseIf (foundModifiers And (SourceMemberFlags.Friend Or SourceMemberFlags.Protected)) = (SourceMemberFlags.Friend Or SourceMemberFlags.Protected) Then
                access = Accessibility.ProtectedOrFriend
            ElseIf (foundModifiers And (SourceMemberFlags.Private Or SourceMemberFlags.Protected)) = (SourceMemberFlags.Private Or SourceMemberFlags.Protected) Then
                access = Accessibility.ProtectedAndFriend
                InternalSyntax.Parser.CheckFeatureAvailability(
                    diagBag,
                    privateProtectedToken.GetLocation(),
                    DirectCast(privateProtectedToken.SyntaxTree, VisualBasicSyntaxTree).Options.LanguageVersion,
                    InternalSyntax.Feature.PrivateProtected)
            ElseIf (foundModifiers And SourceMemberFlags.Friend) <> 0 Then
                access = Accessibility.Friend
            ElseIf (foundModifiers And SourceMemberFlags.Protected) <> 0 Then
                access = Accessibility.Protected
            ElseIf (foundModifiers And SourceMemberFlags.Private) <> 0 Then
                access = Accessibility.Private
            Else
                access = defaultAccessibility
            End If

            If access = Accessibility.Private Then
                If (foundModifiers And SourceMemberFlags.Overridable) <> 0 Then
                    ReportDiagnostic(diagBag, privateOverridableModifier, ERRID.ERR_BadSpecifierCombo2, "Private", "Overridable")
                ElseIf (foundModifiers And SourceMemberFlags.MustOverride) <> 0 Then
                    ReportDiagnostic(diagBag, privateMustOverrideModifier, ERRID.ERR_BadSpecifierCombo2, "Private", "MustOverride")
                ElseIf (foundModifiers And SourceMemberFlags.NotOverridable) <> 0 Then
                    ReportDiagnostic(diagBag, privateNotOverridableModifier, ERRID.ERR_BadSpecifierCombo2, "Private", "NotOverridable")
                End If
                foundModifiers = foundModifiers And Not (SourceMemberFlags.Overridable Or SourceMemberFlags.MustOverride Or SourceMemberFlags.NotOverridable)
            End If

            ' Add accessibility into the flags.
            Return New MemberModifiers(foundModifiers, DirectCast(access, SourceMemberFlags))
        End Function

        ''' <summary>
        ''' Decode a list of parameter modifiers, and return the flags associated with it.
        ''' </summary>
        Private Shared Function DecodeParameterModifiers(container As Symbol,
                                                 modifiers As SyntaxTokenList,
                                                 checkModifier As CheckParameterModifierDelegate,
                                                 diagBag As BindingDiagnosticBag) As SourceParameterFlags
            Dim flags As SourceParameterFlags = Nothing

            ' Go through each modifiers, accumulating flags of what we've seen and reporting errors.
            For Each keywordSyntax In modifiers
                Dim foundFlag As SourceParameterFlags

                Select Case keywordSyntax.Kind
                    Case SyntaxKind.ByRefKeyword : foundFlag = SourceParameterFlags.ByRef
                    Case SyntaxKind.ByValKeyword : foundFlag = SourceParameterFlags.ByVal
                    Case SyntaxKind.OptionalKeyword : foundFlag = SourceParameterFlags.Optional
                    Case SyntaxKind.ParamArrayKeyword : foundFlag = SourceParameterFlags.ParamArray
                End Select

                ' Report errors with the modifier
                If checkModifier IsNot Nothing Then
                    ' check modifier will clear invalid flags
                    foundFlag = checkModifier(container, keywordSyntax, foundFlag, diagBag)
                End If

                flags = flags Or foundFlag
            Next

            Return flags
        End Function

        ''' <summary>
        ''' Create the Nullable version of a type.
        ''' </summary>
        Public Function CreateNullableOf(typeArgument As TypeSymbol,
                                         syntax As VisualBasicSyntaxNode,
                                         syntaxTypeArgument As VisualBasicSyntaxNode,
                                         diagBag As BindingDiagnosticBag) As NamedTypeSymbol
            ' Get the Nullable type
            Dim nullableType As NamedTypeSymbol = DirectCast(GetSpecialType(SpecialType.System_Nullable_T, syntax, diagBag), NamedTypeSymbol)

            ' Construct the Nullable(Of T).
            Dim constructedType = nullableType.Construct(ImmutableArray.Create(typeArgument))

            ' Validate the type argument meets the Structure constraint on the Nullable type.
            If ShouldCheckConstraints Then
                Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance()
                Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing
                constructedType.CheckConstraints(Compilation.LanguageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=GetNewCompoundUseSiteInfo(diagBag))

                If useSiteDiagnosticsBuilder IsNot Nothing Then
                    diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder)
                End If

                For Each pair In diagnosticsBuilder
                    diagBag.Add(pair.UseSiteInfo, syntaxTypeArgument.GetLocation())
                Next
                diagnosticsBuilder.Free()
            End If

            Return constructedType
        End Function

        Public Function GetNewCompoundUseSiteInfo(futureDestination As BindingDiagnosticBag) As CompoundUseSiteInfo(Of AssemblySymbol)
            Return New CompoundUseSiteInfo(Of AssemblySymbol)(futureDestination, Compilation.Assembly)
        End Function

        ''' <summary>
        ''' Possible create the array version of type, given the element type and the array modifier syntax.
        ''' </summary>
        Public Function ApplyArrayRankSpecifiersToType(elementType As TypeSymbol,
                                      arrayModifierSyntax As SyntaxList(Of ArrayRankSpecifierSyntax),
                                      diagnostics As BindingDiagnosticBag) As TypeSymbol

            Dim currentType As TypeSymbol = elementType

            ' Array modifiers must be handled in reverse order, that's the language syntax.
            For i As Integer = arrayModifierSyntax.Count - 1 To 0 Step -1
                ' Rank of the array is the number of commas, plus one.
                Dim arrayModifier = arrayModifierSyntax(i)

                If arrayModifier.Rank > 32 Then
                    ReportDiagnostic(diagnostics, arrayModifier, ERRID.ERR_ArrayRankLimit)
                End If

                currentType = ArrayTypeSymbol.CreateVBArray(currentType, Nothing, arrayModifier.Rank, Compilation)
            Next

            Return currentType
        End Function

        ''' <summary>
        ''' Possibly create the array version of type, given the element type and the array modifier syntax.
        ''' </summary>
        Public Function ApplyArrayRankSpecifiersAndBoundsToType(elementType As TypeSymbol,
                                      arrayModifierSyntax As SyntaxList(Of ArrayRankSpecifierSyntax),
                                      arrayBoundsOpt As ArgumentListSyntax,
                                      diagnostics As BindingDiagnosticBag) As TypeSymbol

            Dim currentType As TypeSymbol = elementType

            ' Array modifiers must be handled in reverse order, that's the language syntax.
            currentType = ApplyArrayRankSpecifiersToType(elementType, arrayModifierSyntax, diagnostics)

            ' Array bounds must be handled in reverse order, that's the language syntax.
            If arrayBoundsOpt IsNot Nothing Then
                Dim rank As Integer = arrayBoundsOpt.Arguments.Count

                If rank = 0 Then
                    rank = 1
                End If

                If rank > 32 Then
                    ReportDiagnostic(diagnostics, arrayBoundsOpt, ERRID.ERR_ArrayRankLimit)
                End If

                currentType = ArrayTypeSymbol.CreateVBArray(currentType, Nothing, rank, Compilation)
            End If

            Return currentType
        End Function

        ''' <summary>
        ''' Create the array version of type, given the element type and the array modifier syntax. Throws if
        ''' there aren't any array modifiers and the result is not an array type.
        ''' </summary>
        Public Function CreateArrayOf(elementType As TypeSymbol,
                                      arrayModifierSyntax As SyntaxList(Of ArrayRankSpecifierSyntax),
                                      arrayBoundsOpt As ArgumentListSyntax,
                                      diagnostics As BindingDiagnosticBag) As ArrayTypeSymbol

            Debug.Assert(arrayModifierSyntax.Count > 0 OrElse
                                  arrayBoundsOpt IsNot Nothing)

            Return DirectCast(ApplyArrayRankSpecifiersAndBoundsToType(elementType,
                                                    arrayModifierSyntax,
                                                    arrayBoundsOpt,
                                                    diagnostics), ArrayTypeSymbol)
        End Function

        ''' <summary>
        ''' Given an identifier and an As clause, return true if the identifier does not have a type
        ''' declared for it (e.g., no type character and no as clause).
        ''' </summary>
        Private Shared Function HasDefaultType(identifierSyntax As SyntaxToken,
                                        asClauseOptSyntax As AsClauseSyntax) As Boolean
            Return (identifierSyntax.GetTypeCharacter() = TypeCharacter.None AndAlso asClauseOptSyntax Is Nothing)
        End Function

        ''' <summary>
        ''' Given an identifier and an As clause, return true if the identifier does not have a type
        ''' declared for it (e.g., no type character and no as clause).
        ''' </summary>
        Private Shared Function HasDefaultType(identifierSyntax As ModifiedIdentifierSyntax,
                                       asClauseOptSyntax As AsClauseSyntax) As Boolean
            Return HasDefaultType(identifierSyntax.Identifier, asClauseOptSyntax)
        End Function

        ''' <summary>
        ''' Given an identifier, return true if the identifier declares an array.
        ''' (e.g., identifier  specifies ())
        ''' </summary>
        Public Shared Function IsArrayType(identifierSyntax As ModifiedIdentifierSyntax) As Boolean
            Return identifierSyntax.ArrayBounds IsNot Nothing OrElse identifierSyntax.ArrayRankSpecifiers.Count > 0
        End Function

        ''' <summary>
        ''' Flags to specify where the decoding of the modified identifier's type happens.
        ''' </summary>
        <Flags()>
        Public Enum ModifiedIdentifierTypeDecoderContext
            ''' <summary>
            ''' No context given (default).
            ''' </summary>
            None = 0

            ''' <summary>
            ''' Modified identifier appeared in a lambda declaration.
            ''' </summary>
            LambdaType = 1 << 1

            ''' <summary>
            ''' Modified identifier appeared in a local declaration.
            ''' </summary>
            LocalType = 1 << 2

            ''' <summary>
            ''' Modified identifier appeared in a field declaration.
            ''' </summary>
            FieldType = 1 << 3

            ''' <summary>
            ''' Modified identifier appeared in a parameter.
            ''' </summary>
            ParameterType = 1 << 4

            ''' <summary>
            ''' Modified identifier appeared in a query range variable declaration.
            ''' </summary>
            QueryRangeVariableType = 1 << 5

            ''' <summary>
            ''' Combined flag to express that a modified identifier appeared in a local or field declaration.
            ''' </summary>
            LocalOrFieldType = LocalType Or
                               FieldType

            ''' <summary>
            ''' Combined flag to express that a modified identifier appeared in a parameter of a lambda.
            ''' </summary>
            LambdaParameterType = LambdaType Or
                                  ParameterType

            StaticLocalType = 1 << 6
        End Enum

        ''' <summary>
        ''' Given a modified identifier and a type, return the actual type to use. Applies the type character
        ''' and type modifiers to the given type.
        ''' </summary>
        ''' <param name="modifiedIdentifier">The modified identifier.</param>
        ''' <param name="asClauseOrValueType">Bound type after the As or a type from the initializing value expression. Can be nothing if no type was supplied.</param>
        ''' <param name="asClauseSyntaxOpt">If specified then it is the syntax for the as clause and the type is the bound type from this syntax.</param>
        ''' <param name="getRequireTypeDiagnosticInfoFunc">Delegate to get diagnostic info to generate if a required type is missing (Option Strict On/Custom) </param>
        ''' <returns>The type, as modified by the type character, type modifiers. Uses Object as default if needed.</returns>
        ''' <remarks></remarks>
        Public Function DecodeModifiedIdentifierType(modifiedIdentifier As ModifiedIdentifierSyntax,
                                                     asClauseOrValueType As TypeSymbol,
                                                     asClauseSyntaxOpt As AsClauseSyntax,
                                                     initializerSyntaxOpt As VisualBasicSyntaxNode,
                                                     getRequireTypeDiagnosticInfoFunc As Func(Of DiagnosticInfo),
                                                     diagBag As BindingDiagnosticBag,
                                                     Optional decoderContext As ModifiedIdentifierTypeDecoderContext = ModifiedIdentifierTypeDecoderContext.None
        ) As TypeSymbol
            Dim baseType As TypeSymbol = DecodeIdentifierType(modifiedIdentifier.Identifier, asClauseOrValueType, getRequireTypeDiagnosticInfoFunc, diagBag, decoderContext)

            ' Create nullable type.
            If modifiedIdentifier.Nullable.Node IsNot Nothing Then

                If asClauseSyntaxOpt IsNot Nothing Then
                    If asClauseSyntaxOpt.Type.Kind = SyntaxKind.NullableType Then
                        ' Special case error for e.g.: "x? as Integer?"
                        ReportDiagnostic(diagBag, asClauseSyntaxOpt, ERRID.ERR_CantSpecifyNullableOnBoth)
                        ' Return the type from the "as" clause, rather than creating a
                        ' second nullable type below (since the user probably intended
                        ' "x? as Integer?" to represent Integer? rather than Integer??).
                        Return baseType
                    ElseIf asClauseSyntaxOpt.Kind = SyntaxKind.AsNewClause Then
                        ReportDiagnostic(diagBag, asClauseSyntaxOpt, ERRID.ERR_CantSpecifyAsNewAndNullable)
                    ElseIf asClauseOrValueType.IsArrayType Then
                        ' ReportDiagnostic(diagBag, asClauseSyntaxOpt, ERRID.ERR_CantSpecifyArrayAndNullableOnBoth)
                    End If
                End If

                If asClauseOrValueType Is Nothing AndAlso baseType.IsObjectType() Then
                    ' We didn't have a type specified and ended up with Object,
                    ' which means that we didn't have type character as well.
                    Debug.Assert(asClauseSyntaxOpt Is Nothing)
                    If (decoderContext And ModifiedIdentifierTypeDecoderContext.ParameterType) <> 0 Then
                        ReportDiagnostic(diagBag, modifiedIdentifier, ERRID.ERR_NullableParameterMustSpecifyType)
                    ElseIf (decoderContext And ModifiedIdentifierTypeDecoderContext.LocalOrFieldType) <> 0 AndAlso
                           ((decoderContext And ModifiedIdentifierTypeDecoderContext.StaticLocalType) <> 0 OrElse
                            (initializerSyntaxOpt Is Nothing AndAlso
                             modifiedIdentifier.ArrayBounds Is Nothing)) AndAlso
                           OptionInfer Then
                        ' Note: fields do not support Option Infer On / local type inference, but Dev10 shared the error 
                        ' reporting between locals and fields like Roslyn does. So these error will also be shown (and they make 
                        ' sense) for fields in case Option Infer is On.

                        ' If option infer is off, variable declarations without an AsClause are explicitly
                        ' set to Object, but this message should only be shown for when the type is implicitly system.object
                        ReportDiagnostic(diagBag, modifiedIdentifier, ERRID.ERR_NullableImplicit)
                    End If

                ElseIf Not baseType.IsValueType Then
                    ' Trying to make a nullable reference type.
                    ReportDiagnostic(diagBag, modifiedIdentifier, ERRID.ERR_BadTypeArgForStructConstraintNull, baseType)
                Else
                    baseType = CreateNullableOf(baseType,
                                                modifiedIdentifier,
                                                If(asClauseSyntaxOpt IsNot Nothing,
                                                   asClauseSyntaxOpt.Type,
                                                   DirectCast(modifiedIdentifier, VisualBasicSyntaxNode)),
                                                diagBag)
                End If
            End If

            ' Check for array modifier on the identifier and array type. VB doesn't allow this combination. 
            If IsArrayType(modifiedIdentifier) Then
                If asClauseSyntaxOpt IsNot Nothing Then
                    If asClauseSyntaxOpt.Type.Kind = SyntaxKind.ArrayType Then
                        ReportDiagnostic(diagBag, asClauseSyntaxOpt.Type, ERRID.ERR_CantSpecifyArraysOnBoth)
                    ElseIf asClauseSyntaxOpt.Type.Kind = SyntaxKind.NullableType Then
                        ' ReportDiagnostic(diagBag, asClauseSyntaxOpt, ERRID.ERR_CantSpecifyArrayAndNullableOnBoth)
                    End If

                ElseIf (decoderContext And ModifiedIdentifierTypeDecoderContext.LambdaParameterType) = ModifiedIdentifierTypeDecoderContext.LambdaParameterType Then
                    ' In lambda parameters's we don't allow array specifiers without the type since
                    ' inference can become ambiguous, see bug dd:80897
                    ReportDiagnostic(diagBag, modifiedIdentifier, ERRID.ERR_CantSpecifyParamsOnLambdaParamNoType)
                End If
            End If

            Return ApplyArrayRankSpecifiersAndBoundsToType(baseType, modifiedIdentifier.ArrayRankSpecifiers, modifiedIdentifier.ArrayBounds, diagBag)

        End Function

        ''' <summary>
        ''' Given a modified identifier and a type syntax, return the actual type to use. Applies the type character
        ''' and type modifiers to the given type.
        ''' </summary>
        ''' <param name="modifiedIdentifier">The modified identifier.</param>
        ''' <param name="asClauseOpt"> As clause syntax. Can be nothing if no type was supplied.</param>
        ''' <param name="getRequireTypeDiagnosticInfoFunc">Delegate to get diagnostic info to generate if a required type is missing (Option Strict On/Custom) </param>
        ''' <param name="asClauseType">The type of the AsClauseOpt before applying any modifiers</param>
        ''' <returns>The type, as modified by the type character, type modifiers. Uses Object as default if needed.</returns>
        Public Function DecodeModifiedIdentifierType(modifiedIdentifier As ModifiedIdentifierSyntax,
                                                     asClauseOpt As AsClauseSyntax,
                                                     initializerSyntaxOpt As EqualsValueSyntax,
                                                     getRequireTypeDiagnosticInfoFunc As Func(Of DiagnosticInfo),
                                                     <Out()> ByRef asClauseType As TypeSymbol,
                                                     diagBag As BindingDiagnosticBag,
                                                     Optional decoderContext As ModifiedIdentifierTypeDecoderContext = Nothing
        ) As TypeSymbol

            If asClauseOpt IsNot Nothing Then

                ' NOTE: This method is NOT supposed to be called for 'As New With {...}'
                '       expression, if it is it should be revised to return bound node
                Debug.Assert(asClauseOpt.Kind <> SyntaxKind.AsNewClause OrElse
                             DirectCast(asClauseOpt, AsNewClauseSyntax).NewExpression.Kind <> SyntaxKind.AnonymousObjectCreationExpression)

                asClauseType = BindTypeSyntax(asClauseOpt.Type, diagBag)
            Else
                asClauseType = Nothing
            End If

            Return DecodeModifiedIdentifierType(modifiedIdentifier, asClauseType, asClauseOpt, initializerSyntaxOpt, getRequireTypeDiagnosticInfoFunc, diagBag, decoderContext)

        End Function

        ''' <summary>
        ''' Given a modified identifier and a type syntax, return the actual type to use. Applies the type character
        ''' and type modifiers to the given type.
        ''' </summary>
        ''' <param name="modifiedIdentifier">The modified identifier.</param>
        ''' <param name="asClauseOpt"> As clause syntax. Can be nothing if no type was supplied.</param>
        ''' <param name="getRequireTypeDiagnosticInfoFunc">Delegate to get diagnostic info to generate if a required type is missing (Option Strict On/Custom) </param>
        ''' <returns>The type, as modified by the type character, type modifiers. Uses Object as default if needed.</returns>
        Public Function DecodeModifiedIdentifierType(modifiedIdentifier As ModifiedIdentifierSyntax,
                                                     asClauseOpt As AsClauseSyntax,
                                                     initializerSyntaxOpt As EqualsValueSyntax,
                                                     getRequireTypeDiagnosticInfoFunc As Func(Of DiagnosticInfo),
                                                     diagBag As BindingDiagnosticBag,
                                                     Optional decoderContext As ModifiedIdentifierTypeDecoderContext = Nothing
        ) As TypeSymbol

            Dim asClauseType As TypeSymbol = Nothing
            Return DecodeModifiedIdentifierType(modifiedIdentifier, asClauseOpt, initializerSyntaxOpt, getRequireTypeDiagnosticInfoFunc, asClauseType, diagBag, decoderContext)

        End Function

        ''' <summary>
        ''' Given a identifier and as clause syntax, return the actual type to use. Uses the type character or the type syntax.
        ''' </summary>
        ''' <param name="identifier">The identifier.</param>
        ''' <param name="asClauseOpt">Syntax for optional as clause. Can be nothing if no type was supplied.</param>
        ''' <param name="getRequireTypeDiagnosticInfoFunc">Delegate to get diagnostic info to generate if a required type is missing (Option Strict On/Custom) </param>
        ''' <returns>The type, either from the type character or the as clause. Uses Object as default if needed.</returns>
        ''' <remarks></remarks>
        Public Function DecodeIdentifierType(identifier As SyntaxToken,
                                             asClauseOpt As AsClauseSyntax,
                                             getRequireTypeDiagnosticInfoFunc As Func(Of DiagnosticInfo),
                                             ByRef asClauseType As TypeSymbol,
                                             diagBag As BindingDiagnosticBag) As TypeSymbol

            If asClauseOpt IsNot Nothing Then
                asClauseType = BindTypeSyntax(asClauseOpt.Type, diagBag)
            Else
                asClauseType = Nothing
            End If

            Return DecodeIdentifierType(identifier, asClauseType, getRequireTypeDiagnosticInfoFunc, diagBag)
        End Function

        ''' <summary>
        ''' Given a identifier and as clause syntax, return the actual type to use. Uses the type character or the type syntax.
        ''' </summary>
        ''' <param name="identifier">The identifier.</param>
        ''' <param name="asClauseOpt">Syntax for optional as clause. Can be nothing if no type was supplied.</param>
        ''' <param name="getRequireTypeDiagnosticInfoFunc">Delegate to get diagnostic info to generate if a required type is missing (Option Strict On/Custom) </param>
        ''' <returns>The type, either from the type character or the as clause. Uses Object as default if needed.</returns>
        ''' <remarks></remarks>
        Public Function DecodeIdentifierType(identifier As SyntaxToken,
                                             asClauseOpt As AsClauseSyntax,
                                             getRequireTypeDiagnosticInfoFunc As Func(Of DiagnosticInfo),
                                             diagBag As BindingDiagnosticBag) As TypeSymbol
            Dim asClauseType As TypeSymbol = Nothing
            Return DecodeIdentifierType(identifier, asClauseOpt, getRequireTypeDiagnosticInfoFunc, asClauseType, diagBag)
        End Function

        ''' <summary>
        ''' Given a identifier and a type, return the actual type to use. Uses the type character or the given type.
        ''' </summary>
        ''' <param name="identifier">The identifier.</param>
        ''' <param name="asClauseType">Bound type after the As. Can be nothing if no type was supplied.</param>
        ''' <param name="getRequireTypeDiagnosticInfoFunc">Delegate to get diagnostic info to generate if a required type is missing (Option Strict On/Custom) </param>
        ''' <returns>The type, either from the type character or the as clause type. Uses Object as default if needed.</returns>
        ''' <remarks></remarks>
        Public Function DecodeIdentifierType(identifier As SyntaxToken,
                                             asClauseType As TypeSymbol,
                                             getRequireTypeDiagnosticInfoFunc As Func(Of DiagnosticInfo),
                                             diagBag As BindingDiagnosticBag,
                                             Optional decoderContext As ModifiedIdentifierTypeDecoderContext = ModifiedIdentifierTypeDecoderContext.None
        ) As TypeSymbol
            Dim typeCharacterType As TypeSymbol = Nothing
            Dim typeCharacterString As String = Nothing
            Dim specialType As SpecialType = GetSpecialTypeForTypeCharacter(identifier.GetTypeCharacter(), typeCharacterString)

            If specialType <> Microsoft.CodeAnalysis.SpecialType.None Then
                typeCharacterType = GetSpecialType(specialType, identifier, diagBag)
            End If

            If asClauseType IsNot Nothing Then
                ' We have a special error for this situation with query range variables, it is reported 
                ' by the query binding code.
                If typeCharacterType IsNot Nothing AndAlso
                   (decoderContext And ModifiedIdentifierTypeDecoderContext.QueryRangeVariableType) = 0 Then
                    ReportDiagnostic(diagBag, identifier, ERRID.ERR_TypeCharWithType1, typeCharacterString)
                End If
                Return asClauseType
            ElseIf typeCharacterType IsNot Nothing Then
                Return typeCharacterType
            Else
                If getRequireTypeDiagnosticInfoFunc IsNot Nothing Then
                    ReportDiagnostic(diagBag, identifier, getRequireTypeDiagnosticInfoFunc())
                End If

                ' default type is object.
                Return GetSpecialType(SpecialType.System_Object, identifier, diagBag)
            End If
        End Function

        Public Shared Function GetSpecialTypeForTypeCharacter(typeChar As TypeCharacter, ByRef typeCharacterString As String) As SpecialType
            Dim specialType As SpecialType = SpecialType.None

            Select Case typeChar
                Case TypeCharacter.Decimal
                    specialType = SpecialType.System_Decimal
                    typeCharacterString = "@"
                Case TypeCharacter.DecimalLiteral
                    specialType = SpecialType.System_Decimal
                    typeCharacterString = "D"
                Case TypeCharacter.Double
                    specialType = SpecialType.System_Double
                    typeCharacterString = "#"
                Case TypeCharacter.DoubleLiteral
                    specialType = SpecialType.System_Double
                    typeCharacterString = "R"
                Case TypeCharacter.Integer
                    specialType = SpecialType.System_Int32
                    typeCharacterString = "%"
                Case TypeCharacter.IntegerLiteral
                    specialType = SpecialType.System_Int32
                    typeCharacterString = "I"
                Case TypeCharacter.Long
                    specialType = SpecialType.System_Int64
                    typeCharacterString = "&"
                Case TypeCharacter.LongLiteral
                    specialType = SpecialType.System_Int64
                    typeCharacterString = "L"
                Case TypeCharacter.ShortLiteral
                    specialType = SpecialType.System_Int16
                    typeCharacterString = "S"
                Case TypeCharacter.Single
                    specialType = SpecialType.System_Single
                    typeCharacterString = "!"
                Case TypeCharacter.SingleLiteral
                    specialType = SpecialType.System_Single
                    typeCharacterString = "F"
                Case TypeCharacter.String
                    specialType = SpecialType.System_String
                    typeCharacterString = "$"
                Case TypeCharacter.UIntegerLiteral
                    specialType = SpecialType.System_UInt32
                    typeCharacterString = "UI"
                Case TypeCharacter.ULongLiteral
                    specialType = SpecialType.System_UInt64
                    typeCharacterString = "UL"
                Case TypeCharacter.UShortLiteral
                    specialType = SpecialType.System_UInt16
                    typeCharacterString = "US"
                Case TypeCharacter.None
                    typeCharacterString = Nothing
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(typeChar)
            End Select

            Return specialType
        End Function

        Public Shared Function ExtractTypeCharacter(node As SyntaxNode) As TypeCharacter
            Dim result As TypeCharacter = TypeCharacter.None

            If node IsNot Nothing Then
                Select Case node.Kind
                    Case SyntaxKind.IdentifierName
                        result = DirectCast(node, IdentifierNameSyntax).Identifier.GetTypeCharacter()

                    Case SyntaxKind.GenericName
                        result = DirectCast(node, GenericNameSyntax).Identifier.GetTypeCharacter()

                    Case SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.DictionaryAccessExpression
                        result = ExtractTypeCharacter(DirectCast(node, MemberAccessExpressionSyntax).Name)
                End Select
            End If

            Return result
        End Function

        ''' <summary>
        ''' Decode an option "On" or "Off" values into true or false. Not specified is considered true.
        ''' </summary>
        Public Shared Function DecodeOnOff(keywordSyntax As SyntaxToken) As Boolean
            If keywordSyntax.Node Is Nothing Then
                Return True
            Else
                Select Case keywordSyntax.Kind
                    Case SyntaxKind.OnKeyword
                        Return True
                    Case SyntaxKind.OffKeyword
                        Return False
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(keywordSyntax.Kind)
                End Select
            End If
        End Function

        ''' <summary>
        ''' Decode an option "Text" or "Binary" value into true or false. The syntax is not optional.
        ''' </summary>
        Public Shared Function DecodeTextBinary(keywordSyntax As SyntaxToken) As Boolean?
            If keywordSyntax.Node Is Nothing Then
                Return Nothing ' Must be a syntax error, an error is reported elsewhere
            End If

            Select Case keywordSyntax.Kind
                Case SyntaxKind.TextKeyword
                    Return True
                Case SyntaxKind.BinaryKeyword
                    Return False
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(keywordSyntax.Kind)
            End Select
        End Function

        ''' <summary>
        ''' Decode a parameter list from a delegate declaration into a list of parameter symbols.
        ''' </summary>
        ''' <param name="container">Containing method declaration.</param>
        ''' <param name="syntaxOpt">Optional parameter list syntax</param>
        Public Function DecodeParameterListOfDelegateDeclaration(
            container As Symbol,
            syntaxOpt As ParameterListSyntax,
            diagBag As BindingDiagnosticBag
        ) As ImmutableArray(Of ParameterSymbol)

            If syntaxOpt Is Nothing Then
                Return ImmutableArray(Of ParameterSymbol).Empty
            End If

            Dim parametersSyntax = syntaxOpt.Parameters
            Dim params = ArrayBuilder(Of ParameterSymbol).GetInstance(parametersSyntax.Count)
            Dim result As ImmutableArray(Of ParameterSymbol)
            DecodeParameterList(
                    container,
                    False,
                    SourceMemberFlags.None,
                    parametersSyntax,
                    params,
                    s_checkDelegateParameterModifierCallback,
                    diagBag)

            ' no checks for the parameters:
            ' Section 9.2.5, "Method Parameters" from the VB Language Reference mentions that duplicate parameter names are 
            ' allowed but discouraged for delegates and extern methods
            ' Dev10: Function delegates are allowed to have one or more parameters called Invoke

            result = params.ToImmutable
            params.Free()

            Return result
        End Function

        ''' <summary>
        ''' Decode a parameter list into a list of parameter symbols.
        ''' </summary>
        ''' <param name="container">Containing method declaration.</param>
        ''' <param name="isFromLambda">Parameter is for a lambda expression rather than a regular method.</param>
        ''' <param name="syntaxOpt">Optional parameter list syntax</param>
        ''' <remarks>DO NOT call this to get the parameters of a delegate declaration (<see>DecodeParameterListOfDelegateDeclaration</see>).</remarks>
        Public Function DecodeParameterList(container As Symbol,
                                            isFromLambda As Boolean,
                                            modifiers As SourceMemberFlags,
                                            syntaxOpt As ParameterListSyntax,
                                            diagBag As BindingDiagnosticBag) As ImmutableArray(Of ParameterSymbol)
            Debug.Assert(Not (container.Kind = SymbolKind.Method AndAlso DirectCast(container, MethodSymbol).MethodKind = MethodKind.DelegateInvoke))

            If syntaxOpt Is Nothing Then
                Return ImmutableArray(Of ParameterSymbol).Empty
            End If

            Dim params = ArrayBuilder(Of ParameterSymbol).GetInstance(syntaxOpt.Parameters.Count)
            Dim result As ImmutableArray(Of ParameterSymbol)
            Dim methodSymbol = TryCast(container, MethodSymbol)
            Dim modifierValidator As CheckParameterModifierDelegate = Nothing

            If methodSymbol IsNot Nothing AndAlso methodSymbol.IsUserDefinedOperator() Then
                modifierValidator = s_checkOperatorParameterModifierCallback
            End If

            DecodeParameterList(
                container,
                isFromLambda,
                modifiers,
                syntaxOpt.Parameters,
                params,
                modifierValidator,
                diagBag)

            Dim typeParams As ImmutableArray(Of TypeParameterSymbol) = If(methodSymbol IsNot Nothing AndAlso Not isFromLambda,
                                                                         methodSymbol.TypeParameters,
                                                                         ImmutableArray(Of TypeParameterSymbol).Empty)

            For i = 0 To params.Count - 1
                Dim paramSyntax = syntaxOpt.Parameters(i)
                Dim param = params(i)

                If methodSymbol IsNot Nothing Then
                    If Not methodSymbol.IsSub AndAlso Not methodSymbol.IsUserDefinedOperator() Then
                        ' "Parameter cannot have the same name as its defining function."
                        CheckReservedParameterName(container.Name, paramSyntax, ERRID.ERR_ParamNameFunctionNameCollision, diagBag)
                    End If

                    ' Section 9.2.5, "Method Parameters" from the VB Language Reference mentions that duplicate parameter names 
                    ' are allowed but discouraged for delegates and extern methods
                    If Not methodSymbol.MethodKind = MethodKind.DeclareMethod Then
                        CheckParameterNameNotDuplicate(params, i, paramSyntax, param, diagBag)
                    End If
                End If

                Dim name = param.Name
                For Each tp In typeParams
                    If CaseInsensitiveComparison.Equals(tp.Name, name) Then
                        ' "'{0}' is already declared as a type parameter of this method."
                        ReportDiagnostic(diagBag, paramSyntax.Identifier, ERRID.ERR_NameSameAsMethodTypeParam1, name)
                        Exit For
                    End If
                Next
            Next

            result = params.ToImmutable
            params.Free()

            Return result
        End Function

        Private Shared ReadOnly s_checkOperatorParameterModifierCallback As CheckParameterModifierDelegate = AddressOf CheckOperatorParameterModifier

        Private Shared Function CheckOperatorParameterModifier(container As Symbol, token As SyntaxToken, flag As SourceParameterFlags, diagnostics As BindingDiagnosticBag) As SourceParameterFlags
            If (flag And SourceParameterFlags.ByRef) <> 0 Then
                diagnostics.Add(ERRID.ERR_ByRefIllegal1, token.GetLocation(), container.GetKindText())
                flag = flag And (Not SourceParameterFlags.ByRef)
            End If

            If (flag And SourceParameterFlags.ParamArray) <> 0 Then
                diagnostics.Add(ERRID.ERR_ParamArrayIllegal1, token.GetLocation(), container.GetKindText())
                flag = flag And (Not SourceParameterFlags.ParamArray)
            End If

            If (flag And SourceParameterFlags.Optional) <> 0 Then
                diagnostics.Add(ERRID.ERR_OptionalIllegal1, token.GetLocation(), container.GetKindText())
                flag = flag And (Not SourceParameterFlags.Optional)
            End If

            Return flag
        End Function

        ''' <summary>
        ''' Decode a parameter list into a list of parameter symbols.
        ''' </summary>
        ''' <param name="container">Containing property declaration.</param>
        ''' <param name="syntaxOpt">Optional parameter list syntax</param>
        Public Function DecodePropertyParameterList(container As PropertySymbol,
                                            syntaxOpt As ParameterListSyntax,
                                            diagBag As BindingDiagnosticBag) As ImmutableArray(Of ParameterSymbol)
            If syntaxOpt Is Nothing Then
                Return ImmutableArray(Of ParameterSymbol).Empty
            End If

            Dim params = ArrayBuilder(Of ParameterSymbol).GetInstance(syntaxOpt.Parameters.Count)
            Dim result As ImmutableArray(Of ParameterSymbol)
            DecodeParameterList(
                    container,
                    False,
                    SourceMemberFlags.None,
                    syntaxOpt.Parameters,
                    params,
                    s_checkPropertyParameterModifierCallback,
                    diagBag)

            For i = 0 To params.Count - 1
                Dim paramSyntax = syntaxOpt.Parameters(i)
                Dim param = params(i)

                ' "Property parameters cannot have the name 'Value'."
                If CheckReservedParameterName(StringConstants.ValueParameterName, paramSyntax, ERRID.ERR_PropertySetParamCollisionWithValue, diagBag) Then
                    ' "Parameter cannot have the same name as its defining function."
                    CheckReservedParameterName(container.Name, paramSyntax, ERRID.ERR_ParamNameFunctionNameCollision, diagBag)
                End If

                CheckParameterNameNotDuplicate(params, i, paramSyntax, param, diagBag)
            Next

            result = params.ToImmutable
            params.Free()

            Return result
        End Function

        Private Shared ReadOnly s_checkPropertyParameterModifierCallback As CheckParameterModifierDelegate = AddressOf CheckPropertyParameterModifier

        Private Shared Function CheckPropertyParameterModifier(container As Symbol, token As SyntaxToken, flag As SourceParameterFlags, diagnostics As BindingDiagnosticBag) As SourceParameterFlags
            If flag = SourceParameterFlags.ByRef Then
                Dim location = token.GetLocation()
                diagnostics.Add(ERRID.ERR_ByRefIllegal1, location, container.GetKindText(), token.ToString())
                Return flag And (Not SourceParameterFlags.ByRef)
            End If
            Return flag
        End Function

        Private Shared Function CheckReservedParameterName(reservedName As String, syntax As ParameterSyntax, errorId As ERRID, diagnostics As BindingDiagnosticBag) As Boolean
            Dim identifier = syntax.Identifier
            Dim name = identifier.Identifier.ValueText
            If IdentifierComparison.Equals(reservedName, name) Then
                Dim location = identifier.GetLocation()
                diagnostics.Add(errorId, location)
                Return False
            End If
            Return True
        End Function

        ' TODO: Caller is O(n^2).
        Friend Shared Sub CheckParameterNameNotDuplicate(
                                                 params As ArrayBuilder(Of ParameterSymbol),
                                                 nParams As Integer,
                                                 syntax As ParameterSyntax,
                                                 parameter As ParameterSymbol,
                                                 diagnostics As BindingDiagnosticBag)
            Dim name = parameter.Name
            For i = 0 To nParams - 1
                If IdentifierComparison.Equals(params(i).Name, name) Then
                    ' "Parameter already declared with name '{0}'."
                    ReportDiagnostic(diagnostics, syntax.Identifier, ERRID.ERR_DuplicateParamName1, name)
                    Return
                End If
            Next
        End Sub

        Friend Delegate Function CheckParameterModifierDelegate(container As Symbol, token As SyntaxToken, flag As SourceParameterFlags, diagnostics As BindingDiagnosticBag) As SourceParameterFlags

        Public Sub DecodeParameterList(container As Symbol,
                                            isFromLambda As Boolean,
                                            modifiers As SourceMemberFlags,
                                            syntax As SeparatedSyntaxList(Of ParameterSyntax),
                                            params As ArrayBuilder(Of ParameterSymbol),
                                            checkModifier As CheckParameterModifierDelegate,
                                            diagBag As BindingDiagnosticBag)

            Dim count As Integer = syntax.Count
            Dim ordinal = params.Count
            Dim paramHasImplicitType = False, paramHasExplicitType = False
            Dim flagsOfPreviousParameters As SourceParameterFlags = Nothing
            Dim reportedError = False

            For i = 0 To count - 1
                Dim paramSyntax = syntax(i)
                Dim name = paramSyntax.Identifier.Identifier.ValueText
                Dim flags As SourceParameterFlags = Nothing

                flags = DecodeParameterModifiers(container, paramSyntax.Modifiers, checkModifier, diagBag)

                If (flagsOfPreviousParameters And SourceParameterFlags.Optional) = SourceParameterFlags.Optional Then
                    If (flags And SourceParameterFlags.ParamArray) = SourceParameterFlags.ParamArray AndAlso
                        Not reportedError Then

                        ' If one of the previous parameters is optional then report an error if paramarray is specified.
                        ReportDiagnostic(diagBag, paramSyntax.Identifier.Identifier, ERRID.ERR_ParamArrayWithOptArgs)
                        reportedError = True

                    ElseIf (flags And SourceParameterFlags.Optional) <> SourceParameterFlags.Optional AndAlso
                        Not reportedError Then

                        ' If one of the previous parameters is optional then report an error if this one isn't optional.
                        ReportDiagnostic(diagBag, paramSyntax.Identifier.Identifier, ERRID.ERR_ExpectedOptional)
                        reportedError = True

                    End If
                End If

                If (flagsOfPreviousParameters And SourceParameterFlags.ParamArray) <> 0 AndAlso
                    Not reportedError Then

                    ReportDiagnostic(diagBag, paramSyntax, ERRID.ERR_ParamArrayMustBeLast)
                    reportedError = True

                End If

                Dim newParam As ParameterSymbol

                If isFromLambda Then
                    newParam = UnboundLambdaParameterSymbol.CreateFromSyntax(paramSyntax, name, flags, ordinal, Me, diagBag)
                Else
                    newParam = SourceComplexParameterSymbol.CreateFromSyntax(container, paramSyntax, name, flags, ordinal, Me, checkModifier, diagBag)
                End If
                ordinal += 1

                If newParam.IsByRef AndAlso (modifiers And SourceMemberFlags.Async) = SourceMemberFlags.Async Then
                    ReportDiagnostic(diagBag, paramSyntax, ERRID.ERR_BadAsyncByRefParam)

                ElseIf newParam.IsByRef AndAlso (modifiers And SourceMemberFlags.Iterator) = SourceMemberFlags.Iterator Then
                    ReportDiagnostic(diagBag, paramSyntax, ERRID.ERR_BadIteratorByRefParam)

                Else
                    Dim paramType As TypeSymbol = newParam.Type

                    If paramType IsNot Nothing Then
                        If paramType.IsArrayType() Then
                            Dim restrictedType As TypeSymbol = Nothing
                            If paramType.IsRestrictedArrayType(restrictedType) Then
                                ReportDiagnostic(diagBag, paramSyntax.AsClause.Type, ERRID.ERR_RestrictedType1, restrictedType)
                            End If
                        ElseIf newParam.IsByRef Then
                            If paramType.IsRestrictedType Then
                                ReportDiagnostic(diagBag, paramSyntax.AsClause.Type, ERRID.ERR_RestrictedType1, paramType)
                            End If
                        ElseIf (modifiers And (SourceMemberFlags.Async Or SourceMemberFlags.Iterator)) <> 0 Then
                            If paramType.IsRestrictedType Then
                                ReportDiagnostic(diagBag, paramSyntax.AsClause.Type, ERRID.ERR_RestrictedResumableType1, paramType)
                            End If
                        End If
                    End If
                End If

                If Not isFromLambda AndAlso Not newParam.Type.IsErrorType() Then
                    AccessCheck.VerifyAccessExposureForParameterType(container, newParam.Name,
                                                                     If(paramSyntax.AsClause IsNot Nothing,
                                                                            paramSyntax.AsClause.Type,
                                                                            DirectCast(paramSyntax, VisualBasicSyntaxNode)),
                                                                     newParam.Type, diagBag)
                End If

                params.Add(newParam)

                If HasDefaultType(paramSyntax.Identifier, paramSyntax.AsClause) Then
                    paramHasImplicitType = True
                Else
                    paramHasExplicitType = True
                End If

                flagsOfPreviousParameters = flagsOfPreviousParameters Or flags
            Next

            ' Special rule: if any parameters have implicit type, all must have implicit type.
            ' Report the error on each parameter with implicit type.
            If paramHasImplicitType AndAlso paramHasExplicitType Then
                For i = 0 To count - 1
                    Dim paramSyntax = syntax(i)
                    If HasDefaultType(paramSyntax.Identifier, paramSyntax.AsClause) Then
                        ReportDiagnostic(diagBag, paramSyntax.Identifier, ERRID.ERR_ParamTypingInconsistency)
                    End If
                Next
            End If
        End Sub

        ' An array consisting of just the NotInheritable keyword.
        Private Shared ReadOnly s_notInheritableKeyword As SyntaxKind() = {SyntaxKind.NotInheritableKeyword}

        ''' <summary>
        ''' Modifier validation code shared between properties and methods.
        ''' </summary>
        Public Function ValidateSharedPropertyAndMethodModifiers(modifierList As SyntaxTokenList,
                                                                 memberModifiers As MemberModifiers,
                                                                 isProperty As Boolean,
                                                                 container As SourceMemberContainerTypeSymbol,
                                                                 diagBag As DiagnosticBag) As MemberModifiers
            Dim flags = memberModifiers.FoundFlags

            If (flags And SourceMemberFlags.Shared) <> 0 AndAlso (flags And SourceMemberFlags.InvalidIfShared) <> 0 Then
                ReportModifierError(modifierList, If(isProperty, ERRID.ERR_BadFlagsOnSharedProperty1, ERRID.ERR_BadFlagsOnSharedMeth1), diagBag, InvalidModifiersIfShared)
                flags = flags And Not SourceMemberFlags.InvalidIfShared
                memberModifiers = New MemberModifiers(flags, memberModifiers.ComputedFlags)
            End If

            Select Case container.TypeKind
                Case TypeKind.Module
                    ' Don't allow overloads in module
                    If (flags And SourceMemberFlags.Overloads) <> 0 Then
                        ReportModifierError(modifierList, ERRID.ERR_OverloadsModifierInModule, diagBag, SyntaxKind.OverloadsKeyword)

                        flags = flags And Not SourceMemberFlags.Overloads
                    End If

                    ' Members in module are implicitly Shared, and cannot be explicitly Shared.
                    If (flags And SourceMemberFlags.InvalidInModule) <> 0 Then
                        ReportModifierError(modifierList, If(isProperty, ERRID.ERR_BadFlagsOnStdModuleProperty1, ERRID.ERR_ModuleCantUseMethodSpecifier1), diagBag, InvalidModifiersInModule)
                        If (flags And SourceMemberFlags.Protected) <> 0 Then
                            flags = flags And Not SourceMemberFlags.Friend ' remove "Friend" if removing "Protected"
                        End If
                        flags = flags And Not SourceMemberFlags.InvalidInModule
                    End If

                    memberModifiers = New MemberModifiers(flags, memberModifiers.ComputedFlags Or SourceMemberFlags.Shared)

                Case TypeKind.Interface
                    If (flags And SourceMemberFlags.InvalidInInterface) <> 0 Then
                        ReportModifierError(modifierList, If(isProperty, ERRID.ERR_BadInterfacePropertyFlags1, ERRID.ERR_BadInterfaceMethodFlags1), diagBag, InvalidModifiersInInterface)
                        flags = flags And Not SourceMemberFlags.InvalidInInterface
                    End If

                    ' Interface members are always public and always implicitly MustOverride.
                    memberModifiers = New MemberModifiers(flags, memberModifiers.ComputedFlags Or SourceMemberFlags.MustOverride)

                Case TypeKind.Structure
                    If (flags And SourceMemberFlags.Protected) <> 0 AndAlso (flags And SourceMemberFlags.Overrides) = 0 Then
                        ReportModifierError(modifierList, If(isProperty, ERRID.ERR_StructCantUseVarSpecifier1, ERRID.ERR_StructureCantUseProtected), diagBag, SyntaxKind.ProtectedKeyword)

                        flags = flags And Not SourceMemberFlags.Protected
                        memberModifiers = New MemberModifiers(flags,
                                          (memberModifiers.ComputedFlags And Not SourceMemberFlags.AccessibilityMask) Or SourceMemberFlags.AccessibilityPrivate)
                    End If
                    If (flags And SourceMemberFlags.InvalidInNotInheritableClass) <> 0 Then
                        ReportModifierError(modifierList, ERRID.ERR_StructCantUseVarSpecifier1, diagBag,
                                            SyntaxKind.OverridableKeyword, SyntaxKind.NotOverridableKeyword, SyntaxKind.MustOverrideKeyword)

                        flags = flags And Not SourceMemberFlags.InvalidInNotInheritableClass
                    End If

            End Select

            ' Don't allow Overridable, etc. on NotInheritable.
            If container.IsNotInheritable Then
                If (flags And SourceMemberFlags.InvalidInNotInheritableClass) <> 0 Then
                    ' Somewhat strangely, the old VB compiler has different behavior depending on whether the containing type DECLARATION
                    ' has NotInheritable, vs. any partial has NotInheritable (although they are semantically the same). If the containing declaration
                    ' does not have NotInheritable, then only MustOverride has an error reported for it, and the error has a different code.

                    Dim containingTypeBLock = GetContainingTypeBlock(modifierList.First())
                    If containingTypeBLock IsNot Nothing AndAlso FindFirstKeyword(containingTypeBLock.BlockStatement.Modifiers, s_notInheritableKeyword).Kind = SyntaxKind.None Then
                        ' Containing type block doesn't have a NotInheritable modifier on it. Must be from other partial declaration.

                        If (flags And SourceMemberFlags.InvalidInNotInheritableOtherPartialClass) <> 0 Then
                            ReportModifierError(modifierList, ERRID.ERR_MustOverOnNotInheritPartClsMem1, diagBag, InvalidModifiersInNotInheritableOtherPartialClass)
                            flags = flags And Not SourceMemberFlags.InvalidInNotInheritableOtherPartialClass
                        End If
                    Else
                        ReportModifierError(modifierList, ERRID.ERR_BadFlagsInNotInheritableClass1, diagBag, InvalidModifiersInNotInheritableClass)
                        flags = flags And Not SourceMemberFlags.InvalidInNotInheritableClass
                    End If

                    memberModifiers = New MemberModifiers(flags, memberModifiers.ComputedFlags)
                End If
            End If

            Return memberModifiers
        End Function

        Public Function ValidateEventModifiers(modifierList As SyntaxTokenList,
                                                memberModifiers As MemberModifiers,
                                                container As SourceMemberContainerTypeSymbol,
                                                diagBag As DiagnosticBag) As MemberModifiers

            Dim flags = memberModifiers.FoundFlags

            Select Case container.TypeKind
                Case TypeKind.Module
                    ' Members in module are implicitly Shared, and cannot be explicitly Shared.
                    If (flags And SourceMemberFlags.InvalidInModule) <> 0 Then
                        ReportModifierError(modifierList, ERRID.ERR_ModuleCantUseEventSpecifier1, diagBag, InvalidModifiersInModule)
                        If (flags And SourceMemberFlags.Protected) <> 0 Then
                            flags = flags And Not SourceMemberFlags.Friend ' remove "Friend" if removing "Protected"
                        End If
                        flags = flags And Not SourceMemberFlags.InvalidInModule
                    End If

                    memberModifiers = New MemberModifiers(flags, memberModifiers.ComputedFlags Or SourceMemberFlags.Shared)

                Case TypeKind.Interface
                    If (flags And SourceMemberFlags.InvalidInInterface) <> 0 Then
                        ReportModifierError(modifierList, ERRID.ERR_InterfaceCantUseEventSpecifier1, diagBag, InvalidModifiersInInterface)
                        flags = flags And Not SourceMemberFlags.InvalidInInterface
                    End If

                    ' Interface members are always public and always implicitly MustOverride.
                    memberModifiers = New MemberModifiers(flags, memberModifiers.ComputedFlags Or SourceMemberFlags.MustOverride)

                Case TypeKind.Structure
                    If (flags And SourceMemberFlags.Protected) <> 0 Then
                        ReportModifierError(modifierList, ERRID.ERR_StructureCantUseProtected, diagBag, SyntaxKind.ProtectedKeyword)

                        flags = flags And Not SourceMemberFlags.Protected
                        memberModifiers = New MemberModifiers(flags,
                                          (memberModifiers.ComputedFlags And Not SourceMemberFlags.AccessibilityMask) Or SourceMemberFlags.AccessibilityPrivate)
                    End If

            End Select

            Return memberModifiers
        End Function

        ' Get the containing type block containing a modifier token on a declared member, or Nothing if no such type block.
        Private Shared Function GetContainingTypeBlock(modifierToken As SyntaxToken) As TypeBlockSyntax
            Dim containingSyntax = modifierToken.Parent
            If TypeOf containingSyntax.Parent Is MethodBlockBaseSyntax OrElse TypeOf containingSyntax.Parent Is PropertyBlockSyntax Then
                containingSyntax = containingSyntax.Parent
            End If
            Return TryCast(containingSyntax.Parent, TypeBlockSyntax)
        End Function

        Public Enum ConstantContext
            [Default]
            ParameterDefaultValue
        End Enum

        ''' <summary>
        ''' This function checks if the given expression is a constant from a language point of view and returns 
        ''' constant value if it is. This is different from the fact that the bound node has a constant value. 
        ''' This method also adds the required diagnostics for non const values.
        ''' </summary>
        ''' <param name="boundExpression">The bound expression.</param>
        ''' <param name="diagnostics">The diagnostics.</param>
        ''' <returns>ConstantValue if the bound expression is compile time constant and can be used 
        ''' for const field/local initializations or enum member initializations. Nothing if not</returns>
        Public Function GetExpressionConstantValueIfAny(boundExpression As BoundExpression, diagnostics As BindingDiagnosticBag, context As ConstantContext) As ConstantValue
            Dim nonConstantDetected As Boolean = False
            Do
                If boundExpression.Kind = BoundKind.Local Then
                    Dim local = DirectCast(boundExpression, BoundLocal).LocalSymbol
                    If Not local.IsConst Then
                        ReportDiagnostic(diagnostics, boundExpression.Syntax, ERRID.ERR_RequiredConstExpr)
                        Return Nothing
                    End If

                    Return If(nonConstantDetected, Nothing, local.GetConstantValue(Me))
                End If

                ' Check that the expression is constant.
                If boundExpression.ConstantValueOpt IsNot Nothing Then
                    ' if no non constant node was found this if node is constant.
                    Return If(nonConstantDetected, Nothing, boundExpression.ConstantValueOpt)

                Else
                    Select Case boundExpression.Kind
                        Case BoundKind.DirectCast
                            Dim conv = DirectCast(boundExpression, BoundDirectCast)
                            Dim result = CheckConversionForConstantExpression(conv, conv.Operand, diagnostics, context)
                            Return If(nonConstantDetected, Nothing, result)

                        Case BoundKind.TryCast
                            Dim conv = DirectCast(boundExpression, BoundTryCast)
                            Dim result = CheckConversionForConstantExpression(conv, conv.Operand, diagnostics, context)
                            Return If(nonConstantDetected, Nothing, result)

                        Case BoundKind.Conversion
                            Dim conv = DirectCast(boundExpression, BoundConversion)
                            Dim result = CheckConversionForConstantExpression(conv, conv.Operand, diagnostics, context)
                            Return If(nonConstantDetected, Nothing, result)

                        Case BoundKind.BinaryOperator
                            ' If we ever got to a binary operator it means there is no constant value,
                            ' but we want to keep the analysis going to properly report errors

                            Dim binaryOperator = DirectCast(boundExpression, BoundBinaryOperator)
                            ' the right side is expected to be shorter for binary operations, so we use
                            ' recursion for this side.
                            GetExpressionConstantValueIfAny(binaryOperator.Right, diagnostics, context)
                            nonConstantDetected = True
                            boundExpression = binaryOperator.Left

                        Case BoundKind.UnaryOperator
                            boundExpression = DirectCast(boundExpression, BoundUnaryOperator).Operand

                        Case BoundKind.Parenthesized
                            boundExpression = DirectCast(boundExpression, BoundParenthesized).Expression

                        Case BoundKind.BadExpression
                            Return Nothing

                        Case Else
                            ReportDiagnostic(diagnostics, boundExpression.Syntax, ERRID.ERR_RequiredConstExpr)
                            Return Nothing
                    End Select
                End If
            Loop

            Return Nothing
        End Function

        Private Shared Function IsNothingLiteralAllowedForAType(type As TypeSymbol) As Boolean
            If type.IsReferenceType Then
                Return True
            End If

            If type.IsEnumType Then
                Return True
            End If

            Select Case type.SpecialType
                Case SpecialType.System_Boolean,
                     SpecialType.System_Byte,
                     SpecialType.System_SByte,
                     SpecialType.System_Int16,
                     SpecialType.System_UInt16,
                     SpecialType.System_Int32,
                     SpecialType.System_UInt32,
                     SpecialType.System_Int64,
                     SpecialType.System_UInt64,
                     SpecialType.System_Single,
                     SpecialType.System_Double,
                     SpecialType.System_Char
                    Return True
            End Select
            Return False
        End Function

        Private Function CheckConversionForConstantExpression(conv As BoundExpression, operand As BoundExpression, diagnostics As BindingDiagnosticBag, context As ConstantContext) As ConstantValue
            If conv.HasErrors Then
                Return Nothing
            End If

            Dim conversionType As TypeSymbol = conv.Type
            Dim operandType As TypeSymbol = operand.Type

            ' First, special handling for the case when operand is a Nothing Literal
            If operand.IsNothingLiteral Then
                If context = ConstantContext.Default Then
                    ' In default context, Nothing literal can only be converted to either a reference type 
                    ' or an intrinsic value type allowing constant values, like int32, etc...
                    If Not IsNothingLiteralAllowedForAType(conversionType) Then
                        ReportDiagnostic(diagnostics,
                                         operand.Syntax,
                                         ERRID.ERR_RequiredConstConversion2,
                                         If(operandType, GetSpecialType(SpecialType.System_Object, operand.Syntax, diagnostics)),
                                         conversionType)
                        Return Nothing
                    End If

                Else
                    ' In ParameterDefaultValue constant context 'Nothing' literal can be 
                    ' converted to any type meaning the type's default value; 
                    Debug.Assert(context = ConstantContext.ParameterDefaultValue)
                End If

                Return operand.ConstantValueOpt
            End If

            ' Guess the constant value of the operand
            Dim nestedConstValue As ConstantValue = GetExpressionConstantValueIfAny(operand, diagnostics, context)
            If nestedConstValue Is Nothing Then
                ' Error should already be generated
                Return Nothing
            End If

            Debug.Assert(conversionType IsNot Nothing)
            Debug.Assert(operandType IsNot Nothing)

            If conversionType.IsObjectType Then
                ' Nothing constants of reference type can always be converted to System.Object
                If operandType.IsReferenceType AndAlso nestedConstValue.IsNothing Then
                    Return nestedConstValue
                End If

                ' In ParameterDefaultValue context we also allow constant of types DateTime, Decimal 
                ' and others mentioned as supported by IsNothingLiteralAllowedForAType() to be converted 
                ' to System.Object type; these constants will be emitted as constant values for appropriate 
                ' parameters and when used converted to target type if needed
                If context = ConstantContext.ParameterDefaultValue AndAlso
                        (IsNothingLiteralAllowedForAType(operandType) OrElse operandType.IsDateTimeType OrElse operandType.IsDecimalType) Then
                    Return nestedConstValue
                End If

                ReportDiagnostic(diagnostics,
                                 operand.Syntax,
                                 ERRID.ERR_RequiredConstConversion2,
                                 operandType,
                                 conversionType)
                Return Nothing
            End If

            If Not nestedConstValue.IsNothing Then
                ' Conversion of a non-nothing constant value to anything but System.Object is bad
                ' Note that all valid intrinsic conversions should have been folded and never reach this point

                ' In ParameterDefaultValue we also allow conversion of T --> S?
                If context = ConstantContext.ParameterDefaultValue AndAlso conversionType.IsNullableType Then
                    If IsSameTypeIgnoringAll(conversionType.GetNullableUnderlyingType, operandType) Then
                        ' A trivial case: T --> T?
                        Return nestedConstValue
                    Else
                        ' Let's convert to the underlying type of the Nullable.
                        ' All diagnostics about this conversion have already been reported, so we can treat it as an explicit conversion and ignore any errors/warnings.
                        Dim conversionToUnderlying As BoundExpression = ApplyConversion(operand.Syntax, conversionType.GetNullableUnderlyingType(), operand, isExplicit:=True, diagnostics:=BindingDiagnosticBag.Discarded)

                        nestedConstValue = conversionToUnderlying.ConstantValueOpt
                        If nestedConstValue IsNot Nothing Then
                            Return nestedConstValue
                        End If
                    End If
                End If

                ReportDiagnostic(diagnostics,
                                 operand.Syntax,
                                 ERRID.ERR_RequiredConstConversion2,
                                 operandType,
                                 conversionType)
                Return Nothing
            End If

            ' We have a 'Nothing' constant value which was converted to some 
            ' type by nested conversion(s) and now has type of the argument

            ' No actual conversion is done
            If IsSameTypeIgnoringAll(operandType, conversionType) Then
                Return nestedConstValue
            End If

            ' It is OK to convert Nothing literal of one reference type to another 
            ' reference type or (only in default parameter value context) Nothing 
            ' literal of reference type to a value type,
            ' if the conversion is not correct, errors should have been reported by now
            If operandType.IsReferenceType AndAlso
                    (conversionType.IsReferenceType OrElse context = ConstantContext.ParameterDefaultValue) Then
                Return nestedConstValue
            End If

            ' In ParameterDefaultValue we also allow conversion of 'Nothing' constants to nullable 
            If context = ConstantContext.ParameterDefaultValue Then
                If conversionType.IsNullableType Then
                    Return nestedConstValue
                End If
            End If

            ReportDiagnostic(diagnostics,
                             operand.Syntax,
                             ERRID.ERR_RequiredConstConversion2,
                             operandType,
                             conversionType)
            Return Nothing
        End Function

        ''' <summary>isWinMd says whether to mangle the name for winmdobj output. See the param tag for details.</summary>
        ''' <param name="isWinMd">isWinMd is only necessary for set properties, so any MethodKind which is definitely not
        ''' a set property can safely set this value to False.</param>
        Friend Shared Function GetAccessorName(name As String, kind As MethodKind, isWinMd As Boolean) As String
            Dim prefix As String
            Select Case kind
                Case MethodKind.PropertyGet
                    prefix = StringConstants.PropertyGetPrefix
                Case MethodKind.PropertySet
                    If isWinMd Then
                        prefix = StringConstants.WinMdPropertySetPrefix
                    Else
                        prefix = StringConstants.PropertySetPrefix
                    End If
                Case MethodKind.EventAdd
                    prefix = StringConstants.EventAddPrefix
                Case MethodKind.EventRemove
                    prefix = StringConstants.EventRemovePrefix
                Case MethodKind.EventRaise
                    prefix = StringConstants.EventRaisePrefix
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(kind)
            End Select

            Return prefix & name
        End Function
    End Class

    ''' <summary>
    ''' Holds information about a member in a compact form. Used for all non-type members for simplicity
    ''' </summary>
    ''' <remarks></remarks>
    <Flags()>
    Friend Enum SourceMemberFlags
        None = 0
        ' These are the actual accessibility level
        ' Bits 0,1,2
        AccessibilityPrivate = CUShort(Accessibility.Private)
        AccessibilityProtected = CUShort(Accessibility.Protected)
        AccessibilityFriend = CUShort(Accessibility.Friend)
        AccessibilityProtectedFriend = CUShort(Accessibility.ProtectedOrFriend)
        AccessibilityPrivateProtected = CUShort(Accessibility.ProtectedAndFriend)
        AccessibilityPublic = CUShort(Accessibility.Public)
        AccessibilityMask = &H7

        ' DeclarationModifierFlags
        ' Bits 3,4,5,...,27

        [Private] = CUInt(DeclarationModifiers.Private) << DeclarationModifierFlagShift
        [Protected] = CUInt(DeclarationModifiers.Protected) << DeclarationModifierFlagShift
        [Friend] = CUInt(DeclarationModifiers.Friend) << DeclarationModifierFlagShift
        [Public] = CUInt(DeclarationModifiers.Public) << DeclarationModifierFlagShift
        AllAccessibilityModifiers = [Private] Or [Friend] Or [Protected] Or [Public]

        [Shared] = CUInt(DeclarationModifiers.Shared) << DeclarationModifierFlagShift

        [ReadOnly] = CUInt(DeclarationModifiers.ReadOnly) << DeclarationModifierFlagShift
        [WriteOnly] = CUInt(DeclarationModifiers.WriteOnly) << DeclarationModifierFlagShift
        AllWriteabilityModifiers = [ReadOnly] Or [WriteOnly]

        [Overrides] = CUInt(DeclarationModifiers.Overrides) << DeclarationModifierFlagShift

        [Overridable] = CUInt(DeclarationModifiers.Overridable) << DeclarationModifierFlagShift
        [MustOverride] = CUInt(DeclarationModifiers.MustOverride) << DeclarationModifierFlagShift
        [NotOverridable] = CUInt(DeclarationModifiers.NotOverridable) << DeclarationModifierFlagShift
        AllOverrideModifiers = [Overridable] Or [MustOverride] Or [NotOverridable]

        PrivateOverridableModifiers = [Overridable] Or [Private]
        PrivateMustOverrideModifiers = [MustOverride] Or [Private]
        PrivateNotOverridableModifiers = [NotOverridable] Or [Private]

        [Overloads] = CUInt(DeclarationModifiers.Overloads) << DeclarationModifierFlagShift
        [Shadows] = CUInt(DeclarationModifiers.Shadows) << DeclarationModifierFlagShift
        AllShadowingModifiers = [Overloads] Or [Shadows]

        ShadowsAndOverrides = [Overrides] Or [Shadows]

        [Default] = CUInt(DeclarationModifiers.Default) << DeclarationModifierFlagShift
        [WithEvents] = CUInt(DeclarationModifiers.WithEvents) << DeclarationModifierFlagShift

        [Widening] = CUInt(DeclarationModifiers.Widening) << DeclarationModifierFlagShift
        [Narrowing] = CUInt(DeclarationModifiers.Narrowing) << DeclarationModifierFlagShift
        AllConversionModifiers = [Widening] Or [Narrowing]

        ' These should be kept in sync with the corresponding arrays in InvalidModifiers below.
        InvalidInNotInheritableClass = [Overridable] Or [NotOverridable] Or [MustOverride]
        InvalidInNotInheritableOtherPartialClass = [MustOverride] ' invalid if containing declaration doesn't have NotInheritable, but other partial does.
        InvalidInModule = [Protected] Or [Shared] Or [Default] Or [MustOverride] Or [Overridable] Or [Shadows] Or [Overrides] Or [NotOverridable]
        InvalidInInterface = AllAccessibilityModifiers Or [Shared] Or [Overrides] Or AllOverrideModifiers Or [Dim] Or [Const] Or
                             [Static] Or [WithEvents] Or AllConversionModifiers Or [Partial] Or [MustInherit] Or [NotInheritable] Or
                             Async Or Iterator
        InvalidIfShared = [Overrides] Or AllOverrideModifiers Or [Default]
        InvalidIfDefault = [Private]

        [Partial] = CUInt(DeclarationModifiers.Partial) << DeclarationModifierFlagShift
        [MustInherit] = CUInt(DeclarationModifiers.MustInherit) << DeclarationModifierFlagShift
        [NotInheritable] = CUInt(DeclarationModifiers.NotInheritable) << DeclarationModifierFlagShift
        TypeInheritModifiers = [MustInherit] Or [NotInheritable]

        Async = CUInt(DeclarationModifiers.Async) << DeclarationModifierFlagShift
        Iterator = CUInt(DeclarationModifiers.Iterator) << DeclarationModifierFlagShift

        [Dim] = CUInt(DeclarationModifiers.Dim) << DeclarationModifierFlagShift
        [Const] = CUInt(DeclarationModifiers.Const) << DeclarationModifierFlagShift
        [Static] = CUInt(DeclarationModifiers.Static) << DeclarationModifierFlagShift

        DeclarationModifierFlagMask = &HFFFFFF8
        DeclarationModifierFlagShift = 3

        ' Bits 25 and above are used for different things depending on the member type.
        ' 25 - [Dim]
        ' 26 - [Const]
        ' 27 - [Static]

        ' Fields only: Indicates that this const field has an inferred type.
        InferredFieldType = 1 << 30

        ' Fields and properties only: Indicates that this is the first field (or property representing a WithEvents field) of a particular type.
        ' In "Dim x, y, z As Integer, w As String, a, b as Decimal", set for "x", "w", and "a"
        FirstFieldDeclarationOfType = 1 << 31

        ' Source Methods only: Indicates this method has a void return type / is a Sub.
        MethodIsSub = 1 << 25

        ' Source methods only: Indicates that method syntax has "Handles"
        MethodHandlesEvents = 1 << 26

        ' Method symbols only: Bits 27,28,29,30,31
        MethodKindOrdinary = CUInt(MethodKind.Ordinary) << MethodKindShift
        MethodKindConstructor = CUInt(MethodKind.Constructor) << MethodKindShift
        MethodKindSharedConstructor = CUInt(MethodKind.SharedConstructor) << MethodKindShift
        MethodKindDelegateInvoke = CUInt(MethodKind.DelegateInvoke) << MethodKindShift
        MethodKindOperator = CUInt(MethodKind.UserDefinedOperator) << MethodKindShift
        MethodKindConversion = CUInt(MethodKind.Conversion) << MethodKindShift
        MethodKindPropertyGet = CUInt(MethodKind.PropertyGet) << MethodKindShift
        MethodKindPropertySet = CUInt(MethodKind.PropertySet) << MethodKindShift
        MethodKindEventAdd = CUInt(MethodKind.EventAdd) << MethodKindShift
        MethodKindEventRemove = CUInt(MethodKind.EventRemove) << MethodKindShift
        MethodKindEventRaise = CUInt(MethodKind.EventRaise) << MethodKindShift
        MethodKindDeclare = CUInt(MethodKind.DeclareMethod) << MethodKindShift

        MethodKindMask = &H1F
        MethodKindShift = 27
    End Enum

    Friend Module SourceMemberFlagsExtensions
        <Extension>
        Friend Function ToMethodKind(flags As SourceMemberFlags) As MethodKind
            Return CType((flags >> SourceMemberFlags.MethodKindShift) And SourceMemberFlags.MethodKindMask, MethodKind)
        End Function
    End Module

    ' These should be kept in sync with the corresponding bit fields in SourceMemberFlags above.
    Friend Module InvalidModifiers

        Public InvalidModifiersInNotInheritableClass() As SyntaxKind =
            {
                SyntaxKind.OverridableKeyword,
                SyntaxKind.NotOverridableKeyword,
                SyntaxKind.MustOverrideKeyword
            }

        Public InvalidModifiersInNotInheritableOtherPartialClass() As SyntaxKind =
            {
                SyntaxKind.MustOverrideKeyword
            }

        Public InvalidModifiersInModule() As SyntaxKind =
            {
                SyntaxKind.SharedKeyword,
                SyntaxKind.ProtectedKeyword,
                SyntaxKind.DefaultKeyword,
                SyntaxKind.MustOverrideKeyword,
                SyntaxKind.OverridableKeyword,
                SyntaxKind.ShadowsKeyword,
                SyntaxKind.OverridesKeyword,
                SyntaxKind.NotOverridableKeyword
            }

        Public InvalidModifiersInInterface() As SyntaxKind =
            {
                SyntaxKind.PublicKeyword,
                SyntaxKind.PrivateKeyword,
                SyntaxKind.ProtectedKeyword,
                SyntaxKind.FriendKeyword,
                SyntaxKind.StaticKeyword,
                SyntaxKind.SharedKeyword,
                SyntaxKind.MustInheritKeyword,
                SyntaxKind.NotInheritableKeyword,
                SyntaxKind.OverridesKeyword,
                SyntaxKind.PartialKeyword,
                SyntaxKind.NotOverridableKeyword,
                SyntaxKind.OverridableKeyword,
                SyntaxKind.MustOverrideKeyword,
                SyntaxKind.DimKeyword,
                SyntaxKind.ConstKeyword,
                SyntaxKind.WithEventsKeyword,
                SyntaxKind.WideningKeyword,
                SyntaxKind.NarrowingKeyword,
                SyntaxKind.CustomKeyword,
                SyntaxKind.AsyncKeyword,
                SyntaxKind.IteratorKeyword
            }

        Public InvalidModifiersIfShared() As SyntaxKind =
            {
                SyntaxKind.OverridesKeyword,
                SyntaxKind.OverridableKeyword,
                SyntaxKind.MustOverrideKeyword,
                SyntaxKind.NotOverridableKeyword,
                SyntaxKind.DefaultKeyword
            }

        Public InvalidModifiersIfDefault() As SyntaxKind =
            {
                SyntaxKind.PrivateKeyword
            }

        Public InvalidAsyncIterator() As SyntaxKind =
            {
                SyntaxKind.AsyncKeyword,
                SyntaxKind.IteratorKeyword
            }
    End Module

    Friend Structure MemberModifiers
        ''' <summary>
        ''' These are the flags that are found in the syntax.  They must correspond to the modifiers list.
        ''' </summary>
        ''' <remarks></remarks>
        Private ReadOnly _foundFlags As SourceMemberFlags
        ''' <summary>
        ''' These are flags that are implied or computed
        ''' </summary>
        ''' <remarks></remarks>
        Private ReadOnly _computedFlags As SourceMemberFlags

        Public Sub New(foundFlags As SourceMemberFlags, computedFlags As SourceMemberFlags)
            _foundFlags = foundFlags
            _computedFlags = computedFlags
        End Sub

        Public ReadOnly Property FoundFlags As SourceMemberFlags
            Get
                Return _foundFlags
            End Get
        End Property

        Public ReadOnly Property ComputedFlags As SourceMemberFlags
            Get
                Return _computedFlags
            End Get
        End Property

        Public ReadOnly Property AllFlags As SourceMemberFlags
            Get
                Return _foundFlags Or _computedFlags
            End Get
        End Property

    End Structure
End Namespace
