/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/CodeGen/EmitConversion.vb
524 строки
25 KB
Tomáš Matoušek
Add support for FieldRva to EnC delta (#78033)
10 май 2025, 20:30
Не верифицирован
10 май 2025, 20:30
7c62502
Код
Авторство
О чём код?
' 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.Reflection.Metadata Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports PrimitiveTypeCode = Microsoft.Cci.PrimitiveTypeCode Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen Partial Friend Class CodeGenerator Private Shared Function IsSimpleType(type As PrimitiveTypeCode) As Boolean Dim result = False Select Case type Case PrimitiveTypeCode.Boolean, PrimitiveTypeCode.Float32, PrimitiveTypeCode.Float64, PrimitiveTypeCode.Int16, PrimitiveTypeCode.Int32, PrimitiveTypeCode.Int64, PrimitiveTypeCode.Int8, PrimitiveTypeCode.UInt16, PrimitiveTypeCode.UInt32, PrimitiveTypeCode.UInt64, PrimitiveTypeCode.UInt8 result = True End Select Return result End Function Private Shared Function IsIntegral(type As PrimitiveTypeCode) As Boolean Dim result = False Select Case type Case PrimitiveTypeCode.Int8, PrimitiveTypeCode.UInt8, PrimitiveTypeCode.Int16, PrimitiveTypeCode.UInt16, PrimitiveTypeCode.Int32, PrimitiveTypeCode.UInt32, PrimitiveTypeCode.Int64, PrimitiveTypeCode.UInt64 result = True End Select Return result End Function Private Sub EmitConvertIntrinsic(conversion As BoundConversion, underlyingFrom As PrimitiveTypeCode, underlyingTo As PrimitiveTypeCode) Debug.Assert(underlyingFrom = conversion.Operand.Type.GetEnumUnderlyingTypeOrSelf().PrimitiveTypeCode) Debug.Assert(underlyingTo = conversion.Type.GetEnumUnderlyingTypeOrSelf().PrimitiveTypeCode) Debug.Assert((IsSimpleType(underlyingFrom) AndAlso IsSimpleType(underlyingTo)) OrElse (underlyingFrom = PrimitiveTypeCode.Char AndAlso underlyingTo = PrimitiveTypeCode.Int32)) ' Generate the expression to convert. EmitExpression(conversion.Operand, True) ' For "identity conversions" from float to float or double to double, ' we require the generation of conv.r4 or conv.r8, if the conversion ' was explicitly written. The runtime can use these instructions to ' truncate precision, and compiler generates them. If underlyingFrom = underlyingTo AndAlso Not conversion.ExplicitCastInCode AndAlso underlyingFrom <> PrimitiveTypeCode.Float32 AndAlso underlyingFrom <> PrimitiveTypeCode.Float64 Then Return End If ' Handle conversions to Boolean If underlyingTo = PrimitiveTypeCode.Boolean Then ' Emit Zero _builder.EmitConstantValue(ConstantValue.Default(underlyingFrom.GetConstantValueTypeDiscriminator()), conversion.Syntax) ' using cgt.un is optimal, but doesn't work in the case of floating point values If underlyingFrom.IsFloatingPoint() Then _builder.EmitOpCode(ILOpCode.Ceq) _builder.EmitOpCode(ILOpCode.Ldc_i4_0) _builder.EmitOpCode(ILOpCode.Ceq) Else _builder.EmitOpCode(ILOpCode.Cgt_un) End If Return End If ' Handle conversions from boolean If underlyingFrom = PrimitiveTypeCode.Boolean Then ' First, normalize to -1 _builder.EmitOpCode(ILOpCode.Ldc_i4_0) _builder.EmitOpCode(ILOpCode.Cgt_un) _builder.EmitOpCode(ILOpCode.Neg) If underlyingTo <> PrimitiveTypeCode.Int32 Then ' Convert to the target type, but don't do overflow checking. This results in unsigned types ' getting their max value. _builder.EmitNumericConversion(PrimitiveTypeCode.Int32, underlyingTo, checked:=False) End If Return End If ' Handle conversion between simple numeric types If underlyingFrom = PrimitiveTypeCode.Float32 AndAlso IsIntegral(underlyingTo) Then ' If converting from an intermediate value, we need to guarantee that ' the intermediate value keeps the precision of its type. The JIT will try to ' promote the precision of intermediate values if it can, and this can lead to ' incorrect results (VS#241243). Select Case conversion.Operand.Kind Case BoundKind.BinaryOperator Select Case (DirectCast(conversion.Operand, BoundBinaryOperator).OperatorKind And BinaryOperatorKind.OpMask) Case BinaryOperatorKind.Add, BinaryOperatorKind.Subtract, BinaryOperatorKind.Multiply, BinaryOperatorKind.Divide, BinaryOperatorKind.Modulo, BinaryOperatorKind.Power _builder.EmitOpCode(ILOpCode.Conv_r4) End Select Case BoundKind.UnaryOperator Select Case (DirectCast(conversion.Operand, BoundUnaryOperator).OperatorKind And UnaryOperatorKind.IntrinsicOpMask) Case UnaryOperatorKind.Minus, UnaryOperatorKind.Plus _builder.EmitOpCode(ILOpCode.Conv_r4) End Select End Select ' no intermediate value in other cases, so no need for the forced convert End If EmitConvertSimpleNumeric(conversion, underlyingFrom, underlyingTo, conversion.Checked) End Sub Private Sub EmitConvertSimpleNumeric(conversion As BoundConversion, typeFrom As PrimitiveTypeCode, typeTo As PrimitiveTypeCode, checked As Boolean) Debug.Assert(IsIntegral(typeFrom) OrElse typeFrom.IsFloatingPoint() OrElse typeFrom = PrimitiveTypeCode.Char) Debug.Assert(IsIntegral(typeTo) OrElse typeTo.IsFloatingPoint()) _builder.EmitNumericConversion(typeFrom, typeTo, checked) End Sub Private Sub EmitConversionExpression(conversion As BoundConversion, used As Boolean) If Not used AndAlso Not ConversionHasSideEffects(conversion) Then EmitExpression(conversion.Operand, False) Return End If Dim typeTo = conversion.Type If conversion.Operand.IsNothingLiteral Then Debug.Assert(typeTo.IsValueType OrElse typeTo.IsTypeParameter) If used Then 'TODO: used EmitLoadDefaultValueOfTypeFromNothingLiteral(typeTo, used:=True, syntaxNode:=conversion.Syntax) End If Else Dim underlyingTo = typeTo.GetEnumUnderlyingTypeOrSelf().PrimitiveTypeCode Dim typeFrom = conversion.Operand.Type Dim underlyingFrom = typeFrom.GetEnumUnderlyingTypeOrSelf().PrimitiveTypeCode If (IsSimpleType(underlyingFrom) AndAlso IsSimpleType(underlyingTo)) OrElse (underlyingFrom = PrimitiveTypeCode.Char AndAlso underlyingTo = PrimitiveTypeCode.Int32) Then ' Allow AscW optimization. EmitConvertIntrinsic(conversion, underlyingFrom, underlyingTo) ElseIf typeFrom.IsNullableType Then Debug.Assert(typeTo.IsReferenceType) EmitExpression(conversion.Operand, used:=True) If (conversion.ConversionKind And ConversionKind.Narrowing) <> 0 Then EmitBox(typeFrom, conversion.Operand.Syntax) _builder.EmitOpCode(ILOpCode.Castclass) EmitSymbolToken(typeTo, conversion.Syntax) ElseIf used Then ' boxing itself is CLR-widening, so no need to emit unused boxing EmitBox(typeFrom, conversion.Operand.Syntax) End If ElseIf typeTo.IsNullableType Then Debug.Assert(typeFrom.IsReferenceType) ' unboxing is CLR-narrowing and may deterministically throw EmitExpression(conversion.Operand, True) EmitUnboxAny(typeTo, conversion.Syntax) Else EmitExpression(conversion.Operand, True) If Not Conversions.IsIdentityConversion(conversion.ConversionKind) Then Debug.Assert(Not typeFrom.IsTypeParameter() AndAlso Not typeTo.IsTypeParameter() AndAlso typeFrom.IsReferenceType AndAlso typeTo.IsValueType) Debug.Assert(typeFrom.SpecialType = SpecialType.System_Object OrElse typeFrom.SpecialType = SpecialType.System_ValueType OrElse typeFrom.SpecialType = SpecialType.System_Enum OrElse typeFrom.IsInterfaceType) ' Conversions from references types to structures should be equivalent to ' conversions from Nothing if the reference is Nothing. Perform the check ' and do the conversion. Dim unboxLabel = New GeneratedLabelSymbol("unbox") Dim resultLabel = New GeneratedLabelSymbol("result") ' If the Reference is not nothing, branch directly to the Unbox _builder.EmitOpCode(ILOpCode.Dup) _builder.EmitBranch(ILOpCode.Brtrue_s, unboxLabel) ' The reference is nothing, so we need to load the "Nothing" value for the ' struct we're converting to. ' ' But first Pop off the Null reference cause we don't need it anymore. _builder.EmitOpCode(ILOpCode.Pop) Dim constructor = GetParameterlessValueTypeConstructor(DirectCast(typeTo, NamedTypeSymbol)) 'TODO: used If constructor Is Nothing OrElse constructor.IsDefaultValueTypeConstructor() Then EmitInitObj(typeTo, used:=True, syntaxNode:=conversion.Syntax) Else ' before we use constructor symbol we need to report use site error if any Dim diagnosticInfo As DiagnosticInfo = constructor.GetUseSiteInfo().DiagnosticInfo If diagnosticInfo IsNot Nothing Then _diagnostics.Add(New VBDiagnostic(diagnosticInfo, conversion.Syntax.Location)) End If EmitNewObj(constructor, ImmutableArray(Of BoundExpression).Empty, used:=True, syntaxNode:=conversion.Syntax) End If _builder.EmitBranch(ILOpCode.Br_s, resultLabel) _builder.MarkLabel(unboxLabel) ' Unbox the reference to get the struct from inside the object. _builder.EmitOpCode(ILOpCode.Unbox_any) EmitSymbolToken(typeTo, conversion.Syntax) _builder.MarkLabel(resultLabel) End If End If EmitPopIfUnused(used) End If End Sub ''' <summary> ''' Returns parameterless value type constructor. ''' </summary> Private Function GetParameterlessValueTypeConstructor(typeTo As NamedTypeSymbol) As MethodSymbol Debug.Assert(typeTo.IsValueType AndAlso Not typeTo.IsTypeParameter) ' find valuetype parameterless constructor and check the accessibility For Each constr In typeTo.InstanceConstructors ' NOTE: we intentionally skip constructors with all ' optional parameters; this matches Dev10 behavior If constr.ParameterCount = 0 Then ' check 'constr' If AccessCheck.IsSymbolAccessible(constr, _method.ContainingType, typeTo, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded) Then Return constr End If ' exit for each in any case Return Nothing End If Next ' This point should not be reachable, because if there is no constructor in the ' loaded value type, we should have generated a synthesized constructor. Throw ExceptionUtilities.Unreachable End Function Private Function IsUnboxingDirectCast(conversion As BoundDirectCast) As Boolean Dim typeTo As TypeSymbol = conversion.Type Dim typeFrom As TypeSymbol = conversion.Operand.Type Return Not conversion.Operand.IsNothingLiteral AndAlso Not Conversions.IsIdentityConversion(conversion.ConversionKind) AndAlso Not typeFrom.GetEnumUnderlyingTypeOrSelf().IsSameTypeIgnoringAll(typeTo.GetEnumUnderlyingTypeOrSelf()) AndAlso Not typeFrom.IsTypeParameter() AndAlso Not typeFrom.IsValueType AndAlso Not typeTo.IsReferenceType End Function Private Sub EmitDirectCastExpression(conversion As BoundDirectCast, used As Boolean) If Not used AndAlso Not ConversionHasSideEffects(conversion) Then EmitExpression(conversion.Operand, False) Return End If 'TODO: Dev10 CodeGenerator::GenerateDirectCast does some optimization for ' a case when conversion to Boolean is applied to comparison operator. ' Do we need to do something special about this case too? If conversion.Operand.IsNothingLiteral Then If conversion.Type.IsTypeParameter() Then EmitLoadDefaultValueOfTypeParameter(conversion.Type, used, conversion.Syntax) Return Else EmitExpression(conversion.Operand, True) If conversion.Type.IsValueType Then ' unbox the return value to get the struct from inside the object EmitUnboxAny(conversion.Type, conversion.Syntax) Else Debug.Assert(conversion.Type.IsReferenceType) End If End If Else EmitExpression(conversion.Operand, True) If Not Conversions.IsIdentityConversion(conversion.ConversionKind) Then Dim typeTo = conversion.Type Dim typeFrom = conversion.Operand.Type If typeFrom.GetEnumUnderlyingTypeOrSelf().IsSameTypeIgnoringAll(typeTo.GetEnumUnderlyingTypeOrSelf()) Then ' Do nothing, it is the same as identity. ElseIf typeFrom.IsTypeParameter() Then ' For any conversion from a generic parameter to any other type, ' box the operand and then allow the explicit cast from Object to ' the target type. This is a clr requirement. EmitBox(typeFrom, conversion.Operand.Syntax) If typeTo.SpecialType <> SpecialType.System_Object Then If typeTo.IsTypeParameter() Then _builder.EmitOpCode(ILOpCode.Unbox_any) EmitSymbolToken(typeTo, conversion.Syntax) 'TODO: is this needed for widening conversions? ElseIf typeTo.IsReferenceType Then _builder.EmitOpCode(ILOpCode.Castclass) EmitSymbolToken(typeTo, conversion.Syntax) Else Debug.Assert(typeTo.IsValueType) ' unbox the return value to get the struct from inside the object EmitUnboxAny(typeTo, conversion.Syntax) End If End If ElseIf typeTo.IsTypeParameter() Then Debug.Assert(Not typeFrom.IsTypeParameter()) If typeFrom.IsValueType Then ' For any conversion from a value type to a generic parameter, ' box the operand and then allow the explicit cast from Object to ' the target generic parameter type. EmitBox(typeFrom, conversion.Operand.Syntax) End If _builder.EmitOpCode(ILOpCode.Unbox_any) EmitSymbolToken(typeTo, conversion.Syntax) ElseIf typeFrom.IsValueType Then EmitBox(typeFrom, conversion.Operand.Syntax) If typeTo.IsInterfaceType() Then ' For any conversion from a value type to an implemented interface, ' box the operand and then allow the explicit conversion from Object to ' the interface below. _builder.EmitOpCode(ILOpCode.Castclass) EmitSymbolToken(typeTo, conversion.Syntax) Else Debug.Assert(typeTo.SpecialType = SpecialType.System_Object OrElse typeTo.SpecialType = SpecialType.System_ValueType OrElse typeTo.SpecialType = SpecialType.System_Enum) End If ElseIf typeTo.IsReferenceType Then Debug.Assert(typeFrom.IsReferenceType) Dim needExplicitCastClass As Boolean = True If Conversions.IsWideningConversion(conversion.ConversionKind) Then needExplicitCastClass = False ' No need to emit explicit cast, except in the following cases. ' TODO: Do we still need this checks or CLR is smarter now? If typeFrom.IsArrayType() Then Dim fromElementType = DirectCast(typeFrom, ArrayTypeSymbol).ElementType ' Bug VSWhidbey 415020 If typeTo.IsArrayType() AndAlso (fromElementType.IsTypeParameter() OrElse DirectCast(typeTo, ArrayTypeSymbol).ElementType.IsTypeParameter()) Then needExplicitCastClass = True ElseIf fromElementType.IsTypeParameter() AndAlso typeTo.IsInterfaceType() Then ' Bug VSWhidbey 517458. Special case for IList(Of T), ICollection(Of T) and IEnumerable(Of T). Dim [interface] = DirectCast(typeTo, NamedTypeSymbol) If [interface].Arity = 1 AndAlso Not [interface].TypeArgumentsNoUseSiteDiagnostics(0).IsSameTypeIgnoringAll(fromElementType) Then needExplicitCastClass = True End If End If End If End If If needExplicitCastClass Then _builder.EmitOpCode(ILOpCode.Castclass) EmitSymbolToken(typeTo, conversion.Syntax) End If Else Debug.Assert(typeTo.IsValueType) Debug.Assert(typeFrom.IsReferenceType) Debug.Assert(typeFrom.SpecialType = SpecialType.System_Object OrElse typeFrom.SpecialType = SpecialType.System_ValueType OrElse typeFrom.SpecialType = SpecialType.System_Enum OrElse typeFrom.IsInterfaceType) Debug.Assert(IsUnboxingDirectCast(conversion)) ' unbox the return value to get the struct from inside the object EmitUnboxAny(typeTo, conversion.Syntax) End If End If End If EmitPopIfUnused(used) End Sub Private Function ConversionHasSideEffects(conversion As BoundConversion) As Boolean ' only some intrinsic conversions are side-effect free ' the only side-effect of an intrinsic conversion is a throw when we fail to convert. ' ' unchecked numeric conv does not throw ' implicit ref cast does not throw ' ... 'TODO: compute this (note: returning true is safe - false would just enable optimizations) Return True End Function Private Function ConversionHasSideEffects(conversion As BoundDirectCast) As Boolean 'TODO: compute this (note: returning true is safe - false would just enable optimizations) Return True End Function Private Function ConversionHasSideEffects(conversion As BoundTryCast) As Boolean 'TODO: compute this (note: returning true is safe - false would just enable optimizations) Return False End Function Private Sub EmitTryCastExpression(conversion As BoundTryCast, used As Boolean) If Not used AndAlso Not ConversionHasSideEffects(conversion) Then EmitExpression(conversion.Operand, False) Return End If If conversion.Operand.IsNothingLiteral Then If conversion.Type.IsTypeParameter() Then 'TODO: used EmitLoadDefaultValueOfTypeParameter(conversion.Type, used:=True, syntaxNode:=conversion.Syntax) Else Debug.Assert(conversion.Type.IsReferenceType) EmitExpression(conversion.Operand, True) End If Else EmitExpression(conversion.Operand, True) If Not Conversions.IsIdentityConversion(conversion.ConversionKind) Then Dim typeTo = conversion.Type Dim typeFrom = conversion.Operand.Type Debug.Assert(typeFrom IsNot Nothing) Debug.Assert(typeTo IsNot Nothing) If typeFrom.IsReferenceType() OrElse typeFrom.IsTypeParameter() OrElse typeTo.IsTypeParameter() Then If Not IsVerifierReference(typeFrom) Then EmitBox(typeFrom, conversion.Operand.Syntax) End If _builder.EmitOpCode(ILOpCode.Isinst) EmitSymbolToken(typeTo, conversion.Syntax) If Not IsVerifierReference(typeTo) Then _builder.EmitOpCode(ILOpCode.Unbox_any) EmitSymbolToken(typeTo, conversion.Syntax) End If Else Debug.Assert(typeFrom.IsValueType) Debug.Assert(typeTo.IsReferenceType) Debug.Assert(typeTo.SpecialType = SpecialType.System_Object OrElse typeTo.SpecialType = SpecialType.System_ValueType OrElse typeTo.SpecialType = SpecialType.System_Enum OrElse typeTo.IsInterfaceType()) EmitBox(typeFrom, conversion.Operand.Syntax) End If End If End If EmitPopIfUnused(used) End Sub End Class End Namespace