提交 5ad86a12 编写于 作者: A AlekseyTs

VB: More optimal IL generation for Elvis operator (changeset 1351309)

上级 931dd927
......@@ -1422,6 +1422,26 @@ lUnsplitAndFinish:
Return Nothing
End Function
Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
VisitRvalue(node.ReceiverOrCondition)
Dim savedState As LocalState = Me.State.Clone()
VisitRvalue(node.WhenNotNull)
IntersectWith(Me.State, savedState)
If node.WhenNullOpt IsNot Nothing Then
savedState = Me.State.Clone()
VisitRvalue(node.WhenNullOpt)
IntersectWith(Me.State, savedState)
End If
Return Nothing
End Function
Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitReturnStatement(node As BoundReturnStatement) As BoundNode
' Set unreachable and pending branch for all returns except for the final return that is auto generated
If Not node.IsEndOfMethodReturn Then
......@@ -1562,7 +1582,7 @@ lUnsplitAndFinish:
Public Overrides Function VisitReferenceAssignment(node As BoundReferenceAssignment) As BoundNode
VisitRvalue(node.ByRefLocal)
VisitRvalue(node.Target)
VisitRvalue(node.LValue)
Return Nothing
End Function
......
......@@ -1754,7 +1754,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Public Overrides Function VisitReferenceAssignment(node As BoundReferenceAssignment) As BoundNode
MyBase.VisitReferenceAssignment(node)
Assign(node.ByRefLocal, node.Target)
Assign(node.ByRefLocal, node.LValue)
Return Nothing
End Function
......
......@@ -142,7 +142,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim local As LocalSymbol = node.ByRefLocal.LocalSymbol
Debug.Assert(Not Me._byRefLocalsInitializers.ContainsKey(local))
Me._byRefLocalsInitializers.Add(local, node.Target)
Me._byRefLocalsInitializers.Add(local, node.LValue)
Return MyBase.VisitReferenceAssignment(node)
End Function
......
......@@ -353,7 +353,7 @@
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<Field Name="ByRefLocal" Type="BoundLocal"/>
<Field Name="Target" Type="BoundExpression" Null="allow"/>
<Field Name="LValue" Type="BoundExpression" Null="allow"/>
<Field Name="IsLValue" PropertyOverrides="true" Type="Boolean"/>
</Node>
......@@ -1721,4 +1721,17 @@
<Field Name="Placeholder" Type="BoundRValuePlaceholder"/>
<Field Name="AccessExpression" Type="BoundExpression"/>
</Node>
<Node Name="BoundConditionalAccessReceiverPlaceholder" Base="BoundRValuePlaceholderBase" HasValidate="true">
<Field Name="PlaceholderId" Type="Integer"/>
</Node>
<Node Name="BoundLoweredConditionalAccess" Base="BoundExpression" HasValidate="true">
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<Field Name="ReceiverOrCondition" Type="BoundExpression"/>
<Field Name="CaptureReceiver" Type="Boolean" Null="NotApplicable"/>
<Field Name="PlaceholderId" Type="Integer"/>
<Field Name="WhenNotNull" Type="BoundExpression"/>
<Field Name="WhenNullOpt" Type="BoundExpression" Null="allow"/>
</Node>
</Tree>
\ No newline at end of file
......@@ -9,7 +9,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Partial Class BoundReferenceAssignment
#If DEBUG Then
Private Sub Validate()
Debug.Assert(ByRefLocal.LocalSymbol.IsByRef AndAlso Type = Target.Type)
Debug.Assert(ByRefLocal.LocalSymbol.IsByRef AndAlso LValue.IsLValue AndAlso Type = LValue.Type)
End Sub
#End If
......@@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Public Shadows Function MakeRValue() As BoundReferenceAssignment
If _IsLValue Then
Return Update(ByRefLocal, Target, False, Type)
Return Update(ByRefLocal, LValue, False, Type)
End If
Return Me
......
......@@ -65,6 +65,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Case BoundKind.ReferenceAssignment
EmitReferenceAssignment(DirectCast(expression, BoundReferenceAssignment), used:=True, needReference:=True)
Case BoundKind.ConditionalAccessReceiverPlaceholder
' do nothing receiver ref must be already pushed
Debug.Assert(Not expression.Type.IsReferenceType)
Debug.Assert(Not expression.Type.IsValueType)
Case BoundKind.Parameter
EmitParameterAddress(DirectCast(expression, BoundParameter))
......@@ -260,6 +265,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
''' Checks if it is allowed to take a writeable reference to expression according to VB rules.
''' </summary>
Private Function AllowedToTakeRef(expression As BoundExpression, addressKind As AddressKind) As Boolean
If expression.Kind = BoundKind.ConditionalAccessReceiverPlaceholder Then
Return addressKind = AddressKind.ReadOnly OrElse addressKind = AddressKind.Immutable
End If
' taking immutable addresses is ok as long as expression has home
If addressKind <> AddressKind.Immutable Then
......@@ -444,7 +454,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Return EmitAddress(receiver, AddressKind.ReadOnly)
Else
EmitExpression(receiver, used:=True)
EmitBox(receiverType, receiver.Syntax)
' conditional receivers are already boxed if needed when pushed
If receiver.Kind <> BoundKind.ConditionalAccessReceiverPlaceholder Then
EmitBox(receiverType, receiver.Syntax)
End If
Return Nothing
End If
End If
......
......@@ -128,6 +128,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
' which is to be able to pass Me reference of value type as ByRef argument in compiler
' generated code, this is why we specifically prohibit emitting this as value
Throw ExceptionUtilities.UnexpectedValue(expression.Kind)
Case BoundKind.LoweredConditionalAccess
EmitConditionalAccess(DirectCast(expression, BoundLoweredConditionalAccess), used)
Case BoundKind.ConditionalAccessReceiverPlaceholder
EmitConditionalAccessReceiverPlaceholder(DirectCast(expression, BoundConditionalAccessReceiverPlaceholder), used)
Case Else
' Code gen should not be invoked if there are errors.
' Debug.Assert(expression.Kind <> BoundKind.BadExpression AndAlso expression.Kind <> BoundKind.Parenthesized)
......@@ -136,6 +143,153 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
End Select
End Sub
Private Sub EmitConditionalAccessReceiverPlaceholder(expression As BoundConditionalAccessReceiverPlaceholder, used As Boolean)
Debug.Assert(Not expression.Type.IsValueType)
If used AndAlso Not expression.Type.IsReferenceType Then
EmitLoadIndirect(expression.Type, expression.Syntax)
End If
EmitPopIfUnused(used)
End Sub
Private Sub EmitConditionalAccess(conditinal As BoundLoweredConditionalAccess, used As Boolean)
Debug.Assert(conditinal.WhenNullOpt IsNot Nothing OrElse Not used)
If conditinal.ReceiverOrCondition.Type.IsBooleanType() Then
' This is a trivial case
Debug.Assert(Not conditinal.CaptureReceiver)
Debug.Assert(conditinal.PlaceholderId = 0)
Dim doneLabel = New Object()
Dim consequenceLabel = New Object()
EmitCondBranch(conditinal.ReceiverOrCondition, consequenceLabel, sense:=True)
If conditinal.WhenNullOpt IsNot Nothing Then
EmitExpression(conditinal.WhenNullOpt, used)
Else
Debug.Assert(Not used)
End If
_builder.EmitBranch(ILOpCode.Br, doneLabel)
If used Then
' If we get to consequenceLabel, we should not have WhenFalse on stack, adjust for that.
_builder.AdjustStack(-1)
End If
_builder.MarkLabel(consequenceLabel)
EmitExpression(conditinal.WhenNotNull, used)
_builder.MarkLabel(doneLabel)
Else
Debug.Assert(Not conditinal.ReceiverOrCondition.Type.IsValueType)
Dim receiverTemp As LocalDefinition = Nothing
Dim temp As LocalDefinition = Nothing
' labels
Dim whenNotNullLabel As New Object()
Dim doneLabel As New Object()
' 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)
Dim receiver As BoundExpression = conditinal.ReceiverOrCondition
Dim receiverType As TypeSymbol = receiver.Type
Dim nullCheckOnCopy = conditinal.CaptureReceiver OrElse (receiverType.IsReferenceType AndAlso receiverType.TypeKind = TypeKind.TypeParameter)
If nullCheckOnCopy Then
EmitReceiverRef(receiver, isAccessConstrained:=Not receiverType.IsReferenceType, addressKind:=AddressKind.ReadOnly)
If Not receiverType.IsReferenceType Then
' unconstrained case needs to handle case where T Is actually a struct.
' such values are never nulls
' we will emit a check for such case, but the check Is realy a JIT-time
' constant since JIT will know if T Is a struct Or Not.
'
' if ((object)default(T) != null)
' {
' goto whenNotNull
' }
' else
' {
' temp = receiverRef
' receiverRef = ref temp
' }
EmitInitObj(receiverType, True, receiver.Syntax)
EmitBox(receiverType, receiver.Syntax)
_builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel)
EmitLoadIndirect(receiverType, receiver.Syntax)
temp = AllocateTemp(receiverType, receiver.Syntax)
_builder.EmitLocalStore(temp)
_builder.EmitLocalAddress(temp)
_builder.EmitLocalLoad(temp)
EmitBox(receiver.Type, receiver.Syntax)
' here we have loaded a ref to a temp And its boxed value { &T, O }
Else
_builder.EmitOpCode(ILOpCode.Dup)
' here we have loaded two copies of a reference { O, O }
End If
Else
EmitExpression(receiver, True)
If Not receiverType.IsReferenceType Then
EmitBox(receiverType, receiver.Syntax)
End If
' here we have loaded just { O }
' we have the most trivial case where we can just reload O when needed
End If
_builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel)
If nullCheckOnCopy Then
_builder.EmitOpCode(ILOpCode.Pop)
End If
If conditinal.WhenNullOpt IsNot Nothing Then
EmitExpression(conditinal.WhenNullOpt, used)
Else
Debug.Assert(Not used)
End If
_builder.EmitBranch(ILOpCode.Br, doneLabel)
If used Then
' If we get to whenNotNullLabel, we should not have WhenNullOpt on stack, adjust for that.
_builder.AdjustStack(-1)
End If
If nullCheckOnCopy Then
' whenNull branch pops copy of the receiver off the stack when nullCheckOnCopy
' however on this branch we still have the stack as it was and need
' to adjust stack depth accordingly.
_builder.AdjustStack(+1)
End If
_builder.MarkLabel(whenNotNullLabel)
If Not nullCheckOnCopy Then
receiverTemp = EmitReceiverRef(receiver, isAccessConstrained:=Not receiverType.IsReferenceType, addressKind:=AddressKind.ReadOnly)
Debug.Assert(receiverTemp Is Nothing)
End If
EmitExpression(conditinal.WhenNotNull, used)
_builder.MarkLabel(doneLabel)
If temp IsNot Nothing Then
FreeTemp(temp)
End If
If receiverTemp IsNot Nothing Then
FreeTemp(receiverTemp)
End If
End If
End Sub
Private Sub EmitDelegateCreationExpression(expression As BoundDelegateCreationExpression, used As Boolean)
Dim invoke = DirectCast(expression.Method, MethodSymbol)
EmitDelegateCreation(expression.ReceiverOpt, invoke, expression.Type, used, expression.Syntax)
......@@ -639,6 +793,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Case BoundKind.FieldAccess
Return DirectCast(receiver, BoundFieldAccess).FieldSymbol.IsCapturedFrame
Case BoundKind.ConditionalAccessReceiverPlaceholder
Return True
'TODO: there must be more non-null cases.
End Select
......@@ -1596,10 +1753,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Private Sub EmitReferenceAssignment(capture As BoundReferenceAssignment, used As Boolean, Optional needReference As Boolean = False)
Debug.Assert(Not needReference OrElse used)
Dim temp = EmitAddress(capture.Target, addressKind:=AddressKind.Writeable)
Dim temp = EmitAddress(capture.LValue, addressKind:=AddressKind.Writeable)
' TODO: We can leak a temp here, but we don't have a good way to infer when it is safe to let it go.
Debug.Assert(temp Is Nothing OrElse Not capture.Target.IsLValue, "reference assignment should not clone the referent")
Debug.Assert(temp Is Nothing, "reference assignment should not clone the referent")
If used Then
_builder.EmitOpCode(ILOpCode.Dup)
......
......@@ -543,7 +543,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Me.assignmentLocal = Nothing
' Visit a l-value expression in context of 'address'
Dim right As BoundExpression = VisitExpression(node.Target, ExprContext.Address)
Dim right As BoundExpression = VisitExpression(node.LValue, ExprContext.Address)
' record the Write to the local
Debug.Assert(storedAssignmentLocal IsNot Nothing)
......@@ -551,7 +551,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
' this assert will fire if code relies on implicit CLR coercions
' - i.e assigns int value to a short local.
' in that case we should force lhs to be a real local
Debug.Assert(node.ByRefLocal.Type.IsSameTypeIgnoringCustomModifiers(node.Target.Type),
Debug.Assert(node.ByRefLocal.Type.IsSameTypeIgnoringCustomModifiers(node.LValue.Type),
"cannot use stack when assignment involves implicit coercion of the value")
RecordVarWrite(storedAssignmentLocal.LocalSymbol)
......@@ -845,6 +845,43 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Return node.Update(condition, whenTrue, whenFalse, node.ConstantValueOpt, node.Type)
End Function
Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
If Not node.ReceiverOrCondition.Type.IsBooleanType() Then
' We may need to load a reference to the receiver, or may need to
' reload it after the null check. This won't work well
' with a stack local.
EnsureOnlyEvalStack()
End If
Dim origStack = Me.evalStack
Dim receiverOrCondition = DirectCast(Me.Visit(node.ReceiverOrCondition), BoundExpression)
Dim cookie = GetStackStateCookie() ' implicit branch here
' access Is evaluated with original stack
' (this Is Not entirely true, codegen will keep receiver on the stack, but that Is irrelevant here)
Me.evalStack = origStack
Dim whenNotNull = DirectCast(Me.Visit(node.WhenNotNull), BoundExpression)
EnsureStackState(cookie) ' implicit label here
Dim whenNull As BoundExpression = Nothing
If node.WhenNullOpt IsNot Nothing Then
Me.evalStack = origStack
whenNull = DirectCast(Me.Visit(node.WhenNullOpt), BoundExpression)
EnsureStackState(cookie) ' implicit label here
End If
Return node.Update(receiverOrCondition, node.CaptureReceiver, node.PlaceholderId, whenNotNull, whenNull, node.Type)
End Function
Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
Return MyBase.VisitConditionalAccessReceiverPlaceholder(node)
End Function
Public Overrides Function VisitBinaryOperator(node As BoundBinaryOperator) As BoundNode
Select Case (node.OperatorKind And BinaryOperatorKind.OpMask)
Case BinaryOperatorKind.AndAlso, BinaryOperatorKind.OrElse
......
......@@ -93,7 +93,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Me.nodeCounter += 1
' Visit the expression being assigned
Dim right = DirectCast(Me.Visit(node.Target), BoundExpression)
Dim right = DirectCast(Me.Visit(node.LValue), BoundExpression)
' this should not be the last store, why would be created such a variable after all???
Debug.Assert(locInfo.localDefs.Any(Function(d) nodeCounter = d.Start AndAlso nodeCounter <= d.End))
......@@ -150,7 +150,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
Dim right = DirectCast(Me.Visit(node.Right), BoundExpression)
' do actual assignment
Debug.Assert(locInfo.localDefs.Any(Function(d) nodeCounter = d.Start AndAlso nodeCounter <= d.End))
Dim isLast As Boolean = IsLastAccess(locInfo, nodeCounter)
......@@ -165,6 +164,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen
End If
End Function
Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
Dim receiverOrCondition As BoundExpression = DirectCast(Me.Visit(node.ReceiverOrCondition), BoundExpression)
Dim whenNotNull As BoundExpression = DirectCast(Me.Visit(node.WhenNotNull), BoundExpression)
Dim whenNullOpt As BoundExpression = node.WhenNullOpt
If whenNullOpt IsNot Nothing Then
whenNullOpt = DirectCast(Me.Visit(whenNullOpt), BoundExpression)
End If
Return node.Update(receiverOrCondition, node.CaptureReceiver, node.PlaceholderId, whenNotNull, whenNullOpt, node.Type)
End Function
End Class
End Class
......
......@@ -255,7 +255,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
' ByRef local was not captured
Dim rewrittenLeft As BoundLocal = DirectCast(Me.VisitExpression(origByRefLocal), BoundLocal)
Dim rewrittenRight As BoundExpression = Me.VisitExpression(node.Target)
Dim rewrittenRight As BoundExpression = Me.VisitExpression(node.LValue)
Debug.Assert(rewrittenRight.IsLValue)
Dim rightRequiresSpill As Boolean = NeedsSpill(rewrittenRight)
......@@ -472,6 +472,181 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return Me.F.Assignment(Me.F.Local(temp, True), expression)
End Function
Private Class ConditionalAccessReceiverPlaceholderReplacementInfo
Public ReadOnly PlaceholderId As Integer
Public IsSpilled As Boolean
Public Sub New(placeholderId As Integer)
Me.PlaceholderId = placeholderId
Me.IsSpilled = False
End Sub
End Class
Private m_ConditionalAccessReceiverPlaceholderReplacementInfo As ConditionalAccessReceiverPlaceholderReplacementInfo = Nothing
Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
Dim type As TypeSymbol = Me.VisitType(node.Type)
Dim receiverOrCondition As BoundExpression = DirectCast(Me.Visit(node.ReceiverOrCondition), BoundExpression)
Dim receiverOrConditionNeedsSpill = NeedsSpill(receiverOrCondition)
Dim saveConditionalAccessReceiverPlaceholderReplacementInfo = m_ConditionalAccessReceiverPlaceholderReplacementInfo
Dim conditionalAccessReceiverPlaceholderReplacementInfo As ConditionalAccessReceiverPlaceholderReplacementInfo
If node.PlaceholderId <> 0 Then
conditionalAccessReceiverPlaceholderReplacementInfo = New ConditionalAccessReceiverPlaceholderReplacementInfo(node.PlaceholderId)
Else
conditionalAccessReceiverPlaceholderReplacementInfo = Nothing
End If
m_ConditionalAccessReceiverPlaceholderReplacementInfo = conditionalAccessReceiverPlaceholderReplacementInfo
Dim whenNotNull As BoundExpression = DirectCast(Me.Visit(node.WhenNotNull), BoundExpression)
Dim whenNotNullNeedsSpill = NeedsSpill(whenNotNull)
Debug.Assert(conditionalAccessReceiverPlaceholderReplacementInfo Is Nothing OrElse
(Not conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled OrElse whenNotNullNeedsSpill))
m_ConditionalAccessReceiverPlaceholderReplacementInfo = Nothing
Dim whenNullOpt As BoundExpression = DirectCast(Me.Visit(node.WhenNullOpt), BoundExpression)
Dim whenNullNeedsSpill = If(whenNullOpt IsNot Nothing, NeedsSpill(whenNullOpt), False)
m_ConditionalAccessReceiverPlaceholderReplacementInfo = saveConditionalAccessReceiverPlaceholderReplacementInfo
If Not receiverOrConditionNeedsSpill AndAlso Not whenNotNullNeedsSpill AndAlso Not whenNullNeedsSpill Then
Return node.Update(receiverOrCondition,
node.CaptureReceiver,
node.PlaceholderId,
whenNotNull,
whenNullOpt,
type)
End If
If Not whenNotNullNeedsSpill AndAlso Not whenNullNeedsSpill Then
Debug.Assert(receiverOrConditionNeedsSpill)
Dim spill = DirectCast(receiverOrCondition, BoundSpillSequence)
Return SpillSequenceWithNewValue(spill, node.Update(spill.ValueOpt,
node.CaptureReceiver,
node.PlaceholderId,
whenNotNull,
whenNullOpt,
type))
End If
Dim builder As New SpillBuilder()
If receiverOrConditionNeedsSpill Then
Dim spill = DirectCast(receiverOrCondition, BoundSpillSequence)
builder.AddSpill(spill)
receiverOrCondition = spill.ValueOpt
End If
If conditionalAccessReceiverPlaceholderReplacementInfo IsNot Nothing Then
' We need to revisit the whenNotNull expression to replace placeholder
If node.CaptureReceiver OrElse conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled Then
' Let's use stack spilling to capture it.
receiverOrCondition = SpillValue(receiverOrCondition, builder)
End If
Dim rewriter As New ConditionalAccessReceiverPlaceholderReplacement(node.PlaceholderId, receiverOrCondition)
whenNotNull = DirectCast(rewriter.Visit(whenNotNull), BoundExpression)
Debug.Assert(rewriter.Replaced)
End If
If Not receiverOrCondition.Type.IsBooleanType() Then
' We need to a add a null check for the receiver
If receiverOrCondition.Type.IsReferenceType Then
receiverOrCondition = Me.F.ReferenceIsNotNothing(receiverOrCondition.MakeRValue())
Else
Debug.Assert(Not receiverOrCondition.Type.IsValueType)
Debug.Assert(receiverOrCondition.Type.IsTypeParameter())
' The "receiver IsNot Nothing" check becomes
' Not <receiver's type is refernce type> OrElse receiver IsNot Nothing
' The <receiver's type is refernce type> is performed by boxing default value of receiver's type and checking if it is a null reference.
Dim notReferenceType = Me.F.ReferenceIsNotNothing(Me.F.DirectCast(Me.F.DirectCast(Me.F.Null(),
receiverOrCondition.Type),
Me.F.SpecialType(SpecialType.System_Object)))
receiverOrCondition = Me.F.LogicalOrElse(notReferenceType,
Me.F.ReferenceIsNotNothing(Me.F.DirectCast(receiverOrCondition.MakeRValue(),
Me.F.SpecialType(SpecialType.System_Object))))
End If
End If
If whenNullOpt Is Nothing Then
Debug.Assert(type.IsVoidType())
builder.AddStatement(
Me.F.If(condition:=receiverOrCondition,
thenClause:=MakeExpressionStatement(whenNotNull, builder)))
Return builder.BuildSequenceAndFree(Me.F, expression:=Nothing)
Else
Debug.Assert(Not type.IsVoidType())
Dim tempLocal As LocalSymbol = Me.F.SynthesizedLocal(type)
builder.AddLocal(tempLocal)
builder.AddStatement(Me.F.If(condition:=receiverOrCondition,
thenClause:=MakeAssignmentStatement(whenNotNull, tempLocal, builder),
elseClause:=MakeAssignmentStatement(whenNullOpt, tempLocal, builder)))
Return builder.BuildSequenceAndFree(Me.F, expression:=Me.F.Local(tempLocal, False))
End If
End Function
Private Class ConditionalAccessReceiverPlaceholderReplacement
Inherits BoundTreeRewriter
Private ReadOnly m_PlaceholderId As Integer
Private ReadOnly m_ReplaceWith As BoundExpression
Private m_Replaced As Boolean
Public Sub New(placeholderId As Integer, replaceWith As BoundExpression)
Me.m_PlaceholderId = placeholderId
Me.m_ReplaceWith = replaceWith
End Sub
Public ReadOnly Property Replaced As Boolean
Get
Return m_Replaced
End Get
End Property
Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
Debug.Assert(m_PlaceholderId = node.PlaceholderId)
If m_PlaceholderId = node.PlaceholderId Then
Debug.Assert(Not m_Replaced)
m_Replaced = True
Dim result = m_ReplaceWith
If node.IsLValue Then
Debug.Assert(result.IsLValue)
Return result
Else
Return result.MakeRValue()
End If
End If
Return node
End Function
End Class
Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
If m_ConditionalAccessReceiverPlaceholderReplacementInfo Is Nothing OrElse m_ConditionalAccessReceiverPlaceholderReplacementInfo.PlaceholderId <> node.PlaceholderId Then
Throw ExceptionUtilities.Unreachable
End If
Return MyBase.VisitConditionalAccessReceiverPlaceholder(node)
End Function
Public Overrides Function VisitArrayCreation(node As BoundArrayCreation) As BoundNode
Dim bounds As ImmutableArray(Of BoundExpression) = Me.VisitList(node.Bounds)
Dim rewrittenInitializer As BoundArrayInitialization = DirectCast(Me.Visit(node.InitializerOpt), BoundArrayInitialization)
......
......@@ -286,6 +286,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return array
Case BoundKind.ConditionalAccessReceiverPlaceholder
Throw ExceptionUtilities.Unreachable
Case BoundKind.FieldAccess
Dim fieldAccess = DirectCast(expr, BoundFieldAccess)
......@@ -340,6 +343,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim arrayInit = DirectCast(expr, BoundArrayInitialization)
Return arrayInit.Update(SpillExpressionList(builder, arrayInit.Initializers), arrayInit.Type)
Case BoundKind.ConditionalAccessReceiverPlaceholder
If m_ConditionalAccessReceiverPlaceholderReplacementInfo Is Nothing OrElse
m_ConditionalAccessReceiverPlaceholderReplacementInfo.PlaceholderId <> DirectCast(expr, BoundConditionalAccessReceiverPlaceholder).PlaceholderId Then
Throw ExceptionUtilities.Unreachable
End If
m_ConditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled = True
Return expr
Case Else
' Create a field for a spill
Dim spillField As FieldSymbol = Me._spillFieldAllocator.AllocateField(expr.Type)
......
......@@ -1129,6 +1129,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return rewritten
End Function
Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
Dim result = DirectCast(MyBase.VisitLoweredConditionalAccess(node), BoundLoweredConditionalAccess)
If Not result.CaptureReceiver AndAlso Not node.ReceiverOrCondition.Type.IsBooleanType() AndAlso
node.ReceiverOrCondition.Kind <> result.ReceiverOrCondition.Kind Then
' It looks like the receiver got lifted into a closure, we cannot assume that it will not change between null check and the following access.
Return result.Update(result.ReceiverOrCondition, True, result.PlaceholderId, result.WhenNotNull, result.WhenNullOpt, result.Type)
Else
Return result
End If
End Function
''' <summary>
''' Optimize the case where we create an instance of a delegate and invoke it right away.
''' Skip the delegate creation and invoke the method directly. Specifically, we are targeting
......
' Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
' Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Diagnostics
......@@ -34,6 +34,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Private currentLineTemporary As LocalSymbol
Private createSequencePointsForTopLevelNonCompilerGeneratedExpressions As Boolean
Private conditionalAccessReceiverPlaceholderId As Integer
#If DEBUG Then
''' <summary>
......
......@@ -778,6 +778,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
' We may also statically know if one is definitely not a null
' we cannot know, though, if whole operator yields null or not.
If rightHasValue AndAlso left.Kind = BoundKind.LoweredConditionalAccess Then
Dim rightValue = NullableValueOrDefault(right)
Dim conditional = DirectCast(left, BoundLoweredConditionalAccess)
If (rightValue.IsConstant OrElse rightValue.Kind = BoundKind.Local OrElse rightValue.Kind = BoundKind.Parameter) AndAlso
HasValue(conditional.WhenNotNull) AndAlso HasNoValue(conditional.WhenNullOpt) Then
Return conditional.Update(conditional.ReceiverOrCondition,
conditional.CaptureReceiver,
conditional.PlaceholderId,
WrapInNullable(ApplyUnliftedBinaryOp(node,
NullableValueOrDefault(conditional.WhenNotNull),
rightValue),
node.Type),
NullableNull(conditional.WhenNullOpt, node.Type),
node.Type)
End If
End If
Dim temps As ArrayBuilder(Of LocalSymbol) = Nothing
Dim inits As ArrayBuilder(Of BoundExpression) = Nothing
......@@ -1093,37 +1112,54 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return node
End If
Dim isIs = node.OperatorKind = BinaryOperatorKind.Is
Dim operand = If(left.IsNothingLiteral, right, left)
Return RewriteNullableIsOrIsNotOperator(node.OperatorKind = BinaryOperatorKind.Is, If(left.IsNothingLiteral, right, left), node.Type)
End Function
Private Function RewriteNullableIsOrIsNotOperator(isIs As Boolean, operand As BoundExpression, resultType As TypeSymbol) As BoundExpression
Debug.Assert(resultType.IsBooleanType())
Debug.Assert(operand.Type.IsNullableType)
Dim result As BoundExpression
If HasNoValue(operand) Then
result = New BoundLiteral(operand.Syntax,
Return New BoundLiteral(operand.Syntax,
If(isIs,
ConstantValue.True,
ConstantValue.False),
node.Type)
resultType)
ElseIf HasValue(operand) Then
result = MakeSequence(operand, New BoundLiteral(operand.Syntax,
Return MakeSequence(operand, New BoundLiteral(operand.Syntax,
If(isIs,
ConstantValue.False,
ConstantValue.True),
node.Type))
resultType))
Else
If operand.Kind = BoundKind.LoweredConditionalAccess Then
Dim conditional = DirectCast(operand, BoundLoweredConditionalAccess)
If HasNoValue(conditional.WhenNullOpt) Then
Return conditional.Update(conditional.ReceiverOrCondition,
conditional.CaptureReceiver,
conditional.PlaceholderId,
RewriteNullableIsOrIsNotOperator(isIs, conditional.WhenNotNull, resultType),
RewriteNullableIsOrIsNotOperator(isIs, conditional.WhenNullOpt, resultType),
resultType)
End If
End If
Dim result As BoundExpression
result = NullableHasValue(operand)
If isIs Then
result = New BoundUnaryOperator(result.Syntax,
UnaryOperatorKind.Not,
result,
False,
result.Type)
resultType)
End If
End If
Return result
Return result
End If
End Function
Private Function RewriteLiftedUserDefinedBinaryOperator(node As BoundUserDefinedBinaryOperator) As BoundNode
......
......@@ -27,191 +27,137 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Select
End Function
Private Function CanCaptureReferenceToConditionalAccessReceiver(receiver As BoundExpression) As Boolean
If receiver IsNot Nothing Then
Debug.Assert(receiver.Type.IsTypeParameter())
' We cannot capture readonly reference to an array element in a ByRef temp and requesting a writable refernce can fail.
Select Case receiver.Kind
Case BoundKind.ArrayAccess
Return False
Case BoundKind.Sequence
Return CanCaptureReferenceToConditionalAccessReceiver(DirectCast(receiver, BoundSequence).ValueOpt)
End Select
End If
Return True
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 factory = New SyntheticBoundNodeFactory(topMethod, currentMethodOrLambda, node.Syntax, compilationState, diagnostics)
Dim condition As BoundExpression
Dim first As BoundExpression
Dim receiverForAccess As BoundExpression
Dim structAccess As BoundExpression = Nothing
Dim notReferenceType As BoundExpression = Nothing
Dim receiverOrCondition 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 byRefLocal 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 ShouldCaptureConditionalAccessReceiver(rewrittenReceiver) Then
temp = New SynthesizedLocal(Me.currentMethodOrLambda, receiverType, SynthesizedLocalKind.LoweringTemp)
first = factory.Sequence(ImmutableArray(Of LocalSymbol).Empty,
ImmutableArray.Create(Of BoundExpression)(
factory.AssignmentExpression(factory.Local(temp, isLValue:=True),
rewrittenReceiver.MakeRValue())),
factory.Local(temp, isLValue:=True))
receiverForAccess = factory.Local(temp, isLValue:=True)
If HasNoValue(rewrittenReceiver) Then
' Nothing
receiverOrCondition = Nothing
needWhenNotNullPart = False
placeholderReplacement = Nothing
ElseIf HasValue(rewrittenReceiver) Then
' receiver. ... -> to Nullable
receiverOrCondition = Nothing
needWhenNullPart = False
placeholderReplacement = NullableValueOrDefault(rewrittenReceiver)
Else
first = rewrittenReceiver
receiverForAccess = rewrittenReceiver
End If
Dim first As BoundExpression
condition = NullableHasValue(first)
receiverForAccess = NullableValueOrDefault(receiverForAccess)
If ShouldCaptureConditionalAccessReceiver(rewrittenReceiver) Then
temp = New SynthesizedLocal(Me.currentMethodOrLambda, receiverType, SynthesizedLocalKind.LoweringTemp)
ElseIf receiverType.IsReferenceType Then
assignment = factory.AssignmentExpression(factory.Local(temp, isLValue:=True), rewrittenReceiver.MakeRValue())
first = factory.Local(temp, isLValue:=True)
placeholderReplacement = factory.Local(temp, isLValue:=True)
Else
first = rewrittenReceiver
placeholderReplacement = rewrittenReceiver
End If
' if( receiver IsNot Nothing, receiver. ... -> to Nullable, Nothing)
If ShouldCaptureConditionalAccessReceiver(rewrittenReceiver) Then
temp = New SynthesizedLocal(Me.currentMethodOrLambda, receiverType, SynthesizedLocalKind.LoweringTemp)
first = factory.AssignmentExpression(factory.Local(temp, isLValue:=True), rewrittenReceiver.MakeRValue())
receiverForAccess = factory.Local(temp, isLValue:=False)
Else
first = rewrittenReceiver.MakeRValue()
receiverForAccess = first
receiverOrCondition = NullableHasValue(first)
placeholderReplacement = NullableValueOrDefault(placeholderReplacement)
End If
condition = factory.ReferenceIsNotNothing(first)
'TODO: Figure out how to suppress calvirt on this receiver
captureReceiver = False
newPlaceHolder = Nothing
Else
Debug.Assert(Not receiverType.IsValueType)
Debug.Assert(receiverType.IsTypeParameter())
' if( receiver IsNot Nothing, receiver. ... -> to Nullable, Nothing)
If ShouldCaptureConditionalAccessReceiver(rewrittenReceiver) Then
notReferenceType = factory.ReferenceIsNotNothing(factory.DirectCast(factory.DirectCast(factory.Null(),
receiverType),
factory.SpecialType(SpecialType.System_Object)))
If Not Me.currentMethodOrLambda.IsAsync AndAlso Not Me.currentMethodOrLambda.IsIterator AndAlso
CanCaptureReferenceToConditionalAccessReceiver(rewrittenReceiver) Then
' We introduce a ByRef local, which will be used to refer to the receiver from within AccessExpression.
' Initially ByRefLocal is set to refer to the rewrittenReceiver location.
' The "receiver IsNot Nothing" check becomes
' Not <receiver's type is refernce type> OrElse { <capture value pointed to by ByRefLocal in a temp>, <store reference to the temp in ByRefLocal>, temp IsNot Nothing }
' Note that after that condition is executed, if it returns true, the ByRefLocal ponts to the captured value of the reference type, which is proven to be Not Nothing,
' and won't change after the null check (we own the local where the value is captured).
' Also, if receiver's type is value type ByRefLocal still points to the original location, which allows access side effects to be observed.
' The <receiver's type is refernce type> is performed by boxing default value of receiver's type and checking if it is a null reference. This makes Nullable
' type to be treated as a reference type, but it is Ok since it is immutable. The only strange thing is that we won't unwrap the nullable.
If rewrittenReceiver.IsConstant Then
receiverOrCondition = Nothing
captureReceiver = False
newPlaceHolder = Nothing
temp = New SynthesizedLocal(Me.currentMethodOrLambda, receiverType, SynthesizedLocalKind.LoweringTemp)
byRefLocal = New SynthesizedLocal(Me.currentMethodOrLambda, receiverType, SynthesizedLocalKind.LoweringTemp, isByRef:=True)
Dim capture As BoundExpression = factory.ReferenceAssignment(byRefLocal, rewrittenReceiver).MakeRValue()
condition = factory.LogicalOrElse(notReferenceType,
factory.Sequence(ImmutableArray(Of LocalSymbol).Empty,
ImmutableArray.Create(Of BoundExpression)(factory.AssignmentExpression(factory.Local(temp, isLValue:=True),
factory.Local(byRefLocal, isLValue:=False)),
factory.ReferenceAssignment(byRefLocal,
factory.Local(temp, isLValue:=True)).MakeRValue()),
factory.ReferenceIsNotNothing(factory.DirectCast(factory.Local(temp, isLValue:=False),
factory.SpecialType(SpecialType.System_Object)))))
condition = factory.Sequence(ImmutableArray(Of LocalSymbol).Empty,
ImmutableArray.Create(capture),
condition)
receiverForAccess = factory.Local(byRefLocal, isLValue:=False)
If rewrittenReceiver.ConstantValueOpt.IsNothing Then
' Nothing
placeholderReplacement = Nothing
needWhenNotNullPart = False
Else
' Async rewriter cannot handle the trick with ByRef local that we are doing above.
' For now we will duplicate access - one access for a value type case, one access for a class case.
' Value type case will use receiver as is.
AddPlaceholderReplacement(node.Placeholder, rewrittenReceiver.MakeRValue())
structAccess = VisitExpressionNode(node.AccessExpression)
RemovePlaceholderReplacement(node.Placeholder)
If Not node.Type.IsVoidType() AndAlso Not structAccess.Type.IsNullableType() AndAlso structAccess.Type.IsValueType Then
structAccess = WrapInNullable(structAccess, node.Type)
End If
' Class case is handled by capturing value in a temp
temp = New SynthesizedLocal(Me.currentMethodOrLambda, receiverType, SynthesizedLocalKind.LoweringTemp)
condition = factory.ReferenceIsNotNothing(
factory.DirectCast(factory.AssignmentExpression(factory.Local(temp, isLValue:=True), rewrittenReceiver.MakeRValue()),
factory.SpecialType(SpecialType.System_Object)))
receiverForAccess = factory.Local(temp, isLValue:=False)
' receiver. ... -> to Nullable
placeholderReplacement = rewrittenReceiver.MakeRValue()
needWhenNullPart = False
End If
Else
condition = factory.ReferenceIsNotNothing(factory.DirectCast(rewrittenReceiver.MakeRValue(), factory.SpecialType(SpecialType.System_Object)))
receiverForAccess = rewrittenReceiver
' if( receiver IsNot Nothing, receiver. ... -> to Nullable, Nothing)
receiverOrCondition = rewrittenReceiver
captureReceiver = ShouldCaptureConditionalAccessReceiver(rewrittenReceiver)
Me.conditionalAccessReceiverPlaceholderId += 1
newPlaceholderId = Me.conditionalAccessReceiverPlaceholderId
Debug.Assert(newPlaceholderId <> 0)
newPlaceHolder = New BoundConditionalAccessReceiverPlaceholder(node.Placeholder.Syntax, newPlaceholderId, node.Placeholder.Type)
placeholderReplacement = newPlaceHolder
End If
End If
AddPlaceholderReplacement(node.Placeholder, receiverForAccess.MakeRValue())
Dim whenTrue As BoundExpression = VisitExpressionNode(node.AccessExpression)
RemovePlaceholderReplacement(node.Placeholder)
Dim whenNotNull As BoundExpression
Dim accessResultType As TypeSymbol = node.AccessExpression.Type
Dim whenFalse As BoundExpression
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
If node.Type.IsVoidType() Then
whenFalse = New BoundSequence(node.Syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray(Of BoundExpression).Empty, Nothing, node.Type)
Dim whenNull As BoundExpression
If Not whenTrue.Type.IsVoidType() Then
whenTrue = New BoundSequence(whenTrue.Syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(whenTrue), Nothing, node.Type)
End If
If node.Type.IsVoidType() Then
whenNull = Nothing
Else
If Not whenTrue.Type.IsNullableType() AndAlso whenTrue.Type.IsValueType Then
whenTrue = WrapInNullable(whenTrue, node.Type)
If needWhenNotNullPart AndAlso Not accessResultType.IsNullableType() AndAlso accessResultType.IsValueType Then
whenNotNull = WrapInNullable(whenNotNull, node.Type)
End If
whenFalse = If(whenTrue.Type.IsNullableType(), NullableNull(node.Syntax, whenTrue.Type), factory.Null(whenTrue.Type))
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
Dim result As BoundExpression = TransformRewrittenTernaryConditionalExpression(factory.TernaryConditionalExpression(condition, whenTrue, whenFalse))
If structAccess IsNot Nothing Then
' Result now handles class case. let's join it with the struct case
result = TransformRewrittenTernaryConditionalExpression(factory.TernaryConditionalExpression(notReferenceType, structAccess, result))
End If
If temp IsNot Nothing Then
Dim temporaries As ImmutableArray(Of LocalSymbol)
Debug.Assert(needWhenNotNullPart OrElse needWhenNullPart)
If byRefLocal IsNot Nothing Then
temporaries = ImmutableArray.Create(temp, byRefLocal)
If needWhenNotNullPart Then
If needWhenNullPart Then
result = New BoundLoweredConditionalAccess(node.Syntax, receiverOrCondition, captureReceiver, newPlaceholderId, whenNotNull, whenNull, node.Type)
Else
temporaries = ImmutableArray.Create(temp)
Debug.Assert(receiverOrCondition Is Nothing)
Debug.Assert(newPlaceHolder Is Nothing)
result = whenNotNull
End If
ElseIf whenNull IsNot Nothing Then
Debug.Assert(receiverOrCondition Is Nothing)
result = whenNull
Else
Debug.Assert(receiverOrCondition 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, temporaries, ImmutableArray.Create(result), Nothing, result.Type)
result = New BoundSequence(node.Syntax, ImmutableArray.Create(temp), ImmutableArray.Create(assignment, result), Nothing, result.Type)
Else
result = New BoundSequence(node.Syntax, temporaries, ImmutableArray(Of BoundExpression).Empty, result, result.Type)
result = New BoundSequence(node.Syntax, ImmutableArray.Create(temp), ImmutableArray.Create(assignment), result, result.Type)
End If
End If
......
......@@ -210,18 +210,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
' == rewrite operands and check for trivial cases
Dim rewrittenLeft = Me.VisitExpressionNode(node.TestExpression)
If HasValue(rewrittenLeft) Then
If node.ConvertedTestExpression Is Nothing Then
Return NullableValueOrDefault(rewrittenLeft)
Else
If rewrittenLeft.Type.IsSameTypeIgnoringCustomModifiers(node.ConvertedTestExpression.Type) Then
' Optimization
Return rewrittenLeft
End If
Return VisitExpressionNode(node.ConvertedTestExpression,
node.TestExpressionPlaceholder,
NullableValueOrDefault(rewrittenLeft))
End If
Return MakeResultFromNonNullLeft(rewrittenLeft, node.ConvertedTestExpression, node.TestExpressionPlaceholder)
End If
Dim rewrittenRight = Me.VisitExpressionNode(node.ElseExpression)
......@@ -229,6 +218,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return rewrittenRight
End If
If rewrittenLeft.Kind = BoundKind.LoweredConditionalAccess Then
Dim conditional = DirectCast(rewrittenLeft, BoundLoweredConditionalAccess)
If HasNoValue(conditional.WhenNullOpt) Then
If HasValue(conditional.WhenNotNull) Then
Return conditional.Update(conditional.ReceiverOrCondition,
conditional.CaptureReceiver,
conditional.PlaceholderId,
MakeResultFromNonNullLeft(conditional.WhenNotNull, node.ConvertedTestExpression, node.TestExpressionPlaceholder),
rewrittenRight,
node.Type)
Else
Debug.Assert(Not HasNoValue(conditional.WhenNotNull)) ' Not optimizing for this case
' CONSIDER: We could do inlining when rewrittenRight.IsConstant
End If
End If
End If
'=== Rewrite binary conditional expression using ternary conditional expression
Dim temp As SynthesizedLocal = Nothing
Dim tempInit As BoundExpression = Nothing
......@@ -270,6 +279,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return result
End Function
Private Function MakeResultFromNonNullLeft(rewrittenLeft As BoundExpression, convertedTestExpression As BoundExpression, testExpressionPlaceholder As BoundRValuePlaceholder) As BoundExpression
If convertedTestExpression Is Nothing Then
Return NullableValueOrDefault(rewrittenLeft)
Else
If rewrittenLeft.Type.IsSameTypeIgnoringCustomModifiers(convertedTestExpression.Type) Then
' Optimization
Return rewrittenLeft
End If
Return VisitExpressionNode(convertedTestExpression,
testExpressionPlaceholder,
NullableValueOrDefault(rewrittenLeft))
End If
End Function
Private Function VisitExpressionNode(node As BoundExpression,
placeholder As BoundValuePlaceholderBase,
placeholderSubstitute As BoundExpression) As BoundExpression
......
......@@ -284,6 +284,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
' converting null
Return NullableNull(result.Syntax, resultType)
Else
If rewrittenOperand.Kind = BoundKind.LoweredConditionalAccess Then
Dim conditional = DirectCast(rewrittenOperand, BoundLoweredConditionalAccess)
If HasValue(conditional.WhenNotNull) AndAlso HasNoValue(conditional.WhenNullOpt) Then
Return conditional.Update(conditional.ReceiverOrCondition,
conditional.CaptureReceiver,
conditional.PlaceholderId,
FinishRewriteNullableConversion(node, resultType, NullableValueOrDefault(conditional.WhenNotNull), Nothing, Nothing, Nothing),
NullableNull(result.Syntax, resultType),
resultType)
End If
End If
' uncaptured locals are safe here because we are dealing with a single operand
result = ProcessNullableOperand(rewrittenOperand, operandHasValue, temps, inits, doNotCaptureLocals:=True)
End If
......@@ -293,17 +306,30 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End If
End If
Return FinishRewriteNullableConversion(node, resultType, result, operandHasValue, temps, inits)
End Function
Private Function FinishRewriteNullableConversion(
node As BoundConversion,
resultType As TypeSymbol,
operand As BoundExpression,
operandHasValue As BoundExpression,
temps As ArrayBuilder(Of LocalSymbol),
inits As ArrayBuilder(Of BoundExpression)
) As BoundExpression
Debug.Assert(resultType Is node.Type)
Dim unwrappedResultType = resultType.GetNullableUnderlyingTypeOrSelf
' apply unlifted conversion
If Not result.Type.IsSameTypeIgnoringCustomModifiers(unwrappedResultType) Then
If Not operand.Type.IsSameTypeIgnoringCustomModifiers(unwrappedResultType) Then
Dim useSiteDiagnostics As HashSet(Of DiagnosticInfo) = Nothing
Dim convKind = Conversions.ClassifyConversion(result.Type, unwrappedResultType, useSiteDiagnostics).Key
Dim convKind = Conversions.ClassifyConversion(operand.Type, unwrappedResultType, useSiteDiagnostics).Key
Debug.Assert(Conversions.ConversionExists(convKind))
diagnostics.Add(node, useSiteDiagnostics)
result = TransformRewrittenConversion(
operand = TransformRewrittenConversion(
New BoundConversion(node.Syntax,
result,
operand,
convKind,
node.Checked,
node.ExplicitCastInCode,
......@@ -316,27 +342,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
' wrap if needed
If resultType.IsNullableType Then
result = WrapInNullable(result, resultType)
operand = WrapInNullable(operand, resultType)
' propagate null from the operand
If operandHasValue IsNot Nothing Then
result = MakeTernaryConditionalExpression(node.Syntax,
operand = MakeTernaryConditionalExpression(node.Syntax,
operandHasValue,
result,
NullableNull(result.Syntax, resultType))
operand,
NullableNull(operand.Syntax, resultType))
' if used temps, arrange a sequence for temps and inits.
If temps IsNot Nothing Then
result = New BoundSequence(result.Syntax,
operand = New BoundSequence(operand.Syntax,
temps.ToImmutableAndFree,
inits.ToImmutableAndFree,
result,
result.Type)
operand,
operand.Type)
End If
End If
End If
Return result
Return operand
End Function
Private Function RewriteNullableReferenceConversion(node As BoundConversion,
......
......@@ -24,6 +24,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return New BoundLiteral(node.Syntax, ConstantValue.False, node.Type)
End If
If operand.Kind = BoundKind.LoweredConditionalAccess Then
Dim conditional = DirectCast(operand, BoundLoweredConditionalAccess)
If HasNoValue(conditional.WhenNullOpt) Then
Debug.Assert(Not HasNoValue(conditional.WhenNotNull))
Return conditional.Update(conditional.ReceiverOrCondition,
conditional.CaptureReceiver,
conditional.PlaceholderId,
NullableValueOrDefault(conditional.WhenNotNull),
New BoundLiteral(node.Syntax, ConstantValue.False, node.Type),
node.Type)
End If
End If
Return NullableValueOrDefault(operand)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册