/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Binding/Binder_WithBlock.vb
421 строка
21 KB
Fred Silberberg
Do not resolve diagnostics early during attribute binding (#71140)
15 дек 2023, 22:36
Не верифицирован
15 дек 2023, 22:36
38f6bcf
Код
Авторство
О чём код?
' 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 System.Threading Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports TypeKind = Microsoft.CodeAnalysis.TypeKind Namespace Microsoft.CodeAnalysis.VisualBasic ''' <summary> ''' Binder used to bind statements inside With blocks. ''' </summary> Friend NotInheritable Class WithBlockBinder Inherits BlockBaseBinder ''' <summary> Reference to a With statement syntax this binder is created for </summary> Private ReadOnly _withBlockSyntax As WithBlockSyntax ''' <summary> Reference to an expression from With statement </summary> Private ReadOnly Property Expression As ExpressionSyntax Get Return Me._withBlockSyntax.WithStatement.Expression End Get End Property ''' <summary> ''' Holds information needed by With block to properly bind ''' references to With block expression placeholder ''' </summary> Private _withBlockInfo As WithBlockInfo = Nothing Friend ReadOnly Property Info As WithBlockInfo Get Return _withBlockInfo End Get End Property ''' <summary> ''' True if there were references to the With statement expression ''' placeholder which prevent ByRef local from being used ''' </summary> Friend ReadOnly Property ExpressionIsAccessedFromNestedLambda As Boolean Get Debug.Assert(Me._withBlockInfo IsNot Nothing) Return Me._withBlockInfo.ExpressionIsAccessedFromNestedLambda End Get End Property ''' <summary> ''' With statement expression placeholder is a bound node being used in initial binding ''' to represent with statement expression. In lowering it is to be replaced with ''' the lowered expression which will actually be emitted. ''' </summary> Friend ReadOnly Property ExpressionPlaceholder As BoundValuePlaceholderBase Get Debug.Assert(Me._withBlockInfo IsNot Nothing) Return Me._withBlockInfo.ExpressionPlaceholder End Get End Property ''' <summary> ''' A draft version of initializers which will be used in this With statement. ''' Initializers are expressions which are used to capture expression in the current ''' With statement; they can be empty in some cases like if the expression is a local ''' variable of value type. ''' ''' Note, the initializers returned by this property are 'draft' because they are ''' generated based on initial bound tree, the real initializers will be generated ''' in lowering based on lowered expression form. ''' </summary> Friend ReadOnly Property DraftInitializers As ImmutableArray(Of BoundExpression) Get Debug.Assert(Me._withBlockInfo IsNot Nothing) Return Me._withBlockInfo.DraftInitializers End Get End Property ''' <summary> ''' A draft version of placeholder substitute which will be used in this With statement. ''' ''' Note, the placeholder substitute returned by this property is 'draft' because it is ''' generated based on initial bound tree, the real substitute will be generated in lowering ''' based on lowered expression form. ''' </summary> Friend ReadOnly Property DraftPlaceholderSubstitute As BoundExpression Get Debug.Assert(Me._withBlockInfo IsNot Nothing) Return Me._withBlockInfo.DraftSubstitute End Get End Property ''' <summary> Holds information needed by With block to properly bind ''' references to With block expression, placeholder, etc... </summary> Friend Class WithBlockInfo Public Sub New(originalExpression As BoundExpression, expressionPlaceholder As BoundValuePlaceholderBase, draftSubstitute As BoundExpression, draftInitializers As ImmutableArray(Of BoundExpression), capturedLvalueByRefCallOrProperty As BoundExpression, diagnostics As ReadOnlyBindingDiagnostic(Of AssemblySymbol)) Debug.Assert(originalExpression IsNot Nothing) Debug.Assert(expressionPlaceholder IsNot Nothing AndAlso (expressionPlaceholder.Kind = BoundKind.WithLValueExpressionPlaceholder OrElse expressionPlaceholder.Kind = BoundKind.WithRValueExpressionPlaceholder)) Debug.Assert(draftSubstitute IsNot Nothing) Debug.Assert(Not draftInitializers.IsDefault) Me.OriginalExpression = originalExpression Me.ExpressionPlaceholder = expressionPlaceholder Me.DraftSubstitute = draftSubstitute Me.CapturedLvalueByRefCallOrProperty = capturedLvalueByRefCallOrProperty Me.DraftInitializers = draftInitializers Me.Diagnostics = diagnostics End Sub ''' <summary> Original bound expression from With statement </summary> Public ReadOnly OriginalExpression As BoundExpression ''' <summary> Bound placeholder expression if used, otherwise Nothing </summary> Public ReadOnly ExpressionPlaceholder As BoundValuePlaceholderBase ''' <summary> Diagnostics produced while binding the expression </summary> Public ReadOnly Diagnostics As ReadOnlyBindingDiagnostic(Of AssemblySymbol) ''' <summary> ''' Draft initializers for With statement, is based on initial binding tree ''' and is only to be used for warnings generation as well as for flow analysis ''' and semantic API; real initializers will be re-calculated in lowering ''' </summary> Public ReadOnly DraftInitializers As ImmutableArray(Of BoundExpression) ''' <summary> ''' Draft substitute for With expression placeholder, is based on initial ''' binding tree and is only to be used for warnings generation as well as ''' for flow analysis and semantic API; real substitute will be re-calculated ''' in lowering ''' </summary> Public ReadOnly DraftSubstitute As BoundExpression Public ReadOnly CapturedLvalueByRefCallOrProperty As BoundExpression Public ReadOnly Property ExpressionIsAccessedFromNestedLambda As Boolean Get Return Me._exprAccessedFromNestedLambda = ThreeState.True End Get End Property Public Sub RegisterAccessFromNestedLambda() If Me._exprAccessedFromNestedLambda <> ThreeState.True Then Dim oldValue = Interlocked.CompareExchange(Me._exprAccessedFromNestedLambda, ThreeState.True, ThreeState.Unknown) Debug.Assert(oldValue = ThreeState.Unknown OrElse oldValue = ThreeState.True) End If End Sub Private _exprAccessedFromNestedLambda As Integer = ThreeState.Unknown ''' <summary> ''' If With statement expression is being used from nested lambda there are some restrictions ''' to the usage of Me reference in this expression. As these restrictions are only to be checked ''' in few scenarios, this flag is being calculated lazily. ''' </summary> Public Function ExpressionHasByRefMeReference(recursionDepth As Integer) As Boolean If Me._exprHasByRefMeReference = ThreeState.Unknown Then ' Analyze the expression which will be used instead of placeholder Dim value As Boolean = ValueTypedMeReferenceFinder.HasByRefMeReference(Me.DraftSubstitute, recursionDepth) Dim newValue As Integer = If(value, ThreeState.True, ThreeState.False) Dim oldValue = Interlocked.CompareExchange(Me._exprHasByRefMeReference, newValue, ThreeState.Unknown) Debug.Assert(newValue = oldValue OrElse oldValue = ThreeState.Unknown) End If Debug.Assert(Me._exprHasByRefMeReference <> ThreeState.Unknown) Return Me._exprHasByRefMeReference = ThreeState.True End Function Private _exprHasByRefMeReference As Integer = ThreeState.Unknown End Class ''' <summary> Create a new instance of With statement binder for a statement syntax provided </summary> Public Sub New(enclosing As Binder, syntax As WithBlockSyntax) MyBase.New(enclosing) Debug.Assert(syntax IsNot Nothing) Debug.Assert(syntax.WithStatement IsNot Nothing) Debug.Assert(syntax.WithStatement.Expression IsNot Nothing) Me._withBlockSyntax = syntax End Sub #Region "Implementation" Friend Overrides Function GetWithStatementPlaceholderSubstitute(placeholder As BoundValuePlaceholderBase) As BoundExpression Me.EnsureExpressionAndPlaceholder() If placeholder Is Me.ExpressionPlaceholder Then Return Me.DraftPlaceholderSubstitute End If Return MyBase.GetWithStatementPlaceholderSubstitute(placeholder) End Function Private Sub EnsureExpressionAndPlaceholder() If Me._withBlockInfo Is Nothing Then ' Because we cannot guarantee that diagnostics will be freed we ' don't allocate this diagnostics bag from a pool Dim diagnostics = BindingDiagnosticBag.GetInstance() ' Bind the expression as a value Dim boundExpression As BoundExpression = Me.ContainingBinder.BindValue(Me.Expression, diagnostics) ' NOTE: If the expression is not an l-value we should make an r-value of it If Not boundExpression.IsLValue Then boundExpression = Me.MakeRValue(boundExpression, diagnostics) Else Dim propertyAccess = TryCast(boundExpression, BoundPropertyAccess) If propertyAccess IsNot Nothing Then WarnOnRecursiveAccess(propertyAccess, PropertyAccessKind.Get, diagnostics) boundExpression = propertyAccess.SetAccessKind(PropertyAccessKind.Get) End If End If ' Prepare draft substitute/initializers for expression placeholder; ' note that those substitute/initializers will be based on initial bound ' form of the original expression captured without using ByRef locals Dim result As WithExpressionRewriter.Result = (New WithExpressionRewriter(Me._withBlockSyntax.WithStatement)).AnalyzeWithExpression(Me.ContainingMember, boundExpression, doNotUseByRefLocal:=True, isDraftRewrite:=True, binder:=Me.ContainingBinder, preserveIdentityOfLValues:=True) ' Create a placeholder if needed Dim placeholder As BoundValuePlaceholderBase = Nothing If boundExpression.IsLValue OrElse boundExpression.IsMeReference Then placeholder = New BoundWithLValueExpressionPlaceholder(Me.Expression, boundExpression.Type) Else placeholder = New BoundWithRValueExpressionPlaceholder(Me.Expression, boundExpression.Type) End If placeholder.SetWasCompilerGenerated() ' It is assumed that the binding result in case of race should still be the same in all racing threads, ' so if the following call fails we can just drop the bound node and diagnostics on the floor Interlocked.CompareExchange(Me._withBlockInfo, New WithBlockInfo(boundExpression, placeholder, result.Expression, result.Initializers, result.CapturedLvalueByRefCallOrProperty, diagnostics.ToReadOnlyAndFree()), Nothing) End If Debug.Assert(Me._withBlockInfo IsNot Nothing) End Sub ''' <summary> ''' A bound tree walker which search for a bound Me and MyClass references of value type. ''' Is being only used for calculating the value of 'ExpressionHasByRefMeReference' ''' </summary> Private Class ValueTypedMeReferenceFinder Inherits BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator Private Sub New(recursionDepth As Integer) MyBase.New(recursionDepth) End Sub Private _found As Boolean = False Public Shared Function HasByRefMeReference(expression As BoundExpression, recursionDepth As Integer) As Boolean Dim walker As New ValueTypedMeReferenceFinder(recursionDepth) walker.Visit(expression) Return walker._found End Function Public Overrides Function Visit(node As BoundNode) As BoundNode If Not _found Then Return MyBase.Visit(node) End If Return Nothing End Function Public Overrides Function VisitMeReference(node As BoundMeReference) As BoundNode Dim type As TypeSymbol = node.Type Debug.Assert(Not type.IsTypeParameter) Debug.Assert(type.IsValueType) Me._found = True Return Nothing End Function Public Overrides Function VisitMyClassReference(node As BoundMyClassReference) As BoundNode Dim type As TypeSymbol = node.Type Debug.Assert(Not type.IsTypeParameter) Debug.Assert(type.IsValueType) Me._found = True Return Nothing End Function End Class #End Region #Region "With block binding" Protected Overrides Function CreateBoundWithBlock(node As WithBlockSyntax, boundBlockBinder As Binder, diagnostics As BindingDiagnosticBag) As BoundStatement Debug.Assert(node Is Me._withBlockSyntax) ' Bind With statement expression Me.EnsureExpressionAndPlaceholder() ' We need to take care of possible diagnostics that might be produced ' by EnsureExpressionAndPlaceholder call, note that this call might have ' been before in which case the diagnostics were stored in '_withBlockInfo' ' See also comment in PrepareBindingOfOmittedLeft(...) diagnostics.AddRange(Me._withBlockInfo.Diagnostics, allowMismatchInDependencyAccumulation:=True) Dim result = New BoundWithStatement(node, Me._withBlockInfo.OriginalExpression, boundBlockBinder.BindBlock(node, node.Statements, diagnostics).MakeCompilerGenerated(), Me) If Me._withBlockInfo.CapturedLvalueByRefCallOrProperty IsNot Nothing Then Dim containingMethod = TryCast(ContainingMember, MethodSymbol) If (containingMethod IsNot Nothing AndAlso (containingMethod.IsIterator OrElse containingMethod.IsAsync)) OrElse result.Binder.ExpressionIsAccessedFromNestedLambda Then ReportDiagnostic(diagnostics, Me._withBlockInfo.CapturedLvalueByRefCallOrProperty.Syntax, ERRID.ERR_UnsupportedRefReturningCallInWithStatement) End If End If Return result End Function #End Region #Region "Other Overrides" ''' <summary> Asserts that the node is NOT from With statement expression </summary> <Conditional("DEBUG")> Private Sub AssertExpressionIsNotFromStatementExpression(node As SyntaxNode) While node IsNot Nothing Debug.Assert(node IsNot Me.Expression) node = node.Parent End While End Sub #If DEBUG Then Public Overrides Function BindStatement(node As StatementSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement AssertExpressionIsNotFromStatementExpression(node) Return MyBase.BindStatement(node, diagnostics) End Function Public Overrides Function GetBinder(node As SyntaxNode) As Binder AssertExpressionIsNotFromStatementExpression(node) Return MyBase.GetBinder(node) End Function #End If Private Sub PrepareBindingOfOmittedLeft(node As VisualBasicSyntaxNode, diagnostics As BindingDiagnosticBag, accessingBinder As Binder) AssertExpressionIsNotFromStatementExpression(node) Debug.Assert((node.Kind = SyntaxKind.SimpleMemberAccessExpression) OrElse (node.Kind = SyntaxKind.DictionaryAccessExpression) OrElse (node.Kind = SyntaxKind.XmlAttributeAccessExpression) OrElse (node.Kind = SyntaxKind.XmlElementAccessExpression) OrElse (node.Kind = SyntaxKind.XmlDescendantAccessExpression) OrElse (node.Kind = SyntaxKind.ConditionalAccessExpression)) Me.EnsureExpressionAndPlaceholder() ' NOTE: In case the call above produced diagnostics they were stored in ' '_withBlockInfo' and will be reported in later call to CreateBoundWithBlock(...) Dim info As WithBlockInfo = Me._withBlockInfo If Me.ContainingMember IsNot accessingBinder.ContainingMember Then ' The expression placeholder from With statement may be captured info.RegisterAccessFromNestedLambda() End If End Sub Protected Friend Overrides Function TryBindOmittedLeftForMemberAccess(node As MemberAccessExpressionSyntax, diagnostics As BindingDiagnosticBag, accessingBinder As Binder, <Out> ByRef wholeMemberAccessExpressionBound As Boolean) As BoundExpression PrepareBindingOfOmittedLeft(node, diagnostics, accessingBinder) wholeMemberAccessExpressionBound = False Return Me._withBlockInfo.ExpressionPlaceholder End Function Protected Overrides Function TryBindOmittedLeftForDictionaryAccess(node As MemberAccessExpressionSyntax, accessingBinder As Binder, diagnostics As BindingDiagnosticBag) As BoundExpression PrepareBindingOfOmittedLeft(node, diagnostics, accessingBinder) Return Me._withBlockInfo.ExpressionPlaceholder End Function Protected Overrides Function TryBindOmittedLeftForConditionalAccess(node As ConditionalAccessExpressionSyntax, accessingBinder As Binder, diagnostics As BindingDiagnosticBag) As BoundExpression PrepareBindingOfOmittedLeft(node, diagnostics, accessingBinder) Return Me._withBlockInfo.ExpressionPlaceholder End Function Protected Friend Overrides Function TryBindOmittedLeftForXmlMemberAccess(node As XmlMemberAccessExpressionSyntax, diagnostics As BindingDiagnosticBag, accessingBinder As Binder) As BoundExpression PrepareBindingOfOmittedLeft(node, diagnostics, accessingBinder) Return Me._withBlockInfo.ExpressionPlaceholder End Function Friend Overrides ReadOnly Property Locals As ImmutableArray(Of LocalSymbol) Get Return ImmutableArray(Of LocalSymbol).Empty End Get End Property #End Region End Class End Namespace