File: Microsoft\VisualBasic\CompilerServices\OverloadResolution.vb
Web Access
Project: src\src\libraries\Microsoft.VisualBasic.Core\src\Microsoft.VisualBasic.Core.vbproj (Microsoft.VisualBasic.Core)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
 
Option Strict On
 
Imports System
Imports System.Reflection
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Text
 
Imports Microsoft.VisualBasic.CompilerServices.Symbols
Imports Microsoft.VisualBasic.CompilerServices.ConversionResolution
Imports Microsoft.VisualBasic.CompilerServices.Utils
Imports Microsoft.VisualBasic.CompilerServices.ReflectionExtensions
Imports System.Diagnostics.CodeAnalysis
 
#Const BINDING_LOG = False
#Const GENERICITY_LOG = False
 
Namespace Microsoft.VisualBasic.CompilerServices
 
    ' Implements VB method overloading semantics.
    Friend NotInheritable Class OverloadResolution
        ' Prevent creation.
        Private Sub New()
        End Sub
 
        Friend Enum ResolutionFailure
            None
            MissingMember
            InvalidArgument
            AmbiguousMatch
            InvalidTarget
        End Enum
 
 
        'perhaps this could go into the Symbols utility module
        Private Shared Function IsExactSignatureMatch(
            ByVal leftSignature As ParameterInfo(),
            ByVal leftTypeParameterCount As Integer,
            ByVal rightSignature As ParameterInfo(),
            ByVal rightTypeParameterCount As Integer) As Boolean
 
            Dim longerSignature As ParameterInfo()
            Dim shorterSignature As ParameterInfo()
 
            If leftSignature.Length >= rightSignature.Length Then
                longerSignature = leftSignature
                shorterSignature = rightSignature
            Else
                longerSignature = rightSignature
                shorterSignature = leftSignature
            End If
 
            'If the signatures differ in length, then the extra parameters of the
            'longer signature must all be optional to be an exact match.
 
            For index As Integer = shorterSignature.Length To longerSignature.Length - 1
                If Not longerSignature(index).IsOptional Then
                    Return False
                End If
            Next
 
            For i As Integer = 0 To shorterSignature.Length - 1
 
                Dim type1 As Type = shorterSignature(i).ParameterType
                Dim type2 As Type = longerSignature(i).ParameterType
 
                If type1.IsByRef Then type1 = type1.GetElementType
                If type2.IsByRef Then type2 = type2.GetElementType
 
                If type1 IsNot type2 AndAlso
                   (Not shorterSignature(i).IsOptional OrElse
                    Not longerSignature(i).IsOptional) Then
                    Return False
                End If
            Next
 
            Return True
        End Function
 
        Private Enum ComparisonType
            ParameterSpecificty
            GenericSpecificityBasedOnMethodGenericParams
            GenericSpecificityBasedOnTypeGenericParams
        End Enum
 
        Private Shared Sub CompareNumericTypeSpecificity(
            ByVal leftType As Type,
            ByVal rightType As Type,
            ByRef leftWins As Boolean,
            ByRef rightWins As Boolean)
 
            'This function implements the notion that signed types are
            'preferred over unsigned types during overload resolution.
 
            Debug.Assert(IsNumericType(leftType) AndAlso Not IsEnum(leftType) AndAlso
                         IsNumericType(rightType) AndAlso Not IsEnum(rightType),
                         "expected only numerics here. : #12/10/2003#")
 
            If leftType Is rightType Then
                'Do nothing since neither wins.
            Else
                Debug.Assert(GetTypeCode(leftType) <> GetTypeCode(rightType),
                             "this should have been caught above")
 
                If NumericSpecificityRank(GetTypeCode(leftType)) <
                        NumericSpecificityRank(GetTypeCode(rightType)) Then
                    leftWins = True
                Else
                    rightWins = True
                End If
            End If
 
            Return
        End Sub
 
        <RequiresUnreferencedCode("ClassifyConversion")>
        Private Shared Sub CompareParameterSpecificity(
            ByVal argumentType As Type,
            ByVal leftParameter As ParameterInfo,
            ByVal leftProcedure As MethodBase,
            ByVal expandLeftParamArray As Boolean,
            ByVal rightParameter As ParameterInfo,
            ByVal rightProcedure As MethodBase,
            ByVal expandRightParamArray As Boolean,
            ByRef leftWins As Boolean,
            ByRef rightWins As Boolean,
            ByRef bothLose As Boolean)
 
 
            bothLose = False
            Dim leftType As Type = leftParameter.ParameterType
            Dim rightType As Type = rightParameter.ParameterType
 
            If leftType.IsByRef Then leftType = GetElementType(leftType)
            If rightType.IsByRef Then rightType = GetElementType(rightType)
 
            If expandLeftParamArray AndAlso IsParamArray(leftParameter) Then
                leftType = GetElementType(leftType)
            End If
 
            If expandRightParamArray AndAlso IsParamArray(rightParameter) Then
                rightType = GetElementType(rightType)
            End If
 
            If IsNumericType(leftType) AndAlso IsNumericType(rightType) AndAlso
               Not IsEnum(leftType) AndAlso Not IsEnum(rightType) Then
 
                CompareNumericTypeSpecificity(leftType, rightType, leftWins, rightWins)
                Return
            End If
 
            'If both the types are different only by generic method type parameters
            'with the same index position, then treat as identity.
 
            If leftProcedure IsNot Nothing AndAlso
               rightProcedure IsNot Nothing AndAlso
               IsRawGeneric(leftProcedure) AndAlso
               IsRawGeneric(rightProcedure) Then
 
                If leftType Is rightType Then Return 'Check this first--shortcut.
 
                Dim leftIndex As Integer = IndexIn(leftType, leftProcedure)
                Dim rightIndex As Integer = IndexIn(rightType, rightProcedure)
 
                If leftIndex = rightIndex AndAlso leftIndex >= 0 Then Return
            End If
 
            Dim operatorMethod As Method = Nothing
            Dim leftToRight As ConversionClass = ClassifyConversion(rightType, leftType, operatorMethod)
 
            If leftToRight = ConversionClass.Identity Then Return
 
            If leftToRight = ConversionClass.Widening Then
 
                If operatorMethod IsNot Nothing AndAlso
                   ClassifyConversion(leftType, rightType, operatorMethod) = ConversionClass.Widening Then
 
                    ' Although W<-->W conversions don't exist in the set of predefined conversions,
                    ' it can occur with user-defined conversions.  If the two param types widen to each other
                    ' (necessarily by using user-defined conversions), and the argument type is known and
                    ' is identical to one of the parameter types, then that parameter wins.  Otherwise,
                    ' if the argument type is not specified, we can't make a decision and both lose.
 
                    If argumentType IsNot Nothing AndAlso argumentType Is leftType Then
                        leftWins = True
                        Return
                    End If
 
                    If argumentType IsNot Nothing AndAlso argumentType Is rightType Then
                        rightWins = True
                        Return
                    End If
 
                    bothLose = True
                    Return
 
                End If
 
                leftWins = True
                Return
            End If
 
            Dim rightToLeft As ConversionClass = ClassifyConversion(leftType, rightType, operatorMethod)
 
            If rightToLeft = ConversionClass.Widening Then
                rightWins = True
                Return
            End If
 
            bothLose = True
            Return
 
        End Sub
 
        Private Shared Sub CompareGenericityBasedOnMethodGenericParams(
            ByVal leftParameter As ParameterInfo,
            ByVal rawLeftParameter As ParameterInfo,
            ByVal leftMember As Method,
            ByVal expandLeftParamArray As Boolean,
            ByVal rightParameter As ParameterInfo,
            ByVal rawRightParameter As ParameterInfo,
            ByVal rightMember As Method,
            ByVal expandRightParamArray As Boolean,
            ByRef leftIsLessGeneric As Boolean,
            ByRef rightIsLessGeneric As Boolean,
            ByRef signatureMismatch As Boolean)
 
            If Not leftMember.IsMethod OrElse Not rightMember.IsMethod Then
                Return
            End If
 
            Dim leftType As Type = leftParameter.ParameterType
            Dim rightType As Type = rightParameter.ParameterType
 
            'Since generic methods are instantiated by this point, the parameter
            'types are bound. However, we need to compare against the unbound types.
            Dim rawLeftType As Type = rawLeftParameter.ParameterType
            Dim rawRightType As Type = rawRightParameter.ParameterType
 
            If leftType.IsByRef Then
                leftType = GetElementType(leftType)
                rawLeftType = GetElementType(rawLeftType)
            End If
 
            If rightType.IsByRef Then
                rightType = GetElementType(rightType)
                rawRightType = GetElementType(rawRightType)
            End If
 
            If expandLeftParamArray AndAlso IsParamArray(leftParameter) Then
                leftType = GetElementType(leftType)
                rawLeftType = GetElementType(rawLeftType)
            End If
 
            If expandRightParamArray AndAlso IsParamArray(rightParameter) Then
                rightType = GetElementType(rightType)
                rawRightType = GetElementType(rawRightType)
            End If
            If leftType IsNot rightType Then
                'The signatures of the two methods are not identical and so the "least generic" rule
                'does not apply.
                signatureMismatch = True
                Return
            End If
 
            Dim leftProcedure As MethodBase = leftMember.AsMethod
            Dim rightProcedure As MethodBase = rightMember.AsMethod
 
            If IsGeneric(leftProcedure) Then leftProcedure = DirectCast(leftProcedure, MethodInfo).GetGenericMethodDefinition
            If IsGeneric(rightProcedure) Then rightProcedure = DirectCast(rightProcedure, MethodInfo).GetGenericMethodDefinition
 
            ' Only references to generic parameters of the procedures count. For the purpose of this
            ' function, references to generic parameters of a type do not make a procedure more generic.
 
            If RefersToGenericParameter(rawLeftType, leftProcedure) Then
                If Not RefersToGenericParameter(rawRightType, rightProcedure) Then
                    rightIsLessGeneric = True
                End If
            ElseIf RefersToGenericParameter(rawRightType, rightProcedure) Then
                If Not RefersToGenericParameter(rawLeftType, leftProcedure) Then
                    leftIsLessGeneric = True
                End If
            End If
 
        End Sub
 
        Private Shared Sub CompareGenericityBasedOnTypeGenericParams(
            ByVal leftParameter As ParameterInfo,
            ByVal rawLeftParameter As ParameterInfo,
            ByVal leftMember As Method,
            ByVal expandLeftParamArray As Boolean,
            ByVal rightParameter As ParameterInfo,
            ByVal rawRightParameter As ParameterInfo,
            ByVal rightMember As Method,
            ByVal expandRightParamArray As Boolean,
            ByRef leftIsLessGeneric As Boolean,
            ByRef rightIsLessGeneric As Boolean,
            ByRef signatureMismatch As Boolean)
 
            Dim leftType As Type = leftParameter.ParameterType
            Dim rightType As Type = rightParameter.ParameterType
 
            'Since generic methods are instantiated by this point, the parameter
            'types are bound. However, we need to compare against the unbound types.
            Dim rawLeftType As Type = rawLeftParameter.ParameterType
            Dim rawRightType As Type = rawRightParameter.ParameterType
 
            If leftType.IsByRef Then
                leftType = GetElementType(leftType)
                rawLeftType = GetElementType(rawLeftType)
            End If
 
            If rightType.IsByRef Then
                rightType = GetElementType(rightType)
                rawRightType = GetElementType(rawRightType)
            End If
 
            If expandLeftParamArray AndAlso IsParamArray(leftParameter) Then
                leftType = GetElementType(leftType)
                rawLeftType = GetElementType(rawLeftType)
            End If
 
            If expandRightParamArray AndAlso IsParamArray(rightParameter) Then
                rightType = GetElementType(rightType)
                rawRightType = GetElementType(rawRightType)
            End If
 
            'Need to check type equivalency for the NoPIA case
            If leftType IsNot rightType Then
                'The signatures of the two methods are not identical and so the "least generic" rule
                'does not apply.
                signatureMismatch = True
                Return
            End If
 
            ' Only references to generic parameters of the generic types count. For the purpose of this
            ' function, references to generic parameters of a method do not make a procedure more generic.
            '
            Dim leftDeclaringType As Type = leftMember.RawDeclaringType
            Dim rightDeclaringType As Type = rightMember.RawDeclaringType
 
