|
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Generic
Imports System.Collections.Immutable
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
Friend Enum SymbolComparisonResults
NameMismatch = 1 << 0
ReturnTypeMismatch = 1 << 1
ArityMismatch = 1 << 2
ConstraintMismatch = 1 << 3
CallingConventionMismatch = 1 << 4
CustomModifierMismatch = 1 << 5
''' <summary>
''' One of the methods has more parameters than the other
''' AND
''' at least one of the extra parameters is NOT optional
''' </summary>
RequiredExtraParameterMismatch = 1 << 6
''' <summary>
''' One of the methods has more parameters than the other
''' AND at least one of the extra parameters IS optional
''' OR
''' there is at least one parameter in one method with optionality (being optional or
''' required) not equal to that of the matching parameter from the other method
''' </summary>
OptionalParameterMismatch = 1 << 7
RequiredParameterTypeMismatch = 1 << 8
OptionalParameterTypeMismatch = 1 << 9
OptionalParameterValueMismatch = 1 << 10
ParameterByrefMismatch = 1 << 11
ParamArrayMismatch = 1 << 12
PropertyAccessorMismatch = 1 << 13
''' <summary>
''' Taken into consideration only if <see cref="PropertyAccessorMismatch"/> is set.
''' </summary>
PropertyInitOnlyMismatch = 1 << 14
VarargMismatch = 1 << 15
''' <summary>
''' Mismatch in total number of parameters, both required and optional
''' </summary>
''' <remarks></remarks>
TotalParameterCountMismatch = 1 << 16
TupleNamesMismatch = 1 << 17
AllMismatches = (1 << 18) - 1
AllParameterMismatches =
OptionalParameterMismatch Or
RequiredExtraParameterMismatch Or
TotalParameterCountMismatch Or
OptionalParameterTypeMismatch Or
RequiredParameterTypeMismatch Or
CustomModifierMismatch Or
ParameterByrefMismatch Or
ParamArrayMismatch Or
OptionalParameterValueMismatch Or
TupleNamesMismatch
' The set of mismatches for DetailedCompare that are ignored
' when testing for conflicting method in a class, or first
' pass of finding an overridden method. See 4.1.1 of language spec.
MismatchesForConflictingMethods =
ReturnTypeMismatch Or
ParameterByrefMismatch Or
ParamArrayMismatch Or
ConstraintMismatch Or
CustomModifierMismatch Or
OptionalParameterMismatch Or
OptionalParameterValueMismatch Or
PropertyAccessorMismatch Or
CallingConventionMismatch Or
TupleNamesMismatch
' The set of mismatches for DetailedCompare that are ignored
' when finding the method/property that was implemented.
' Note that constraints are validated later, so are ignored
' when finding the method.
MismatchesForExplicitInterfaceImplementations =
NameMismatch Or
ConstraintMismatch Or
CustomModifierMismatch Or
PropertyAccessorMismatch Or
TupleNamesMismatch
End Enum
''' <summary>
''' Implementation of IEqualityComparer for MethodSymbols, with options for various aspects
''' to compare.
''' </summary>
Friend NotInheritable Class MethodSignatureComparer
Implements IEqualityComparer(Of MethodSymbol)
''' <summary>
''' This instance is intended to reflect the definition of signature equality used by the runtime (ECMA 335 Section 8.6.1.6).
''' It considers return type, name, parameters, calling convention, and custom modifiers.
''' </summary>
Public Shared ReadOnly RuntimeMethodSignatureComparer As MethodSignatureComparer =
New MethodSignatureComparer(considerName:=True,
considerReturnType:=True,
considerTypeConstraints:=False,
considerByRef:=True,
considerCallingConvention:=True,
considerCustomModifiers:=True,
considerTupleNames:=False)
''' <summary>
''' This instance is used to compare all aspects.
''' </summary>
Public Shared ReadOnly AllAspectsSignatureComparer As MethodSignatureComparer =
New MethodSignatureComparer(considerName:=True,
considerReturnType:=True,
considerTypeConstraints:=True,
considerByRef:=True,
considerCallingConvention:=True,
considerCustomModifiers:=True,
considerTupleNames:=True)
''' <summary>
''' This instance is used to compare parameter and return types, including byref.
''' </summary>
Public Shared ReadOnly ParametersAndReturnTypeSignatureComparer As MethodSignatureComparer =
New MethodSignatureComparer(considerName:=False,
considerReturnType:=True,
considerTypeConstraints:=False,
considerByRef:=True,
considerCallingConvention:=False,
considerCustomModifiers:=False,
considerTupleNames:=False)
''' <summary>
''' This instance is used to compare custom modifiers, parameter and return types, including byref.
''' </summary>
Public Shared ReadOnly CustomModifiersAndParametersAndReturnTypeSignatureComparer As MethodSignatureComparer =
New MethodSignatureComparer(considerName:=False,
considerReturnType:=True,
considerTypeConstraints:=False,
considerByRef:=True,
considerCallingConvention:=False,
considerCustomModifiers:=True,
considerTupleNames:=False)
''' <summary>
''' This instance is used to search for methods that have the same signature, return type,
''' and constraints according to the VisualBasic definition. Custom modifiers are ignored.
''' </summary>
Public Shared ReadOnly VisualBasicSignatureAndConstraintsAndReturnTypeComparer As MethodSignatureComparer =
New MethodSignatureComparer(considerName:=True,
considerReturnType:=True,
considerTypeConstraints:=True,
considerByRef:=True,
considerCallingConvention:=True,
considerCustomModifiers:=False,
considerTupleNames:=False)
''' <summary>
''' This instance is used to search for methods that have identical signatures in every regard.
''' </summary>
Public Shared ReadOnly RetargetedExplicitMethodImplementationComparer As MethodSignatureComparer =
New MethodSignatureComparer(considerName:=True,
considerReturnType:=True,
considerTypeConstraints:=False,
considerByRef:=True,
considerCallingConvention:=True,
considerCustomModifiers:=True,
considerTupleNames:=False)
''' <summary>
''' This instance is used to compare potential WinRT fake methods in type projection.
'''
''' FIXME(angocke): This is almost certainly wrong. The semantics of WinRT conflict
''' comparison should probably match overload resolution (i.e., we should not add a member
''' to lookup that would result in ambiguity), but this is closer to what Dev12 does.
'''
''' The real fix here is to establish a spec for how WinRT conflict comparison should be
''' performed. Once this is done we should remove these comments.
''' </summary>
Public Shared ReadOnly WinRTConflictComparer As MethodSignatureComparer =
New MethodSignatureComparer(considerName:=True,
considerReturnType:=False,
considerTypeConstraints:=False,
considerByRef:=False,
considerCallingConvention:=False,
considerCustomModifiers:=False,
considerTupleNames:=False)
' Compare the "unqualified" part of the method name (no explicit part)
Private ReadOnly _considerName As Boolean
' Compare the type symbols of the return types
Private ReadOnly _considerReturnType As Boolean
' Compare the type constraints
Private ReadOnly _considerTypeConstraints As Boolean
' Compare the full calling conventions. Still compares varargs if false.
Private ReadOnly _considerCallingConvention As Boolean
' Consider byref during matching
Private ReadOnly _considerByRef As Boolean
' Consider custom modifiers on/in parameters and return types (if return is considered).
Private ReadOnly _considerCustomModifiers As Boolean
' Consider tuple names in parameters and return types (if return is considered).
Private ReadOnly _considerTupleNames As Boolean
Private Sub New(considerName As Boolean,
considerReturnType As Boolean,
considerTypeConstraints As Boolean,
considerCallingConvention As Boolean,
considerByRef As Boolean,
considerCustomModifiers As Boolean,
considerTupleNames As Boolean)
Me._considerName = considerName
Me._considerReturnType = considerReturnType
Me._considerTypeConstraints = considerTypeConstraints
Me._considerCallingConvention = considerCallingConvention
Me._considerByRef = considerByRef
Me._considerCustomModifiers = considerCustomModifiers
Me._considerTupleNames = considerTupleNames
End Sub
#Region "IEqualityComparer(Of MethodSymbol) Members"
Public Overloads Function Equals(method1 As MethodSymbol, method2 As MethodSymbol) As Boolean _
Implements IEqualityComparer(Of MethodSymbol).Equals
If method1 Is method2 Then
Return True
End If
If method1 Is Nothing OrElse method2 Is Nothing Then
Return False
End If
If method1.Arity <> method2.Arity Then
Return False
End If
If _considerName Then
If Not IdentifierComparison.Equals(method1.Name, method2.Name) Then
Return False
End If
End If
Dim typeSubstitution1 = GetTypeSubstitution(method1)
Dim typeSubstitution2 = GetTypeSubstitution(method2)
If _considerReturnType Then
If Not HaveSameReturnTypes(method1, typeSubstitution1, method2, typeSubstitution2, _considerCustomModifiers, _considerTupleNames) Then
Return False
End If
End If
If method1.ParameterCount > 0 OrElse method2.ParameterCount > 0 Then
If Not HaveSameParameterTypes(method1.Parameters, typeSubstitution1, method2.Parameters, typeSubstitution2,
_considerByRef, _considerCustomModifiers, _considerTupleNames) Then
Return False
End If
End If
If _considerCallingConvention Then
If method1.CallingConvention <> method2.CallingConvention Then
Return False
End If
Else
If method1.IsVararg <> method2.IsVararg Then
Return False
End If
End If
If _considerTypeConstraints Then
If Not HaveSameConstraints(method1, typeSubstitution1, method2, typeSubstitution2) Then
Return False
End If
End If
Return True
End Function
Public Overloads Function GetHashCode(method As MethodSymbol) As Integer _
Implements IEqualityComparer(Of MethodSymbol).GetHashCode
Dim _hash As Integer = 1
If method IsNot Nothing Then
If _considerName Then
_hash = Hash.Combine(method.Name, _hash)
End If
If _considerReturnType AndAlso Not method.IsGenericMethod AndAlso Not _considerCustomModifiers Then
_hash = Hash.Combine(method.ReturnType, _hash)
End If
' CONSIDER: modify hash for constraints?
_hash = Hash.Combine(_hash, method.Arity)
_hash = Hash.Combine(_hash, method.ParameterCount)
_hash = Hash.Combine(method.IsVararg, _hash)
End If
Return _hash
End Function
#End Region
#Region "Detailed comparison functions"
Public Shared Function DetailedCompare(
method1 As MethodSymbol,
method2 As MethodSymbol,
comparisons As SymbolComparisonResults,
Optional stopIfAny As SymbolComparisonResults = 0
) As SymbolComparisonResults
Dim results As SymbolComparisonResults = Nothing
If method1 = method2 Then
Return Nothing
End If
If (comparisons And SymbolComparisonResults.ArityMismatch) <> 0 Then
If method1.Arity <> method2.Arity Then
results = results Or SymbolComparisonResults.ArityMismatch
If (stopIfAny And SymbolComparisonResults.ArityMismatch) <> 0 Then
GoTo Done
End If
End If
End If
If (stopIfAny And SymbolComparisonResults.TotalParameterCountMismatch) <> 0 Then
If method1.ParameterCount <> method2.ParameterCount Then
results = results Or SymbolComparisonResults.TotalParameterCountMismatch
GoTo Done
End If
End If
Dim typeSubstitution1 As New LazyTypeSubstitution(method1)
Dim typeSubstitution2 As New LazyTypeSubstitution(method2)
If (comparisons And (SymbolComparisonResults.ReturnTypeMismatch Or SymbolComparisonResults.CustomModifierMismatch Or SymbolComparisonResults.TupleNamesMismatch)) <> 0 Then
Dim origDef1 = method1.OriginalDefinition
Dim origDef2 = method2.OriginalDefinition
results = results Or DetailedReturnTypeCompare(origDef1.ReturnsByRef,
New TypeWithModifiers(origDef1.ReturnType, origDef1.ReturnTypeCustomModifiers),
origDef1.RefCustomModifiers,
typeSubstitution1.Value,
origDef2.ReturnsByRef,
New TypeWithModifiers(origDef2.ReturnType, origDef2.ReturnTypeCustomModifiers),
origDef2.RefCustomModifiers,
typeSubstitution2.Value,
comparisons,
stopIfAny)
If (stopIfAny And results) <> 0 Then
GoTo Done
End If
End If
If (comparisons And SymbolComparisonResults.AllParameterMismatches) <> 0 Then
results = results Or DetailedParameterCompare(method1.Parameters, typeSubstitution1,
method2.Parameters, typeSubstitution2,
comparisons, stopIfAny)
If (stopIfAny And results) <> 0 Then
GoTo Done
End If
End If
If (comparisons And SymbolComparisonResults.CallingConventionMismatch) <> 0 Then
If method1.CallingConvention <> method2.CallingConvention Then
results = results Or SymbolComparisonResults.CallingConventionMismatch
If (stopIfAny And SymbolComparisonResults.CallingConventionMismatch) <> 0 Then
GoTo Done
End If
End If
End If
If (comparisons And SymbolComparisonResults.VarargMismatch) <> 0 Then
If method1.IsVararg <> method2.IsVararg Then
results = results Or SymbolComparisonResults.VarargMismatch
If (stopIfAny And SymbolComparisonResults.VarargMismatch) <> 0 Then
GoTo Done
End If
End If
End If
If (comparisons And SymbolComparisonResults.ConstraintMismatch) <> 0 Then
' If the arity is different, we cannot compare constraints. We can't just return
' ConstraintMismatch if the arity is different though since, if there are no
' constraints, then arguably the constraints do match. Therefore, the caller
' must check for ArityMismatch when checking for ConstraintMismatch.
Debug.Assert((comparisons And SymbolComparisonResults.ArityMismatch) <> 0)
If ((results And SymbolComparisonResults.ArityMismatch) = 0) AndAlso
Not HaveSameConstraints(method1, typeSubstitution1.Value, method2, typeSubstitution2.Value) Then
results = results Or SymbolComparisonResults.ConstraintMismatch
If (stopIfAny And SymbolComparisonResults.ConstraintMismatch) <> 0 Then
GoTo Done
End If
End If
End If
' It turns out name comparison is rather expensive relative to the other checks.
If (comparisons And SymbolComparisonResults.NameMismatch) <> 0 Then
If Not IdentifierComparison.Equals(method1.Name, method2.Name) Then
results = results Or SymbolComparisonResults.NameMismatch
If (stopIfAny And SymbolComparisonResults.NameMismatch) <> 0 Then
GoTo Done
End If
End If
End If
Done:
Return results And comparisons
End Function
Public Structure LazyTypeSubstitution
Private _typeSubstitution As TypeSubstitution
Private _method As MethodSymbol
Public Sub New(method As MethodSymbol)
_method = method
End Sub
Public ReadOnly Property Value As TypeSubstitution
Get
If _typeSubstitution Is Nothing AndAlso _method IsNot Nothing Then
_typeSubstitution = GetTypeSubstitution(_method)
_method = Nothing
End If
Return _typeSubstitution
End Get
End Property
End Structure
' Compare two return types and return the detailed comparison of them.
Public Shared Function DetailedReturnTypeCompare(
returnsByRef1 As Boolean,
type1 As TypeWithModifiers,
refCustomModifiers1 As ImmutableArray(Of CustomModifier),
typeSubstitution1 As TypeSubstitution,
returnsByRef2 As Boolean,
type2 As TypeWithModifiers,
refCustomModifiers2 As ImmutableArray(Of CustomModifier),
typeSubstitution2 As TypeSubstitution,
comparisons As SymbolComparisonResults,
Optional stopIfAny As SymbolComparisonResults = 0
) As SymbolComparisonResults
If returnsByRef1 <> returnsByRef2 Then
Return SymbolComparisonResults.ReturnTypeMismatch
End If
type1 = SubstituteType(typeSubstitution1, type1)
type2 = SubstituteType(typeSubstitution2, type2)
If Not type1.Type.IsSameType(type2.Type, TypeCompareKind.AllIgnoreOptionsForVB) Then
Return SymbolComparisonResults.ReturnTypeMismatch
End If
Dim result As SymbolComparisonResults = 0
If (comparisons And SymbolComparisonResults.TupleNamesMismatch) <> 0 AndAlso
Not type1.Type.IsSameType(type2.Type, TypeCompareKind.AllIgnoreOptionsForVB And Not TypeCompareKind.IgnoreTupleNames) Then
result = result Or SymbolComparisonResults.TupleNamesMismatch
If (stopIfAny And SymbolComparisonResults.TupleNamesMismatch) <> 0 Then
GoTo Done
End If
End If
If (comparisons And SymbolComparisonResults.CustomModifierMismatch) <> 0 AndAlso
(Not type1.IsSameType(type2, TypeCompareKind.AllIgnoreOptionsForVB And Not TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) OrElse
Not SubstituteModifiers(typeSubstitution1, refCustomModifiers1).SequenceEqual(SubstituteModifiers(typeSubstitution2, refCustomModifiers2))) Then
result = result Or SymbolComparisonResults.CustomModifierMismatch
If (stopIfAny And SymbolComparisonResults.CustomModifierMismatch) <> 0 Then
GoTo Done
End If
End If
Done:
Return result
End Function
Private Shared Function SubstituteModifiers(typeSubstitution As TypeSubstitution, customModifiers As ImmutableArray(Of CustomModifier)) As ImmutableArray(Of CustomModifier)
If typeSubstitution IsNot Nothing Then
Return typeSubstitution.SubstituteCustomModifiers(customModifiers)
Else
Return customModifiers
End If
End Function
Public Shared Function DetailedParameterCompare(
params1 As ImmutableArray(Of ParameterSymbol),
<[In]> ByRef lazyTypeSubstitution1 As LazyTypeSubstitution,
params2 As ImmutableArray(Of ParameterSymbol),
<[In]> ByRef lazyTypeSubstitution2 As LazyTypeSubstitution,
comparisons As SymbolComparisonResults,
Optional stopIfAny As SymbolComparisonResults = 0
) As SymbolComparisonResults
Dim results As SymbolComparisonResults = Nothing
Dim commonParamCount As Integer
Dim longerParameters As ImmutableArray(Of ParameterSymbol)
If params1.Length > params2.Length Then
commonParamCount = params2.Length
longerParameters = params1
ElseIf params1.Length < params2.Length Then
commonParamCount = params1.Length
longerParameters = params2
Else
commonParamCount = params1.Length
longerParameters = Nothing
End If
If Not longerParameters.IsDefault Then
results = results Or SymbolComparisonResults.TotalParameterCountMismatch
If (stopIfAny And SymbolComparisonResults.TotalParameterCountMismatch) <> 0 Then
GoTo Done
End If
For i As Integer = commonParamCount To longerParameters.Length - 1
If longerParameters(i).IsOptional Then
results = results Or SymbolComparisonResults.OptionalParameterMismatch
If (stopIfAny And SymbolComparisonResults.OptionalParameterMismatch) <> 0 Then
GoTo Done
End If
Else
results = results Or SymbolComparisonResults.RequiredExtraParameterMismatch
If (stopIfAny And SymbolComparisonResults.RequiredExtraParameterMismatch) <> 0 Then
GoTo Done
End If
End If
Next
End If
If commonParamCount <> 0 Then
Dim typeSubstitution1 As TypeSubstitution
Dim typeSubstitution2 As TypeSubstitution
Dim checkTypes As Boolean
If (comparisons And
(SymbolComparisonResults.OptionalParameterTypeMismatch Or
SymbolComparisonResults.RequiredParameterTypeMismatch Or
SymbolComparisonResults.CustomModifierMismatch Or
SymbolComparisonResults.TupleNamesMismatch)) <> 0 Then
checkTypes = True
typeSubstitution1 = lazyTypeSubstitution1.Value
typeSubstitution2 = lazyTypeSubstitution2.Value
Else
checkTypes = False
typeSubstitution1 = Nothing
typeSubstitution2 = Nothing
End If
For i As Integer = 0 To commonParamCount - 1
Dim param1 = params1(i)
Dim param2 = params2(i)
Dim bothOptional As Boolean = param1.IsOptional AndAlso param2.IsOptional
If param1.IsOptional <> param2.IsOptional Then
results = results Or SymbolComparisonResults.OptionalParameterMismatch
If (stopIfAny And SymbolComparisonResults.OptionalParameterMismatch) <> 0 Then
GoTo Done
End If
End If
If checkTypes Then
Dim type1 As TypeWithModifiers = GetTypeWithModifiers(typeSubstitution1, param1)
Dim type2 As TypeWithModifiers = GetTypeWithModifiers(typeSubstitution2, param2)
If Not type1.Type.IsSameType(type2.Type, TypeCompareKind.AllIgnoreOptionsForVB) Then
If bothOptional Then
results = results Or SymbolComparisonResults.OptionalParameterTypeMismatch
If (stopIfAny And SymbolComparisonResults.OptionalParameterTypeMismatch) <> 0 Then
GoTo Done
End If
Else
results = results Or SymbolComparisonResults.RequiredParameterTypeMismatch
If (stopIfAny And SymbolComparisonResults.RequiredParameterTypeMismatch) <> 0 Then
GoTo Done
End If
End If
Else
If (comparisons And SymbolComparisonResults.TupleNamesMismatch) <> 0 AndAlso
Not type1.Type.IsSameType(type2.Type, TypeCompareKind.AllIgnoreOptionsForVB And
Not TypeCompareKind.IgnoreTupleNames) Then
results = results Or SymbolComparisonResults.TupleNamesMismatch
If (stopIfAny And SymbolComparisonResults.TupleNamesMismatch) <> 0 Then
GoTo Done
End If
End If
If (comparisons And SymbolComparisonResults.CustomModifierMismatch) <> 0 AndAlso
(Not type1.IsSameType(type2, TypeCompareKind.AllIgnoreOptionsForVB And
Not TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) OrElse
Not GetRefModifiers(typeSubstitution1, param1).SequenceEqual(GetRefModifiers(typeSubstitution2, param2))) Then
results = results Or SymbolComparisonResults.CustomModifierMismatch
If (stopIfAny And SymbolComparisonResults.CustomModifierMismatch) <> 0 Then
GoTo Done
End If
End If
End If
End If
If param1.IsByRef <> param2.IsByRef Then
results = results Or SymbolComparisonResults.ParameterByrefMismatch
If (stopIfAny And SymbolComparisonResults.ParameterByrefMismatch) <> 0 Then
GoTo Done
End If
End If
If (comparisons And SymbolComparisonResults.ParamArrayMismatch) <> 0 Then
If param1.IsParamArray <> param2.IsParamArray Then
results = results Or SymbolComparisonResults.ParamArrayMismatch
If (stopIfAny And SymbolComparisonResults.ParamArrayMismatch) <> 0 Then
GoTo Done
End If
End If
End If
If bothOptional AndAlso
(comparisons And SymbolComparisonResults.OptionalParameterValueMismatch) <> 0 Then
Dim bothHaveExplicitDefaultValue = param1.HasExplicitDefaultValue AndAlso param2.HasExplicitDefaultValue
Dim optionalParameterMismatch As Boolean
' For source symbols, parameter default values must match. i.e. if one has a default value then both must have a default value
' and the two values must be equal. When one of the parameters is from metadata, we relax the check and only report
' a mismatch if both symbols have default values. This is to handle the case that a metadata symbol has the opt flag set but no
' default value and a source parameter symbol in method that implements or overrides a metadata method specifies a default value.
If bothHaveExplicitDefaultValue Then
optionalParameterMismatch = ParameterDefaultValueMismatch(param1, param2)
Else
' Strictly speaking, what we would like to check is that both parameters are from the "current" compilation.
' However, the only way to know the current compilation at this point is to pass it into every method
' signature comparison (tedious, since we can't change the signature while implementing IEqualityComparer,
' so we'd have to give up having constant instances). Fortunately, we can make a good approximation: we can
' require that both parameters be from the same (non-nothing) compilation. With this rule, an inexact result
' can never change the interaction between two assemblies (i.e. there will never be an observable difference
' between referencing a source assembly and referencing the corresponding metadata assembly).
Dim comp1 = param1.DeclaringCompilation
Dim comp2 = param2.DeclaringCompilation
optionalParameterMismatch = comp1 IsNot Nothing AndAlso comp1 Is comp2
End If
If optionalParameterMismatch Then
results = results Or SymbolComparisonResults.OptionalParameterValueMismatch
If (stopIfAny And SymbolComparisonResults.OptionalParameterValueMismatch) <> 0 Then
GoTo Done
End If
End If
End If
Next
End If
Done:
Return results
End Function
Private Shared Function GetTypeWithModifiers(typeSubstitution As TypeSubstitution, param As ParameterSymbol) As TypeWithModifiers
If typeSubstitution IsNot Nothing Then
Return SubstituteType(typeSubstitution, New TypeWithModifiers(param.OriginalDefinition.Type, param.OriginalDefinition.CustomModifiers))
Else
Return New TypeWithModifiers(param.Type, param.CustomModifiers)
End If
End Function
Private Shared Function GetRefModifiers(typeSubstitution As TypeSubstitution, param As ParameterSymbol) As ImmutableArray(Of CustomModifier)
If typeSubstitution IsNot Nothing Then
Return typeSubstitution.SubstituteCustomModifiers(param.OriginalDefinition.RefCustomModifiers)
Else
Return param.RefCustomModifiers
End If
End Function
Private Shared Function ParameterDefaultValueMismatch(param1 As ParameterSymbol, param2 As ParameterSymbol) As Boolean
Dim constValue1 As ConstantValue = param1.ExplicitDefaultConstantValue
Dim constValue2 As ConstantValue = param2.ExplicitDefaultConstantValue
' bad constants do not match
If constValue1.IsBad OrElse constValue2.IsBad Then
Return True
End If
' Since Nothing literal essentially means the type's Default value it is equal
' to zero value of types which allow zero values, for example for decimal 0;
' so, for signature comparison purpose we have to treat them same as zeroes.
' replace Nothing constants with corresponding Zeros if possible
If constValue1.IsNothing Then
Dim descriminator = ConstantValue.GetDiscriminator(param1.Type.GetEnumUnderlyingTypeOrSelf.SpecialType)
If descriminator <> ConstantValueTypeDiscriminator.Bad Then
constValue1 = ConstantValue.Default(descriminator)
End If
End If
If constValue2.IsNothing Then
Dim descriminator = ConstantValue.GetDiscriminator(param2.Type.GetEnumUnderlyingTypeOrSelf.SpecialType)
If descriminator <> ConstantValueTypeDiscriminator.Bad Then
constValue2 = ConstantValue.Default(descriminator)
End If
End If
Return Not constValue1.Equals(constValue2)
End Function
#End Region
Public Shared Function HaveSameParameterTypes(params1 As ImmutableArray(Of ParameterSymbol), typeSubstitution1 As TypeSubstitution,
params2 As ImmutableArray(Of ParameterSymbol), typeSubstitution2 As TypeSubstitution,
considerByRef As Boolean,
considerCustomModifiers As Boolean,
considerTupleNames As Boolean) As Boolean
Dim numParams = params1.Length
If numParams <> params2.Length Then
Return False
End If
For i As Integer = 0 To numParams - 1
Dim param1 = params1(i)
Dim param2 = params2(i)
Dim type1 As TypeWithModifiers = GetTypeWithModifiers(typeSubstitution1, param1)
Dim type2 As TypeWithModifiers = GetTypeWithModifiers(typeSubstitution2, param2)
Dim comparison As TypeCompareKind = MakeTypeCompareKind(considerCustomModifiers, considerTupleNames)
If Not type1.IsSameType(type2, comparison) Then
Return False
End If
If considerCustomModifiers Then
If Not GetRefModifiers(typeSubstitution1, param1).SequenceEqual(GetRefModifiers(typeSubstitution2, param2)) Then
Return False
End If
End If
If considerByRef AndAlso param1.IsByRef <> param2.IsByRef Then
Return False
End If
Next
Return True
End Function
Friend Shared Function MakeTypeCompareKind(considerCustomModifiers As Boolean, considerTupleNames As Boolean) As TypeCompareKind
Dim comparison As TypeCompareKind = TypeCompareKind.ConsiderEverything
If Not considerCustomModifiers Then
comparison = comparison Or TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds
End If
If Not considerTupleNames Then
comparison = comparison Or TypeCompareKind.IgnoreTupleNames
End If
Return comparison
End Function
Private Shared Function HaveSameReturnTypes(method1 As MethodSymbol, typeSubstitution1 As TypeSubstitution,
method2 As MethodSymbol, typeSubstitution2 As TypeSubstitution,
considerCustomModifiers As Boolean, considerTupleNames As Boolean) As Boolean
'short-circuit type map building in the easiest cases
Dim isSub1 = method1.IsSub
Dim isSub2 = method2.IsSub
If isSub1 <> isSub2 Then
Return False
ElseIf isSub1 Then
Return True
End If
If method1.ReturnsByRef <> method2.ReturnsByRef Then
Return False
End If
Dim origDef1 = method1.OriginalDefinition
Dim origDef2 = method2.OriginalDefinition
Dim returnType1 = SubstituteType(typeSubstitution1, New TypeWithModifiers(origDef1.ReturnType, origDef1.ReturnTypeCustomModifiers))
Dim returnType2 = SubstituteType(typeSubstitution2, New TypeWithModifiers(origDef2.ReturnType, origDef2.ReturnTypeCustomModifiers))
' the runtime compares custom modifiers using (effectively) SequenceEqual
Dim comparison As TypeCompareKind = MakeTypeCompareKind(considerCustomModifiers, considerTupleNames)
Return returnType1.IsSameType(returnType2, comparison) AndAlso
(Not considerCustomModifiers OrElse SubstituteModifiers(typeSubstitution1, origDef1.RefCustomModifiers).SequenceEqual(SubstituteModifiers(typeSubstitution2, origDef2.RefCustomModifiers)))
End Function
' If this method is generic, get a TypeSubstitution that substitutes IndexedTypeParameterSymbols
' for each method type parameter. This allows correctly comparing parameter and return types
' between two signatures:
' Function goo(Of T)(p As T) As IEnumerable(Of T)
' Function goo(Of U)(p As U) As IEnumerable(Of U)
'
' The substitution returned is to be applied to the ORIGINAL definition of the method.
Private Shared Function GetTypeSubstitution(method As MethodSymbol) As TypeSubstitution
Dim containingType As NamedTypeSymbol = method.ContainingType
If method.Arity = 0 Then
If containingType Is Nothing OrElse method.IsDefinition Then
' [containingType Is Nothing] is for lambda case.
Return Nothing
Else
Return containingType.TypeSubstitution
End If
Else
Dim indexedTypeArguments = StaticCast(Of TypeSymbol).From(IndexedTypeParameterSymbol.Take(method.Arity))
' Checking method.IsDefinition instead of [containingSubstitution Is Nothing]
' because this condition works better for SignatureOnlyMethodSymbol, which
' always reports itself as a definition, even when attached to a constructed/specialized
' type.
If method.IsDefinition Then
Debug.Assert(containingType.TypeSubstitution Is Nothing OrElse TypeOf method Is SignatureOnlyMethodSymbol)
Return TypeSubstitution.Create(method, method.TypeParameters, indexedTypeArguments)
Else
Return TypeSubstitution.Create(containingType.TypeSubstitution, method.OriginalDefinition, indexedTypeArguments)
End If
End If
End Function
' Apply the substitution created in GetTypeSubstitution() to a particular Type. If the substitution is
' Nothing, just return the type.
'
' WARNING: This must always be applied to a type obtained from the ORIGINAL DEFINITION of the method symbol
' being compared.
Private Shared Function SubstituteType(typeSubstitution As TypeSubstitution, typeSymbol As TypeWithModifiers) As TypeWithModifiers
Return typeSymbol.InternalSubstituteTypeParameters(typeSubstitution)
End Function
Friend Shared Function HaveSameConstraints(method1 As MethodSymbol, method2 As MethodSymbol) As Boolean
Return HaveSameConstraints(method1, GetTypeSubstitution(method1), method2, GetTypeSubstitution(method2))
End Function
Private Shared Function HaveSameConstraints(method1 As MethodSymbol,
typeSubstitution1 As TypeSubstitution,
method2 As MethodSymbol,
typeSubstitution2 As TypeSubstitution) As Boolean
Dim typeParameters1 = method1.OriginalDefinition.TypeParameters
Dim typeParameters2 = method2.OriginalDefinition.TypeParameters
Dim arity = typeParameters1.Length
' Caller must ensure arity matches since if there are no constraints,
' then one could argue that constraints match, even if arity does not.
Debug.Assert(typeParameters2.Length = arity)
For i = 0 To arity - 1
If Not HaveSameConstraints(typeParameters1(i), typeSubstitution1, typeParameters2(i), typeSubstitution2) Then
Return False
End If
Next
Return True
End Function
Friend Shared Function HaveSameConstraints(typeParameter1 As TypeParameterSymbol,
typeSubstitution1 As TypeSubstitution,
typeParameter2 As TypeParameterSymbol,
typeSubstitution2 As TypeSubstitution) As Boolean
If (typeParameter1.HasConstructorConstraint <> typeParameter2.HasConstructorConstraint) OrElse
(typeParameter1.HasReferenceTypeConstraint <> typeParameter2.HasReferenceTypeConstraint) OrElse
(typeParameter1.HasValueTypeConstraint <> typeParameter2.HasValueTypeConstraint) OrElse
(typeParameter1.AllowsRefLikeType <> typeParameter2.AllowsRefLikeType) OrElse
(typeParameter1.HasUnmanagedTypeConstraint <> typeParameter2.HasUnmanagedTypeConstraint) OrElse
(typeParameter1.Variance <> typeParameter2.Variance) Then
Return False
End If
' Check that constraintTypes1 is a subset of constraintTypes2 and
' also that constraintTypes2 is a subset of constraintTypes1.
Dim constraintTypes1 = typeParameter1.ConstraintTypesNoUseSiteDiagnostics
Dim constraintTypes2 = typeParameter2.ConstraintTypesNoUseSiteDiagnostics
' The two sets of constraints may differ in size but still be considered the same
' due to duplicated constraints, but if both are zero size, the sets must be equal.
If (constraintTypes1.Length = 0) AndAlso (constraintTypes2.Length = 0) Then
Return True
End If
Dim substitutedTypes1 = ArrayBuilder(Of TypeSymbol).GetInstance()
Dim substitutedTypes2 = ArrayBuilder(Of TypeSymbol).GetInstance()
SubstituteConstraintTypes(constraintTypes1, substitutedTypes1, typeSubstitution1)
SubstituteConstraintTypes(constraintTypes2, substitutedTypes2, typeSubstitution2)
' After substitution, the sets are equal if all constraints in each set are present in the
' other. This is because VB requires all inherited constraints to be included explicitly
' in overriding or implementing methods. That is distinct from C# which allows
' redundant System.Object and System.ValueType constraints to be omitted.
' For instance if a base class C(Of T) contains method M(Of U As {Structure, T}),
' then the type parameter U in an override C(Of Object).M(Of U) must have
' {Structure, Object} constraints even though Object is redundant. Similarly, U in an
' override of C(Of System.ValueType).M(Of U) must have {Structure, System.ValueType}
' constraints even though System.ValueType is redundant.
Dim result = AreConstraintTypesSubset(substitutedTypes1, substitutedTypes2) AndAlso
AreConstraintTypesSubset(substitutedTypes2, substitutedTypes1)
substitutedTypes1.Free()
substitutedTypes2.Free()
Return result
End Function
''' <summary>
''' Returns true if the first set of constraint types
''' is a subset of the second set.
''' </summary>
Private Shared Function AreConstraintTypesSubset(constraintTypes1 As ArrayBuilder(Of TypeSymbol), constraintTypes2 As ArrayBuilder(Of TypeSymbol)) As Boolean
For Each constraintType In constraintTypes1
' Skip object type.
If constraintType.IsObjectType() Then
Continue For
End If
If Not ContainsIgnoringCustomModifiers(constraintTypes2, constraintType) Then
Return False
End If
Next
Return True
End Function
Private Shared Function ContainsIgnoringCustomModifiers(types As ArrayBuilder(Of TypeSymbol), type As TypeSymbol) As Boolean
For Each t In types
If t.IsSameTypeIgnoringAll(type) Then
Return True
End If
Next
Return False
End Function
Private Shared Sub SubstituteConstraintTypes(constraintTypes As ImmutableArray(Of TypeSymbol), result As ArrayBuilder(Of TypeSymbol), substitution As TypeSubstitution)
For Each constraintType In constraintTypes
result.Add(SubstituteType(substitution, New TypeWithModifiers(constraintType)).Type)
Next
End Sub
End Class
End Namespace
|