/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Lowering/LocalRewriter/LocalRewriter_ConditionalAccess.vb
224 строки
10 KB
AlekseyTs
VB: Don't capture conditional access receiver into a temp local during lowering. (#76712)
17 янв 2025, 06:16
Не верифицирован
17 янв 2025, 06:16
82dc313
Код
Авторство
О чём код?
' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable Imports System.Diagnostics Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports TypeKind = Microsoft.CodeAnalysis.TypeKind Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend NotInheritable Class LocalRewriter Private Shared Function ShouldCaptureConditionalAccessReceiver(receiver As BoundExpression) As Boolean Select Case receiver.Kind Case BoundKind.MeReference Return False Case BoundKind.Parameter Return DirectCast(receiver, BoundParameter).ParameterSymbol.IsByRef Case BoundKind.Local Return DirectCast(receiver, BoundLocal).LocalSymbol.IsByRef Case Else Return Not receiver.IsDefaultValue() End Select End Function Public Overrides Function VisitConditionalAccess(node As BoundConditionalAccess) As BoundNode Debug.Assert(node.Type IsNot Nothing) Dim rewrittenReceiver As BoundExpression = VisitExpressionNode(node.Receiver) Dim receiverType As TypeSymbol = rewrittenReceiver.Type Dim receiver As BoundExpression Dim placeholderReplacement As BoundExpression Dim newPlaceholderId As Integer = 0 Dim newPlaceHolder As BoundConditionalAccessReceiverPlaceholder Dim captureReceiver As Boolean Dim temp As LocalSymbol = Nothing Dim assignment As BoundExpression = Nothing Dim needWhenNotNullPart As Boolean = True Dim needWhenNullPart As Boolean = True Dim factory = New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, node.Syntax, _compilationState, _diagnostics) If receiverType.IsNullableType() Then ' if( receiver.HasValue, receiver.GetValueOrDefault(). ... -> to Nullable, Nothing) If HasNoValue(rewrittenReceiver) Then ' Nothing receiver = Nothing needWhenNotNullPart = False placeholderReplacement = Nothing captureReceiver = False newPlaceHolder = Nothing ElseIf HasValue(rewrittenReceiver) Then ' receiver. ... -> to Nullable receiver = Nothing needWhenNullPart = False placeholderReplacement = NullableValueOrDefault(rewrittenReceiver) captureReceiver = False newPlaceHolder = Nothing Else captureReceiver = ShouldCaptureConditionalAccessReceiver(rewrittenReceiver) Me._conditionalAccessReceiverPlaceholderId += 1 newPlaceholderId = Me._conditionalAccessReceiverPlaceholderId Debug.Assert(newPlaceholderId <> 0) newPlaceHolder = New BoundConditionalAccessReceiverPlaceholder(node.Placeholder.Syntax, newPlaceholderId, captureReceiver, receiverType) receiver = rewrittenReceiver TryGetSpecialMember(Of MethodSymbol)(Nothing, SpecialMember.System_Nullable_T_get_HasValue, receiver.Syntax) placeholderReplacement = NullableValueOrDefault(newPlaceHolder) End If Else If rewrittenReceiver.IsConstant Then receiver = Nothing captureReceiver = False newPlaceHolder = Nothing If rewrittenReceiver.ConstantValueOpt.IsNothing Then ' Nothing placeholderReplacement = Nothing needWhenNotNullPart = False Else ' receiver. ... -> to Nullable placeholderReplacement = rewrittenReceiver.MakeRValue() needWhenNullPart = False End If Else ' if( receiver IsNot Nothing, receiver. ... -> to Nullable, Nothing) receiver = rewrittenReceiver ' we need a copy if we deal with nonlocal value (to capture the value) ' Or if we have a ref-constrained T (to do box just once) captureReceiver = (Not receiverType.IsReferenceType AndAlso Not receiverType.IsValueType AndAlso Not DirectCast(receiverType, TypeParameterSymbol).HasInterfaceConstraint) OrElse ' This could be a nullable value type, which must be copied in order to not mutate the original value (receiverType.IsReferenceType AndAlso receiverType.TypeKind = TypeKind.TypeParameter) OrElse ShouldCaptureConditionalAccessReceiver(rewrittenReceiver) Me._conditionalAccessReceiverPlaceholderId += 1 newPlaceholderId = Me._conditionalAccessReceiverPlaceholderId Debug.Assert(newPlaceholderId <> 0) newPlaceHolder = New BoundConditionalAccessReceiverPlaceholder(node.Placeholder.Syntax, newPlaceholderId, captureReceiver, node.Placeholder.Type) placeholderReplacement = newPlaceHolder End If End If Dim whenNotNull As BoundExpression Dim accessResultType As TypeSymbol = node.AccessExpression.Type If needWhenNotNullPart Then AddPlaceholderReplacement(node.Placeholder, placeholderReplacement) whenNotNull = VisitExpressionNode(node.AccessExpression) RemovePlaceholderReplacement(node.Placeholder) Else whenNotNull = Nothing ' We should simply produce Nothing as the result, if we need the result. End If Dim whenNull As BoundExpression If node.Type.IsVoidType() Then whenNull = Nothing Else If needWhenNotNullPart AndAlso Not accessResultType.IsNullableType() AndAlso accessResultType.IsValueType Then whenNotNull = WrapInNullable(whenNotNull, node.Type) End If If needWhenNullPart Then whenNull = If(node.Type.IsNullableType(), NullableNull(node.Syntax, node.Type), factory.Null(node.Type)) Else whenNull = Nothing End If End If Dim result As BoundExpression Debug.Assert(needWhenNotNullPart OrElse needWhenNullPart) If needWhenNotNullPart Then If needWhenNullPart Then result = New BoundLoweredConditionalAccess(node.Syntax, receiver, captureReceiver, newPlaceholderId, whenNotNull, whenNull, node.Type) Else Debug.Assert(receiver Is Nothing) Debug.Assert(newPlaceHolder Is Nothing) result = whenNotNull End If ElseIf whenNull IsNot Nothing Then Debug.Assert(receiver Is Nothing) result = whenNull Else Debug.Assert(receiver Is Nothing) Debug.Assert(node.Type.IsVoidType()) result = New BoundSequence(node.Syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray(Of BoundExpression).Empty, Nothing, node.Type) End If If temp IsNot Nothing Then If result.Type.IsVoidType() Then result = New BoundSequence(node.Syntax, ImmutableArray.Create(temp), ImmutableArray.Create(assignment, result), Nothing, result.Type) Else result = New BoundSequence(node.Syntax, ImmutableArray.Create(temp), ImmutableArray.Create(assignment), result, result.Type) End If End If Return result End Function Private Shared Function IsConditionalAccess(operand As BoundExpression, <Out> ByRef whenNotNull As BoundExpression, <Out> ByRef whenNull As BoundExpression) As Boolean If operand.Kind = BoundKind.Sequence Then Dim sequence = DirectCast(operand, BoundSequence) If sequence.ValueOpt Is Nothing Then whenNotNull = Nothing whenNull = Nothing Return False End If operand = sequence.ValueOpt End If If operand.Kind = BoundKind.LoweredConditionalAccess Then Dim conditional = DirectCast(operand, BoundLoweredConditionalAccess) whenNotNull = conditional.WhenNotNull whenNull = conditional.WhenNullOpt Return True End If whenNotNull = Nothing whenNull = Nothing Return False End Function Private Shared Function UpdateConditionalAccess(operand As BoundExpression, whenNotNull As BoundExpression, whenNull As BoundExpression) As BoundExpression Dim sequence As BoundSequence If operand.Kind = BoundKind.Sequence Then sequence = DirectCast(operand, BoundSequence) operand = sequence.ValueOpt Else sequence = Nothing End If Dim conditional = DirectCast(operand, BoundLoweredConditionalAccess) operand = conditional.Update(conditional.Receiver, conditional.CaptureReceiver, conditional.PlaceholderId, whenNotNull, whenNull, whenNotNull.Type) If sequence Is Nothing Then Return operand End If Return sequence.Update(sequence.Locals, sequence.SideEffects, operand, operand.Type) End Function End Class End Namespace