#If GENERICITY_LOG Then
            Console.Writeline("----------CompareGenericityBasedOnTypeGenericParams---------")
            Console.Writeline("LeftType: " & LeftType.MetaDataToken & " - " & LeftType.ToString())
            Console.Writeline("LeftRawType: " & RawLeftType.MetaDataToken & " - " & RawLeftType.ToString())
            Console.Writeline("LeftDeclaringType: " & LeftDeclaringType.MetaDataToken & " - " & LeftDeclaringType.ToString())
            Console.Writeline("RightType: " & RightType.MetaDataToken & " - " & RightType.ToString())
            Console.Writeline("RightRawType: " & RawRightType.MetaDataToken & " - " & RawRightType.ToString())
            Console.Writeline("RightDeclaringType: " & RightDeclaringType.MetaDataToken & " - " & RightDeclaringType.ToString())
#End If
 
            If RefersToGenericParameterCLRSemantics(rawLeftType, leftDeclaringType) Then
                If Not RefersToGenericParameterCLRSemantics(rawRightType, rightDeclaringType) Then
                    rightIsLessGeneric = True
                End If
            ElseIf RefersToGenericParameterCLRSemantics(rawRightType, rightDeclaringType) Then
                leftIsLessGeneric = True
            End If
        End Sub
 
        Private Shared Function LeastGenericProcedure(
            ByVal left As Method,
            ByVal right As Method,
            ByVal compareGenericity As ComparisonType,
            ByRef signatureMismatch As Boolean) As Method
 
            Dim leftWinsAtLeastOnce As Boolean = False
            Dim rightWinsAtLeastOnce As Boolean = False
            signatureMismatch = False
 
            If Not left.IsMethod OrElse Not right.IsMethod Then Return Nothing
 
            Dim paramIndex As Integer = 0
            Dim leftParamsLen As Integer = left.Parameters.Length
            Dim rightParamsLen As Integer = right.Parameters.Length
 
            Do While paramIndex < leftParamsLen AndAlso paramIndex < rightParamsLen
 
                Select Case compareGenericity
                    Case ComparisonType.GenericSpecificityBasedOnMethodGenericParams
 
                        CompareGenericityBasedOnMethodGenericParams(
                            left.Parameters(paramIndex),
                            left.RawParameters(paramIndex),
                            left,
                            left.ParamArrayExpanded,
                            right.Parameters(paramIndex),
                            right.RawParameters(paramIndex),
                            right,
                            False,
                            leftWinsAtLeastOnce,
                            rightWinsAtLeastOnce,
                            signatureMismatch)
 
                    Case ComparisonType.GenericSpecificityBasedOnTypeGenericParams
 
                        CompareGenericityBasedOnTypeGenericParams(
                            left.Parameters(paramIndex),
                            left.RawParameters(paramIndex),
                            left,
                            left.ParamArrayExpanded,
                            right.Parameters(paramIndex),
                            right.RawParameters(paramIndex),
                            right,
                            False,
                            leftWinsAtLeastOnce,
                            rightWinsAtLeastOnce,
                            signatureMismatch)
 
                    Case Else
                        Debug.Assert(False, "Unexpected comparison type!!!")
                End Select
 
                If signatureMismatch OrElse (leftWinsAtLeastOnce AndAlso rightWinsAtLeastOnce) Then
                    Return Nothing
                End If
 
                paramIndex += 1
            Loop
 
            Debug.Assert(Not (leftWinsAtLeastOnce AndAlso rightWinsAtLeastOnce),
                         "Least generic method logic is confused.")
 
            If paramIndex < leftParamsLen OrElse paramIndex < rightParamsLen Then
                'The procedures have different numbers of parameters, and so don't have matching signatures.
                Return Nothing
            End If
 
            If leftWinsAtLeastOnce Then
                Return left
            End If
 
            If rightWinsAtLeastOnce Then
                Return right
            End If
 
            Return Nothing
        End Function
 
        Friend Shared Function LeastGenericProcedure(
            ByVal left As Method,
            ByVal right As Method) As Method
 
            If Not (left.IsGeneric OrElse
                    right.IsGeneric OrElse
                    IsGeneric(left.DeclaringType) OrElse
                    IsGeneric(right.DeclaringType)) Then
                Return Nothing
            End If
 
            Dim signatureMismatch As Boolean = False
 
            Dim leastGeneric As Method =
                LeastGenericProcedure(
                    left,
                    right,
                    ComparisonType.GenericSpecificityBasedOnMethodGenericParams,
                    signatureMismatch)
 
            If leastGeneric Is Nothing AndAlso Not signatureMismatch Then
 
                leastGeneric =
                    LeastGenericProcedure(
                        left,
                        right,
                        ComparisonType.GenericSpecificityBasedOnTypeGenericParams,
                        signatureMismatch)
            End If
 
            Return leastGeneric
 
        End Function
 
        <RequiresUnreferencedCode("Calls RejectUncallableProcedure")>
        Private Shared Sub InsertIfMethodAvailable(
            ByVal newCandidate As MemberInfo,
            ByVal newCandidateSignature As ParameterInfo(),
            ByVal newCandidateParamArrayIndex As Integer,
            ByVal expandNewCandidateParamArray As Boolean,
            ByVal arguments As Object(),
            ByVal argumentCount As Integer,
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal collectOnlyOperators As Boolean,
            ByVal candidates As List(Of Method),
            ByVal baseReference As Container)
 
            'Note that Arguments, ArgumentNames and TypeNames will be nothing when collecting operators.
            '
            Debug.Assert(arguments Is Nothing OrElse arguments.Length = argumentCount, "Inconsistency in arguments!!!")
 
            Dim newCandidateNode As Method = Nothing
 
            'If we're collecting only operators, then hiding by name and signature doesn't apply (neither do
            'ParamArrays), so skip all of this logic.
            If Not collectOnlyOperators Then
 
                Dim newCandidateMethod As MethodBase = TryCast(newCandidate, MethodBase)
                Dim inferenceFailedForNewCandidate As Boolean = False
 
                ' Note that operators cannot be generic methods
                '
                ' Need to complete type argument inference for generic methods when no type arguments
                ' have been supplied and when type arguments are supplied, the generic method needs to
                ' to be instantiated. Need to complete this so early in the overload process so that
                ' hid-by-sig, paramarray disambiguation etc. are done using the substitued signature.
                '
                If newCandidate.MemberType = MemberTypes.Method AndAlso IsRawGeneric(newCandidateMethod) Then
 
                    newCandidateNode =
                        New Method(
                            newCandidateMethod,
                            newCandidateSignature,
                            newCandidateParamArrayIndex,
                            expandNewCandidateParamArray)
 
                    ' Inferring of type arguments is done when determining the callability of this
                    ' procedure with these arguments by comparing them against the corresponding parameters.
                    '
                    ' Note that although RejectUncallableProcedure needs to be invoked on the non-generics
                    ' candidates too, it is not done here because some of the candidates might be rejected
                    ' for various reasons like hide-by-sig, paramarray disambiguation, etc. and RejectUncall-
                    ' -ableProcedure would not have to be invoked for them. This is especially important
                    ' because the RejectUncallableProcedure task is expensive.
                    '
 
                    RejectUncallableProcedure(
                        newCandidateNode,
                        arguments,
                        argumentNames,
                        typeArguments)
 
 
                    ' Get the instantiated method for this candidate
                    '
                    newCandidate = newCandidateNode.AsMethod
                    newCandidateSignature = newCandidateNode.Parameters
 
                End If
 
                ' Verify if TypeInference succeeded. This should only happen for Methods
                If newCandidate IsNot Nothing AndAlso
                    newCandidate.MemberType = MemberTypes.Method AndAlso
                    IsRawGeneric(TryCast(newCandidate, MethodBase)) Then
                    inferenceFailedForNewCandidate = True
                End If
 
                For index As Integer = 0 To candidates.Count - 1
 
                    Dim existing As Method = candidates.Item(index)
                    If existing Is Nothing Then Continue For 'This item was killed earlier, so skip it.
 
                    Dim existingCandidateSignature As ParameterInfo() = existing.Parameters
                    Dim existingCandidate As MethodBase
                    If existing.IsMethod Then existingCandidate = existing.AsMethod Else existingCandidate = Nothing
 
                    If newCandidate = existing Then Continue For
 
                    Dim newCandidateParameterIndex As Integer = 0
                    Dim existingCandidateParameterIndex As Integer = 0
 
                    For currentArgument As Integer = 1 To argumentCount
 
                        Dim bothLose As Boolean = False
                        Dim newCandidateWins As Boolean = False
                        Dim existingCandidateWins As Boolean = False
 
                        CompareParameterSpecificity(
                            Nothing,
                            newCandidateSignature(newCandidateParameterIndex),
                            newCandidateMethod,
                            expandNewCandidateParamArray,
                            existingCandidateSignature(existingCandidateParameterIndex),
                            existingCandidate,
                            existing.ParamArrayExpanded,
                            newCandidateWins,
                            existingCandidateWins,
                            bothLose)
 
                        If bothLose Or newCandidateWins Or existingCandidateWins Then
                            GoTo continueloop
                        End If
 
                        'If a parameter is a param array, there is no next parameter and so advancing
                        'through the parameter list is bad.
 
                        If newCandidateParameterIndex <> newCandidateParamArrayIndex OrElse Not expandNewCandidateParamArray Then
                            newCandidateParameterIndex += 1
                        End If
 
                        If existingCandidateParameterIndex <> existing.ParamArrayIndex OrElse Not existing.ParamArrayExpanded Then
                            existingCandidateParameterIndex += 1
                        End If
 
                    Next
 
                    Dim exactSignature As Boolean =
                        IsExactSignatureMatch(
                            newCandidateSignature,
                            GetTypeParameters(newCandidate).Length,
                            existing.Parameters,
                            existing.TypeParameters.Length)
 
                    If Not exactSignature Then
 
                        ' If inference failed for any of the candidates, then don't compare them.
                        '
                        ' This simple strategy besides fixing the problems associated with an inference
                        ' failed candidate beating a inference passing candidate also helps with better
                        ' error reporting by showing the inference failed candidates too.
                        '
                        If inferenceFailedForNewCandidate OrElse
                           (existingCandidate IsNot Nothing AndAlso IsRawGeneric(existingCandidate)) Then
                            Continue For
                        End If
 
 
                        If Not expandNewCandidateParamArray AndAlso existing.ParamArrayExpanded Then
                            'Delete current item from list and continue.
                            candidates.Item(index) = Nothing
                            Continue For
 
                        ElseIf expandNewCandidateParamArray AndAlso Not existing.ParamArrayExpanded Then
                            Return
 
                        ElseIf Not expandNewCandidateParamArray AndAlso Not existing.ParamArrayExpanded Then
                            'In theory, this shouldn't happen, but another language could
                            'theoretically define two methods with optional arguments that
                            'end up being equivalent. So don't prefer one over the other.
                            Continue For
 
                        Else
                            'If both are expanded, then see if one uses more on actual
                            'parameters than the other. If so, we prefer the one that uses
                            'more on actual parameters.
 
                            If (newCandidateParameterIndex > existingCandidateParameterIndex) Then
                                'Delete current item from list and continue.
                                candidates.Item(index) = Nothing
                                Continue For
 
                            ElseIf existingCandidateParameterIndex > newCandidateParameterIndex Then
                                Return
 
                            End If
 
                            Continue For
 
                        End If
                    Else
                        Debug.Assert((baseReference IsNot Nothing AndAlso baseReference.IsWindowsRuntimeObject) OrElse
                                     IsOrInheritsFrom(existing.DeclaringType, newCandidate.DeclaringType),
                                     "expected inheritance or WinRT collection types here")
 
                        If newCandidate.DeclaringType Is existing.DeclaringType Then
                            'If the two members are declared in the same container, both should be added to the set
                            'of overloads.  This results in intelligent ambiguity error messages.
                            Exit For
 
                        End If
 
                        ' If the base container is a WinRT object implementing collection interfaces they could have
                        ' the same method name with the same signature. We need to add them to the set to throw
                        ' ambiguity errors.
                        If baseReference IsNot Nothing AndAlso baseReference.IsWindowsRuntimeObject() AndAlso
                           Symbols.IsCollectionInterface(newCandidate.DeclaringType) AndAlso
                           Symbols.IsCollectionInterface(existing.DeclaringType) Then
                            Exit For
                        End If
 
                        'If inference did not fail for the base candidate, but failed for the derived candidate, then
                        'the derived candidate cannot hide the base candidate. VSWhidbey Bug 369042.
                        '
                        If Not inferenceFailedForNewCandidate AndAlso
                           (existingCandidate IsNot Nothing AndAlso IsRawGeneric(existingCandidate)) Then
                            Continue For
                        End If
 
                        Return
 
                    End If
 
                    Debug.Assert(False, "unexpected code path")
 
