|
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.Operations
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.TypeSymbolExtensions
Namespace Microsoft.CodeAnalysis.VisualBasic
''' <summary>
''' Summarizes whether a conversion is allowed, and if so, which kind of conversion (and in some cases, the
''' associated symbol).
''' </summary>
Public Structure Conversion
Implements IEquatable(Of Conversion), IConvertibleConversion
Private ReadOnly _convKind As ConversionKind
Private ReadOnly _method As MethodSymbol
Friend Sub New(conv As KeyValuePair(Of ConversionKind, MethodSymbol))
_convKind = conv.Key
_method = conv.Value
End Sub
Friend ReadOnly Property Kind As ConversionKind
Get
Return _convKind
End Get
End Property
''' <summary>
''' Returns True if the conversion exists, either as a widening or narrowing conversion.
''' </summary>
''' <remarks>
''' If this returns True, exactly one of <see cref="IsNarrowing"/> or <see cref="IsWidening"/> will return True.
''' If this returns False, neither <see cref="IsNarrowing"/> or <see cref="IsWidening"/> will return True.
''' </remarks>
Public ReadOnly Property Exists As Boolean
Get
Return Not Conversions.NoConversion(_convKind)
End Get
End Property
''' <summary>
''' Returns True if this conversion a narrowing conversion, and not a widening conversion.
''' </summary>
Public ReadOnly Property IsNarrowing As Boolean
Get
Return Conversions.IsNarrowingConversion(_convKind)
End Get
End Property
''' <summary>
''' Returns True if this conversion is a widening conversion, and not a narrowing conversion.
''' </summary>
Public ReadOnly Property IsWidening As Boolean
Get
Return Conversions.IsWideningConversion(_convKind)
End Get
End Property
''' <summary>
''' Returns True if this conversion is an identity conversion.
''' </summary>
''' <remarks>
''' Note that identity conversion are also considered widening conversions.
''' </remarks>
Public ReadOnly Property IsIdentity As Boolean
Get
Return Conversions.IsIdentityConversion(_convKind)
End Get
End Property
''' <summary>
''' Returns True if this conversion is a default conversion (a conversion from the "Nothing" literal).
''' </summary>
''' <remarks>Note that default conversions are considered widening conversions.</remarks>
Public ReadOnly Property IsDefault As Boolean
Get
Return (_convKind And ConversionKind.WideningNothingLiteral) = ConversionKind.WideningNothingLiteral
End Get
End Property
''' <summary>
''' Returns True if this conversion is a widening numeric conversion or a narrowing numeric conversion, as defined in
''' section 8.3.
''' </summary>
Public ReadOnly Property IsNumeric As Boolean
Get
Return (_convKind And ConversionKind.Numeric) <> 0
End Get
End Property
''' <summary>
''' Returns True if this conversion is a narrowing boolean conversion, as defined in section 8.2.
''' </summary>
Public ReadOnly Property IsBoolean As Boolean
Get
Return (_convKind And ConversionKind.Boolean) <> 0
End Get
End Property
''' <summary>
''' Returns True if this conversion is a widening reference conversion or narrowing reference conversion, as defined in
''' section 8.4.
''' </summary>
Public ReadOnly Property IsReference As Boolean
Get
Return (_convKind And ConversionKind.Reference) <> 0
End Get
End Property
''' <summary>
''' Returns True if this conversion is a widening anonymous delegate conversion as defined in section 8.8, or a
''' narrowing anonymous delegate conversion as defined in section 8.9.
''' </summary>
Public ReadOnly Property IsAnonymousDelegate As Boolean
Get
Return (_convKind And ConversionKind.AnonymousDelegate) <> 0
End Get
End Property
''' <summary>
''' Returns True if this is a lambda conversion.
''' </summary>
Public ReadOnly Property IsLambda As Boolean
Get
Return (_convKind And ConversionKind.Lambda) <> 0
End Get
End Property
''' <summary>
''' Returns True if this conversion is a widening array conversion or a narrowing array conversion, as defined in
''' section 8.5.
''' </summary>
Public ReadOnly Property IsArray As Boolean
Get
Return (_convKind And ConversionKind.Array) <> 0
End Get
End Property
''' <summary>
''' Returns True if this conversion a widening value type conversion or a narrowing value type conversion as defined in
''' section 8.6.
''' </summary>
Public ReadOnly Property IsValueType As Boolean
Get
Return (_convKind And ConversionKind.Value) <> 0
End Get
End Property
''' <summary>
''' Returns True if this conversion a widening nullable value type conversion or a narrowing nullable value type
''' conversion as defined in section 8.6.1.
''' </summary>
Public ReadOnly Property IsNullableValueType As Boolean
Get
Return (_convKind And ConversionKind.Nullable) <> 0
End Get
End Property
''' <summary>
''' Returns True if this conversion a widening string conversion or a narrowing string conversion as defined in section
''' 8.7.
''' </summary>
Public ReadOnly Property IsString As Boolean
Get
Return (_convKind And ConversionKind.String) <> 0
End Get
End Property
''' <summary>
''' Returns True if this conversion a widening type parameter or a narrowing type parameter conversion, as defined in
''' section 8.10.
''' </summary>
Public ReadOnly Property IsTypeParameter As Boolean
Get
Return (_convKind And ConversionKind.TypeParameter) <> 0
End Get
End Property
''' <summary>
''' Returns True if this conversion a widening user defined or a narrowing user defined conversion, as defined in
''' section 8.11.
''' </summary>
''' <remarks>
''' If this returns True, the involved conversion method can be obtained with the <see cref="Method"/>
''' property.
''' </remarks>
Public ReadOnly Property IsUserDefined As Boolean
Get
Return (_convKind And ConversionKind.UserDefined) <> 0
End Get
End Property
Friend ReadOnly Property Method As MethodSymbol
Get
Return _method
End Get
End Property
''' <summary>
''' Returns the method that defines the user defined conversion, if any. Otherwise returns Nothing.
''' </summary>
Public ReadOnly Property MethodSymbol As IMethodSymbol
Get
Return _method
End Get
End Property
''' <summary>
''' Returns True if two <see cref="Conversion"/> values are equal.
''' </summary>
''' <param name="left">The left value.</param>
''' <param name="right">The right value.</param>
Public Shared Operator =(left As Conversion, right As Conversion) As Boolean
Return left.Equals(right)
End Operator
''' <summary>
''' Returns True if two <see cref="Conversion"/> values are not equal.
''' </summary>
''' <param name="left">The left value.</param>
''' <param name="right">The right value.</param>
Public Shared Operator <>(left As Conversion, right As Conversion) As Boolean
Return Not (left = right)
End Operator
''' <summary>
''' Creates a <see cref="CommonConversion"/> from this Visual Basic conversion.
''' </summary>
''' <returns>The <see cref="CommonConversion"/> that represents this conversion.</returns>
''' <remarks>
''' This is a lossy conversion; it is not possible to recover the original <see cref="Conversion"/>
''' from the <see cref="CommonConversion"/> struct.
''' </remarks>
Public Function ToCommonConversion() As CommonConversion Implements IConvertibleConversion.ToCommonConversion
Return New CommonConversion(Exists, IsIdentity, IsNumeric, IsReference, IsWidening, IsNullableValueType, MethodSymbol, constrainedToType:=Nothing)
End Function
''' <summary>
''' Determines whether the specified object is equal to the current object.
''' </summary>
''' <param name="obj">
''' The object to compare with the current object.
''' </param>
Public Overloads Overrides Function Equals(obj As Object) As Boolean
Return TypeOf obj Is Conversion AndAlso
Me = DirectCast(obj, Conversion)
End Function
''' <summary>
''' Determines whether the specified object is equal to the current object.
''' </summary>
''' <param name="other">
''' The object to compare with the current object.
''' </param>
Public Overloads Function Equals(other As Conversion) As Boolean Implements IEquatable(Of Conversion).Equals
Return Me._convKind = other._convKind AndAlso Me.Method = other.Method
End Function
''' <summary>
''' Returns a hash code for the current object.
''' </summary>
Public Overrides Function GetHashCode() As Integer
Return Hash.Combine(Method, CInt(_convKind))
End Function
''' <summary>
''' Returns a string that represents the current object.
''' </summary>
Public Overrides Function ToString() As String
' Work around runtime change on Enum.ToString behavior for Flags values
If _convKind = ConversionKind.DelegateRelaxationLevelNone Then
Return "DelegateRelaxationLevelNone"
End If
Return _convKind.ToString()
End Function
End Structure
<Flags()>
Friend Enum ConversionKind
' If there is a conversion, either [Widening] or [Narrowing] bit must be set, but not both.
' All VB conversions are either Widening or Narrowing.
'
' To indicate the fact that no conversion exists:
' 1) Neither [Widening] nor [Narrowing] are set.
' 2) Additional flags may be set in order to provide specific reason.
'
' Bits from the following values are never set at the same time :
' Identity, Numeric, Nullable, Reference, Array, TypeParameter, Value, [String], WideningNothingLiteral, InterpolatedString
FailedDueToNumericOverflow = 1 << 31 ' Failure flag
FailedDueToIntegerOverflow = FailedDueToNumericOverflow Or (1 << 30) ' Failure flag
FailedDueToNumericOverflowMask = FailedDueToNumericOverflow Or FailedDueToIntegerOverflow
FailedDueToQueryLambdaBodyMismatch = 1 << 29 ' Failure flag to indicate that conversion failed because body of a query lambda couldn't be converted to the target delegate return type.
FailedDueToArrayLiteralElementConversion = 1 << 28 ' Failed because array literal element could not be converted to the target element type.
' If there is a conversion, one and only one of the following two bits must be set.
' All VB conversions are either Widening or Narrowing.
[Widening] = 1 << 0
[Narrowing] = 1 << 1
''' <summary>
''' Because flags can be combined, use the method IsIdentityConversion when testing for ConversionKind.Identity
''' </summary>
''' <remarks></remarks>
Identity = [Widening] Or 1 << 2 ' According to VB spec, identity conversion is Widening
Numeric = 1 << 3
WideningNumeric = [Widening] Or Numeric
NarrowingNumeric = [Narrowing] Or Numeric
''' <summary>
''' Can be combined with <see cref="ConversionKind.Tuple"/> to indicate that the underlying value conversion is a predefined tuple conversion
''' </summary>
Nullable = 1 << 4
WideningNullable = [Widening] Or Nullable
NarrowingNullable = [Narrowing] Or Nullable
Reference = 1 << 5
WideningReference = [Widening] Or Reference
NarrowingReference = [Narrowing] Or Reference
Array = 1 << 6
WideningArray = [Widening] Or Array
NarrowingArray = [Narrowing] Or Array
TypeParameter = 1 << 7
WideningTypeParameter = [Widening] Or TypeParameter
NarrowingTypeParameter = [Narrowing] Or TypeParameter
Value = 1 << 8
WideningValue = [Widening] Or Value
NarrowingValue = [Narrowing] Or Value
[String] = 1 << 9
WideningString = [Widening] Or [String]
NarrowingString = [Narrowing] Or [String]
[Boolean] = 1 << 10
' Note: there are no widening boolean conversions.
NarrowingBoolean = [Narrowing] Or [Boolean]
WideningNothingLiteral = [Widening] Or (1 << 11)
' Compiler might be interested in knowing if constant numeric conversion involves narrowing
' for constant's original type. When user-defined conversions are involved, this flag can be
' combined with widening conversions other than WideningNumericConstant.
'
' If this flag is combined with Narrowing, there should be no other reasons to treat
' conversion as narrowing. In some scenarios overload resolution is likely to dismiss
' narrowing in presence of this flag. Also, it appears that with Option Strict On, Dev10
' compiler does not report errors for narrowing conversions from an integral constant
' expression to an integral type (assuming integer overflow checks are disabled) or from a
' floating constant to a floating type.
InvolvesNarrowingFromNumericConstant = 1 << 12
' This flag is set when conversion involves conversion enum <-> underlying type,
' or conversion between two enums, etc
InvolvesEnumTypeConversions = 1 << 13
' Lambda conversion
Lambda = 1 << 14
' Delegate relaxation levels for Lambda and Delegate conversions
DelegateRelaxationLevelNone = 0 ' Identity / Whidbey
DelegateRelaxationLevelWidening = 1 << 15
DelegateRelaxationLevelWideningDropReturnOrArgs = 2 << 15
DelegateRelaxationLevelWideningToNonLambda = 3 << 15
DelegateRelaxationLevelNarrowing = 4 << 15 ' OrcasStrictOff
DelegateRelaxationLevelInvalid = 5 << 15 ' Keep invalid the biggest number
DelegateRelaxationLevelMask = 7 << 15 ' Three bits used!
'Can be combined with Narrowing
VarianceConversionAmbiguity = 1 << 18
' This bit can be combined with NoConversion to indicate that, even though there is no conversion
' from the language point of view, there is a slight chance that conversion might succeed at run-time
' under the right circumstances. It is used to detect possibly ambiguous variance conversions to an
' interface, so it is set only for scenarios that are relevant to variance conversions to an
' interface.
MightSucceedAtRuntime = 1 << 19
AnonymousDelegate = 1 << 20
NeedAStub = 1 << 21
ConvertedToExpressionTree = 1 << 22 ' Combined with Lambda, indicates a conversion of lambda to Expression(Of T).
UserDefined = 1 << 23
' Some variance delegate conversions are treated as special narrowing (Dev10 #820752).
' This flag is combined with Narrowing to indicate the fact.
NarrowingDueToContraVarianceInDelegate = 1 << 24
' Interpolated string conversions
InterpolatedString = [Widening] Or (1 << 25)
' Tuple conversions
''' <summary>
''' Can be combined with <see cref="ConversionKind.Nullable"/> to indicate that the underlying value conversion is a predefined tuple conversion
''' </summary>
Tuple = (1 << 26)
WideningTuple = [Widening] Or Tuple
NarrowingTuple = [Narrowing] Or Tuple
WideningNullableTuple = WideningNullable Or Tuple
NarrowingNullableTuple = NarrowingNullable Or Tuple
' Bits 28 - 31 are reserved for failure flags.
End Enum
<Flags()>
Friend Enum MethodConversionKind
Identity = &H0
OneArgumentIsVbOrBoxWidening = &H1
OneArgumentIsClrWidening = &H2
OneArgumentIsNarrowing = &H4
' TODO: It looks like Dev10 MethodConversionKinds for return are badly named because
' they appear to give classification in the direction opposite to the data
' flow. This is very confusing. However, I am not going to rename them just yet.
' Will do this when all parts are ported and working together, otherwise it will
' be very hard to port the rest of the feature.
ReturnIsWidening = &H8
ReturnIsClrNarrowing = &H10
ReturnIsIsVbOrBoxNarrowing = &H20
ReturnValueIsDropped = &H40
AllArgumentsIgnored = &H80
ExcessOptionalArgumentsOnTarget = &H100
Error_ByRefByValMismatch = &H200
Error_Unspecified = &H400
Error_IllegalToIgnoreAllArguments = &H800
Error_RestrictedType = &H1000
Error_SubToFunction = &H2000
Error_ReturnTypeMismatch = &H4000
Error_OverloadResolution = &H8000
Error_StubNotSupported = &H10000
AllErrorReasons = Error_ByRefByValMismatch Or
Error_Unspecified Or
Error_IllegalToIgnoreAllArguments Or
Error_RestrictedType Or
Error_SubToFunction Or
Error_ReturnTypeMismatch Or
Error_OverloadResolution Or
Error_StubNotSupported
End Enum
''' <summary>
''' The purpose of this class is to answer questions about convertibility of one type to another.
''' It also answers questions about conversions from an expression to a type.
'''
''' The code is organized such that each method attempts to implement exactly one section of the
''' specification.
''' </summary>
''' <remarks></remarks>
Friend NotInheritable Class Conversions
Public Shared ReadOnly Identity As New KeyValuePair(Of ConversionKind, MethodSymbol)(ConversionKind.Identity, Nothing)
Private Sub New()
Throw ExceptionUtilities.Unreachable
End Sub
Friend Class ConversionEasyOut
' There are situations in which we know that there is no unusual conversion going on (such as a conversion involving constants,
' enumerated types, and so on.) In those situations we can classify conversions via a simple table lookup:
' PERF: Use Integer instead of ConversionKind so the compiler can use array literal initialization.
' The most natural type choice, Enum arrays, are not blittable due to a CLR limitation.
Private Shared ReadOnly s_convkind As Integer(,)
Shared Sub New()
Const NOC As Integer = Nothing 'ConversionKind.NoConversion
Const IDN As Integer = ConversionKind.Identity
Const NUM As Integer = ConversionKind.WideningNumeric
Const NNM As Integer = ConversionKind.NarrowingNumeric
Const IRF As Integer = ConversionKind.WideningReference
Const NRF As Integer = ConversionKind.NarrowingReference
Const WIV As Integer = ConversionKind.WideningValue
Const NAV As Integer = ConversionKind.NarrowingValue
Const NST As Integer = ConversionKind.NarrowingString
Const WST As Integer = ConversionKind.WideningString
Const NBO As Integer = ConversionKind.NarrowingBoolean
' TO TYPE
' Obj Str Bool Char SByt Shrt Int Long Byte UShr UInt ULng Sngl Dbl Dec Date
s_convkind = New Integer(,) { ' FROM TYPE
{IDN, NRF, NAV, NAV, NAV, NAV, NAV, NAV, NAV, NAV, NAV, NAV, NAV, NAV, NAV, NAV}, ' Obj
{IRF, IDN, NST, NST, NST, NST, NST, NST, NST, NST, NST, NST, NST, NST, NST, NST}, ' Str
{WIV, NST, IDN, NOC, NBO, NBO, NBO, NBO, NBO, NBO, NBO, NBO, NBO, NBO, NBO, NOC}, ' Bool
{WIV, WST, NOC, IDN, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC}, ' Char
{WIV, NST, NBO, NOC, IDN, NUM, NUM, NUM, NNM, NNM, NNM, NNM, NUM, NUM, NUM, NOC}, ' SByt
{WIV, NST, NBO, NOC, NNM, IDN, NUM, NUM, NNM, NNM, NNM, NNM, NUM, NUM, NUM, NOC}, ' Shrt
{WIV, NST, NBO, NOC, NNM, NNM, IDN, NUM, NNM, NNM, NNM, NNM, NUM, NUM, NUM, NOC}, ' Int
{WIV, NST, NBO, NOC, NNM, NNM, NNM, IDN, NNM, NNM, NNM, NNM, NUM, NUM, NUM, NOC}, ' Long
{WIV, NST, NBO, NOC, NNM, NUM, NUM, NUM, IDN, NUM, NUM, NUM, NUM, NUM, NUM, NOC}, ' Byte
{WIV, NST, NBO, NOC, NNM, NNM, NUM, NUM, NNM, IDN, NUM, NUM, NUM, NUM, NUM, NOC}, ' UShr
{WIV, NST, NBO, NOC, NNM, NNM, NNM, NUM, NNM, NNM, IDN, NUM, NUM, NUM, NUM, NOC}, ' UInt
{WIV, NST, NBO, NOC, NNM, NNM, NNM, NNM, NNM, NNM, NNM, IDN, NUM, NUM, NUM, NOC}, ' ULng
{WIV, NST, NBO, NOC, NNM, NNM, NNM, NNM, NNM, NNM, NNM, NNM, IDN, NUM, NNM, NOC}, ' Sngl
{WIV, NST, NBO, NOC, NNM, NNM, NNM, NNM, NNM, NNM, NNM, NNM, NNM, IDN, NNM, NOC}, ' Dbl
{WIV, NST, NBO, NOC, NNM, NNM, NNM, NNM, NNM, NNM, NNM, NNM, NUM, NUM, IDN, NOC}, ' Dec
{WIV, NST, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, NOC, IDN} ' Date
}
' Obj Str Bool Char SByt Shrt Int Long Byte UShr UInt ULng Sngl Dbl Dec Date
End Sub
Public Shared Function ClassifyPredefinedConversion(source As TypeSymbol, target As TypeSymbol) As ConversionKind?
If source Is Nothing OrElse target Is Nothing Then
Return Nothing
End If
' First, dig through Nullable
Dim sourceNullableUnderlying = source.GetNullableUnderlyingTypeOrSelf()
Dim sourceIsNullable As Boolean = (sourceNullableUnderlying IsNot source)
Dim targetNullableUnderlying = target.GetNullableUnderlyingTypeOrSelf()
Dim targetIsNullable As Boolean = (targetNullableUnderlying IsNot target)
' Now dig through Enum
Dim sourceEnumUnderlying = sourceNullableUnderlying.GetEnumUnderlyingTypeOrSelf()
Dim sourceIsEnum As Boolean = (sourceEnumUnderlying IsNot sourceNullableUnderlying)
Dim targetEnumUnderlying = targetNullableUnderlying.GetEnumUnderlyingTypeOrSelf()
Dim targetIsEnum As Boolean = (targetEnumUnderlying IsNot targetNullableUnderlying)
' Filter out unexpected underlying types for Nullable and enum types
If (sourceIsEnum OrElse sourceIsNullable) AndAlso
(sourceEnumUnderlying.IsStringType() OrElse sourceEnumUnderlying.IsObjectType()) Then
Return Nothing
End If
If (targetIsEnum OrElse targetIsNullable) AndAlso
(targetEnumUnderlying.IsStringType() OrElse targetEnumUnderlying.IsObjectType()) Then
Return Nothing
End If
' Classify conversion between underlying types.
Dim sourceIndex As Integer? = TypeToIndex(sourceEnumUnderlying)
If sourceIndex Is Nothing Then
Return Nothing
End If
Dim targetIndex As Integer? = TypeToIndex(targetEnumUnderlying)
If targetIndex Is Nothing Then
Return Nothing
End If
' Table lookup for underlying type doesn't give correct classification
' for Nullable <=> Object conversion. Need to check them explicitly.
If sourceIsNullable Then
If target.IsObjectType() Then
Return ConversionKind.WideningValue
End If
ElseIf targetIsNullable Then
If source.IsObjectType() Then
Return ConversionKind.NarrowingValue
End If
End If
Dim conv As ConversionKind = CType(s_convkind(sourceIndex.Value, targetIndex.Value), ConversionKind)
If Conversions.NoConversion(conv) Then
Return conv
End If
' Adjust classification for enum conversions first, but don't adjust conversions enum <=> Object.
If ((sourceIsEnum AndAlso Not target.IsObjectType()) OrElse
(targetIsEnum AndAlso Not source.IsObjectType())) Then
'§8.8 Widening Conversions
'• From an enumerated type to its underlying numeric type, or to a numeric type
' that its underlying numeric type has a widening conversion to.
'§8.9 Narrowing Conversions
'• From a numeric type to an enumerated type.
'• From an enumerated type to a numeric type its underlying numeric type has a narrowing conversion to.
'• From an enumerated type to another enumerated type.
'!!! Spec doesn't mention this, but VB also supports conversions between enums and non-numeric intrinsic types.
Dim sourceEnum = sourceNullableUnderlying
Dim targetEnum = targetNullableUnderlying
If sourceIsEnum Then
If targetIsEnum Then
If Not (Conversions.IsIdentityConversion(conv) AndAlso sourceEnum.IsSameTypeIgnoringAll(targetEnum)) Then
'• From an enumerated type to another enumerated type.
conv = ConversionKind.NarrowingNumeric Or ConversionKind.InvolvesEnumTypeConversions
End If
ElseIf Conversions.IsWideningConversion(conv) Then
'• From an enumerated type to its underlying numeric type, or to a type
' that its underlying numeric type has a widening conversion to.
conv = conv Or ConversionKind.InvolvesEnumTypeConversions
If (conv And ConversionKind.Identity) <> 0 Then
conv = (conv And Not ConversionKind.Identity) Or ConversionKind.Widening Or ConversionKind.Numeric
End If
Else
Debug.Assert(Conversions.IsNarrowingConversion(conv))
'• From an enumerated type to a numeric type its underlying numeric type has a narrowing conversion to.
conv = conv Or ConversionKind.InvolvesEnumTypeConversions
End If
Else
Debug.Assert(targetIsEnum)
'• From a type convertible to underlying type to an enumerated type.
conv = (conv And Not ConversionKind.Widening) Or ConversionKind.Narrowing Or ConversionKind.InvolvesEnumTypeConversions
If (conv And ConversionKind.Identity) <> 0 Then
conv = (conv And Not ConversionKind.Identity) Or ConversionKind.Numeric
End If
End If
Debug.Assert(Conversions.IsIdentityConversion(conv) OrElse (conv And ConversionKind.InvolvesEnumTypeConversions) <> 0)
End If
' Now adjust classification for Nullable conversions.
If sourceIsNullable OrElse targetIsNullable Then
Debug.Assert(Not source.IsObjectType() AndAlso Not target.IsObjectType())
'§8.8 Widening Conversions
'• From a type T to the type T?.
'• From a type T? to a type S?, where there is a widening conversion from the type T to the type S.
'• From a type T to a type S?, where there is a widening conversion from the type T to the type S.
'• From a type T? to an interface type that the type T implements.
'§8.9 Narrowing Conversions
'• From a type T? to a type T.
'• From a type T? to a type S?, where there is a narrowing conversion from the type T to the type S.
'• From a type T to a type S?, where there is a narrowing conversion from the type T to the type S.
'• From a type S? to a type T, where there is a conversion from the type S to the type T.
Dim sourceNullable = source
Dim targetNullable = target
If sourceIsNullable Then
If targetIsNullable Then
If Conversions.IsNarrowingConversion(conv) Then
'• From a type T? to a type S?, where there is a narrowing conversion from the type T to the type S.
conv = ConversionKind.NarrowingNullable
ElseIf Not Conversions.IsIdentityConversion(conv) Then
Debug.Assert(Conversions.IsWideningConversion(conv))
'• From a type T? to a type S?, where there is a widening conversion from the type T to the type S.
conv = ConversionKind.WideningNullable
Else
Debug.Assert(Conversions.IsIdentityConversion(conv) AndAlso sourceNullable.IsSameTypeIgnoringAll(targetNullable))
End If
Else
'• From a type T? to a type T.
'• From a type S? to a type T, where there is a conversion from the type S to the type T.
conv = ConversionKind.NarrowingNullable
End If
Else
Debug.Assert(targetIsNullable)
If Conversions.IsWideningConversion(conv) Then
'• From a type T to the type T?.
'• From a type T to a type S?, where there is a widening conversion from the type T to the type S.
conv = ConversionKind.WideningNullable
Else
Debug.Assert(Conversions.IsNarrowingConversion(conv))
'• From a type T to a type S?, where there is a narrowing conversion from the type T to the type S.
conv = ConversionKind.NarrowingNullable
End If
End If
End If
Return conv
End Function
End Class
Private Shared Function FastClassifyPredefinedConversion(source As TypeSymbol, target As TypeSymbol) As ConversionKind?
Return ConversionEasyOut.ClassifyPredefinedConversion(source, target)
End Function
''' <summary>
''' Attempts to fold conversion of a constant expression.
'''
''' Returns Nothing if conversion cannot be folded.
'''
''' If conversion failed due to non-integer overflow, ConstantValue.Bad is returned. Consumer
''' is responsible for reporting appropriate diagnostics.
'''
''' If integer overflow occurs, integerOverflow is set to True and ConstantValue for overflowed result is returned.
''' Consumer is responsible for reporting appropriate diagnostics and potentially discarding the result.
''' </summary>
Public Shared Function TryFoldConstantConversion(
source As BoundExpression,
destination As TypeSymbol,
ByRef integerOverflow As Boolean
) As ConstantValue
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
integerOverflow = False
Dim sourceValue As ConstantValue = source.ConstantValueOpt
If sourceValue Is Nothing OrElse sourceValue.IsBad Then
' Not a constant
Return Nothing
End If
If Not destination.AllowsCompileTimeConversions() Then
Return Nothing
End If
If source.IsNothingLiteral() Then
' A Nothing literal turns into the default value of the target type.
If destination.IsStringType() Then
Return source.ConstantValueOpt
End If
Dim dstDiscriminator As ConstantValueTypeDiscriminator = destination.GetConstantValueTypeDiscriminator()
Debug.Assert((dstDiscriminator <> ConstantValueTypeDiscriminator.Bad) AndAlso (dstDiscriminator <> ConstantValueTypeDiscriminator.Nothing))
Return ConstantValue.Default(dstDiscriminator)
End If
Dim sourceExpressionType = source.Type
If Not sourceExpressionType.AllowsCompileTimeConversions() Then
Return Nothing
End If
Debug.Assert(sourceExpressionType IsNot Nothing)
Debug.Assert(sourceExpressionType.IsValidForConstantValue(sourceValue))
Dim sourceType = sourceExpressionType.GetEnumUnderlyingTypeOrSelf()
Dim targetType = destination.GetEnumUnderlyingTypeOrSelf()
' Shortcut for identity conversions
If sourceType Is targetType Then
Return sourceValue
End If
' Convert the value of the constant to the result type.
If IsStringType(sourceType) Then
If IsCharType(targetType) Then
Dim str As String = If(sourceValue.IsNothing, Nothing, sourceValue.StringValue)
Dim [char] As Char
If str Is Nothing OrElse str.Length = 0 Then
[char] = ChrW(0)
Else
[char] = str(0)
End If
Return ConstantValue.Create([char])
End If
ElseIf IsCharType(sourceType) Then
If IsStringType(targetType) Then
Return ConstantValue.Create(New String(sourceValue.CharValue, 1))
End If
Else
Dim result = TryFoldConstantNumericOrBooleanConversion(
sourceValue,
sourceType,
targetType,
integerOverflow)
Debug.Assert(result Is Nothing OrElse Not result.IsBad OrElse integerOverflow = False)
Return result
End If
Return Nothing
End Function
''' <summary>
''' Attempts to fold conversion of a constant expression.
'''
''' Returns Nothing if conversion cannot be folded, i.e. unexpected source and destination types.
''' Returns Bad value (Discriminator = ConstantValueTypeDiscriminator.Bad) if conversion failed due to non-integer overflow.
'''
''' If integer overflow occurs, integerOverflow is set to True and the overflowed result is returned.
''' </summary>
Private Shared Function TryFoldConstantNumericOrBooleanConversion(
ByRef sourceValue As ConstantValue,
sourceType As TypeSymbol,
targetType As TypeSymbol,
ByRef integerOverflow As Boolean
) As ConstantValue
integerOverflow = False
If IsIntegralType(sourceType) OrElse IsBooleanType(sourceType) Then
If IsNumericType(targetType) OrElse IsBooleanType(targetType) Then
Dim value As Int64 = GetConstantValueAsInt64(sourceValue)
' // Converting True to an arithmetic value produces -1.
If IsBooleanType(sourceType) AndAlso value <> 0 Then
Const BASIC_TRUE As Integer = (-1)
If IsUnsignedIntegralType(targetType) Then
Dim ignoreOverflow As Boolean = False
value = NarrowIntegralResult(BASIC_TRUE, sourceType, targetType, ignoreOverflow)
Else
value = BASIC_TRUE
End If
End If
Return ConvertIntegralValue(value,
sourceValue.Discriminator,
targetType.GetConstantValueTypeDiscriminator(),
integerOverflow)
End If
ElseIf IsFloatingType(sourceType) Then
If IsNumericType(targetType) OrElse IsBooleanType(targetType) Then
Dim result = ConvertFloatingValue(
If(sourceValue.Discriminator = ConstantValueTypeDiscriminator.Double, sourceValue.DoubleValue, sourceValue.SingleValue),
targetType.GetConstantValueTypeDiscriminator(),
integerOverflow)
If result.IsBad Then
integerOverflow = False
End If
Return result
End If
ElseIf IsDecimalType(sourceType) Then
If IsNumericType(targetType) OrElse IsBooleanType(targetType) Then
Dim result = ConvertDecimalValue(
sourceValue.DecimalValue,
targetType.GetConstantValueTypeDiscriminator(),
integerOverflow)
If result.IsBad Then
integerOverflow = False
End If
Return result
End If
End If
Return Nothing
End Function
Public Shared Function TryFoldNothingReferenceConversion(
source As BoundExpression,
conversion As ConversionKind,
targetType As TypeSymbol
) As ConstantValue
Debug.Assert(source IsNot Nothing)
Debug.Assert(targetType IsNot Nothing)
Debug.Assert(targetType.Kind <> SymbolKind.ErrorType)
Dim sourceValue As ConstantValue = source.ConstantValueOpt
Return TryFoldNothingReferenceConversion(sourceValue, conversion, targetType)
End Function
Friend Shared Function TryFoldNothingReferenceConversion(
sourceValue As ConstantValue,
conversion As ConversionKind,
targetType As TypeSymbol
) As ConstantValue
Debug.Assert(targetType IsNot Nothing)
Debug.Assert(targetType.Kind <> SymbolKind.ErrorType)
If sourceValue Is Nothing OrElse Not sourceValue.IsNothing OrElse
targetType.IsTypeParameter() OrElse Not targetType.IsReferenceType() Then
Return Nothing
End If
If conversion = ConversionKind.WideningNothingLiteral OrElse
IsIdentityConversion(conversion) OrElse
(conversion And ConversionKind.WideningReference) = ConversionKind.WideningReference OrElse
(conversion And ConversionKind.WideningArray) = ConversionKind.WideningArray Then
Return sourceValue
End If
Return Nothing
End Function
''' <summary>
''' This function classifies all intrinsic language conversions and user-defined conversions.
''' </summary>
Public Shared Function ClassifyConversion(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As KeyValuePair(Of ConversionKind, MethodSymbol)
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(source.Kind <> SymbolKind.ErrorType)
Debug.Assert(Not TypeOf source Is ArrayLiteralTypeSymbol)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
Dim predefinedConversion As ConversionKind = ClassifyPredefinedConversion(source, destination, useSiteInfo)
If ConversionExists(predefinedConversion) Then
Return New KeyValuePair(Of ConversionKind, MethodSymbol)(predefinedConversion, Nothing)
End If
Return ClassifyUserDefinedConversion(source, destination, useSiteInfo)
End Function
''' <summary>
''' This function classifies all intrinsic language conversions, such as inheritance,
''' implementation, array covariance, and conversions between intrinsic types.
''' </summary>
Public Shared Function ClassifyPredefinedConversion(source As BoundExpression, destination As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
Return ClassifyPredefinedConversion(source, destination, binder, userDefinedConversionsMightStillBeApplicable:=Nothing, useSiteInfo:=useSiteInfo)
End Function
Private Shared Function ClassifyPredefinedConversion(
source As BoundExpression,
destination As TypeSymbol,
binder As Binder,
<Out()> ByRef userDefinedConversionsMightStillBeApplicable As Boolean,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
userDefinedConversionsMightStillBeApplicable = False
Dim conv As ConversionKind
' Using source.IsConstant for field accesses can result in an infinite loop.
' To resolve the cycle, first call GetConstantValue with the already visited constants from the binder.
' The check for source.IsConstant is still necessary because the node might still
' be considered as a non-constant in some error conditions (a reference before declaration,
' for example).
Dim sourceIsConstant As Boolean = False
If source.Kind = BoundKind.FieldAccess Then
sourceIsConstant = DirectCast(source, BoundFieldAccess).FieldSymbol.GetConstantValue(binder.ConstantFieldsInProgress) IsNot Nothing AndAlso source.IsConstant
ElseIf source.Kind = BoundKind.Local Then
sourceIsConstant = DirectCast(source, BoundLocal).LocalSymbol.GetConstantValue(binder) IsNot Nothing AndAlso source.IsConstant
Else
sourceIsConstant = source.IsConstant
End If
If sourceIsConstant Then
'§8.8 Widening Conversions
'• From the literal Nothing to a type.
conv = ClassifyNothingLiteralConversion(source, destination)
If ConversionExists(conv) Then
Return conv
End If
'§8.8 Widening Conversions
'• From the literal 0 to an enumerated type.
'• From a constant expression of type ULong, Long, UInteger, Integer, UShort, Short, Byte, or SByte to
' a narrower type, provided the value of the constant expression is within the range of the destination type.
conv = ClassifyNumericConstantConversion(source, destination, binder)
If ConversionExists(conv) OrElse FailedDueToNumericOverflow(conv) Then
Return conv
End If
End If
If Not (source.IsValue) Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim sourceType As TypeSymbol = If(source.Kind = BoundKind.TupleLiteral,
DirectCast(source, BoundTupleLiteral).InferredType,
source.Type)
If sourceType Is Nothing Then
Dim mostEnclosing = source.GetMostEnclosedParenthesizedExpression().Kind
userDefinedConversionsMightStillBeApplicable = mostEnclosing = BoundKind.ArrayLiteral OrElse
mostEnclosing = BoundKind.TupleLiteral
' The node doesn't have a type yet and reclassification failed.
Return Nothing ' No conversion
End If
If sourceType.Kind <> SymbolKind.ErrorType Then
Dim predefinedConversion As ConversionKind = ClassifyPredefinedConversion(sourceType, destination, useSiteInfo)
If ConversionExists(predefinedConversion) Then
Return predefinedConversion
End If
userDefinedConversionsMightStillBeApplicable = True
End If
Return Nothing 'ConversionKind.NoConversion
End Function
''' <summary>
''' This function classifies all intrinsic language conversions and user-defined conversions.
''' </summary>
Public Shared Function ClassifyConversion(source As BoundExpression, destination As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As KeyValuePair(Of ConversionKind, MethodSymbol)
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
Dim conv As ConversionKind
' Reclassify lambdas, array literals, etc.
conv = ClassifyExpressionReclassification(source, destination, binder, useSiteInfo)
If ConversionExists(conv) OrElse FailedDueToQueryLambdaBodyMismatch(conv) OrElse
(conv And (ConversionKind.Lambda Or ConversionKind.FailedDueToArrayLiteralElementConversion)) <> 0 Then
Return New KeyValuePair(Of ConversionKind, MethodSymbol)(conv, Nothing)
End If
Dim userDefinedConversionsMightStillBeApplicable As Boolean = False
conv = ClassifyPredefinedConversion(source, destination, binder, userDefinedConversionsMightStillBeApplicable, useSiteInfo)
If ConversionExists(conv) OrElse Not userDefinedConversionsMightStillBeApplicable Then
Return New KeyValuePair(Of ConversionKind, MethodSymbol)(conv, Nothing)
End If
' There could be some interesting conversions between the source expression
' and the input type of the conversion operator. We might need to keep track
' of them.
Return ClassifyUserDefinedConversion(source, destination, binder, useSiteInfo)
End Function
''' <summary>
''' Reclassify lambdas, array literals, etc.
''' </summary>
Private Shared Function ClassifyExpressionReclassification(source As BoundExpression, destination As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
Select Case source.Kind
Case BoundKind.Parenthesized
If source.Type Is Nothing Then
' Try to reclassify enclosed expression.
Return ClassifyExpressionReclassification(DirectCast(source, BoundParenthesized).Expression, destination, binder, useSiteInfo)
End If
Case BoundKind.UnboundLambda
Return ClassifyUnboundLambdaConversion(DirectCast(source, UnboundLambda), destination)
Case BoundKind.QueryLambda
Return ClassifyQueryLambdaConversion(DirectCast(source, BoundQueryLambda), destination, binder, useSiteInfo)
Case BoundKind.GroupTypeInferenceLambda
Return ClassifyGroupTypeInferenceLambdaConversion(DirectCast(source, GroupTypeInferenceLambda), destination)
Case BoundKind.AddressOfOperator
Return ClassifyAddressOfConversion(DirectCast(source, BoundAddressOfOperator), destination)
Case BoundKind.ArrayLiteral
Return ClassifyArrayLiteralConversion(DirectCast(source, BoundArrayLiteral), destination, binder, useSiteInfo)
Case BoundKind.InterpolatedStringExpression
Return ClassifyInterpolatedStringConversion(DirectCast(source, BoundInterpolatedStringExpression), destination, binder)
Case BoundKind.TupleLiteral
Return ClassifyTupleConversion(DirectCast(source, BoundTupleLiteral), destination, binder, useSiteInfo)
End Select
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassifyUnboundLambdaConversion(source As UnboundLambda, destination As TypeSymbol) As ConversionKind
Dim leastRelaxationLevel As ConversionKind = ConversionKind.DelegateRelaxationLevelNone
Dim conversionKindExpressionTree As ConversionKind = Nothing ' Set to ConversionKind.ConvertedToExpressionTree if expression tree involved.
Dim delegateInvoke As MethodSymbol = Nothing
' Dev10#626389, Dev10#693976: If you convert a lambda to Object/Delegate/MulticastDelegate, then
' the best it can ever be is DelegateRelaxationWideningToNonLambda.
' And if you drop the return value from a lambda, the best it can be is DelegateRelaxationLevelWideningDropReturnOrArgs...
If destination.IsStrictSupertypeOfConcreteDelegate() Then ' covers Object, System.Delegate, System.MulticastDelegate
leastRelaxationLevel = ConversionKind.DelegateRelaxationLevelWideningToNonLambda
' Infer Anonymous Delegate as the target for the lambda.
Dim anonymousDelegateInfo As KeyValuePair(Of NamedTypeSymbol, ReadOnlyBindingDiagnostic(Of AssemblySymbol)) = source.InferredAnonymousDelegate
' If we have errors for the inference, we know that there is no conversion.
If Not anonymousDelegateInfo.Value.Diagnostics.IsDefault AndAlso anonymousDelegateInfo.Value.Diagnostics.HasAnyErrors() Then
Return ConversionKind.Lambda ' No conversion
End If
delegateInvoke = anonymousDelegateInfo.Key.DelegateInvokeMethod
Else
Dim wasExpressionTree As Boolean
Dim delegateType As NamedTypeSymbol = destination.DelegateOrExpressionDelegate(source.Binder, wasExpressionTree)
delegateInvoke = If(delegateType IsNot Nothing,
delegateType.DelegateInvokeMethod,
Nothing)
If wasExpressionTree Then
conversionKindExpressionTree = ConversionKind.ConvertedToExpressionTree
End If
If delegateInvoke Is Nothing OrElse delegateInvoke.GetUseSiteInfo().DiagnosticInfo IsNot Nothing Then
Return ConversionKind.Lambda Or conversionKindExpressionTree ' No conversion
End If
If source.IsInferredDelegateForThisLambda(delegateInvoke.ContainingType) Then
Dim inferenceDiagnostics As ReadOnlyBindingDiagnostic(Of AssemblySymbol) = source.InferredAnonymousDelegate.Value
' If we have errors for the inference, we know that there is no conversion.
If Not inferenceDiagnostics.Diagnostics.IsDefault AndAlso inferenceDiagnostics.Diagnostics.HasAnyErrors() Then
Return ConversionKind.Lambda Or conversionKindExpressionTree ' No conversion
End If
End If
End If
Debug.Assert(delegateInvoke IsNot Nothing)
Dim bound As BoundLambda = source.Bind(New UnboundLambda.TargetSignature(delegateInvoke))
Debug.Assert(Not bound.Diagnostics.Diagnostics.HasAnyErrors OrElse
bound.DelegateRelaxation = ConversionKind.DelegateRelaxationLevelInvalid)
If bound.DelegateRelaxation = ConversionKind.DelegateRelaxationLevelInvalid Then
Return ConversionKind.Lambda Or ConversionKind.DelegateRelaxationLevelInvalid Or conversionKindExpressionTree ' No conversion
End If
Return ConversionKind.Lambda Or conversionKindExpressionTree Or
If(IsNarrowingMethodConversion(bound.MethodConversionKind, isForAddressOf:=False), ConversionKind.Narrowing, ConversionKind.Widening) Or
CType(Math.Max(bound.DelegateRelaxation, leastRelaxationLevel), ConversionKind)
End Function
Public Shared Function ClassifyArrayLiteralConversion(source As BoundArrayLiteral, destination As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
' § 11.1.1
' 9. An array literal can be reclassified as a value. The type of the value is determined as follows:
' 9.1. If the reclassification occurs in the context of a conversion where the target type is known and the target type is an array type,
' then the array literal is reclassified as a value of type T(). If the target type is System.Collections.Generic.IList(Of T),
' System.Collections.Generic.ICollection(Of T), or System.Collections.Generic.IEnumerable(Of T), and the array literal has one level of nesting,
' then the array literal is reclassified as a value of type T().
' 9.2. If the reclassification occurs in the context of a conversion where the target type is known and there is a user-defined conversion
' to the target type from an array type T() or from IList/ICollection/IEnumerable(Of T) as above, then the array literal is reclassified
' as a value of type T().
' 9.3. Otherwise, the array literal is reclassified to a value whose type is an array of rank equal to the level of nesting is used, with
' element type determined by the dominant type of the elements in the initializer; if no dominant type can be determined, Object is used.
' An array literal {e0,e1} can be converted to...
' * an array T() so long as each element can be converted to T
' * IEnumerable(Of T) / ICollection(Of T) / IList(Of T) so long as each element can be converted to T
' * IEnumerable / ICollection / IList
' * System.Array / System.Object
'
' Incidentally, it might be that all elements can each convert to T even though
' the array literal doesn't have a dominant type -- e.g. {lambda} or {} have no dominant type.
' And if there are two elements {e0,e1} such that e0 narrows to e1 and e1 narrows to e0 then
' again there's no dominant type.
'
' Observe that if every element has a reference conversion to T, then the conversions
' from {e0,e1} to T()/IEnumerable(Of T)/ICollection(Of T)/IList(Of T) are all predefined CLR conversions.
' Observe that the conversions to IEnumerable/ICollection/IList/System.Array/System.Object
' are always predefined CLR conversions.
Dim sourceType = source.InferredType
Dim targetType = TryCast(destination, NamedTypeSymbol)
Dim originalTargetType = If(targetType IsNot Nothing, targetType.OriginalDefinition, Nothing)
Dim targetElementType As TypeSymbol = Nothing
Dim targetArrayType As ArrayTypeSymbol = TryCast(destination, ArrayTypeSymbol)
If targetArrayType IsNot Nothing AndAlso (sourceType.Rank = targetArrayType.Rank OrElse source.IsEmptyArrayLiteral) Then
targetElementType = targetArrayType.ElementType
ElseIf (sourceType.Rank = 1 OrElse source.IsEmptyArrayLiteral) AndAlso
originalTargetType IsNot Nothing AndAlso
(originalTargetType.SpecialType = SpecialType.System_Collections_Generic_IEnumerable_T OrElse
originalTargetType.SpecialType = SpecialType.System_Collections_Generic_IList_T OrElse
originalTargetType.SpecialType = SpecialType.System_Collections_Generic_ICollection_T OrElse
originalTargetType.SpecialType = SpecialType.System_Collections_Generic_IReadOnlyList_T OrElse
originalTargetType.SpecialType = SpecialType.System_Collections_Generic_IReadOnlyCollection_T) Then
targetElementType = targetType.TypeArgumentsWithDefinitionUseSiteDiagnostics(useSiteInfo)(0)
Else
Dim conv As ConversionKind = ClassifyStringConversion(sourceType, destination)
If Conversions.NoConversion(conv) Then
' No char() to string conversion
conv = ClassifyDirectCastConversion(sourceType, destination, useSiteInfo)
End If
If Conversions.NoConversion(conv) Then
' If no predefined conversion then we're done
Return Nothing
End If
Dim arrayLiteralElementConv = ClassifyArrayInitialization(source.Initializer, sourceType.ElementType, binder, useSiteInfo)
If Conversions.NoConversion(arrayLiteralElementConv) Then
' No conversion for array elements. Preserve ConversionKind.FailedDueToArrayLiteralElementConversion
Return arrayLiteralElementConv
End If
If Conversions.IsWideningConversion(conv) Then
' If the source to destination conversion is a widening then return the array literal element conversion.
' That conversion will never be better than widening and it preserves ConversionKind.InvolvesNarrowingFromNumericConstant.
Return arrayLiteralElementConv
End If
' Return the ConversionKind.Narrowing because we do not want to propagate any additional conversion kind information.
Return ConversionKind.Narrowing
End If
Return ClassifyArrayInitialization(source.Initializer, targetElementType, binder, useSiteInfo)
End Function
Public Shared Function ClassifyInterpolatedStringConversion(source As BoundInterpolatedStringExpression, destination As TypeSymbol, binder As Binder) As ConversionKind
' A special conversion exist from an interpolated string expression to System.IFormattable or System.FormattableString.
If destination.Equals(binder.Compilation.GetWellKnownType(WellKnownType.System_FormattableString)) OrElse
destination.Equals(binder.Compilation.GetWellKnownType(WellKnownType.System_IFormattable)) _
Then
Return ConversionKind.InterpolatedString
End If
Return Nothing
End Function
Public Shared Function ClassifyTupleConversion(source As BoundTupleLiteral, destination As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
If TypeSymbol.Equals(source.Type, destination, TypeCompareKind.ConsiderEverything) Then
Return ConversionKind.Identity
End If
Dim arguments = source.Arguments
Dim wideningConversion = ConversionKind.WideningTuple
Dim narrowingConversion = ConversionKind.NarrowingTuple
If destination.IsNullableType Then
destination = destination.GetNullableUnderlyingType()
wideningConversion = ConversionKind.WideningNullableTuple
narrowingConversion = ConversionKind.NarrowingNullableTuple
End If
' tuple literal converts to its inferred type
If source.InferredType?.IsSameTypeIgnoringAll(destination) Then
Return wideningConversion
End If
' Now we can try element-wise conversion
' check if the type is actually compatible type for a tuple of given cardinality
If Not destination.IsTupleOrCompatibleWithTupleOfCardinality(arguments.Length) Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim targetElementTypes As ImmutableArray(Of TypeSymbol) = destination.GetElementTypesOfTupleOrCompatible()
Debug.Assert(arguments.Length = targetElementTypes.Length)
' check arguments against flattened list of target element types
Dim result As ConversionKind = wideningConversion
' Note, as an optimization this local may accumulate flags other than ConversionKind.InvolvesNarrowingFromNumericConstant,
' but we are going to pay attention only to that bit at the end.
Dim involvesNarrowingFromNumericConstant As ConversionKind = Nothing
Dim allNarrowingIsFromNumericConstant = ConversionKind.InvolvesNarrowingFromNumericConstant
Dim maxDelegateRelaxationLevel = ConversionKind.DelegateRelaxationLevelNone
For i As Integer = 0 To arguments.Length - 1
Dim argument = arguments(i)
Dim targetElementType = targetElementTypes(i)
If argument.HasErrors OrElse targetElementType.IsErrorType Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim elementConversion = ClassifyConversion(argument, targetElementType, binder, useSiteInfo).Key
If NoConversion(elementConversion) Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim elementDelegateRelaxationLevel = elementConversion And ConversionKind.DelegateRelaxationLevelMask
If elementDelegateRelaxationLevel > maxDelegateRelaxationLevel Then
maxDelegateRelaxationLevel = elementDelegateRelaxationLevel
End If
involvesNarrowingFromNumericConstant = involvesNarrowingFromNumericConstant Or elementConversion
If IsNarrowingConversion(elementConversion) Then
allNarrowingIsFromNumericConstant = allNarrowingIsFromNumericConstant And elementConversion
result = narrowingConversion
End If
Next
Debug.Assert((allNarrowingIsFromNumericConstant And Not ConversionKind.InvolvesNarrowingFromNumericConstant) = 0)
Return result Or (involvesNarrowingFromNumericConstant And allNarrowingIsFromNumericConstant) Or maxDelegateRelaxationLevel
End Function
Private Shared Function ClassifyArrayInitialization(source As BoundArrayInitialization, targetElementType As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
' Now we have to check that every element converts to TargetElementType.
' It's tempting to say "if the dominant type converts to TargetElementType, then it must be true that
' every element converts." But this isn't true for several reasons. First, the dominant type might
' be the unique NARROWING candidate among the elements in the case that no widest elements existed.
If targetElementType.IsErrorType Then
Return ConversionKind.FailedDueToArrayLiteralElementConversion
End If
Dim result = ConversionKind.Widening
Dim mergedInvolvesNarrowingFromNumericConstant As ConversionKind = ConversionKind.InvolvesNarrowingFromNumericConstant
Dim propagateInvolvesNarrowingFromNumericConstant As Boolean = False 'Has a value been set in mergedInvolvesNarrowingFromNumericConstant?
' We need to propagate InvolvesNarrowingFromNumericConstant bit. If there is at least one narrowing without the bit,
' it shouldn't be propagated, otherwise if there was a widening or narrowing with this bit, it should be propagated.
For Each sourceElement In source.Initializers
Dim elementConv As ConversionKind
If sourceElement.Kind = BoundKind.ArrayInitialization Then
elementConv = ClassifyArrayInitialization(DirectCast(sourceElement, BoundArrayInitialization), targetElementType, binder, useSiteInfo)
Else
elementConv = ClassifyConversion(sourceElement, targetElementType, binder, useSiteInfo).Key
End If
If IsNarrowingConversion(elementConv) Then
result = ConversionKind.Narrowing
'If any narrowing conversion is missing InvolvesNarrowingFromNumericConstant then clear the bit.
mergedInvolvesNarrowingFromNumericConstant = mergedInvolvesNarrowingFromNumericConstant And elementConv
propagateInvolvesNarrowingFromNumericConstant = True
ElseIf NoConversion(elementConv) Then
result = ConversionKind.FailedDueToArrayLiteralElementConversion
propagateInvolvesNarrowingFromNumericConstant = False
Exit For
End If
If IsWideningConversion(result) AndAlso (elementConv And ConversionKind.InvolvesNarrowingFromNumericConstant) <> 0 Then
' If all conversions up to this point are widening and this element has InvolvesNarrowingFromNumericConstant then
' result should include InvolvesNarrowingFromNumericConstant.
Debug.Assert((mergedInvolvesNarrowingFromNumericConstant And ConversionKind.InvolvesNarrowingFromNumericConstant) <> 0)
propagateInvolvesNarrowingFromNumericConstant = True
End If
Next
If propagateInvolvesNarrowingFromNumericConstant Then
result = result Or mergedInvolvesNarrowingFromNumericConstant
End If
Return result
End Function
Private Shared Function ClassifyAddressOfConversion(source As BoundAddressOfOperator, destination As TypeSymbol) As ConversionKind
Return Binder.ClassifyAddressOfConversion(source, destination)
End Function
Private Shared Function ClassifyQueryLambdaConversion(source As BoundQueryLambda, destination As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
' The delegate type we're converting to (could be type argument of Expression(Of T)).
Dim wasExpressionTree As Boolean
Dim delegateDestination As NamedTypeSymbol = destination.DelegateOrExpressionDelegate(binder, wasExpressionTree)
If delegateDestination Is Nothing Then
Return Nothing ' No conversion
End If
Dim conversionKindExpressionTree As ConversionKind = If(wasExpressionTree, ConversionKind.ConvertedToExpressionTree, Nothing)
Dim invoke As MethodSymbol = delegateDestination.DelegateInvokeMethod
If invoke Is Nothing OrElse invoke.GetUseSiteInfo().DiagnosticInfo IsNot Nothing OrElse invoke.IsSub Then
Return Nothing ' No conversion
End If
' Parameter types should match.
If invoke.ParameterCount <> source.LambdaSymbol.ParameterCount Then
Return Nothing ' No conversion
End If
Dim lambdaParams As ImmutableArray(Of ParameterSymbol) = source.LambdaSymbol.Parameters
Dim invokeParams As ImmutableArray(Of ParameterSymbol) = invoke.Parameters
For i As Integer = 0 To lambdaParams.Length - 1
Dim lambdaParam = lambdaParams(i)
Dim invokeParam = invokeParams(i)
If lambdaParam.IsByRef <> invokeParam.IsByRef OrElse
Not lambdaParam.Type.IsSameTypeIgnoringAll(invokeParam.Type) Then
Return Nothing ' No conversion
End If
Next
If source.LambdaSymbol.ReturnType Is LambdaSymbol.ReturnTypePendingDelegate Then
If Not invoke.ReturnType.IsErrorType() Then
' TODO: May need to do classification in a special way when ExprIsOperandOfConditionalBranch==True,
' because we may need to check for IsTrue operator.
Dim conv As KeyValuePair(Of ConversionKind, MethodSymbol)
If source.ExprIsOperandOfConditionalBranch AndAlso invoke.ReturnType.IsBooleanType() Then
conv = ClassifyConversionOfOperandOfConditionalBranch(source.Expression, invoke.ReturnType, binder, Nothing, Nothing, useSiteInfo)
Else
conv = ClassifyConversion(source.Expression, invoke.ReturnType, binder, useSiteInfo)
End If
If IsIdentityConversion(conv.Key) Then
Return conv.Key And (Not ConversionKind.Identity) Or (ConversionKind.Widening Or ConversionKind.Lambda) Or conversionKindExpressionTree
ElseIf NoConversion(conv.Key) Then
Return conv.Key Or (ConversionKind.Lambda Or ConversionKind.FailedDueToQueryLambdaBodyMismatch) Or conversionKindExpressionTree
Else
Debug.Assert(((conv.Key And ConversionKind.UserDefined) <> 0) = (conv.Value IsNot Nothing))
Return (conv.Key And (Not (ConversionKind.UserDefined Or ConversionKind.Nullable Or ConversionKind.Tuple))) Or ConversionKind.Lambda Or conversionKindExpressionTree
End If
End If
ElseIf invoke.ReturnType.IsSameTypeIgnoringAll(source.LambdaSymbol.ReturnType) Then
Return ConversionKind.Widening Or ConversionKind.Lambda Or conversionKindExpressionTree
End If
Return Nothing ' No conversion
End Function
Public Shared Function ClassifyConversionOfOperandOfConditionalBranch(
operand As BoundExpression,
booleanType As TypeSymbol,
binder As Binder,
<Out> ByRef applyNullableIsTrueOperator As Boolean,
<Out> ByRef isTrueOperator As OverloadResolution.OverloadResolutionResult,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As KeyValuePair(Of ConversionKind, MethodSymbol)
Debug.Assert(operand IsNot Nothing)
Debug.Assert(booleanType IsNot Nothing)
Debug.Assert(booleanType.Kind <> SymbolKind.ErrorType)
Debug.Assert(booleanType.IsBooleanType())
' 11.19 Boolean Expressions
' A Boolean expression is an expression that can be tested to see if it is true or if it is false.
' A type T can be used in a Boolean expression if, in order of preference:
' - T is Boolean or Boolean?
' - T has a widening conversion to Boolean
' - T has a widening conversion to Boolean?
' - T defines two pseudo operators, IsTrue and IsFalse.
' - T has a narrowing conversion to Boolean? that does not involve a conversion from Boolean to Boolean?.
' - T has a narrowing conversion to Boolean.
'
' If a Boolean expression is typed as or converted to Boolean or Boolean?,
' then it is true if the value is True and false otherwise.
' Otherwise, a Boolean expression calls the IsTrue operator and returns True if the operator returned True;
' otherwise it is false (but never calls the IsFalse operator).
applyNullableIsTrueOperator = False
isTrueOperator = Nothing
Dim conv As KeyValuePair(Of ConversionKind, MethodSymbol) = Conversions.ClassifyConversion(operand, booleanType, binder, useSiteInfo)
' See if we need to use IsTrue operator.
If Conversions.IsWideningConversion(conv.Key) Then
' - T is Boolean
' - T has a widening conversion to Boolean
Return conv
End If
Dim sourceType = operand.Type
If sourceType IsNot Nothing AndAlso Not sourceType.IsErrorType() AndAlso Not sourceType.IsObjectType() Then
Dim nullableOfBoolean As TypeSymbol = Nothing
Dim convToNullableOfBoolean As KeyValuePair(Of ConversionKind, MethodSymbol) = Nothing
If sourceType.IsNullableOfBoolean() Then
' The source is Boolean?
convToNullableOfBoolean = Conversions.Identity
nullableOfBoolean = sourceType
Else
Dim nullableOfT As NamedTypeSymbol = booleanType.ContainingAssembly.GetSpecialType(SpecialType.System_Nullable_T)
If Not nullableOfT.IsErrorType() AndAlso
(sourceType.IsNullableType() OrElse sourceType.CanContainUserDefinedOperators(useSiteInfo)) Then
nullableOfBoolean = nullableOfT.Construct(ImmutableArray.Create(Of TypeSymbol)(booleanType))
convToNullableOfBoolean = Conversions.ClassifyConversion(operand, nullableOfBoolean, binder, useSiteInfo)
End If
End If
If Conversions.IsWideningConversion(convToNullableOfBoolean.Key) Then
' - T is Boolean?
' - T has a widening conversion to Boolean?
applyNullableIsTrueOperator = True
Return convToNullableOfBoolean
Else
' Is there IsTrue operator that we can use.
Dim results As OverloadResolution.OverloadResolutionResult = Nothing
If sourceType.CanContainUserDefinedOperators(useSiteInfo) Then
results = OverloadResolution.ResolveIsTrueOperator(operand, binder, useSiteInfo)
End If
If results.BestResult.HasValue Then
' - T defines two pseudo operators, IsTrue and IsFalse.
isTrueOperator = results
If results.BestResult.Value.Candidate.IsLifted Then
applyNullableIsTrueOperator = True
End If
Debug.Assert(Not results.BestResult.Value.RequiresNarrowingConversion)
Return New KeyValuePair(Of ConversionKind, MethodSymbol)(ConversionKind.Widening, Nothing)
ElseIf Conversions.IsNarrowingConversion(convToNullableOfBoolean.Key) AndAlso
Not ((convToNullableOfBoolean.Key And (ConversionKind.UserDefined Or ConversionKind.Nullable)) =
ConversionKind.UserDefined AndAlso
convToNullableOfBoolean.Value.ReturnType.IsBooleanType()) Then
' - T has a narrowing conversion to Boolean? that does not involve a conversion from Boolean to Boolean?.
applyNullableIsTrueOperator = True
Return convToNullableOfBoolean
End If
End If
End If
' - T has a narrowing conversion to Boolean.
' - No conversion
Return conv
End Function
Private Shared Function ClassifyGroupTypeInferenceLambdaConversion(source As GroupTypeInferenceLambda, destination As TypeSymbol) As ConversionKind
Debug.Assert(source.Type Is Nothing) ' Shouldn't try to convert already converted query lambda.
Dim delegateType As NamedTypeSymbol = destination.DelegateOrExpressionDelegate(source.Binder)
If delegateType Is Nothing Then
Return Nothing ' No conversion
End If
Dim invoke As MethodSymbol = delegateType.DelegateInvokeMethod
' Type of the first parameter of the delegate must be source.TypeOfGroupingKey.
' Return type of the delegate must be an Anonymous Type corresponding to the following initializer:
' New With {key .$VB$ItAnonymous = <delegates's second parameter> }
If invoke Is Nothing OrElse invoke.GetUseSiteInfo().DiagnosticInfo IsNot Nothing OrElse invoke.IsSub Then
Return Nothing ' No conversion
End If
' Parameter types should match.
Dim lambdaParams As ImmutableArray(Of ParameterSymbol) = source.Parameters
If invoke.ParameterCount <> lambdaParams.Length Then
Return Nothing ' No conversion
End If
Dim invokeParams As ImmutableArray(Of ParameterSymbol) = invoke.Parameters
For i As Integer = 0 To lambdaParams.Length - 1
Dim lambdaParam = lambdaParams(i)
Dim invokeParam = invokeParams(i)
If lambdaParam.IsByRef <> invokeParam.IsByRef OrElse
(lambdaParam.Type IsNot Nothing AndAlso Not lambdaParam.Type.IsSameTypeIgnoringAll(invokeParam.Type)) Then
Return Nothing ' No conversion
End If
Next
If Not invoke.ReturnType.IsAnonymousType Then
Return Nothing ' No conversion
End If
Dim returnType = DirectCast(invoke.ReturnType, NamedTypeSymbol)
Dim anonymousType = DirectCast(returnType, AnonymousTypeManager.AnonymousTypePublicSymbol)
If anonymousType.Properties.Length <> 1 OrElse
anonymousType.Properties(0).SetMethod IsNot Nothing OrElse
Not anonymousType.Properties(0).Name.Equals(GeneratedNameConstants.ItAnonymous) OrElse
Not invokeParams(1).Type.IsSameTypeIgnoringAll(anonymousType.Properties(0).Type) Then
Return Nothing ' No conversion
End If
Return (ConversionKind.Widening Or ConversionKind.Lambda)
End Function
Private Shared Function ClassifyNumericConstantConversion(constantExpression As BoundExpression, destination As TypeSymbol, binder As Binder) As ConversionKind
Debug.Assert(constantExpression.ConstantValueOpt IsNot Nothing)
If constantExpression.ConstantValueOpt.IsBad Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim targetDestinationType As TypeSymbol = destination.GetNullableUnderlyingTypeOrSelf()
'§8.8 Widening Conversions
'• From the literal 0 to an enumerated type.
If constantExpression.IsIntegerZeroLiteral() AndAlso targetDestinationType.IsEnumType() AndAlso
DirectCast(targetDestinationType, NamedTypeSymbol).EnumUnderlyingType.IsIntegralType() Then
If targetDestinationType Is destination Then
Return ConversionKind.WideningNumeric Or ConversionKind.InvolvesEnumTypeConversions
Else
' Target is nullable, conversion is narrowing, but we need to preserve the fact that it was from literal.
Return ConversionKind.NarrowingNullable Or ConversionKind.InvolvesNarrowingFromNumericConstant
End If
End If
Dim sourceType As TypeSymbol = constantExpression.Type
Debug.Assert(sourceType IsNot Nothing) ' Shouldn't this be a [Nothing] literal?
If sourceType Is Nothing Then
Return Nothing 'ConversionKind.NoConversion
End If
If sourceType Is destination Then
Return ConversionKind.Identity
End If
Dim conv As ConversionKind = Nothing 'ConversionKind.NoConversions
Dim integerOverflow As Boolean = False
Dim result As ConstantValue
If sourceType.IsIntegralType() Then
'§8.8 Widening Conversions
'• From a constant expression of type ULong, Long, UInteger, Integer, UShort, Short, Byte, or SByte to
' a narrower type, provided the value of the constant expression is within the range of the destination type.
If targetDestinationType.IsIntegralType() Then
conv = FastClassifyPredefinedConversion(sourceType, destination).Value
If IsNarrowingConversion(conv) Then
conv = conv Or ConversionKind.InvolvesNarrowingFromNumericConstant
' check if the value is within the target range
result = TryFoldConstantNumericOrBooleanConversion(constantExpression.ConstantValueOpt, sourceType, targetDestinationType,
integerOverflow)
Debug.Assert(Not result.IsBad)
If Not integerOverflow Then
' Reclassify as widening, but not for a Nullable target type
If targetDestinationType Is destination Then
conv = (conv And (Not ConversionKind.Narrowing)) Or ConversionKind.Widening
End If
ElseIf binder.CheckOverflow Then
' Compiler generated code (for example, implementation of GetHashCode for Anonymous Types)
' not always uses project level setting for the option.
Debug.Assert(sourceType.AllowsCompileTimeConversions() AndAlso targetDestinationType.AllowsCompileTimeConversions())
Return ConversionKind.FailedDueToIntegerOverflow
End If
Else
Debug.Assert(IsWideningConversion(conv))
End If
Return conv
End If
ElseIf sourceType.IsFloatingType() Then
If targetDestinationType.IsFloatingType() Then
conv = FastClassifyPredefinedConversion(sourceType, destination).Value
If IsNarrowingConversion(conv) Then
conv = conv Or ConversionKind.InvolvesNarrowingFromNumericConstant
Else
Debug.Assert(IsWideningConversion(conv))
End If
End If
End If
' We need to detect overflow errors for constant conversions during classification to make sure overload resolution
' takes the overflow errors into consideration.
If Not IsWideningConversion(conv) Then
Dim underlyingSourceType = sourceType.GetEnumUnderlyingTypeOrSelf()
Dim underlyingDestination = destination.GetNullableUnderlyingTypeOrSelf().GetEnumUnderlyingTypeOrSelf()
If underlyingSourceType.IsNumericType() AndAlso underlyingDestination.IsNumericType() Then
Debug.Assert(sourceType.AllowsCompileTimeConversions() AndAlso destination.GetNullableUnderlyingTypeOrSelf().AllowsCompileTimeConversions())
result = TryFoldConstantNumericOrBooleanConversion(constantExpression.ConstantValueOpt, underlyingSourceType, underlyingDestination,
integerOverflow)
If result.IsBad Then
conv = ConversionKind.FailedDueToNumericOverflow
ElseIf integerOverflow AndAlso binder.CheckOverflow Then
' Compiler generated code (for example, implementation of GetHashCode for Anonymous Types)
' not always uses project level setting for the option.
Return ConversionKind.FailedDueToIntegerOverflow
End If
End If
End If
Return conv
End Function
Private Shared Function ClassifyNothingLiteralConversion(constantExpression As BoundExpression, destination As TypeSymbol) As ConversionKind
'§8.8 Widening Conversions
'• From the literal Nothing to a type.
' We fold NOTHING conversions, as Dev10 does. And for the purpose of conversion classification,
' Dev10 ignores explicitly converted NOTHING.
If constantExpression.IsStrictNothingLiteral() Then
If destination.IsObjectType() AndAlso constantExpression.Type IsNot Nothing AndAlso
constantExpression.Type.IsObjectType() Then
Return ConversionKind.Identity
End If
Return ConversionKind.WideningNothingLiteral
End If
Return Nothing 'ConversionKind.NoConversion
End Function
Public Shared Function ClassifyDirectCastConversion(
source As TypeSymbol,
destination As TypeSymbol,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(source.Kind <> SymbolKind.ErrorType)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
'§8.12 Native Conversions
'The conversions classified as native conversions are:
'identity conversions, default conversions, reference conversions,
'array conversions, value type conversions, and type parameter conversions.
Dim result As ConversionKind
'Identity/Default conversions
result = ClassifyIdentityConversion(source, destination)
If ConversionExists(result) Then
Return result
End If
' Numeric conversions
' CLI spec says "enums shall have a built-in integer type" (ECMA I $8.5.2) and gives
' a list of built-in types (ECMA I $8.2.2). The only built-in integer types are i8,ui8,i16,ui16,i32,ui32,i64,ui64
If source.IsIntegralType() Then
If destination.TypeKind = TypeKind.Enum AndAlso
DirectCast(destination, NamedTypeSymbol).EnumUnderlyingType.Equals(source) Then
Return ConversionKind.NarrowingNumeric Or ConversionKind.InvolvesEnumTypeConversions
End If
ElseIf destination.IsIntegralType() Then
If source.TypeKind = TypeKind.Enum AndAlso
DirectCast(source, NamedTypeSymbol).EnumUnderlyingType.Equals(destination) Then
Return ConversionKind.WideningNumeric Or ConversionKind.InvolvesEnumTypeConversions
End If
ElseIf source.TypeKind = TypeKind.Enum AndAlso destination.TypeKind = TypeKind.Enum Then
Dim srcUnderlying = DirectCast(source, NamedTypeSymbol).EnumUnderlyingType
If srcUnderlying.IsIntegralType() AndAlso
srcUnderlying.Equals(DirectCast(destination, NamedTypeSymbol).EnumUnderlyingType) Then
Return ConversionKind.NarrowingNumeric Or ConversionKind.InvolvesEnumTypeConversions
End If
End If
'Reference conversions
result = ClassifyReferenceConversion(source, destination, varianceCompatibilityClassificationDepth:=0, useSiteInfo:=useSiteInfo)
If ConversionExists(result) Then
Return result
End If
'Array conversions
result = ClassifyArrayConversion(source, destination, varianceCompatibilityClassificationDepth:=0, useSiteInfo:=useSiteInfo)
If ConversionExists(result) Then
Return result
End If
'Value Type conversions
result = ClassifyValueTypeConversion(source, destination, useSiteInfo)
If ConversionExists(result) Then
Return result
End If
'Type Parameter conversions
result = ClassifyTypeParameterConversion(source, destination, varianceCompatibilityClassificationDepth:=0, useSiteInfo:=useSiteInfo)
Return result
End Function
Public Shared Function ClassifyDirectCastConversion(source As BoundExpression, destination As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
Dim conv As ConversionKind
If source.IsConstant Then
conv = ClassifyNothingLiteralConversion(source, destination)
If ConversionExists(conv) Then
Return conv
End If
End If
' Reclassify lambdas, array literals, etc.
conv = ClassifyExpressionReclassification(source, destination, binder, useSiteInfo)
If ConversionExists(conv) OrElse (conv And (ConversionKind.Lambda Or ConversionKind.FailedDueToArrayLiteralElementConversion)) <> 0 Then
Return conv
End If
If Not (source.IsValue) Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim sourceType As TypeSymbol = source.Type
If sourceType Is Nothing Then
' The node doesn't have a type yet and reclassification failed.
Return Nothing ' No conversion
End If
If sourceType.Kind <> SymbolKind.ErrorType Then
Return ClassifyDirectCastConversion(sourceType, destination, useSiteInfo)
End If
Return Nothing
End Function
Public Shared Function ClassifyTryCastConversion(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(source.Kind <> SymbolKind.ErrorType)
Debug.Assert(Not TypeOf source Is ArrayLiteralTypeSymbol)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
Dim result As ConversionKind
result = ClassifyDirectCastConversion(source, destination, useSiteInfo)
If ConversionExists(result) Then
Return result
End If
Return ClassifyTryCastConversionForTypeParameters(source, destination, useSiteInfo)
End Function
Public Shared Function ClassifyTryCastConversion(source As BoundExpression, destination As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
Dim conv As ConversionKind
If source.IsConstant Then
conv = ClassifyNothingLiteralConversion(source, destination)
If ConversionExists(conv) Then
Return conv
End If
End If
' Reclassify lambdas, array literals, etc.
conv = ClassifyExpressionReclassification(source, destination, binder, useSiteInfo)
If ConversionExists(conv) OrElse (conv And (ConversionKind.Lambda Or ConversionKind.FailedDueToArrayLiteralElementConversion)) <> 0 Then
Return conv
End If
If Not (source.IsValue) Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim sourceType As TypeSymbol = source.Type
If sourceType Is Nothing Then
' The node doesn't have a type yet and reclassification failed.
Return Nothing ' No conversion
End If
If sourceType.Kind <> SymbolKind.ErrorType Then
Return ClassifyTryCastConversion(sourceType, destination, useSiteInfo)
End If
Return Nothing
End Function
Private Shared Function ClassifyTryCastConversionForTypeParameters(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
Dim sourceKind = source.Kind
Dim destinationKind = destination.Kind
If sourceKind = SymbolKind.ArrayType AndAlso destinationKind = SymbolKind.ArrayType Then
Dim sourceArray = DirectCast(source, ArrayTypeSymbol)
Dim destinationArray = DirectCast(destination, ArrayTypeSymbol)
Dim sourceElement = sourceArray.ElementType
Dim destinationElement = destinationArray.ElementType
If sourceElement.IsReferenceType Then
If destinationElement.IsValueType Then
Return Nothing 'ConversionKind.NoConversion
End If
ElseIf sourceElement.IsValueType Then
If destinationElement.IsReferenceType Then
Return Nothing 'ConversionKind.NoConversion
End If
End If
' Note that Dev10 compiler does not require arrays rank to match, which is probably
' an oversight. Will match the same behavior for now.
Return ClassifyTryCastConversionForTypeParameters(sourceElement, destinationElement, useSiteInfo)
End If
If sourceKind <> SymbolKind.TypeParameter AndAlso destinationKind <> SymbolKind.TypeParameter Then
Return Nothing 'ConversionKind.NoConversion
End If
' Notation:
' T - non class constrained type parameter
' TC - class constrained type parameter
' CC - Class constraint - eg: CC1 is class constraint of TC1
' C - class
' NC - Notinheritable class
'
' A - Conversions between Type parameters:
'
' 1. T1 -> T2 Conversion possible because T1 and T2 could be any types and thus be potentially related
'
' 2. TC1 -> T2 Conversion possible because T2 could be any type and thus be potentially related to TC1
'
' 3. T1 -> TC2 Conversion possible because T1 could be any type and thus be potentially related to TC2
'
' 4. TC1 -> TC2 Conversion possible only when CC1 and CC2 are related through inheritance, else
' TC1 and TC2 would always be guaranteed to be classes along 2 completely unrelated
' inheritance hierarchies.
'
'
' B - Conversions between Type parameter and a non-type parameter type:
'
' 1. T1 -> C2 Conversion possible because T1 could be any type and thus potentially related to C2
'
' 2. C1 -> T2 Conversion possible because T2 could be any type and thus potentially related to C1
'
' 3. TC1 -> C2 Conversion possible only when CC1 and C2 are related through inheritance, else
' TC1 and C2 would always be guaranteed to be classes along unrelated inheritance
' hierarchies.
'
' 4. C1 -> TC2 Conversion possible only when C1 and CC2 are related through inheritance, else
' C1 and CC2 would always be guaranteed to be classes along unrelated inheritance
' hierarchies.
'
' 5. NC1 -> TC2 Conversion possible only when one of NC1 or its bases satisfies constraints of TC2
' because those are the only types related to NC1 that could ever be passed to TC2.
'
' 6. TC1 -> NC2 Conversion possible only when one of NC2 or its bases satisfies constraints of TC1
' because those are the only types related to NC1 that could ever be passed to TC1.
'
'
' Both A and B above are unified conceptually by treating C1 and C2 to be type parameters constrained
' to class constraints C1 and C2 respectively.
'
' Note that type params with the value type constraint i.e "Structure" is treated as a param with a class
' constraint of System.ValueType.
'
' Note that the reference type constraint does not affect the try cast conversions because it does not prevent
' System.ValueType and System.Enum and so value types can still be related to type params with such constraints.
'
Dim src = GetNonInterfaceTypeConstraintOrSelf(source, useSiteInfo)
Dim dst = GetNonInterfaceTypeConstraintOrSelf(destination, useSiteInfo)
' If the class constraint is Nothing, then conversion is possible because the non-class constrained
' type parameter can be any type related to the other type or type parameter.
'
' Handles cases: A.1, A.2, A.3, B.1 and B.2
'
If src Is Nothing OrElse dst Is Nothing Then
Return ConversionKind.Narrowing
End If
Debug.Assert(src.Kind <> SymbolKind.TypeParameter)
Debug.Assert(dst.Kind <> SymbolKind.TypeParameter)
' partially handles cases: A.4, B.3, B.4, B.5 and B.6
'
Dim conv As ConversionKind = ClassifyDirectCastConversion(src, dst, useSiteInfo)
If IsWideningConversion(conv) Then
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
' Since NotInheritable classes cannot be inherited, more conversion possibilities
' could be ruled out at compiler time if the NotInheritable class or any of its
' base can never be used as a type argument for this type parameter during type
' instantiation.
'
' partially handles cases: B.5 and B.6
'
If destinationKind = SymbolKind.TypeParameter AndAlso
(src.TypeKind <> TypeKind.Class OrElse DirectCast(src, NamedTypeSymbol).IsNotInheritable) AndAlso
Not ClassOrBasesSatisfyConstraints(src, DirectCast(destination, TypeParameterSymbol), useSiteInfo) Then
Return Nothing 'ConversionKind.NoConversion
End If
Return ConversionKind.Narrowing Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
' partially handles cases: A.4, B.3 and B.4
conv = ClassifyDirectCastConversion(dst, src, useSiteInfo)
If IsWideningConversion(conv) Then
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
' Since NotInheritable classes cannot be inherited, more conversion possibilities
' could be rules out at compiler time if the NotInheritable class or any of its
' base can never be used as a type argument for this type parameter during type
' instantiation.
'
' partially handles cases: B.5 and B.6
'
If sourceKind = SymbolKind.TypeParameter AndAlso
(dst.TypeKind <> TypeKind.Class OrElse DirectCast(dst, NamedTypeSymbol).IsNotInheritable) AndAlso
Not ClassOrBasesSatisfyConstraints(dst, DirectCast(source, TypeParameterSymbol), useSiteInfo) Then
Return Nothing 'ConversionKind.NoConversion
End If
Return ConversionKind.Narrowing Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
' No conversion ever possible
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassOrBasesSatisfyConstraints([class] As TypeSymbol, typeParam As TypeParameterSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Dim candidate As TypeSymbol = [class]
While candidate IsNot Nothing
If ConstraintsHelper.CheckConstraints(constructedSymbol:=Nothing,
LanguageVersion.Latest, ' Classifying conversions from/to type parameters. This is meaningful only when they and their constraints are declared in source
substitution:=Nothing,
typeParameter:=typeParam,
typeArgument:=candidate,
diagnosticsBuilder:=Nothing,
useSiteInfo:=useSiteInfo) Then
Return True
End If
candidate = candidate.BaseTypeWithDefinitionUseSiteDiagnostics(useSiteInfo)
End While
Return False
End Function
Private Shared Function GetNonInterfaceTypeConstraintOrSelf(type As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As TypeSymbol
If type.Kind = SymbolKind.TypeParameter Then
Dim typeParameter = DirectCast(type, TypeParameterSymbol)
If typeParameter.HasValueTypeConstraint Then
' This method returns System.ValueType if the type parameter has a 'Structure'
' constraint, even if there is a more specific constraint type. We could return
' typeParameter.GetNonInterfaceConstraint(), if not Nothing, but that would be
' a breaking change from Dev10. Specifically, in the following, "TypeOf _1 Is U2"
' would be reported (correctly) as an error ("BC31430: Expression of type 'U1'
' can never be of type 'U2'"). Dev10 does not report an error in this case.
'
' Public Overrides Sub M(Of U1 As {Structure, S1}, U2 As {Structure, S2})(_1 As U1)
' If TypeOf _1 Is U2 Then
' End If
' End Sub
Dim valueType = typeParameter.ContainingAssembly.GetSpecialType(SpecialType.System_ValueType)
Return If(valueType.Kind = SymbolKind.ErrorType, Nothing, valueType)
End If
Return typeParameter.GetNonInterfaceConstraint(useSiteInfo)
End If
Return type
End Function
''' <summary>
''' This function classifies user-defined conversions between two types.
''' </summary>
''' <param name="source"></param>
''' <param name="destination"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Shared Function ClassifyUserDefinedConversion(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As KeyValuePair(Of ConversionKind, MethodSymbol)
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(source.Kind <> SymbolKind.ErrorType)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
' ClassifyUserDefinedConversion is the only method that allows the source to be an ArrayLiteralTypeSymbol.
If IsInterfaceType(source) OrElse
IsInterfaceType(destination) OrElse
Not (source.CanContainUserDefinedOperators(useSiteInfo) OrElse destination.CanContainUserDefinedOperators(useSiteInfo)) Then
Return Nothing 'ConversionKind.NoConversion
End If
Return OverloadResolution.ResolveUserDefinedConversion(source, destination, useSiteInfo)
End Function
''' <summary>
''' This function classifies user-defined conversions.
''' </summary>
''' <param name="source"></param>
''' <param name="destination"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Shared Function ClassifyUserDefinedConversion(source As BoundExpression, destination As TypeSymbol, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As KeyValuePair(Of ConversionKind, MethodSymbol)
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
Dim sourceType As TypeSymbol = source.Type
If sourceType Is Nothing Then
source = source.GetMostEnclosedParenthesizedExpression()
If source.Kind = BoundKind.ArrayLiteral Then
sourceType = New ArrayLiteralTypeSymbol(DirectCast(source, BoundArrayLiteral))
ElseIf source.Kind = BoundKind.TupleLiteral Then
sourceType = DirectCast(source, BoundTupleLiteral).InferredType
If sourceType Is Nothing Then
Return Nothing
End If
Else
sourceType = source.Type
End If
End If
Debug.Assert(sourceType IsNot Nothing)
Debug.Assert(sourceType.Kind <> SymbolKind.ErrorType)
Dim conv As KeyValuePair(Of ConversionKind, MethodSymbol) = ClassifyUserDefinedConversion(sourceType, destination, useSiteInfo)
If NoConversion(conv.Key) Then
Return conv
End If
If IsNarrowingConversion(conv.Key) Then
' Conversion between types can exist, but it might still be unsupported due to numeric overflow detected, etc.
Dim userDefinedInputType = conv.Value.Parameters(0).Type
Dim inConversion As ConversionKind
If (source.Kind <> BoundKind.ArrayLiteral) Then
inConversion = ClassifyPredefinedConversion(source, userDefinedInputType, binder, useSiteInfo)
Else
inConversion = ClassifyArrayLiteralConversion(DirectCast(source, BoundArrayLiteral), userDefinedInputType, binder, useSiteInfo)
End If
If NoConversion(inConversion) Then
Debug.Assert(FailedDueToNumericOverflow(inConversion))
' When we classify user-defined conversion from array literal, we are using the array literal
' rather than its inferred type and failure due to a numeric overflow should be detected then
' and ClassifyUserDefinedConversion should return NoConversion.
Debug.Assert(source.Kind <> BoundKind.ArrayLiteral)
If FailedDueToNumericOverflow(inConversion) Then
' Preserve the fact of numeric overflow.
conv = New KeyValuePair(Of ConversionKind, MethodSymbol)((conv.Key And Not ConversionKind.Narrowing) Or
(inConversion And ConversionKind.FailedDueToNumericOverflowMask),
conv.Value)
Debug.Assert(NoConversion(conv.Key))
Return conv
End If
Return Nothing
End If
' Need to keep track of the fact that narrowing is from numeric constant.
If (inConversion And ConversionKind.InvolvesNarrowingFromNumericConstant) <> 0 AndAlso
OverloadResolution.IsWidening(conv.Value) AndAlso
IsWideningConversion(ClassifyPredefinedConversion(conv.Value.ReturnType, destination, useSiteInfo)) Then
Dim newConv As ConversionKind = conv.Key Or ConversionKind.InvolvesNarrowingFromNumericConstant
If IsWideningConversion(inConversion) Then
' Treat the whole conversion as widening.
newConv = (newConv And Not ConversionKind.Narrowing) Or ConversionKind.Widening
End If
conv = New KeyValuePair(Of ConversionKind, MethodSymbol)(newConv, conv.Value)
End If
End If
Return conv
End Function
''' <summary>
''' This function classifies all intrinsic language conversions, such as inheritance,
''' implementation, array covariance, and conversions between intrinsic types.
''' </summary>
Public Shared Function ClassifyPredefinedConversion(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
Debug.Assert(source IsNot Nothing)
Debug.Assert(destination IsNot Nothing)
Debug.Assert(source.Kind <> SymbolKind.ErrorType)
Debug.Assert(Not TypeOf source Is ArrayLiteralTypeSymbol)
Debug.Assert(destination.Kind <> SymbolKind.ErrorType)
' Try using the short-circuit "fast-conversion" path.
Dim fastConversion = FastClassifyPredefinedConversion(source, destination)
If fastConversion.HasValue Then
Return fastConversion.Value
End If
Return ClassifyPredefinedConversionSlow(source, destination, useSiteInfo)
End Function
Private Shared Function ClassifyPredefinedConversionSlow(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
Dim result As ConversionKind
'Identity/Default conversions
result = ClassifyIdentityConversion(source, destination)
If ConversionExists(result) Then
Return result
End If
'Reference conversions
result = ClassifyReferenceConversion(source, destination, varianceCompatibilityClassificationDepth:=0, useSiteInfo:=useSiteInfo)
If ConversionExists(result) Then
Return AddDelegateRelaxationInformationForADelegate(source, destination, result)
End If
'Anonymous Delegate conversions
result = ClassifyAnonymousDelegateConversion(source, destination, useSiteInfo)
If ConversionExists(result) Then
Return result
End If
'Array conversions
result = ClassifyArrayConversion(source, destination, varianceCompatibilityClassificationDepth:=0, useSiteInfo:=useSiteInfo)
If ConversionExists(result) Then
Return result
End If
'Tuple conversions
result = ClassifyTupleConversion(source, destination, useSiteInfo)
If ConversionExists(result) Then
Return result
End If
'Value Type conversions
result = ClassifyValueTypeConversion(source, destination, useSiteInfo)
If ConversionExists(result) Then
Return result
End If
'Nullable Value Type conversions
result = ClassifyNullableConversion(source, destination, useSiteInfo)
If ConversionExists(result) Then
Return result
End If
'String conversions
result = ClassifyStringConversion(source, destination)
If ConversionExists(result) Then
Return result
End If
'Type Parameter conversions
result = ClassifyTypeParameterConversion(source, destination, varianceCompatibilityClassificationDepth:=0, useSiteInfo:=useSiteInfo)
Return AddDelegateRelaxationInformationForADelegate(source, destination, result)
End Function
Private Shared Function AddDelegateRelaxationInformationForADelegate(source As TypeSymbol, destination As TypeSymbol, convKind As ConversionKind) As ConversionKind
Debug.Assert((convKind And ConversionKind.DelegateRelaxationLevelMask) = 0)
' Dev10#703313: for e.g. Func(Of String)->Func(Of Object) we have to record it as DelegateRelaxationLevelWidening.
' for e.g. Func(Of String)->Object we have to record it as DelegateRelaxationLevelWideningToNonLambda
If source.IsDelegateType() Then
convKind = convKind And (Not ConversionKind.DelegateRelaxationLevelMask)
If Not ConversionExists(convKind) Then
Return convKind Or ConversionKind.DelegateRelaxationLevelInvalid
ElseIf IsWideningConversion(convKind) Then
If IsIdentityConversion(convKind) Then
Return convKind
ElseIf Not destination.IsDelegateType() OrElse destination.IsStrictSupertypeOfConcreteDelegate() Then
Return convKind Or ConversionKind.DelegateRelaxationLevelWideningToNonLambda
Else
Return convKind Or ConversionKind.DelegateRelaxationLevelWidening
End If
Else
Debug.Assert(IsNarrowingConversion(convKind))
Return convKind Or ConversionKind.DelegateRelaxationLevelNarrowing
End If
End If
Return convKind
End Function
Private Shared Function ClassifyIdentityConversion(source As TypeSymbol, destination As TypeSymbol) As ConversionKind
'§8.8 Widening Conversions
'Identity/Default conversions
'• From a type to itself.
'• From an anonymous delegate type generated for a lambda method reclassification to any delegate type with an identical signature.
'From a type to itself
If source.IsSameTypeIgnoringAll(destination) Then
Return ConversionKind.Identity
End If
'TODO: From an anonymous delegate type generated for a lambda method reclassification to any delegate type with an identical signature.
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassifyReferenceConversion(
source As TypeSymbol,
destination As TypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
'§8.8 Widening Conversions
'• From a reference type to a base type.
'• From a reference type to an interface type, provided that the type
' implements the interface or a variant compatible interface.
'• From an interface type to Object.
'• From an interface type to a variant compatible interface type.
'• From a delegate type to a variant compatible delegate type.
'§8.9 Narrowing Conversions
'• From a reference type to a more derived type.
'• From a class type to an interface type, provided the class type does not implement
' the interface type or an interface type variant compatible with it.
'• From an interface type to a class type.
'• From an interface type to another interface type, provided there is no inheritance
' relationship between the two types and provided they are not variant compatible.
If source.SpecialType = SpecialType.System_Void OrElse destination.SpecialType = SpecialType.System_Void Then
'CLR has nothing to say about conversions of these things.
Return Nothing 'ConversionKind.NoConversion
End If
Dim srcIsClassType As Boolean
Dim srcIsDelegateType As Boolean
Dim srcIsInterfaceType As Boolean
Dim srcIsArrayType As Boolean
Dim dstIsClassType As Boolean
Dim dstIsDelegateType As Boolean
Dim dstIsInterfaceType As Boolean
Dim dstIsArrayType As Boolean
If Not Conversions.ClassifyAsReferenceType(source, srcIsClassType, srcIsDelegateType, srcIsInterfaceType, srcIsArrayType) OrElse
Not Conversions.ClassifyAsReferenceType(destination, dstIsClassType, dstIsDelegateType, dstIsInterfaceType, dstIsArrayType) Then
Return Nothing 'ConversionKind.NoConversion
End If
If destination.SpecialType = SpecialType.System_Object Then
'From an interface type to Object.
'From a reference type to a base type (shortcut).
Return ConversionKind.WideningReference
End If
If srcIsInterfaceType Then
If dstIsClassType Then
'From an interface type to a class type.
Return ConversionKind.NarrowingReference
ElseIf dstIsArrayType Then
' !!! VB spec doesn't mention this explicitly, but
Dim conv As ConversionKind = ClassifyReferenceConversionFromArrayToAnInterface(destination, source, varianceCompatibilityClassificationDepth, useSiteInfo)
If NoConversion(conv) Then
Return Nothing 'ConversionKind.NoConversion
End If
' Possibly dropping ConversionKind.VarianceConversionAmbiguity because it is not
' the only reason for the narrowing.
Return ConversionKind.NarrowingReference Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
End If
If dstIsInterfaceType Then
Debug.Assert(srcIsInterfaceType OrElse srcIsClassType OrElse srcIsArrayType)
If (srcIsInterfaceType OrElse srcIsClassType) Then
Dim conv As ConversionKind = ToInterfaceConversionClassifier.ClassifyConversionToVariantCompatibleInterface(DirectCast(source, NamedTypeSymbol),
DirectCast(destination, NamedTypeSymbol),
varianceCompatibilityClassificationDepth,
useSiteInfo)
If ConversionExists(conv) Then
'From an interface type to a variant compatible interface type.
'From a reference type to an interface type, provided that the type implements the interface or a variant compatible interface.
Debug.Assert((conv And Not (ConversionKind.Widening Or ConversionKind.Narrowing Or
ConversionKind.InvolvesEnumTypeConversions Or
ConversionKind.VarianceConversionAmbiguity)) = 0)
Return conv Or ConversionKind.Reference
End If
'From a class type to an interface type, provided the class type does not implement
'the interface type or an interface type variant compatible with it.
'From an interface type to another interface type, provided there is no inheritance
'relationship between the two types and provided they are not variant compatible.
Return ConversionKind.NarrowingReference
ElseIf srcIsArrayType Then
' !!! Spec doesn't mention this conversion explicitly.
Return ClassifyReferenceConversionFromArrayToAnInterface(source, destination, varianceCompatibilityClassificationDepth, useSiteInfo)
End If
Else
Debug.Assert(dstIsClassType OrElse dstIsArrayType)
If (srcIsClassType OrElse srcIsArrayType) Then
If dstIsClassType AndAlso IsDerivedFrom(source, destination, useSiteInfo) Then
'From a reference type to a base type.
Return ConversionKind.WideningReference
ElseIf srcIsClassType AndAlso IsDerivedFrom(destination, source, useSiteInfo) Then
'From a reference type to a more derived type.
Return ConversionKind.NarrowingReference
ElseIf srcIsDelegateType AndAlso dstIsDelegateType Then
'From a delegate type to a variant compatible delegate type.
Dim conv As ConversionKind = ClassifyConversionToVariantCompatibleDelegateType(DirectCast(source, NamedTypeSymbol),
DirectCast(destination, NamedTypeSymbol),
varianceCompatibilityClassificationDepth,
useSiteInfo)
If ConversionExists(conv) Then
Debug.Assert((conv And Not (ConversionKind.Widening Or ConversionKind.Narrowing Or
ConversionKind.InvolvesEnumTypeConversions Or
ConversionKind.NarrowingDueToContraVarianceInDelegate)) = 0)
Return conv Or ConversionKind.Reference
ElseIf (conv And ConversionKind.MightSucceedAtRuntime) <> 0 Then
Return ConversionKind.MightSucceedAtRuntime
End If
End If
End If
End If
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassifyReferenceConversionFromArrayToAnInterface(
source As TypeSymbol,
destination As TypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
Debug.Assert(source IsNot Nothing AndAlso Conversions.IsArrayType(source))
Debug.Assert(destination IsNot Nothing AndAlso Conversions.IsInterfaceType(destination))
'Check interfaces implemented by System.Array first.
Dim base = source.BaseTypeWithDefinitionUseSiteDiagnostics(useSiteInfo)
If base IsNot Nothing Then
If Not base.IsErrorType() AndAlso base.TypeKind = TypeKind.Class AndAlso
IsWideningConversion(ClassifyDirectCastConversion(base, destination, useSiteInfo)) Then
'From a reference type to an interface type, provided that the type implements the interface or a variant compatible interface.
Return ConversionKind.WideningReference
End If
End If
Dim array = DirectCast(source, ArrayTypeSymbol)
'For one-dimensional arrays, if the target interface is IList(Of U) or ICollection(Of U) or IEnumerable(Of U),
'look for any conversions that start with array covariance T()->U()
'and then have a single array-generic conversion step U()->IList/ICollection/IEnumerable(Of U)
If Not array.IsSZArray Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim dstUnderlying = DirectCast(destination.OriginalDefinition, NamedTypeSymbol)
If dstUnderlying Is destination OrElse dstUnderlying.Kind = SymbolKind.ErrorType Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim dstUnderlyingSpecial = dstUnderlying.SpecialType
If dstUnderlyingSpecial <> SpecialType.System_Collections_Generic_IList_T AndAlso
dstUnderlyingSpecial <> SpecialType.System_Collections_Generic_ICollection_T AndAlso
dstUnderlyingSpecial <> SpecialType.System_Collections_Generic_IEnumerable_T AndAlso
dstUnderlyingSpecial <> SpecialType.System_Collections_Generic_IReadOnlyList_T AndAlso
dstUnderlyingSpecial <> SpecialType.System_Collections_Generic_IReadOnlyCollection_T Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim dstUnderlyingElement = DirectCast(destination, NamedTypeSymbol).TypeArgumentsWithDefinitionUseSiteDiagnostics(useSiteInfo)(0)
If dstUnderlyingElement.Kind = SymbolKind.ErrorType Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim arrayElement = array.ElementType
If arrayElement.Kind = SymbolKind.ErrorType Then
Return Nothing 'ConversionKind.NoConversion
End If
If arrayElement.IsSameTypeIgnoringAll(dstUnderlyingElement) Then
Return ConversionKind.WideningReference
End If
Dim conv As ConversionKind = ClassifyArrayConversionBasedOnElementTypes(arrayElement, dstUnderlyingElement, varianceCompatibilityClassificationDepth, useSiteInfo)
If IsWideningConversion(conv) Then
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
Return ConversionKind.WideningReference Or (conv And ConversionKind.InvolvesEnumTypeConversions)
ElseIf IsNarrowingConversion(conv) Then
Return ConversionKind.NarrowingReference Or
(conv And (ConversionKind.InvolvesEnumTypeConversions Or ConversionKind.VarianceConversionAmbiguity))
End If
' Dev10 #831390 Closely match Orcas behavior of this function to never return ConversionError.
' Note, because of this we don't need to do anything special about ConversionKind.MightSucceedAtRuntime bit,
' since we are returning narrowing anyway.
Return ConversionKind.NarrowingReference
End Function
Public Shared Function HasWideningDirectCastConversionButNotEnumTypeConversion(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
If source.IsErrorType() OrElse destination.IsErrorType Then
Return source.IsSameTypeIgnoringAll(destination)
End If
Dim conv As ConversionKind = ClassifyDirectCastConversion(source, destination, useSiteInfo)
If Conversions.IsWideningConversion(conv) AndAlso
(conv And ConversionKind.InvolvesEnumTypeConversions) = 0 Then
Return True
End If
Return False
End Function
Private Shared Function ClassifyConversionToVariantCompatibleDelegateType(
source As NamedTypeSymbol,
destination As NamedTypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
Debug.Assert(source IsNot Nothing AndAlso Conversions.IsDelegateType(source))
Debug.Assert(destination IsNot Nothing AndAlso Conversions.IsDelegateType(destination))
Const validBits As ConversionKind = (ConversionKind.Widening Or ConversionKind.Narrowing Or
ConversionKind.InvolvesEnumTypeConversions Or
ConversionKind.VarianceConversionAmbiguity Or
ConversionKind.MightSucceedAtRuntime Or
ConversionKind.NarrowingDueToContraVarianceInDelegate)
Dim forwardConv As ConversionKind = ClassifyImmediateVarianceCompatibility(source, destination, varianceCompatibilityClassificationDepth, useSiteInfo)
Debug.Assert((forwardConv And Not validBits) = 0)
If ConversionExists(forwardConv) Then
Return forwardConv
End If
Dim backwardConv As ConversionKind = ClassifyImmediateVarianceCompatibility(destination, source, varianceCompatibilityClassificationDepth, useSiteInfo)
Debug.Assert((backwardConv And Not validBits) = 0)
If ConversionExists(backwardConv) Then
Return (backwardConv And Not (ConversionKind.Widening Or ConversionKind.NarrowingDueToContraVarianceInDelegate)) Or ConversionKind.Narrowing
End If
Return ((forwardConv Or backwardConv) And ConversionKind.MightSucceedAtRuntime)
End Function
''' <summary>
''' Helper structure to classify conversions from named types to interfaces
''' in accumulating fashion.
''' </summary>
Private Structure ToInterfaceConversionClassifier
Private _conv As ConversionKind
Private _match As NamedTypeSymbol
Public ReadOnly Property Result As ConversionKind
Get
If IsIdentityConversion(_conv) Then
Return ConversionKind.Widening
End If
Debug.Assert(_conv = Nothing OrElse
(_match.HasVariance() AndAlso
(_conv = ConversionKind.Widening OrElse
_conv = ConversionKind.Narrowing OrElse
_conv = (ConversionKind.Widening Or ConversionKind.InvolvesEnumTypeConversions) OrElse
_conv = (ConversionKind.Narrowing Or ConversionKind.InvolvesEnumTypeConversions) OrElse
_conv = (ConversionKind.Narrowing Or ConversionKind.VarianceConversionAmbiguity))))
Return _conv
End Get
End Property
<Conditional("DEBUG")>
Public Sub AssertFoundIdentity()
Debug.Assert(IsIdentityConversion(_conv))
End Sub
Public Shared Function ClassifyConversionToVariantCompatibleInterface(
source As NamedTypeSymbol,
destination As NamedTypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
Dim helper As ToInterfaceConversionClassifier = Nothing
helper.AccumulateConversionClassificationToVariantCompatibleInterface(source, destination, varianceCompatibilityClassificationDepth, useSiteInfo)
Return helper.Result
End Function
''' <summary>
''' Accumulates classification information about conversion to interface.
''' Returns True when classification gets promoted to Identity, this method should not
''' be called after that.
''' </summary>
Public Function AccumulateConversionClassificationToVariantCompatibleInterface(
source As NamedTypeSymbol,
destination As NamedTypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Boolean
Debug.Assert(source IsNot Nothing AndAlso
(Conversions.IsInterfaceType(source) OrElse Conversions.IsClassType(source) OrElse Conversions.IsValueType(source)))
Debug.Assert(destination IsNot Nothing AndAlso Conversions.IsInterfaceType(destination))
Debug.Assert(Not IsIdentityConversion(_conv))
If IsIdentityConversion(_conv) Then
Return True
End If
If Conversions.IsInterfaceType(source) Then
ClassifyInterfaceImmediateVarianceCompatibility(source, destination, varianceCompatibilityClassificationDepth, useSiteInfo)
Debug.Assert(Not IsIdentityConversion(_conv))
End If
For Each [interface] In source.AllInterfacesWithDefinitionUseSiteDiagnostics(useSiteInfo)
If [interface].IsErrorType() Then
Continue For
End If
If ClassifyInterfaceImmediateVarianceCompatibility([interface], destination, varianceCompatibilityClassificationDepth, useSiteInfo) Then
Return True
End If
Next
Return False
End Function
''' <summary>
''' Returns when classification gets promoted to Identity.
''' </summary>
Private Function ClassifyInterfaceImmediateVarianceCompatibility(
source As NamedTypeSymbol,
destination As NamedTypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Boolean
Debug.Assert(Conversions.IsInterfaceType(source) AndAlso Conversions.IsInterfaceType(destination))
Debug.Assert(Not IsIdentityConversion(_conv))
Dim addConv As ConversionKind = ClassifyImmediateVarianceCompatibility(source, destination, varianceCompatibilityClassificationDepth, useSiteInfo)
Debug.Assert((addConv And ConversionKind.NarrowingDueToContraVarianceInDelegate) = 0)
If (addConv And ConversionKind.MightSucceedAtRuntime) <> 0 Then
' Treat conversions that possibly might succeed at runtime as at least narrowing.
addConv = ConversionKind.Narrowing Or (addConv And (ConversionKind.InvolvesEnumTypeConversions Or ConversionKind.VarianceConversionAmbiguity))
End If
Const validNonidentityBits As ConversionKind = (ConversionKind.Widening Or ConversionKind.Narrowing Or
ConversionKind.InvolvesEnumTypeConversions Or
ConversionKind.VarianceConversionAmbiguity)
Debug.Assert(IsIdentityConversion(addConv) OrElse (addConv And Not validNonidentityBits) = 0)
If ConversionExists(addConv) Then
If IsIdentityConversion(addConv) Then
_conv = ConversionKind.Identity
Return True
End If
If _match IsNot Nothing Then
Debug.Assert(ConversionExists(_conv))
If (_conv And ConversionKind.VarianceConversionAmbiguity) <> 0 Then
Debug.Assert(IsNarrowingConversion(_conv))
ElseIf Not _match.IsSameTypeIgnoringAll(source) Then
' ambiguity
_conv = ConversionKind.Narrowing Or ConversionKind.VarianceConversionAmbiguity
Else
Debug.Assert(_conv = (addConv And validNonidentityBits))
End If
Else
_match = source
_conv = (addConv And validNonidentityBits)
End If
End If
Return False
End Function
End Structure
Private Shared Function ClassifyImmediateVarianceCompatibility(
source As NamedTypeSymbol,
destination As NamedTypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
Debug.Assert(Conversions.IsInterfaceType(source) OrElse Conversions.IsDelegateType(source))
Debug.Assert(Conversions.IsInterfaceType(destination) OrElse Conversions.IsDelegateType(destination))
Debug.Assert(Conversions.IsInterfaceType(source) = Conversions.IsInterfaceType(destination))
If Not source.OriginalDefinition.IsSameTypeIgnoringAll(destination.OriginalDefinition) Then
Return Nothing ' Incompatible.
End If
' *****************
' (*) STACK OVERFLOW
' There are some computations for variance that will result in an infinite recursion
' e.g. the conversion C -> N(Of C) given "Interface N(Of In X)" and "Class C : Implements N(Of N(Of C))"
' The CLR spec is recursive on this topic, hence ambiguous, so it's not known whether there
' is a conversion. Theoretically we should detect such cases by maintaining a set of all
' conversion-pairs (SourceType,TargetType) that we've visited so far in our quest to tell whether
' a given conversion is possible: if we revisit a given pair, then we've encountered an ambiguity.
' But maintaining such a set is too onerous. So instead we keep a simple "RecursionCount".
' Once that reaches above a certain arbitrary limit, we'll return "ConversionNarrowing". That's
' our way of saying that the conversion might be allowed by the CLR, or it might not, but we're not
' sure. Note that even though we use an arbitrary limit, we're still typesafe.
Const depthLimit As Integer = 20
If varianceCompatibilityClassificationDepth > depthLimit Then
Return ConversionKind.Narrowing
End If
varianceCompatibilityClassificationDepth += 1
' CLI I $8.7
' Given an arbitrary generic type signature X(Of X1,...,Xn), a value of type
' X(Of U1,...,Un) can be stored in a location of type X(Of T1,...,Tn) when all
' the following hold:
' * For each "in Xi" where Ti is not a value-type or generic parameter, we
' require that a value of type Ti can be stored in a location Ui. [Note: since
' Ti is not a value-type or generic parameter, it must be a reference type; and
' references can only be stored in locations that hold references; so we know that
' Ui is also a reference type.]
' * For each "out Xi" where Ti is not a value-type or generic parameter, we require
' that a value of type Ui can be stored in a location of type Ti. [Note: reference
' locations can only ever hold values that are references, so we know that Ui is
' also a reference type.]
' * For each "Xi" neither in nor out where Ti is not a value-type or generic parameter,
' we require that Ti must be the exact same type as Ui. [Note: therefore Ui is
' also a reference type.]
' * For each "Xi" where Ti is a value-type or generic parameter, we require that
' Ti must be the exact same type as Ui.
'
' e.g. a class that implements IReadOnly(Of Mammal) can be stored in a location
' that holds IReadOnly(Of Animal), by the second bullet point, given IReadOnly(Of Out T),
' since a Mammal can be stored in an Animal location.
' but IReadOnly(Of Car) cannot be stored, since a Car cannot be stored in an Animal location.
Dim conversionExists As Boolean = True
Dim identity As Boolean = True
Dim atMostNarrowingDueToContraVarianceInDelegate As Boolean = False
Dim involvesEnumTypeConversions As ConversionKind = Nothing
Dim varianceConversionAmbiguity As ConversionKind = ConversionKind.VarianceConversionAmbiguity
Dim classifyingInterfaceConversions As Boolean = Conversions.IsInterfaceType(source)
Do
Dim typeParameters As ImmutableArray(Of TypeParameterSymbol) = source.TypeParameters
Dim sourceArguments As ImmutableArray(Of TypeSymbol) = source.TypeArgumentsWithDefinitionUseSiteDiagnostics(useSiteInfo)
Dim destinationArguments As ImmutableArray(Of TypeSymbol) = destination.TypeArgumentsWithDefinitionUseSiteDiagnostics(useSiteInfo)
For i As Integer = 0 To typeParameters.Length - 1
Dim sourceArg As TypeSymbol = sourceArguments(i)
Dim destinationArg As TypeSymbol = destinationArguments(i)
If sourceArg.IsSameTypeIgnoringAll(destinationArg) Then
Continue For
End If
If sourceArg.IsErrorType() OrElse destinationArg.IsErrorType() Then
Return Nothing ' Incompatible.
End If
If sourceArg.IsValueType OrElse destinationArg.IsValueType Then
Return Nothing ' Incompatible.
End If
identity = False
Dim conv As ConversionKind = Nothing
Select Case typeParameters(i).Variance
Case VarianceKind.Out
conv = Classify_Reference_Array_TypeParameterConversion(sourceArg, destinationArg, varianceCompatibilityClassificationDepth, useSiteInfo)
If Not classifyingInterfaceConversions AndAlso IsNarrowingConversion(conv) AndAlso
(conv And ConversionKind.NarrowingDueToContraVarianceInDelegate) <> 0 Then
' Dev10 #820752
atMostNarrowingDueToContraVarianceInDelegate = True
varianceConversionAmbiguity = Nothing
Continue For
End If
Case VarianceKind.In
conv = Classify_Reference_Array_TypeParameterConversion(destinationArg, sourceArg, varianceCompatibilityClassificationDepth, useSiteInfo)
If Not classifyingInterfaceConversions AndAlso Not IsWideningConversion(conv) AndAlso
destinationArg.IsReferenceType AndAlso sourceArg.IsReferenceType Then
' Dev10 #820752 For delegates, treat conversion as narrowing if both type arguments are reference types.
atMostNarrowingDueToContraVarianceInDelegate = True
varianceConversionAmbiguity = Nothing
Continue For
End If
Case Else
Return Nothing ' Incompatible.
End Select
If NoConversion(conv) Then
' Do not give up yet if conversion might succeed at runtime and thus might cause an ambiguity.
If (conv And ConversionKind.MightSucceedAtRuntime) = 0 Then
Return Nothing ' Incompatible.
End If
conversionExists = False
varianceConversionAmbiguity = Nothing
Else
If (conv And ConversionKind.InvolvesEnumTypeConversions) <> 0 Then
involvesEnumTypeConversions = ConversionKind.InvolvesEnumTypeConversions
End If
If IsNarrowingConversion(conv) Then
conversionExists = False
varianceConversionAmbiguity = varianceConversionAmbiguity And conv
Else
Debug.Assert(Not IsIdentityConversion(conv))
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
End If
End If
Next
source = source.ContainingType
destination = destination.ContainingType
Loop While source IsNot Nothing
Debug.Assert((Not atMostNarrowingDueToContraVarianceInDelegate AndAlso involvesEnumTypeConversions = 0 AndAlso conversionExists) OrElse Not identity)
If identity Then
Return ConversionKind.Identity
ElseIf Not conversionExists Then
' Since we didn't return earlier, conversion might succeed at runtime
Return ConversionKind.MightSucceedAtRuntime Or varianceConversionAmbiguity Or involvesEnumTypeConversions
ElseIf atMostNarrowingDueToContraVarianceInDelegate Then
Debug.Assert(varianceConversionAmbiguity = Nothing)
Return ConversionKind.Narrowing Or ConversionKind.NarrowingDueToContraVarianceInDelegate Or involvesEnumTypeConversions
Else
Return ConversionKind.Widening Or involvesEnumTypeConversions
End If
End Function
Private Shared Function ClassifyAsReferenceType(
candidate As TypeSymbol,
ByRef isClassType As Boolean,
ByRef isDelegateType As Boolean,
ByRef isInterfaceType As Boolean,
ByRef isArrayType As Boolean
) As Boolean
Select Case candidate.TypeKind
Case TypeKind.Class,
TypeKind.Module
isClassType = True
isDelegateType = False
isInterfaceType = False
isArrayType = False
Case TypeKind.Delegate
isClassType = True
isDelegateType = True
isInterfaceType = False
isArrayType = False
Case TypeKind.Interface
isClassType = False
isDelegateType = False
isInterfaceType = True
isArrayType = False
Case TypeKind.Array
isClassType = False
isDelegateType = False
isInterfaceType = False
isArrayType = True
Case Else
isClassType = False
isDelegateType = False
isInterfaceType = False
isArrayType = False
Return False
End Select
Return True
End Function
Private Shared Function IsClassType(type As TypeSymbol) As Boolean
Dim typeKind = type.TypeKind
Return typeKind = TypeKind.Class OrElse typeKind = TypeKind.Module OrElse typeKind = TypeKind.Delegate
End Function
Private Shared Function IsValueType(type As TypeSymbol) As Boolean
Dim typeKind = type.TypeKind
Return typeKind = TypeKind.Enum OrElse typeKind = TypeKind.Structure
End Function
Private Shared Function IsDelegateType(type As TypeSymbol) As Boolean
Return type.TypeKind = TypeKind.Delegate
End Function
Private Shared Function IsArrayType(type As TypeSymbol) As Boolean
Return type.TypeKind = TypeKind.Array
End Function
Private Shared Function IsInterfaceType(type As TypeSymbol) As Boolean
Return type.IsInterfaceType()
End Function
''' <summary>
''' Returns true if and only if baseType is a base class of derivedType.
''' </summary>
''' <param name="derivedType">
''' Derived class type.
''' </param>
''' <param name="baseType">
''' Target base class type.
''' </param>
''' <returns></returns>
''' <remarks>
''' </remarks>
Public Shared Function IsDerivedFrom(derivedType As TypeSymbol, baseType As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Debug.Assert(derivedType IsNot Nothing AndAlso
(Conversions.IsClassType(derivedType) OrElse Conversions.IsArrayType(derivedType) OrElse Conversions.IsValueType(derivedType)))
Debug.Assert(baseType IsNot Nothing AndAlso Conversions.IsClassType(baseType))
Return baseType.IsBaseTypeOf(derivedType, useSiteInfo)
End Function
Private Shared Function ClassifyAnonymousDelegateConversion(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
'§8.8 Widening Conversions
'• From an anonymous delegate type generated for a lambda method reclassification to any wider delegate type.
'§8.9 Narrowing Conversions
'• From an anonymous delegate type generated for a lambda method reclassification to any narrower delegate type.
Debug.Assert(Not source.IsSameTypeIgnoringAll(destination))
If source.IsAnonymousType AndAlso source.IsDelegateType() AndAlso destination.IsDelegateType() Then
Dim delegateInvoke As MethodSymbol = DirectCast(destination, NamedTypeSymbol).DelegateInvokeMethod
If delegateInvoke Is Nothing OrElse delegateInvoke.GetUseSiteInfo().DiagnosticInfo IsNot Nothing Then
Return Nothing ' No conversion
End If
Dim methodConversion As MethodConversionKind = ClassifyMethodConversionForLambdaOrAnonymousDelegate(delegateInvoke,
DirectCast(source, NamedTypeSymbol).DelegateInvokeMethod,
useSiteInfo)
If Not IsDelegateRelaxationSupportedFor(methodConversion) Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim additionalFlags As ConversionKind = DetermineDelegateRelaxationLevel(methodConversion)
If IsStubRequiredForMethodConversion(methodConversion) Then
additionalFlags = additionalFlags Or ConversionKind.NeedAStub
End If
' Note, intentional change in behavior from Dev10, using AddressOf semantics to detect narrowing.
' New behavior is more inline with the language spec:
' Section 8.4.2
' A compatible delegate type is any delegate type that can be created using a delegate creation
' expression with the anonymous delegate type's Invoke method as a parameter.
' As a result, "zero argument" relaxation is treated as narrowing conversion. Dev10 treats it as
' widening, but, with Option Strict On, reports a conversion error later.
If Conversions.IsNarrowingMethodConversion(methodConversion, isForAddressOf:=True) Then
Return ConversionKind.AnonymousDelegate Or ConversionKind.Narrowing Or additionalFlags
Else
Return ConversionKind.AnonymousDelegate Or ConversionKind.Widening Or additionalFlags
End If
End If
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassifyArrayConversion(
source As TypeSymbol,
destination As TypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
'§8.8 Widening Conversions
'• From an array type S with an element type SE to an array type T with an element type TE, provided all of the following are true:
' • S and T differ only in element type.
' • Both SE and TE are reference types or are type parameters known to be a reference type.
' • A widening reference, array, or type parameter conversion exists from SE to TE.
'• From an array type S with an enumerated element type SE to an array type T with an element type TE,
' provided all of the following are true:
' • S and T differ only in element type.
' • TE is the underlying type of SE.
'§8.9 Narrowing Conversions
'• From an array type S with an element type SE, to an array type T with an element type TE,
' provided that all of the following are true:
' • S and T differ only in element type.
' • Both SE and TE are reference types or are type parameters not known to be value types.
' • A narrowing reference, array, or type parameter conversion exists from SE to TE.
'• From an array type S with an element type SE to an array type T with an enumerated element type TE,
' provided all of the following are true:
' • S and T differ only in element type.
' • SE is the underlying type of TE.
If Not Conversions.IsArrayType(source) OrElse Not Conversions.IsArrayType(destination) Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim srcArray = DirectCast(source, ArrayTypeSymbol)
Dim dstArray = DirectCast(destination, ArrayTypeSymbol)
If Not srcArray.HasSameShapeAs(dstArray) Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim srcElem = srcArray.ElementType
Dim dstElem = dstArray.ElementType
If srcElem.Kind = SymbolKind.ErrorType OrElse dstElem.Kind = SymbolKind.ErrorType Then
Return Nothing 'ConversionKind.NoConversion
End If
Return ClassifyArrayConversionBasedOnElementTypes(srcElem, dstElem, varianceCompatibilityClassificationDepth, useSiteInfo)
End Function
Public Shared Function ClassifyArrayElementConversion(srcElem As TypeSymbol, dstElem As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
Dim result As ConversionKind
'Identity/Default conversions
result = ClassifyIdentityConversion(srcElem, dstElem)
If ConversionExists(result) Then
Return result
End If
Return ClassifyArrayConversionBasedOnElementTypes(srcElem, dstElem, varianceCompatibilityClassificationDepth:=0, useSiteInfo:=useSiteInfo)
End Function
Friend Shared Function Classify_Reference_Array_TypeParameterConversion(
srcElem As TypeSymbol,
dstElem As TypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
Dim conv = ClassifyReferenceConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, useSiteInfo)
If NoConversion(conv) AndAlso (conv And ConversionKind.MightSucceedAtRuntime) = 0 Then
conv = ClassifyArrayConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, useSiteInfo)
If NoConversion(conv) AndAlso (conv And ConversionKind.MightSucceedAtRuntime) = 0 Then
conv = ClassifyTypeParameterConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, useSiteInfo)
Else
Debug.Assert(NoConversion(ClassifyTypeParameterConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, CompoundUseSiteInfo(Of AssemblySymbol).Discarded)))
End If
Else
Debug.Assert(NoConversion(ClassifyArrayConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, CompoundUseSiteInfo(Of AssemblySymbol).Discarded)))
Debug.Assert(NoConversion(ClassifyTypeParameterConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, CompoundUseSiteInfo(Of AssemblySymbol).Discarded)))
End If
Return conv
End Function
Private Shared Function ClassifyArrayConversionBasedOnElementTypes(
srcElem As TypeSymbol,
dstElem As TypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
'§8.8 Widening Conversions
'• From an array type S with an element type SE to an array type T with an element type TE, provided all of the following are true:
' • S and T differ only in element type.
' • Both SE and TE are reference types or are type parameters known to be a reference type.
' • A widening reference, array, or type parameter conversion exists from SE to TE.
'• From an array type S with an enumerated element type SE to an array type T with an element type TE,
' provided all of the following are true:
' • S and T differ only in element type.
' • TE is the underlying type of SE.
'§8.9 Narrowing Conversions
'• From an array type S with an element type SE, to an array type T with an element type TE,
' provided that all of the following are true:
' • S and T differ only in element type.
' • Both SE and TE are reference types or are type parameters not known to be value types.
' • A narrowing reference, array, or type parameter conversion exists from SE to TE.
'• From an array type S with an element type SE to an array type T with an enumerated element type TE,
' provided all of the following are true:
' • S and T differ only in element type.
' • SE is the underlying type of TE.
'Shouldn't get here for identity conversion
Debug.Assert(Not srcElem.IsSameTypeIgnoringAll(dstElem))
Dim srcElemIsValueType As Boolean = srcElem.IsValueType
Dim dstElemIsValueType As Boolean = dstElem.IsValueType
If Not srcElemIsValueType AndAlso Not dstElemIsValueType Then
Dim conv = Classify_Reference_Array_TypeParameterConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, useSiteInfo)
If IsWideningConversion(conv) Then
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
If srcElem.IsReferenceType AndAlso dstElem.IsReferenceType Then
'Both SE and TE are reference types or are type parameters known to be a reference type.
'A widening reference, array, or type parameter conversion exists from SE to TE.
Return ConversionKind.WideningArray Or (conv And ConversionKind.InvolvesEnumTypeConversions)
ElseIf srcElem.Kind = SymbolKind.TypeParameter AndAlso
dstElem.Kind = SymbolKind.TypeParameter Then
' Important: We have widening conversion between two type parameters.
' This can happen only if srcElem is directly or indirectly constrained by dstElem.
If srcElem.IsReferenceType Then
Debug.Assert(Not dstElem.IsReferenceType)
'srcElem is constrained by dstElem and srcElem is a reference type.
'Therefore, we can infer that dstElem is known to be a reference type,
'assuming there are no conflicting constraints.
Debug.Assert(Not dstElemIsValueType) ' enforced by one of the outer "If"s
'Both SE and TE are reference types or are type parameters known to be a reference type.
'A widening reference, array, or type parameter conversion exists from SE to TE.
Return ConversionKind.WideningArray Or (conv And ConversionKind.InvolvesEnumTypeConversions)
ElseIf dstElem.IsReferenceType Then
'srcElem is constrained by dstElem
'srcElem is not known to be a reference type
'srcElem is not known to be a value type.
' !!! Spec doesn't mention this explicitly, but Dev10 compiler treats this
' !!! as narrowing conversion. BTW, it looks like C# compiler doesn't support this conversion.
Return ConversionKind.NarrowingArray Or (conv And ConversionKind.InvolvesEnumTypeConversions)
Else
'srcElem is constrained by dstElem
'Both are not known to be a reference type,
'both are not known to be a value type.
' !!! Spec doesn't mention this explicitly, but Dev10 compiler treats this
' !!! as narrowing conversion. BTW, it looks like C# compiler doesn't support this conversion.
Return ConversionKind.NarrowingArray Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
ElseIf srcElem.Kind = SymbolKind.TypeParameter OrElse
dstElem.Kind = SymbolKind.TypeParameter Then
' One and only one is a type parameter
' !!! Spec doesn't mention this explicitly, but Dev10 compiler treats this
' !!! as narrowing conversion.
Return ConversionKind.NarrowingArray Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
ElseIf IsNarrowingConversion(conv) Then
Debug.Assert(Not srcElemIsValueType AndAlso Not dstElemIsValueType)
'Both SE and TE are reference types or are type parameters not known to be value types.
'A narrowing reference, array, or type parameter conversion exists from SE to TE.
Return ConversionKind.NarrowingArray Or
(conv And (ConversionKind.InvolvesEnumTypeConversions Or ConversionKind.VarianceConversionAmbiguity))
ElseIf (conv And ConversionKind.MightSucceedAtRuntime) <> 0 Then
' Preserve the fact that conversion might succeed at runtime.
Return ConversionKind.MightSucceedAtRuntime
End If
Return Nothing 'ConversionKind.NoConversion
Else
' At least one of the elements is known to be a value type
Debug.Assert(srcElemIsValueType OrElse dstElemIsValueType)
If srcElemIsValueType Then
If dstElemIsValueType Then
Dim mightSucceedAtRuntime As ConversionKind = Nothing
If srcElem.Kind = SymbolKind.TypeParameter OrElse
dstElem.Kind = SymbolKind.TypeParameter Then
' Must be the same type if there is a conversion.
Dim conv = ClassifyTypeParameterConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, useSiteInfo)
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
If IsWideningConversion(conv) Then
' !!! Spec doesn't mention this explicitly, but Dev10 compiler treats this
' !!! as widening conversion.
Return ConversionKind.WideningArray Or (conv And ConversionKind.InvolvesEnumTypeConversions)
ElseIf IsNarrowingConversion(conv) Then
' !!! Spec doesn't mention this explicitly, but Dev10 compiler treats this
' !!! as narrowing conversion.
Return ConversionKind.NarrowingArray Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
If (conv And ConversionKind.MightSucceedAtRuntime) <> 0 Then
mightSucceedAtRuntime = ConversionKind.MightSucceedAtRuntime
End If
End If
Dim srcValueType As TypeSymbol = srcElem
Dim dstValueType As TypeSymbol = dstElem
If srcElem.Kind = SymbolKind.TypeParameter Then
Dim valueType = GetValueTypeConstraint(srcElem, useSiteInfo)
If valueType IsNot Nothing Then
srcValueType = valueType
End If
End If
If dstElem.Kind = SymbolKind.TypeParameter Then
Dim valueType = GetValueTypeConstraint(dstElem, useSiteInfo)
If valueType IsNot Nothing Then
dstValueType = valueType
End If
End If
Dim srcUnderlying As NamedTypeSymbol = GetNonErrorEnumUnderlyingType(srcValueType)
Dim dstUnderlying As NamedTypeSymbol = GetNonErrorEnumUnderlyingType(dstValueType)
'TODO:
' !!! The following logic is strange, it matches enums to enums and numeric types,
' !!! but it doesn't match numeric types to each other. Looks like an oversight
' !!! in Dev10 compiler. Follow the same logic for now.
If srcUnderlying IsNot Nothing Then
If IsNumericType(srcUnderlying) Then
If dstUnderlying IsNot Nothing Then
If srcUnderlying.Equals(dstUnderlying) Then
' !!! Spec doesn't mention this explicitly, but Dev10 supports narrowing conversion
' !!! between arrays of enums, as long as the underlying type is the same.
Return ConversionKind.NarrowingArray Or ConversionKind.InvolvesEnumTypeConversions
End If
ElseIf srcUnderlying.Equals(dstValueType) Then
'TE is the underlying type of SE.
If dstElem Is dstValueType Then
Return ConversionKind.WideningArray Or ConversionKind.InvolvesEnumTypeConversions
Else
' Dev10 degrades this to narrowing if dstElem is generic parameter
Return ConversionKind.NarrowingArray Or ConversionKind.InvolvesEnumTypeConversions
End If
End If
End If
ElseIf dstUnderlying IsNot Nothing Then
If IsNumericType(dstUnderlying) AndAlso dstUnderlying.Equals(srcValueType) Then
'SE is the underlying type of TE.
Return ConversionKind.NarrowingArray Or ConversionKind.InvolvesEnumTypeConversions
End If
End If
If mightSucceedAtRuntime = Nothing Then
' CLR spec $8.7 says that integral()->integral() is possible so long as they have the same bit size.
' It claims that bool is to be taken as the same size as int8/uint8, so allowing e.g. bool()->uint8().
' That isn't allowed in practice by the current CLR runtime, but since it's in the spec,
' we'll return "ConversionKind.MightSucceedAtRuntime" to mean that it might potentially possibly occur.
' Remember that we're in the business here of "OverestimateNarrowingConversions" after all.
Dim srcSize As Integer = ArrayElementBitSize(srcValueType)
If srcSize > 0 AndAlso srcSize = ArrayElementBitSize(dstValueType) Then
mightSucceedAtRuntime = ConversionKind.MightSucceedAtRuntime
End If
End If
Return mightSucceedAtRuntime
ElseIf dstElem.Kind = SymbolKind.TypeParameter AndAlso
Not dstElem.IsReferenceType Then
If srcElem.Kind = SymbolKind.TypeParameter Then
Dim conv = ClassifyTypeParameterConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, useSiteInfo)
If IsWideningConversion(conv) Then
' !!! Spec doesn't mention this explicitly, but Dev10 compiler treats this
' !!! as narrowing conversion.
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
Return ConversionKind.NarrowingArray Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
Debug.Assert(NoConversion(conv))
If (conv And ConversionKind.MightSucceedAtRuntime) <> 0 Then
Return ConversionKind.MightSucceedAtRuntime
End If
Return Nothing 'ConversionKind.NoConversion
ElseIf ArrayElementBitSize(srcElem) > 0 Then
Return ConversionKind.MightSucceedAtRuntime
End If
End If
ElseIf srcElem.Kind = SymbolKind.TypeParameter AndAlso Not srcElem.IsReferenceType Then
Debug.Assert(dstElemIsValueType)
If dstElem.Kind = SymbolKind.TypeParameter Then
Dim conv = ClassifyTypeParameterConversion(srcElem, dstElem, varianceCompatibilityClassificationDepth, useSiteInfo)
If IsNarrowingConversion(conv) Then
' !!! Spec doesn't mention this explicitly, but Dev10 compiler treats this
' !!! as narrowing conversion.
' Possibly dropping ConversionKind.VarianceConversionAmbiguity because it is not
' the only reason for the narrowing.
Return ConversionKind.NarrowingArray Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
Debug.Assert(NoConversion(conv))
If (conv And ConversionKind.MightSucceedAtRuntime) <> 0 Then
Return ConversionKind.MightSucceedAtRuntime
End If
ElseIf ArrayElementBitSize(dstElem) > 0 Then
Return ConversionKind.MightSucceedAtRuntime
End If
End If
Return Nothing 'ConversionKind.NoConversion
End If
End Function
Private Shared Function ArrayElementBitSize(type As TypeSymbol) As Integer
Select Case type.GetEnumUnderlyingTypeOrSelf().SpecialType
Case SpecialType.System_Byte, SpecialType.System_SByte, SpecialType.System_Boolean
Return 8
Case SpecialType.System_Int16, SpecialType.System_UInt16
Return 16
Case SpecialType.System_Int32, SpecialType.System_UInt32
Return 32
Case SpecialType.System_Int64, SpecialType.System_UInt64
Return 64
Case Else
Return 0 ' Unknown
End Select
End Function
Private Shared Function GetValueTypeConstraint(typeParam As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As TypeSymbol
Dim constraint = DirectCast(typeParam, TypeParameterSymbol).GetNonInterfaceConstraint(useSiteInfo)
If constraint IsNot Nothing AndAlso constraint.IsValueType Then
Return constraint
End If
Return Nothing
End Function
Private Shared Function GetNonErrorEnumUnderlyingType(type As TypeSymbol) As NamedTypeSymbol
If type.TypeKind = TypeKind.Enum Then
Dim underlying = DirectCast(type, NamedTypeSymbol).EnumUnderlyingType
If underlying.Kind <> SymbolKind.ErrorType Then
Return underlying
End If
End If
Return Nothing
End Function
Private Shared Function ClassifyValueTypeConversion(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
'§8.8 Widening Conversions
'• From a value type to a base type.
'• From a value type to an interface type that the type implements.
'§8.9 Narrowing Conversions
'• From a reference type to a more derived value type.
'• From an interface type to a value type, provided the value type implements the interface type.
'System.Void is actually a value type.
If source.SpecialType = SpecialType.System_Void OrElse destination.SpecialType = SpecialType.System_Void Then
'CLR has nothing to say about conversions of these things.
Return Nothing 'ConversionKind.NoConversion
End If
If IsValueType(source) Then
' Disallow boxing of restricted types
If Not source.IsRestrictedType() Then
If destination.SpecialType = SpecialType.System_Object Then
'From a value type to a base type.
Return ConversionKind.WideningValue ' Shortcut
End If
If IsClassType(destination) Then
If IsDerivedFrom(source, destination, useSiteInfo) Then
'From a value type to a base type.
Return ConversionKind.WideningValue
End If
ElseIf IsInterfaceType(destination) Then
Dim conv As ConversionKind = ToInterfaceConversionClassifier.ClassifyConversionToVariantCompatibleInterface(
DirectCast(source, NamedTypeSymbol),
DirectCast(destination, NamedTypeSymbol),
varianceCompatibilityClassificationDepth:=0,
useSiteInfo:=useSiteInfo)
If ConversionExists(conv) Then
'From a value type to an interface type that the type implements.
' !!! Note that the spec doesn't mention anything about variance, but
' !!! it appears to be taken into account by Dev10 compiler.
Debug.Assert((conv And Not (ConversionKind.Widening Or ConversionKind.Narrowing Or
ConversionKind.InvolvesEnumTypeConversions Or
ConversionKind.VarianceConversionAmbiguity)) = 0)
Return conv Or ConversionKind.Value
End If
End If
End If
ElseIf IsValueType(destination) Then
If source.SpecialType = SpecialType.System_Object Then
'From a reference type to a more derived value type.
Return ConversionKind.NarrowingValue ' Shortcut
End If
If IsClassType(source) Then
If IsDerivedFrom(destination, source, useSiteInfo) Then
'From a reference type to a more derived value type.
Return ConversionKind.NarrowingValue
End If
ElseIf IsInterfaceType(source) Then
For Each [interface] In destination.AllInterfacesWithDefinitionUseSiteDiagnostics(useSiteInfo)
If [interface].IsErrorType() Then
Continue For
ElseIf [interface].IsSameTypeIgnoringAll(source) Then
' From an interface type to a value type, provided the value type implements the interface type.
' Note, variance is not taken into consideration here.
Return ConversionKind.NarrowingValue
End If
Next
End If
End If
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassifyNullableConversion(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
'§8.8 Widening Conversions
'• From a type T to the type T?.
'• From a type T? to a type S?, where there is a widening conversion from the type T to the type S.
'• From a type T to a type S?, where there is a widening conversion from the type T to the type S.
'• From a type T? to an interface type that the type T implements.
'§8.9 Narrowing Conversions
'• From a type T? to a type T.
'• From a type T? to a type S?, where there is a narrowing conversion from the type T to the type S.
'• From a type T to a type S?, where there is a narrowing conversion from the type T to the type S.
'• From a type S? to a type T, where there is a conversion from the type S to the type T.
Dim srcIsNullable As Boolean = source.IsNullableType()
Dim dstIsNullable As Boolean = destination.IsNullableType()
If Not srcIsNullable AndAlso Not dstIsNullable Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim srcUnderlying As TypeSymbol = Nothing
Dim dstUnderlying As TypeSymbol = Nothing
If srcIsNullable Then
srcUnderlying = source.GetNullableUnderlyingType()
If srcUnderlying.Kind = SymbolKind.ErrorType OrElse Not srcUnderlying.IsValueType OrElse srcUnderlying.IsNullableType() Then
Return Nothing 'ConversionKind.NoConversion
End If
End If
If dstIsNullable Then
dstUnderlying = destination.GetNullableUnderlyingType()
If dstUnderlying.Kind = SymbolKind.ErrorType OrElse Not dstUnderlying.IsValueType OrElse dstUnderlying.IsNullableType() Then
Return Nothing 'ConversionKind.NoConversion
End If
End If
Const preserveConversionKindFromUnderlyingPredefinedConversion As ConversionKind = ConversionKind.Tuple Or ConversionKind.DelegateRelaxationLevelMask
If srcIsNullable Then
Dim conv As ConversionKind
If dstIsNullable Then
'From a type T? to a type S?
conv = ClassifyPredefinedConversion(srcUnderlying, dstUnderlying, useSiteInfo)
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
Debug.Assert((conv And ConversionKind.DelegateRelaxationLevelMask) = 0 OrElse (conv And ConversionKind.Tuple) <> 0)
If IsWideningConversion(conv) Then
'From a type T? to a type S?, where there is a widening conversion from the type T to the type S.
Return ConversionKind.WideningNullable Or (conv And preserveConversionKindFromUnderlyingPredefinedConversion)
ElseIf IsNarrowingConversion(conv) Then
'From a type T? to a type S?, where there is a narrowing conversion from the type T to the type S.
Return ConversionKind.NarrowingNullable Or (conv And preserveConversionKindFromUnderlyingPredefinedConversion)
End If
ElseIf IsInterfaceType(destination) Then
' !!! Note that the spec doesn't mention anything about variance, but
' !!! it appears to be taken into account by Dev10 compiler.
conv = ClassifyDirectCastConversion(srcUnderlying, destination, useSiteInfo)
Debug.Assert((conv And ConversionKind.Tuple) = 0)
If IsWideningConversion(conv) Then
'From a type T? to an interface type that the type T implements.
Return ConversionKind.WideningNullable
ElseIf IsNarrowingConversion(conv) Then
' !!! Note, spec doesn't mention this conversion.
Return ConversionKind.NarrowingNullable
End If
ElseIf srcUnderlying.IsSameTypeIgnoringAll(destination) Then
'From a type T? to a type T.
Return ConversionKind.NarrowingNullable
Else
conv = ClassifyPredefinedConversion(srcUnderlying, destination, useSiteInfo)
Debug.Assert((conv And ConversionKind.DelegateRelaxationLevelMask) = 0 OrElse (conv And ConversionKind.Tuple) <> 0)
If ConversionExists(conv) Then
'From a type S? to a type T, where there is a conversion from the type S to the type T.
Return ConversionKind.NarrowingNullable Or (conv And preserveConversionKindFromUnderlyingPredefinedConversion)
End If
End If
Else
Debug.Assert(dstIsNullable)
'From a type T to a type S?
If source.IsSameTypeIgnoringAll(dstUnderlying) Then
'From a type T to the type T?.
Return ConversionKind.WideningNullable
End If
Dim conv = ClassifyPredefinedConversion(source, dstUnderlying, useSiteInfo)
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
Debug.Assert((conv And ConversionKind.DelegateRelaxationLevelMask) = 0 OrElse (conv And ConversionKind.Tuple) <> 0)
If IsWideningConversion(conv) Then
'From a type T to a type S?, where there is a widening conversion from the type T to the type S.
Return ConversionKind.WideningNullable Or (conv And preserveConversionKindFromUnderlyingPredefinedConversion)
ElseIf IsNarrowingConversion(conv) Then
'From a type T to a type S?, where there is a narrowing conversion from the type T to the type S.
Return ConversionKind.NarrowingNullable Or (conv And preserveConversionKindFromUnderlyingPredefinedConversion)
End If
End If
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassifyTupleConversion(source As TypeSymbol, destination As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ConversionKind
If Not source.IsTupleType Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim sourceElementTypes = DirectCast(source, TupleTypeSymbol).TupleElementTypes
' check if the type is actually compatible type for a tuple of given cardinality
If Not destination.IsTupleOrCompatibleWithTupleOfCardinality(sourceElementTypes.Length) Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim targetElementTypes As ImmutableArray(Of TypeSymbol) = destination.GetElementTypesOfTupleOrCompatible()
Debug.Assert(sourceElementTypes.Length = targetElementTypes.Length)
' check arguments against flattened list of target element types
Dim result As ConversionKind = ConversionKind.WideningTuple
Dim maxDelegateRelaxationLevel = ConversionKind.DelegateRelaxationLevelNone
For i As Integer = 0 To sourceElementTypes.Length - 1
Dim argumentType = sourceElementTypes(i)
Dim targetType = targetElementTypes(i)
If argumentType.IsErrorType OrElse targetType.IsErrorType Then
Return Nothing 'ConversionKind.NoConversion
End If
Dim elementConversion = ClassifyConversion(argumentType, targetType, useSiteInfo).Key
If NoConversion(elementConversion) Then
Return Nothing 'ConversionKind.NoConversion
End If
Debug.Assert((elementConversion And ConversionKind.InvolvesNarrowingFromNumericConstant) = 0)
Dim elementDelegateRelaxationLevel = elementConversion And ConversionKind.DelegateRelaxationLevelMask
If elementDelegateRelaxationLevel > maxDelegateRelaxationLevel Then
maxDelegateRelaxationLevel = elementDelegateRelaxationLevel
End If
If IsNarrowingConversion(elementConversion) Then
result = ConversionKind.NarrowingTuple
End If
Next
Return result Or maxDelegateRelaxationLevel
End Function
Public Shared Function ClassifyStringConversion(source As TypeSymbol, destination As TypeSymbol) As ConversionKind
'§8.8 Widening Conversions
'• From Char() to String.
'§8.9 Narrowing Conversions
'• From String to Char().
Dim shouldBeArray As TypeSymbol
If source.SpecialType = SpecialType.System_String Then
shouldBeArray = destination
ElseIf destination.SpecialType = SpecialType.System_String Then
shouldBeArray = source
Else
Return Nothing 'ConversionKind.NoConversion
End If
If shouldBeArray.Kind = SymbolKind.ArrayType Then
Dim array = DirectCast(shouldBeArray, ArrayTypeSymbol)
If array.IsSZArray AndAlso array.ElementType.SpecialType = SpecialType.System_Char Then
If array Is source Then
Return ConversionKind.WideningString
Else
Return ConversionKind.NarrowingString
End If
End If
End If
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassifyTypeParameterConversion(
source As TypeSymbol,
destination As TypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
'§8.8 Widening Conversions
'• From a type parameter to Object.
'• From a type parameter to an interface type constraint or any interface variant compatible with an interface type constraint.
'• From a type parameter to an interface implemented by a class constraint.
'• From a type parameter to an interface variant compatible with an interface implemented by a class constraint.
'• From a type parameter to a class constraint, or a base type of the class constraint.
'• From a type parameter T to a type parameter constraint TX, or anything TX has a widening conversion to.
'§8.9 Narrowing Conversions
'• From Object to a type parameter.
'• From a type parameter to an interface type, provided the type parameter is not constrained
' to that interface or constrained to a class that implements that interface.
'• From an interface type to a type parameter.
' !!! The following two narrowing conversions aren't actually honored by VB/C# Dev10 compiler,
' !!! I am not going to honor them either.
'•- From a type parameter to a derived type of a class constraint.
'•- From a type parameter T to anything a type parameter constraint TX has a narrowing conversion to.
' !!! The following two narrowing conversions are not mentioned in the spec, but are honored in Dev10:
'• From a class constraint, or a base type of the class constraint to a type parameter.
'• From a type parameter constraint TX to a type parameter T, or from anything that has narrowing conversion to TX.
Dim conv As ConversionKind
If source.Kind = SymbolKind.TypeParameter Then
conv = ClassifyConversionFromTypeParameter(DirectCast(source, TypeParameterSymbol), destination, varianceCompatibilityClassificationDepth, useSiteInfo)
If ConversionExists(conv) Then
Return conv
End If
End If
If destination.Kind = SymbolKind.TypeParameter Then
conv = ClassifyConversionToTypeParameter(source, DirectCast(destination, TypeParameterSymbol), varianceCompatibilityClassificationDepth, useSiteInfo)
If ConversionExists(conv) Then
Debug.Assert(IsNarrowingConversion(conv)) ' We are relying on this while classifying conversions from type parameter to avoid need for recursion.
Return conv
End If
End If
If source.Kind = SymbolKind.TypeParameter OrElse destination.Kind = SymbolKind.TypeParameter Then
Return ConversionKind.MightSucceedAtRuntime
End If
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassifyConversionFromTypeParameter(
typeParameter As TypeParameterSymbol,
destination As TypeSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
If destination.SpecialType = SpecialType.System_Object Then
'From a type parameter to Object.
Return ConversionKind.WideningTypeParameter
End If
Dim queue As ArrayBuilder(Of TypeParameterSymbol) = Nothing
Dim result As ConversionKind = ClassifyConversionFromTypeParameter(typeParameter, destination, queue, varianceCompatibilityClassificationDepth, useSiteInfo)
If queue IsNot Nothing Then
queue.Free()
End If
Return result
End Function
Private Shared Function ClassifyConversionFromTypeParameter(
typeParameter As TypeParameterSymbol,
destination As TypeSymbol,
<[In], Out> ByRef queue As ArrayBuilder(Of TypeParameterSymbol),
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
Dim queueIndex As Integer = 0
Dim checkedValueTypeConstraint As Boolean = False
Dim dstIsClassType As Boolean
Dim dstIsDelegateType As Boolean
Dim dstIsInterfaceType As Boolean
Dim dstIsArrayType As Boolean
Dim convToInterface As ToInterfaceConversionClassifier = Nothing
Dim destinationInterface As NamedTypeSymbol = Nothing
ClassifyAsReferenceType(destination, dstIsClassType, dstIsDelegateType, dstIsInterfaceType, dstIsArrayType)
If dstIsInterfaceType Then
destinationInterface = DirectCast(destination, NamedTypeSymbol)
End If
Do
If Not checkedValueTypeConstraint AndAlso typeParameter.HasValueTypeConstraint Then
If destination.SpecialType = SpecialType.System_ValueType Then
' !!! Not mentioned explicitly in the spec.
Return ConversionKind.WideningTypeParameter
End If
If dstIsInterfaceType Then
Dim valueType = typeParameter.ContainingAssembly.GetSpecialType(SpecialType.System_ValueType)
If valueType.Kind <> SymbolKind.ErrorType Then
' !!! Not mentioned explicitly in the spec.
If convToInterface.AccumulateConversionClassificationToVariantCompatibleInterface(valueType, destinationInterface,
varianceCompatibilityClassificationDepth,
useSiteInfo) Then
convToInterface.AssertFoundIdentity()
Return ConversionKind.WideningTypeParameter
End If
End If
End If
checkedValueTypeConstraint = True
End If
' Iterate over the constraints
For Each constraint As TypeSymbol In typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(useSiteInfo)
If constraint.Kind = SymbolKind.ErrorType Then
Continue For
End If
If constraint.IsSameTypeIgnoringAll(destination) Then
'From a type parameter to an interface type constraint
'From a type parameter to a class constraint
'From a type parameter T to a type parameter constraint TX
Return ConversionKind.WideningTypeParameter
ElseIf constraint.TypeKind = TypeKind.Enum AndAlso
DirectCast(constraint, NamedTypeSymbol).EnumUnderlyingType.IsSameTypeIgnoringAll(destination) Then
' !!! Spec doesn't mention this, but Dev10 allows conversion
' !!! to the underlying type of the enum
Return ConversionKind.WideningTypeParameter Or ConversionKind.InvolvesEnumTypeConversions
End If
Dim constraintIsClassType As Boolean
Dim constraintIsDelegateType As Boolean
Dim constraintIsInterfaceType As Boolean
Dim constraintIsArrayType As Boolean
Dim constraintIsValueType As Boolean = False
If Not ClassifyAsReferenceType(constraint, constraintIsClassType, constraintIsDelegateType, constraintIsInterfaceType, constraintIsArrayType) Then
constraintIsValueType = IsValueType(constraint)
End If
If dstIsInterfaceType Then
' Conversions to an interface
If (constraintIsClassType OrElse constraintIsInterfaceType OrElse constraintIsValueType) Then
If convToInterface.AccumulateConversionClassificationToVariantCompatibleInterface(DirectCast(constraint, NamedTypeSymbol),
destinationInterface,
varianceCompatibilityClassificationDepth,
useSiteInfo) Then
convToInterface.AssertFoundIdentity()
'From a type parameter to any interface variant compatible with an interface type constraint.
'From a type parameter to an interface implemented by a class constraint.
'From a type parameter to an interface variant compatible with an interface implemented by a class constraint.
' !!! Spec doesn't explicitly mention interfaces implemented by interface constraints, but Dev10 compiler takes them
' !!! into consideration.
Return ConversionKind.WideningTypeParameter
End If
ElseIf constraintIsArrayType Then
Dim conv As ConversionKind = ClassifyReferenceConversionFromArrayToAnInterface(constraint, destination, varianceCompatibilityClassificationDepth, useSiteInfo)
If IsWideningConversion(conv) Then
'From a type parameter to an interface implemented by a class constraint.
'From a type parameter to an interface variant compatible with an interface implemented by a class constraint.
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
Return ConversionKind.WideningTypeParameter Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
End If
ElseIf dstIsClassType Then
If (constraintIsClassType OrElse constraintIsValueType OrElse constraintIsArrayType) AndAlso
IsDerivedFrom(constraint, destination, useSiteInfo) Then
'From a type parameter to a base type of the class constraint.
Return ConversionKind.WideningTypeParameter
End If
ElseIf dstIsArrayType Then
If constraintIsArrayType Then
Dim conv As ConversionKind = ClassifyArrayConversion(constraint, destination, varianceCompatibilityClassificationDepth, useSiteInfo)
If IsWideningConversion(conv) Then
' !!! Spec doesn't explicitly mention array covariance, but Dev10 compiler takes them
' !!! into consideration.
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
Return ConversionKind.WideningTypeParameter Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
' We don't need to do anything special about ConversionKind.MightSucceedAtRuntime bit here
' because the caller of this function, ClassifyTypeParameterConversion, returns
' ConversionKind.MightSucceedAtRuntime on its own in case of NoConversion.
End If
' Unit test includes scenario for narrowing conversion between arrays. It produces expected result.
End If
If constraint.Kind = SymbolKind.TypeParameter Then
If queue Is Nothing Then
queue = ArrayBuilder(Of TypeParameterSymbol).GetInstance()
End If
'From a type parameter T to anything type parameter constraint TX has a widening conversion to.
queue.Add(DirectCast(constraint, TypeParameterSymbol))
End If
Next
If queue IsNot Nothing Then
If queueIndex < queue.Count Then
typeParameter = queue(queueIndex)
queueIndex += 1
Continue Do
End If
End If
Exit Do
Loop
If dstIsInterfaceType Then
Dim conv As ConversionKind = convToInterface.Result
If ConversionExists(conv) Then
'From a type parameter to any interface variant compatible with an interface type constraint.
'From a type parameter to an interface implemented by a class constraint.
'From a type parameter to an interface variant compatible with an interface implemented by a class constraint.
' !!! Spec doesn't explicitly mention interfaces implemented by interface constraints, but Dev10 compiler takes them
' !!! into consideration.
Debug.Assert((conv And Not (ConversionKind.Widening Or ConversionKind.Narrowing Or
ConversionKind.InvolvesEnumTypeConversions Or
ConversionKind.VarianceConversionAmbiguity)) = 0)
Return ConversionKind.TypeParameter Or conv
End If
'From a type parameter to an interface type, provided the type parameter is not constrained
'to that interface or constrained to a class that implements that interface.
Return ConversionKind.NarrowingTypeParameter
End If
Return Nothing 'ConversionKind.NoConversion
End Function
Private Shared Function ClassifyConversionToTypeParameter(
source As TypeSymbol,
typeParameter As TypeParameterSymbol,
varianceCompatibilityClassificationDepth As Integer,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
If source.SpecialType = SpecialType.System_Object Then
'From Object to a type parameter.
Return ConversionKind.NarrowingTypeParameter
End If
If typeParameter.HasValueTypeConstraint Then
If source.SpecialType = SpecialType.System_ValueType Then
' !!! Not mentioned explicitly in the spec.
Return ConversionKind.NarrowingTypeParameter
End If
If IsClassType(source) Then
Dim valueType = typeParameter.ContainingAssembly.GetSpecialType(SpecialType.System_ValueType)
If valueType.Kind <> SymbolKind.ErrorType AndAlso IsDerivedFrom(valueType, source, useSiteInfo) Then
' !!! Not mentioned explicitly in the spec.
Return ConversionKind.NarrowingTypeParameter
End If
End If
End If
Dim srcIsClassType As Boolean
Dim srcIsDelegateType As Boolean
Dim srcIsInterfaceType As Boolean
Dim srcIsArrayType As Boolean
ClassifyAsReferenceType(source, srcIsClassType, srcIsDelegateType, srcIsInterfaceType, srcIsArrayType)
If srcIsInterfaceType Then
'From an interface type to a type parameter.
Return ConversionKind.NarrowingTypeParameter
Else
' Iterate over constraints
For Each constraint As TypeSymbol In typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(useSiteInfo)
If constraint.Kind = SymbolKind.ErrorType Then
Continue For
End If
If constraint.IsSameTypeIgnoringAll(source) Then
'From a class constraint to a type parameter.
'From a type parameter constraint TX to a type parameter T
Return ConversionKind.NarrowingTypeParameter
ElseIf constraint.TypeKind = TypeKind.Enum AndAlso
DirectCast(constraint, NamedTypeSymbol).EnumUnderlyingType.IsSameTypeIgnoringAll(source) Then
' !!! Spec doesn't mention this, but Dev10 allows conversion
' !!! from the underlying type of the enum
Return ConversionKind.NarrowingTypeParameter Or ConversionKind.InvolvesEnumTypeConversions
End If
Dim constraintIsClassType As Boolean
Dim constraintIsDelegateType As Boolean
Dim constraintIsInterfaceType As Boolean
Dim constraintIsArrayType As Boolean
Dim constraintIsValueType As Boolean = False
If Not ClassifyAsReferenceType(constraint, constraintIsClassType, constraintIsDelegateType, constraintIsInterfaceType, constraintIsArrayType) Then
constraintIsValueType = IsValueType(constraint)
End If
If (constraintIsClassType OrElse constraintIsValueType OrElse constraintIsArrayType) Then
If srcIsClassType Then
If IsDerivedFrom(constraint, source, useSiteInfo) Then
'From a base type of the class constraint to a type parameter.
Return ConversionKind.NarrowingTypeParameter
End If
ElseIf srcIsArrayType Then
If constraintIsArrayType Then
Dim conv = ClassifyArrayConversion(constraint, source, varianceCompatibilityClassificationDepth, useSiteInfo)
If IsWideningConversion(conv) Then
' !!! Spec doesn't explicitly mention array covariance, but Dev10 compiler takes them
' !!! into consideration.
Debug.Assert((conv And ConversionKind.VarianceConversionAmbiguity) = 0)
Return ConversionKind.NarrowingTypeParameter Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
' We don't need to do anything special about ConversionKind.MightSucceedAtRuntime bit here
' because the caller of this function, ClassifyTypeParameterConversion, returns
' ConversionKind.MightSucceedAtRuntime on its own in case of NoConversion.
End If
' Unit test includes scenario for narrowing conversion between arrays. It produces expected result.
End If
ElseIf constraint.Kind = SymbolKind.TypeParameter Then
Dim conv As ConversionKind = ClassifyTypeParameterConversion(source, constraint, varianceCompatibilityClassificationDepth, useSiteInfo)
If IsNarrowingConversion(conv) Then
'From anything that has narrowing conversion to a type parameter constraint TX.
' Possibly dropping ConversionKind.VarianceConversionAmbiguity because it is not
' the only reason for the narrowing.
Return ConversionKind.NarrowingTypeParameter Or (conv And ConversionKind.InvolvesEnumTypeConversions)
End If
' We don't need to do anything special about ConversionKind.MightSucceedAtRuntime bit here
' because the caller of this function, ClassifyTypeParameterConversion, returns
' ConversionKind.MightSucceedAtRuntime on its own in case of NoConversion.
End If
Next
End If
Return Nothing 'ConversionKind.NoConversion
End Function
''' <summary>
''' Calculate MethodConversionKind based on required return type conversion.
'''
''' TODO: It looks like Dev10 MethodConversionKinds for return are badly named because
''' they appear to give classification in the direction opposite to the data
''' flow. This is very confusing. However, I am not going to rename them just yet.
''' Will do this when all parts are ported and working together, otherwise it will
''' be very hard to port the rest of the feature.
'''
''' We are trying to classify conversion between methods
''' ConvertFrom(...) As returnTypeOfConvertFromMethod -> ConvertTo(...) As returnTypeOfConvertToMethod
'''
''' The relaxation stub would look like:
''' Stub(...) As returnTypeOfConvertToMethod
''' Return ConvertFrom(...)
''' End ...
''' </summary>
Public Shared Function ClassifyMethodConversionBasedOnReturn(
returnTypeOfConvertFromMethod As TypeSymbol,
convertFromMethodIsByRef As Boolean,
returnTypeOfConvertToMethod As TypeSymbol,
convertToMethodIsByRef As Boolean,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As MethodConversionKind
If convertToMethodIsByRef <> convertFromMethodIsByRef Then
Return MethodConversionKind.Error_ByRefByValMismatch
End If
Return ClassifyMethodConversionBasedOnReturnType(returnTypeOfConvertFromMethod, returnTypeOfConvertToMethod, convertFromMethodIsByRef, useSiteInfo)
End Function
Public Shared Function ClassifyMethodConversionBasedOnReturnType(
returnTypeOfConvertFromMethod As TypeSymbol,
returnTypeOfConvertToMethod As TypeSymbol,
isRefReturning As Boolean,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As MethodConversionKind
Debug.Assert(returnTypeOfConvertFromMethod IsNot Nothing)
Debug.Assert(returnTypeOfConvertToMethod IsNot Nothing)
If returnTypeOfConvertToMethod.IsVoidType() Then
If returnTypeOfConvertFromMethod.IsVoidType() Then
Return MethodConversionKind.Identity
Else
Return MethodConversionKind.ReturnValueIsDropped
End If
ElseIf returnTypeOfConvertFromMethod.IsVoidType() Then
Return MethodConversionKind.Error_SubToFunction
End If
' Note: this check is done after the void check to still support
' comparison of two subs without error messages.
' this can happen if e.g. references are missing
If returnTypeOfConvertFromMethod.IsErrorType OrElse returnTypeOfConvertToMethod.IsErrorType Then
If returnTypeOfConvertFromMethod Is returnTypeOfConvertToMethod AndAlso
returnTypeOfConvertFromMethod Is LambdaSymbol.ReturnTypeVoidReplacement Then
Return MethodConversionKind.Identity
End If
Return MethodConversionKind.Error_Unspecified
End If
Dim typeConversion As ConversionKind = ClassifyConversion(returnTypeOfConvertFromMethod, returnTypeOfConvertToMethod, useSiteInfo).Key
If isRefReturning AndAlso Not IsIdentityConversion(typeConversion) Then
Return MethodConversionKind.Error_ReturnTypeMismatch
End If
Dim result As MethodConversionKind
If IsNarrowingConversion(typeConversion) Then
result = MethodConversionKind.ReturnIsWidening
ElseIf IsWideningConversion(typeConversion) Then
If IsIdentityConversion(typeConversion) Then
result = MethodConversionKind.Identity
Else
' For return type, CLR will not relax on value types, only reference types
' so treat these as relaxations that needs a stub.
If Not (returnTypeOfConvertFromMethod.IsReferenceType AndAlso returnTypeOfConvertToMethod.IsReferenceType) OrElse
(typeConversion And ConversionKind.UserDefined) <> 0 Then
result = MethodConversionKind.ReturnIsIsVbOrBoxNarrowing
Else
Dim clrTypeConversion = ClassifyDirectCastConversion(returnTypeOfConvertFromMethod, returnTypeOfConvertToMethod, useSiteInfo)
If IsWideningConversion(clrTypeConversion) Then
result = MethodConversionKind.ReturnIsClrNarrowing
Else
result = MethodConversionKind.ReturnIsIsVbOrBoxNarrowing
End If
End If
End If
Else
result = MethodConversionKind.Error_ReturnTypeMismatch
End If
Return result
End Function
''' <summary>
''' Returns the methods conversions for the given conversion kind
'''
''' We are trying to classify conversion between methods arguments
''' delegateInvoke(parameterConvertFrom) -> targetMethod(parameterConvertTo)
'''
''' The relaxation stub would look like (stub has same signature as delegate invoke):
''' Stub(parameterConvertFrom)
''' return targetMethod(parameterConvertTo)
''' End Method
''' </summary>
''' <param name="conversion">The conversion.</param>
''' <param name="delegateParameterType">The delegate parameter type.</param>
Public Shared Function ClassifyMethodConversionBasedOnArgumentConversion(
conversion As ConversionKind,
delegateParameterType As TypeSymbol
) As MethodConversionKind
If Conversions.NoConversion(conversion) Then
Return MethodConversionKind.Error_OverloadResolution
ElseIf Conversions.IsNarrowingConversion(conversion) Then
Return MethodConversionKind.OneArgumentIsNarrowing
ElseIf Not Conversions.IsIdentityConversion(conversion) Then
Debug.Assert(Conversions.IsWideningConversion(conversion))
If Conversions.IsCLRPredefinedConversion(conversion) AndAlso
delegateParameterType.IsReferenceType Then
Return MethodConversionKind.OneArgumentIsClrWidening
Else
Return MethodConversionKind.OneArgumentIsVbOrBoxWidening
End If
End If
Return MethodConversionKind.Identity
End Function
Public Shared Function ClassifyMethodConversionForLambdaOrAnonymousDelegate(
toMethod As MethodSymbol,
lambdaOrDelegateInvokeSymbol As MethodSymbol,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As MethodConversionKind
Return ClassifyMethodConversionForLambdaOrAnonymousDelegate(New UnboundLambda.TargetSignature(toMethod), lambdaOrDelegateInvokeSymbol, useSiteInfo)
End Function
Public Shared Function ClassifyMethodConversionForLambdaOrAnonymousDelegate(
toMethodSignature As UnboundLambda.TargetSignature,
lambdaOrDelegateInvokeSymbol As MethodSymbol,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As MethodConversionKind
' determine conversions based on return type
Dim methodConversions = Conversions.ClassifyMethodConversionBasedOnReturn(lambdaOrDelegateInvokeSymbol.ReturnType, lambdaOrDelegateInvokeSymbol.ReturnsByRef,
toMethodSignature.ReturnType, toMethodSignature.ReturnsByRef, useSiteInfo)
' determine conversions based on arguments
methodConversions = methodConversions Or ClassifyMethodConversionForLambdaOrAnonymousDelegateBasedOnParameters(toMethodSignature, lambdaOrDelegateInvokeSymbol.Parameters, useSiteInfo)
Debug.Assert(Not lambdaOrDelegateInvokeSymbol.ReturnsByRef) ' No interaction of ByRef return with other relaxations
Return methodConversions
End Function
Public Shared Function ClassifyMethodConversionForEventRaise(
toDelegateInvokeMethod As MethodSymbol,
parameters As ImmutableArray(Of ParameterSymbol),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As MethodConversionKind
Debug.Assert(toDelegateInvokeMethod.MethodKind = MethodKind.DelegateInvoke)
Return ClassifyMethodConversionForLambdaOrAnonymousDelegateBasedOnParameters(New UnboundLambda.TargetSignature(toDelegateInvokeMethod), parameters, useSiteInfo)
End Function
Private Shared Function ClassifyMethodConversionForLambdaOrAnonymousDelegateBasedOnParameters(
toMethodSignature As UnboundLambda.TargetSignature,
parameters As ImmutableArray(Of ParameterSymbol),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As MethodConversionKind
' TODO: Take custom modifiers into account, if needed.
Dim methodConversions As MethodConversionKind
' determine conversions based on arguments
If parameters.Length = 0 AndAlso toMethodSignature.ParameterTypes.Length > 0 Then
' special flag for ignoring all arguments (zero argument relaxation)
methodConversions = methodConversions Or MethodConversionKind.AllArgumentsIgnored
ElseIf parameters.Length <> toMethodSignature.ParameterTypes.Length Then
methodConversions = methodConversions Or MethodConversionKind.Error_OverloadResolution
Else
For parameterIndex As Integer = 0 To parameters.Length - 1
' Check ByRef
If toMethodSignature.ParameterIsByRef(parameterIndex) <> parameters(parameterIndex).IsByRef Then
methodConversions = methodConversions Or MethodConversionKind.Error_ByRefByValMismatch
End If
' Check conversion
Dim toParameterType As TypeSymbol = toMethodSignature.ParameterTypes(parameterIndex)
Dim lambdaParameterType As TypeSymbol = parameters(parameterIndex).Type
If Not toParameterType.IsErrorType() AndAlso Not lambdaParameterType.IsErrorType() Then
methodConversions = methodConversions Or
Conversions.ClassifyMethodConversionBasedOnArgumentConversion(
Conversions.ClassifyConversion(toParameterType, lambdaParameterType, useSiteInfo).Key,
toParameterType)
' Check copy back conversion.
If toMethodSignature.ParameterIsByRef(parameterIndex) Then
methodConversions = methodConversions Or
Conversions.ClassifyMethodConversionBasedOnArgumentConversion(
Conversions.ClassifyConversion(lambdaParameterType, toParameterType, useSiteInfo).Key,
lambdaParameterType)
End If
End If
Next
End If
Return methodConversions
End Function
''' <summary>
''' Will set only bits used for delegate relaxation level.
''' </summary>
Public Shared Function DetermineDelegateRelaxationLevelForLambdaReturn(
expressionOpt As BoundExpression,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As ConversionKind
If expressionOpt Is Nothing OrElse expressionOpt.Kind <> BoundKind.Conversion OrElse expressionOpt.HasErrors Then
Return ConversionKind.DelegateRelaxationLevelNone
End If
Dim conversion = DirectCast(expressionOpt, BoundConversion)
If conversion.ExplicitCastInCode Then
Return ConversionKind.DelegateRelaxationLevelNone
End If
' It is tempting to use ConversionKind from this node, but this would produce incorrect result in some cases
' because conversion from an expression to a type and from a type to a type can have different kind.
' The node captures the former, but relaxation classification should use latter.
Dim methodConversion As MethodConversionKind
Dim operandType As TypeSymbol = conversion.Operand.Type
If operandType Is Nothing Then
Debug.Assert(conversion.Operand.IsNothingLiteral() OrElse conversion.Operand.Kind = BoundKind.Lambda)
methodConversion = MethodConversionKind.Identity
Else
methodConversion = ClassifyMethodConversionBasedOnReturnType(operandType, conversion.Type, isRefReturning:=False, useSiteInfo:=useSiteInfo)
End If
Return DetermineDelegateRelaxationLevel(methodConversion)
End Function
''' <summary>
''' Determine the relaxation level of a given conversion. This will be used by
''' overload resolution in case of conflict. This is to prevent applications that compiled in VB8
''' to fail in VB9 because there are more matches. And the same for flipping strict On to Off.
'''
''' Will set only bits used for delegate relaxation level.
''' </summary>
Public Shared Function DetermineDelegateRelaxationLevel(
methodConversion As MethodConversionKind
) As ConversionKind
Dim result As ConversionKind
If methodConversion = MethodConversionKind.Identity Then
result = ConversionKind.DelegateRelaxationLevelNone
ElseIf Not IsDelegateRelaxationSupportedFor(methodConversion) Then
result = ConversionKind.DelegateRelaxationLevelInvalid
ElseIf (methodConversion And (MethodConversionKind.OneArgumentIsNarrowing Or MethodConversionKind.ReturnIsWidening)) <> 0 Then
result = ConversionKind.DelegateRelaxationLevelNarrowing
ElseIf (methodConversion And (MethodConversionKind.ReturnValueIsDropped Or MethodConversionKind.AllArgumentsIgnored)) = 0 Then
result = ConversionKind.DelegateRelaxationLevelWidening
Else
result = ConversionKind.DelegateRelaxationLevelWideningDropReturnOrArgs
End If
Return result
End Function
Public Shared Function IsDelegateRelaxationSupportedFor(methodConversion As MethodConversionKind) As Boolean
Return (methodConversion And MethodConversionKind.AllErrorReasons) = 0
End Function
''' <summary>
''' Determines whether a stub needed for the delegate creations conversion based on the given method conversions.
''' </summary>
''' <param name="methodConversions">The method conversions.</param><returns>
''' <c>true</c> if a stub needed for conversion; otherwise, <c>false</c>.
''' </returns>
Public Shared Function IsStubRequiredForMethodConversion(methodConversions As MethodConversionKind) As Boolean
Const methodConversionsRequiringStubs = (MethodConversionKind.OneArgumentIsNarrowing Or
MethodConversionKind.OneArgumentIsVbOrBoxWidening Or
MethodConversionKind.ReturnIsWidening Or
MethodConversionKind.ReturnIsIsVbOrBoxNarrowing Or
MethodConversionKind.ReturnValueIsDropped Or
MethodConversionKind.AllArgumentsIgnored Or
MethodConversionKind.ExcessOptionalArgumentsOnTarget)
Return (methodConversions And methodConversionsRequiringStubs) <> 0 AndAlso
(methodConversions And MethodConversionKind.AllErrorReasons) = 0
End Function
''' <summary>
''' Tells whether the method conversion is considered to be narrowing or not.
''' </summary>
Public Shared Function IsNarrowingMethodConversion(
methodConversion As MethodConversionKind,
isForAddressOf As Boolean
) As Boolean
Dim checkForBits As MethodConversionKind
If isForAddressOf Then
checkForBits = MethodConversionKind.OneArgumentIsNarrowing Or MethodConversionKind.ReturnIsWidening Or MethodConversionKind.AllArgumentsIgnored
Else
checkForBits = MethodConversionKind.OneArgumentIsNarrowing Or MethodConversionKind.ReturnIsWidening
End If
Return (methodConversion And checkForBits) <> 0
End Function
Public Shared Function InvertConversionRequirement(restriction As RequiredConversion) As RequiredConversion
Debug.Assert(RequiredConversion.Count = 8, "If you've updated the type argument inference restrictions, then please also update InvertConversionRequirement()")
' [reverse chain] [None] < AnyReverse < ReverseReference < Identity
' [middle chain] None < [Any,AnyReverse] < AnyConversionAndReverse < Identity
' [forward chain] [None] < Any < ArrayElement < Reference < Identity
' from reverse chain to forward chain:
If restriction = RequiredConversion.AnyReverse Then
Return RequiredConversion.Any
ElseIf restriction = RequiredConversion.ReverseReference Then
Return RequiredConversion.Reference
End If
' from forward chain to reverse chain:
If restriction = RequiredConversion.Any Then
Return RequiredConversion.AnyReverse
ElseIf restriction = RequiredConversion.ArrayElement Then
Debug.Assert(False, "unexpected: ArrayElementConversion restriction has no reverse")
Return RequiredConversion.ReverseReference
ElseIf restriction = RequiredConversion.Reference Then
Return RequiredConversion.ReverseReference
End If
' otherwise we're either in the middle chain, or identity
Return restriction
End Function
' Strengthens the restriction to at least ReferenceRestriction or ReverseReferenceRestriction
' Note: AnyConversionAndReverse strengthens to Identity
Public Shared Function StrengthenConversionRequirementToReference(restriction As RequiredConversion) As RequiredConversion
Debug.Assert(RequiredConversion.Count = 8, "If you've updated the type argument inference restrictions, then please also update StrengthenConversionRequirementToReference()")
' [reverse chain] [None] < AnyReverse < ReverseReference < Identity
' [middle chain] None < [Any,AnyReverse] < AnyConversionAndReverse < Identity
' [forward chain] [None] < Any < ArrayElement < Reference < Identity
If restriction = RequiredConversion.AnyReverse Then
Return RequiredConversion.ReverseReference
ElseIf restriction = RequiredConversion.Any OrElse restriction = RequiredConversion.ArrayElement Then
Return RequiredConversion.Reference
ElseIf restriction = RequiredConversion.AnyAndReverse Then
Return RequiredConversion.Identity
Else
Return restriction
End If
End Function
' Combining inference restrictions: the least upper bound of the two restrictions
Public Shared Function CombineConversionRequirements(
restriction1 As RequiredConversion,
restriction2 As RequiredConversion
) As RequiredConversion
Debug.Assert(RequiredConversion.Count = 8, "If you've updated the type argument inference restrictions, then please also update CombineInferenceRestrictions()")
' [reverse chain] [None] < AnyReverse < ReverseReference < Identity
' [middle chain] None < [Any,AnyReverse] < AnyConversionAndReverse < Identity
' [forward chain] [None] < Any < ArrayElement < Reference < Identity
' identical?
If restriction1 = restriction2 Then
Return restriction1
End If
' none?
If restriction1 = RequiredConversion.None Then
Return restriction2
ElseIf restriction2 = RequiredConversion.None Then
Return restriction1
End If
' forced to the top of the lattice?
If restriction1 = RequiredConversion.Identity OrElse restriction2 = RequiredConversion.Identity Then
Return RequiredConversion.Identity
End If
' within the reverse chain?
If (restriction1 = RequiredConversion.AnyReverse OrElse restriction1 = RequiredConversion.ReverseReference) AndAlso
(restriction2 = RequiredConversion.AnyReverse OrElse restriction2 = RequiredConversion.ReverseReference) Then
Return RequiredConversion.ReverseReference
End If
' within the middle chain?
If (restriction1 = RequiredConversion.Any OrElse restriction1 = RequiredConversion.AnyReverse OrElse restriction1 = RequiredConversion.AnyAndReverse) AndAlso
(restriction2 = RequiredConversion.Any OrElse restriction2 = RequiredConversion.AnyReverse OrElse restriction2 = RequiredConversion.AnyAndReverse) Then
Return RequiredConversion.AnyAndReverse
End If
' within the forward chain?
If (restriction1 = RequiredConversion.Any OrElse restriction1 = RequiredConversion.ArrayElement) AndAlso
(restriction2 = RequiredConversion.Any OrElse restriction2 = RequiredConversion.ArrayElement) Then
Return RequiredConversion.ArrayElement
ElseIf (restriction1 = RequiredConversion.Any OrElse restriction1 = RequiredConversion.ArrayElement OrElse restriction1 = RequiredConversion.Reference) AndAlso
(restriction2 = RequiredConversion.Any OrElse restriction2 = RequiredConversion.ArrayElement OrElse restriction2 = RequiredConversion.Reference) Then
Return RequiredConversion.Reference
End If
' otherwise we've crossed chains
Return RequiredConversion.Identity
End Function
Public Shared Function IsWideningConversion(conv As ConversionKind) As Boolean
Debug.Assert(NoConversion(conv) OrElse
((conv And ConversionKind.Widening) <> 0) <> ((conv And ConversionKind.Narrowing) <> 0))
Return (conv And ConversionKind.Widening) <> 0
End Function
Public Shared Function IsNarrowingConversion(conv As ConversionKind) As Boolean
Debug.Assert(NoConversion(conv) OrElse
((conv And ConversionKind.Widening) <> 0) <> ((conv And ConversionKind.Narrowing) <> 0))
Return (conv And ConversionKind.Narrowing) <> 0
End Function
Public Shared Function NoConversion(conv As ConversionKind) As Boolean
Return (conv And (ConversionKind.Narrowing Or ConversionKind.Widening)) = 0
End Function
Public Shared Function ConversionExists(conv As ConversionKind) As Boolean
Return (conv And (ConversionKind.Narrowing Or ConversionKind.Widening)) <> 0
End Function
Public Shared Function IsIdentityConversion(conv As ConversionKind) As Boolean
Debug.Assert(NoConversion(conv) OrElse
((conv And ConversionKind.Widening) <> 0) <> ((conv And ConversionKind.Narrowing) <> 0))
Return (conv And ConversionKind.Identity) = ConversionKind.Identity
End Function
Public Shared Function FailedDueToNumericOverflow(conv As ConversionKind) As Boolean
Return (conv And (ConversionKind.Narrowing Or ConversionKind.Widening Or ConversionKind.FailedDueToNumericOverflow)) = ConversionKind.FailedDueToNumericOverflow
End Function
Public Shared Function FailedDueToQueryLambdaBodyMismatch(conv As ConversionKind) As Boolean
Return (conv And (ConversionKind.Narrowing Or ConversionKind.Widening Or ConversionKind.FailedDueToQueryLambdaBodyMismatch)) = ConversionKind.FailedDueToQueryLambdaBodyMismatch
End Function
''' <summary>
''' Determines whether the given conversion is CLR supported conversion or not.
''' </summary>
''' <param name="conversion">The conversion.</param><returns>
''' <c>true</c> if the given conversion is a CLR supported conversion; otherwise, <c>false</c>.
''' </returns>
Public Shared Function IsCLRPredefinedConversion(conversion As ConversionKind) As Boolean
If IsIdentityConversion(conversion) Then
Return True
Else
Const combinedClrConversions = ConversionKind.Reference Or ConversionKind.Array Or ConversionKind.TypeParameter
If (conversion And combinedClrConversions) <> 0 Then
Return True
End If
End If
Return False
End Function
End Class
''' <summary>
''' Used by ClassifyUserDefinedConversion to pass an ArrayTypeSymbol that has a link back to the BoundArrayLiteral node.
''' This allows the ClassifyConversionOperatorInOutConversions to properly classify a conversion from the inferred array
''' type to the input type of a user defined conversion.
''' </summary>
''' <remarks></remarks>
Friend NotInheritable Class ArrayLiteralTypeSymbol
Inherits ArrayTypeSymbol
Private ReadOnly _arrayLiteral As BoundArrayLiteral
''' <summary>
''' Create a new ArrayTypeSymbol.
''' </summary>
Friend Sub New(arrayLiteral As BoundArrayLiteral)
Me._arrayLiteral = arrayLiteral
End Sub
Friend ReadOnly Property ArrayLiteral As BoundArrayLiteral
Get
Return _arrayLiteral
End Get
End Property
Friend Overrides ReadOnly Property IsSZArray As Boolean
Get
Return _arrayLiteral.InferredType.IsSZArray
End Get
End Property
Public Overrides ReadOnly Property Rank As Integer
Get
Return _arrayLiteral.InferredType.Rank
End Get
End Property
Friend Overrides ReadOnly Property HasDefaultSizesAndLowerBounds As Boolean
Get
Return _arrayLiteral.InferredType.HasDefaultSizesAndLowerBounds
End Get
End Property
Friend Overrides ReadOnly Property InterfacesNoUseSiteDiagnostics As ImmutableArray(Of NamedTypeSymbol)
Get
Return _arrayLiteral.InferredType.InterfacesNoUseSiteDiagnostics
End Get
End Property
Friend Overrides ReadOnly Property BaseTypeNoUseSiteDiagnostics As NamedTypeSymbol
Get
Return _arrayLiteral.InferredType.BaseTypeNoUseSiteDiagnostics
End Get
End Property
Public Overrides ReadOnly Property CustomModifiers As ImmutableArray(Of CustomModifier)
Get
Return _arrayLiteral.InferredType.CustomModifiers
End Get
End Property
Public Overrides ReadOnly Property ElementType As TypeSymbol
Get
Return _arrayLiteral.InferredType.ElementType
End Get
End Property
Friend Overrides Function InternalSubstituteTypeParameters(substitution As TypeSubstitution) As TypeWithModifiers
Throw ExceptionUtilities.Unreachable
End Function
Friend Overrides Function WithElementType(elementType As TypeSymbol) As ArrayTypeSymbol
Throw ExceptionUtilities.Unreachable
End Function
End Class
End Namespace
|