|
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Diagnostics
Imports System.Linq
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
Imports ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer
Namespace Microsoft.CodeAnalysis.VisualBasic
Friend NotInheritable Class OverloadResolution
Private Sub New()
Throw ExceptionUtilities.Unreachable
End Sub
''' <summary>
''' Information about a candidate from a group.
''' Will have different implementation for methods, extension methods and properties.
''' </summary>
''' <remarks></remarks>
Public MustInherit Class Candidate
Public MustOverride ReadOnly Property UnderlyingSymbol As Symbol
Friend MustOverride Function Construct(typeArguments As ImmutableArray(Of TypeSymbol)) As Candidate
''' <summary>
''' Whether the method is used as extension method vs. called as a static method.
''' </summary>
Public Overridable ReadOnly Property IsExtensionMethod As Boolean
Get
Return False
End Get
End Property
''' <summary>
''' Whether the method is used as an operator.
''' </summary>
Public Overridable ReadOnly Property IsOperator As Boolean
Get
Return False
End Get
End Property
''' <summary>
''' Whether the method is used in a lifted to nullable form.
''' </summary>
Public Overridable ReadOnly Property IsLifted As Boolean
Get
Return False
End Get
End Property
''' <summary>
''' Precedence level for an extension method.
''' </summary>
Public Overridable ReadOnly Property PrecedenceLevel As Integer
Get
Return 0
End Get
End Property
''' <summary>
''' Extension method type parameters that were fixed during currying, if any.
''' If none were fixed, BitArray.Null should be returned.
''' </summary>
Public Overridable ReadOnly Property FixedTypeParameters As BitVector
Get
Return BitVector.Null
End Get
End Property
Public MustOverride ReadOnly Property IsGeneric As Boolean
Public MustOverride ReadOnly Property ParameterCount As Integer
Public MustOverride Function Parameters(index As Integer) As ParameterSymbol
Public MustOverride ReadOnly Property ReturnType As TypeSymbol
Public MustOverride ReadOnly Property Arity As Integer
Public MustOverride ReadOnly Property TypeParameters As ImmutableArray(Of TypeParameterSymbol)
Friend Sub GetAllParameterCounts(
ByRef requiredCount As Integer,
ByRef maxCount As Integer,
ByRef hasParamArray As Boolean
)
maxCount = Me.ParameterCount
hasParamArray = False
requiredCount = -1
Dim last = maxCount - 1
For i As Integer = 0 To last Step 1
Dim param As ParameterSymbol = Me.Parameters(i)
If i = last AndAlso param.IsParamArray Then
hasParamArray = True
ElseIf Not param.IsOptional Then
requiredCount = i
End If
Next
requiredCount += 1
End Sub
Friend Function TryGetNamedParamIndex(name As String, ByRef index As Integer) As Boolean
For i As Integer = 0 To Me.ParameterCount - 1 Step 1
Dim param As ParameterSymbol = Me.Parameters(i)
If IdentifierComparison.Equals(name, param.Name) Then
index = i
Return True
End If
Next
index = -1
Return False
End Function
''' <summary>
''' Receiver type for extension method. Otherwise, containing type.
''' </summary>
Public MustOverride ReadOnly Property ReceiverType As TypeSymbol
''' <summary>
''' For extension methods, the type of the fist parameter in method's definition (i.e. before type parameters are substituted).
''' Otherwise, same as the ReceiverType.
''' </summary>
Public MustOverride ReadOnly Property ReceiverTypeDefinition As TypeSymbol
Friend MustOverride Function IsOverriddenBy(otherSymbol As Symbol) As Boolean
End Class
''' <summary>
''' Implementation for an ordinary method (based on usage).
''' </summary>
Public Class MethodCandidate
Inherits Candidate
Protected ReadOnly m_Method As MethodSymbol
Public Sub New(method As MethodSymbol)
Debug.Assert(method IsNot Nothing)
Debug.Assert(method.ReducedFrom Is Nothing OrElse Me.IsExtensionMethod)
m_Method = method
End Sub
Friend Overrides Function Construct(typeArguments As ImmutableArray(Of TypeSymbol)) As Candidate
Return New MethodCandidate(m_Method.Construct(typeArguments))
End Function
Public Overrides ReadOnly Property IsGeneric As Boolean
Get
Return m_Method.IsGenericMethod
End Get
End Property
Public Overrides ReadOnly Property ParameterCount As Integer
Get
Return m_Method.ParameterCount
End Get
End Property
Public Overrides Function Parameters(index As Integer) As ParameterSymbol
Return m_Method.Parameters(index)
End Function
Public Overrides ReadOnly Property ReturnType As TypeSymbol
Get
Return m_Method.ReturnType
End Get
End Property
Public Overrides ReadOnly Property ReceiverType As TypeSymbol
Get
Return m_Method.ContainingType
End Get
End Property
Public Overrides ReadOnly Property ReceiverTypeDefinition As TypeSymbol
Get
Return m_Method.ContainingType
End Get
End Property
Public Overrides ReadOnly Property Arity As Integer
Get
Return m_Method.Arity
End Get
End Property
Public Overrides ReadOnly Property TypeParameters As ImmutableArray(Of TypeParameterSymbol)
Get
Return m_Method.TypeParameters
End Get
End Property
Public Overrides ReadOnly Property UnderlyingSymbol As Symbol
Get
Return m_Method
End Get
End Property
Friend Overrides Function IsOverriddenBy(otherSymbol As Symbol) As Boolean
Dim definition As MethodSymbol = m_Method.OriginalDefinition
If definition.IsOverridable OrElse definition.IsOverrides OrElse definition.IsMustOverride Then
Dim otherMethod As MethodSymbol = DirectCast(otherSymbol, MethodSymbol).OverriddenMethod
While otherMethod IsNot Nothing
If otherMethod.OriginalDefinition.Equals(definition) Then
Return True
End If
otherMethod = otherMethod.OverriddenMethod
End While
End If
Return False
End Function
End Class
''' <summary>
''' Implementation for an extension method, i.e. it is used as an extension method.
''' </summary>
Public NotInheritable Class ExtensionMethodCandidate
Inherits MethodCandidate
Private _fixedTypeParameters As BitVector
Public Sub New(method As MethodSymbol)
Me.New(method, GetFixedTypeParameters(method))
End Sub
' TODO: Consider building this bitmap lazily, on demand.
Private Shared Function GetFixedTypeParameters(method As MethodSymbol) As BitVector
If method.FixedTypeParameters.Length > 0 Then
Dim fixedTypeParameters = BitVector.Create(method.ReducedFrom.Arity)
For Each fixed As KeyValuePair(Of TypeParameterSymbol, TypeSymbol) In method.FixedTypeParameters
fixedTypeParameters(fixed.Key.Ordinal) = True
Next
Return fixedTypeParameters
End If
Return Nothing
End Function
Private Sub New(method As MethodSymbol, fixedTypeParameters As BitVector)
MyBase.New(method)
Debug.Assert(method.ReducedFrom IsNot Nothing)
_fixedTypeParameters = fixedTypeParameters
End Sub
Public Overrides ReadOnly Property IsExtensionMethod As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property PrecedenceLevel As Integer
Get
Return m_Method.Proximity
End Get
End Property
Public Overrides ReadOnly Property FixedTypeParameters As BitVector
Get
Return _fixedTypeParameters
End Get
End Property
Friend Overrides Function Construct(typeArguments As ImmutableArray(Of TypeSymbol)) As Candidate
Return New ExtensionMethodCandidate(m_Method.Construct(typeArguments), _fixedTypeParameters)
End Function
Public Overrides ReadOnly Property ReceiverType As TypeSymbol
Get
Return m_Method.ReceiverType
End Get
End Property
Public Overrides ReadOnly Property ReceiverTypeDefinition As TypeSymbol
Get
Return m_Method.ReducedFrom.Parameters(0).Type
End Get
End Property
Friend Overrides Function IsOverriddenBy(otherSymbol As Symbol) As Boolean
Return False ' Extension methods never override/overridden
End Function
End Class
''' <summary>
''' Implementation for an operator
''' </summary>
Public Class OperatorCandidate
Inherits MethodCandidate
Public Sub New(method As MethodSymbol)
MyBase.New(method)
End Sub
Public NotOverridable Overrides ReadOnly Property IsOperator As Boolean
Get
Return True
End Get
End Property
End Class
''' <summary>
''' Implementation for a lifted operator.
''' </summary>
Public Class LiftedOperatorCandidate
Inherits OperatorCandidate
Private ReadOnly _parameters As ImmutableArray(Of ParameterSymbol)
Private ReadOnly _returnType As TypeSymbol
Public Sub New(method As MethodSymbol, parameters As ImmutableArray(Of ParameterSymbol), returnType As TypeSymbol)
MyBase.New(method)
Debug.Assert(parameters.Length = method.ParameterCount)
_parameters = parameters
_returnType = returnType
End Sub
Public Overrides ReadOnly Property ParameterCount As Integer
Get
Return _parameters.Length
End Get
End Property
Public Overrides Function Parameters(index As Integer) As ParameterSymbol
Return _parameters(index)
End Function
Public Overrides ReadOnly Property ReturnType As TypeSymbol
Get
Return _returnType
End Get
End Property
Public Overrides ReadOnly Property IsLifted As Boolean
Get
Return True
End Get
End Property
End Class
''' <summary>
''' Implementation for a property.
''' </summary>
Public NotInheritable Class PropertyCandidate
Inherits Candidate
Private ReadOnly _property As PropertySymbol
Public Sub New([property] As PropertySymbol)
Debug.Assert([property] IsNot Nothing)
_property = [property]
End Sub
Friend Overrides Function Construct(typeArguments As ImmutableArray(Of TypeSymbol)) As Candidate
Throw ExceptionUtilities.Unreachable
End Function
Public Overrides ReadOnly Property IsGeneric As Boolean
Get
Return False
End Get
End Property
Public Overrides ReadOnly Property ParameterCount As Integer
Get
Return _property.Parameters.Length
End Get
End Property
Public Overrides Function Parameters(index As Integer) As ParameterSymbol
Return _property.Parameters(index)
End Function
Public Overrides ReadOnly Property ReturnType As TypeSymbol
Get
Return _property.Type
End Get
End Property
Public Overrides ReadOnly Property ReceiverType As TypeSymbol
Get
Return _property.ContainingType
End Get
End Property
Public Overrides ReadOnly Property ReceiverTypeDefinition As TypeSymbol
Get
Return _property.ContainingType
End Get
End Property
Public Overrides ReadOnly Property Arity As Integer
Get
Return 0
End Get
End Property
Public Overrides ReadOnly Property TypeParameters As ImmutableArray(Of TypeParameterSymbol)
Get
Return ImmutableArray(Of TypeParameterSymbol).Empty
End Get
End Property
Public Overrides ReadOnly Property UnderlyingSymbol As Symbol
Get
Return _property
End Get
End Property
Friend Overrides Function IsOverriddenBy(otherSymbol As Symbol) As Boolean
Dim definition As PropertySymbol = _property.OriginalDefinition
If definition.IsOverridable OrElse definition.IsOverrides OrElse definition.IsMustOverride Then
Dim otherProperty As PropertySymbol = DirectCast(otherSymbol, PropertySymbol).OverriddenProperty
While otherProperty IsNot Nothing
If otherProperty.OriginalDefinition.Equals(definition) Then
Return True
End If
otherProperty = otherProperty.OverriddenProperty
End While
End If
Return False
End Function
End Class
Private Const s_stateSize = 8 ' bit size of the following enum
Public Enum CandidateAnalysisResultState As Byte
Applicable
' All following states are to indicate inapplicability
HasUnsupportedMetadata
HasUseSiteError
Ambiguous
BadGenericArity
ArgumentCountMismatch
TypeInferenceFailed
ArgumentMismatch
GenericConstraintsViolated
RequiresNarrowing
RequiresNarrowingNotFromObject
ExtensionMethodVsInstanceMethod
Shadowed
LessApplicable
ExtensionMethodVsLateBinding
Count
End Enum
<Flags()>
Private Enum SmallFieldMask As Integer
State = (1 << s_stateSize) - 1
IsExpandedParamArrayForm = 1 << (s_stateSize + 0)
InferenceLevelShift = (s_stateSize + 1)
InferenceLevelMask = 3 << InferenceLevelShift ' 2 bits are used
ArgumentMatchingDone = 1 << (s_stateSize + 3)
RequiresNarrowingConversion = 1 << (s_stateSize + 4)
RequiresNarrowingNotFromObject = 1 << (s_stateSize + 5)
RequiresNarrowingNotFromNumericConstant = 1 << (s_stateSize + 6)
' Must be equal to ConversionKind.DelegateRelaxationLevelMask
' Compile time "asserts" below enforce it by reporting a compilation error in case of a violation.
' I am not using the form of
' DelegateRelaxationLevelMask = ConversionKind.DelegateRelaxationLevelMask
' to make it easier to reason about bits used relative to other values in this enum.
DelegateRelaxationLevelMask = 7 << (s_stateSize + 7) ' 3 bits used!
SomeInferenceFailed = 1 << (s_stateSize + 10)
AllFailedInferenceIsDueToObject = 1 << (s_stateSize + 11)
InferenceErrorReasonsShift = (s_stateSize + 12)
InferenceErrorReasonsMask = 3 << InferenceErrorReasonsShift
IgnoreExtensionMethods = 1 << (s_stateSize + 14)
IllegalInAttribute = 1 << (s_stateSize + 15)
End Enum
#If DEBUG Then
' Compile time asserts.
#Disable Warning IDE0051 ' Remove unused private members
Private Const s_delegateRelaxationLevelMask_AssertZero = SmallFieldMask.DelegateRelaxationLevelMask - ConversionKind.DelegateRelaxationLevelMask
Private ReadOnly _delegateRelaxationLevelMask_Assert1(s_delegateRelaxationLevelMask_AssertZero) As Boolean
Private ReadOnly _delegateRelaxationLevelMask_Assert2(-s_delegateRelaxationLevelMask_AssertZero) As Boolean
Private Const s_inferenceLevelMask_AssertZero = CByte((SmallFieldMask.InferenceLevelMask >> SmallFieldMask.InferenceLevelShift) <> ((TypeArgumentInference.InferenceLevel.Invalid << 1) - 1))
Private ReadOnly _inferenceLevelMask_Assert1(s_inferenceLevelMask_AssertZero) As Boolean
Private ReadOnly _inferenceLevelMask_Assert2(-s_inferenceLevelMask_AssertZero) As Boolean
#Enable Warning IDE0051 ' Remove unused private members
#End If
Public Structure OptionalArgument
Public ReadOnly DefaultValue As BoundExpression
Public ReadOnly Conversion As KeyValuePair(Of ConversionKind, MethodSymbol)
Public ReadOnly Dependencies As ImmutableArray(Of AssemblySymbol)
Public Sub New(value As BoundExpression, conversion As KeyValuePair(Of ConversionKind, MethodSymbol), dependencies As ImmutableArray(Of AssemblySymbol))
Me.DefaultValue = value
Me.Conversion = conversion
Me.Dependencies = dependencies.NullToEmpty()
End Sub
End Structure
Public Structure CandidateAnalysisResult
Public ReadOnly Property IsExpandedParamArrayForm As Boolean
Get
Return (_smallFields And SmallFieldMask.IsExpandedParamArrayForm) <> 0
End Get
End Property
Public Sub SetIsExpandedParamArrayForm()
_smallFields = _smallFields Or SmallFieldMask.IsExpandedParamArrayForm
End Sub
Public ReadOnly Property InferenceLevel As TypeArgumentInference.InferenceLevel
Get
Return CType((_smallFields And SmallFieldMask.InferenceLevelMask) >> SmallFieldMask.InferenceLevelShift, TypeArgumentInference.InferenceLevel)
End Get
End Property
Public Sub SetInferenceLevel(level As TypeArgumentInference.InferenceLevel)
Dim value As Integer = CInt(level) << SmallFieldMask.InferenceLevelShift
Debug.Assert((value And SmallFieldMask.InferenceLevelMask) = value)
_smallFields = (_smallFields And (Not SmallFieldMask.InferenceLevelMask)) Or (value And SmallFieldMask.InferenceLevelMask)
End Sub
Public ReadOnly Property ArgumentMatchingDone As Boolean
Get
Return (_smallFields And SmallFieldMask.ArgumentMatchingDone) <> 0
End Get
End Property
Public Sub SetArgumentMatchingDone()
_smallFields = _smallFields Or SmallFieldMask.ArgumentMatchingDone
End Sub
Public ReadOnly Property RequiresNarrowingConversion As Boolean
Get
Return (_smallFields And SmallFieldMask.RequiresNarrowingConversion) <> 0
End Get
End Property
Public Sub SetRequiresNarrowingConversion()
_smallFields = _smallFields Or SmallFieldMask.RequiresNarrowingConversion
End Sub
Public ReadOnly Property RequiresNarrowingNotFromObject As Boolean
Get
Return (_smallFields And SmallFieldMask.RequiresNarrowingNotFromObject) <> 0
End Get
End Property
Public Sub SetRequiresNarrowingNotFromObject()
_smallFields = _smallFields Or SmallFieldMask.RequiresNarrowingNotFromObject
End Sub
Public ReadOnly Property RequiresNarrowingNotFromNumericConstant As Boolean
Get
Return (_smallFields And SmallFieldMask.RequiresNarrowingNotFromNumericConstant) <> 0
End Get
End Property
Public Sub SetRequiresNarrowingNotFromNumericConstant()
Debug.Assert(RequiresNarrowingConversion)
IgnoreExtensionMethods = False
_smallFields = _smallFields Or SmallFieldMask.RequiresNarrowingNotFromNumericConstant
End Sub
''' <summary>
''' Only bits specific to delegate relaxation level are returned.
''' </summary>
Public ReadOnly Property MaxDelegateRelaxationLevel As ConversionKind
Get
Return CType(_smallFields And SmallFieldMask.DelegateRelaxationLevelMask, ConversionKind)
End Get
End Property
Public Sub RegisterDelegateRelaxationLevel(conversionKind As ConversionKind)
Dim relaxationLevel As Integer = (conversionKind And SmallFieldMask.DelegateRelaxationLevelMask)
If relaxationLevel > (_smallFields And SmallFieldMask.DelegateRelaxationLevelMask) Then
Debug.Assert(relaxationLevel <= ConversionKind.DelegateRelaxationLevelNarrowing)
If relaxationLevel = ConversionKind.DelegateRelaxationLevelNarrowing Then
IgnoreExtensionMethods = False
End If
_smallFields = (_smallFields And (Not SmallFieldMask.DelegateRelaxationLevelMask)) Or relaxationLevel
End If
End Sub
Public Sub SetSomeInferenceFailed()
_smallFields = _smallFields Or SmallFieldMask.SomeInferenceFailed
End Sub
Public ReadOnly Property SomeInferenceFailed As Boolean
Get
Return (_smallFields And SmallFieldMask.SomeInferenceFailed) <> 0
End Get
End Property
Public Sub SetIllegalInAttribute()
_smallFields = _smallFields Or SmallFieldMask.IllegalInAttribute
End Sub
Public ReadOnly Property IsIllegalInAttribute As Boolean
Get
Return (_smallFields And SmallFieldMask.IllegalInAttribute) <> 0
End Get
End Property
Public Sub SetAllFailedInferenceIsDueToObject()
_smallFields = _smallFields Or SmallFieldMask.AllFailedInferenceIsDueToObject
End Sub
Public ReadOnly Property AllFailedInferenceIsDueToObject As Boolean
Get
Return (_smallFields And SmallFieldMask.AllFailedInferenceIsDueToObject) <> 0
End Get
End Property
Public Sub SetInferenceErrorReasons(reasons As InferenceErrorReasons)
Dim value As Integer = CInt(reasons) << SmallFieldMask.InferenceErrorReasonsShift
Debug.Assert((value And SmallFieldMask.InferenceErrorReasonsMask) = value)
_smallFields = (_smallFields And (Not SmallFieldMask.InferenceErrorReasonsMask)) Or (value And SmallFieldMask.InferenceErrorReasonsMask)
End Sub
Public ReadOnly Property InferenceErrorReasons As InferenceErrorReasons
Get
Return CType((_smallFields And SmallFieldMask.InferenceErrorReasonsMask) >> SmallFieldMask.InferenceErrorReasonsShift, InferenceErrorReasons)
End Get
End Property
Public Property State As CandidateAnalysisResultState
Get
Return CType(_smallFields And SmallFieldMask.State, CandidateAnalysisResultState)
End Get
Set(value As CandidateAnalysisResultState)
Debug.Assert((value And (Not SmallFieldMask.State)) = 0)
Dim newFields = _smallFields And (Not SmallFieldMask.State)
newFields = newFields Or value
_smallFields = newFields
End Set
End Property
Public Property IgnoreExtensionMethods As Boolean
Get
Return (_smallFields And SmallFieldMask.IgnoreExtensionMethods) <> 0
End Get
Set(value As Boolean)
If value Then
_smallFields = _smallFields Or SmallFieldMask.IgnoreExtensionMethods
Else
_smallFields = _smallFields And (Not SmallFieldMask.IgnoreExtensionMethods)
End If
End Set
End Property
Private _smallFields As Integer
Public Candidate As Candidate
Public ExpandedParamArrayArgumentsUsed As Integer
Public EquallyApplicableCandidatesBucket As Integer
' When this is null, it means that arguments map to parameters sequentially
Public ArgsToParamsOpt As ImmutableArray(Of Integer)
' When these are null, it means that all conversions are identity conversions
Public ConversionsOpt As ImmutableArray(Of KeyValuePair(Of ConversionKind, MethodSymbol))
Public ConversionsBackOpt As ImmutableArray(Of KeyValuePair(Of ConversionKind, MethodSymbol))
' When this is null, it means that there aren't any optional arguments
' This array is indexed by parameter index, not the argument index.
Public OptionalArguments As ImmutableArray(Of OptionalArgument)
Public ReadOnly Property UsedOptionalParameterDefaultValue As Boolean
Get
Return Not OptionalArguments.IsDefault
End Get
End Property
Public NotInferredTypeArguments As BitVector
Public TypeArgumentInferenceDiagnosticsOpt As ReadOnlyBindingDiagnostic(Of AssemblySymbol)
Public Sub New(candidate As Candidate, state As CandidateAnalysisResultState)
Me.Candidate = candidate
Me.State = state
End Sub
Public Sub New(candidate As Candidate)
Me.Candidate = candidate
Me.State = CandidateAnalysisResultState.Applicable
End Sub
End Structure
' Represents a simple overload resolution result
Friend Structure OverloadResolutionResult
Private ReadOnly _bestResult As CandidateAnalysisResult?
Private ReadOnly _allResults As ImmutableArray(Of CandidateAnalysisResult)
Private ReadOnly _resolutionIsLateBound As Boolean
Private ReadOnly _remainingCandidatesRequireNarrowingConversion As Boolean
Public ReadOnly AsyncLambdaSubToFunctionMismatch As ImmutableArray(Of BoundExpression)
' Create an overload resolution result from a full set of results.
Public Sub New(allResults As ImmutableArray(Of CandidateAnalysisResult), resolutionIsLateBound As Boolean,
remainingCandidatesRequireNarrowingConversion As Boolean,
asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression))
Me._allResults = allResults
Me._resolutionIsLateBound = resolutionIsLateBound
Me._remainingCandidatesRequireNarrowingConversion = remainingCandidatesRequireNarrowingConversion
Me.AsyncLambdaSubToFunctionMismatch = If(asyncLambdaSubToFunctionMismatch Is Nothing,
ImmutableArray(Of BoundExpression).Empty,
asyncLambdaSubToFunctionMismatch.ToArray().AsImmutableOrNull())
If Not resolutionIsLateBound Then
Me._bestResult = GetBestResult(allResults)
End If
End Sub
Public ReadOnly Property Candidates As ImmutableArray(Of CandidateAnalysisResult)
Get
Return _allResults
End Get
End Property
' Returns the best method. Note that if overload resolution succeeded, the set of conversion kinds will NOT be returned.
Public ReadOnly Property BestResult As CandidateAnalysisResult?
Get
Return _bestResult
End Get
End Property
Public ReadOnly Property ResolutionIsLateBound As Boolean
Get
Return _resolutionIsLateBound
End Get
End Property
''' <summary>
''' This might simplify error reporting. If not, consider getting rid of this property.
''' </summary>
Public ReadOnly Property RemainingCandidatesRequireNarrowingConversion As Boolean
Get
Return _remainingCandidatesRequireNarrowingConversion
End Get
End Property
Private Shared Function GetBestResult(allResults As ImmutableArray(Of CandidateAnalysisResult)) As CandidateAnalysisResult?
Dim best As CandidateAnalysisResult? = Nothing
Dim i As Integer = 0
While i < allResults.Length
Dim current = allResults(i)
If current.State = CandidateAnalysisResultState.Applicable Then
If best IsNot Nothing Then
Return Nothing
End If
best = current
End If
i = i + 1
End While
Return best
End Function
End Structure
''' <summary>
''' Perform overload resolution on the given method or property group, with the given arguments and names.
''' The names can be null if no names were supplied to any arguments.
''' </summary>
Public Shared Function MethodOrPropertyInvocationOverloadResolution(
group As BoundMethodOrPropertyGroup,
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
binder As Binder,
callerInfoOpt As SyntaxNode,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
Optional includeEliminatedCandidates As Boolean = False,
Optional forceExpandedForm As Boolean = False
) As OverloadResolutionResult
If group.Kind = BoundKind.MethodGroup Then
Dim methodGroup = DirectCast(group, BoundMethodGroup)
Return MethodInvocationOverloadResolution(
methodGroup,
arguments,
argumentNames,
binder,
callerInfoOpt,
useSiteInfo,
includeEliminatedCandidates,
forceExpandedForm:=forceExpandedForm)
Else
Dim propertyGroup = DirectCast(group, BoundPropertyGroup)
Return PropertyInvocationOverloadResolution(
propertyGroup,
arguments,
argumentNames,
binder,
callerInfoOpt,
useSiteInfo,
includeEliminatedCandidates)
End If
End Function
''' <summary>
''' Perform overload resolution on the given method group, with the given arguments.
''' </summary>
Public Shared Function QueryOperatorInvocationOverloadResolution(
methodGroup As BoundMethodGroup,
arguments As ImmutableArray(Of BoundExpression),
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
Optional includeEliminatedCandidates As Boolean = False
) As OverloadResolutionResult
Return MethodInvocationOverloadResolution(
methodGroup,
arguments,
Nothing,
binder,
callerInfoOpt:=Nothing,
useSiteInfo:=useSiteInfo,
includeEliminatedCandidates:=includeEliminatedCandidates,
lateBindingIsAllowed:=False,
isQueryOperatorInvocation:=True)
End Function
''' <summary>
''' Perform overload resolution on the given method group, with the given arguments and names.
''' The names can be null if no names were supplied to any arguments.
''' </summary>
Public Shared Function MethodInvocationOverloadResolution(
methodGroup As BoundMethodGroup,
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
binder As Binder,
callerInfoOpt As SyntaxNode,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
Optional includeEliminatedCandidates As Boolean = False,
Optional delegateReturnType As TypeSymbol = Nothing,
Optional delegateReturnTypeReferenceBoundNode As BoundNode = Nothing,
Optional lateBindingIsAllowed As Boolean = True,
Optional isQueryOperatorInvocation As Boolean = False,
Optional forceExpandedForm As Boolean = False
) As OverloadResolutionResult
Debug.Assert(methodGroup.ResultKind = LookupResultKind.Good OrElse methodGroup.ResultKind = LookupResultKind.Inaccessible)
Dim typeArguments = If(methodGroup.TypeArgumentsOpt IsNot Nothing, methodGroup.TypeArgumentsOpt.Arguments, ImmutableArray(Of TypeSymbol).Empty)
' To simplify code later
If typeArguments.IsDefault Then
typeArguments = ImmutableArray(Of TypeSymbol).Empty
End If
If arguments.IsDefault Then
arguments = ImmutableArray(Of BoundExpression).Empty
End If
Dim candidates = ArrayBuilder(Of CandidateAnalysisResult).GetInstance()
Dim instanceCandidates As ArrayBuilder(Of Candidate) = ArrayBuilder(Of Candidate).GetInstance()
Dim curriedCandidates As ArrayBuilder(Of Candidate) = ArrayBuilder(Of Candidate).GetInstance()
Dim methods As ImmutableArray(Of MethodSymbol) = methodGroup.Methods
If Not methods.IsDefault Then
' Create MethodCandidates for ordinary methods and ExtensionMethodCandidates
' for curried methods, separating them.
For Each method As MethodSymbol In methods
If method.ReducedFrom Is Nothing Then
instanceCandidates.Add(New MethodCandidate(method))
Else
curriedCandidates.Add(New ExtensionMethodCandidate(method))
End If
Next
End If
Dim asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression) = Nothing
Dim applicableNarrowingCandidateCount As Integer = 0
Dim applicableInstanceCandidateCount As Integer = 0
' First collect instance methods.
If instanceCandidates.Count > 0 Then
CollectOverloadedCandidates(
binder, candidates, instanceCandidates, typeArguments,
arguments, argumentNames, delegateReturnType, delegateReturnTypeReferenceBoundNode,
includeEliminatedCandidates, isQueryOperatorInvocation, forceExpandedForm, asyncLambdaSubToFunctionMismatch,
useSiteInfo)
applicableInstanceCandidateCount = EliminateNotApplicableToArguments(methodGroup, candidates, arguments, argumentNames, binder,
applicableNarrowingCandidateCount, asyncLambdaSubToFunctionMismatch,
callerInfoOpt,
forceExpandedForm,
useSiteInfo)
End If
instanceCandidates.Free()
instanceCandidates = Nothing
' Now add extension methods if they should be considered.
Dim addedExtensionMethods As Boolean = False
If ShouldConsiderExtensionMethods(candidates) Then
' Request additional extension methods, if any available.
If methodGroup.ResultKind = LookupResultKind.Good Then
methods = methodGroup.AdditionalExtensionMethods(useSiteInfo)
For Each method As MethodSymbol In methods
curriedCandidates.Add(New ExtensionMethodCandidate(method))
Next
End If
If curriedCandidates.Count > 0 Then
addedExtensionMethods = True
CollectOverloadedCandidates(
binder, candidates, curriedCandidates, typeArguments,
arguments, argumentNames, delegateReturnType, delegateReturnTypeReferenceBoundNode,
includeEliminatedCandidates, isQueryOperatorInvocation, forceExpandedForm, asyncLambdaSubToFunctionMismatch,
useSiteInfo)
End If
End If
curriedCandidates.Free()
Dim result As OverloadResolutionResult
If applicableInstanceCandidateCount = 0 AndAlso Not addedExtensionMethods Then
result = ReportOverloadResolutionFailedOrLateBound(candidates, applicableInstanceCandidateCount, lateBindingIsAllowed AndAlso binder.OptionStrict <> OptionStrict.On, asyncLambdaSubToFunctionMismatch)
Else
result = ResolveOverloading(methodGroup, candidates, arguments, argumentNames, delegateReturnType, lateBindingIsAllowed, binder, asyncLambdaSubToFunctionMismatch, callerInfoOpt, forceExpandedForm,
useSiteInfo)
End If
candidates.Free()
Return result
End Function
Private Shared Function ReportOverloadResolutionFailedOrLateBound(candidates As ArrayBuilder(Of CandidateAnalysisResult),
applicableNarrowingCandidateCount As Integer,
lateBindingIsAllowed As Boolean,
asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression)) As OverloadResolutionResult
Dim isLateBound As Boolean = False
If lateBindingIsAllowed Then
For Each candidate In candidates
If candidate.State = CandidateAnalysisResultState.TypeInferenceFailed Then
If candidate.AllFailedInferenceIsDueToObject AndAlso Not candidate.Candidate.IsExtensionMethod Then
isLateBound = True
Exit For
End If
End If
Next
End If
Return New OverloadResolutionResult(candidates.ToImmutable, isLateBound, applicableNarrowingCandidateCount > 0, asyncLambdaSubToFunctionMismatch)
End Function
''' <summary>
''' Perform overload resolution on the given array of property symbols.
''' </summary>
Public Shared Function PropertyInvocationOverloadResolution(
propertyGroup As BoundPropertyGroup,
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
binder As Binder,
callerInfoOpt As SyntaxNode,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
Optional includeEliminatedCandidates As Boolean = False
) As OverloadResolutionResult
Debug.Assert(propertyGroup.ResultKind = LookupResultKind.Good OrElse propertyGroup.ResultKind = LookupResultKind.Inaccessible)
Dim properties As ImmutableArray(Of PropertySymbol) = propertyGroup.Properties
' To simplify code later
If arguments.IsDefault Then
arguments = ImmutableArray(Of BoundExpression).Empty
End If
Dim results = ArrayBuilder(Of CandidateAnalysisResult).GetInstance()
Dim candidates = ArrayBuilder(Of Candidate).GetInstance(properties.Length - 1)
For i As Integer = 0 To properties.Length - 1 Step 1
candidates.Add(New PropertyCandidate(properties(i)))
Next
Dim asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression) = Nothing
CollectOverloadedCandidates(binder, results, candidates, ImmutableArray(Of TypeSymbol).Empty,
arguments, argumentNames, Nothing, Nothing, includeEliminatedCandidates,
isQueryOperatorInvocation:=False, forceExpandedForm:=False, asyncLambdaSubToFunctionMismatch:=asyncLambdaSubToFunctionMismatch,
useSiteInfo:=useSiteInfo)
Debug.Assert(asyncLambdaSubToFunctionMismatch Is Nothing)
candidates.Free()
Dim result = ResolveOverloading(propertyGroup, results, arguments, argumentNames, delegateReturnType:=Nothing, lateBindingIsAllowed:=True, binder:=binder,
asyncLambdaSubToFunctionMismatch:=asyncLambdaSubToFunctionMismatch, callerInfoOpt:=callerInfoOpt, forceExpandedForm:=False,
useSiteInfo:=useSiteInfo)
results.Free()
Return result
End Function
''' <summary>
''' Given instance method candidates gone through applicability analysis,
''' figure out if we should consider extension methods, if any.
''' </summary>
Private Shared Function ShouldConsiderExtensionMethods(
candidates As ArrayBuilder(Of CandidateAnalysisResult)
) As Boolean
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim candidate = candidates(i)
Debug.Assert(Not candidate.Candidate.IsExtensionMethod)
If candidate.IgnoreExtensionMethods Then
Return False
End If
Next
Return True
End Function
Private Shared Function ResolveOverloading(
methodOrPropertyGroup As BoundMethodOrPropertyGroup,
candidates As ArrayBuilder(Of CandidateAnalysisResult),
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
delegateReturnType As TypeSymbol,
lateBindingIsAllowed As Boolean,
binder As Binder,
<[In](), Out()> ByRef asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression),
callerInfoOpt As SyntaxNode,
forceExpandedForm As Boolean,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As OverloadResolutionResult
Debug.Assert(argumentNames.IsDefault OrElse argumentNames.Length = arguments.Length)
Dim applicableCandidates As Integer
Dim resolutionIsLateBound As Boolean = False
Dim narrowingCandidatesRemainInTheSet As Boolean = False
Dim applicableNarrowingCandidates As Integer = 0
'TODO: Where does this fit?
'Semantics::ResolveOverloading
'// See if type inference failed for all candidates and it failed from
'// Object. For this scenario, in non-strict mode, treat the call
'// as latebound.
'§11.8.1 Overloaded Method Resolution
'2. Next, eliminate all members from the set that are inaccessible or not applicable to the argument list.
' Note, similar to Dev10 compiler this process will eliminate candidates requiring narrowing conversions
' if strict semantics is used, exception are candidates that require narrowing only from numeric constants.
applicableCandidates = EliminateNotApplicableToArguments(methodOrPropertyGroup, candidates, arguments, argumentNames, binder,
applicableNarrowingCandidates, asyncLambdaSubToFunctionMismatch,
callerInfoOpt,
forceExpandedForm,
useSiteInfo)
If applicableCandidates < 2 Then
narrowingCandidatesRemainInTheSet = (applicableNarrowingCandidates > 0)
GoTo ResolutionComplete
End If
' §11.8.1 Overloaded Method Resolution.
' 7.8. If one or more arguments are AddressOf or lambda expressions, and all of the corresponding
' delegate types in M match exactly, but not all do in N, eliminate N from the set.
' 7.9. If one or more arguments are AddressOf or lambda expressions, and all of the corresponding
' delegate types in M are widening conversions, but not all are in N, eliminate N from the set.
'
' The spec implies that this rule is applied to the set of most applicable candidate as one of the tie breaking rules.
' However, doing it there wouldn't have any effect because all candidates in the set of most applicable candidates
' are equally applicable, therefore, have the same types for corresponding parameters. Thus all the candidates
' have exactly the same delegate relaxation level and none would be eliminated.
' Dev10 applies this rule much earlier, even before eliminating narrowing candidates, and it does it across the board.
' I am going to do the same.
applicableCandidates = ShadowBasedOnDelegateRelaxation(candidates, applicableNarrowingCandidates)
If applicableCandidates < 2 Then
narrowingCandidatesRemainInTheSet = (applicableNarrowingCandidates > 0)
GoTo ResolutionComplete
End If
' §11.8.1 Overloaded Method Resolution.
'7.7. If M and N both required type inference to produce type arguments, and M did not
' require determining the dominant type for any of its type arguments (i.e. each the
' type arguments inferred to a single type), but N did, eliminate N from the set.
' Despite what the spec says, this rule is applied after shadowing based on delegate relaxation
' level, however it needs other tie breaking rules applied to equally applicable candidates prior
' to figuring out the minimal inference level to use as the filter.
ShadowBasedOnInferenceLevel(candidates, arguments, Not argumentNames.IsDefault, delegateReturnType, binder,
applicableCandidates, applicableNarrowingCandidates, useSiteInfo)
If applicableCandidates < 2 Then
narrowingCandidatesRemainInTheSet = (applicableNarrowingCandidates > 0)
GoTo ResolutionComplete
End If
'3. Next, eliminate all members from the set that require narrowing conversions
' to be applicable to the argument list, except for the case where the argument
' expression type is Object.
'4. Next, eliminate all remaining members from the set that require narrowing coercions
' to be applicable to the argument list. If the set is empty, the type containing the
' method group is not an interface, and strict semantics are not being used, the
' invocation target expression is reclassified as a late-bound method access.
' Otherwise, the normal rules apply.
If applicableCandidates = applicableNarrowingCandidates Then
' All remaining candidates are narrowing, deal with them.
narrowingCandidatesRemainInTheSet = True
applicableCandidates = AnalyzeNarrowingCandidates(candidates, arguments, delegateReturnType,
lateBindingIsAllowed AndAlso binder.OptionStrict <> OptionStrict.On, binder,
resolutionIsLateBound,
useSiteInfo)
Else
If applicableNarrowingCandidates > 0 Then
Debug.Assert(applicableNarrowingCandidates < applicableCandidates)
applicableCandidates = EliminateNarrowingCandidates(candidates)
Debug.Assert(applicableCandidates > 0)
If applicableCandidates < 2 Then
GoTo ResolutionComplete
End If
End If
'5. Next, if any instance methods remain in the set,
' eliminate all extension methods from the set.
' !!! I don't think we need to do this explicitly. ResolveMethodOverloading doesn't add
' !!! extension methods in the list if we need to remove them here.
'applicableCandidates = EliminateExtensionMethodsInPresenceOfInstanceMethods(candidates)
'If applicableCandidates < 2 Then
' GoTo ResolutionComplete
'End If
'6. Next, if, given any two members of the set, M and N, M is more applicable than N
' to the argument list, eliminate N from the set. If more than one member remains
' in the set and the remaining members are not equally applicable to the argument
' list, a compile-time error results.
'7. Otherwise, given any two members of the set, M and N, apply the following tie-breaking rules, in order.
applicableCandidates = EliminateLessApplicableToTheArguments(candidates, arguments, delegateReturnType,
False, ' appliedTieBreakingRules
binder, useSiteInfo)
End If
ResolutionComplete:
If Not resolutionIsLateBound AndAlso applicableCandidates = 0 Then
Return ReportOverloadResolutionFailedOrLateBound(candidates, applicableCandidates, lateBindingIsAllowed AndAlso binder.OptionStrict <> OptionStrict.On, asyncLambdaSubToFunctionMismatch)
End If
Return New OverloadResolutionResult(candidates.ToImmutable(), resolutionIsLateBound, narrowingCandidatesRemainInTheSet, asyncLambdaSubToFunctionMismatch)
End Function
Private Shared Function EliminateNarrowingCandidates(
candidates As ArrayBuilder(Of CandidateAnalysisResult)
) As Integer
Dim applicableCandidates As Integer = 0
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State = CandidateAnalysisResultState.Applicable Then
If current.RequiresNarrowingConversion Then
current.State = CandidateAnalysisResultState.RequiresNarrowing
candidates(i) = current
Else
applicableCandidates += 1
End If
End If
Next
Return applicableCandidates
End Function
''' <summary>
''' §11.8.1 Overloaded Method Resolution
''' 6. Next, if, given any two members of the set, M and N, M is more applicable than N
''' to the argument list, eliminate N from the set. If more than one member remains
''' in the set and the remaining members are not equally applicable to the argument
''' list, a compile-time error results.
''' 7. Otherwise, given any two members of the set, M and N, apply the following tie-breaking rules, in order.
'''
''' Returns amount of applicable candidates left.
'''
''' Note that less applicable candidates are going to be eliminated if and only if there are most applicable
''' candidates.
''' </summary>
Private Shared Function EliminateLessApplicableToTheArguments(
candidates As ArrayBuilder(Of CandidateAnalysisResult),
arguments As ImmutableArray(Of BoundExpression),
delegateReturnType As TypeSymbol,
appliedTieBreakingRules As Boolean,
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
Optional mostApplicableMustNarrowOnlyFromNumericConstants As Boolean = False
) As Integer
Dim applicableCandidates As Integer
Dim indexesOfEqualMostApplicableCandidates As ArrayBuilder(Of Integer) = ArrayBuilder(Of Integer).GetInstance()
If FastFindMostApplicableCandidates(candidates, arguments, indexesOfEqualMostApplicableCandidates, binder, useSiteInfo) AndAlso
(mostApplicableMustNarrowOnlyFromNumericConstants = False OrElse
candidates(indexesOfEqualMostApplicableCandidates(0)).RequiresNarrowingNotFromNumericConstant = False OrElse
indexesOfEqualMostApplicableCandidates.Count = CountApplicableCandidates(candidates)) Then
' We have most applicable candidates.
' Mark those that lost applicability comparison.
' Applicable candidates with indexes before the first value in indexesOfEqualMostApplicableCandidates,
' after the last value in indexesOfEqualMostApplicableCandidates and in between consecutive values in
' indexesOfEqualMostApplicableCandidates are less applicable.
Debug.Assert(indexesOfEqualMostApplicableCandidates.Count > 0)
Dim nextMostApplicable As Integer = 0 ' and index into indexesOfEqualMostApplicableCandidates
Dim indexOfNextMostApplicable As Integer = indexesOfEqualMostApplicableCandidates(nextMostApplicable)
For i As Integer = 0 To candidates.Count - 1 Step 1
If i = indexOfNextMostApplicable Then
nextMostApplicable += 1
If nextMostApplicable < indexesOfEqualMostApplicableCandidates.Count Then
indexOfNextMostApplicable = indexesOfEqualMostApplicableCandidates(nextMostApplicable)
Else
indexOfNextMostApplicable = candidates.Count
End If
Continue For
End If
Dim contender As CandidateAnalysisResult = candidates(i)
If contender.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
contender.State = CandidateAnalysisResultState.LessApplicable
candidates(i) = contender
Next
' Apply tie-breaking rules
If Not appliedTieBreakingRules Then
applicableCandidates = ApplyTieBreakingRules(candidates, indexesOfEqualMostApplicableCandidates, arguments, delegateReturnType, binder, useSiteInfo)
Else
applicableCandidates = indexesOfEqualMostApplicableCandidates.Count
End If
ElseIf Not appliedTieBreakingRules Then
' Overload resolution failed, we couldn't find most applicable candidates.
' We still need to apply shadowing rules to the sets of equally applicable candidates,
' this will provide better error reporting experience. As we are doing this, we will redo
' applicability comparisons that we've done earlier in FastFindMostApplicableCandidates, but we are willing to
' pay the price for erroneous code.
applicableCandidates = ApplyTieBreakingRulesToEquallyApplicableCandidates(candidates, arguments, delegateReturnType, binder, useSiteInfo)
Else
applicableCandidates = CountApplicableCandidates(candidates)
End If
indexesOfEqualMostApplicableCandidates.Free()
Return applicableCandidates
End Function
Private Shared Function CountApplicableCandidates(candidates As ArrayBuilder(Of CandidateAnalysisResult)) As Integer
Dim applicableCandidates As Integer = 0
For i As Integer = 0 To candidates.Count - 1 Step 1
If candidates(i).State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
applicableCandidates += 1
Next
Return applicableCandidates
End Function
''' <summary>
''' Returns amount of applicable candidates left.
''' </summary>
Private Shared Function ApplyTieBreakingRulesToEquallyApplicableCandidates(
candidates As ArrayBuilder(Of CandidateAnalysisResult),
arguments As ImmutableArray(Of BoundExpression),
delegateReturnType As TypeSymbol,
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Integer
' First, let's break all remaining candidates into buckets of equally applicable candidates
Dim buckets = GroupEquallyApplicableCandidates(candidates, arguments, binder)
Debug.Assert(buckets.Count > 0)
Dim applicableCandidates As Integer = 0
' Apply tie-breaking rules
For i As Integer = 0 To buckets.Count - 1 Step 1
applicableCandidates += ApplyTieBreakingRules(candidates, buckets(i), arguments, delegateReturnType, binder, useSiteInfo)
Next
' Release memory we no longer need.
For i As Integer = 0 To buckets.Count - 1 Step 1
buckets(i).Free()
Next
buckets.Free()
Return applicableCandidates
End Function
''' <summary>
''' Returns True if there are most applicable candidates.
'''
''' indexesOfMostApplicableCandidates will contain indexes of equally applicable candidates, which are most applicable
''' by comparison to the other (non-equal) candidates. The indexes will be in ascending order.
''' </summary>
Private Shared Function FastFindMostApplicableCandidates(
candidates As ArrayBuilder(Of CandidateAnalysisResult),
arguments As ImmutableArray(Of BoundExpression),
indexesOfMostApplicableCandidates As ArrayBuilder(Of Integer),
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Boolean
Dim mightBeTheMostApplicableIndex As Integer = -1
Dim mightBeTheMostApplicable As CandidateAnalysisResult = Nothing
indexesOfMostApplicableCandidates.Clear()
' Use linear complexity algorithm to find the first most applicable candidate.
' We are saying "the first" because there could be a number of candidates equally applicable
' by comparison to the one we find, their indexes are collected in indexesOfMostApplicableCandidates.
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim contender As CandidateAnalysisResult = candidates(i)
If contender.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
If mightBeTheMostApplicableIndex = -1 Then
mightBeTheMostApplicableIndex = i
mightBeTheMostApplicable = contender
indexesOfMostApplicableCandidates.Add(i)
Else
Dim cmp As ApplicabilityComparisonResult = CompareApplicabilityToTheArguments(mightBeTheMostApplicable, contender, arguments, binder, useSiteInfo)
If cmp = ApplicabilityComparisonResult.RightIsMoreApplicable Then
mightBeTheMostApplicableIndex = i
mightBeTheMostApplicable = contender
indexesOfMostApplicableCandidates.Clear()
indexesOfMostApplicableCandidates.Add(i)
ElseIf cmp = ApplicabilityComparisonResult.Undefined Then
mightBeTheMostApplicableIndex = -1
indexesOfMostApplicableCandidates.Clear()
ElseIf cmp = ApplicabilityComparisonResult.EquallyApplicable Then
indexesOfMostApplicableCandidates.Add(i)
Else
Debug.Assert(cmp = ApplicabilityComparisonResult.LeftIsMoreApplicable)
End If
End If
Next
For i As Integer = 0 To mightBeTheMostApplicableIndex - 1 Step 1
Dim contender As CandidateAnalysisResult = candidates(i)
If contender.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
Dim cmp As ApplicabilityComparisonResult = CompareApplicabilityToTheArguments(mightBeTheMostApplicable, contender, arguments, binder, useSiteInfo)
If cmp = ApplicabilityComparisonResult.RightIsMoreApplicable OrElse
cmp = ApplicabilityComparisonResult.Undefined OrElse
cmp = ApplicabilityComparisonResult.EquallyApplicable Then
' We do this for equal applicability too because this contender was dropped during the first loop, so,
' if we continue, the mightBeTheMostApplicable candidate will be definitely dropped too.
mightBeTheMostApplicableIndex = -1
Exit For
Else
Debug.Assert(cmp = ApplicabilityComparisonResult.LeftIsMoreApplicable)
End If
Next
Return (mightBeTheMostApplicableIndex > -1)
End Function
''' <summary>
''' §11.8.1 Overloaded Method Resolution
''' 7. Otherwise, given any two members of the set, M and N, apply the following tie-breaking rules, in order.
''' </summary>
Private Shared Function ApplyTieBreakingRules(
candidates As ArrayBuilder(Of CandidateAnalysisResult),
bucket As ArrayBuilder(Of Integer),
arguments As ImmutableArray(Of BoundExpression),
delegateReturnType As TypeSymbol,
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Integer
Dim leftWins As Boolean
Dim rightWins As Boolean
Dim applicableCandidates As Integer = bucket.Count
For i = 0 To bucket.Count - 1 Step 1
Dim left = candidates(bucket(i))
If left.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
For j = i + 1 To bucket.Count - 1 Step 1
Dim right = candidates(bucket(j))
If right.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
If ShadowBasedOnTieBreakingRules(left, right, arguments, delegateReturnType, leftWins, rightWins, binder, useSiteInfo) Then
Debug.Assert(Not (leftWins AndAlso rightWins))
If leftWins Then
right.State = CandidateAnalysisResultState.Shadowed
candidates(bucket(j)) = right
applicableCandidates -= 1
Else
Debug.Assert(rightWins)
left.State = CandidateAnalysisResultState.Shadowed
candidates(bucket(i)) = left
applicableCandidates -= 1
Exit For
End If
End If
Next
Next
Debug.Assert(applicableCandidates >= 0)
Return applicableCandidates
End Function
''' <summary>
''' §11.8.1 Overloaded Method Resolution
''' 7. Otherwise, given any two members of the set, M and N, apply the following tie-breaking rules, in order.
''' </summary>
Private Shared Function ShadowBasedOnTieBreakingRules(
left As CandidateAnalysisResult,
right As CandidateAnalysisResult,
arguments As ImmutableArray(Of BoundExpression),
delegateReturnType As TypeSymbol,
ByRef leftWins As Boolean,
ByRef rightWins As Boolean,
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Boolean
' Let's apply various shadowing and tie-breaking rules
' from section 7 of §11.8.1 Overloaded Method Resolution.
leftWins = False
rightWins = False
'• If M has fewer parameters from an expanded paramarray than N, eliminate N from the set.
If ShadowBasedOnParamArrayUsage(left, right, leftWins, rightWins) Then
Return True
End If
'7.1. If M is defined in a more derived type than N, eliminate N from the set.
' This rule also applies to the types that extension methods are defined on.
'7.2. If M and N are extension methods and the target type of M is a class or
' structure and the target type of N is an interface, eliminate N from the set.
If ShadowBasedOnReceiverType(left, right, leftWins, rightWins, useSiteInfo) Then
Return True ' I believe we can get here only in presence of named arguments and optional parameters. Otherwise, CombineCandidates takes care of this shadowing.
End If
'7.3. If M and N are extension methods and the target type of M has fewer type
' parameters than the target type of N, eliminate N from the set.
' !!! Note that spec talks about "fewer type parameters", but it is not really about count.
' !!! It is about one refers to a type parameter and the other one doesn't.
If ShadowBasedOnExtensionMethodTargetTypeGenericity(left, right, leftWins, rightWins) Then
Return True ' I believe we can get here only in presence of named arguments and optional parameters. Otherwise, CombineCandidates takes care of this shadowing.
End If
'7.4. If M is less generic than N, eliminate N from the set.
If ShadowBasedOnGenericity(left, right, leftWins, rightWins, arguments, binder) Then
Return True
End If
'7.5. If M is not an extension method and N is, eliminate N from the set.
'7.6. If M and N are extension methods and M was found before N, eliminate N from the set.
If ShadowBasedOnExtensionVsInstanceAndPrecedence(left, right, leftWins, rightWins) Then
Return True
End If
'7.7. If M and N both required type inference to produce type arguments, and M did not
' require determining the dominant type for any of its type arguments (i.e. each the
' type arguments inferred to a single type), but N did, eliminate N from the set.
' The spec is incorrect, this shadowing doesn't belong here, it is applied across the board
' after these tie breaking rules. For more information, see comment in ResolveOverloading.
'7.8. If one or more arguments are AddressOf or lambda expressions, and all of the corresponding delegate types in M match exactly, but not all do in N, eliminate N from the set.
'7.9. If one or more arguments are AddressOf or lambda expressions, and all of the corresponding delegate types in M are widening conversions, but not all are in N, eliminate N from the set.
' The spec is incorrect, this shadowing doesn't belong here, it is applied much earlier.
' For more information, see comment in ResolveOverloading.
' 7.9. If M did not use any optional parameter defaults in place of explicit
' arguments, but N did, then eliminate N from the set.
'
' !!!WARNING!!! The index (7.9) is based on "VB11 spec [draft 3]" version of documentation rather
' than Dev10 documentation.
If ShadowBasedOnOptionalParametersDefaultsUsed(left, right, leftWins, rightWins) Then
Return True
End If
'7.10. If the overload resolution is being done to resolve the target of a delegate-creation expression from an AddressOf expression and M is a function, while N is a subroutine, eliminate N from the set.
If ShadowBasedOnSubOrFunction(left, right, delegateReturnType, leftWins, rightWins) Then
Return True
End If
' 7.10. Before type arguments have been substituted, if M has greater depth of
' genericity (Section 11.8.1.3) than N, then eliminate N from the set.
'
' !!!WARNING!!! The index (7.10) is based on "VB11 spec [draft 3]" version of documentation
' rather than Dev10 documentation.
'
' NOTE: Dev11 puts this analysis in a second phase with the first phase
' performing analysis of { $11.8.1:6 + 7.9/7.10/7.11/7.8 }, see comments in
' OverloadResolution.cpp: bool Semantics::AreProceduresEquallySpecific(...)
'
' Placing this analysis here seems to be more natural than
' matching Dev11 implementation
If ShadowBasedOnDepthOfGenericity(left, right, leftWins, rightWins, arguments, binder) Then
Return True
End If
Return False
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1 Overloaded Method Resolution.
''' 7.10. If the overload resolution is being done to resolve the target of a
''' delegate-creation expression from an AddressOf expression and M is a
''' function, while N is a subroutine, eliminate N from the set.
''' </summary>
Private Shared Function ShadowBasedOnSubOrFunction(
left As CandidateAnalysisResult, right As CandidateAnalysisResult,
delegateReturnType As TypeSymbol,
ByRef leftWins As Boolean, ByRef rightWins As Boolean
) As Boolean
' !!! Actually, the spec isn't accurate here. If the target delegate is a Sub, we prefer a Sub. !!!
' !!! If the target delegate is a Function, we prefer a Function. !!!
If delegateReturnType Is Nothing Then
Return False
End If
Dim leftReturnsVoid As Boolean = left.Candidate.ReturnType.IsVoidType()
Dim rightReturnsVoid As Boolean = right.Candidate.ReturnType.IsVoidType()
If leftReturnsVoid = rightReturnsVoid Then
Return False
End If
If delegateReturnType.IsVoidType() = leftReturnsVoid Then
leftWins = True
Return True
End If
Debug.Assert(delegateReturnType.IsVoidType() = rightReturnsVoid)
rightWins = True
Return True
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1 Overloaded Method Resolution.
''' 7.8. If one or more arguments are AddressOf or lambda expressions, and all of the corresponding
''' delegate types in M match exactly, but not all do in N, eliminate N from the set.
''' 7.9. If one or more arguments are AddressOf or lambda expressions, and all of the corresponding
''' delegate types in M are widening conversions, but not all are in N, eliminate N from the set.
''' </summary>
Private Shared Function ShadowBasedOnDelegateRelaxation(
candidates As ArrayBuilder(Of CandidateAnalysisResult),
ByRef applicableNarrowingCandidates As Integer
) As Integer
' Find the minimal MaxDelegateRelaxationLevel
Dim minMaxRelaxation As ConversionKind = ConversionKind.DelegateRelaxationLevelInvalid
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State = CandidateAnalysisResultState.Applicable Then
Dim relaxation As ConversionKind = current.MaxDelegateRelaxationLevel
If relaxation < minMaxRelaxation Then
minMaxRelaxation = relaxation
End If
End If
Next
' Now eliminate all candidates with relaxation level bigger than the minimal.
Dim applicableCandidates As Integer = 0
applicableNarrowingCandidates = 0
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
Dim relaxation As ConversionKind = current.MaxDelegateRelaxationLevel
If relaxation > minMaxRelaxation Then
current.State = CandidateAnalysisResultState.Shadowed
candidates(i) = current
Else
applicableCandidates += 1
If current.RequiresNarrowingConversion Then
applicableNarrowingCandidates += 1
End If
End If
Next
Return applicableCandidates
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1 Overloaded Method Resolution.
''' 7.9. If M did not use any optional parameter defaults in place of explicit
''' arguments, but N did, then eliminate N from the set.
'''
''' !!!WARNING!!! The index (7.9) is based on "VB11 spec [draft 3]" version of documentation rather
''' than Dev10 documentation.
''' TODO: Update indexes of other overload method resolution rules
''' </summary>
Private Shared Function ShadowBasedOnOptionalParametersDefaultsUsed(
left As CandidateAnalysisResult, right As CandidateAnalysisResult,
ByRef leftWins As Boolean, ByRef rightWins As Boolean
) As Boolean
Dim leftUsesOptionalParameterDefaults As Boolean = left.UsedOptionalParameterDefaultValue
If leftUsesOptionalParameterDefaults = right.UsedOptionalParameterDefaultValue Then
Return False ' No winner
End If
If Not leftUsesOptionalParameterDefaults Then
leftWins = True
Else
rightWins = True
End If
Return True
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1 Overloaded Method Resolution.
''' 7.7. If M and N both required type inference to produce type arguments, and M did not
''' require determining the dominant type for any of its type arguments (i.e. each the
''' type arguments inferred to a single type), but N did, eliminate N from the set.
''' </summary>
Private Shared Sub ShadowBasedOnInferenceLevel(
candidates As ArrayBuilder(Of CandidateAnalysisResult),
arguments As ImmutableArray(Of BoundExpression),
haveNamedArguments As Boolean,
delegateReturnType As TypeSymbol,
binder As Binder,
ByRef applicableCandidates As Integer,
ByRef applicableNarrowingCandidates As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
)
Debug.Assert(Not haveNamedArguments OrElse Not candidates(0).Candidate.IsOperator)
' See if there are candidates with different InferenceLevel
Dim haveDifferentInferenceLevel As Boolean = False
Dim theOnlyInferenceLevel As TypeArgumentInference.InferenceLevel = CType(Byte.MaxValue, TypeArgumentInference.InferenceLevel)
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State = CandidateAnalysisResultState.Applicable Then
Dim inferenceLevel As TypeArgumentInference.InferenceLevel = current.InferenceLevel
If theOnlyInferenceLevel = Byte.MaxValue Then
theOnlyInferenceLevel = inferenceLevel
ElseIf inferenceLevel <> theOnlyInferenceLevel Then
haveDifferentInferenceLevel = True
Exit For
End If
End If
Next
If Not haveDifferentInferenceLevel Then
' Nothing to do.
Return
End If
' Native compiler used to have a bug where CombineCandidates was applying shadowing in presence of named arguments
' before figuring out whether candidates are applicable. We fixed that. However, in cases when candidates were applicable
' after all, that shadowing had impact on the shadowing based on the inference level by affecting minimal inference level.
' To compensate, we will perform the CombineCandidates-style shadowing here. Note that we cannot simply call
' ApplyTieBreakingRulesToEquallyApplicableCandidates to do this because shadowing performed by CombineCandidates is more
' constrained.
If haveNamedArguments Then
Debug.Assert(Not candidates(0).Candidate.IsOperator)
Dim indexesOfApplicableCandidates = ArrayBuilder(Of Integer).GetInstance(applicableCandidates)
For i As Integer = 0 To candidates.Count - 1 Step 1
If candidates(i).State = CandidateAnalysisResultState.Applicable Then
indexesOfApplicableCandidates.Add(i)
End If
Next
Debug.Assert(indexesOfApplicableCandidates.Count = applicableCandidates)
' Sort indexes by inference level
indexesOfApplicableCandidates.Sort(New InferenceLevelComparer(candidates))
#If DEBUG Then
Dim level As TypeArgumentInference.InferenceLevel = TypeArgumentInference.InferenceLevel.None
For Each index As Integer In indexesOfApplicableCandidates
Debug.Assert(level <= candidates(index).InferenceLevel)
level = candidates(index).InferenceLevel
Next
#End If
' In order of sorted indexes, apply constrained shadowing rules looking for the first one survived.
' This will be sufficient to calculate "correct" minimal inference level. We don't have to apply
' shadowing to each pair of candidates.
For i As Integer = 0 To indexesOfApplicableCandidates.Count - 2
Dim left As CandidateAnalysisResult = candidates(indexesOfApplicableCandidates(i))
If left.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
For j As Integer = i + 1 To indexesOfApplicableCandidates.Count - 1
Dim right As CandidateAnalysisResult = candidates(indexesOfApplicableCandidates(j))
If right.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
' Shadowing is applied only to candidates that have the same types for corresponding parameters
' in virtual signatures
Dim equallyApplicable As Boolean = True
For k = 0 To arguments.Length - 1 Step 1
Dim leftParamType As TypeSymbol = GetParameterTypeFromVirtualSignature(left, left.ArgsToParamsOpt(k))
Dim rightParamType As TypeSymbol = GetParameterTypeFromVirtualSignature(right, right.ArgsToParamsOpt(k))
If Not leftParamType.IsSameTypeIgnoringAll(rightParamType) Then
' Signatures are different, shadowing rules do not apply
equallyApplicable = False
Exit For
End If
Next
If Not equallyApplicable Then
Continue For
End If
Dim signatureMatch As Boolean = True
' Compare complete signature, with no regard to arguments
If left.Candidate.ParameterCount <> right.Candidate.ParameterCount Then
signatureMatch = False
Else
For k As Integer = 0 To left.Candidate.ParameterCount - 1 Step 1
Dim leftType As TypeSymbol = left.Candidate.Parameters(k).Type
Dim rightType As TypeSymbol = right.Candidate.Parameters(k).Type
If Not leftType.IsSameTypeIgnoringAll(rightType) Then
signatureMatch = False
Exit For
End If
Next
End If
Dim leftWins As Boolean = False
Dim rightWins As Boolean = False
If (Not signatureMatch AndAlso ShadowBasedOnParamArrayUsage(left, right, leftWins, rightWins)) OrElse
ShadowBasedOnReceiverType(left, right, leftWins, rightWins, useSiteInfo) OrElse
ShadowBasedOnExtensionMethodTargetTypeGenericity(left, right, leftWins, rightWins) Then
Debug.Assert(leftWins Xor rightWins)
If leftWins Then
right.State = CandidateAnalysisResultState.Shadowed
candidates(indexesOfApplicableCandidates(j)) = right
ElseIf rightWins Then
left.State = CandidateAnalysisResultState.Shadowed
candidates(indexesOfApplicableCandidates(i)) = left
Exit For ' advance to the next left
End If
End If
Next
If left.State = CandidateAnalysisResultState.Applicable Then
' left has survived
Exit For
End If
Next
End If
' Find the minimal InferenceLevel
Dim minInferenceLevel = TypeArgumentInference.InferenceLevel.Invalid
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State = CandidateAnalysisResultState.Applicable Then
Dim inferenceLevel As TypeArgumentInference.InferenceLevel = current.InferenceLevel
If inferenceLevel < minInferenceLevel Then
minInferenceLevel = inferenceLevel
End If
End If
Next
' Now eliminate all candidates with inference level bigger than the minimal.
applicableCandidates = 0
applicableNarrowingCandidates = 0
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
Dim inferenceLevel As TypeArgumentInference.InferenceLevel = current.InferenceLevel
If inferenceLevel > minInferenceLevel Then
current.State = CandidateAnalysisResultState.Shadowed
candidates(i) = current
Else
applicableCandidates += 1
If current.RequiresNarrowingConversion Then
applicableNarrowingCandidates += 1
End If
End If
Next
' Done.
End Sub
Private Class InferenceLevelComparer
Implements IComparer(Of Integer)
Private ReadOnly _candidates As ArrayBuilder(Of CandidateAnalysisResult)
Public Sub New(candidates As ArrayBuilder(Of CandidateAnalysisResult))
_candidates = candidates
End Sub
Public Function Compare(indexX As Integer, indexY As Integer) As Integer Implements IComparer(Of Integer).Compare
Return CInt(_candidates(indexX).InferenceLevel).CompareTo(_candidates(indexY).InferenceLevel)
End Function
End Class
''' <summary>
''' §11.8.1.1 Applicability
''' </summary>
Private Shared Function CompareApplicabilityToTheArguments(
ByRef left As CandidateAnalysisResult,
ByRef right As CandidateAnalysisResult,
arguments As ImmutableArray(Of BoundExpression),
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ApplicabilityComparisonResult
' §11.8.1.1 Applicability
'A member M is considered more applicable than N if their signatures are different and at least one
'parameter type in M is more applicable than a parameter type in N, and no parameter type in N is more
'applicable than a parameter type in M.
Dim equallyApplicable As Boolean = True
Dim leftHasMoreApplicableParameterType As Boolean = False
Dim rightHasMoreApplicableParameterType As Boolean = False
Dim leftParamIndex As Integer = 0
Dim rightParamIndex As Integer = 0
For i = 0 To arguments.Length - 1 Step 1
Dim leftParamType As TypeSymbol
Debug.Assert(left.ArgsToParamsOpt.IsDefault = right.ArgsToParamsOpt.IsDefault)
If left.ArgsToParamsOpt.IsDefault Then
leftParamType = GetParameterTypeFromVirtualSignature(left, leftParamIndex)
AdvanceParameterInVirtualSignature(left, leftParamIndex)
Else
leftParamType = GetParameterTypeFromVirtualSignature(left, left.ArgsToParamsOpt(i))
End If
Dim rightParamType As TypeSymbol
If right.ArgsToParamsOpt.IsDefault Then
rightParamType = GetParameterTypeFromVirtualSignature(right, rightParamIndex)
AdvanceParameterInVirtualSignature(right, rightParamIndex)
Else
rightParamType = GetParameterTypeFromVirtualSignature(right, right.ArgsToParamsOpt(i))
End If
' Parameters matching omitted arguments do not participate.
If arguments(i).Kind = BoundKind.OmittedArgument Then
Continue For
End If
Dim cmp = CompareParameterTypeApplicability(leftParamType, rightParamType, arguments(i), binder, useSiteInfo)
If cmp = ApplicabilityComparisonResult.LeftIsMoreApplicable Then
leftHasMoreApplicableParameterType = True
If rightHasMoreApplicableParameterType Then
Return ApplicabilityComparisonResult.Undefined ' Neither is more applicable
End If
equallyApplicable = False
ElseIf cmp = ApplicabilityComparisonResult.RightIsMoreApplicable Then
rightHasMoreApplicableParameterType = True
If leftHasMoreApplicableParameterType Then
Return ApplicabilityComparisonResult.Undefined ' Neither is more applicable
End If
equallyApplicable = False
ElseIf cmp = ApplicabilityComparisonResult.Undefined Then
equallyApplicable = False
Else
Debug.Assert(cmp = ApplicabilityComparisonResult.EquallyApplicable)
End If
Next
Debug.Assert(Not (leftHasMoreApplicableParameterType AndAlso rightHasMoreApplicableParameterType))
If leftHasMoreApplicableParameterType Then
Return ApplicabilityComparisonResult.LeftIsMoreApplicable
End If
If rightHasMoreApplicableParameterType Then
Return ApplicabilityComparisonResult.RightIsMoreApplicable
End If
Return If(equallyApplicable, ApplicabilityComparisonResult.EquallyApplicable, ApplicabilityComparisonResult.Undefined)
End Function
Private Enum ApplicabilityComparisonResult
Undefined
EquallyApplicable
LeftIsMoreApplicable
RightIsMoreApplicable
End Enum
''' <summary>
''' §11.8.1.1 Applicability
''' </summary>
Private Shared Function CompareParameterTypeApplicability(
left As TypeSymbol,
right As TypeSymbol,
argument As BoundExpression,
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ApplicabilityComparisonResult
Debug.Assert(argument Is Nothing OrElse argument.Kind <> BoundKind.OmittedArgument)
' §11.8.1.1 Applicability
'Given a pair of parameters Mj and Nj that matches an argument Aj,
'the type of Mj is considered more applicable than the type of Nj if one of the following conditions is true:
Dim leftToRightConversion = Conversions.ClassifyConversion(left, right, useSiteInfo)
'1. Mj and Nj have identical types, or
' !!! Does this rule make sense? Not implementing it for now.
If Conversions.IsIdentityConversion(leftToRightConversion.Key) Then
Return ApplicabilityComparisonResult.EquallyApplicable
End If
'2. There exists a widening conversion from the type of Mj to the type Nj, or
If Conversions.IsWideningConversion(leftToRightConversion.Key) Then
' !!! For user defined conversions that widen in both directions there is a tie-breaking rule
' !!! not mentioned in the spec. The type that matches argument's type is more applicable.
' !!! Otherwise neither is more applicable.
If Conversions.IsWideningConversion(Conversions.ClassifyConversion(right, left, useSiteInfo).Key) Then
GoTo BreakTheTie
End If
' !!! Spec makes it look like rule #3 is a separate rule applied after the second, but this isn't the case
' !!! because enumerated type widens to its underlying type, however, if argument is a zero literal,
' !!! underlying type should win.
' !!! Also, based on Dev10 implementation, Mj doesn't have to be a numeric type, it is enough if it is not
' !!! an enumerated type.
'3. Aj is the literal 0, Mj is a numeric type and Nj is an enumerated type, or
If argument IsNot Nothing AndAlso argument.IsIntegerZeroLiteral AndAlso
left.TypeKind = TypeKind.Enum AndAlso right.TypeKind <> TypeKind.Enum Then
Return ApplicabilityComparisonResult.RightIsMoreApplicable
End If
Return ApplicabilityComparisonResult.LeftIsMoreApplicable
End If
If Conversions.IsWideningConversion(Conversions.ClassifyConversion(right, left, useSiteInfo).Key) Then
' !!! Spec makes it look like rule #3 is a separate rule applied after the second, but this isn't the case
' !!! because enumerated type widens to its underlying type, however, if argument is a zero literal,
' !!! underlying type should win.
' !!! Also, based on Dev10 implementation, Mj doesn't have to be a numeric type, it is enough if it is not
' !!! an enumerated type.
'3. Aj is the literal 0, Mj is a numeric type and Nj is an enumerated type, or
If argument IsNot Nothing AndAlso argument.IsIntegerZeroLiteral AndAlso
right.TypeKind = TypeKind.Enum AndAlso left.TypeKind <> TypeKind.Enum Then
Return ApplicabilityComparisonResult.LeftIsMoreApplicable
End If
Return ApplicabilityComparisonResult.RightIsMoreApplicable
End If
''3. Aj is the literal 0, Mj is a numeric type and Nj is an enumerated type, or
'If argument IsNot Nothing AndAlso argument.IsIntegerZeroLiteral Then
' If left.IsNumericType() Then
' If right.TypeKind = TypeKind.Enum Then
' leftIsMoreApplicable = True
' Return
' End If
' ElseIf right.IsNumericType() Then
' If left.TypeKind = TypeKind.Enum Then
' rightIsMoreApplicable = True
' Return
' End If
' End If
'End If
'4. Mj is Byte and Nj is SByte, or
'5. Mj is Short and Nj is UShort, or
'6. Mj is Integer and Nj is UInteger, or
'7. Mj is Long and Nj is ULong.
'!!! Plus rules not mentioned in the spec
If left.IsNumericType() AndAlso right.IsNumericType() Then
Dim leftSpecialType = left.SpecialType
Dim rightSpecialType = right.SpecialType
If leftSpecialType = SpecialType.System_Byte AndAlso rightSpecialType = SpecialType.System_SByte Then
Return ApplicabilityComparisonResult.LeftIsMoreApplicable
End If
If leftSpecialType = SpecialType.System_SByte AndAlso rightSpecialType = SpecialType.System_Byte Then
Return ApplicabilityComparisonResult.RightIsMoreApplicable
End If
' This comparison depends on the ordering of the SpecialType enum. There is a unit-test that verifies the ordering.
If leftSpecialType < rightSpecialType Then
Return ApplicabilityComparisonResult.LeftIsMoreApplicable
Else
Debug.Assert(rightSpecialType < leftSpecialType)
Return ApplicabilityComparisonResult.RightIsMoreApplicable
End If
End If
'8. Mj and Nj are delegate function types and the return type of Mj is more specific than the return type of Nj.
' If Aj is classified as a lambda method, and Mj or Nj is System.Linq.Expressions.Expression(Of T), then the
' type argument of the type (assuming it is a delegate type) is substituted for the type being compared.
If argument IsNot Nothing Then
Dim leftIsExpressionTree As Boolean, rightIsExpressionTree As Boolean
Dim leftDelegateType As NamedTypeSymbol = left.DelegateOrExpressionDelegate(binder, leftIsExpressionTree)
Dim rightDelegateType As NamedTypeSymbol = right.DelegateOrExpressionDelegate(binder, rightIsExpressionTree)
' Native compiler will only compare D1 and D2 for Expression(Of D1) and D2 if the argument is a lambda. It will compare
' Expression(Of D1) and Expression (Of D2) regardless of the argument.
If leftDelegateType IsNot Nothing AndAlso rightDelegateType IsNot Nothing AndAlso
((leftIsExpressionTree = rightIsExpressionTree) OrElse argument.IsAnyLambda()) Then
Dim leftInvoke As MethodSymbol = leftDelegateType.DelegateInvokeMethod
Dim rightInvoke As MethodSymbol = rightDelegateType.DelegateInvokeMethod
If leftInvoke IsNot Nothing AndAlso Not leftInvoke.IsSub AndAlso rightInvoke IsNot Nothing AndAlso Not rightInvoke.IsSub Then
Dim newArgument As BoundExpression = Nothing
' TODO: Should probably handle GroupTypeInferenceLambda too.
If argument.Kind = BoundKind.QueryLambda Then
newArgument = DirectCast(argument, BoundQueryLambda).Expression
End If
Return CompareParameterTypeApplicability(leftInvoke.ReturnType, rightInvoke.ReturnType, newArgument, binder, useSiteInfo)
End If
End If
End If
BreakTheTie:
' !!! There is a tie-breaking rule not mentioned in the spec. The type that matches argument's type is more applicable.
' !!! Otherwise neither is more applicable.
If argument IsNot Nothing Then
Dim argType As TypeSymbol = If(argument.Kind <> BoundKind.ArrayLiteral, argument.Type, DirectCast(argument, BoundArrayLiteral).InferredType)
If argType IsNot Nothing Then
If left.IsSameTypeIgnoringAll(argType) Then
Return ApplicabilityComparisonResult.LeftIsMoreApplicable
End If
If right.IsSameTypeIgnoringAll(argType) Then
Return ApplicabilityComparisonResult.RightIsMoreApplicable
End If
End If
End If
' Neither is more applicable
Return ApplicabilityComparisonResult.Undefined
End Function
''' <summary>
''' This method groups equally applicable (§11.8.1.1 Applicability) candidates into buckets.
'''
''' Returns an ArrayBuilder of buckets. Each bucket is represented by an ArrayBuilder(Of Integer),
''' which contains indexes of equally applicable candidates from input parameter 'candidates'.
''' </summary>
Private Shared Function GroupEquallyApplicableCandidates(
candidates As ArrayBuilder(Of CandidateAnalysisResult),
arguments As ImmutableArray(Of BoundExpression),
binder As Binder
) As ArrayBuilder(Of ArrayBuilder(Of Integer))
Dim buckets = ArrayBuilder(Of ArrayBuilder(Of Integer)).GetInstance()
Dim i As Integer
Dim j As Integer
' §11.8.1.1 Applicability
' A member M is considered equally applicable as N if their signatures are the same or
' if each parameter type in M is the same as the corresponding parameter type in N.
For i = 0 To candidates.Count - 1 Step 1
Dim left As CandidateAnalysisResult = candidates(i)
If left.State <> CandidateAnalysisResultState.Applicable OrElse
left.EquallyApplicableCandidatesBucket > 0 Then
Continue For
End If
left.EquallyApplicableCandidatesBucket = buckets.Count + 1
candidates(i) = left
Dim b = ArrayBuilder(Of Integer).GetInstance()
b.Add(i)
buckets.Add(b)
For j = i + 1 To candidates.Count - 1 Step 1
Dim right As CandidateAnalysisResult = candidates(j)
If right.State <> CandidateAnalysisResultState.Applicable OrElse
right.EquallyApplicableCandidatesBucket > 0 OrElse
right.Candidate Is left.Candidate Then
Continue For
End If
If CandidatesAreEquallyApplicableToArguments(left, right, arguments, binder) Then
right.EquallyApplicableCandidatesBucket = left.EquallyApplicableCandidatesBucket
candidates(j) = right
b.Add(j)
End If
Next
Next
Return buckets
End Function
Private Shared Function CandidatesAreEquallyApplicableToArguments(
ByRef left As CandidateAnalysisResult,
ByRef right As CandidateAnalysisResult,
arguments As ImmutableArray(Of BoundExpression),
binder As Binder
) As Boolean
' §11.8.1.1 Applicability
' A member M is considered equally applicable as N if their signatures are the same or
' if each parameter type in M is the same as the corresponding parameter type in N.
' Compare types of corresponding parameters
Dim k As Integer
Dim leftParamIndex As Integer = 0
Dim rightParamIndex As Integer = 0
For k = 0 To arguments.Length - 1 Step 1
Dim leftParamType As TypeSymbol
Debug.Assert(left.ArgsToParamsOpt.IsDefault = right.ArgsToParamsOpt.IsDefault)
If left.ArgsToParamsOpt.IsDefault Then
leftParamType = GetParameterTypeFromVirtualSignature(left, leftParamIndex)
AdvanceParameterInVirtualSignature(left, leftParamIndex)
Else
leftParamType = GetParameterTypeFromVirtualSignature(left, left.ArgsToParamsOpt(k))
End If
Dim rightParamType As TypeSymbol
If right.ArgsToParamsOpt.IsDefault Then
rightParamType = GetParameterTypeFromVirtualSignature(right, rightParamIndex)
AdvanceParameterInVirtualSignature(right, rightParamIndex)
Else
rightParamType = GetParameterTypeFromVirtualSignature(right, right.ArgsToParamsOpt(k))
End If
' Parameters matching omitted arguments do not participate.
If arguments(k).Kind <> BoundKind.OmittedArgument AndAlso
Not ParametersAreEquallyApplicableToArgument(leftParamType, rightParamType, arguments(k), binder) Then
' Signatures are different
Exit For
End If
Next
Return k >= arguments.Length
End Function
Private Shared Function ParametersAreEquallyApplicableToArgument(
leftParamType As TypeSymbol,
rightParamType As TypeSymbol,
argument As BoundExpression,
binder As Binder
) As Boolean
Debug.Assert(argument Is Nothing OrElse argument.Kind <> BoundKind.OmittedArgument)
If Not leftParamType.IsSameTypeIgnoringAll(rightParamType) Then
If argument IsNot Nothing Then
Dim leftIsExpressionTree As Boolean, rightIsExpressionTree As Boolean
Dim leftDelegateType As NamedTypeSymbol = leftParamType.DelegateOrExpressionDelegate(binder, leftIsExpressionTree)
Dim rightDelegateType As NamedTypeSymbol = rightParamType.DelegateOrExpressionDelegate(binder, rightIsExpressionTree)
' Native compiler will only compare D1 and D2 for Expression(Of D1) and D2 if the argument is a lambda. It will compare
' Expression(Of D1) and Expression (Of D2) regardless of the argument.
If leftDelegateType IsNot Nothing AndAlso rightDelegateType IsNot Nothing AndAlso
((leftIsExpressionTree = rightIsExpressionTree) OrElse argument.IsAnyLambda()) Then
Dim leftInvoke As MethodSymbol = leftDelegateType.DelegateInvokeMethod
Dim rightInvoke As MethodSymbol = rightDelegateType.DelegateInvokeMethod
If leftInvoke IsNot Nothing AndAlso Not leftInvoke.IsSub AndAlso rightInvoke IsNot Nothing AndAlso Not rightInvoke.IsSub Then
Dim newArgument As BoundExpression = Nothing
' TODO: Should probably handle GroupTypeInferenceLambda too.
If argument.Kind = BoundKind.QueryLambda Then
newArgument = DirectCast(argument, BoundQueryLambda).Expression
End If
Return ParametersAreEquallyApplicableToArgument(leftInvoke.ReturnType, rightInvoke.ReturnType, newArgument, binder)
End If
End If
End If
' Signatures are different
Return False
End If
Return True
End Function
''' <summary>
''' §11.8.1 Overloaded Method Resolution
''' 3. Next, eliminate all members from the set that require narrowing conversions
''' to be applicable to the argument list, except for the case where the argument
''' expression type is Object.
''' 4. Next, eliminate all remaining members from the set that require narrowing coercions
''' to be applicable to the argument list. If the set is empty, the type containing the
''' method group is not an interface, and strict semantics are not being used, the
''' invocation target expression is reclassified as a late-bound method access.
''' Otherwise, the normal rules apply.
'''
''' Returns amount of applicable candidates left.
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Private Shared Function AnalyzeNarrowingCandidates(
candidates As ArrayBuilder(Of CandidateAnalysisResult),
arguments As ImmutableArray(Of BoundExpression),
delegateReturnType As TypeSymbol,
lateBindingIsAllowed As Boolean,
binder As Binder,
ByRef resolutionIsLateBound As Boolean,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Integer
Dim applicableCandidates As Integer = 0
Dim appliedTieBreakingRules As Boolean = False
' Look through the candidate set for lifted operators that require narrowing conversions whose
' source operators also require narrowing conversions. In that case, we only want to keep one method in
' the set. If the source operator requires nullables to be unwrapped, then we discard it and keep the lifted operator.
' If it does not, then we discard the lifted operator and keep the source operator. This will prevent the presence of
' lifted operators from causing overload resolution conflicts where there otherwise wouldn't be one. However,
' if the source operator only requires narrowing conversions from numeric literals, then we keep both in the set,
' because the conversion in that case is not really narrowing.
If candidates(0).Candidate.IsOperator Then
' As an optimization, we can rely on the fact that lifted operator, if added, immediately
' follows source operator.
Dim i As Integer
For i = 0 To candidates.Count - 2 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
Debug.Assert(current.Candidate.IsOperator)
If current.State = CandidateAnalysisResultState.Applicable AndAlso
Not current.Candidate.IsLifted AndAlso
current.RequiresNarrowingNotFromNumericConstant Then
Dim contender As CandidateAnalysisResult = candidates(i + 1)
Debug.Assert(contender.Candidate.IsOperator)
If contender.State = CandidateAnalysisResultState.Applicable AndAlso
contender.Candidate.IsLifted AndAlso
current.Candidate.UnderlyingSymbol Is contender.Candidate.UnderlyingSymbol Then
Exit For
End If
End If
Next
If i < candidates.Count - 1 Then
' [i] is the index of the first "interesting" pair of source/lifted operators.
If Not appliedTieBreakingRules Then
' Apply shadowing rules, Dev10 compiler does that for narrowing candidates too.
applicableCandidates = ApplyTieBreakingRulesToEquallyApplicableCandidates(candidates, arguments, delegateReturnType, binder, useSiteInfo)
appliedTieBreakingRules = True
Debug.Assert(applicableCandidates > 1) ' source and lifted operators are not equally applicable.
End If
' Let's do the elimination pass now.
For i = i To candidates.Count - 2 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
Debug.Assert(current.Candidate.IsOperator)
If current.State = CandidateAnalysisResultState.Applicable AndAlso
Not current.Candidate.IsLifted AndAlso
current.RequiresNarrowingNotFromNumericConstant Then
Dim contender As CandidateAnalysisResult = candidates(i + 1)
Debug.Assert(contender.Candidate.IsOperator)
If contender.State = CandidateAnalysisResultState.Applicable AndAlso
contender.Candidate.IsLifted AndAlso
current.Candidate.UnderlyingSymbol Is contender.Candidate.UnderlyingSymbol Then
For j As Integer = 0 To arguments.Length - 1
Dim conv As KeyValuePair(Of ConversionKind, MethodSymbol) = current.ConversionsOpt(j)
If Conversions.IsNarrowingConversion(conv.Key) Then
Dim lost As Boolean = False
If (conv.Key And ConversionKind.UserDefined) = 0 Then
If IsUnwrappingNullable(conv.Key, arguments(j).Type, current.Candidate.Parameters(j).Type) Then
lost = True
End If
Else
' Lifted user-defined conversions don't unwrap nullables, they are marked with Nullable bit.
If (conv.Key And ConversionKind.Nullable) = 0 Then
If IsUnwrappingNullable(arguments(j).Type, conv.Value.Parameters(0).Type, useSiteInfo) Then
lost = True
ElseIf IsUnwrappingNullable(conv.Value.ReturnType, current.Candidate.Parameters(j).Type, useSiteInfo) Then
lost = True
End If
End If
End If
If lost Then
' unwrapping nullable, current lost
current.State = CandidateAnalysisResultState.Shadowed
candidates(i) = current
i = i + 1
GoTo Next_i
End If
End If
Next
' contender lost
contender.State = CandidateAnalysisResultState.Shadowed
candidates(i + 1) = contender
i = i + 1
GoTo Next_i
End If
End If
Next_i:
Next
End If
End If
If lateBindingIsAllowed Then
' Are there all narrowing from object candidates?
Dim haveAllNarrowingFromObject As Boolean = HaveNarrowingOnlyFromObjectCandidates(candidates)
If haveAllNarrowingFromObject AndAlso Not appliedTieBreakingRules Then
' Apply shadowing rules, Dev10 compiler does that for narrowing candidates too.
applicableCandidates = ApplyTieBreakingRulesToEquallyApplicableCandidates(candidates, arguments, delegateReturnType, binder, useSiteInfo)
appliedTieBreakingRules = True
If applicableCandidates < 2 Then
Return applicableCandidates
End If
haveAllNarrowingFromObject = HaveNarrowingOnlyFromObjectCandidates(candidates)
End If
If haveAllNarrowingFromObject Then
' Get rid of candidates that require narrowing from something other than Object
applicableCandidates = 0
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State = CandidateAnalysisResultState.Applicable Then
If (current.RequiresNarrowingNotFromObject OrElse current.Candidate.IsExtensionMethod) Then
current.State = CandidateAnalysisResultState.ExtensionMethodVsLateBinding
candidates(i) = current
Else
applicableCandidates += 1
End If
End If
Next
Debug.Assert(applicableCandidates > 0)
If applicableCandidates > 1 Then
resolutionIsLateBound = True
End If
Return applicableCandidates
End If
End If
' Although all candidates narrow, there may be a best choice when factoring in narrowing of numeric constants.
' Note that EliminateLessApplicableToTheArguments applies shadowing rules, Dev10 compiler does that for narrowing candidates too.
applicableCandidates = EliminateLessApplicableToTheArguments(candidates, arguments, delegateReturnType, appliedTieBreakingRules, binder,
mostApplicableMustNarrowOnlyFromNumericConstants:=True, useSiteInfo:=useSiteInfo)
' If we ended up with 2 applicable candidates, make sure it is not the same method in
' ParamArray expanded and non-expanded form. The non-expanded form should win in this case.
If applicableCandidates = 2 Then
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim first As CandidateAnalysisResult = candidates(i)
If first.State = CandidateAnalysisResultState.Applicable Then
For j As Integer = i + 1 To candidates.Count - 1 Step 1
Dim second As CandidateAnalysisResult = candidates(j)
If second.State = CandidateAnalysisResultState.Applicable Then
If first.Candidate.UnderlyingSymbol.Equals(second.Candidate.UnderlyingSymbol) Then
Dim firstWins As Boolean = False
Dim secondWins As Boolean = False
If ShadowBasedOnParamArrayUsage(first, second, firstWins, secondWins) Then
If firstWins Then
second.State = CandidateAnalysisResultState.Shadowed
candidates(j) = second
applicableCandidates = 1
ElseIf secondWins Then
first.State = CandidateAnalysisResultState.Shadowed
candidates(i) = first
applicableCandidates = 1
End If
Debug.Assert(applicableCandidates = 1)
End If
End If
GoTo Done
End If
Next
Debug.Assert(False, "Should not reach this line.")
End If
Next
End If
Done:
Return applicableCandidates
End Function
Private Shared Function IsUnwrappingNullable(
conv As ConversionKind,
sourceType As TypeSymbol,
targetType As TypeSymbol
) As Boolean
Debug.Assert((conv And ConversionKind.UserDefined) = 0)
Return (conv And ConversionKind.Nullable) <> 0 AndAlso
sourceType IsNot Nothing AndAlso
sourceType.IsNullableType() AndAlso
Not targetType.IsNullableType()
End Function
Private Shared Function IsUnwrappingNullable(
sourceType As TypeSymbol,
targetType As TypeSymbol,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Boolean
Return sourceType IsNot Nothing AndAlso
IsUnwrappingNullable(Conversions.ClassifyPredefinedConversion(sourceType, targetType, useSiteInfo), sourceType, targetType)
End Function
Private Shared Function HaveNarrowingOnlyFromObjectCandidates(
candidates As ArrayBuilder(Of CandidateAnalysisResult)
) As Boolean
Dim haveAllNarrowingFromObject As Boolean = False
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State = CandidateAnalysisResultState.Applicable AndAlso
Not current.RequiresNarrowingNotFromObject AndAlso
Not current.Candidate.IsExtensionMethod Then
haveAllNarrowingFromObject = True
Exit For
End If
Next
Return haveAllNarrowingFromObject
End Function
''' <summary>
''' §11.8.1 Overloaded Method Resolution
''' 2. Next, eliminate all members from the set that are inaccessible or not applicable to the argument list.
'''
''' Note, similar to Dev10 compiler this process will eliminate candidates requiring narrowing conversions
''' if strict semantics is used, exception are candidates that require narrowing only from numeric constants.
'''
''' Returns amount of applicable candidates left.
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Private Shared Function EliminateNotApplicableToArguments(
methodOrPropertyGroup As BoundMethodOrPropertyGroup,
candidates As ArrayBuilder(Of CandidateAnalysisResult),
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
binder As Binder,
<Out()> ByRef applicableNarrowingCandidates As Integer,
<[In](), Out()> ByRef asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression),
callerInfoOpt As SyntaxNode,
forceExpandedForm As Boolean,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Integer
Dim applicableCandidates As Integer = 0
Dim illegalInAttribute As Integer = 0
applicableNarrowingCandidates = 0
' Filter out inapplicable candidates
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State <> CandidateAnalysisResultState.Applicable Then
Continue For
End If
If Not current.ArgumentMatchingDone Then
MatchArguments(methodOrPropertyGroup, current, arguments, argumentNames, binder, asyncLambdaSubToFunctionMismatch, callerInfoOpt, forceExpandedForm, useSiteInfo)
current.SetArgumentMatchingDone()
candidates(i) = current
End If
If current.State = CandidateAnalysisResultState.Applicable Then
applicableCandidates += 1
If current.RequiresNarrowingConversion Then
applicableNarrowingCandidates += 1
End If
If current.IsIllegalInAttribute Then
illegalInAttribute += 1
End If
End If
Next
' Filter out candidates with IsIllegalInAttribute if there are other applicable candidates
If illegalInAttribute > 0 AndAlso applicableCandidates > illegalInAttribute Then
For i As Integer = 0 To candidates.Count - 1 Step 1
Dim current As CandidateAnalysisResult = candidates(i)
If current.State = CandidateAnalysisResultState.Applicable AndAlso current.IsIllegalInAttribute Then
applicableCandidates -= 1
If current.RequiresNarrowingConversion Then
applicableNarrowingCandidates -= 1
End If
current.State = CandidateAnalysisResultState.ArgumentMismatch
candidates(i) = current
End If
Next
Debug.Assert(applicableCandidates > 0)
End If
Return applicableCandidates
End Function
''' <summary>
''' Figure out corresponding arguments for parameters §11.8.2 Applicable Methods.
'''
''' Note, this function mutates the candidate structure.
'''
''' If non-Nothing ArrayBuilders are returned through parameterToArgumentMap and paramArrayItems
''' parameters, the caller is responsible fo returning them into the pool.
'''
''' Assumptions:
''' 1) This function is never called for a candidate that should be rejected due to parameter count.
''' 2) Omitted arguments [ Call Goo(a, , b) ] are represented by OmittedArgumentExpression node in the arguments array.
''' 3) Omitted argument never has name.
''' 4) argumentNames contains Nothing for all positional arguments.
'''
''' !!! Should keep this function in sync with Binder.PassArguments, which uses data this function populates. !!!
''' !!! Should keep this function in sync with Binder.ReportOverloadResolutionFailureForASingleCandidate. !!!
''' !!! Everything we flag as an error here, Binder.ReportOverloadResolutionFailureForASingleCandidate should detect as well. !!!
''' </summary>
Private Shared Sub BuildParameterToArgumentMap(
ByRef candidate As CandidateAnalysisResult,
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
ByRef parameterToArgumentMap As ArrayBuilder(Of Integer),
ByRef paramArrayItems As ArrayBuilder(Of Integer)
)
Debug.Assert(Not arguments.IsDefault)
Debug.Assert(argumentNames.IsDefault OrElse (argumentNames.Length > 0 AndAlso argumentNames.Length = arguments.Length))
Debug.Assert(Not candidate.ArgumentMatchingDone)
Debug.Assert(candidate.State = CandidateAnalysisResultState.Applicable)
parameterToArgumentMap = ArrayBuilder(Of Integer).GetInstance(candidate.Candidate.ParameterCount, -1)
Dim argsToParams As ArrayBuilder(Of Integer) = Nothing
If Not argumentNames.IsDefault Then
argsToParams = ArrayBuilder(Of Integer).GetInstance(arguments.Length, -1)
End If
paramArrayItems = Nothing
If candidate.IsExpandedParamArrayForm Then
paramArrayItems = ArrayBuilder(Of Integer).GetInstance()
End If
'§11.8.2 Applicable Methods
'1. First, match each positional argument in order to the list of method parameters.
'If there are more positional arguments than parameters and the last parameter is not a paramarray, the method is not applicable.
'Otherwise, the paramarray parameter is expanded with parameters of the paramarray element type to match the number of positional arguments.
'If a positional argument is omitted, the method is not applicable.
' !!! Not sure about the last sentence: "If a positional argument is omitted, the method is not applicable."
' !!! Dev10 allows omitting positional argument as long as the corresponding parameter is optional.
Dim positionalArguments As Integer = 0
Dim paramIndex = 0
For i As Integer = 0 To arguments.Length - 1 Step 1
If Not argumentNames.IsDefault AndAlso argumentNames(i) IsNot Nothing Then
' A named argument
If Not candidate.Candidate.TryGetNamedParamIndex(argumentNames(i), paramIndex) Then
' ERRID_NamedParamNotFound1
' ERRID_NamedParamNotFound2
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
GoTo Bailout
End If
If paramIndex <> i Then
' all remaining arguments must be named
Exit For
End If
If paramIndex = candidate.Candidate.ParameterCount - 1 AndAlso
candidate.Candidate.Parameters(paramIndex).IsParamArray Then
' ERRID_NamedParamArrayArgument
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
GoTo Bailout
End If
Debug.Assert(parameterToArgumentMap(paramIndex) = -1)
End If
positionalArguments += 1
If argsToParams IsNot Nothing Then
argsToParams(i) = paramIndex
End If
If arguments(i).Kind = BoundKind.OmittedArgument Then
If paramIndex = candidate.Candidate.ParameterCount - 1 AndAlso
candidate.Candidate.Parameters(paramIndex).IsParamArray Then
' Omitted ParamArray argument at the call site
' ERRID_OmittedParamArrayArgument
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
GoTo Bailout
Else
parameterToArgumentMap(paramIndex) = i
paramIndex += 1
End If
ElseIf (candidate.IsExpandedParamArrayForm AndAlso
paramIndex = candidate.Candidate.ParameterCount - 1) Then
paramArrayItems.Add(i)
Else
parameterToArgumentMap(paramIndex) = i
paramIndex += 1
End If
Next
'§11.8.2 Applicable Methods
'2. Next, match each named argument to a parameter with the given name.
'If one of the named arguments fails to match, matches a paramarray parameter,
'or matches an argument already matched with another positional or named argument,
'the method is not applicable.
For i As Integer = positionalArguments To arguments.Length - 1 Step 1
Debug.Assert(argumentNames(i) Is Nothing OrElse argumentNames(i).Length > 0)
If argumentNames(i) Is Nothing Then
' Unnamed argument follows named arguments, parser should have detected an error.
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
GoTo Bailout
'Continue For
End If
If Not candidate.Candidate.TryGetNamedParamIndex(argumentNames(i), paramIndex) Then
' ERRID_NamedParamNotFound1
' ERRID_NamedParamNotFound2
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
GoTo Bailout
'Continue For
End If
If argsToParams IsNot Nothing Then
argsToParams(i) = paramIndex
End If
If paramIndex = candidate.Candidate.ParameterCount - 1 AndAlso
candidate.Candidate.Parameters(paramIndex).IsParamArray Then
' ERRID_NamedParamArrayArgument
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
GoTo Bailout
'Continue For
End If
If parameterToArgumentMap(paramIndex) <> -1 Then
' ERRID_NamedArgUsedTwice1
' ERRID_NamedArgUsedTwice2
' ERRID_NamedArgUsedTwice3
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
GoTo Bailout
'Continue For
End If
' It is an error for a named argument to specify
' a value for an explicitly omitted positional argument.
If paramIndex < positionalArguments Then
'ERRID_NamedArgAlsoOmitted1
'ERRID_NamedArgAlsoOmitted2
'ERRID_NamedArgAlsoOmitted3
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
GoTo Bailout
'Continue For
End If
parameterToArgumentMap(paramIndex) = i
Next
If argsToParams IsNot Nothing Then
candidate.ArgsToParamsOpt = argsToParams.ToImmutableAndFree()
argsToParams = Nothing
End If
Bailout:
If argsToParams IsNot Nothing Then
argsToParams.Free()
argsToParams = Nothing
End If
End Sub
''' <summary>
''' Match candidate's parameters to arguments §11.8.2 Applicable Methods.
'''
''' Note, similar to Dev10 compiler this process will eliminate candidate requiring narrowing conversions
''' if strict semantics is used, exception are candidates that require narrowing only from numeric constants.
'''
''' Assumptions:
''' 1) This function is never called for a candidate that should be rejected due to parameter count.
''' 2) Omitted arguments [ Call Goo(a, , b) ] are represented by OmittedArgumentExpression node in the arguments array.
''' 3) Omitted argument never has name.
''' 4) argumentNames contains Nothing for all positional arguments.
'''
''' !!! Should keep this function in sync with Binder.PassArguments, which uses data this function populates. !!!
''' !!! Should keep this function in sync with Binder.ReportOverloadResolutionFailureForASingleCandidate. !!!
''' !!! Should keep this function in sync with InferenceGraph.PopulateGraph. !!!
''' !!! Everything we flag as an error here, Binder.ReportOverloadResolutionFailureForASingleCandidate should detect as well. !!!
''' </summary>
Private Shared Sub MatchArguments(
methodOrPropertyGroup As BoundMethodOrPropertyGroup,
ByRef candidate As CandidateAnalysisResult,
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
binder As Binder,
<[In](), Out()> ByRef asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression),
callerInfoOpt As SyntaxNode,
forceExpandedForm As Boolean,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
)
Debug.Assert(Not arguments.IsDefault)
Debug.Assert(argumentNames.IsDefault OrElse (argumentNames.Length > 0 AndAlso argumentNames.Length = arguments.Length))
Debug.Assert(Not candidate.ArgumentMatchingDone)
Debug.Assert(candidate.State = CandidateAnalysisResultState.Applicable)
Debug.Assert(Not candidate.Candidate.UnderlyingSymbol.IsReducedExtensionMethod() OrElse methodOrPropertyGroup.ReceiverOpt IsNot Nothing OrElse TypeOf methodOrPropertyGroup.SyntaxTree Is DummySyntaxTree)
Dim parameterToArgumentMap As ArrayBuilder(Of Integer) = Nothing
Dim paramArrayItems As ArrayBuilder(Of Integer) = Nothing
Dim conversionKinds As KeyValuePair(Of ConversionKind, MethodSymbol)() = Nothing
Dim conversionBackKinds As KeyValuePair(Of ConversionKind, MethodSymbol)() = Nothing
Dim optionalArguments As OptionalArgument() = Nothing
Dim defaultValueDiagnostics As BindingDiagnosticBag = Nothing
BuildParameterToArgumentMap(candidate, arguments, argumentNames, parameterToArgumentMap, paramArrayItems)
If candidate.State <> CandidateAnalysisResultState.Applicable Then
Debug.Assert(Not candidate.IgnoreExtensionMethods)
GoTo Bailout
End If
' At this point we will set IgnoreExtensionMethods to true and will
' clear it when appropriate because not every failure should allow
' us to consider extension methods.
If Not candidate.Candidate.IsExtensionMethod Then
candidate.IgnoreExtensionMethods = True
End If
'§11.8.2 Applicable Methods
'The type arguments, if any, must satisfy the constraints, if any, on the matching type parameters.
Dim candidateSymbol = candidate.Candidate.UnderlyingSymbol
If candidateSymbol.Kind = SymbolKind.Method Then
Dim method = DirectCast(candidateSymbol, MethodSymbol)
If method.IsGenericMethod Then
Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance()
Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing
Dim satisfiedConstraints = method.CheckConstraints(binder.Compilation.LanguageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=useSiteInfo)
diagnosticsBuilder.Free()
If useSiteDiagnosticsBuilder IsNot Nothing AndAlso useSiteDiagnosticsBuilder.Count > 0 Then
For Each diag In useSiteDiagnosticsBuilder
useSiteInfo.Add(diag.UseSiteInfo)
Next
End If
If Not satisfiedConstraints Then
' Do not clear IgnoreExtensionMethods flag if constraints are violated.
candidate.State = CandidateAnalysisResultState.GenericConstraintsViolated
GoTo Bailout
End If
End If
End If
' Traverse the parameters, converting corresponding arguments
' as appropriate.
Dim argIndex As Integer
Dim candidateIsAProperty As Boolean = (candidate.Candidate.UnderlyingSymbol.Kind = SymbolKind.Property)
For paramIndex = 0 To candidate.Candidate.ParameterCount - 1 Step 1
If candidate.State <> CandidateAnalysisResultState.Applicable AndAlso
Not candidate.IgnoreExtensionMethods Then
' There is no reason to continue. We will not learn anything new.
GoTo Bailout
End If
Dim param As ParameterSymbol = candidate.Candidate.Parameters(paramIndex)
Dim isByRef As Boolean = param.IsByRef
Dim targetType As TypeSymbol = param.Type
If param.IsParamArray AndAlso paramIndex = candidate.Candidate.ParameterCount - 1 Then
If targetType.Kind <> SymbolKind.ArrayType Then
' ERRID_ParamArrayWrongType
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
candidate.IgnoreExtensionMethods = False
GoTo Bailout
'Continue For
End If
If Not candidate.IsExpandedParamArrayForm Then
argIndex = parameterToArgumentMap(paramIndex)
Dim paramArrayArgument = If(argIndex = -1, Nothing, arguments(argIndex))
Debug.Assert(paramArrayArgument Is Nothing OrElse paramArrayArgument.Kind <> BoundKind.OmittedArgument)
'§11.8.2 Applicable Methods
'If the conversion from the type of the argument expression to the paramarray type is narrowing,
'then the method is only applicable in its expanded form.
'!!! However, there is an exception to that rule - narrowing conversion from semantical Nothing literal is Ok. !!!
Dim arrayConversion As KeyValuePair(Of ConversionKind, MethodSymbol) = Nothing
If Not (paramArrayArgument IsNot Nothing AndAlso
Not paramArrayArgument.HasErrors AndAlso CanPassToParamArray(paramArrayArgument, targetType, arrayConversion, binder, useSiteInfo)) Then
' It doesn't look like native compiler reports any errors in this case.
' Probably due to assumption that either errors were already reported for bad argument expression or
' we will report errors for expanded version of the same candidate.
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
candidate.IgnoreExtensionMethods = False
GoTo Bailout
'Continue For
ElseIf Conversions.IsNarrowingConversion(arrayConversion.Key) Then
' We can get here only for Object with constant value == Nothing.
Debug.Assert(paramArrayArgument.IsNothingLiteral())
' Unlike for other arguments, Dev10 doesn't make a note of this narrowing.
' However, should this narrowing cause a conversion error, the error must be noted.
If binder.OptionStrict = OptionStrict.On Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
' Note, this doesn't clear IgnoreExtensionMethods flag.
Continue For
End If
Else
Debug.Assert(Conversions.IsWideningConversion(arrayConversion.Key))
End If
' Since CanPassToParamArray succeeded, there is no need to check conversions for this argument again
If Not Conversions.IsIdentityConversion(arrayConversion.Key) Then
If conversionKinds Is Nothing Then
conversionKinds = New KeyValuePair(Of ConversionKind, MethodSymbol)(arguments.Length - 1) {}
For i As Integer = 0 To conversionKinds.Length - 1
conversionKinds(i) = Conversions.Identity
Next
End If
conversionKinds(argIndex) = arrayConversion
End If
Else
Debug.Assert(candidate.IsExpandedParamArrayForm)
'§11.8.2 Applicable Methods
' If the argument expression is the literal Nothing, then the method is only applicable in its unexpanded form.
' Note, that explicitly converted NOTHING is treated the same way by Dev10.
' But, for the purpose of interpolated string lowering the method is applicable even if the argument expression
' is the literal Nothing. This is because for interpolated string lowering we want to always call methods
' in their expanded form. E.g. $"{Nothing}" should be lowered to String.Format("{0}", New Object() {Nothing}) not
' String.Format("{0}", CType(Nothing, Object())).
If paramArrayItems.Count = 1 AndAlso arguments(paramArrayItems(0)).IsNothingLiteral() AndAlso Not forceExpandedForm Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
candidate.IgnoreExtensionMethods = False
GoTo Bailout
'Continue For
End If
' Otherwise, for a ParamArray parameter, all the matching arguments are passed
' ByVal as instances of the element type of the ParamArray.
' Perform the conversions to the element type of the ParamArray here.
Dim arrayType = DirectCast(targetType, ArrayTypeSymbol)
If Not arrayType.IsSZArray Then
' ERRID_ParamArrayWrongType
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
candidate.IgnoreExtensionMethods = False
GoTo Bailout
'Continue For
End If
targetType = arrayType.ElementType
If targetType.Kind = SymbolKind.ErrorType Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
' Note, IgnoreExtensionMethods is not cleared.
Continue For
End If
For j As Integer = 0 To paramArrayItems.Count - 1 Step 1
Dim conv As KeyValuePair(Of ConversionKind, MethodSymbol) = Nothing
If arguments(paramArrayItems(j)).HasErrors Then ' UNDONE: should HasErrors really always cause argument mismatch [petergo, 3/9/2011]
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
candidate.IgnoreExtensionMethods = False
GoTo Bailout
'Continue For
End If
If Not MatchArgumentToByValParameter(methodOrPropertyGroup, candidate, arguments(paramArrayItems(j)), targetType, binder, conv, asyncLambdaSubToFunctionMismatch, useSiteInfo) Then
' Note, IgnoreExtensionMethods is not cleared here, MatchArgumentToByValParameter makes required changes.
Continue For
End If
' typically all conversions in otherwise acceptable candidate are identity conversions
If Not Conversions.IsIdentityConversion(conv.Key) Then
If conversionKinds Is Nothing Then
conversionKinds = New KeyValuePair(Of ConversionKind, MethodSymbol)(arguments.Length - 1) {}
For i As Integer = 0 To conversionKinds.Length - 1
conversionKinds(i) = Conversions.Identity
Next
End If
conversionKinds(paramArrayItems(j)) = conv
End If
Next
End If
Continue For
End If
argIndex = parameterToArgumentMap(paramIndex)
Dim argument = If(argIndex = -1, Nothing, arguments(argIndex))
Dim defaultArgument As BoundExpression = Nothing
If argument Is Nothing OrElse argument.Kind = BoundKind.OmittedArgument Then
' Deal with Optional arguments.
If defaultValueDiagnostics Is Nothing Then
defaultValueDiagnostics = BindingDiagnosticBag.GetInstance()
Else
defaultValueDiagnostics.Clear()
End If
Dim receiverOpt As BoundExpression = Nothing
If candidateSymbol.IsReducedExtensionMethod() Then
receiverOpt = methodOrPropertyGroup.ReceiverOpt
End If
defaultArgument = binder.GetArgumentForParameterDefaultValue(param, If(argument, methodOrPropertyGroup).Syntax, defaultValueDiagnostics, callerInfoOpt, parameterToArgumentMap, arguments, receiverOpt)
If defaultArgument IsNot Nothing AndAlso Not defaultValueDiagnostics.HasAnyErrors Then
Debug.Assert(Not defaultValueDiagnostics.DiagnosticBag.AsEnumerable().Any())
' Mark these as compiler generated so they are ignored by later phases. For example,
' these bound nodes will mess up the incremental binder cache, because they use the
' the same syntax node as the method identifier from the invocation / AddressOf if they
' are not marked.
defaultArgument.SetWasCompilerGenerated()
argument = defaultArgument
Else
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
'Note, IgnoreExtensionMethods flag should not be cleared due to a badness of default value.
candidate.IgnoreExtensionMethods = False
GoTo Bailout
End If
End If
If targetType.Kind = SymbolKind.ErrorType Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
' Note, IgnoreExtensionMethods is not cleared.
Continue For
End If
If argument.HasErrors Then ' UNDONE: should HasErrors really always cause argument mismatch [petergo, 3/9/2011]
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
candidate.IgnoreExtensionMethods = False
GoTo Bailout
End If
Dim conversion As KeyValuePair(Of ConversionKind, MethodSymbol) = Nothing
Dim conversionBack As KeyValuePair(Of ConversionKind, MethodSymbol) = Nothing
Debug.Assert(Not isByRef OrElse param.IsExplicitByRef OrElse targetType.IsStringType())
' Arguments for properties are always passed with ByVal semantics. Even if
' parameter in metadata is defined ByRef, we always pass corresponding argument
' through a temp without copy-back.
' Non-string arguments for implicitly ByRef string parameters of Declare functions
' are passed through a temp without copy-back.
If isByRef AndAlso Not candidateIsAProperty AndAlso defaultArgument Is Nothing AndAlso
(param.IsExplicitByRef OrElse (argument.Type IsNot Nothing AndAlso argument.Type.IsStringType())) Then
MatchArgumentToByRefParameter(methodOrPropertyGroup, candidate, argument, targetType, binder, conversion, conversionBack, asyncLambdaSubToFunctionMismatch, useSiteInfo)
Else
conversionBack = Conversions.Identity
MatchArgumentToByValParameter(methodOrPropertyGroup, candidate, argument, targetType, binder, conversion, asyncLambdaSubToFunctionMismatch, useSiteInfo, defaultArgument IsNot Nothing)
End If
' typically all conversions in otherwise acceptable candidate are identity conversions
If Not Conversions.IsIdentityConversion(conversion.Key) Then
If conversionKinds Is Nothing Then
conversionKinds = New KeyValuePair(Of ConversionKind, MethodSymbol)(arguments.Length - 1) {}
For i As Integer = 0 To conversionKinds.Length - 1
conversionKinds(i) = Conversions.Identity
Next
End If
' If this is not a default argument then store the conversion in the conversionKinds.
' For default arguments the conversion is stored below.
If defaultArgument Is Nothing Then
conversionKinds(argIndex) = conversion
End If
End If
' If this is a default argument then add it to the candidate result default arguments.
' Note these arguments are stored by parameter index. Default arguments are missing so they
' may not have an argument index.
If defaultArgument IsNot Nothing Then
If optionalArguments Is Nothing Then
optionalArguments = New OptionalArgument(candidate.Candidate.ParameterCount - 1) {}
End If
optionalArguments(paramIndex) = New OptionalArgument(defaultArgument, conversion, defaultValueDiagnostics.DependenciesBag.ToImmutableArray())
End If
If Not Conversions.IsIdentityConversion(conversionBack.Key) Then
If conversionBackKinds Is Nothing Then
' There should never be a copy back conversion with a default argument.
Debug.Assert(defaultArgument Is Nothing)
conversionBackKinds = New KeyValuePair(Of ConversionKind, MethodSymbol)(arguments.Length - 1) {}
For i As Integer = 0 To conversionBackKinds.Length - 1
conversionBackKinds(i) = Conversions.Identity
Next
End If
conversionBackKinds(argIndex) = conversionBack
End If
Next
Bailout:
If defaultValueDiagnostics IsNot Nothing Then
defaultValueDiagnostics.Free()
End If
If paramArrayItems IsNot Nothing Then
paramArrayItems.Free()
End If
If conversionKinds IsNot Nothing Then
candidate.ConversionsOpt = conversionKinds.AsImmutableOrNull()
End If
If conversionBackKinds IsNot Nothing Then
candidate.ConversionsBackOpt = conversionBackKinds.AsImmutableOrNull()
End If
If optionalArguments IsNot Nothing Then
candidate.OptionalArguments = optionalArguments.AsImmutableOrNull()
End If
If parameterToArgumentMap IsNot Nothing Then
parameterToArgumentMap.Free()
End If
End Sub
''' <summary>
''' Should be in sync with Binder.ReportByRefConversionErrors.
''' </summary>
Private Shared Sub MatchArgumentToByRefParameter(
methodOrPropertyGroup As BoundMethodOrPropertyGroup,
ByRef candidate As CandidateAnalysisResult,
argument As BoundExpression,
targetType As TypeSymbol,
binder As Binder,
<Out()> ByRef outConversionKind As KeyValuePair(Of ConversionKind, MethodSymbol),
<Out()> ByRef outConversionBackKind As KeyValuePair(Of ConversionKind, MethodSymbol),
<[In](), Out()> ByRef asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
)
If argument.IsSupportingAssignment() Then
If argument.IsLValue() AndAlso targetType.IsSameTypeIgnoringAll(argument.Type) Then
outConversionKind = Conversions.Identity
outConversionBackKind = Conversions.Identity
Else
outConversionBackKind = Conversions.Identity
If MatchArgumentToByValParameter(methodOrPropertyGroup, candidate, argument, targetType, binder, outConversionKind, asyncLambdaSubToFunctionMismatch, useSiteInfo) Then
' Check copy back conversion
Dim copyBackType = argument.GetTypeOfAssignmentTarget()
Dim conv As KeyValuePair(Of ConversionKind, MethodSymbol) = Conversions.ClassifyConversion(targetType, copyBackType, useSiteInfo)
outConversionBackKind = conv
If Conversions.NoConversion(conv.Key) Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch ' Possible only with user-defined conversions, I think.
candidate.IgnoreExtensionMethods = False
Else
If Conversions.IsNarrowingConversion(conv.Key) Then
' Similar to Dev10 compiler, we will eliminate candidate requiring narrowing conversions
' if strict semantics is used, exception are candidates that require narrowing only from
' numeric(Constants.
candidate.SetRequiresNarrowingConversion()
Debug.Assert((conv.Key And ConversionKind.InvolvesNarrowingFromNumericConstant) = 0)
candidate.SetRequiresNarrowingNotFromNumericConstant()
If binder.OptionStrict = OptionStrict.On Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
Return
End If
If targetType.SpecialType <> SpecialType.System_Object Then
candidate.SetRequiresNarrowingNotFromObject()
End If
End If
candidate.RegisterDelegateRelaxationLevel(conv.Key)
End If
End If
End If
Else
' No copy back needed
' If we are inside a lambda in a constructor and are passing ByRef a non-LValue field, which
' would be an LValue field, if it were referred to in the constructor outside of a lambda,
' we need to report an error because the operation will result in a simulated pass by
' ref (through a temp, without a copy back), which might be not the intent.
If binder.Report_ERRID_ReadOnlyInClosure(argument) Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
' Note, we do not change IgnoreExtensionMethods flag here.
End If
outConversionBackKind = Conversions.Identity
MatchArgumentToByValParameter(methodOrPropertyGroup, candidate, argument, targetType, binder, outConversionKind, asyncLambdaSubToFunctionMismatch, useSiteInfo)
End If
End Sub
''' <summary>
''' Should be in sync with Binder.ReportByValConversionErrors.
''' </summary>
Private Shared Function MatchArgumentToByValParameter(
methodOrPropertyGroup As BoundMethodOrPropertyGroup,
ByRef candidate As CandidateAnalysisResult,
argument As BoundExpression,
targetType As TypeSymbol,
binder As Binder,
<Out()> ByRef outConversionKind As KeyValuePair(Of ConversionKind, MethodSymbol),
<[In](), Out()> ByRef asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
Optional isDefaultValueArgument As Boolean = False
) As Boolean
outConversionKind = Nothing 'VBConversions.NoConversion
' TODO: Do we need to do more thorough check for error types here, i.e. dig into generics,
' arrays, etc., detect types from unreferenced assemblies, ... ?
If targetType.IsErrorType() Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
' Note, IgnoreExtensionMethods is not cleared.
Return False
End If
Dim conv As KeyValuePair(Of ConversionKind, MethodSymbol) = Conversions.ClassifyConversion(argument, targetType, binder, useSiteInfo)
outConversionKind = conv
If Conversions.NoConversion(conv.Key) Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
candidate.IgnoreExtensionMethods = False
If (conv.Key And (ConversionKind.DelegateRelaxationLevelMask Or ConversionKind.Lambda)) = (ConversionKind.DelegateRelaxationLevelInvalid Or ConversionKind.Lambda) Then
' Dig through parenthesized
Dim underlying As BoundExpression = argument
While underlying.Kind = BoundKind.Parenthesized AndAlso underlying.Type Is Nothing
underlying = DirectCast(underlying, BoundParenthesized).Expression
End While
Dim unbound = If(underlying.Kind = BoundKind.UnboundLambda, DirectCast(underlying, UnboundLambda), Nothing)
If unbound IsNot Nothing AndAlso Not unbound.IsFunctionLambda AndAlso
(unbound.Flags And SourceMemberFlags.Async) <> 0 AndAlso
targetType.IsDelegateType Then
Dim delegateInvoke As MethodSymbol = DirectCast(targetType, NamedTypeSymbol).DelegateInvokeMethod
Debug.Assert(delegateInvoke IsNot Nothing)
If delegateInvoke IsNot Nothing Then
Dim bound As BoundLambda = unbound.GetBoundLambda(New UnboundLambda.TargetSignature(delegateInvoke))
Debug.Assert(bound IsNot Nothing)
If bound IsNot Nothing AndAlso (bound.MethodConversionKind And MethodConversionKind.AllErrorReasons) = MethodConversionKind.Error_SubToFunction AndAlso
(Not bound.Diagnostics.Diagnostics.HasAnyErrors) Then
If asyncLambdaSubToFunctionMismatch Is Nothing Then
asyncLambdaSubToFunctionMismatch = New HashSet(Of BoundExpression)(ReferenceEqualityComparer.Instance)
End If
asyncLambdaSubToFunctionMismatch.Add(unbound)
End If
End If
End If
End If
Return False
End If
' Characteristics of conversion applied to a default value for an optional parameter shouldn't be used to disambiguate
' between two candidates.
If Conversions.IsNarrowingConversion(conv.Key) Then
' Similar to Dev10 compiler, we will eliminate candidate requiring narrowing conversions
' if strict semantics is used, exception are candidates that require narrowing only from
' numeric constants.
If Not isDefaultValueArgument Then
candidate.SetRequiresNarrowingConversion()
End If
If (conv.Key And ConversionKind.InvolvesNarrowingFromNumericConstant) = 0 Then
If Not isDefaultValueArgument Then
candidate.SetRequiresNarrowingNotFromNumericConstant()
End If
If binder.OptionStrict = OptionStrict.On Then
candidate.State = CandidateAnalysisResultState.ArgumentMismatch
Return False
End If
End If
Dim argumentType = argument.Type
If argumentType Is Nothing OrElse
argumentType.SpecialType <> SpecialType.System_Object Then
If Not isDefaultValueArgument Then
candidate.SetRequiresNarrowingNotFromObject()
End If
End If
ElseIf (conv.Key And ConversionKind.InvolvesNarrowingFromNumericConstant) <> 0 Then
' Dev10 overload resolution treats conversions that involve narrowing from numeric constant type
' as narrowing.
If Not isDefaultValueArgument Then
candidate.SetRequiresNarrowingConversion()
candidate.SetRequiresNarrowingNotFromObject()
End If
End If
If Not isDefaultValueArgument Then
candidate.RegisterDelegateRelaxationLevel(conv.Key)
End If
' If we are in attribute context, keep track of candidates that will result in illegal arguments.
' They should be dismissed in favor of other applicable candidates.
If binder.BindingLocation = BindingLocation.Attribute AndAlso
Not candidate.IsIllegalInAttribute AndAlso
Not methodOrPropertyGroup.WasCompilerGenerated AndAlso
methodOrPropertyGroup.Kind = BoundKind.MethodGroup AndAlso
IsWithinAppliedAttributeName(methodOrPropertyGroup.Syntax) AndAlso
DirectCast(candidate.Candidate.UnderlyingSymbol, MethodSymbol).MethodKind = MethodKind.Constructor AndAlso
binder.Compilation.GetWellKnownType(WellKnownType.System_Attribute).IsBaseTypeOf(candidate.Candidate.UnderlyingSymbol.ContainingType, useSiteInfo) Then
Debug.Assert(Not argument.HasErrors)
Dim passedExpression As BoundExpression = binder.PassArgumentByVal(argument, conv, targetType, BindingDiagnosticBag.Discarded)
If Not passedExpression.IsConstant Then ' Trying to match native compiler behavior in Semantics::IsValidAttributeConstant
Dim visitor As New Binder.AttributeExpressionVisitor(binder, passedExpression.HasErrors)
visitor.VisitExpression(passedExpression, BindingDiagnosticBag.Discarded)
If visitor.HasErrors Then
candidate.SetIllegalInAttribute()
End If
End If
End If
Return True
End Function
Private Shared Function IsWithinAppliedAttributeName(syntax As SyntaxNode) As Boolean
Dim parent As SyntaxNode = syntax.Parent
While parent IsNot Nothing
If parent.Kind = SyntaxKind.Attribute Then
Return DirectCast(parent, AttributeSyntax).Name.Span.Contains(syntax.Position)
ElseIf TypeOf parent Is ExpressionSyntax OrElse TypeOf parent Is StatementSyntax Then
Exit While
End If
parent = parent.Parent
End While
Return False
End Function
Public Shared Function CanPassToParamArray(
expression As BoundExpression,
targetType As TypeSymbol,
<Out()> ByRef outConvKind As KeyValuePair(Of ConversionKind, MethodSymbol),
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Boolean
'§11.8.2 Applicable Methods
'If the conversion from the type of the argument expression to the paramarray type is narrowing,
'then the method is only applicable in its expanded form.
outConvKind = Conversions.ClassifyConversion(expression, targetType, binder, useSiteInfo)
' Note, user-defined conversions are acceptable here.
If Conversions.IsWideningConversion(outConvKind.Key) Then
Return True
End If
' Dev10 allows explicitly converted NOTHING as an argument for a ParamArray parameter,
' even if conversion to the array type is narrowing.
If IsNothingLiteral(expression) Then
Debug.Assert(Conversions.IsNarrowingConversion(outConvKind.Key))
Return True
End If
Return False
End Function
''' <summary>
''' Performs an initial pass through the group of candidates and does
''' the following in the process.
''' 1) Eliminates candidates based on the number of supplied arguments and number of supplied generic type arguments.
''' 2) Adds additional entries for expanded ParamArray forms when applicable.
''' 3) Infers method's generic type arguments if needed.
''' 4) Substitutes method's generic type arguments.
''' 5) Eliminates candidates based on shadowing by signature.
''' This partially takes care of §11.8.1 Overloaded Method Resolution, section 7.1.
''' If M is defined in a more derived type than N, eliminate N from the set.
''' 6) Eliminates candidates with identical virtual signatures by applying various shadowing and
''' tie-breaking rules from §11.8.1 Overloaded Method Resolution, section 7.0
''' • If M has fewer parameters from an expanded paramarray than N, eliminate N from the set.
''' 7) Takes care of unsupported overloading within the same type for instance methods/properties.
'''
''' Assumptions:
''' 1) Shadowing by name has been already applied.
''' 2) group can include extension methods.
''' 3) group contains original definitions, i.e. method type arguments have not been substituted yet.
''' Exception are extension methods with type parameters substituted based on receiver type rather
''' than based on type arguments supplied at the call site.
''' 4) group contains only accessible candidates.
''' 5) group doesn't contain members involved into unsupported overloading, i.e. differ by casing or custom modifiers only.
''' 6) group does not contain duplicates.
''' 7) All elements of arguments array are Not Nothing, omitted arguments are represented by OmittedArgumentExpression node.
''' </summary>
''' <remarks>
''' This method is destructive to content of the [group] parameter.
''' </remarks>
Private Shared Sub CollectOverloadedCandidates(
binder As Binder,
results As ArrayBuilder(Of CandidateAnalysisResult),
group As ArrayBuilder(Of Candidate),
typeArguments As ImmutableArray(Of TypeSymbol),
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
delegateReturnType As TypeSymbol,
delegateReturnTypeReferenceBoundNode As BoundNode,
includeEliminatedCandidates As Boolean,
isQueryOperatorInvocation As Boolean,
forceExpandedForm As Boolean,
<[In](), Out()> ByRef asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
)
Debug.Assert(results IsNot Nothing)
Debug.Assert(argumentNames.IsDefault OrElse (argumentNames.Length > 0 AndAlso argumentNames.Length = arguments.Length))
Dim quickInfo = ArrayBuilder(Of QuickApplicabilityInfo).GetInstance()
Dim sourceModule As ModuleSymbol = binder.SourceModule
For i As Integer = 0 To group.Count - 1
If group(i) Is Nothing Then
Continue For
End If
Dim info As QuickApplicabilityInfo = DoQuickApplicabilityCheck(group(i), typeArguments, arguments, isQueryOperatorInvocation, forceExpandedForm, useSiteInfo)
If info.Candidate Is Nothing Then
Continue For
End If
If info.Candidate.UnderlyingSymbol.ContainingModule Is sourceModule OrElse
info.Candidate.IsExtensionMethod Then
CollectOverloadedCandidate(results, info, typeArguments, arguments, argumentNames,
delegateReturnType, delegateReturnTypeReferenceBoundNode,
includeEliminatedCandidates, binder, asyncLambdaSubToFunctionMismatch,
useSiteInfo)
Continue For
End If
' Deal with VB-illegal overloading in imported types.
' We are trying to avoid doing signature comparison as much as possible and limit them to
' cases when at least one candidate is applicable based on the quick applicability check.
' Similar code exists in overriding checks in OverrideHidingHelper.RemoveMembersWithConflictingAccessibility.
Dim container As Symbol = info.Candidate.UnderlyingSymbol.ContainingSymbol
' If there are more candidates from this type, collect all of them in quickInfo array,
' but keep the applicable candidates at the beginning
quickInfo.Clear()
quickInfo.Add(info)
Dim applicableCount As Integer = If(info.State = CandidateAnalysisResultState.Applicable, 1, 0)
For j As Integer = i + 1 To group.Count - 1
If group(j) Is Nothing OrElse
group(j).IsExtensionMethod Then ' VS2013 ignores illegal overloading for extension methods
Continue For
End If
If container = group(j).UnderlyingSymbol.ContainingSymbol Then
info = DoQuickApplicabilityCheck(group(j), typeArguments, arguments, isQueryOperatorInvocation, forceExpandedForm, useSiteInfo)
group(j) = Nothing
If info.Candidate Is Nothing Then
Continue For
End If
' Keep applicable candidates at the beginning.
If info.State <> CandidateAnalysisResultState.Applicable Then
quickInfo.Add(info)
ElseIf applicableCount = quickInfo.Count Then
quickInfo.Add(info)
applicableCount += 1
Else
quickInfo.Add(quickInfo(applicableCount))
quickInfo(applicableCount) = info
applicableCount += 1
End If
End If
Next
' Now see if any candidates are ambiguous or lose against other candidates in the quickInfo array.
' This loop is destructive to the content of the quickInfo, some applicable candidates could be replaced with
' a "better" candidate, even though that candidate is not applicable, "losers" are deleted, etc.
For k As Integer = 0 To If(applicableCount > 0 OrElse Not includeEliminatedCandidates, applicableCount, quickInfo.Count) - 1
info = quickInfo(k)
If info.Candidate Is Nothing OrElse info.State = CandidateAnalysisResultState.Ambiguous Then
Continue For
End If
#If DEBUG Then
Dim isExtensionMethod As Boolean = info.Candidate.IsExtensionMethod
#End If
Dim firstSymbol As Symbol = info.Candidate.UnderlyingSymbol.OriginalDefinition
If firstSymbol.IsReducedExtensionMethod() Then
firstSymbol = DirectCast(firstSymbol, MethodSymbol).ReducedFrom
End If
For l As Integer = k + 1 To quickInfo.Count - 1
Dim info2 As QuickApplicabilityInfo = quickInfo(l)
If info2.Candidate Is Nothing OrElse info2.State = CandidateAnalysisResultState.Ambiguous Then
Continue For
End If
#If DEBUG Then
Debug.Assert(isExtensionMethod = info2.Candidate.IsExtensionMethod)
#End If
Dim secondSymbol As Symbol = info2.Candidate.UnderlyingSymbol.OriginalDefinition
If secondSymbol.IsReducedExtensionMethod() Then
secondSymbol = DirectCast(secondSymbol, MethodSymbol).ReducedFrom
End If
' The following check should be similar to the one performed by SourceNamedTypeSymbol.CheckForOverloadsErrors
' However, we explicitly ignore custom modifiers here, since this part is NYI for SourceNamedTypeSymbol.
Const significantDifferences As SymbolComparisonResults = SymbolComparisonResults.AllMismatches And
Not SymbolComparisonResults.MismatchesForConflictingMethods
Dim comparisonResults As SymbolComparisonResults = OverrideHidingHelper.DetailedSignatureCompare(
firstSymbol,
secondSymbol,
significantDifferences,
significantDifferences)
' Signature must be considered equal following VB rules.
If comparisonResults = 0 Then
Dim accessibilityCmp As Integer = LookupResult.CompareAccessibilityOfSymbolsConflictingInSameContainer(firstSymbol, secondSymbol)
If accessibilityCmp > 0 Then
' first wins
quickInfo(l) = Nothing
ElseIf accessibilityCmp < 0 Then
' second wins
quickInfo(k) = info2
quickInfo(l) = Nothing
firstSymbol = secondSymbol
info = info2
Else
Debug.Assert(accessibilityCmp = 0)
info = New QuickApplicabilityInfo(info.Candidate, CandidateAnalysisResultState.Ambiguous)
quickInfo(k) = info
quickInfo(l) = New QuickApplicabilityInfo(info2.Candidate, CandidateAnalysisResultState.Ambiguous)
End If
End If
Next
If info.State <> CandidateAnalysisResultState.Ambiguous Then
CollectOverloadedCandidate(results, info, typeArguments, arguments, argumentNames,
delegateReturnType, delegateReturnTypeReferenceBoundNode,
includeEliminatedCandidates, binder, asyncLambdaSubToFunctionMismatch,
useSiteInfo)
ElseIf includeEliminatedCandidates Then
CollectOverloadedCandidate(results, info, typeArguments, arguments, argumentNames,
delegateReturnType, delegateReturnTypeReferenceBoundNode,
includeEliminatedCandidates, binder, asyncLambdaSubToFunctionMismatch,
useSiteInfo)
For l As Integer = k + 1 To quickInfo.Count - 1
Dim info2 As QuickApplicabilityInfo = quickInfo(l)
If info2.Candidate IsNot Nothing AndAlso info2.State = CandidateAnalysisResultState.Ambiguous Then
quickInfo(l) = Nothing
CollectOverloadedCandidate(results, info2, typeArguments, arguments, argumentNames,
delegateReturnType, delegateReturnTypeReferenceBoundNode,
includeEliminatedCandidates, binder, asyncLambdaSubToFunctionMismatch,
useSiteInfo)
End If
Next
End If
Next
Next
quickInfo.Free()
#If DEBUG Then
group.Clear()
#End If
End Sub
Private Structure QuickApplicabilityInfo
Public ReadOnly Candidate As Candidate
Public ReadOnly State As CandidateAnalysisResultState
Public ReadOnly AppliesToNormalForm As Boolean
Public ReadOnly AppliesToParamArrayForm As Boolean
Public Sub New(
candidate As Candidate,
state As CandidateAnalysisResultState,
Optional appliesToNormalForm As Boolean = True,
Optional appliesToParamArrayForm As Boolean = True
)
Debug.Assert(candidate IsNot Nothing)
Debug.Assert(appliesToNormalForm OrElse appliesToParamArrayForm)
Me.Candidate = candidate
Me.State = state
Me.AppliesToNormalForm = appliesToNormalForm
Me.AppliesToParamArrayForm = appliesToParamArrayForm
End Sub
End Structure
Private Shared Function DoQuickApplicabilityCheck(
candidate As Candidate,
typeArguments As ImmutableArray(Of TypeSymbol),
arguments As ImmutableArray(Of BoundExpression),
isQueryOperatorInvocation As Boolean,
forceExpandedForm As Boolean,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As QuickApplicabilityInfo
If isQueryOperatorInvocation AndAlso DirectCast(candidate.UnderlyingSymbol, MethodSymbol).IsSub Then
' Subs are never considered as candidates for Query Operators, but method group might have subs in it.
Return Nothing
End If
If candidate.UnderlyingSymbol.HasUnsupportedMetadata Then
Return New QuickApplicabilityInfo(candidate, CandidateAnalysisResultState.HasUnsupportedMetadata)
End If
' If type arguments have been supplied, eliminate procedures that don't have an
' appropriate number of type parameters.
'§11.8.2 Applicable Methods
'Section 4.
' If type arguments have been specified, they are matched against the type parameter list.
' If the two lists do not have the same number of elements, the method is not applicable,
' unless the type argument list is empty.
If typeArguments.Length > 0 AndAlso candidate.Arity <> typeArguments.Length Then
Return New QuickApplicabilityInfo(candidate, CandidateAnalysisResultState.BadGenericArity)
End If
' Eliminate procedures that cannot accept the number of supplied arguments.
Dim requiredCount As Integer
Dim maxCount As Integer
Dim hasParamArray As Boolean
candidate.GetAllParameterCounts(requiredCount, maxCount, hasParamArray)
'§11.8.2 Applicable Methods
'If there are more positional arguments than parameters and the last parameter is not a paramarray,
'the method is not applicable. Otherwise, the paramarray parameter is expanded with parameters of
'the paramarray element type to match the number of positional arguments. If a single argument expression
'matches a paramarray parameter and the type of the argument expression is convertible to both the type of
'the paramarray parameter and the paramarray element type, the method is applicable in both its expanded
'and unexpanded forms, with two exceptions. If the conversion from the type of the argument expression to
'the paramarray type is narrowing, then the method is only applicable in its expanded form. If the argument
'expression is the literal Nothing, then the method is only applicable in its unexpanded form.
If isQueryOperatorInvocation Then
' Query operators require exact match for argument count.
If arguments.Length <> maxCount Then
Return New QuickApplicabilityInfo(candidate, CandidateAnalysisResultState.ArgumentCountMismatch, True, False)
End If
ElseIf arguments.Length < requiredCount OrElse
(Not hasParamArray AndAlso arguments.Length > maxCount) Then
Return New QuickApplicabilityInfo(candidate, CandidateAnalysisResultState.ArgumentCountMismatch, Not hasParamArray, hasParamArray)
End If
Dim candidateUseSiteInfo As UseSiteInfo(Of AssemblySymbol) = candidate.UnderlyingSymbol.GetUseSiteInfo()
useSiteInfo.Add(candidateUseSiteInfo)
If candidateUseSiteInfo.DiagnosticInfo IsNot Nothing Then
Return New QuickApplicabilityInfo(candidate, CandidateAnalysisResultState.HasUseSiteError)
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). 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).
Dim applicableInNormalForm As Boolean = False
Dim applicableInParamArrayForm As Boolean = False
If Not hasParamArray OrElse (arguments.Length = maxCount AndAlso Not forceExpandedForm) Then
applicableInNormalForm = True
End If
' How about it's expanded form? It always applies if there's a paramarray.
If hasParamArray AndAlso Not isQueryOperatorInvocation Then
applicableInParamArrayForm = True
End If
Return New QuickApplicabilityInfo(candidate, CandidateAnalysisResultState.Applicable, applicableInNormalForm, applicableInParamArrayForm)
End Function
Private Shared Sub CollectOverloadedCandidate(
results As ArrayBuilder(Of CandidateAnalysisResult),
candidate As QuickApplicabilityInfo,
typeArguments As ImmutableArray(Of TypeSymbol),
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
delegateReturnType As TypeSymbol,
delegateReturnTypeReferenceBoundNode As BoundNode,
includeEliminatedCandidates As Boolean,
binder As Binder,
<[In](), Out()> ByRef asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
)
Select Case candidate.State
Case CandidateAnalysisResultState.HasUnsupportedMetadata
If includeEliminatedCandidates Then
results.Add(New CandidateAnalysisResult(candidate.Candidate, CandidateAnalysisResultState.HasUnsupportedMetadata))
End If
Case CandidateAnalysisResultState.HasUseSiteError
If includeEliminatedCandidates Then
results.Add(New CandidateAnalysisResult(candidate.Candidate, CandidateAnalysisResultState.HasUseSiteError))
End If
Case CandidateAnalysisResultState.BadGenericArity
If includeEliminatedCandidates Then
results.Add(New CandidateAnalysisResult(candidate.Candidate, CandidateAnalysisResultState.BadGenericArity))
End If
Case CandidateAnalysisResultState.ArgumentCountMismatch
Debug.Assert(candidate.AppliesToNormalForm <> candidate.AppliesToParamArrayForm)
If includeEliminatedCandidates Then
Dim candidateAnalysis As New CandidateAnalysisResult(ConstructIfNeedTo(candidate.Candidate, typeArguments), CandidateAnalysisResultState.ArgumentCountMismatch)
If candidate.AppliesToParamArrayForm Then
candidateAnalysis.SetIsExpandedParamArrayForm()
End If
results.Add(candidateAnalysis)
End If
Case CandidateAnalysisResultState.Applicable
Dim candidateAnalysis As CandidateAnalysisResult
If typeArguments.Length > 0 Then
candidateAnalysis = New CandidateAnalysisResult(candidate.Candidate.Construct(typeArguments))
Else
candidateAnalysis = New CandidateAnalysisResult(candidate.Candidate)
End If
#If DEBUG Then
Dim triedToAddSomething As Boolean = False
#End If
If candidate.AppliesToNormalForm Then
#If DEBUG Then
triedToAddSomething = True
#End If
InferTypeArgumentsIfNeedToAndCombineWithExistingCandidates(results, candidateAnalysis, typeArguments,
arguments, argumentNames,
delegateReturnType, delegateReturnTypeReferenceBoundNode,
binder, asyncLambdaSubToFunctionMismatch,
useSiteInfo)
End If
' How about it's expanded form? It always applies if there's a paramarray.
If candidate.AppliesToParamArrayForm Then
#If DEBUG Then
triedToAddSomething = True
#End If
candidateAnalysis.SetIsExpandedParamArrayForm()
candidateAnalysis.ExpandedParamArrayArgumentsUsed = Math.Max(arguments.Length - candidate.Candidate.ParameterCount + 1, 0)
InferTypeArgumentsIfNeedToAndCombineWithExistingCandidates(results, candidateAnalysis, typeArguments,
arguments, argumentNames,
delegateReturnType, delegateReturnTypeReferenceBoundNode,
binder, asyncLambdaSubToFunctionMismatch,
useSiteInfo)
End If
#If DEBUG Then
Debug.Assert(triedToAddSomething)
#End If
Case CandidateAnalysisResultState.Ambiguous
If includeEliminatedCandidates Then
results.Add(New CandidateAnalysisResult(candidate.Candidate, CandidateAnalysisResultState.Ambiguous))
End If
Case Else
Throw ExceptionUtilities.UnexpectedValue(candidate.State)
End Select
End Sub
Private Shared Sub InferTypeArgumentsIfNeedToAndCombineWithExistingCandidates(
results As ArrayBuilder(Of CandidateAnalysisResult),
newCandidate As CandidateAnalysisResult,
typeArguments As ImmutableArray(Of TypeSymbol),
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
delegateReturnType As TypeSymbol,
delegateReturnTypeReferenceBoundNode As BoundNode,
binder As Binder,
<[In](), Out()> ByRef asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
)
If typeArguments.Length = 0 AndAlso newCandidate.Candidate.Arity > 0 Then
'§11.8.2 Applicable Methods
'Section 4.
'If the type argument list is empty, type inferencing is used to try and infer the type argument list.
'If type inferencing fails, the method is not applicable. Otherwise, the type arguments are filled
'in the place of the type parameters in the signature.
If Not InferTypeArguments(newCandidate, arguments, argumentNames, delegateReturnType, delegateReturnTypeReferenceBoundNode,
asyncLambdaSubToFunctionMismatch, binder, useSiteInfo) Then
Debug.Assert(newCandidate.State <> CandidateAnalysisResultState.Applicable)
results.Add(newCandidate)
Return
End If
End If
CombineCandidates(results, newCandidate, arguments.Length, argumentNames, useSiteInfo)
End Sub
''' <summary>
''' Combine new candidate with the list of existing candidates, applying various shadowing and
''' tie-breaking rules. New candidate may or may not be added to the result, some
''' existing candidates may be removed from the result.
''' </summary>
Private Shared Sub CombineCandidates(
results As ArrayBuilder(Of CandidateAnalysisResult),
newCandidate As CandidateAnalysisResult,
argumentCount As Integer,
argumentNames As ImmutableArray(Of String),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
)
Debug.Assert(newCandidate.State = CandidateAnalysisResultState.Applicable)
Dim operatorResolution As Boolean = newCandidate.Candidate.IsOperator
Debug.Assert(newCandidate.Candidate.ParameterCount >= argumentCount OrElse newCandidate.IsExpandedParamArrayForm)
Debug.Assert(argumentNames.IsDefault OrElse argumentNames.Length > 0)
Debug.Assert(Not operatorResolution OrElse argumentNames.IsDefault)
Dim i As Integer = 0
While i < results.Count
Dim existingCandidate As CandidateAnalysisResult = results(i)
' Skip over some eliminated candidates, which we will be unable to match signature against.
If existingCandidate.State = CandidateAnalysisResultState.ArgumentCountMismatch OrElse
existingCandidate.State = CandidateAnalysisResultState.BadGenericArity OrElse
existingCandidate.State = CandidateAnalysisResultState.Ambiguous Then
GoTo ContinueCandidatesLoop
End If
' Candidate can't hide another form of itself
If existingCandidate.Candidate Is newCandidate.Candidate Then
Debug.Assert(Not operatorResolution)
GoTo ContinueCandidatesLoop
End If
Dim existingWins As Boolean = False
Dim newWins As Boolean = False
' An overriding method hides the methods it overrides.
' In particular, this rule takes care of bug VSWhidbey #385900. Where type argument inference fails
' for an overriding method due to named argument name mismatch, but succeeds for the overridden method
' from base (the overridden method uses parameter name matching the named argument name). At the end,
' however, the overriding method is called, even though it doesn't have parameter with matching name.
' Also helps with methods overridden by restricted types (TypedReference, etc.), ShadowBasedOnReceiverType
' doesn't do the job for them because it relies on Conversions.ClassifyDirectCastConversion, which
' disallows boxing conversion for restricted types.
If Not operatorResolution AndAlso ShadowBasedOnOverriding(existingCandidate, newCandidate, existingWins, newWins) Then
GoTo DeterminedTheWinner
End If
If existingCandidate.State = CandidateAnalysisResultState.TypeInferenceFailed OrElse existingCandidate.SomeInferenceFailed OrElse
existingCandidate.State = CandidateAnalysisResultState.HasUseSiteError OrElse
existingCandidate.State = CandidateAnalysisResultState.HasUnsupportedMetadata Then
' Won't be able to match signature.
GoTo ContinueCandidatesLoop
End If
' It looks like the following code is applying some tie-breaking rules from section 7 of
' §11.8.1 Overloaded Method Resolution, but not all of them and even skips ParamArrays tie-breaking
' rule in some scenarios. I couldn't find an explanation of this behavior in the spec and
' simply tried to keep this code close to Dev10.
' Spec says that the tie-breaking rules should be applied only for members equally applicable to the argument list.
' [§11.8.1.1 Applicability] defines equally applicable members as follows:
' A member M is considered equally applicable as N if
' 1) their signatures are the same or
' 2) if each parameter type in M is the same as the corresponding parameter type in N.
' We can always check if signature is the same, but we cannot check the second condition in presence
' of named arguments because for them we don't know yet which parameter in M corresponds to which
' parameter in N.
Debug.Assert(existingCandidate.Candidate.ParameterCount >= argumentCount OrElse existingCandidate.IsExpandedParamArrayForm)
If argumentNames.IsDefault Then
Dim existingParamIndex As Integer = 0
Dim newParamIndex As Integer = 0
'CONSIDER: Can we somehow merge this with the complete signature comparison?
For j As Integer = 0 To argumentCount - 1 Step 1
Dim existingType As TypeSymbol = GetParameterTypeFromVirtualSignature(existingCandidate, existingParamIndex)
Dim newType As TypeSymbol = GetParameterTypeFromVirtualSignature(newCandidate, newParamIndex)
If Not existingType.IsSameTypeIgnoringAll(newType) Then
' Signatures are different, shadowing rules do not apply
GoTo ContinueCandidatesLoop
End If
' Advance to the next parameter in the existing candidate,
' unless we are on the expanded ParamArray parameter.
AdvanceParameterInVirtualSignature(existingCandidate, existingParamIndex)
' Advance to the next parameter in the new candidate,
' unless we are on the expanded ParamArray parameter.
AdvanceParameterInVirtualSignature(newCandidate, newParamIndex)
Next
Else
Debug.Assert(Not operatorResolution)
End If
Dim signatureMatch As Boolean = True
' Compare complete signature, with no regard to arguments
If existingCandidate.Candidate.ParameterCount <> newCandidate.Candidate.ParameterCount Then
Debug.Assert(Not operatorResolution)
signatureMatch = False
ElseIf operatorResolution Then
Debug.Assert(argumentCount = existingCandidate.Candidate.ParameterCount)
Debug.Assert(signatureMatch)
' Not lifted operators are preferred over lifted.
If existingCandidate.Candidate.IsLifted Then
If Not newCandidate.Candidate.IsLifted Then
newWins = True
GoTo DeterminedTheWinner
End If
ElseIf newCandidate.Candidate.IsLifted Then
Debug.Assert(Not existingCandidate.Candidate.IsLifted)
existingWins = True
GoTo DeterminedTheWinner
End If
Else
For j As Integer = 0 To existingCandidate.Candidate.ParameterCount - 1 Step 1
Dim existingType As TypeSymbol = existingCandidate.Candidate.Parameters(j).Type
Dim newType As TypeSymbol = newCandidate.Candidate.Parameters(j).Type
If Not existingType.IsSameTypeIgnoringAll(newType) Then
signatureMatch = False
Exit For
End If
Next
End If
If Not argumentNames.IsDefault AndAlso Not signatureMatch Then
' Signatures are different, shadowing rules do not apply
GoTo ContinueCandidatesLoop
End If
If Not signatureMatch Then
Debug.Assert(argumentNames.IsDefault)
' If we have gotten to this point it means that the 2 procedures have equal specificity,
' but signatures that do not match exactly (after generic substitution). This
' implies that we are dealing with differences in shape due to param arrays
' or optional arguments.
' So we look and see if one procedure maps fewer arguments to the
' param array than the other. The one using more, is then shadowed by the one using less.
'• If M has fewer parameters from an expanded paramarray than N, eliminate N from the set.
If ShadowBasedOnParamArrayUsage(existingCandidate, newCandidate, existingWins, newWins) Then
GoTo DeterminedTheWinner
End If
Else
' The signatures of the two methods match (after generic parameter substitution).
' This means that param array shadowing doesn't come into play.
' !!! Why? Where is this mentioned in the spec?
End If
Debug.Assert(argumentNames.IsDefault OrElse signatureMatch)
' In presence of named arguments, the following shadowing rules
' cannot be applied if any candidate is extension method because
' full signature match doesn't guarantee equal applicability (in presence of named arguments)
' and instance methods hide by signature regardless applicability rules do not apply to extension methods.
If argumentNames.IsDefault OrElse
Not (existingCandidate.Candidate.IsExtensionMethod OrElse newCandidate.Candidate.IsExtensionMethod) Then
'7.1. If M is defined in a more derived type than N, eliminate N from the set.
' This rule also applies to the types that extension methods are defined on.
'7.2. If M and N are extension methods and the target type of M is a class or
' structure and the target type of N is an interface, eliminate N from the set.
If ShadowBasedOnReceiverType(existingCandidate, newCandidate, existingWins, newWins, useSiteInfo) Then
GoTo DeterminedTheWinner
End If
'7.3. If M and N are extension methods and the target type of M has fewer type
' parameters than the target type of N, eliminate N from the set.
' !!! Note that spec talks about "fewer type parameters", but it is not really about count.
' !!! It is about one refers to a type parameter and the other one doesn't.
If ShadowBasedOnExtensionMethodTargetTypeGenericity(existingCandidate, newCandidate, existingWins, newWins) Then
GoTo DeterminedTheWinner
End If
End If
DeterminedTheWinner:
Debug.Assert(Not existingWins OrElse Not newWins) ' Both cannot win!
If newWins Then
' Remove existing
results.RemoveAt(i)
' We should continue the loop because at least with
' extension methods in the picture, there could be other
' winners and losers in the results.
' Since we removed the element, we should bypass index increment.
Continue While
ElseIf existingWins Then
' New candidate lost, shouldn't add it.
Return
End If
ContinueCandidatesLoop:
i += 1
End While
results.Add(newCandidate)
End Sub
Private Shared Function ShadowBasedOnOverriding(
existingCandidate As CandidateAnalysisResult, newCandidate As CandidateAnalysisResult,
ByRef existingWins As Boolean, ByRef newWins As Boolean
) As Boolean
Dim existingSymbol As Symbol = existingCandidate.Candidate.UnderlyingSymbol
Dim newSymbol As Symbol = newCandidate.Candidate.UnderlyingSymbol
Dim existingType As NamedTypeSymbol = existingSymbol.ContainingType
Dim newType As NamedTypeSymbol = newSymbol.ContainingType
' Optimization: We will rely on ShadowBasedOnReceiverType to give us the
' same effect later on for cases when existingCandidate is
' applicable and neither candidate is from restricted type.
Dim existingIsApplicable As Boolean = (existingCandidate.State = CandidateAnalysisResultState.Applicable)
If existingIsApplicable AndAlso Not existingType.IsRestrictedType() AndAlso Not newType.IsRestrictedType() Then
Return False
End If
' Optimization: symbols from the same type can't override each other.
' ShadowBasedOnReceiverType
If existingType.OriginalDefinition IsNot newType.OriginalDefinition Then
If newCandidate.Candidate.IsOverriddenBy(existingSymbol) Then
existingWins = True
Return True
ElseIf existingIsApplicable AndAlso existingCandidate.Candidate.IsOverriddenBy(newSymbol) Then
newWins = True
Return True
End If
End If
Return False
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1 Overloaded Method Resolution.
''' 7.5. If M is not an extension method and N is, eliminate N from the set.
''' 7.6. If M and N are extension methods and M was found before N, eliminate N from the set.
''' </summary>
Private Shared Function ShadowBasedOnExtensionVsInstanceAndPrecedence(
left As CandidateAnalysisResult, right As CandidateAnalysisResult,
ByRef leftWins As Boolean, ByRef rightWins As Boolean
) As Boolean
If left.Candidate.IsExtensionMethod Then
If Not right.Candidate.IsExtensionMethod Then
'7.5.
rightWins = True
Return True
Else
' Both are extensions
If left.Candidate.PrecedenceLevel < right.Candidate.PrecedenceLevel Then
'7.6.
leftWins = True
Return True
ElseIf left.Candidate.PrecedenceLevel > right.Candidate.PrecedenceLevel Then
'7.6.
rightWins = True
Return True
End If
End If
ElseIf right.Candidate.IsExtensionMethod Then
'7.5.
leftWins = True
Return True
End If
Return False
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1 Overloaded Method Resolution.
''' 7.4. If M is less generic than N, eliminate N from the set.
''' </summary>
Private Shared Function ShadowBasedOnGenericity(
left As CandidateAnalysisResult, right As CandidateAnalysisResult,
ByRef leftWins As Boolean, ByRef rightWins As Boolean,
arguments As ImmutableArray(Of BoundExpression),
binder As Binder
) As Boolean
' §11.8.1.2 Genericity
' A member M is determined to be less generic than a member N as follows:
'
' 1. If, for each pair of matching parameters Mj and Nj, Mj is less or equally generic than Nj
' with respect to type parameters on the method, and at least one Mj is less generic with
' respect to type parameters on the method.
' 2. Otherwise, if for each pair of matching parameters Mj and Nj, Mj is less or equally generic
' than Nj with respect to type parameters on the type, and at least one Mj is less generic with
' respect to type parameters on the type, then M is less generic than N.
'
' A parameter M is considered to be equally generic to a parameter N if their types Mt and Nt
' both refer to type parameters or both don't refer to type parameters. M is considered to be less
' generic than N if Mt does not refer to a type parameter and Nt does.
'
' Extension method type parameters that were fixed during currying are considered type parameters on the type,
' not type parameters on the method.
' At the beginning we will track both method and type type parameters.
Dim track As TypeParameterKind = TypeParameterKind.Both
If Not (left.Candidate.IsGeneric OrElse right.Candidate.IsGeneric) Then
track = track And (Not TypeParameterKind.Method)
End If
If Not ((left.Candidate.UnderlyingSymbol.ContainingType.IsOrInGenericType() OrElse
(left.Candidate.IsExtensionMethod AndAlso Not left.Candidate.FixedTypeParameters.IsNull)) OrElse
(right.Candidate.UnderlyingSymbol.ContainingType.IsOrInGenericType() OrElse
(right.Candidate.IsExtensionMethod AndAlso Not right.Candidate.FixedTypeParameters.IsNull))) Then
track = track And (Not TypeParameterKind.Type)
End If
If track = TypeParameterKind.None Then
Return False ' There is no winner.
End If
#If DEBUG Then
Dim saveTrack = track
#End If
Dim leftHasLeastGenericParameterAgainstMethod As Boolean = False
Dim leftHasLeastGenericParameterAgainstType As Boolean = False
Dim rightHasLeastGenericParameterAgainstMethod As Boolean = False
Dim rightHasLeastGenericParameterAgainstType As Boolean = False
Dim leftParamIndex As Integer = 0
Dim rightParamIndex As Integer = 0
For i = 0 To arguments.Length - 1 Step 1
Dim leftParamType As TypeSymbol
Dim leftParamTypeForGenericityCheck As TypeSymbol = Nothing
Debug.Assert(left.ArgsToParamsOpt.IsDefault = right.ArgsToParamsOpt.IsDefault)
If left.ArgsToParamsOpt.IsDefault Then
leftParamType = GetParameterTypeFromVirtualSignature(left, leftParamIndex, leftParamTypeForGenericityCheck)
AdvanceParameterInVirtualSignature(left, leftParamIndex)
Else
leftParamType = GetParameterTypeFromVirtualSignature(left, left.ArgsToParamsOpt(i), leftParamTypeForGenericityCheck)
End If
Dim rightParamType As TypeSymbol
Dim rightParamTypeForGenericityCheck As TypeSymbol = Nothing
If right.ArgsToParamsOpt.IsDefault Then
rightParamType = GetParameterTypeFromVirtualSignature(right, rightParamIndex, rightParamTypeForGenericityCheck)
AdvanceParameterInVirtualSignature(right, rightParamIndex)
Else
rightParamType = GetParameterTypeFromVirtualSignature(right, right.ArgsToParamsOpt(i), rightParamTypeForGenericityCheck)
End If
' Parameters matching omitted arguments do not participate.
If arguments(i).Kind = BoundKind.OmittedArgument Then
Continue For
End If
If SignatureMismatchForThePurposeOfShadowingBasedOnGenericity(leftParamType, rightParamType, arguments(i), binder) Then
Return False
End If
Dim leftRefersTo As TypeParameterKind = DetectReferencesToGenericParameters(leftParamTypeForGenericityCheck, track, left.Candidate.FixedTypeParameters)
Dim rightRefersTo As TypeParameterKind = DetectReferencesToGenericParameters(rightParamTypeForGenericityCheck, track, right.Candidate.FixedTypeParameters)
' Still looking for less generic with respect to type parameters on the method.
If (track And TypeParameterKind.Method) <> 0 Then
If (leftRefersTo And TypeParameterKind.Method) = 0 Then
If (rightRefersTo And TypeParameterKind.Method) <> 0 Then
leftHasLeastGenericParameterAgainstMethod = True
End If
ElseIf (rightRefersTo And TypeParameterKind.Method) = 0 Then
rightHasLeastGenericParameterAgainstMethod = True
End If
' If both won at least once, neither candidate is less generic with respect to type parameters on the method.
' Stop checking for this.
If leftHasLeastGenericParameterAgainstMethod AndAlso rightHasLeastGenericParameterAgainstMethod Then
track = track And (Not TypeParameterKind.Method)
End If
End If
' Still looking for less generic with respect to type parameters on the type.
If (track And TypeParameterKind.Type) <> 0 Then
If (leftRefersTo And TypeParameterKind.Type) = 0 Then
If (rightRefersTo And TypeParameterKind.Type) <> 0 Then
leftHasLeastGenericParameterAgainstType = True
End If
ElseIf (rightRefersTo And TypeParameterKind.Type) = 0 Then
rightHasLeastGenericParameterAgainstType = True
End If
' If both won at least once, neither candidate is less generic with respect to type parameters on the type.
' Stop checking for this.
If leftHasLeastGenericParameterAgainstType AndAlso rightHasLeastGenericParameterAgainstType Then
track = track And (Not TypeParameterKind.Type)
End If
End If
' Are we still looking for a winner?
If track = TypeParameterKind.None Then
#If DEBUG Then
Debug.Assert((saveTrack And TypeParameterKind.Method) = 0 OrElse (leftHasLeastGenericParameterAgainstMethod AndAlso rightHasLeastGenericParameterAgainstMethod))
Debug.Assert((saveTrack And TypeParameterKind.Type) = 0 OrElse (leftHasLeastGenericParameterAgainstType AndAlso rightHasLeastGenericParameterAgainstType))
#End If
Return False ' There is no winner.
End If
Next
If leftHasLeastGenericParameterAgainstMethod Then
If Not rightHasLeastGenericParameterAgainstMethod Then
leftWins = True
Return True
End If
ElseIf rightHasLeastGenericParameterAgainstMethod Then
rightWins = True
Return True
End If
If leftHasLeastGenericParameterAgainstType Then
If Not rightHasLeastGenericParameterAgainstType Then
leftWins = True
Return True
End If
ElseIf rightHasLeastGenericParameterAgainstType Then
rightWins = True
Return True
End If
Return False
End Function
Private Shared Function SignatureMismatchForThePurposeOfShadowingBasedOnGenericity(
leftParamType As TypeSymbol,
rightParamType As TypeSymbol,
argument As BoundExpression,
binder As Binder
) As Boolean
Debug.Assert(argument.Kind <> BoundKind.OmittedArgument)
' See Semantics::CompareGenericityIsSignatureMismatch in native compiler.
If leftParamType.IsSameTypeIgnoringAll(rightParamType) Then
Return False
Else
' Note: Undocumented rule.
' Different types. We still consider them the same if they are delegates with
' equivalent signatures, after possibly unwrapping Expression(Of D).
Dim leftIsExpressionTree As Boolean, rightIsExpressionTree As Boolean
Dim leftDelegateType As NamedTypeSymbol = leftParamType.DelegateOrExpressionDelegate(binder, leftIsExpressionTree)
Dim rightDelegateType As NamedTypeSymbol = rightParamType.DelegateOrExpressionDelegate(binder, rightIsExpressionTree)
' Native compiler will only compare D1 and D2 for Expression(Of D1) and D2 if the argument is a lambda. It will compare
' Expression(Of D1) and Expression (Of D2) regardless of the argument.
If leftDelegateType IsNot Nothing AndAlso rightDelegateType IsNot Nothing AndAlso
((leftIsExpressionTree = rightIsExpressionTree) OrElse argument.IsAnyLambda()) Then
Dim leftInvoke = leftDelegateType.DelegateInvokeMethod
Dim rightInvoke = rightDelegateType.DelegateInvokeMethod
If leftInvoke IsNot Nothing AndAlso rightInvoke IsNot Nothing AndAlso
MethodSignatureComparer.ParametersAndReturnTypeSignatureComparer.Equals(leftInvoke, rightInvoke) Then
Return False
End If
End If
End If
Return True
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1.3 Depth of genericity
''' </summary>
Private Shared Function ShadowBasedOnDepthOfGenericity(
left As CandidateAnalysisResult, right As CandidateAnalysisResult,
ByRef leftWins As Boolean, ByRef rightWins As Boolean,
arguments As ImmutableArray(Of BoundExpression),
binder As Binder
) As Boolean
' §11.8.1.3 Depth of Genericity
' A member M is determined to have greater depth of genericity than a member N if, for each pair
' of matching parameters Mj and Nj, Mj has greater or equal depth of genericity than Nj, and at
' least one Mj has greater depth of genericity. Depth of genericity is defined as follows:
'
' 1. Anything other than a type parameter has greater depth of genericity than a type parameter;
' 2. Recursively, a constructed type has greater depth of genericity than another constructed type
' (with the same number of type arguments) if at least one type argument has greater depth
' of genericity and no type argument has less depth than the corresponding type argument in the other.
' 3. An array type has greater depth of genericity than another array type (with the same number
' of dimensions) if the element type of the first has greater depth of genericity than the
' element type of the second.
'
' For example:
'
' Module Test
' Sub f(Of T)(x As Task(Of T))
' End Sub
' Sub f(Of T)(x As T)
' End Sub
' Sub Main()
' Dim x As Task(Of Integer) = Nothing
' f(x) ' Calls the first overload
' End Sub
' End Module
Dim leftParamIndex As Integer = 0
Dim rightParamIndex As Integer = 0
For i = 0 To arguments.Length - 1 Step 1
Dim leftParamType As TypeSymbol
Dim leftParamTypeForGenericityCheck As TypeSymbol = Nothing
Debug.Assert(left.ArgsToParamsOpt.IsDefault = right.ArgsToParamsOpt.IsDefault)
If left.ArgsToParamsOpt.IsDefault Then
leftParamType = GetParameterTypeFromVirtualSignature(left, leftParamIndex, leftParamTypeForGenericityCheck)
AdvanceParameterInVirtualSignature(left, leftParamIndex)
Else
leftParamType = GetParameterTypeFromVirtualSignature(left, left.ArgsToParamsOpt(i), leftParamTypeForGenericityCheck)
End If
Dim rightParamType As TypeSymbol
Dim rightParamTypeForGenericityCheck As TypeSymbol = Nothing
If right.ArgsToParamsOpt.IsDefault Then
rightParamType = GetParameterTypeFromVirtualSignature(right, rightParamIndex, rightParamTypeForGenericityCheck)
AdvanceParameterInVirtualSignature(right, rightParamIndex)
Else
rightParamType = GetParameterTypeFromVirtualSignature(right, right.ArgsToParamsOpt(i), rightParamTypeForGenericityCheck)
End If
' Parameters matching omitted arguments do not participate.
If arguments(i).Kind = BoundKind.OmittedArgument Then
Continue For
End If
If SignatureMismatchForThePurposeOfShadowingBasedOnGenericity(leftParamType, rightParamType, arguments(i), binder) Then
Return False ' no winner if the types of the parameter are different
End If
Dim leftParamWins As Boolean = False
Dim rightParamWins As Boolean = False
If CompareParameterTypeGenericDepth(leftParamTypeForGenericityCheck, rightParamTypeForGenericityCheck, leftParamWins, rightParamWins) Then
Debug.Assert(leftParamWins <> rightParamWins)
If leftParamWins Then
If rightWins Then
rightWins = False
Return False ' both won
Else
leftWins = True
End If
Else
If leftWins Then
leftWins = False
Return False ' both won
Else
rightWins = True
End If
End If
End If
Next
Debug.Assert(Not leftWins OrElse Not rightWins)
Return leftWins OrElse rightWins
End Function
''' <summary>
'''
''' </summary>
''' <returns>False if node of candidates wins</returns>
Private Shared Function CompareParameterTypeGenericDepth(leftType As TypeSymbol, rightType As TypeSymbol,
ByRef leftWins As Boolean, ByRef rightWins As Boolean) As Boolean
' Depth of genericity is defined as follows:
' 1. Anything other than a type parameter has greater depth of genericity than a type parameter;
' 2. Recursively, a constructed type has greater depth of genericity than another constructed
' type (with the same number of type arguments) if at least one type argument has greater
' depth of genericity and no type argument has less depth than the corresponding type
' argument in the other.
' 3. An array type has greater depth of genericity than another array type (with the same number
' of dimensions) if the element type of the first has greater depth of genericity than the
' element type of the second.
'
' For exact rules see Dev11 OverloadResolution.cpp: void Semantics::CompareParameterTypeGenericDepth(...)
If leftType Is rightType Then
Return False
End If
If leftType.IsTypeParameter Then
If rightType.IsTypeParameter Then
' Both type parameters => no winner
Return False
Else
' Left is a type parameter, but right is not
rightWins = True
Return True
End If
ElseIf rightType.IsTypeParameter Then
' Right is a type parameter, but left is not
leftWins = True
Return True
End If
' None of the two is a type parameter
If leftType.IsArrayType AndAlso rightType.IsArrayType Then
' Both are arrays
Dim leftArray = DirectCast(leftType, ArrayTypeSymbol)
Dim rightArray = DirectCast(rightType, ArrayTypeSymbol)
If leftArray.HasSameShapeAs(rightArray) Then
Return CompareParameterTypeGenericDepth(leftArray.ElementType, rightArray.ElementType, leftWins, rightWins)
End If
End If
' Both are generics
If leftType.Kind = SymbolKind.NamedType AndAlso rightType.Kind = SymbolKind.NamedType Then
Dim leftNamedType = DirectCast(leftType.GetTupleUnderlyingTypeOrSelf(), NamedTypeSymbol)
Dim rightNamedType = DirectCast(rightType.GetTupleUnderlyingTypeOrSelf(), NamedTypeSymbol)
' If their arities are equal
If leftNamedType.Arity = rightNamedType.Arity Then
Dim leftTypeArguments As ImmutableArray(Of TypeSymbol) = leftNamedType.TypeArgumentsNoUseSiteDiagnostics
Dim rightTypeArguments As ImmutableArray(Of TypeSymbol) = rightNamedType.TypeArgumentsNoUseSiteDiagnostics
For i = 0 To leftTypeArguments.Length - 1
Dim leftArgWins As Boolean = False
Dim rightArgWins As Boolean = False
If CompareParameterTypeGenericDepth(leftTypeArguments(i), rightTypeArguments(i), leftArgWins, rightArgWins) Then
Debug.Assert(leftArgWins <> rightArgWins)
If leftArgWins Then
If rightWins Then
rightWins = False
Return False
Else
leftWins = True
End If
Else
If leftWins Then
leftWins = False
Return False
Else
rightWins = True
End If
End If
End If
Next
Debug.Assert(Not leftWins OrElse Not rightWins)
Return leftWins OrElse rightWins
End If
End If
Return False
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1 Overloaded Method Resolution.
''' 7.3. If M and N are extension methods and the target type of M has fewer type
''' parameters than the target type of N, eliminate N from the set.
''' !!! Note that spec talks about "fewer type parameters", but it is not really about count.
''' !!! It is about one refers to a type parameter and the other one doesn't.
''' </summary>
Private Shared Function ShadowBasedOnExtensionMethodTargetTypeGenericity(
left As CandidateAnalysisResult, right As CandidateAnalysisResult,
ByRef leftWins As Boolean, ByRef rightWins As Boolean
) As Boolean
If Not left.Candidate.IsExtensionMethod OrElse Not right.Candidate.IsExtensionMethod Then
Return False
End If
'!!! Note, the spec does not mention this explicitly, but this rule applies only if receiver type
'!!! is the same for both methods.
If Not left.Candidate.ReceiverType.IsSameTypeIgnoringAll(right.Candidate.ReceiverType) Then
Return False
End If
' Only interested in method type parameters.
Dim leftRefersToATypeParameter = DetectReferencesToGenericParameters(left.Candidate.ReceiverTypeDefinition,
TypeParameterKind.Method,
BitVector.Null)
' Only interested in method type parameters.
Dim rightRefersToATypeParameter = DetectReferencesToGenericParameters(right.Candidate.ReceiverTypeDefinition,
TypeParameterKind.Method,
BitVector.Null)
If (leftRefersToATypeParameter And TypeParameterKind.Method) <> 0 Then
If (rightRefersToATypeParameter And TypeParameterKind.Method) = 0 Then
rightWins = True
Return True
End If
ElseIf (rightRefersToATypeParameter And TypeParameterKind.Method) <> 0 Then
leftWins = True
Return True
End If
Return False
End Function
<Flags()>
Private Enum TypeParameterKind
None = 0
Method = 1 << 0
Type = 1 << 1
Both = Method Or Type
End Enum
Private Shared Function DetectReferencesToGenericParameters(
symbol As NamedTypeSymbol,
track As TypeParameterKind,
methodTypeParametersToTreatAsTypeTypeParameters As BitVector
) As TypeParameterKind
Dim result As TypeParameterKind = TypeParameterKind.None
Do
If symbol Is symbol.OriginalDefinition Then
If (track And TypeParameterKind.Type) = 0 Then
Return result
End If
If symbol.Arity > 0 Then
Return result Or TypeParameterKind.Type
End If
Else
For Each argument As TypeSymbol In symbol.TypeArgumentsNoUseSiteDiagnostics
result = result Or DetectReferencesToGenericParameters(argument, track,
methodTypeParametersToTreatAsTypeTypeParameters)
If (result And track) = track Then
Return result
End If
Next
End If
symbol = symbol.ContainingType
Loop While symbol IsNot Nothing
Return result
End Function
Private Shared Function DetectReferencesToGenericParameters(
symbol As TypeParameterSymbol,
track As TypeParameterKind,
methodTypeParametersToTreatAsTypeTypeParameters As BitVector
) As TypeParameterKind
If symbol.ContainingSymbol.Kind = SymbolKind.NamedType Then
If (track And TypeParameterKind.Type) <> 0 Then
Return TypeParameterKind.Type
End If
Else
If methodTypeParametersToTreatAsTypeTypeParameters.IsNull OrElse Not methodTypeParametersToTreatAsTypeTypeParameters(symbol.Ordinal) Then
If (track And TypeParameterKind.Method) <> 0 Then
Return TypeParameterKind.Method
End If
Else
If (track And TypeParameterKind.Type) <> 0 Then
Return TypeParameterKind.Type
End If
End If
End If
Return TypeParameterKind.None
End Function
Private Shared Function DetectReferencesToGenericParameters(
this As TypeSymbol,
track As TypeParameterKind,
methodTypeParametersToTreatAsTypeTypeParameters As BitVector
) As TypeParameterKind
Select Case this.Kind
Case SymbolKind.TypeParameter
Return DetectReferencesToGenericParameters(DirectCast(this, TypeParameterSymbol), track,
methodTypeParametersToTreatAsTypeTypeParameters)
Case SymbolKind.ArrayType
Return DetectReferencesToGenericParameters(DirectCast(this, ArrayTypeSymbol).ElementType, track,
methodTypeParametersToTreatAsTypeTypeParameters)
Case SymbolKind.NamedType, SymbolKind.ErrorType
Return DetectReferencesToGenericParameters(DirectCast(this, NamedTypeSymbol), track,
methodTypeParametersToTreatAsTypeTypeParameters)
Case Else
Throw ExceptionUtilities.UnexpectedValue(this.Kind)
End Select
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1 Overloaded Method Resolution.
''' 7.1. If M is defined in a more derived type than N, eliminate N from the set.
''' This rule also applies to the types that extension methods are defined on.
''' 7.2. If M and N are extension methods and the target type of M is a class or
''' structure and the target type of N is an interface, eliminate N from the set.
''' </summary>
Private Shared Function ShadowBasedOnReceiverType(
left As CandidateAnalysisResult, right As CandidateAnalysisResult,
ByRef leftWins As Boolean, ByRef rightWins As Boolean,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Boolean
Dim leftType = left.Candidate.ReceiverType
Dim rightType = right.Candidate.ReceiverType
If Not leftType.IsSameTypeIgnoringAll(rightType) Then
If DoesReceiverMatchInstance(leftType, rightType, useSiteInfo) Then
leftWins = True
Return True
ElseIf DoesReceiverMatchInstance(rightType, leftType, useSiteInfo) Then
rightWins = True
Return True
End If
End If
Return False
End Function
''' <summary>
''' For a receiver to match an instance, more or less, the type of that instance has to be convertible
''' to the type of the receiver with the same bit-representation (i.e. identity on value-types
''' and reference-convertibility on reference types).
''' Actually, we don't include the reference-convertibilities that seem nonsensical, e.g. enum() to underlyingtype()
''' We do include inheritance, implements and variance conversions amongst others.
''' </summary>
Public Shared Function DoesReceiverMatchInstance(instanceType As TypeSymbol, receiverType As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Return Conversions.HasWideningDirectCastConversionButNotEnumTypeConversion(instanceType, receiverType, useSiteInfo)
End Function
''' <summary>
''' Implements shadowing based on
''' §11.8.1 Overloaded Method Resolution.
''' • If M has fewer parameters from an expanded paramarray than N, eliminate N from the set.
''' </summary>
Private Shared Function ShadowBasedOnParamArrayUsage(
left As CandidateAnalysisResult, right As CandidateAnalysisResult,
ByRef leftWins As Boolean, ByRef rightWins As Boolean
) As Boolean
If left.IsExpandedParamArrayForm Then
If right.IsExpandedParamArrayForm Then
If left.ExpandedParamArrayArgumentsUsed > right.ExpandedParamArrayArgumentsUsed Then
rightWins = True
Return True
ElseIf left.ExpandedParamArrayArgumentsUsed < right.ExpandedParamArrayArgumentsUsed Then
leftWins = True
Return True
End If
Else
rightWins = True
Return True
End If
ElseIf right.IsExpandedParamArrayForm Then
leftWins = True
Return True
End If
Return False
End Function
Friend Shared Function GetParameterTypeFromVirtualSignature(
ByRef candidate As CandidateAnalysisResult,
paramIndex As Integer
) As TypeSymbol
Dim paramType As TypeSymbol = candidate.Candidate.Parameters(paramIndex).Type
If candidate.IsExpandedParamArrayForm AndAlso
paramIndex = candidate.Candidate.ParameterCount - 1 AndAlso
paramType.Kind = SymbolKind.ArrayType Then
paramType = DirectCast(paramType, ArrayTypeSymbol).ElementType
End If
Return paramType
End Function
Private Shared Function GetParameterTypeFromVirtualSignature(
ByRef candidate As CandidateAnalysisResult,
paramIndex As Integer,
ByRef typeForGenericityCheck As TypeSymbol
) As TypeSymbol
Dim param As ParameterSymbol = candidate.Candidate.Parameters(paramIndex)
Dim paramForGenericityCheck = param.OriginalDefinition
If paramForGenericityCheck.ContainingSymbol.Kind = SymbolKind.Method Then
Dim method = DirectCast(paramForGenericityCheck.ContainingSymbol, MethodSymbol)
If method.IsReducedExtensionMethod Then
paramForGenericityCheck = method.ReducedFrom.Parameters(paramIndex + 1)
End If
End If
Dim paramType As TypeSymbol = param.Type
typeForGenericityCheck = paramForGenericityCheck.Type
If candidate.IsExpandedParamArrayForm AndAlso
paramIndex = candidate.Candidate.ParameterCount - 1 AndAlso
paramType.Kind = SymbolKind.ArrayType Then
paramType = DirectCast(paramType, ArrayTypeSymbol).ElementType
typeForGenericityCheck = DirectCast(typeForGenericityCheck, ArrayTypeSymbol).ElementType
End If
Return paramType
End Function
Friend Shared Sub AdvanceParameterInVirtualSignature(
ByRef candidate As CandidateAnalysisResult,
ByRef paramIndex As Integer
)
If Not (candidate.IsExpandedParamArrayForm AndAlso
paramIndex = candidate.Candidate.ParameterCount - 1) Then
paramIndex += 1
End If
End Sub
Private Shared Function InferTypeArguments(
ByRef candidate As CandidateAnalysisResult,
arguments As ImmutableArray(Of BoundExpression),
argumentNames As ImmutableArray(Of String),
delegateReturnType As TypeSymbol,
delegateReturnTypeReferenceBoundNode As BoundNode,
<[In](), Out()> ByRef asyncLambdaSubToFunctionMismatch As HashSet(Of BoundExpression),
binder As Binder,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Boolean
Dim parameterToArgumentMap As ArrayBuilder(Of Integer) = Nothing
Dim paramArrayItems As ArrayBuilder(Of Integer) = Nothing
BuildParameterToArgumentMap(candidate, arguments, argumentNames, parameterToArgumentMap, paramArrayItems)
If candidate.State = CandidateAnalysisResultState.Applicable Then
Dim typeArguments As ImmutableArray(Of TypeSymbol) = Nothing
Dim inferenceLevel As TypeArgumentInference.InferenceLevel = TypeArgumentInference.InferenceLevel.None
Dim allFailedInferenceIsDueToObject As Boolean = False
Dim someInferenceFailed As Boolean = False
Dim inferenceErrorReasons As InferenceErrorReasons = InferenceErrorReasons.Other
Dim inferredTypeByAssumption As BitVector = Nothing
Dim typeArgumentsLocation As ImmutableArray(Of SyntaxNodeOrToken) = Nothing
Dim inferenceDiagnosticsBag = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, useSiteInfo.AccumulatesDependencies)
If TypeArgumentInference.Infer(DirectCast(candidate.Candidate.UnderlyingSymbol, MethodSymbol),
arguments, parameterToArgumentMap, paramArrayItems,
delegateReturnType:=delegateReturnType,
delegateReturnTypeReferenceBoundNode:=delegateReturnTypeReferenceBoundNode,
typeArguments:=typeArguments,
inferenceLevel:=inferenceLevel,
someInferenceFailed:=someInferenceFailed,
allFailedInferenceIsDueToObject:=allFailedInferenceIsDueToObject,
inferenceErrorReasons:=inferenceErrorReasons,
inferredTypeByAssumption:=inferredTypeByAssumption,
typeArgumentsLocation:=typeArgumentsLocation,
asyncLambdaSubToFunctionMismatch:=asyncLambdaSubToFunctionMismatch,
useSiteInfo:=useSiteInfo,
diagnostic:=inferenceDiagnosticsBag) Then
candidate.SetInferenceLevel(inferenceLevel)
candidate.Candidate = candidate.Candidate.Construct(typeArguments)
' Need check for Option Strict and warn if parameter type is an assumed inferred type.
If binder.OptionStrict = OptionStrict.On AndAlso Not inferredTypeByAssumption.IsNull Then
For i As Integer = 0 To typeArguments.Length - 1 Step 1
If inferredTypeByAssumption(i) Then
Binder.ReportDiagnostic(inferenceDiagnosticsBag,
typeArgumentsLocation(i),
ERRID.WRN_TypeInferenceAssumed3,
candidate.Candidate.TypeParameters(i),
DirectCast(candidate.Candidate.UnderlyingSymbol, MethodSymbol).OriginalDefinition,
typeArguments(i))
End If
Next
End If
Else
candidate.State = CandidateAnalysisResultState.TypeInferenceFailed
If someInferenceFailed Then
candidate.SetSomeInferenceFailed()
End If
If allFailedInferenceIsDueToObject Then
candidate.SetAllFailedInferenceIsDueToObject()
If Not candidate.Candidate.IsExtensionMethod Then
candidate.IgnoreExtensionMethods = True
End If
End If
candidate.SetInferenceErrorReasons(inferenceErrorReasons)
candidate.NotInferredTypeArguments = BitVector.Create(typeArguments.Length)
For i As Integer = 0 To typeArguments.Length - 1 Step 1
If typeArguments(i) Is Nothing Then
candidate.NotInferredTypeArguments(i) = True
End If
Next
End If
candidate.TypeArgumentInferenceDiagnosticsOpt = inferenceDiagnosticsBag.ToReadOnlyAndFree()
Else
candidate.SetSomeInferenceFailed()
End If
If paramArrayItems IsNot Nothing Then
paramArrayItems.Free()
End If
If parameterToArgumentMap IsNot Nothing Then
parameterToArgumentMap.Free()
End If
Return (candidate.State = CandidateAnalysisResultState.Applicable)
End Function
Private Shared Function ConstructIfNeedTo(candidate As Candidate, typeArguments As ImmutableArray(Of TypeSymbol)) As Candidate
If typeArguments.Length > 0 Then
Return candidate.Construct(typeArguments)
End If
Return candidate
End Function
End Class
End Namespace
|