continueloop:
                Next
            End If
 
            If newCandidateNode IsNot Nothing Then
                candidates.Add(newCandidateNode)
            ElseIf newCandidate.MemberType = MemberTypes.Property Then
                candidates.Add(
                    New Method(
                        DirectCast(newCandidate, PropertyInfo),
                        newCandidateSignature,
                        newCandidateParamArrayIndex,
                        expandNewCandidateParamArray))
            Else
                candidates.Add(
                    New Method(
                        DirectCast(newCandidate, MethodBase),
                        newCandidateSignature,
                        newCandidateParamArrayIndex,
                        expandNewCandidateParamArray))
            End If
 
        End Sub
 
        <RequiresUnreferencedCode("Calls InsertIfMethodAvailable")>
        Friend Shared Function CollectOverloadCandidates(
            ByVal members As MemberInfo(),
            ByVal arguments As Object(),
            ByVal argumentCount As Integer,
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal collectOnlyOperators As Boolean,
            ByVal terminatingScope As System.Type,
            ByRef rejectedForArgumentCount As Integer,
            ByRef rejectedForTypeArgumentCount As Integer,
            ByVal baseReference As Container) As List(Of Method)
 
 
            'Note that Arguments, ArgumentNames and TypeNames will be nothing when collecting operators.
            '
            Debug.Assert(arguments Is Nothing OrElse arguments.Length = argumentCount, "Inconsistency in arguments!!!")
 
            Dim typeArgumentCount As Integer = 0
            If typeArguments IsNot Nothing Then
                typeArgumentCount = typeArguments.Length
            End If
 
            Dim candidates As List(Of Method) = New List(Of Method)(members.Length)
 
            If members.Length = 0 Then
                Return candidates
            End If
 
            Dim keepSearching As Boolean = True
            Dim index As Integer = 0
 
            Do
                Dim currentScope As Type = members(index).DeclaringType
 
                'The terminating scope parameter controls at which point candidate collection
                'will stop. This is useful for overloaded operator resolution where the left
                'and right operands may have a common ancestor and we wish to collect the common
                'candidates only once.
                If terminatingScope IsNot Nothing AndAlso IsOrInheritsFrom(terminatingScope, currentScope) Then Exit Do
                Do
                    Dim candidate As MemberInfo = members(index)
                    Dim candidateSignature As ParameterInfo() = Nothing
                    Dim typeParameterCount As Integer = 0
 
                    Select Case candidate.MemberType
 
                        Case MemberTypes.Constructor,
                             MemberTypes.Method
 
                            Dim currentMethod As MethodBase = DirectCast(candidate, MethodBase)
 
                            If collectOnlyOperators AndAlso Not IsUserDefinedOperator(currentMethod) Then
                                GoTo nextcandidate
                            End If
 
                            candidateSignature = currentMethod.GetParameters
                            typeParameterCount = GetTypeParameters(currentMethod).Length
 
                            If IsShadows(currentMethod) Then keepSearching = False
 
                        Case MemberTypes.Property
 
                            If collectOnlyOperators Then GoTo nextcandidate
 
                            Dim propertyBlock As PropertyInfo = DirectCast(candidate, PropertyInfo)
                            Dim getMethod As MethodInfo = propertyBlock.GetGetMethod
 
                            If getMethod IsNot Nothing Then
                                candidateSignature = getMethod.GetParameters
 
                                Debug.Assert(propertyBlock.GetSetMethod Is Nothing OrElse
                                             IsShadows(propertyBlock.GetSetMethod) = IsShadows(getMethod),
                                             "unexpected mismatched shadows on accessors")
                                If IsShadows(getMethod) Then keepSearching = False
 
                            Else
                                Dim setMethod As MethodInfo = propertyBlock.GetSetMethod
                                Debug.Assert(setMethod IsNot Nothing, "must have set here")
 
                                Dim setParameters As ParameterInfo() = setMethod.GetParameters
                                candidateSignature = New ParameterInfo(setParameters.Length - 2) {}
                                System.Array.Copy(setParameters, candidateSignature, candidateSignature.Length)
 
                                If IsShadows(setMethod) Then keepSearching = False
 
                            End If
 
                        Case MemberTypes.Custom,
                             MemberTypes.Event,
                             MemberTypes.Field,
                             MemberTypes.TypeInfo,
                             MemberTypes.NestedType
 
                            'All of these items automatically shadow.
                            If Not collectOnlyOperators Then
                                keepSearching = False
                            End If
                            GoTo nextcandidate
 
                        Case Else
                            Debug.Assert(False, "what is this?  just ignore it.")
                            GoTo nextcandidate
 
                    End Select
 
 
                    'We have a possible candidate method if we make it this far.  Insert it into the
                    'list if it qualifies.
 
                    Debug.Assert(candidateSignature IsNot Nothing, "must have signature if we have a method")
 
                    Dim requiredParameterCount As Integer = 0
                    Dim maximumParameterCount As Integer = 0
                    Dim paramArrayIndex As Integer = -1
 
                    'Weed out procedures that cannot accept the number of supplied arguments.
 
                    GetAllParameterCounts(candidateSignature, requiredParameterCount, maximumParameterCount, paramArrayIndex)
 
                    Dim hasParamArray As Boolean = paramArrayIndex >= 0
                    If argumentCount < requiredParameterCount OrElse
                       (Not hasParamArray AndAlso argumentCount > maximumParameterCount) Then
                        rejectedForArgumentCount += 1
                        GoTo nextcandidate
                    End If
 
                    'If type arguments have been supplied, weed out procedures that don't have an
                    'appropriate number of type parameters.
 
                    If typeArgumentCount > 0 AndAlso typeArgumentCount <> typeParameterCount Then
                        rejectedForTypeArgumentCount += 1
                        GoTo nextcandidate
                    End If
 
                    ' A method with a paramarray can be considered in two forms: in an
                    ' expanded form or in an unexpanded form (i.e. as if the paramarray
                    ' decoration was not specified). Weirdly, it can apply in both forms, as
                    ' in the case of passing Object() to ParamArray x As Object() (because
                    ' Object() converts to both Object() and Object).
 
                    ' Does the method apply in its unexpanded form? This can only happen if
                    ' either there is no paramarray or if the argument count matches exactly
                    ' (if it's less, then the paramarray is expanded to nothing, if it's more,
                    ' it's expanded to one or more parameters).
 
                    If Not hasParamArray OrElse argumentCount = maximumParameterCount Then
                        InsertIfMethodAvailable(
                            candidate,
                            candidateSignature,
                            paramArrayIndex,
                            False,
                            arguments,
                            argumentCount,
                            argumentNames,
                            typeArguments,
                            collectOnlyOperators,
                            candidates,
                            baseReference)
                    End If
 
                    'How about it's expanded form? It always applies if there's a paramarray.
 
                    If hasParamArray Then
                        Debug.Assert(Not collectOnlyOperators, "didn't expect operator with paramarray")
                        InsertIfMethodAvailable(
                            candidate,
                            candidateSignature,
                            paramArrayIndex,
                            True,
                            arguments,
                            argumentCount,
                            argumentNames,
                            typeArguments,
                            collectOnlyOperators,
                            candidates,
                            baseReference)
                    End If
 
nextcandidate:
                    index += 1
 
                Loop While index < members.Length AndAlso members(index).DeclaringType Is currentScope
 
            Loop While keepSearching AndAlso index < members.Length
 
#If BINDING_LOG Then
            Console.WriteLine("== COLLECTION AND SHADOWING ==")
            For Each item As Method In Candidates
                If item Is Nothing Then
                    Console.WriteLine("dead")
                Else
                    Console.WriteLine(item.DumpContents)
                End If
            Next
#End If
 
            'Remove the dead entries from the list--simplifies code later on.
            index = 0
            While index < candidates.Count
                If candidates(index) Is Nothing Then
                    Dim span As Integer = index + 1
                    While span < candidates.Count AndAlso candidates(span) Is Nothing
                        span += 1
                    End While
                    candidates.RemoveRange(index, span - index)
                End If
                index += 1
            End While
 
            Return candidates
        End Function
 
        <RequiresUnreferencedCode("Calls ClassifyConversion")>
        Private Shared Function CanConvert(
            ByVal targetType As Type,
            ByVal sourceType As Type,
            ByVal rejectNarrowingConversion As Boolean,
            ByVal errors As List(Of String),
            ByVal parameterName As String,
            ByVal isByRefCopyBackContext As Boolean,
            ByRef requiresNarrowingConversion As Boolean,
            ByRef allNarrowingIsFromObject As Boolean) As Boolean
 
            Dim conversionResult As ConversionClass = ClassifyConversion(targetType, sourceType, Nothing)
 
            Select Case conversionResult
 
                Case ConversionClass.Identity, ConversionClass.Widening
                    Return True
 
                Case ConversionClass.Narrowing
 
                    If rejectNarrowingConversion Then
                        If errors IsNot Nothing Then
                            ReportError(
                                errors,
                                IIf(isByRefCopyBackContext, SR.ArgumentNarrowingCopyBack3, SR.ArgumentNarrowing3),
                                parameterName,
                                sourceType,
                                targetType)
                        End If
 
                        Return False
                    Else
                        requiresNarrowingConversion = True
                        If sourceType IsNot GetType(Object) Then allNarrowingIsFromObject = False
 
                        Return True
                    End If
 
            End Select
 
            If errors IsNot Nothing Then
                ReportError(
                    errors,
                    IIf(conversionResult = ConversionClass.Ambiguous,
                        IIf(isByRefCopyBackContext,
                            SR.ArgumentMismatchAmbiguousCopyBack3,
                            SR.ArgumentMismatchAmbiguous3),
                        IIf(isByRefCopyBackContext,
                            SR.ArgumentMismatchCopyBack3,
                            SR.ArgumentMismatch3)),
                    parameterName,
                    sourceType,
                    targetType)
            End If
 
            Return False
        End Function
 
        <RequiresUnreferencedCode("Calls GetInterfaces on argument type recursively")>
        Private Shared Function InferTypeArgumentsFromArgument(
            ByVal argumentType As Type,
            ByVal parameterType As Type,
            ByVal typeInferenceArguments As Type(),
            ByVal targetProcedure As MethodBase,
            ByVal digThroughToBasesAndImplements As Boolean) As Boolean
 
            Dim inferred As Boolean =
                InferTypeArgumentsFromArgumentDirectly(
                    argumentType,
                    parameterType,
                    typeInferenceArguments,
                    targetProcedure,
                    digThroughToBasesAndImplements)
 
 
            If (inferred OrElse
                Not digThroughToBasesAndImplements OrElse
                Not IsInstantiatedGeneric(parameterType) OrElse
                (Not parameterType.IsClass AndAlso Not parameterType.IsInterface)) Then
 
                'can only inherit from classes or interfaces.
                'can ignore generic parameters here because it
                'were a generic parameter, inference would
                'definitely have succeeded.
 
                Return inferred
            End If
 
 
            Dim rawGenericParameterType As Type = parameterType.GetGenericTypeDefinition
 
            If (IsArrayType(argumentType)) Then
 
                '1. Generic IList is implemented only by one dimensional arrays
                '
                '2. If parameter type is a class, then no other inference from
                '   array is possible.
                '
                If (argumentType.GetArrayRank > 1 OrElse
                    parameterType.IsClass) Then
 
                    Return False
                End If
                'For arrays, change the argument type to be IList(Of Array element type)
 
                argumentType =
                    GetType(System.Collections.Generic.IList(Of )).MakeGenericType(New Type() {argumentType.GetElementType})
 
                If (GetType(System.Collections.Generic.IList(Of )) Is rawGenericParameterType) Then
                    GoTo RetryInference
                End If
 
            ElseIf (Not argumentType.IsClass AndAlso
                     Not argumentType.IsInterface) Then
 
                Debug.Assert(Not IsGenericParameter(argumentType), "Generic parameter unexpected!!!")
 
                Return False
 
            ElseIf (IsInstantiatedGeneric(argumentType) AndAlso
                     argumentType.GetGenericTypeDefinition Is rawGenericParameterType) Then
 
                Return False
            End If
 
 
            If (parameterType.IsClass) Then
 
                If (Not argumentType.IsClass) Then
                    Return False
                End If
 
                Dim base As Type = argumentType.BaseType
 
                While (base IsNot Nothing)
 
                    If (IsInstantiatedGeneric(base) AndAlso
                         base.GetGenericTypeDefinition Is rawGenericParameterType) Then
 
                        Exit While
                    End If
 
                    base = base.BaseType
                End While
 
                argumentType = base
            Else
 
                Dim implementedMatch As Type = Nothing
                For Each implemented As Type In argumentType.GetInterfaces
 
                    If (IsInstantiatedGeneric(implemented) AndAlso
                        implemented.GetGenericTypeDefinition Is rawGenericParameterType) Then
 
                        If (implementedMatch IsNot Nothing) Then
                            'Ambiguous
                            '
                            Return False
                        End If
 
                        implementedMatch = implemented
                    End If
                Next
 
                argumentType = implementedMatch
            End If
 
            If (argumentType Is Nothing) Then
                Return False
            End If
 
RetryInference:
 
            Return InferTypeArgumentsFromArgumentDirectly(
                    argumentType,
                    parameterType,
                    typeInferenceArguments,
                    targetProcedure,
                    digThroughToBasesAndImplements)
 
        End Function
 
 
        <RequiresUnreferencedCode("Calls InferTypeArgumentsFromArgument")>
        Private Shared Function InferTypeArgumentsFromArgumentDirectly(
            ByVal argumentType As Type,
            ByVal parameterType As Type,
            ByVal typeInferenceArguments As Type(),
            ByVal targetProcedure As MethodBase,
            ByVal digThroughToBasesAndImplements As Boolean) As Boolean
 
            Debug.Assert(Not parameterType.IsByRef, "didn't expect byref parameter type here")
            Debug.Assert(IsRawGeneric(targetProcedure), "Type inference for instantiated generic unexpected!!!")
 
            If Not RefersToGenericParameter(parameterType, targetProcedure) Then
                Return True
            End If
 
            'If a generic method is parameterized by T, an argument of type A matching a parameter of type
            'P can be used to infer a type for T by these patterns:
            '
            '  -- If P is T, then infer A for T
            '  -- If P is G(Of T) and A is G(Of X), then infer X for T
            '  -- If P is or implements G(Of T) and A is G(Of X), then infer X for T
            '  -- If P is Array Of T, and A is Array Of X, then infer X for T
 
            If IsGenericParameter(parameterType) Then
                If AreGenericMethodDefsEqual(parameterType.DeclaringMethod, targetProcedure) Then
                    Dim parameterIndex As Integer = parameterType.GenericParameterPosition
                    If typeInferenceArguments(parameterIndex) Is Nothing Then
                        typeInferenceArguments(parameterIndex) = argumentType
 
                    ElseIf typeInferenceArguments(parameterIndex) IsNot argumentType Then
                        Return False
 
                    End If
                End If
 
            ElseIf IsInstantiatedGeneric(parameterType) Then
 
                Dim bestMatchType As Type = Nothing
 
                If IsInstantiatedGeneric(argumentType) AndAlso
                    argumentType.GetGenericTypeDefinition Is parameterType.GetGenericTypeDefinition Then
                    bestMatchType = argumentType
                End If
 
                If bestMatchType Is Nothing AndAlso digThroughToBasesAndImplements Then
                    For Each possibleGenericType As Type In argumentType.GetInterfaces
                        If IsInstantiatedGeneric(possibleGenericType) AndAlso
                            possibleGenericType.GetGenericTypeDefinition Is parameterType.GetGenericTypeDefinition Then
 
                            If bestMatchType Is Nothing Then
                                bestMatchType = possibleGenericType
                            Else
                                ' Multiple generic interfaces match the parameter type
                                Return False
                            End If
                        End If
                    Next
                End If
 
                If bestMatchType IsNot Nothing Then
                    Dim parameterTypeParameters As Type() = GetTypeArguments(parameterType)
                    Dim argumentTypeArguments As Type() = GetTypeArguments(bestMatchType)
 
                    Debug.Assert(parameterTypeParameters.Length = argumentTypeArguments.Length,
                                 "inconsistent parameter counts")
 
                    For index As Integer = 0 To argumentTypeArguments.Length - 1
                        If Not InferTypeArgumentsFromArgument(
                                    argumentTypeArguments(index),
                                    parameterTypeParameters(index),
                                    typeInferenceArguments,
                                    targetProcedure,
                                    False) Then     'Don't dig through because generics covariance is not allowed
                            Return False
                        End If
                    Next
 
                    Return True
                End If
 
                Return False
 
            ElseIf IsArrayType(parameterType) Then
 
                If IsArrayType(argumentType) Then
                    If parameterType.GetArrayRank = argumentType.GetArrayRank Then
                        Return InferTypeArgumentsFromArgument(
                                GetElementType(argumentType),
                                GetElementType(parameterType),
                                typeInferenceArguments,
                                targetProcedure,
                                digThroughToBasesAndImplements)
                    End If
                End If
 
                Return False
            End If
 
            Return True
 
        End Function
 
        <RequiresUnreferencedCode("Calls ClassifyConversion")>
        Private Shared Function CanPassToParamArray(
            ByVal targetProcedure As Method,
            ByVal argument As Object,
            ByVal parameter As ParameterInfo) As Boolean
 
            'This method generates no errors because errors are reported only on the expanded form and
            'the unexpanded form is always accompanied by the expanded form.
 
            Debug.Assert(IsParamArray(parameter), "expected ParamArray parameter")
 
            'A Nothing argument can be passed as an unexpanded ParamArray.
            If argument Is Nothing Then Return True
 
            Dim parameterType As Type = parameter.ParameterType
            Dim argumentType As Type = GetArgumentType(argument)
            Dim conversionResult As ConversionClass = ClassifyConversion(parameterType, argumentType, Nothing)
            Return conversionResult = ConversionClass.Widening OrElse conversionResult = ConversionClass.Identity
        End Function
 
        <RequiresUnreferencedCode("Calls CanConvert")>
        Friend Shared Function CanPassToParameter(
            ByVal targetProcedure As Method,
            ByVal argument As Object,
            ByVal parameter As ParameterInfo,
            ByVal isExpandedParamArray As Boolean,
            ByVal rejectNarrowingConversions As Boolean,
            ByVal errors As List(Of String),
            ByRef requiresNarrowingConversion As Boolean,
            ByRef allNarrowingIsFromObject As Boolean) As Boolean
 
            'A Nothing argument always matches a parameter. Also, it doesn't contribute to type inferencing.
            If argument Is Nothing Then Return True
 
            Dim parameterType As Type = parameter.ParameterType
            Dim isByRef As Boolean = parameterType.IsByRef
 
            If isByRef OrElse isExpandedParamArray Then
                parameterType = GetElementType(parameterType)
            End If
            Dim argumentType As Type = GetArgumentType(argument)
            'A Missing argument always matches an optional parameter.
            If argument Is System.Reflection.Missing.Value Then
                If parameter.IsOptional Then
                    Return True
                ElseIf Not IsRootObjectType(parameterType) OrElse Not isExpandedParamArray Then
                    'Trying to pass a Missing argument to a non-optional parameter.
                    'CLR throws if that's the case, so we disallow it here.
                    If errors IsNot Nothing Then
                        If isExpandedParamArray Then
                            'Trying to pass a Missing argument to an expanded ParamArray.
                            ReportError(errors, SR.OmittedParamArrayArgument)
                        Else
                            ReportError(errors, SR.OmittedArgument1, parameter.Name)
                        End If
                    End If
                    Return False
                End If
            End If
 
            'Check if the conversion from the argument type to the
            'parameter type can succeed.
            Dim canCopyIn As Boolean =
                CanConvert(
                    parameterType,
                    argumentType,
                    rejectNarrowingConversions,
                    errors,
                    parameter.Name,
                    False,
                    requiresNarrowingConversion,
                    allNarrowingIsFromObject)
 
            If Not isByRef OrElse Not canCopyIn Then
                Return canCopyIn
            End If
 
            'If the parameter is ByRef, check if the conversion from
            'the parameter type to the argument type can succeed.
            Return CanConvert(
                    argumentType,
                    parameterType,
                    rejectNarrowingConversions,
                    errors,
                    parameter.Name,
                    True,
                    requiresNarrowingConversion,
                    allNarrowingIsFromObject)
 
        End Function
 
        <RequiresUnreferencedCode("Calls InferTypArgumentsFromArgument")>
        Friend Shared Function InferTypeArgumentsFromArgument(
            ByVal targetProcedure As Method,
            ByVal argument As Object,
            ByVal parameter As ParameterInfo,
            ByVal isExpandedParamArray As Boolean,
            ByVal errors As List(Of String)) As Boolean
 
            'A Nothing argument doesn't contribute to type inferencing.
            If argument Is Nothing Then Return True
 
            Dim parameterType As Type = parameter.ParameterType
            Dim isByRef As Boolean = parameterType.IsByRef
 
            If isByRef OrElse isExpandedParamArray Then
                parameterType = GetElementType(parameterType)
            End If
            Dim argumentType As Type = GetArgumentType(argument)
            Debug.Assert(targetProcedure.IsMethod, "we shouldn't be inferring type arguments for non-methods")
            If Not InferTypeArgumentsFromArgument(
                        argumentType,
                        parameterType,
                        targetProcedure.TypeArguments,
                        targetProcedure.AsMethod,
                        True) Then
 
                If errors IsNot Nothing Then
                    ReportError(errors, SR.TypeInferenceFails1, parameter.Name)
                End If
                Return False
            End If
 
            Return True
        End Function
 
        <RequiresUnreferencedCode("Uses Type.GetElementType which cannot be statically analyzed.")>
        Friend Shared Function PassToParameter(
            ByVal argument As Object,
            ByVal parameter As ParameterInfo,
            <DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)>
            ByVal parameterType As Type) As Object
 
            'This function takes an object and modifies it so it can be passed
            'as the parameter described by the ParameterInfo.  This involves casting it
            'to the parameter type and/or substituting optional values for Missing
            'arguments.
 
            Debug.Assert(parameter IsNot Nothing AndAlso parameterType IsNot Nothing)
 
            Dim isByRef As Boolean = parameterType.IsByRef
            If isByRef Then
                parameterType = parameterType.GetElementType
            End If
 
            'An argument represented by a TypedNothing is actually a Nothing
            'reference with a type. This argument passes to the parameter as Nothing.
            If TypeOf argument Is TypedNothing Then
                argument = Nothing
            End If
 
            'A Missing argument loads the parameter's optional value.
            If argument Is System.Reflection.Missing.Value AndAlso parameter.IsOptional Then
                argument = parameter.DefaultValue
            End If
 
            'If the argument is a boxed ValueType and we're passing it to a
            'ByRef parameter, then we must forcefully copy it to avoid
            'aliasing since the invocation will modify the boxed ValueType
            'in place.
            If isByRef Then
                Dim argumentType As Type = GetArgumentType(argument)
                If argumentType IsNot Nothing AndAlso IsValueType(argumentType) Then
                    argument = Conversions.ForceValueCopy(argument, argumentType)
                End If
            End If
 
            'Perform the conversion to the parameter type and return the result.
            Return Conversions.ChangeType(argument, parameterType)
        End Function
 
        Private Shared Function FindParameterByName(ByVal parameters As ParameterInfo(), ByVal name As String, ByRef index As Integer) As Boolean
            'Find the Index of the parameter in Parameters which matches Name. Return True if such a parameter is found.
 
            Dim paramIndex As Integer = 0
            Do While paramIndex < parameters.Length
                If Operators.CompareString(name, parameters(paramIndex).Name, True) = 0 Then
                    index = paramIndex
                    Return True
                End If
                paramIndex += 1
            Loop
            Return False
        End Function
 
        Private Shared Function CreateMatchTable(ByVal size As Integer, ByVal lastPositionalMatchIndex As Integer) As Boolean()
            'Create a table for keeping track of which parameters have been matched with
            'an argument. Used for detecting multiple matches during named argument matching,
            'and also for loading the optional values of unmatched parameters.
 
            Dim result As Boolean() = New Boolean(size - 1) {}
            For index As Integer = 0 To lastPositionalMatchIndex
                result(index) = True
            Next
            Return result
        End Function
 
        <RequiresUnreferencedCode("Calls InstantiateGenericMethod")>
        Friend Shared Function CanMatchArguments(
            ByVal targetProcedure As Method,
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal rejectNarrowingConversions As Boolean,
            ByVal errors As List(Of String)) As Boolean
 
            Dim reportErrors As Boolean = errors IsNot Nothing
 
            targetProcedure.ArgumentsValidated = True
 
            'First instantiate the generic method.  If type arguments aren't supplied,
            'we need to infer them first.
            'In error cases, the method might already be instantiated, so need to use the
            'passed in type params only if the method has not yet been instantiated.
            'In the non-error case, the method is always uninstantiated at this time.
            '
            Debug.Assert(Not (errors Is Nothing AndAlso
                              targetProcedure.IsMethod AndAlso
                              targetProcedure.AsMethod.IsGenericMethod AndAlso (Not targetProcedure.AsMethod.IsGenericMethodDefinition)),
                                "Instantiated generic method unexpected!!!")
 
            If targetProcedure.IsMethod AndAlso IsRawGeneric(targetProcedure.AsMethod) Then
                If typeArguments.Length = 0 Then
                    typeArguments = New Type(targetProcedure.TypeParameters.Length - 1) {}
                    targetProcedure.TypeArguments = typeArguments
 
                    If Not InferTypeArguments(targetProcedure, arguments, argumentNames, typeArguments, errors) Then
                        Return False
                    End If
                Else
                    targetProcedure.TypeArguments = typeArguments
                End If
 
                If Not InstantiateGenericMethod(targetProcedure, typeArguments, errors) Then
                    Return False
                End If
            End If
 
            Dim parameters As ParameterInfo() = targetProcedure.Parameters
            Debug.Assert(arguments.Length <= parameters.Length OrElse
                            (targetProcedure.ParamArrayExpanded AndAlso targetProcedure.ParamArrayIndex >= 0),
                         "argument count mismatch -- this method should have been rejected already")
 
            Dim argIndex As Integer = argumentNames.Length
            Dim paramIndex As Integer = 0
 
            'STEP 1
            'Match all positional arguments until we encounter a ParamArray or run out of positional arguments.
            Do While argIndex < arguments.Length
 
                'The loop is finished if we encounter a ParamArray.
                If paramIndex = targetProcedure.ParamArrayIndex Then Exit Do
 
                If Not CanPassToParameter(
                            targetProcedure,
                            arguments(argIndex),
                            parameters(paramIndex),
                            False,
                            rejectNarrowingConversions,
                            errors,
                            targetProcedure.RequiresNarrowingConversion,
                            targetProcedure.AllNarrowingIsFromObject) Then
 
                    'If errors are needed, keep going to catch them all.
                    If Not reportErrors Then Return False
 
                End If
 
                argIndex += 1
                paramIndex += 1
            Loop
 
            'STEP 2
            'Match all remaining positional arguments to the ParamArray.
            If targetProcedure.HasParamArray Then
 
                Debug.Assert(paramIndex = targetProcedure.ParamArrayIndex,
                             "current parameter must be param array by this point")
 
                If targetProcedure.ParamArrayExpanded Then
                    'Treat the ParamArray in its expanded form. Match remaining arguments to the
                    'ParamArray's element type.
 
                    'Nothing passed to a ParamArray will widen to both the type of the ParamArray and the
                    'array type of the ParamArray. In that case, we explicitly disallow matching an
                    'expanded ParamArray. If one argument remains and it is Nothing, reject the match.
 
                    If argIndex = arguments.Length - 1 AndAlso arguments(argIndex) Is Nothing Then
                        'No need to generate an error for this case since Nothing will always match
                        'the associated unexpanded form.
                        Return False
                    End If
 
                    Do While argIndex < arguments.Length
 
                        If Not CanPassToParameter(
                                    targetProcedure,
                                    arguments(argIndex),
                                    parameters(paramIndex),
                                    True,
                                    rejectNarrowingConversions,
                                    errors,
                                    targetProcedure.RequiresNarrowingConversion,
                                    targetProcedure.AllNarrowingIsFromObject) Then
 
                            'If errors are needed, keep going to catch them all.
                            If Not reportErrors Then Return False
 
                        End If
 
                        argIndex += 1
                    Loop
 
                Else
                    'Treat the ParamArray in its unexpanded form. Determine if the argument can
                    'be passed directly as a ParamArray.
 
                    Debug.Assert(arguments.Length - argIndex <= 1,
                                 "must have zero or one arg left to match the unexpanded paramarray")  'Candidate collection guarantees this.
 
                    'Need one argument left over for the unexpanded form to be applicable.
                    If arguments.Length - argIndex <> 1 Then
                        'No need to generate an error for this case because the error
                        'reporting will be done on the expanded form. All we need to do is
                        'disqualify the unexpanded form.
                        Return False
                    End If
 
                    If Not CanPassToParamArray(
                                targetProcedure,
                                arguments(argIndex),
                                parameters(paramIndex)) Then
 
                        ' We do need to report errors when only the
                        ' unexpanded form is being considered.
                        If reportErrors Then
                            ReportError(
                                errors,
                                SR.ArgumentMismatch3,
                                parameters(paramIndex).Name,
                                GetArgumentType(arguments(argIndex)),
                                parameters(paramIndex).ParameterType)
                        End If
 
                        Return False
                    End If
 
                End If
 
                'Matching the ParamArray consumes this parameter. Increment the parameter index.
                paramIndex += 1
            End If
 
            'If needed, create the table which keeps track of matched Parameters.
            'Initialize it using the positional matches we've found thus far.
            'This table is needed if we potentially have unmatched Optional parameters.
            'This can happen when named arguments exist or when the number of positional
            'arguments is less than the number of parameters.
            Dim matchedParameters As Boolean() = Nothing
 
            If argumentNames.Length > 0 OrElse paramIndex < parameters.Length Then
                matchedParameters = CreateMatchTable(parameters.Length, paramIndex - 1)
            End If
 
            'STEP 3
            'Match all named arguments.
            If argumentNames.Length > 0 Then
 
                Debug.Assert(parameters.Length > 0, "expected some parameters here")  'Candidate collection guarantees this.
 
                'The named argument mapping table contains indices into the
                'parameters array to describe the association between arguments
                'and parameters.
                '
                'Given an array of arguments and an array of argument names, the
                'index n into each of these arrays represents the nth named argument
                'and its associated name. If argument n matches the name of the
                'parameter at index m in the array of parameters, then the named
                'argument mapping table will contain the value m at index n.
 
                Dim namedArgumentMapping As Integer() = New Integer(argumentNames.Length - 1) {}
 
                argIndex = 0
                Do While argIndex < argumentNames.Length
 
                    If Not FindParameterByName(parameters, argumentNames(argIndex), paramIndex) Then
                        'This named argument does not match the name of any parameter.
                        'If errors are needed, keep going to catch them all.
                        If Not reportErrors Then Return False
                        ReportError(errors, SR.NamedParamNotFound2, argumentNames(argIndex), targetProcedure)
                        GoTo skipargument
                    End If
 
                    If paramIndex = targetProcedure.ParamArrayIndex Then
                        'This named argument matches a ParamArray parameter.
                        'If errors are needed, keep going to catch them all.
                        If Not reportErrors Then Return False
                        ReportError(errors, SR.NamedParamArrayArgument1, argumentNames(argIndex))
                        GoTo skipargument
                    End If
 
                    If matchedParameters(paramIndex) Then
                        'This named argument matches a parameter which has already been specified.
                        'If errors are needed, keep going to catch them all.
                        If Not reportErrors Then Return False
                        ReportError(errors, SR.NamedArgUsedTwice2, argumentNames(argIndex), targetProcedure)
                        GoTo skipargument
                    End If
 
                    If Not CanPassToParameter(
                                targetProcedure,
                                arguments(argIndex),
                                parameters(paramIndex),
                                False,
                                rejectNarrowingConversions,
                                errors,
                                targetProcedure.RequiresNarrowingConversion,
                                targetProcedure.AllNarrowingIsFromObject) Then
 
                        'If errors are needed, keep going to catch them all.
                        If Not reportErrors Then Return False
 
                    End If
 
                    matchedParameters(paramIndex) = True
                    namedArgumentMapping(argIndex) = paramIndex
skipargument:
                    argIndex += 1
                Loop
 
                'Store this away for use when/if we invoke this method.
                targetProcedure.NamedArgumentMapping = namedArgumentMapping
            End If
 
            'All remaining unmatched parameters must be Optional.
            If matchedParameters IsNot Nothing Then
                For index As Integer = 0 To matchedParameters.Length - 1
                    If matchedParameters(index) = False AndAlso Not parameters(index).IsOptional Then
                        'This parameter is not optional.
                        'If errors are needed, keep going to catch them all.
                        If Not reportErrors Then Return False
                        ReportError(errors, SR.OmittedArgument1, parameters(index).Name)
                    End If
                Next
            End If
 
            'If errors were generated, the arguments failed to match the target procedure.
            If errors IsNot Nothing AndAlso errors.Count > 0 Then
                Return False
            End If
 
            Return True
 
        End Function
 
        <RequiresUnreferencedCode("Calls Method.BindGenericArguments")>
        Private Shared Function InstantiateGenericMethod(
            ByVal targetProcedure As Method,
            ByVal typeArguments As Type(),
            ByVal errors As List(Of String)) As Boolean
 
            'Verify that all type arguments have been supplied.
            Debug.Assert(typeArguments.Length = targetProcedure.TypeParameters.Length, "expected length match")
 
            Dim reportErrors As Boolean = errors IsNot Nothing
 
            For typeArgumentIndex As Integer = 0 To typeArguments.Length - 1
 
                If typeArguments(typeArgumentIndex) Is Nothing Then
                    If Not reportErrors Then Return False
                    ReportError(
                            errors,
                            SR.UnboundTypeParam1,
                            targetProcedure.TypeParameters(typeArgumentIndex).Name)
                End If
 
            Next
 
            If errors Is Nothing OrElse errors.Count = 0 Then
                'Create the instantiated form of the generic method using the type arguments
                'inferred during argument matching.
                If Not targetProcedure.BindGenericArguments Then
                    If Not reportErrors Then Return False
                    ReportError(errors, SR.FailedTypeArgumentBinding)
                End If
            End If
 
            'If errors were generated, the instantiation failed.
            If errors IsNot Nothing AndAlso errors.Count > 0 Then
                Return False
            End If
 
            Return True
        End Function
 
        'may not want Method as TargetProcedure - may instead want to pass the required information in separately.
        'this means that for the simple case of only one method we do not need to allocate a Method object.
        <RequiresUnreferencedCode("Cannot statically analyze the parameter types of the targetProcedure")>
        Friend Shared Sub MatchArguments(
            ByVal targetProcedure As Method,
            ByVal arguments As Object(),
            ByVal matchedArguments As Object())
 
            Dim parameters As ParameterInfo() = targetProcedure.Parameters
 
            Debug.Assert(targetProcedure.ArgumentsValidated,
                         "expected validation of arguments to be made before matching")
            Debug.Assert(matchedArguments.Length = parameters.Length OrElse
                         matchedArguments.Length = parameters.Length + 1,
                         "size of matched arguments array must equal number of parameters")
            Debug.Assert(arguments.Length <= parameters.Length OrElse
                            (targetProcedure.ParamArrayExpanded AndAlso targetProcedure.ParamArrayIndex >= 0),
                         "argument count mismatch -- this method should have been rejected already")
 
            Dim namedArgumentMapping As Integer() = targetProcedure.NamedArgumentMapping
 
            Dim argIndex As Integer = 0
            If namedArgumentMapping IsNot Nothing Then argIndex = namedArgumentMapping.Length
            Dim paramIndex As Integer = 0
 
            'STEP 1
            'Match all positional arguments until we encounter a ParamArray or run out of positional arguments.
            Do While argIndex < arguments.Length
 
                'The loop is finished if we encounter a ParamArray.
                If paramIndex = targetProcedure.ParamArrayIndex Then Exit Do
 
                matchedArguments(paramIndex) =
                    PassToParameter(arguments(argIndex), parameters(paramIndex), parameters(paramIndex).ParameterType)
 
                argIndex += 1
                paramIndex += 1
            Loop
 
            'STEP 2
            'Match all remaining positional arguments to the ParamArray.
            If targetProcedure.HasParamArray Then
                Debug.Assert(paramIndex = targetProcedure.ParamArrayIndex,
                             "current parameter must be param array by this point")
 
                If targetProcedure.ParamArrayExpanded Then
                    'Treat the ParamArray in its expanded form. Pass the remaining arguments into
                    'the ParamArray.
 
                    Dim remainingArgumentCount As Integer = arguments.Length - argIndex
                    Dim paramArrayParameter As ParameterInfo = parameters(paramIndex)
                    Dim paramArrayElementType As System.Type = paramArrayParameter.ParameterType.GetElementType
 
                    Dim paramArrayArgument As System.Array =
                        System.Array.CreateInstance(paramArrayElementType, remainingArgumentCount)
 
                    Dim paramArrayIndex As Integer = 0
                    Do While argIndex < arguments.Length
 
                        paramArrayArgument.SetValue(
                            PassToParameter(arguments(argIndex), paramArrayParameter, paramArrayElementType),
                            paramArrayIndex)
 
                        argIndex += 1
                        paramArrayIndex += 1
                    Loop
 
                    matchedArguments(paramIndex) = paramArrayArgument
 
                Else
                    Debug.Assert(arguments.Length - argIndex = 1,
                                 "must have one arg left to match the unexpanded paramarray")
 
                    'Treat the ParamArray in its unexpanded form. Pass the one remaining argument
                    'directly as a ParamArray.
                    matchedArguments(paramIndex) =
                        PassToParameter(arguments(argIndex), parameters(paramIndex), parameters(paramIndex).ParameterType)
                End If
 
                'Matching the ParamArray consumes this parameter. Increment the parameter index.
                paramIndex += 1
            End If
 
            'If needed, create the table which keeps track of matched Parameters.
            'Initialize it using the positional matches we've found thus far.
            'This table is needed if we potentially have unmatched Optional parameters.
            'This can happen when named arguments exist or when the number of positional
            'arguments is less than the number of parameters.
            Dim matchedParameters As Boolean() = Nothing
 
            If namedArgumentMapping IsNot Nothing OrElse paramIndex < parameters.Length Then
                matchedParameters = CreateMatchTable(parameters.Length, paramIndex - 1)
            End If
 
            'STEP 3
            'Match all named arguments.
            If namedArgumentMapping IsNot Nothing Then
 
                Debug.Assert(parameters.Length > 0, "expected some parameters here")  'Candidate collection guarantees this.
 
                'The named argument mapping table contains indices into the
                'parameters array to describe the association between arguments
                'and parameters.
                '
                'Given an array of arguments and an array of argument names, the
                'index n into each of these arrays represents the nth named argument
                'and its associated name. If argument n matches the name of the
                'parameter at index m in the array of parameters, then the named
                'argument mapping table will contain the value m at index n.
 
                argIndex = 0
                Do While argIndex < namedArgumentMapping.Length
                    paramIndex = namedArgumentMapping(argIndex)
 
                    matchedArguments(paramIndex) =
                        PassToParameter(arguments(argIndex), parameters(paramIndex), parameters(paramIndex).ParameterType)
 
                    Debug.Assert(Not matchedParameters(paramIndex), "named argument match collision")
                    matchedParameters(paramIndex) = True
                    argIndex += 1
                Loop
 
            End If
 
            'If all has gone well, by this point any unmatched parameters are Optional.
            'Fill in unmatched parameters with their optional values.
            If matchedParameters IsNot Nothing Then
                For index As Integer = 0 To matchedParameters.Length - 1
                    If matchedParameters(index) = False Then
                        Debug.Assert(parameters(index).IsOptional,
                                     "unmatched, non-optional parameter. How did we get this far?")
                        matchedArguments(index) =
                            PassToParameter(System.Reflection.Missing.Value, parameters(index), parameters(index).ParameterType)
                    End If
                Next
            End If
 
            Return
        End Sub
 
        <RequiresUnreferencedCode("Calls InferTypeArgumentsFromArgument")>
        Private Shared Function InferTypeArguments(
            ByVal targetProcedure As Method,
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal errors As List(Of String)) As Boolean
 
            Dim reportErrors As Boolean = errors IsNot Nothing
 
            Dim parameters As ParameterInfo() = targetProcedure.RawParameters
 
            Debug.Assert(arguments.Length <= parameters.Length OrElse
                            (targetProcedure.ParamArrayExpanded AndAlso targetProcedure.ParamArrayIndex >= 0),
                         "argument count mismatch -- this method should have been rejected already")
 
            Dim argIndex As Integer = argumentNames.Length
            Dim paramIndex As Integer = 0
 
            'STEP 1
            'Infer from all positional arguments until we encounter a ParamArray or run out of positional arguments.
            Do While argIndex < arguments.Length
 
                'The loop is finished if we encounter a ParamArray.
                If paramIndex = targetProcedure.ParamArrayIndex Then Exit Do
 
                If Not InferTypeArgumentsFromArgument(
                            targetProcedure,
                            arguments(argIndex),
                            parameters(paramIndex),
                            False,
                            errors) Then
 
                    'If errors are needed, keep going to catch them all.
                    If Not reportErrors Then Return False
                End If
 
                argIndex += 1
                paramIndex += 1
            Loop
 
            'STEP 2
            'Infer from all remaining positional arguments matching a ParamArray.
            If targetProcedure.HasParamArray Then
 
                Debug.Assert(paramIndex = targetProcedure.ParamArrayIndex,
                             "current parameter must be param array by this point")
 
                If targetProcedure.ParamArrayExpanded Then
                    'Treat the ParamArray in its expanded form. Infer the element type from the remaining arguments.
                    Do While argIndex < arguments.Length
 
                        If Not InferTypeArgumentsFromArgument(
                                    targetProcedure,
                                    arguments(argIndex),
                                    parameters(paramIndex),
                                    True,
                                    errors) Then
 
                            'If errors are needed, keep going to catch them all.
                            If Not reportErrors Then Return False
 
                        End If
 
                        argIndex += 1
                    Loop
 
                Else
                    'Treat the ParamArray in its unexpanded form. Infer the ParamArray type from the argument.
 
                    Debug.Assert(arguments.Length - argIndex <= 1,
                                 "must have zero or one arg left to match the unexpanded paramarray")  'Candidate collection guarantees this.
 
                    If arguments.Length - argIndex <> 1 Then
                        'Type inferencing not possible here.
                        Return True
                    End If
 
                    If Not InferTypeArgumentsFromArgument(
                                targetProcedure,
                                arguments(argIndex),
                                parameters(paramIndex),
                                False,
                                errors) Then
                        Return False
                    End If
 
                End If
 
                'Matching the ParamArray consumes this parameter. Increment the parameter index.
                paramIndex += 1
            End If
 
            'STEP 3
            'Infer from named arguments.
            If argumentNames.Length > 0 Then
 
                Debug.Assert(parameters.Length > 0, "expected some parameters here")  'Candidate collection guarantees this.
 
                argIndex = 0
                Do While argIndex < argumentNames.Length
 
                    If Not FindParameterByName(parameters, argumentNames(argIndex), paramIndex) Then
                        GoTo skipargument
                    End If
 
                    If paramIndex = targetProcedure.ParamArrayIndex Then
                        GoTo skipargument
                    End If
 
                    If Not InferTypeArgumentsFromArgument(
                                targetProcedure,
                                arguments(argIndex),
                                parameters(paramIndex),
                                False,
                                errors) Then
 
                        'If errors are needed, keep going to catch them all.
                        If Not reportErrors Then Return False
 
                    End If
skipargument:
                    argIndex += 1
                Loop
 
            End If
 
            'If errors were generated, inference of type arguments failed.
            If errors IsNot Nothing AndAlso errors.Count > 0 Then
                Return False
            End If
 
            Return True
        End Function
 
        Friend Shared Sub ReorderArgumentArray(
            ByVal targetProcedure As Method,
            ByVal parameterResults As Object(),
            ByVal arguments As Object(),
            ByVal copyBack As Boolean(),
            ByVal lookupFlags As BindingFlags)
 
            'No need to copy back if there are no valid targets .
            'The copy back array will be Nothing if the compiler determined that all
            'arguments are Rvalues.
            If copyBack Is Nothing Then
                Return
            End If
 
            'Initialize the copy back array to all ByVal.
            For index As Integer = 0 To copyBack.Length - 1
                copyBack(index) = False
            Next
 
            'No need to copy back if there are no byref parameters. Properties can't have
            'ByRef arguments, so skip these as well.
            If HasFlag(lookupFlags, BindingFlagsSetProperty) OrElse
               Not targetProcedure.HasByRefParameter Then
                Return
            End If
 
            Debug.Assert(copyBack.Length = arguments.Length, "array sizes must match")
            Debug.Assert(parameterResults.Length = targetProcedure.Parameters.Length, "parameter arrays must match")
 
            Dim parameters As ParameterInfo() = targetProcedure.Parameters
            Dim namedArgumentMapping As Integer() = targetProcedure.NamedArgumentMapping
 
            Dim argIndex As Integer = 0
            If namedArgumentMapping IsNot Nothing Then argIndex = namedArgumentMapping.Length
            Dim paramIndex As Integer = 0
 
            'STEP 1
            'Copy back all positional parameters until we encounter a ParamArray or run out of positional arguments.
            Do While argIndex < arguments.Length
 
                'The loop is finished if we encounter a ParamArray.
                If paramIndex = targetProcedure.ParamArrayIndex Then Exit Do
 
                If parameters(paramIndex).ParameterType.IsByRef Then
                    arguments(argIndex) = parameterResults(paramIndex)
                    copyBack(argIndex) = True
                End If
 
                argIndex += 1
                paramIndex += 1
            Loop
 
            'STEP 2
            'No need to copy back from the ParamArray because they can't be ByRef. Skip it.
 
            'STEP 3
            'Copy back all named arguments.
            If namedArgumentMapping IsNot Nothing Then
                argIndex = 0
                Do While argIndex < namedArgumentMapping.Length
                    paramIndex = namedArgumentMapping(argIndex)
 
                    If parameters(paramIndex).ParameterType.IsByRef Then
                        arguments(argIndex) = parameterResults(paramIndex)
                        copyBack(argIndex) = True
                    End If
 
                    argIndex += 1
                Loop
            End If
 
            Return
        End Sub
 
        <RequiresUnreferencedCode("Calls RejectUncallableProcedure")>
        Private Shared Function RejectUncallableProcedures(
            ByVal candidates As List(Of Method),
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByRef candidateCount As Integer,
            ByRef someCandidatesAreGeneric As Boolean) As Method
 
            Dim bestCandidate As Method = Nothing
 
            For index As Integer = 0 To candidates.Count - 1
 
                Dim candidateProcedure As Method = candidates(index)
 
                If Not candidateProcedure.ArgumentMatchingDone Then
 
                    RejectUncallableProcedure(
                        candidateProcedure,
                        arguments,
                        argumentNames,
                        typeArguments)
                End If
 
                If candidateProcedure.NotCallable Then
                    candidateCount -= 1
                Else
                    bestCandidate = candidateProcedure
 
                    If candidateProcedure.IsGeneric OrElse IsGeneric(candidateProcedure.DeclaringType) Then
                        someCandidatesAreGeneric = True
                    End If
 
                End If
 
            Next
 
 
#If BINDING_LOG Then
            Console.WriteLine("== REJECT UNCALLABLE ==")
            For Each item As Method In Candidates
                If item Is Nothing Then
                    Console.WriteLine("dead ** didn't expect this here.")
                Else
                    Console.WriteLine(item.DumpContents)
                End If
            Next
#End If
            Return bestCandidate
 
        End Function
 
        <RequiresUnreferencedCode("Calls CanMatchArguments")>
        Private Shared Sub RejectUncallableProcedure(
            ByVal candidate As Method,
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type())
 
            Debug.Assert(candidate.ArgumentMatchingDone = False, "Argument matching being done multiple times!!!")
 
            If Not CanMatchArguments(
                        candidate,
                        arguments,
                        argumentNames,
                        typeArguments,
                        False,
                        Nothing) Then
 
                candidate.NotCallable = True
            End If
 
            candidate.ArgumentMatchingDone = True
 
        End Sub
 
        ' Type.IsEquivalentTo is not supported in .NET Framework 4.0
        Private Shared Function GetArgumentType(ByVal argument As Object) As Type
            'A Nothing object has no type.
            If argument Is Nothing Then Return Nothing
            'A typed Nothing object stores the type that Nothing should be considered as.
            Dim typedNothingArgument As TypedNothing = TryCast(argument, TypedNothing)
 
            If typedNothingArgument IsNot Nothing Then Return typedNothingArgument.Type
            'Otherwise, just return the type of the object.
            Return argument.GetType
        End Function
 
        <RequiresUnreferencedCode("Calls Method.RawParametersFromType")>
        Private Shared Function MoreSpecificProcedure(
            ByVal left As Method,
            ByVal right As Method,
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal compareGenericity As ComparisonType,
            Optional ByRef bothLose As Boolean = False,
            Optional ByVal continueWhenBothLose As Boolean = False) As Method
 
            bothLose = False
            Dim leftWinsAtLeastOnce As Boolean = False
            Dim rightWinsAtLeastOnce As Boolean = False
 
            'Compare the parameters that match the supplied positional arguments.
 
            Dim leftMethod As MethodBase
            Dim rightMethod As MethodBase
            If left.IsMethod Then leftMethod = left.AsMethod Else leftMethod = Nothing
            If right.IsMethod Then rightMethod = right.AsMethod Else rightMethod = Nothing
 
            Dim leftParamIndex As Integer = 0
            Dim rightParamIndex As Integer = 0
 
            Dim argIndex As Integer = argumentNames.Length
            Do While argIndex < arguments.Length
 
                Dim argumentType As Type = GetArgumentType(arguments(argIndex))
 
                Select Case compareGenericity
                    Case ComparisonType.GenericSpecificityBasedOnMethodGenericParams
                        ' Compare GenericSpecificity
                        CompareGenericityBasedOnMethodGenericParams(
                            left.Parameters(leftParamIndex),
                            left.RawParameters(leftParamIndex),
                            left,
                            left.ParamArrayExpanded,
                            right.Parameters(rightParamIndex),
                            right.RawParameters(rightParamIndex),
                            right,
                            right.ParamArrayExpanded,
                            leftWinsAtLeastOnce,
                            rightWinsAtLeastOnce,
                            bothLose)
 
                    Case ComparisonType.GenericSpecificityBasedOnTypeGenericParams
                        ' Compare GenericSpecificity
                        CompareGenericityBasedOnTypeGenericParams(
                            left.Parameters(leftParamIndex),
                            left.RawParametersFromType(leftParamIndex),
                            left,
                            left.ParamArrayExpanded,
                            right.Parameters(rightParamIndex),
                            right.RawParametersFromType(rightParamIndex),
                            right,
                            right.ParamArrayExpanded,
                            leftWinsAtLeastOnce,
                            rightWinsAtLeastOnce,
                            bothLose)
 
                    Case ComparisonType.ParameterSpecificty
                        ' Compare ParameterSpecificity
                        CompareParameterSpecificity(
                                argumentType,
                                left.Parameters(leftParamIndex),
                                leftMethod,
                                left.ParamArrayExpanded,
                                right.Parameters(rightParamIndex),
                                rightMethod,
                                right.ParamArrayExpanded,
                                leftWinsAtLeastOnce,
                                rightWinsAtLeastOnce,
                                bothLose)
 
                    Case Else
                        Debug.Assert(False, "Unexpected comparison type!!!")
                End Select
 
                If (bothLose AndAlso (Not continueWhenBothLose)) OrElse
                   (leftWinsAtLeastOnce AndAlso rightWinsAtLeastOnce) Then
                    Return Nothing
                End If
 
                If leftParamIndex <> left.ParamArrayIndex Then leftParamIndex += 1
                If rightParamIndex <> right.ParamArrayIndex Then rightParamIndex += 1
                argIndex += 1
            Loop
 
            argIndex = 0
            Do While argIndex < argumentNames.Length
 
                Dim leftParameterFound As Boolean = FindParameterByName(left.Parameters, argumentNames(argIndex), leftParamIndex)
                Dim rightParameterFound As Boolean = FindParameterByName(right.Parameters, argumentNames(argIndex), rightParamIndex)
 
                If Not leftParameterFound OrElse Not rightParameterFound Then
                    Throw New InternalErrorException()
                End If
 
                Dim argumentType As Type = GetArgumentType(arguments(argIndex))
 
                Select Case compareGenericity
                    Case ComparisonType.GenericSpecificityBasedOnMethodGenericParams
                        ' Compare GenericSpecificity
                        CompareGenericityBasedOnMethodGenericParams(
                            left.Parameters(leftParamIndex),
                            left.RawParameters(leftParamIndex),
                            left,
                            True,
                            right.Parameters(rightParamIndex),
                            right.RawParameters(rightParamIndex),
                            right,
                            True,
                            leftWinsAtLeastOnce,
                            rightWinsAtLeastOnce,
                            bothLose)
 
                    Case ComparisonType.GenericSpecificityBasedOnTypeGenericParams
                        ' Compare GenericSpecificity
                        CompareGenericityBasedOnTypeGenericParams(
                            left.Parameters(leftParamIndex),
                            left.RawParameters(leftParamIndex),
                            left,
                            True,
                            right.Parameters(rightParamIndex),
                            right.RawParameters(rightParamIndex),
                            right,
                            True,
                            leftWinsAtLeastOnce,
                            rightWinsAtLeastOnce,
                            bothLose)
 
                    Case ComparisonType.ParameterSpecificty
                        ' Compare ParameterSpecificity
                        CompareParameterSpecificity(
                            argumentType,
                            left.Parameters(leftParamIndex),
                            leftMethod,
                            True,
                            right.Parameters(rightParamIndex),
                            rightMethod,
                            True,
                            leftWinsAtLeastOnce,
                            rightWinsAtLeastOnce,
                            bothLose)
                End Select
 
                If (bothLose AndAlso (Not continueWhenBothLose)) OrElse
                   (leftWinsAtLeastOnce AndAlso rightWinsAtLeastOnce) Then
                    Return Nothing
                End If
 
                argIndex += 1
            Loop
 
            Debug.Assert(Not (leftWinsAtLeastOnce AndAlso rightWinsAtLeastOnce),
                         "Most specific method logic is confused.")
 
            If leftWinsAtLeastOnce Then Return left
            If rightWinsAtLeastOnce Then Return right
 
            Return Nothing
        End Function
 
        <RequiresUnreferencedCode("Calls MoreSpecificProcedure")>
        Private Shared Function MostSpecificProcedure(
            ByVal candidates As List(Of Method),
            ByRef candidateCount As Integer,
            ByVal arguments As Object(),
            ByVal argumentNames As String()) As Method
 
 
            For Each currentCandidate As Method In candidates
 
                If currentCandidate.NotCallable OrElse currentCandidate.RequiresNarrowingConversion Then
                    Continue For
                End If
 
                Dim currentCandidateIsBest As Boolean = True
 
                For Each contender As Method In candidates
 
                    If contender.NotCallable OrElse
                       contender.RequiresNarrowingConversion OrElse
                       (contender = currentCandidate AndAlso
                            contender.ParamArrayExpanded = currentCandidate.ParamArrayExpanded) Then
 
                        Continue For
                    End If
 
                    Dim bestOfTheTwo As Method =
                        MoreSpecificProcedure(
                            currentCandidate,
                            contender,
                            arguments,
                            argumentNames,
                            ComparisonType.ParameterSpecificty,
                            continueWhenBothLose:=True)
 
                    If bestOfTheTwo Is currentCandidate Then
                        If Not contender.LessSpecific Then
                            contender.LessSpecific = True
                            candidateCount -= 1
                        End If
                    Else
                        'The current candidate can't be the most specific.
                        currentCandidateIsBest = False
 
                        If bestOfTheTwo Is contender AndAlso Not currentCandidate.LessSpecific Then
                            currentCandidate.LessSpecific = True
                            candidateCount -= 1
                        End If
                    End If
                Next
 
                If currentCandidateIsBest Then
                    Debug.Assert(candidateCount = 1, "Surprising overload candidate remains.")
                    Return currentCandidate
                End If
 
            Next
 
            Return Nothing
        End Function
 
        <RequiresUnreferencedCode("Calls MoreSpecificProcedure")>
        Private Shared Function RemoveRedundantGenericProcedures(
            ByVal candidates As List(Of Method),
            ByRef candidateCount As Integer,
            ByVal arguments As Object(),
            ByVal argumentNames As String()) As Method
 
 
            For leftIndex As Integer = 0 To candidates.Count - 1
                Dim left As Method = candidates(leftIndex)
 
                If Not left.NotCallable Then
 
                    For rightIndex As Integer = leftIndex + 1 To candidates.Count - 1
                        Dim right As Method = candidates(rightIndex)
 
                        If Not right.NotCallable AndAlso
                           left.RequiresNarrowingConversion = right.RequiresNarrowingConversion Then
 
                            Dim leastGeneric As Method = Nothing
                            Dim signatureMismatch As Boolean = False
 
                            ' Least generic based on generic method's type parameters
 
                            If left.IsGeneric() OrElse right.IsGeneric() Then
 
                                leastGeneric =
                                    MoreSpecificProcedure(
                                        left,
                                        right,
                                        arguments,
                                        argumentNames,
                                        ComparisonType.GenericSpecificityBasedOnMethodGenericParams,
                                        signatureMismatch)
 
                                If leastGeneric IsNot Nothing Then
                                    candidateCount -= 1
                                    If candidateCount = 1 Then
                                        Return leastGeneric
                                    End If
                                    If leastGeneric Is left Then
                                        right.NotCallable = True
                                    Else
                                        left.NotCallable = True
                                        Exit For
                                    End If
                                End If
                            End If
 
 
                            ' Least generic based on method's generic parent's type parameters
 
                            If Not signatureMismatch AndAlso
                               leastGeneric Is Nothing AndAlso
                               (IsGeneric(left.DeclaringType) OrElse IsGeneric(right.DeclaringType)) Then
 
                                leastGeneric =
                                    MoreSpecificProcedure(
                                        left,
                                        right,
                                        arguments,
                                        argumentNames,
                                        ComparisonType.GenericSpecificityBasedOnTypeGenericParams,
                                        signatureMismatch)
 
                                If leastGeneric IsNot Nothing Then
                                    candidateCount -= 1
                                    If candidateCount = 1 Then
                                        Return leastGeneric
                                    End If
                                    If leastGeneric Is left Then
                                        right.NotCallable = True
                                    Else
                                        left.NotCallable = True
                                        Exit For
                                    End If
                                End If
                            End If
                        End If
 
                    Next
 
                End If
            Next
 
            Return Nothing
        End Function
 
 
        Private Shared Sub ReportError(
            ByVal errors As List(Of String),
            ByVal resourceID As String,
            ByVal substitution1 As String,
            ByVal substitution2 As Type,
            ByVal substitution3 As Type)
 
            Debug.Assert(errors IsNot Nothing, "expected error table")
            errors.Add(
                SR.Format(
                    resourceID,
                    substitution1,
                    VBFriendlyName(substitution2),
                    VBFriendlyName(substitution3)))
        End Sub
 
        Private Shared Sub ReportError(
            ByVal errors As List(Of String),
            ByVal resourceID As String,
            ByVal substitution1 As String,
            ByVal substitution2 As Method)
 
            Debug.Assert(errors IsNot Nothing, "expected error table")
            errors.Add(
                SR.Format(
                    resourceID,
                    substitution1,
                    substitution2.ToString))
        End Sub
 
        Private Shared Sub ReportError(
            ByVal errors As List(Of String),
            ByVal resourceID As String,
            ByVal substitution1 As String)
 
            Debug.Assert(errors IsNot Nothing, "expected error table")
            errors.Add(
                SR.Format(
                    resourceID,
                    substitution1))
        End Sub
 
        Private Shared Sub ReportError(ByVal errors As List(Of String), ByVal resourceID As String)
 
            Debug.Assert(errors IsNot Nothing, "expected error table")
            errors.Add(resourceID)
        End Sub
 
        Private Delegate Function ArgumentDetector(
            ByVal targetProcedure As Method,
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal errors As List(Of String)) As Boolean
 
        Private Delegate Function CandidateProperty(ByVal candidate As Method) As Boolean
 
        Private Shared Function ReportOverloadResolutionFailure(
            ByVal overloadedProcedureName As String,
            ByVal candidates As List(Of Method),
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal errorID As String,
            ByVal failure As ResolutionFailure,
            ByVal detector As ArgumentDetector,
            ByVal candidateFilter As CandidateProperty) As Exception
 
            Dim errorMessage As StringBuilder = New StringBuilder
            Dim errors As New List(Of String)
            Dim candidateReportCount As Integer = 0
 
            For index As Integer = 0 To candidates.Count - 1
 
                Dim candidateProcedure As Method = candidates(index)
 
                If candidateFilter(candidateProcedure) Then
 
                    If candidateProcedure.HasParamArray Then
                        'We may have two versions of paramarray methods in the list. So skip the first
                        'one (the unexpanded one). However, we don't want to skip the unexpanded form
                        'if the expanded form will fail the filter.
                        Dim indexAhead As Integer = index + 1
                        While indexAhead < candidates.Count
                            If candidateFilter(candidates(indexAhead)) AndAlso
                               candidates(indexAhead) = candidateProcedure Then
                                Continue For
                            End If
                            indexAhead += 1
                        End While
                    End If
 
                    candidateReportCount += 1
 
                    errors.Clear()
                    Dim result As Boolean =
                        detector(candidateProcedure, arguments, argumentNames, typeArguments, errors)
                    Debug.Assert(result = False AndAlso errors.Count > 0, "expected this candidate to fail")
 
                    errorMessage.Append(vbCrLf & "    '")
                    errorMessage.Append(candidateProcedure.ToString)
                    errorMessage.Append("':")
                    For Each errorString As String In errors
                        errorMessage.Append(vbCrLf & "        ")
                        errorMessage.Append(errorString)
                    Next
                End If
 
            Next
 
            Debug.Assert(candidateReportCount > 0, "expected at least one candidate")
 
            Dim message As String = SR.Format(errorID, overloadedProcedureName, errorMessage.ToString)
            If candidateReportCount = 1 Then
                'ParamArrays may cause only one candidate to get reported. In this case, reporting an
                'ambiguity is misleading.
                'Using the same error message for the single-candidate case
                'is also misleading, but the benefit is not high enough for constructing a better message.
                'InvalidCastException is thrown only for back compat.  It would
                'be nice if the latebinder had its own set of exceptions to throw.
                Return New InvalidCastException(message)
            Else
                Return New AmbiguousMatchException(message)
            End If
        End Function
 
        <RequiresUnreferencedCode("Calls CanMatchArguments")>
        Private Shared Function DetectArgumentErrors(
            ByVal targetProcedure As Method,
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal errors As List(Of String)) As Boolean
 
            Return CanMatchArguments(
                    targetProcedure,
                    arguments,
                    argumentNames,
                    typeArguments,
                    False,
                    errors)
        End Function
 
        Private Shared Function CandidateIsNotCallable(ByVal candidate As Method) As Boolean
            Return candidate.NotCallable
        End Function
 
        <RequiresUnreferencedCode("Calls ReportOverloadResolutionFailure")>
        Private Shared Function ReportUncallableProcedures(
            ByVal overloadedProcedureName As String,
            ByVal candidates As List(Of Method),
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal failure As ResolutionFailure) As Exception
 
            Return ReportOverloadResolutionFailure(
                    overloadedProcedureName,
                    candidates,
                    arguments,
                    argumentNames,
                    typeArguments,
                    SR.NoCallableOverloadCandidates2,
                    failure,
                    AddressOf DetectArgumentErrors,
                    AddressOf CandidateIsNotCallable)
        End Function
 
        <RequiresUnreferencedCode("Calls CanMatchArguments")>
        Private Shared Function DetectArgumentNarrowing(
            ByVal targetProcedure As Method,
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal errors As List(Of String)) As Boolean
 
            Return CanMatchArguments(
                    targetProcedure,
                    arguments,
                    argumentNames,
                    typeArguments,
                    True,
                    errors)
        End Function
 
        Private Shared Function CandidateIsNarrowing(ByVal candidate As Method) As Boolean
            Return Not candidate.NotCallable AndAlso candidate.RequiresNarrowingConversion
        End Function
 
        <RequiresUnreferencedCode("Calls ReportOverloadResolutionFailure")>
        Private Shared Function ReportNarrowingProcedures(
            ByVal overloadedProcedureName As String,
            ByVal candidates As List(Of Method),
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal failure As ResolutionFailure) As Exception
 
            Return ReportOverloadResolutionFailure(
                    overloadedProcedureName,
                    candidates,
                    arguments,
                    argumentNames,
                    typeArguments,
                    SR.NoNonNarrowingOverloadCandidates2,
                    failure,
                    AddressOf DetectArgumentNarrowing,
                    AddressOf CandidateIsNarrowing)
        End Function
 
        Private Shared Function DetectUnspecificity(
            ByVal targetProcedure As Method,
            ByVal arguments As Object(),
            ByVal argumentNames As String(),
            ByVal typeArguments As Type(),
            ByVal errors As List(Of String)) As Boolean
 
            ReportError(errors, SR.NotMostSpecificOverload)
            Return False
        End Function
 
        Private Shared Function CandidateIsUnspecific(ByVal candidate As Method) As Boolean
            Return Not candidate.NotCallable AndAlso Not candidate.RequiresNarrowingConversion AndAlso Not candidate.LessSpecific
        End Function
 
        Private Shared Function ReportUnspecificProcedures(
            ByVal overloadedProcedureName As String,
            ByVal candidates As List(Of Method),
            ByVal failure As ResolutionFailure) As Exception
 
            Return ReportOverloadResolutionFailure(
                    overloadedProcedureName,
                    candidates,
                    Nothing,
                    Nothing,
                    Nothing,
                    SR.NoMostSpecificOverload2,
                    failure,
                    AddressOf DetectUnspecificity,
                    AddressOf CandidateIsUnspecific)
        End Function
 
        <RequiresUnreferencedCode("Calls MostSpecificProcedure and RemoveRedundantGenericProcedures")>
        Friend Shared Function ResolveOverloadedCall(
                ByVal methodName As String,
                ByVal candidates As List(Of Method),
                ByVal arguments As Object(),
                ByVal argumentNames As String(),
                ByVal typeArguments As Type(),
                ByVal lookupFlags As BindingFlags,
                ByVal reportErrors As Boolean,
                ByRef failure As ResolutionFailure) As Method
 
            'Optimistically hope to succeed.
            failure = ResolutionFailure.None
 
            'From here on, CandidateCount will be used to keep track of the
            'number of remaining viable Candidates in the list.
            Dim candidateCount As Integer = candidates.Count
            Dim someCandidatesAreGeneric As Boolean = False
 
            Dim best As Method =
                RejectUncallableProcedures(
                    candidates,
                    arguments,
                    argumentNames,
                    typeArguments,
                    candidateCount,
                    someCandidatesAreGeneric)
 
            If candidateCount = 1 Then
                Return best
            End If
 
            If candidateCount = 0 Then
                failure = ResolutionFailure.InvalidArgument
                If reportErrors Then
                    Throw ReportUncallableProcedures(methodName, candidates, arguments, argumentNames, typeArguments, failure)
                End If
                Return Nothing
            End If
 
            If someCandidatesAreGeneric Then
                best = RemoveRedundantGenericProcedures(candidates, candidateCount, arguments, argumentNames)
                If candidateCount = 1 Then
                    Return best
                End If
            End If
 
            'See if only one does not require narrowing.  If all candidates require narrowing,
            'but one does so only from Object, pick that candidate.
 
            Dim narrowOnlyFromObjectCount As Integer = 0
            Dim bestNarrowingCandidate As Method = Nothing
 
            For Each candidate As Method In candidates
 
                If Not candidate.NotCallable Then
                    If candidate.RequiresNarrowingConversion Then
 
                        candidateCount -= 1
 
                        If candidate.AllNarrowingIsFromObject Then
                            narrowOnlyFromObjectCount += 1
                            bestNarrowingCandidate = candidate
                        End If
                    Else
                        best = candidate
                    End If
                End If
 
            Next
 
            If candidateCount = 1 Then
                Return best
            End If
 
            If candidateCount = 0 Then
                If narrowOnlyFromObjectCount = 1 Then
                    Return bestNarrowingCandidate
                End If
 
                failure = ResolutionFailure.AmbiguousMatch
                If reportErrors Then
                    Throw ReportNarrowingProcedures(methodName, candidates, arguments, argumentNames, typeArguments, failure)
                End If
                Return Nothing
            End If
 
            best = MostSpecificProcedure(candidates, candidateCount, arguments, argumentNames)
 
            If best IsNot Nothing Then
                Return best
            End If
 
            failure = ResolutionFailure.AmbiguousMatch
            If reportErrors Then
                Throw ReportUnspecificProcedures(methodName, candidates, failure)
            End If
            Return Nothing
        End Function
 
        <RequiresUnreferencedCode("Calls ResolveOverloadedCall")>
        Friend Shared Function ResolveOverloadedCall(
                ByVal methodName As String,
                ByVal members As MemberInfo(),
                ByVal arguments As Object(),
                ByVal argumentNames As String(),
                ByVal typeArguments As Type(),
                ByVal lookupFlags As BindingFlags,
                ByVal reportErrors As Boolean,
                ByRef failure As ResolutionFailure,
                ByVal baseReference As Container) As Method
 
#If BINDING_LOG Then
            Console.WriteLine("== MEMBERS ==")
            For Each m As MemberInfo In Members
                Console.WriteLine(MemberToString(m))
            Next
#End If
 
            'Build the list of candidate Methods, one of which overload resolution will
            'select.
            Dim rejectedForArgumentCount As Integer = 0
            Dim rejectedForTypeArgumentCount As Integer = 0
 
            Dim candidates As List(Of Method) =
                CollectOverloadCandidates(
                    members,
                    arguments,
                    arguments.Length,
                    argumentNames,
                    typeArguments,
                    False,
                    Nothing,
                    rejectedForArgumentCount,
                    rejectedForTypeArgumentCount,
                    baseReference)
 
            ' If there is only one candidate and it is NotCallable, let ResolveOverloadedCall
            ' figure out the error message and exception.
            If candidates.Count = 1 AndAlso Not candidates.Item(0).NotCallable Then
                Return candidates.Item(0)
            End If
 
            If candidates.Count = 0 Then
                failure = ResolutionFailure.MissingMember
 
                If reportErrors Then
                    Dim errorID As String = SR.NoViableOverloadCandidates1
 
                    If rejectedForArgumentCount > 0 Then
                        errorID = SR.NoArgumentCountOverloadCandidates1
                    ElseIf rejectedForTypeArgumentCount > 0 Then
                        errorID = SR.NoTypeArgumentCountOverloadCandidates1
                    End If
                    Throw New MissingMemberException(SR.Format(errorID, methodName))
                End If
                Return Nothing
            End If
 
            Return ResolveOverloadedCall(
                    methodName,
                    candidates,
                    arguments,
                    argumentNames,
                    typeArguments,
                    lookupFlags,
                    reportErrors,
                    failure)
 
        End Function
 
    End Class
 
End Namespace