|
' 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
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.SyntaxFacts
Namespace Microsoft.CodeAnalysis.VisualBasic
' This portion of the binder converts an ExpressionSyntax into a BoundExpression
Partial Friend Class Binder
' !!! PLEASE KEEP BindExpression FUNCTION AT THE TOP !!!
Public Function BindExpression(
node As ExpressionSyntax,
diagnostics As BindingDiagnosticBag
) As BoundExpression
Return BindExpression(node, False, False, False, diagnostics)
End Function
''' <summary>
''' The dispatcher method that handles syntax nodes for all stand-alone expressions.
''' </summary>
Public Function BindExpression(
node As ExpressionSyntax,
isInvocationOrAddressOf As Boolean,
isOperandOfConditionalBranch As Boolean,
eventContext As Boolean,
diagnostics As BindingDiagnosticBag
) As BoundExpression
If IsEarlyAttributeBinder AndAlso Not EarlyWellKnownAttributeBinder.CanBeValidAttributeArgument(node, Me) Then
Return BadExpression(node, ErrorTypeSymbol.UnknownResultType)
End If
#Disable Warning BC40000 ' Type or member is obsolete
Dim result As BoundExpression = BindExpressionCore(node, isInvocationOrAddressOf, isOperandOfConditionalBranch, eventContext, diagnostics)
#Enable Warning BC40000 ' Type or member is obsolete
If IsEarlyAttributeBinder AndAlso result.Kind = BoundKind.MethodGroup AndAlso Not IsNameOfArgument(node) Then
Dim boundMethodGroup = DirectCast(result, BoundMethodGroup)
Dim compilation As VisualBasicCompilation = Me.Compilation
For Each method In boundMethodGroup.Methods
If Not EarlyWellKnownAttributeBinder.IsConstantOptimizableLibraryMethod(compilation, method) Then
Return BadExpression(node, ErrorTypeSymbol.UnknownResultType)
End If
Next
End If
Return result
End Function
<Obsolete("Use BindExpression that is immediately above instead.")>
Private Function BindExpressionCore(node As ExpressionSyntax, isInvocationOrAddressOf As Boolean, isOperandOfConditionalBranch As Boolean, eventContext As Boolean, diagnostics As BindingDiagnosticBag) As BoundExpression
Select Case node.Kind
Case SyntaxKind.MeExpression
Return BindMeExpression(DirectCast(node, MeExpressionSyntax), diagnostics)
Case SyntaxKind.MyBaseExpression
Return BindMyBaseExpression(DirectCast(node, MyBaseExpressionSyntax), diagnostics)
Case SyntaxKind.MyClassExpression
Return BindMyClassExpression(DirectCast(node, MyClassExpressionSyntax), diagnostics)
Case SyntaxKind.IdentifierName, SyntaxKind.GenericName
Return BindSimpleName(DirectCast(node, SimpleNameSyntax), isInvocationOrAddressOf, diagnostics)
Case SyntaxKind.PredefinedType, SyntaxKind.NullableType
Return BindNamespaceOrTypeExpression(DirectCast(node, TypeSyntax), diagnostics)
Case SyntaxKind.SimpleMemberAccessExpression
Return BindMemberAccess(DirectCast(node, MemberAccessExpressionSyntax), eventContext, diagnostics:=diagnostics)
Case SyntaxKind.DictionaryAccessExpression
Return BindDictionaryAccess(DirectCast(node, MemberAccessExpressionSyntax), diagnostics)
Case SyntaxKind.InvocationExpression
Return BindInvocationExpression(DirectCast(node, InvocationExpressionSyntax), diagnostics)
Case SyntaxKind.CollectionInitializer
Return BindArrayLiteralExpression(DirectCast(node, CollectionInitializerSyntax), diagnostics)
Case SyntaxKind.AnonymousObjectCreationExpression
Return BindAnonymousObjectCreationExpression(DirectCast(node, AnonymousObjectCreationExpressionSyntax), diagnostics)
Case SyntaxKind.ArrayCreationExpression
Return BindArrayCreationExpression(DirectCast(node, ArrayCreationExpressionSyntax), diagnostics)
Case SyntaxKind.ObjectCreationExpression
Return BindObjectCreationExpression(DirectCast(node, ObjectCreationExpressionSyntax), diagnostics)
Case SyntaxKind.NumericLiteralExpression,
SyntaxKind.StringLiteralExpression,
SyntaxKind.CharacterLiteralExpression,
SyntaxKind.TrueLiteralExpression,
SyntaxKind.FalseLiteralExpression,
SyntaxKind.NothingLiteralExpression,
SyntaxKind.DateLiteralExpression
Return BindLiteralConstant(DirectCast(node, LiteralExpressionSyntax), diagnostics)
Case SyntaxKind.ParenthesizedExpression
' Parenthesis tokens are ignored, and operand is bound in the context of
' parent expression.
' Dev10 allows parenthesized type expressions, let's bind as a general expression first.
Dim operand As BoundExpression = BindExpression(DirectCast(node, ParenthesizedExpressionSyntax).Expression,
isInvocationOrAddressOf:=False,
isOperandOfConditionalBranch:=isOperandOfConditionalBranch,
eventContext, diagnostics)
If operand.Kind = BoundKind.TypeExpression Then
Dim asType = DirectCast(operand, BoundTypeExpression)
Return New BoundTypeExpression(node, asType.UnevaluatedReceiverOpt, asType.AliasOpt, operand.Type, operand.HasErrors)
ElseIf operand.Kind = BoundKind.ArrayLiteral Then
' Convert the array literal to its inferred array type, reporting warnings/errors if necessary.
' It would have been nice to put this reclassification in ReclassifyAsValue, however, that is called in too many situations. We only
' want to reclassify the array literal this early when it is within parentheses.
Dim arrayLiteral = DirectCast(operand, BoundArrayLiteral)
Dim reclassified = ReclassifyArrayLiteralExpression(SyntaxKind.CTypeKeyword, arrayLiteral.Syntax, ConversionKind.Widening, False, arrayLiteral, arrayLiteral.InferredType, diagnostics)
Return New BoundParenthesized(node, reclassified, reclassified.Type)
Else
Return New BoundParenthesized(node, operand, operand.Type)
End If
Case SyntaxKind.UnaryPlusExpression,
SyntaxKind.UnaryMinusExpression,
SyntaxKind.NotExpression
Return BindUnaryOperator(DirectCast(node, UnaryExpressionSyntax), diagnostics)
Case SyntaxKind.AddExpression,
SyntaxKind.ConcatenateExpression,
SyntaxKind.LikeExpression,
SyntaxKind.EqualsExpression,
SyntaxKind.NotEqualsExpression,
SyntaxKind.LessThanOrEqualExpression,
SyntaxKind.GreaterThanOrEqualExpression,
SyntaxKind.LessThanExpression,
SyntaxKind.GreaterThanExpression,
SyntaxKind.SubtractExpression,
SyntaxKind.MultiplyExpression,
SyntaxKind.ExponentiateExpression,
SyntaxKind.DivideExpression,
SyntaxKind.ModuloExpression,
SyntaxKind.IntegerDivideExpression,
SyntaxKind.LeftShiftExpression,
SyntaxKind.RightShiftExpression,
SyntaxKind.ExclusiveOrExpression,
SyntaxKind.OrExpression,
SyntaxKind.OrElseExpression,
SyntaxKind.AndExpression,
SyntaxKind.AndAlsoExpression
Return BindBinaryOperator(DirectCast(node, BinaryExpressionSyntax), isOperandOfConditionalBranch, diagnostics)
Case SyntaxKind.IsExpression,
SyntaxKind.IsNotExpression
Return BindIsExpression(DirectCast(node, BinaryExpressionSyntax), diagnostics)
Case SyntaxKind.GetTypeExpression
Return BindGetTypeExpression(DirectCast(node, GetTypeExpressionSyntax), diagnostics)
Case SyntaxKind.NameOfExpression
Return BindNameOfExpression(DirectCast(node, NameOfExpressionSyntax), diagnostics)
Case SyntaxKind.AddressOfExpression
Return BindAddressOfExpression(node, diagnostics)
Case SyntaxKind.CTypeExpression,
SyntaxKind.TryCastExpression,
SyntaxKind.DirectCastExpression
Return BindCastExpression(DirectCast(node, CastExpressionSyntax), diagnostics)
Case SyntaxKind.PredefinedCastExpression
Return BindPredefinedCastExpression(DirectCast(node, PredefinedCastExpressionSyntax), diagnostics)
Case SyntaxKind.TypeOfIsExpression,
SyntaxKind.TypeOfIsNotExpression
Return BindTypeOfExpression(DirectCast(node, TypeOfExpressionSyntax), diagnostics)
Case SyntaxKind.BinaryConditionalExpression
Return BindBinaryConditionalExpression(DirectCast(node, BinaryConditionalExpressionSyntax), diagnostics)
Case SyntaxKind.TernaryConditionalExpression
Return BindTernaryConditionalExpression(DirectCast(node, TernaryConditionalExpressionSyntax), diagnostics)
Case SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression,
SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression
Return BindLambdaExpression(DirectCast(node, LambdaExpressionSyntax), diagnostics)
Case SyntaxKind.GlobalName
Return New BoundNamespaceExpression(node, Nothing, Compilation.GlobalNamespace)
Case SyntaxKind.QueryExpression
Return BindQueryExpression(DirectCast(node, QueryExpressionSyntax), diagnostics)
Case SyntaxKind.GroupAggregation
Return BindGroupAggregationExpression(DirectCast(node, GroupAggregationSyntax), diagnostics)
Case SyntaxKind.FunctionAggregation
Return BindFunctionAggregationExpression(DirectCast(node, FunctionAggregationSyntax), diagnostics)
Case SyntaxKind.NextLabel,
SyntaxKind.NumericLabel,
SyntaxKind.IdentifierLabel
Return BindLabel(DirectCast(node, LabelSyntax), diagnostics)
Case SyntaxKind.QualifiedName
' This code is not used during method body binding, but it might be used by SemanticModel for erroneous cases.
Return BindQualifiedName(DirectCast(node, QualifiedNameSyntax), diagnostics)
Case SyntaxKind.GetXmlNamespaceExpression
Return BindGetXmlNamespace(DirectCast(node, GetXmlNamespaceExpressionSyntax), diagnostics)
Case SyntaxKind.XmlComment
Return BindXmlComment(DirectCast(node, XmlCommentSyntax), rootInfoOpt:=Nothing, diagnostics:=diagnostics)
Case SyntaxKind.XmlDocument
Return BindXmlDocument(DirectCast(node, XmlDocumentSyntax), diagnostics)
Case SyntaxKind.XmlProcessingInstruction
Return BindXmlProcessingInstruction(DirectCast(node, XmlProcessingInstructionSyntax), diagnostics)
Case SyntaxKind.XmlEmptyElement
Return BindXmlEmptyElement(DirectCast(node, XmlEmptyElementSyntax), rootInfoOpt:=Nothing, diagnostics:=diagnostics)
Case SyntaxKind.XmlElement
Return BindXmlElement(DirectCast(node, XmlElementSyntax), rootInfoOpt:=Nothing, diagnostics:=diagnostics)
Case SyntaxKind.XmlEmbeddedExpression
' This case handles embedded expressions that are outside of XML
' literals (error cases). The parser will have reported BC31172
' already, so no error is reported here. (Valid uses of embedded
' expressions are handled explicitly in BindXmlElement, etc.)
Return BindXmlEmbeddedExpression(DirectCast(node, XmlEmbeddedExpressionSyntax), diagnostics:=diagnostics)
Case SyntaxKind.XmlCDataSection
Return BindXmlCData(DirectCast(node, XmlCDataSectionSyntax), rootInfoOpt:=Nothing, diagnostics:=diagnostics)
Case SyntaxKind.XmlElementAccessExpression
Return BindXmlElementAccess(DirectCast(node, XmlMemberAccessExpressionSyntax), diagnostics)
Case SyntaxKind.XmlAttributeAccessExpression
Return BindXmlAttributeAccess(DirectCast(node, XmlMemberAccessExpressionSyntax), diagnostics)
Case SyntaxKind.XmlDescendantAccessExpression
Return BindXmlDescendantAccess(DirectCast(node, XmlMemberAccessExpressionSyntax), diagnostics)
Case SyntaxKind.AwaitExpression
Return BindAwait(DirectCast(node, AwaitExpressionSyntax), diagnostics)
Case SyntaxKind.ConditionalAccessExpression
Return BindConditionalAccessExpression(DirectCast(node, ConditionalAccessExpressionSyntax), diagnostics)
Case SyntaxKind.InterpolatedStringExpression
Return BindInterpolatedStringExpression(DirectCast(node, InterpolatedStringExpressionSyntax), diagnostics)
Case SyntaxKind.TupleExpression
Return BindTupleExpression(DirectCast(node, TupleExpressionSyntax), diagnostics)
Case Else
' e.g. SyntaxKind.MidExpression is handled elsewhere
' NOTE: There were too many "else" cases to justify listing them explicitly and throwing on
' anything unexpected.
Debug.Assert(IsSemanticModelBinder OrElse node.ContainsDiagnostics, String.Format("Unexpected {0} syntax does not have diagnostics", node.Kind))
Return BadExpression(node, ImmutableArray(Of BoundExpression).Empty, ErrorTypeSymbol.UnknownResultType)
End Select
End Function
''' <summary>
''' Create a BoundBadExpression node for the given syntax node. No symbols or bound nodes are associated with it.
''' </summary>
Protected Shared Function BadExpression(node As SyntaxNode, resultType As TypeSymbol) As BoundBadExpression
Return New BoundBadExpression(node, LookupResultKind.Empty, ImmutableArray(Of Symbol).Empty, ImmutableArray(Of BoundExpression).Empty, resultType, hasErrors:=True)
End Function
''' <summary>
''' Create a BoundBadExpression node for the given child-expression, which is preserved as a sub-expression.
''' No ResultKind is associated
''' </summary>
Private Shared Function BadExpression(node As SyntaxNode, expr As BoundExpression, resultType As TypeSymbol) As BoundBadExpression
Return New BoundBadExpression(node, LookupResultKind.Empty, ImmutableArray(Of Symbol).Empty, ImmutableArray.Create(expr), resultType, hasErrors:=True)
End Function
''' <summary>
''' Create a BoundBadExpression node for the given child-expression, which is preserved as a sub-expression.
''' A ResultKind explains why the node is bad.
''' </summary>
Private Shared Function BadExpression(node As SyntaxNode, expr As BoundExpression, resultKind As LookupResultKind, resultType As TypeSymbol) As BoundBadExpression
Return New BoundBadExpression(node, resultKind, ImmutableArray(Of Symbol).Empty, ImmutableArray.Create(expr), resultType, hasErrors:=True)
End Function
''' <summary>
''' Create a BoundBadExpression node for the given child expression, which is preserved as a sub-expression. Symbols
''' associated with the child node are not given a result kind.
''' </summary>
Private Shared Function BadExpression(node As SyntaxNode, exprs As ImmutableArray(Of BoundExpression), resultType As TypeSymbol) As BoundBadExpression
Return New BoundBadExpression(node, LookupResultKind.Empty, ImmutableArray(Of Symbol).Empty, exprs, resultType, hasErrors:=True)
End Function
Private Shared Function BadExpression(expr As BoundExpression) As BoundBadExpression
Return BadExpression(LookupResultKind.Empty, expr)
End Function
' Use this for a bad expression with no known type, and a single child whose type and syntax is preserved as the type of the bad expression.
Private Shared Function BadExpression(resultKind As LookupResultKind, wrappedExpression As BoundExpression) As BoundBadExpression
Dim wrappedBadExpression As BoundBadExpression = TryCast(wrappedExpression, BoundBadExpression)
If wrappedBadExpression IsNot Nothing Then
Return New BoundBadExpression(wrappedBadExpression.Syntax, resultKind, wrappedBadExpression.Symbols, wrappedBadExpression.ChildBoundNodes, wrappedBadExpression.Type, hasErrors:=True)
Else
Return New BoundBadExpression(wrappedExpression.Syntax, resultKind, ImmutableArray(Of Symbol).Empty, ImmutableArray.Create(wrappedExpression), wrappedExpression.Type, hasErrors:=True)
End If
End Function
Private Function BindTupleExpression(node As TupleExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
Dim arguments As SeparatedSyntaxList(Of SimpleArgumentSyntax) = node.Arguments
Dim numElements As Integer = arguments.Count
If numElements < 2 Then
' this should be a parse error already.
Dim args = If(numElements = 1,
ImmutableArray.Create(BindRValue(arguments(0).Expression, diagnostics)),
ImmutableArray(Of BoundExpression).Empty)
Return BadExpression(node, args, ErrorTypeSymbol.UnknownResultType)
End If
Dim hasNaturalType = True
Dim hasInferredType = True
Dim boundArguments = ArrayBuilder(Of BoundExpression).GetInstance(arguments.Count)
Dim elementTypes = ArrayBuilder(Of TypeSymbol).GetInstance(arguments.Count)
Dim elementLocations = ArrayBuilder(Of Location).GetInstance(arguments.Count)
' prepare names
Dim names = ExtractTupleElementNames(arguments, diagnostics)
Dim elementNames = names.elementNames
Dim inferredPositions = names.inferredPositions
Dim hasErrors = names.hasErrors
' prepare types and locations
For i As Integer = 0 To numElements - 1
Dim argumentSyntax As SimpleArgumentSyntax = arguments(i)
Dim nameSyntax As IdentifierNameSyntax = argumentSyntax.NameColonEquals?.Name
If nameSyntax IsNot Nothing Then
elementLocations.Add(nameSyntax.GetLocation)
' check type character
Dim typeChar As TypeCharacter = nameSyntax.Identifier.GetTypeCharacter()
If typeChar <> TypeCharacter.None Then
ReportDiagnostic(diagnostics, nameSyntax, ERRID.ERR_TupleLiteralDisallowsTypeChar)
End If
Else
elementLocations.Add(argumentSyntax.GetLocation)
End If
Dim boundArgument As BoundExpression = BindValue(argumentSyntax.Expression, diagnostics)
Dim elementType = GetTupleFieldType(boundArgument, argumentSyntax, diagnostics, hasNaturalType)
If elementType Is Nothing Then
hasInferredType = False
End If
If boundArgument.Type IsNot Nothing Then
boundArgument = MakeRValue(boundArgument, diagnostics)
End If
boundArguments.Add(boundArgument)
elementTypes.Add(elementType)
Next
Dim elements = elementTypes.ToImmutableAndFree()
Dim locations = elementLocations.ToImmutableAndFree()
Dim inferredType As TupleTypeSymbol = Nothing
If hasInferredType Then
Dim disallowInferredNames = Me.Compilation.LanguageVersion.DisallowInferredTupleElementNames()
inferredType = TupleTypeSymbol.Create(node.GetLocation, elements, locations, elementNames, Me.Compilation,
shouldCheckConstraints:=True,
errorPositions:=If(disallowInferredNames, inferredPositions, Nothing),
syntax:=node, diagnostics:=diagnostics)
End If
Dim tupleTypeOpt As NamedTypeSymbol = If(hasNaturalType, inferredType, Nothing)
'' Always track the inferred positions in the bound node, so that conversions don't produce a warning
'' for "dropped names" when the name was inferred.
Return New BoundTupleLiteral(node, inferredType, elementNames, inferredPositions, boundArguments.ToImmutableAndFree(), tupleTypeOpt, hasErrors)
End Function
Private Shared Function ExtractTupleElementNames(arguments As SeparatedSyntaxList(Of SimpleArgumentSyntax), diagnostics As BindingDiagnosticBag) _
As (elementNames As ImmutableArray(Of String), inferredPositions As ImmutableArray(Of Boolean), hasErrors As Boolean)
Dim hasErrors = False
' set of names already used
Dim uniqueFieldNames = New HashSet(Of String)(IdentifierComparison.Comparer)
Dim elementNames As ArrayBuilder(Of String) = Nothing
Dim inferredElementNames As ArrayBuilder(Of String) = Nothing
' prepare and check element names and types
Dim numElements As Integer = arguments.Count
For i As Integer = 0 To numElements - 1
Dim argumentSyntax As SimpleArgumentSyntax = arguments(i)
Dim name As String = Nothing
Dim inferredName As String = Nothing
Dim nameSyntax As IdentifierNameSyntax = argumentSyntax.NameColonEquals?.Name
If nameSyntax IsNot Nothing Then
name = nameSyntax.Identifier.ValueText
If Not CheckTupleMemberName(name, i, argumentSyntax.NameColonEquals.Name, diagnostics, uniqueFieldNames) Then
hasErrors = True
End If
Else
inferredName = InferTupleElementName(argumentSyntax.Expression)
End If
CollectTupleFieldMemberName(name, i, numElements, elementNames)
CollectTupleFieldMemberName(inferredName, i, numElements, inferredElementNames)
Next
RemoveDuplicateInferredTupleNamesAndFreeIfEmptied(inferredElementNames, uniqueFieldNames)
Dim result = MergeTupleElementNames(elementNames, inferredElementNames)
elementNames?.Free()
inferredElementNames?.Free()
Return (result.names, result.inferred, hasErrors)
End Function
Private Shared Function MergeTupleElementNames(elementNames As ArrayBuilder(Of String),
inferredElementNames As ArrayBuilder(Of String)) As (names As ImmutableArray(Of String),
inferred As ImmutableArray(Of Boolean))
If elementNames Is Nothing Then
If inferredElementNames Is Nothing Then
Return (Nothing, Nothing)
Else
Dim finalNames = inferredElementNames.ToImmutable()
Return (finalNames, finalNames.SelectAsArray(Function(n) n IsNot Nothing))
End If
End If
If inferredElementNames Is Nothing Then
Return (elementNames.ToImmutable(), Nothing)
End If
Debug.Assert(elementNames.Count = inferredElementNames.Count)
Dim builder = ArrayBuilder(Of Boolean).GetInstance(elementNames.Count)
For i = 0 To elementNames.Count - 1
Dim inferredName As String = inferredElementNames(i)
If elementNames(i) Is Nothing AndAlso inferredName IsNot Nothing Then
elementNames(i) = inferredName
builder.Add(True)
Else
builder.Add(False)
End If
Next
Return (elementNames.ToImmutable(), builder.ToImmutableAndFree())
End Function
''' <summary>
''' Removes duplicate entries in <paramref name="inferredElementNames"/> and frees it if only nulls remain.
''' </summary>
Private Shared Sub RemoveDuplicateInferredTupleNamesAndFreeIfEmptied(ByRef inferredElementNames As ArrayBuilder(Of String), uniqueFieldNames As HashSet(Of String))
If inferredElementNames Is Nothing Then
Return
End If
' Inferred names that duplicate an explicit name or a previous inferred name are tagged for removal
Dim toRemove = New HashSet(Of String)(IdentifierComparison.Comparer)
For Each name In inferredElementNames
If name IsNot Nothing AndAlso Not uniqueFieldNames.Add(name) Then
toRemove.Add(name)
End If
Next
For index = 0 To inferredElementNames.Count - 1
Dim inferredName As String = inferredElementNames(index)
If inferredName IsNot Nothing AndAlso toRemove.Contains(inferredName) Then
inferredElementNames(index) = Nothing
End If
Next
If inferredElementNames.All(Function(n) n Is Nothing) Then
inferredElementNames.Free()
inferredElementNames = Nothing
End If
End Sub
Private Shared Function InferTupleElementName(element As ExpressionSyntax) As String
Dim ignore As XmlNameSyntax = Nothing
Dim nameToken As SyntaxToken = element.ExtractAnonymousTypeMemberName(ignore)
If nameToken.Kind() = SyntaxKind.IdentifierToken Then
Dim name As String = nameToken.ValueText
' Reserved names are never candidates to be inferred names, at any position
If TupleTypeSymbol.IsElementNameReserved(name) = -1 Then
Return name
End If
End If
Return Nothing
End Function
''' <summary>
''' Returns the type to be used as a field type.
''' </summary>
Private Function GetTupleFieldType(expression As BoundExpression,
errorSyntax As VisualBasicSyntaxNode,
diagnostics As BindingDiagnosticBag,
ByRef hasNaturalType As Boolean) As TypeSymbol
Dim expressionType As TypeSymbol = expression.Type
If expressionType Is Nothing Then
hasNaturalType = False
' Dig through parenthesized.
If Not expression.IsNothingLiteral Then
expression = expression.GetMostEnclosedParenthesizedExpression()
End If
Select Case expression.Kind
Case BoundKind.UnboundLambda
expressionType = DirectCast(expression, UnboundLambda).InferredAnonymousDelegate.Key
Case BoundKind.TupleLiteral
expressionType = DirectCast(expression, BoundTupleLiteral).InferredType
Case BoundKind.ArrayLiteral
expressionType = DirectCast(expression, BoundArrayLiteral).InferredType
Case Else
If expression.IsNothingLiteral Then
expressionType = GetSpecialType(SpecialType.System_Object, expression.Syntax, diagnostics)
End If
End Select
End If
Return expressionType
End Function
Private Shared Sub CollectTupleFieldMemberName(name As String, elementIndex As Integer, tupleSize As Integer, ByRef elementNames As ArrayBuilder(Of String))
' add the name to the list
' names would typically all be there or none at all
' but in case we need to handle this in error cases
If elementNames IsNot Nothing Then
elementNames.Add(name)
Else
If name IsNot Nothing Then
elementNames = ArrayBuilder(Of String).GetInstance(tupleSize)
For j As Integer = 1 To elementIndex
elementNames.Add(Nothing)
Next
elementNames.Add(name)
End If
End If
End Sub
Private Shared Function CheckTupleMemberName(name As String, index As Integer, syntax As SyntaxNodeOrToken, diagnostics As BindingDiagnosticBag, uniqueFieldNames As HashSet(Of String)) As Boolean
Dim reserved As Integer = TupleTypeSymbol.IsElementNameReserved(name)
If reserved = 0 Then
Binder.ReportDiagnostic(diagnostics, syntax, ERRID.ERR_TupleReservedElementNameAnyPosition, name)
Return False
ElseIf reserved > 0 AndAlso reserved <> index + 1 Then
Binder.ReportDiagnostic(diagnostics, syntax, ERRID.ERR_TupleReservedElementName, name, reserved)
Return False
ElseIf (Not uniqueFieldNames.Add(name)) Then
Binder.ReportDiagnostic(diagnostics, syntax, ERRID.ERR_TupleDuplicateElementName)
Return False
End If
Return True
End Function
Public Function BindNamespaceOrTypeExpression(node As TypeSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
Dim symbol = Me.BindNamespaceOrTypeOrAliasSyntax(node, diagnostics)
Dim [alias] = TryCast(symbol, AliasSymbol)
If [alias] IsNot Nothing Then
symbol = [alias].Target
' check for use site errors
ReportUseSite(diagnostics, node, symbol)
End If
Dim [type] = TryCast(symbol, TypeSymbol)
If [type] IsNot Nothing Then
Return New BoundTypeExpression(node, Nothing, [alias], [type])
End If
Dim ns = TryCast(symbol, NamespaceSymbol)
If ns IsNot Nothing Then
Return New BoundNamespaceExpression(node, Nothing, [alias], ns)
End If
' BindNamespaceOrTypeSyntax should always return a type or a namespace (might be an error type).
Throw ExceptionUtilities.Unreachable
End Function
Public Function BindNamespaceOrTypeOrExpressionSyntaxForSemanticModel(node As ExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
If (node.Kind = SyntaxKind.PredefinedType) OrElse
(((TypeOf node Is NameSyntax) OrElse node.Kind = SyntaxKind.ArrayType OrElse node.Kind = SyntaxKind.TupleType) AndAlso SyntaxFacts.IsInNamespaceOrTypeContext(node)) Then
Dim result As BoundExpression = Me.BindNamespaceOrTypeExpression(DirectCast(node, TypeSyntax), diagnostics)
' Deal with the case of a namespace group. We may need to bind more in order to see if the ambiguity can be resolved.
If node.Parent IsNot Nothing AndAlso
node.Parent.Kind = SyntaxKind.QualifiedName AndAlso
DirectCast(node.Parent, QualifiedNameSyntax).Left Is node AndAlso
result.Kind = BoundKind.NamespaceExpression Then
Dim namespaceExpr = DirectCast(result, BoundNamespaceExpression)
If namespaceExpr.NamespaceSymbol.NamespaceKind = NamespaceKindNamespaceGroup Then
Dim boundParent As BoundExpression = BindNamespaceOrTypeOrExpressionSyntaxForSemanticModel(DirectCast(node.Parent, QualifiedNameSyntax), BindingDiagnosticBag.Discarded)
Dim symbols = ArrayBuilder(Of Symbol).GetInstance()
BindNamespaceOrTypeSyntaxForSemanticModelGetExpressionSymbols(boundParent, symbols)
If symbols.Count = 0 Then
' If we didn't get anything, let's bind normally and see if any symbol comes out.
boundParent = BindExpression(DirectCast(node.Parent, QualifiedNameSyntax), BindingDiagnosticBag.Discarded)
BindNamespaceOrTypeSyntaxForSemanticModelGetExpressionSymbols(boundParent, symbols)
End If
result = AdjustReceiverNamespace(namespaceExpr, symbols)
symbols.Free()
End If
End If
Return result
Else
Return Me.BindExpression(node, isInvocationOrAddressOf:=SyntaxFacts.IsInvocationOrAddressOfOperand(node), diagnostics:=diagnostics, isOperandOfConditionalBranch:=False, eventContext:=False)
End If
End Function
Private Shared Sub BindNamespaceOrTypeSyntaxForSemanticModelGetExpressionSymbols(expression As BoundExpression, symbols As ArrayBuilder(Of Symbol))
expression.GetExpressionSymbols(symbols)
If symbols.Count = 1 AndAlso symbols(0).Kind = SymbolKind.ErrorType Then
Dim errorType = DirectCast(symbols(0), ErrorTypeSymbol)
symbols.Clear()
Dim diagnosticInfo = TryCast(errorType.ErrorInfo, IDiagnosticInfoWithSymbols)
If diagnosticInfo IsNot Nothing Then
diagnosticInfo.GetAssociatedSymbols(symbols)
End If
End If
End Sub
''' <summary>
''' This function is only needed for SemanticModel to perform binding for erroneous cases.
''' </summary>
Private Function BindQualifiedName(name As QualifiedNameSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
Return Me.BindMemberAccess(name, BindExpression(name.Left, diagnostics), name.Right, eventContext:=False, diagnostics:=diagnostics)
End Function
Private Function BindGetTypeExpression(node As GetTypeExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
' Create a special binder that allows unbound types
Dim getTypeBinder = New GetTypeBinder(node.Type, Me)
' GetType is more permissive on what is considered a valid type.
' for example it allows modules, System.Void or open generic types.
'returns either a type, an alias that refers to a type, or an error type
Dim typeOrAlias As Symbol = TypeBinder.BindTypeOrAliasSyntax(node.Type, getTypeBinder, diagnostics,
suppressUseSiteError:=False, inGetTypeContext:=True, resolvingBaseType:=False)
Dim aliasSym As AliasSymbol = TryCast(typeOrAlias, AliasSymbol)
Dim typeSym As TypeSymbol = DirectCast(If(aliasSym IsNot Nothing, aliasSym.Target, typeOrAlias), TypeSymbol)
Dim typeExpression = New BoundTypeExpression(node.Type, Nothing, aliasSym, typeSym, typeSym.IsErrorType())
' System.Void() is not allowed for VB.
If typeSym.IsArrayType AndAlso DirectCast(typeSym, ArrayTypeSymbol).ElementType.SpecialType = SpecialType.System_Void Then
ReportDiagnostic(diagnostics, node.Type, ErrorFactory.ErrorInfo(ERRID.ERR_VoidArrayDisallowed))
End If
Return New BoundGetType(node, typeExpression, getTypeFromHandle:=Nothing, GetWellKnownType(WellKnownType.System_Type, node, diagnostics))
End Function
Private Function BindNameOfExpression(node As NameOfExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
' Suppress diagnostics if argument has syntax errors
If node.Argument.HasErrors Then
diagnostics = BindingDiagnosticBag.Discarded
End If
Dim value As String = Nothing
Select Case node.Argument.Kind
Case SyntaxKind.SimpleMemberAccessExpression
value = DirectCast(node.Argument, MemberAccessExpressionSyntax).Name.Identifier.ValueText
Case SyntaxKind.IdentifierName,
SyntaxKind.GenericName
value = DirectCast(node.Argument, SimpleNameSyntax).Identifier.ValueText
Case Else
' Must be a syntax error
Debug.Assert(node.Argument.HasErrors)
End Select
' Bind the argument
Dim argument As BoundExpression = BindExpression(node.Argument, diagnostics)
Select Case argument.Kind
Case BoundKind.MethodGroup
Dim group = DirectCast(argument, BoundMethodGroup)
If group.ResultKind = LookupResultKind.Inaccessible Then
ReportDiagnostic(diagnostics,
If(node.Argument.Kind = SyntaxKind.SimpleMemberAccessExpression,
DirectCast(node.Argument, MemberAccessExpressionSyntax).Name,
node.Argument),
GetInaccessibleErrorInfo(group.Methods.First))
ElseIf group.ResultKind = LookupResultKind.Good AndAlso group.TypeArgumentsOpt IsNot Nothing Then
ReportDiagnostic(diagnostics, group.TypeArgumentsOpt.Syntax, ERRID.ERR_MethodTypeArgsUnexpected)
Else
For Each method In group.Methods
diagnostics.AddDependency(method.ContainingAssembly)
Next
End If
Case BoundKind.PropertyGroup
Dim group = DirectCast(argument, BoundPropertyGroup)
If group.ResultKind = LookupResultKind.Inaccessible Then
ReportDiagnostic(diagnostics,
If(node.Argument.Kind = SyntaxKind.SimpleMemberAccessExpression,
DirectCast(node.Argument, MemberAccessExpressionSyntax).Name,
node.Argument),
GetInaccessibleErrorInfo(group.Properties.First))
Else
For Each prop In group.Properties
diagnostics.AddDependency(prop.ContainingAssembly)
Next
End If
Case BoundKind.NamespaceExpression
diagnostics.AddAssembliesUsedByNamespaceReference(DirectCast(argument, BoundNamespaceExpression).NamespaceSymbol)
End Select
Return New BoundNameOfOperator(node, argument, ConstantValue.Create(value), GetSpecialType(SpecialType.System_String, node, diagnostics))
End Function
Private Function BindTypeOfExpression(node As TypeOfExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
Dim operand = BindRValue(node.Expression, diagnostics, isOperandOfConditionalBranch:=False)
Dim operandType = operand.Type
Dim operatorIsIsNot = (node.Kind = SyntaxKind.TypeOfIsNotExpression)
Dim targetSymbol As Symbol = BindTypeOrAliasSyntax(node.Type, diagnostics)
Dim targetType = DirectCast(If(TryCast(targetSymbol, TypeSymbol), DirectCast(targetSymbol, AliasSymbol).Target), TypeSymbol)
Dim resultType As TypeSymbol = GetSpecialType(SpecialType.System_Boolean, node, diagnostics)
If operand.HasErrors OrElse operandType.IsErrorType() OrElse targetType.IsErrorType() Then
' If operand is bad or either the source or target types have errors, bail out preventing more cascading errors.
Return New BoundTypeOf(node, operand, operatorIsIsNot, targetType, resultType)
End If
If Not operandType.IsReferenceType AndAlso
Not operandType.IsTypeParameter() Then
ReportDiagnostic(diagnostics, node.Expression, ERRID.ERR_TypeOfRequiresReferenceType1, operandType)
Else
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Dim convKind As ConversionKind = Conversions.ClassifyTryCastConversion(operandType, targetType, useSiteInfo)
If diagnostics.Add(node, useSiteInfo) Then
' Suppress any additional diagnostics
diagnostics = BindingDiagnosticBag.Discarded
ElseIf Not Conversions.ConversionExists(convKind) Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_TypeOfExprAlwaysFalse2, operandType, targetType)
End If
End If
If operandType.IsTypeParameter() Then
operand = ApplyImplicitConversion(node, GetSpecialType(SpecialType.System_Object, node.Expression, diagnostics), operand, diagnostics)
End If
Return New BoundTypeOf(node, operand, operatorIsIsNot, targetType, resultType)
End Function
''' <summary>
''' BindValue evaluates the node and returns a BoundExpression. BindValue snaps expressions to values. For now that means that method groups
''' become invocations.
''' </summary>
Friend Function BindValue(
node As ExpressionSyntax,
diagnostics As BindingDiagnosticBag,
Optional isOperandOfConditionalBranch As Boolean = False
) As BoundExpression
Dim expr = BindExpression(node, diagnostics:=diagnostics, isOperandOfConditionalBranch:=isOperandOfConditionalBranch, isInvocationOrAddressOf:=False, eventContext:=False)
Return MakeValue(expr, diagnostics)
End Function
Private Function AdjustReceiverTypeOrValue(receiver As BoundExpression,
node As SyntaxNode,
isShared As Boolean,
diagnostics As BindingDiagnosticBag,
ByRef resolvedTypeOrValueExpression As BoundExpression) As BoundExpression
Dim unused As QualificationKind
Return AdjustReceiverTypeOrValue(receiver, node, isShared, True, diagnostics, unused, resolvedTypeOrValueExpression)
End Function
Private Function AdjustReceiverTypeOrValue(receiver As BoundExpression,
node As SyntaxNode,
isShared As Boolean,
diagnostics As BindingDiagnosticBag,
ByRef qualKind As QualificationKind) As BoundExpression
Dim unused As BoundExpression = Nothing
Return AdjustReceiverTypeOrValue(receiver, node, isShared, False, diagnostics, qualKind, unused)
End Function
''' <summary>
''' Adjusts receiver of a call or a member access.
''' * will turn Unknown property access into Get property access
''' * will turn TypeOrValueExpression into a value expression
''' </summary>
Private Function AdjustReceiverTypeOrValue(receiver As BoundExpression,
node As SyntaxNode,
isShared As Boolean,
clearIfShared As Boolean,
diagnostics As BindingDiagnosticBag,
ByRef qualKind As QualificationKind,
ByRef resolvedTypeOrValueExpression As BoundExpression) As BoundExpression
If receiver Is Nothing Then
Return receiver
End If
If isShared Then
If receiver.Kind = BoundKind.TypeOrValueExpression Then
Dim typeOrValue = DirectCast(receiver, BoundTypeOrValueExpression)
diagnostics.AddRange(typeOrValue.Data.TypeDiagnostics)
receiver = typeOrValue.Data.TypeExpression
qualKind = QualificationKind.QualifiedViaTypeName
resolvedTypeOrValueExpression = receiver
End If
If clearIfShared Then
receiver = Nothing
End If
Else
If receiver.Kind = BoundKind.TypeOrValueExpression Then
Dim typeOrValue = DirectCast(receiver, BoundTypeOrValueExpression)
diagnostics.AddRange(typeOrValue.Data.ValueDiagnostics)
receiver = MakeValue(typeOrValue.Data.ValueExpression, diagnostics)
qualKind = QualificationKind.QualifiedViaValue
resolvedTypeOrValueExpression = receiver
End If
receiver = AdjustReceiverValue(receiver, node, diagnostics)
End If
Return receiver
End Function
''' <summary>
''' Adjusts receiver of a call or a member access if the receiver is an
''' ambiguous BoundTypeOrValueExpression. This can only happen if the
''' receiver is the LHS of a member access expression in which the
''' RHS cannot be resolved (i.e. the RHS is an error or a late-bound
''' invocation/access).
''' </summary>
Private Shared Function AdjustReceiverAmbiguousTypeOrValue(receiver As BoundExpression, diagnostics As BindingDiagnosticBag) As BoundExpression
If receiver IsNot Nothing AndAlso receiver.Kind = BoundKind.TypeOrValueExpression Then
Dim typeOrValue = DirectCast(receiver, BoundTypeOrValueExpression)
diagnostics.AddRange(typeOrValue.Data.ValueDiagnostics)
receiver = typeOrValue.Data.ValueExpression
End If
Return receiver
End Function
Private Shared Function AdjustReceiverAmbiguousTypeOrValue(ByRef group As BoundMethodOrPropertyGroup, diagnostics As BindingDiagnosticBag) As BoundExpression
Debug.Assert(group IsNot Nothing)
Dim receiver = group.ReceiverOpt
If receiver IsNot Nothing AndAlso receiver.Kind = BoundKind.TypeOrValueExpression Then
receiver = AdjustReceiverAmbiguousTypeOrValue(receiver, diagnostics)
Select Case group.Kind
Case BoundKind.MethodGroup
Dim methodGroup = DirectCast(group, BoundMethodGroup)
group = methodGroup.Update(methodGroup.TypeArgumentsOpt,
methodGroup.Methods,
methodGroup.PendingExtensionMethodsOpt,
methodGroup.ResultKind,
receiver,
methodGroup.QualificationKind)
Case BoundKind.PropertyGroup
Dim propertyGroup = DirectCast(group, BoundPropertyGroup)
group = propertyGroup.Update(propertyGroup.Properties,
propertyGroup.ResultKind,
receiver,
propertyGroup.QualificationKind)
Case Else
Throw ExceptionUtilities.UnexpectedValue(group.Kind)
End Select
End If
Return receiver
End Function
''' <summary>
''' Adjusts receiver of a call or a member access if it is a value
''' * will turn Unknown property access into Get property access
''' </summary>
Private Function AdjustReceiverValue(receiver As BoundExpression,
node As SyntaxNode,
diagnostics As BindingDiagnosticBag) As BoundExpression
If Not receiver.IsValue() Then
receiver = MakeValue(receiver, diagnostics)
End If
If Not receiver.IsLValue AndAlso Not receiver.IsPropertyOrXmlPropertyAccess() Then
receiver = MakeRValue(receiver, diagnostics)
End If
Dim type = receiver.Type
If type Is Nothing OrElse type.IsErrorType() Then
Return BadExpression(node, receiver, LookupResultKind.NotAValue, ErrorTypeSymbol.UnknownResultType)
End If
Return receiver
End Function
Friend Function ReclassifyAsValue(
expr As BoundExpression,
diagnostics As BindingDiagnosticBag
) As BoundExpression
If expr.Kind = BoundKind.ConditionalAccess AndAlso expr.Type Is Nothing Then
Dim conditionalAccess = DirectCast(expr, BoundConditionalAccess)
Dim access As BoundExpression = Me.MakeRValue(conditionalAccess.AccessExpression, diagnostics)
Dim resultType As TypeSymbol = access.Type
If Not resultType.IsErrorType() Then
If resultType.IsValueType AndAlso Not resultType.IsRestrictedType Then
If Not resultType.IsNullableType() Then
resultType = GetSpecialType(SpecialType.System_Nullable_T, expr.Syntax, diagnostics).Construct(resultType)
End If
ElseIf Not resultType.IsReferenceType Then
' Access cannot have unconstrained generic type or a restricted type
ReportDiagnostic(diagnostics, access.Syntax, ERRID.ERR_CannotBeMadeNullable1, resultType)
resultType = ErrorTypeSymbol.UnknownResultType
End If
End If
Return conditionalAccess.Update(conditionalAccess.Receiver, conditionalAccess.Placeholder, access, resultType)
End If
If expr.HasErrors Then
Return expr
End If
Select Case expr.Kind
Case BoundKind.Parenthesized
If Not expr.IsNothingLiteral() Then
Return MakeRValue(expr, diagnostics)
End If
Case BoundKind.MethodGroup,
BoundKind.PropertyGroup
Dim group = DirectCast(expr, BoundMethodOrPropertyGroup)
If IsGroupOfConstructors(group) Then
' cannot reclassify a constructor call
ReportDiagnostic(diagnostics, group.Syntax, ERRID.ERR_InvalidConstructorCall)
Return New BoundBadVariable(expr.Syntax, expr, ErrorTypeSymbol.UnknownResultType, hasErrors:=True)
Else
expr = BindInvocationExpression(expr.Syntax,
expr.Syntax,
ExtractTypeCharacter(expr.Syntax),
group,
s_noArguments,
Nothing,
diagnostics,
callerInfoOpt:=expr.Syntax)
End If
Case BoundKind.TypeExpression
' Try default instance property through DefaultInstanceAlias
Dim instance As BoundExpression = TryDefaultInstanceProperty(DirectCast(expr, BoundTypeExpression), diagnostics)
If instance Is Nothing Then
Dim type = expr.Type
ReportDiagnostic(diagnostics, expr.Syntax, GetTypeNotExpressionErrorId(type), type)
Return New BoundBadVariable(expr.Syntax, expr, ErrorTypeSymbol.UnknownResultType, hasErrors:=True)
End If
expr = instance
Case BoundKind.EventAccess
ReportDiagnostic(diagnostics, expr.Syntax, ERRID.ERR_CannotCallEvent1, DirectCast(expr, BoundEventAccess).EventSymbol)
Return New BoundBadVariable(expr.Syntax, expr, ErrorTypeSymbol.UnknownResultType, hasErrors:=True)
Case BoundKind.NamespaceExpression
ReportDiagnostic(diagnostics, expr.Syntax, ERRID.ERR_NamespaceNotExpression1, DirectCast(expr, BoundNamespaceExpression).NamespaceSymbol)
Return New BoundBadVariable(expr.Syntax, expr, ErrorTypeSymbol.UnknownResultType, hasErrors:=True)
Case BoundKind.Label
ReportDiagnostic(diagnostics, expr.Syntax, ERRID.ERR_VoidValue, DirectCast(expr, BoundLabel).Label.Name)
Return New BoundBadVariable(expr.Syntax, expr, ErrorTypeSymbol.UnknownResultType, hasErrors:=True)
End Select
Debug.Assert(expr.IsValue)
Return expr
End Function
Friend Overridable ReadOnly Property IsDefaultInstancePropertyAllowed As Boolean
Get
Return m_containingBinder.IsDefaultInstancePropertyAllowed
End Get
End Property
Friend Overridable ReadOnly Property SuppressCallerInfo As Boolean
Get
Return m_containingBinder.SuppressCallerInfo
End Get
End Property
Friend Function TryDefaultInstanceProperty(typeExpr As BoundTypeExpression, diagnostics As BindingDiagnosticBag) As BoundExpression
If Not IsDefaultInstancePropertyAllowed Then
Return Nothing
End If
' See Semantics::CheckForDefaultInstanceProperty.
Dim type As TypeSymbol = typeExpr.Type
If type.IsErrorType() OrElse
SourceModule IsNot type.ContainingModule OrElse
type.TypeKind <> TYPEKIND.Class Then
Return Nothing
End If
Dim classType = DirectCast(type, NamedTypeSymbol)
If classType.IsGenericType Then
Return Nothing
End If
Dim prop As SynthesizedMyGroupCollectionPropertySymbol = SourceModule.GetMyGroupCollectionPropertyWithDefaultInstanceAlias(classType)
If prop Is Nothing Then
Return Nothing
End If
Debug.Assert(prop.Type Is classType)
' Lets try to parse and bind an expression of the following form:
' <DefaultInstanceAlias>.<MyGroupCollectionProperty name>
' If any error happens, return Nothing without reporting any diagnostics.
' Note, native compiler doesn't escape DefaultInstanceAlias if it is a reserved keyword.
Dim codeToParse As String =
"Class DefaultInstanceAlias" & vbCrLf &
"Function DefaultInstanceAlias()" & vbCrLf &
"Return " & prop.DefaultInstanceAlias & "." & prop.Name & vbCrLf &
"End Function" & vbCrLf &
"End Class" & vbCrLf
' It looks like Dev11 ignores project level conditional compilation here, which makes sense since expression cannot contain #If directives.
Dim tree = VisualBasicSyntaxTree.ParseText(SourceText.From(codeToParse))
Dim root As CompilationUnitSyntax = tree.GetCompilationUnitRoot()
Dim hasErrors As Boolean = False
For Each diag As Diagnostic In tree.GetDiagnostics(root)
Dim cdiag = TryCast(diag, DiagnosticWithInfo)
Debug.Assert(cdiag Is Nothing OrElse Not cdiag.HasLazyInfo,
"If we decide to allow lazy syntax diagnostics, we'll have to check all call sites of SyntaxTree.GetDiagnostics")
If diag.Severity = DiagnosticSeverity.Error Then
Return Nothing
End If
Next
Dim classBlock = DirectCast(root.Members(0), ClassBlockSyntax)
Dim functionBlock = DirectCast(classBlock.Members(0), MethodBlockSyntax)
' We expect there to be only one statement, which is [Return] statement.
If functionBlock.Statements.Count > 1 Then
Return Nothing
End If
Dim ret = DirectCast(functionBlock.Statements(0), ReturnStatementSyntax)
Dim exprDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=diagnostics.AccumulatesDependencies)
Dim result As BoundExpression = (New DefaultInstancePropertyBinder(Me)).BindValue(ret.Expression, exprDiagnostics)
If result.HasErrors OrElse exprDiagnostics.HasAnyErrors() Then
exprDiagnostics.Free()
Return Nothing
End If
diagnostics.AddDependencies(exprDiagnostics)
exprDiagnostics.Free()
' if the default inst expression cannot be correctly bound to an instance of the same type as the class
' ignore it.
If result.Type IsNot classType Then
Return Nothing
End If
If ContainingType Is classType AndAlso Not ContainingMember.IsShared Then
ReportDiagnostic(diagnostics, typeExpr.Syntax, ERRID.ERR_CantReferToMyGroupInsideGroupType1, classType)
End If
' We need to change syntax node for the result to match typeExpr's syntax node.
' This will allow SemanticModel to report the node as a default instance access rather than
' as a type reference.
Select Case result.Kind
Case BoundKind.PropertyAccess
Dim access = DirectCast(result, BoundPropertyAccess)
result = New BoundPropertyAccess(typeExpr.Syntax, access.PropertySymbol, access.PropertyGroupOpt, access.AccessKind,
isWriteable:=access.IsWriteable,
isLValue:=False,
receiverOpt:=access.ReceiverOpt,
arguments:=access.Arguments,
defaultArguments:=access.DefaultArguments,
type:=access.Type,
hasErrors:=access.HasErrors)
Case BoundKind.FieldAccess
Dim access = DirectCast(result, BoundFieldAccess)
result = New BoundFieldAccess(typeExpr.Syntax, access.ReceiverOpt, access.FieldSymbol, access.IsLValue,
access.SuppressVirtualCalls, access.ConstantsInProgressOpt, access.Type, access.HasErrors)
Case BoundKind.Call
Dim [call] = DirectCast(result, BoundCall)
result = New BoundCall(typeExpr.Syntax, [call].Method, [call].MethodGroupOpt, [call].ReceiverOpt, [call].Arguments,
[call].DefaultArguments, [call].ConstantValueOpt,
isLValue:=False,
suppressObjectClone:=[call].SuppressObjectClone,
type:=[call].Type,
hasErrors:=[call].HasErrors)
Case Else
Throw ExceptionUtilities.UnexpectedValue(result.Kind)
End Select
Return result
End Function
Private Class DefaultInstancePropertyBinder
Inherits Binder
Public Sub New(containingBinder As Binder)
MyBase.New(containingBinder)
End Sub
Public Overrides ReadOnly Property ImplicitVariableDeclarationAllowed As Boolean
Get
Return False
End Get
End Property
Friend Overrides ReadOnly Property IsDefaultInstancePropertyAllowed As Boolean
Get
Return False
End Get
End Property
Friend Overrides ReadOnly Property SuppressCallerInfo As Boolean
Get
Return True
End Get
End Property
End Class
Private Shared Function GetTypeNotExpressionErrorId(type As TypeSymbol) As ERRID
Select Case type.TypeKind
Case TYPEKIND.Class
Return ERRID.ERR_ClassNotExpression1
Case TYPEKIND.Interface
Return ERRID.ERR_InterfaceNotExpression1
Case TYPEKIND.Enum
Return ERRID.ERR_EnumNotExpression1
Case TYPEKIND.Structure
Return ERRID.ERR_StructureNotExpression1
' TODO Modules??
Case Else
Return ERRID.ERR_TypeNotExpression1
End Select
End Function
Private Function MakeValue(
expr As BoundExpression,
diagnostics As BindingDiagnosticBag
) As BoundExpression
If expr.Kind = BoundKind.Parenthesized Then
If Not expr.IsNothingLiteral() Then
Dim parenthesized = DirectCast(expr, BoundParenthesized)
Dim enclosed As BoundExpression = MakeValue(parenthesized.Expression, diagnostics)
Return parenthesized.Update(enclosed, enclosed.Type)
End If
End If
expr = ReclassifyAsValue(expr, diagnostics)
If expr.HasErrors Then
If Not expr.IsValue OrElse expr.Type Is Nothing OrElse expr.Type.IsVoidType Then
Return BadExpression(expr.Syntax, expr, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
Else
Return expr
End If
End If
Dim exprType = expr.Type
Dim syntax = expr.Syntax
If Not expr.IsValue() OrElse
(exprType IsNot Nothing AndAlso exprType.SpecialType = SpecialType.System_Void) Then
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_VoidValue)
Return BadExpression(syntax, expr, LookupResultKind.NotAValue, ErrorTypeSymbol.UnknownResultType)
ElseIf expr.Kind = BoundKind.PropertyAccess Then
Dim propertyAccess = DirectCast(expr, BoundPropertyAccess)
Select Case propertyAccess.AccessKind
Case PropertyAccessKind.Set
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_VoidValue)
Return BadExpression(syntax, expr, LookupResultKind.NotAValue, ErrorTypeSymbol.UnknownResultType)
Case PropertyAccessKind.Unknown
Dim hasError = True
Dim propertySymbol = propertyAccess.PropertySymbol
If Not propertySymbol.IsReadable Then
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_NoGetProperty1, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
Else
Dim getMethod = propertySymbol.GetMostDerivedGetMethod()
Debug.Assert(getMethod IsNot Nothing)
If Not ReportUseSite(diagnostics, syntax, getMethod) Then
Dim accessThroughType = GetAccessThroughType(propertyAccess.ReceiverOpt)
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If IsAccessible(getMethod, useSiteInfo, accessThroughType) OrElse
Not IsAccessible(propertySymbol, useSiteInfo, accessThroughType) Then
hasError = False
Else
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_NoAccessibleGet, CustomSymbolDisplayFormatter.ShortErrorName(propertySymbol))
End If
diagnostics.Add(syntax, useSiteInfo)
End If
End If
If hasError Then
Return BadExpression(syntax, expr, LookupResultKind.NotAValue, propertySymbol.Type)
End If
End Select
ElseIf expr.IsLateBound() Then
If (expr.GetLateBoundAccessKind() And (LateBoundAccessKind.Set Or LateBoundAccessKind.Call)) <> 0 Then
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_VoidValue)
Return BadExpression(syntax, expr, LookupResultKind.NotAValue, ErrorTypeSymbol.UnknownResultType)
End If
ElseIf expr.Kind = BoundKind.AddressOfOperator Then
Return expr
End If
Return expr
End Function
Private Function GetAccessThroughType(receiverOpt As BoundExpression) As TypeSymbol
If receiverOpt Is Nothing OrElse receiverOpt.Kind = BoundKind.MyBaseReference Then
' NOTE: If we are accessing the symbol via MyBase reference we may
' assume the access is being performed via 'Me'
Return ContainingType
Else
Debug.Assert(receiverOpt.Type IsNot Nothing)
Return receiverOpt.Type
End If
End Function
''' <summary>
''' BindRValue evaluates the node and returns a BoundExpression.
''' It ensures that the expression is a value that can be used on the right hand side of an assignment.
''' If not, it reports an error.
'''
''' Note that this function will reclassify all expressions to have their "default" type, i.e.
''' Anonymous Delegate for a lambda, default array type for an array literal, will report an error
''' for an AddressOf, etc. So, if you are in a context where there is a known target type for the
''' expression, do not use this function. Instead, use BindValue followed by
''' ApplyImplicitConversion/ApplyConversion.
''' </summary>
Private Function BindRValue(
node As ExpressionSyntax,
diagnostics As BindingDiagnosticBag,
Optional isOperandOfConditionalBranch As Boolean = False
) As BoundExpression
Dim expr = BindExpression(node, diagnostics:=diagnostics, isOperandOfConditionalBranch:=isOperandOfConditionalBranch, isInvocationOrAddressOf:=False, eventContext:=False)
Return MakeRValue(expr, diagnostics)
End Function
Friend Function MakeRValue(
expr As BoundExpression,
diagnostics As BindingDiagnosticBag
) As BoundExpression
If expr.Kind = BoundKind.Parenthesized AndAlso Not expr.IsNothingLiteral() Then
Dim parenthesized = DirectCast(expr, BoundParenthesized)
Dim enclosed As BoundExpression = MakeRValue(parenthesized.Expression, diagnostics)
Return parenthesized.Update(enclosed, enclosed.Type)
ElseIf expr.Kind = BoundKind.XmlMemberAccess Then
Dim memberAccess = DirectCast(expr, BoundXmlMemberAccess)
Dim enclosed = MakeRValue(memberAccess.MemberAccess, diagnostics)
Return memberAccess.Update(enclosed)
End If
expr = MakeValue(expr, diagnostics)
If expr.HasErrors Then
Return expr.MakeRValue()
End If
Debug.Assert(expr.IsValue())
Dim exprType = expr.Type
If exprType Is Nothing Then
Return ReclassifyExpression(expr, diagnostics)
End If
' Transform LValue to RValue.
If expr.IsLValue Then
expr = expr.MakeRValue()
ElseIf expr.Kind = BoundKind.PropertyAccess Then
Dim propertyAccess = DirectCast(expr, BoundPropertyAccess)
Dim getMethod = propertyAccess.PropertySymbol.GetMostDerivedGetMethod()
Debug.Assert(getMethod IsNot Nothing)
ReportUseSite(diagnostics, expr.Syntax, getMethod)
ReportDiagnosticsIfObsoleteOrNotSupported(diagnostics, getMethod, expr.Syntax)
Select Case propertyAccess.AccessKind
Case PropertyAccessKind.Get
' Nothing to do.
Case PropertyAccessKind.Unknown
Debug.Assert(propertyAccess.PropertySymbol.IsReadable)
WarnOnRecursiveAccess(propertyAccess, PropertyAccessKind.Get, diagnostics)
expr = propertyAccess.SetAccessKind(PropertyAccessKind.Get)
Case Else
Throw ExceptionUtilities.UnexpectedValue(propertyAccess.AccessKind)
End Select
ElseIf expr.Kind = BoundKind.InterpolatedStringExpression Then
expr = BindUnconvertedInterpolatedStringToString(DirectCast(expr, BoundInterpolatedStringExpression), diagnostics)
ElseIf expr.IsLateBound() Then
Select Case expr.GetLateBoundAccessKind()
Case LateBoundAccessKind.Get
' Nothing to do.
Case LateBoundAccessKind.Unknown
expr = expr.SetLateBoundAccessKind(LateBoundAccessKind.Get)
Case Else
Throw ExceptionUtilities.UnexpectedValue(expr.GetLateBoundAccessKind())
End Select
End If
Return expr
End Function
Private Function MakeRValueAndIgnoreDiagnostics(
expr As BoundExpression
) As BoundExpression
expr = MakeRValue(expr, BindingDiagnosticBag.Discarded)
Return expr
End Function
Friend Function ReclassifyExpression(
expr As BoundExpression,
diagnostics As BindingDiagnosticBag
) As BoundExpression
If expr.IsNothingLiteral() Then
' This is a Nothing literal without a type.
' Reclassify as Object.
Return New BoundConversion(expr.Syntax, expr, ConversionKind.WideningNothingLiteral, False, False, expr.ConstantValueOpt,
GetSpecialType(SpecialType.System_Object, expr.Syntax, diagnostics), Nothing)
End If
Select Case expr.Kind
Case BoundKind.Parenthesized
' Reclassify enclosed expression.
Dim parenthesized = DirectCast(expr, BoundParenthesized)
Dim enclosed As BoundExpression = ReclassifyExpression(parenthesized.Expression, diagnostics)
Return parenthesized.Update(enclosed, enclosed.Type)
Case BoundKind.UnboundLambda
Return ReclassifyUnboundLambdaExpression(DirectCast(expr, UnboundLambda), diagnostics)
Case BoundKind.AddressOfOperator
Dim address = DirectCast(expr, BoundAddressOfOperator)
If address.MethodGroup.ResultKind = LookupResultKind.Inaccessible Then
If address.MethodGroup.Methods.Length = 1 Then
ReportDiagnostic(diagnostics, address.MethodGroup.Syntax, GetInaccessibleErrorInfo(address.MethodGroup.Methods(0)))
Else
ReportDiagnostic(diagnostics, address.MethodGroup.Syntax, ERRID.ERR_NoViableOverloadCandidates1,
address.MethodGroup.Methods(0).Name)
End If
Else
Debug.Assert(address.MethodGroup.ResultKind = LookupResultKind.Good)
End If
Case BoundKind.ArrayLiteral
Return ReclassifyArrayLiteralExpression(DirectCast(expr, BoundArrayLiteral), diagnostics)
Case BoundKind.TupleLiteral
Dim tupleLiteral = DirectCast(expr, BoundTupleLiteral)
If tupleLiteral.InferredType IsNot Nothing Then
Return ReclassifyTupleLiteralExpression(tupleLiteral, diagnostics)
End If
Case Else
End Select
'TODO: We need to do other expression reclassifications here.
' For now, we simply report an error.
ReportDiagnostic(diagnostics, expr.Syntax, ERRID.ERR_VoidValue)
Return BadExpression(expr.Syntax, expr, ErrorTypeSymbol.UnknownResultType)
End Function
Private Function ReclassifyArrayLiteralExpression(conversionSemantics As SyntaxKind,
tree As SyntaxNode,
conv As ConversionKind,
isExplicit As Boolean,
arrayLiteral As BoundArrayLiteral,
destination As TypeSymbol,
diagnostics As BindingDiagnosticBag) As BoundExpression
Debug.Assert((conv And ConversionKind.UserDefined) = 0)
Debug.Assert(Not (TypeOf destination Is ArrayLiteralTypeSymbol)) 'An array literal should never be reclassified as an array literal.
If Conversions.NoConversion(conv) AndAlso (conv And ConversionKind.FailedDueToArrayLiteralElementConversion) = 0 Then
If Not arrayLiteral.HasDominantType Then
' When there is a conversion error and there isn't a dominant type, report "error BC36717: Cannot infer an element type.
' Specifying the type of the array might correct this error." instead of the specific conversion error because the inferred
' type used in classification was just a guess.
ReportDiagnostic(diagnostics, arrayLiteral.Syntax, ERRID.ERR_ArrayInitNoType)
Else
ReportNoConversionError(arrayLiteral.Syntax, arrayLiteral.InferredType, destination, diagnostics, Nothing)
End If
' Because we've already reported a no conversion error, ignore any diagnostics in ApplyImplicitConversion
Dim argument As BoundExpression = ApplyImplicitConversion(arrayLiteral.Syntax, arrayLiteral.InferredType, arrayLiteral, BindingDiagnosticBag.Discarded)
If conversionSemantics = SyntaxKind.CTypeKeyword Then
argument = New BoundConversion(tree, argument, conv, False, isExplicit, destination)
ElseIf conversionSemantics = SyntaxKind.DirectCastKeyword Then
argument = New BoundDirectCast(tree, argument, conv, destination)
ElseIf conversionSemantics = SyntaxKind.TryCastKeyword Then
argument = New BoundTryCast(tree, argument, conv, destination)
Else
Throw ExceptionUtilities.UnexpectedValue(conversionSemantics)
End If
Return argument
End If
' This code must be kept in sync with Conversions.ClassifyArrayLiteralConversion
Dim sourceType = arrayLiteral.InferredType
Dim targetType = TryCast(destination, NamedTypeSymbol)
Dim originalTargetType = If(targetType IsNot Nothing, targetType.OriginalDefinition, Nothing)
Dim targetArrayType As ArrayTypeSymbol = TryCast(destination, ArrayTypeSymbol)
Dim targetElementType As TypeSymbol = Nothing
If targetArrayType IsNot Nothing AndAlso (sourceType.Rank = targetArrayType.Rank OrElse arrayLiteral.IsEmptyArrayLiteral) Then
targetElementType = targetArrayType.ElementType
sourceType = targetArrayType
ElseIf (sourceType.Rank = 1 OrElse arrayLiteral.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.TypeArgumentsNoUseSiteDiagnostics(0)
sourceType = ArrayTypeSymbol.CreateVBArray(targetElementType, Nothing, 1, Compilation)
Else
' Use the inferred type
targetArrayType = sourceType
targetElementType = sourceType.ElementType
End If
ReportArrayLiteralDiagnostics(arrayLiteral, targetArrayType, diagnostics)
Dim arrayInitialization As BoundArrayInitialization
Dim bounds As ImmutableArray(Of BoundExpression)
If arrayLiteral.IsEmptyArrayLiteral Then
Dim knownSizes(sourceType.Rank - 1) As DimensionSize
arrayInitialization = ReclassifyEmptyArrayInitialization(arrayLiteral, sourceType.Rank)
bounds = CreateArrayBounds(arrayLiteral.Syntax, knownSizes, diagnostics)
Else
arrayInitialization = ReclassifyArrayInitialization(arrayLiteral.Initializer, targetElementType, diagnostics)
bounds = arrayLiteral.Bounds
End If
' Mark as compiler generated so that semantic model does not select the array initialization bound node.
' The array initialization node is not a real expression and lacks a type.
arrayInitialization.SetWasCompilerGenerated()
Debug.Assert(Not Conversions.IsIdentityConversion(conv))
Dim arrayCreation = New BoundArrayCreation(arrayLiteral.Syntax, bounds, arrayInitialization, arrayLiteral, conv, sourceType)
If conversionSemantics = SyntaxKind.CTypeKeyword Then
Return ApplyConversion(tree, destination, arrayCreation, isExplicit, diagnostics)
Else
Dim expr As BoundExpression = arrayCreation
' Apply char() to string conversion before directcast/trycast
conv = Conversions.ClassifyStringConversion(sourceType, destination)
If Conversions.IsWideningConversion(conv) Then
expr = CreatePredefinedConversion(arrayLiteral.Syntax, arrayCreation, conv, isExplicit, destination, diagnostics)
End If
If conversionSemantics = SyntaxKind.DirectCastKeyword Then
Return ApplyDirectCastConversion(tree, expr, destination, diagnostics)
ElseIf conversionSemantics = SyntaxKind.TryCastKeyword Then
Return ApplyTryCastConversion(tree, expr, destination, diagnostics)
Else
Throw ExceptionUtilities.UnexpectedValue(conversionSemantics)
End If
End If
End Function
Private Sub ReportArrayLiteralDiagnostics(arrayLiteral As BoundArrayLiteral, targetArrayType As ArrayTypeSymbol, diagnostics As BindingDiagnosticBag)
If targetArrayType Is arrayLiteral.InferredType Then
' Note, array type symbols do not preserve identity. If the target array is the same as the inferred array then we
' assume that the target has inferred its type from the array literal.
ReportArrayLiteralInferredTypeDiagnostics(arrayLiteral, diagnostics)
End If
End Sub
Private Sub ReportArrayLiteralInferredTypeDiagnostics(arrayLiteral As BoundArrayLiteral, diagnostics As BindingDiagnosticBag)
Dim targetElementType = arrayLiteral.InferredType.ElementType
If targetElementType.IsRestrictedType Then
ReportDiagnostic(diagnostics, arrayLiteral.Syntax, ERRID.ERR_RestrictedType1, targetElementType)
ElseIf Not arrayLiteral.HasDominantType Then
ReportDiagnostic(diagnostics, arrayLiteral.Syntax, ERRID.ERR_ArrayInitNoType)
Else
' Possibly warn or report an error depending on the value of option strict
Select Case OptionStrict
Case VisualBasic.OptionStrict.On
If arrayLiteral.NumberOfCandidates = 0 Then
ReportDiagnostic(diagnostics, arrayLiteral.Syntax, ERRID.ERR_ArrayInitNoTypeObjectDisallowed)
ElseIf arrayLiteral.NumberOfCandidates > 1 Then
ReportDiagnostic(diagnostics, arrayLiteral.Syntax, ERRID.ERR_ArrayInitTooManyTypesObjectDisallowed)
End If
Case VisualBasic.OptionStrict.Custom
If arrayLiteral.NumberOfCandidates = 0 Then
ReportDiagnostic(diagnostics, arrayLiteral.Syntax, ErrorFactory.ErrorInfo(ERRID.WRN_ObjectAssumed1,
ErrorFactory.ErrorInfo(ERRID.WRN_ArrayInitNoTypeObjectAssumed)))
ElseIf arrayLiteral.NumberOfCandidates > 1 Then
ReportDiagnostic(diagnostics, arrayLiteral.Syntax, ErrorFactory.ErrorInfo(ERRID.WRN_ObjectAssumed1,
ErrorFactory.ErrorInfo(ERRID.WRN_ArrayInitTooManyTypesObjectAssumed)))
End If
End Select
End If
End Sub
Private Function ReclassifyArrayInitialization(arrayInitialization As BoundArrayInitialization, elementType As TypeSymbol, diagnostics As BindingDiagnosticBag) As BoundArrayInitialization
Dim initializers = ArrayBuilder(Of BoundExpression).GetInstance
' Apply implicit conversion to the elements.
For Each expr In arrayInitialization.Initializers
If expr.Kind = BoundKind.ArrayInitialization Then
expr = ReclassifyArrayInitialization(DirectCast(expr, BoundArrayInitialization), elementType, diagnostics)
Else
expr = ApplyImplicitConversion(expr.Syntax, elementType, expr, diagnostics)
End If
initializers.Add(expr)
Next
arrayInitialization = New BoundArrayInitialization(arrayInitialization.Syntax, initializers.ToImmutableAndFree, Nothing)
Return arrayInitialization
End Function
Private Function ReclassifyEmptyArrayInitialization(arrayLiteral As BoundArrayLiteral, rank As Integer) As BoundArrayInitialization
Dim arrayInitialization As BoundArrayInitialization = arrayLiteral.Initializer
If rank = 1 Then
Return arrayInitialization
End If
Dim initializers = ImmutableArray(Of BoundExpression).Empty
For i = 1 To rank - 1
arrayInitialization = New BoundArrayInitialization(arrayInitialization.Syntax, initializers, Nothing).MakeCompilerGenerated()
initializers = ImmutableArray.Create(Of BoundExpression)(arrayInitialization)
Next
Return New BoundArrayInitialization(arrayInitialization.Syntax, initializers, Nothing)
End Function
Private Function ReclassifyTupleLiteralExpression(
tupleLiteral As BoundTupleLiteral,
diagnostics As BindingDiagnosticBag
) As BoundExpression
Return ApplyImplicitConversion(tupleLiteral.Syntax,
tupleLiteral.InferredType,
tupleLiteral,
diagnostics)
End Function
Private Function ReclassifyArrayLiteralExpression(
arrayLiteral As BoundArrayLiteral,
diagnostics As BindingDiagnosticBag
) As BoundExpression
Return ApplyImplicitConversion(arrayLiteral.Syntax,
arrayLiteral.InferredType,
arrayLiteral,
diagnostics)
End Function
Private Function ReclassifyUnboundLambdaExpression(
lambda As UnboundLambda,
diagnostics As BindingDiagnosticBag
) As BoundExpression
Return ApplyImplicitConversion(lambda.Syntax,
lambda.InferredAnonymousDelegate.Key,
lambda,
diagnostics,
isOperandOfConditionalBranch:=False)
End Function
Private Function BindAssignmentTarget(
node As ExpressionSyntax,
diagnostics As BindingDiagnosticBag
) As BoundExpression
Dim expression = BindExpression(node, diagnostics)
Return BindAssignmentTarget(node, expression, diagnostics)
End Function
Private Function BindAssignmentTarget(
node As SyntaxNode,
expression As BoundExpression,
diagnostics As BindingDiagnosticBag
) As BoundExpression
expression = ReclassifyAsValue(expression, diagnostics)
If Not IsValidAssignmentTarget(expression) Then
If Not expression.HasErrors Then
ReportAssignmentToRValue(expression, diagnostics)
End If
expression = BadExpression(node, expression, LookupResultKind.NotAVariable, ErrorTypeSymbol.UnknownResultType)
ElseIf expression.Kind = BoundKind.LateInvocation Then
' Since this is a target of an assignment, it is guaranteed to be an array or a property,
' therefore, arguments will be passed ByVal, let's capture this fact in the tree,
' this will simplify analysis later.
Dim invocation = DirectCast(expression, BoundLateInvocation)
If Not invocation.ArgumentsOpt.IsEmpty Then
Dim newArguments(invocation.ArgumentsOpt.Length - 1) As BoundExpression
For i As Integer = 0 To newArguments.Length - 1
newArguments(i) = MakeRValue(invocation.ArgumentsOpt(i), diagnostics)
Next
expression = invocation.Update(invocation.Member,
newArguments.AsImmutableOrNull(),
invocation.ArgumentNamesOpt,
invocation.AccessKind,
invocation.MethodOrPropertyGroupOpt,
invocation.Type)
End If
End If
Return expression
End Function
Friend Shared Function IsValidAssignmentTarget(expression As BoundExpression) As Boolean
Select Case expression.Kind
Case BoundKind.PropertyAccess
Dim propertyAccess = DirectCast(expression, BoundPropertyAccess)
Dim [property] = propertyAccess.PropertySymbol
Dim receiver = propertyAccess.ReceiverOpt
Debug.Assert(propertyAccess.AccessKind <> PropertyAccessKind.Get)
Return propertyAccess.AccessKind <> PropertyAccessKind.Get AndAlso
([property].IsShared OrElse
receiver Is Nothing OrElse
receiver.IsLValue() OrElse
receiver.IsMeReference() OrElse
receiver.IsMyClassReference() OrElse
Not receiver.Type.IsValueType) ' If this logic changes, logic in UseTwiceRewriter.UseTwicePropertyAccess might need to change too.
Case BoundKind.XmlMemberAccess
Return IsValidAssignmentTarget(DirectCast(expression, BoundXmlMemberAccess).MemberAccess)
Case BoundKind.Call
Return DirectCast(expression, BoundCall).IsLValue
Case BoundKind.LateInvocation
Dim invocation = DirectCast(expression, BoundLateInvocation)
Debug.Assert(invocation.AccessKind <> LateBoundAccessKind.Get AndAlso invocation.AccessKind <> LateBoundAccessKind.Call)
Return invocation.AccessKind <> LateBoundAccessKind.Get AndAlso invocation.AccessKind <> LateBoundAccessKind.Call
Case BoundKind.LateMemberAccess
Dim member = DirectCast(expression, BoundLateMemberAccess)
Debug.Assert(member.AccessKind <> LateBoundAccessKind.Get AndAlso member.AccessKind <> LateBoundAccessKind.Call)
Return member.AccessKind <> LateBoundAccessKind.Get AndAlso member.AccessKind <> LateBoundAccessKind.Call
Case Else
Return expression.IsLValue
End Select
End Function
Private Shared Sub ReportAssignmentToRValue(expr As BoundExpression, diagnostics As BindingDiagnosticBag)
Dim err As ERRID
If expr.IsConstant Then
err = ERRID.ERR_CantAssignToConst
ElseIf ExpressionRefersToReadonlyVariable(expr) Then
err = ERRID.ERR_ReadOnlyAssignment
Else
err = ERRID.ERR_LValueRequired
End If
ReportDiagnostic(diagnostics, expr.Syntax, err)
End Sub
Public Shared Function ExpressionRefersToReadonlyVariable(
node As BoundExpression,
Optional digThroughProperty As Boolean = True
) As Boolean
' TODO: Check base expressions for properties if digThroughProperty==true.
If node.Kind = BoundKind.FieldAccess Then
Dim field = DirectCast(node, BoundFieldAccess)
If field.FieldSymbol.IsReadOnly Then
Return True
End If
Dim base = field.ReceiverOpt
If base IsNot Nothing AndAlso base.IsValue() AndAlso
base.Type.IsValueType Then
Return ExpressionRefersToReadonlyVariable(base, False)
End If
ElseIf node.Kind = BoundKind.Local Then
Return DirectCast(node, BoundLocal).LocalSymbol.IsReadOnly
End If
Return False
End Function
''' <summary>
''' Determine whether field access should be treated as LValue.
''' </summary>
Friend Function IsLValueFieldAccess(field As FieldSymbol, receiver As BoundExpression) As Boolean
If field.IsConst Then
Return False
End If
If Not field.IsShared AndAlso
receiver IsNot Nothing AndAlso
receiver.IsValue() Then
Dim receiverType = receiver.Type
Debug.Assert(Not receiverType.IsTypeParameter() OrElse receiverType.IsReferenceType,
"Member variable access through non-class constrained type param unexpected!!!")
' Dev10 comment:
' Note that this is needed so that we can determine whether the structure
' is not an LValue (eg: RField.m_x = 20 where goo is a readonly field of a
' structure type). In such cases, the structure's field m_x cannot be modified.
'
' This does not apply to type params because for type params we do want to
' allow setting the fields even in such scenarios because only class constrained
' type params have fields and readonly reference typed fields' fields can be
' modified.
If Not receiverType.IsTypeParameter() AndAlso
receiverType.IsValueType AndAlso
Not receiver.IsLValue() AndAlso
Not receiver.IsMeReference() AndAlso
Not receiver.IsMyClassReference() Then
Return False
End If
End If
If Not field.IsReadOnly Then
Return True
End If
Dim containingMethodKind As MethodKind = Me.KindOfContainingMethodAtRunTime()
If containingMethodKind = MethodKind.Constructor Then
If field.IsShared OrElse Not (receiver IsNot Nothing AndAlso receiver.IsInstanceReference()) Then
Return False
End If
ElseIf containingMethodKind = MethodKind.SharedConstructor Then
If Not field.IsShared Then
Return False
End If
Else
Return False
End If
' We are in constructor, now verify that the field belongs to constructor's type.
' Note, ReadOnly fields accessed in lambda within the constructor are not LValues because the
' lambda in the end will be generated as a separate procedure where the ReadOnly field is not
' an LValue. In this case, containingMember will be a LambdaSymbol rather than a symbol for
' constructor.
' We duplicate a bug in the native compiler for compatibility in non-strict mode
Return If(Me.Compilation.FeatureStrictEnabled,
Me.ContainingMember.ContainingSymbol Is field.ContainingSymbol,
Me.ContainingMember.ContainingSymbol.OriginalDefinition Is field.ContainingSymbol.OriginalDefinition)
End Function
''' <summary>
''' Return MethodKind corresponding to the method the code being interpreted is going to end up in.
''' </summary>
Private Function KindOfContainingMethodAtRunTime() As MethodKind
Dim containingMember = Me.ContainingMember
If containingMember IsNot Nothing Then
Select Case containingMember.Kind
Case SymbolKind.Method
' Binding a method body.
Return DirectCast(containingMember, MethodSymbol).MethodKind
Case SymbolKind.Field, SymbolKind.Property
' Binding field or property initializer.
If containingMember.IsShared Then
Return MethodKind.SharedConstructor
Else
Return MethodKind.Constructor
End If
Case SymbolKind.NamedType, SymbolKind.Namespace, SymbolKind.Parameter
Exit Select
Case Else
' What else can it be?
Throw ExceptionUtilities.UnexpectedValue(containingMember.Kind)
End Select
End If
Return MethodKind.Ordinary ' Looks like a good default.
End Function
Private Function BindTernaryConditionalExpression(node As TernaryConditionalExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
' bind arguments as values
Dim boundConditionArg = BindBooleanExpression(node.Condition, diagnostics)
Dim boundWhenTrueArg = BindValue(node.WhenTrue, diagnostics)
Dim boundWhenFalseArg = BindValue(node.WhenFalse, diagnostics)
Dim hasErrors = boundConditionArg.HasErrors OrElse boundWhenTrueArg.HasErrors OrElse boundWhenFalseArg.HasErrors
' infer dominant type of the resulting expression
Dim dominantType As TypeSymbol
If boundWhenTrueArg.IsNothingLiteral AndAlso boundWhenFalseArg.IsNothingLiteral Then
' From Dev10: backwards compatibility with Orcas... IF(b,Nothing,Nothing) infers Object with no complaint
dominantType = GetSpecialType(SpecialType.System_Object, node, diagnostics)
Else
Dim numCandidates As Integer = 0
Dim array = ArrayBuilder(Of BoundExpression).GetInstance(2)
array.Add(boundWhenTrueArg)
array.Add(boundWhenFalseArg)
dominantType = InferDominantTypeOfExpressions(node, array, diagnostics, numCandidates)
array.Free()
' check the resulting type
If Not hasErrors Then
hasErrors = GenerateDiagnosticsForDominantTypeInferenceInIfExpression(dominantType, numCandidates, node, diagnostics)
End If
End If
' Void type will be filtered out in BindValue calls
Debug.Assert(dominantType Is Nothing OrElse Not dominantType.IsVoidType())
' convert arguments to the dominant type if necessary
If Not hasErrors OrElse dominantType IsNot Nothing Then
boundWhenTrueArg = Me.ApplyImplicitConversion(node.WhenTrue, dominantType, boundWhenTrueArg, diagnostics)
boundWhenFalseArg = Me.ApplyImplicitConversion(node.WhenFalse, dominantType, boundWhenFalseArg, diagnostics)
hasErrors = hasErrors OrElse boundWhenTrueArg.HasErrors OrElse boundWhenFalseArg.HasErrors
Else
boundWhenTrueArg = MakeRValueAndIgnoreDiagnostics(boundWhenTrueArg)
boundWhenFalseArg = MakeRValueAndIgnoreDiagnostics(boundWhenFalseArg)
End If
' check for a constant value
Dim constVal As ConstantValue = Nothing
If Not hasErrors AndAlso IsConstantAllowingCompileTimeFolding(boundWhenTrueArg) AndAlso
IsConstantAllowingCompileTimeFolding(boundWhenFalseArg) AndAlso IsConstantAllowingCompileTimeFolding(boundConditionArg) Then
constVal = If(boundConditionArg.ConstantValueOpt.BooleanValue, boundWhenTrueArg.ConstantValueOpt, boundWhenFalseArg.ConstantValueOpt)
End If
Return New BoundTernaryConditionalExpression(node,
boundConditionArg,
boundWhenTrueArg,
boundWhenFalseArg,
constVal,
If(dominantType, ErrorTypeSymbol.UnknownResultType),
hasErrors:=hasErrors)
End Function
Private Shared Function IsConstantAllowingCompileTimeFolding(candidate As BoundExpression) As Boolean
Return candidate.IsConstant AndAlso
Not candidate.ConstantValueOpt.IsBad AndAlso
(candidate.IsNothingLiteral OrElse (candidate.Type IsNot Nothing AndAlso candidate.Type.AllowsCompileTimeOperations()))
End Function
Private Function BindBinaryConditionalExpression(node As BinaryConditionalExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
' bind arguments
Dim boundFirstArg = BindValue(node.FirstExpression, diagnostics)
Dim boundSecondArg = BindValue(node.SecondExpression, diagnostics)
Dim hasErrors = boundFirstArg.HasErrors OrElse boundSecondArg.HasErrors OrElse node.ContainsDiagnostics
' infer dominant type of the resulting expression
Dim dominantType As TypeSymbol
If boundFirstArg.IsNothingLiteral AndAlso boundSecondArg.IsNothingLiteral Then
' SPECIAL CASE (Dev10): IF(Nothing,Nothing) yields type System.Object
' NOTE: Reuse System.Object from boundFirstArg or boundSecondArg if exists
dominantType = If(boundFirstArg.Type, If(boundSecondArg.Type, GetSpecialType(SpecialType.System_Object, node, diagnostics)))
ElseIf boundFirstArg.Type IsNot Nothing AndAlso boundFirstArg.Type.IsNullableType AndAlso boundSecondArg.IsNothingLiteral Then
' SPECIAL CASE (Dev10): IF(Nullable<T>, Nothing) yields type Nullable<T>, whereas IF(Nullable<Int>, Int) yields Int.
dominantType = boundFirstArg.Type
Else
' calculate dominant type
Dim numCandidates As Integer = 0
Dim array = ArrayBuilder(Of BoundExpression).GetInstance(2)
If boundFirstArg.Type IsNot Nothing AndAlso boundFirstArg.Type.IsNullableType AndAlso
Not (boundSecondArg.Type IsNot Nothing AndAlso boundSecondArg.Type.IsNullableType) Then
' From Dev10: Special case: "nullable lifting": when the first argument has a value of nullable
' data type and the second does not, the first is being converted to underlying type
' create a temp variable
Dim underlyingType = boundFirstArg.Type.GetNullableUnderlyingType
array.Add(New BoundRValuePlaceholder(node.FirstExpression,
underlyingType))
Else
array.Add(boundFirstArg)
End If
array.Add(boundSecondArg)
dominantType = InferDominantTypeOfExpressions(node, array, diagnostics, numCandidates)
array.Free()
' check the resulting type
If Not hasErrors Then
hasErrors = GenerateDiagnosticsForDominantTypeInferenceInIfExpression(dominantType, numCandidates, node, diagnostics)
End If
End If
' check for a constant value
If Not hasErrors AndAlso IsConstantAllowingCompileTimeFolding(boundFirstArg) AndAlso
IsConstantAllowingCompileTimeFolding(boundSecondArg) AndAlso
(boundFirstArg.IsNothingLiteral OrElse boundFirstArg.ConstantValueOpt.IsString) Then
Dim constVal As ConstantValue
If (boundFirstArg.IsNothingLiteral) Then
constVal = boundSecondArg.ConstantValueOpt
If Not boundSecondArg.IsNothingLiteral Then
dominantType = boundSecondArg.Type
Else
Debug.Assert(dominantType.IsObjectType)
End If
Else
constVal = boundFirstArg.ConstantValueOpt
dominantType = boundFirstArg.Type
End If
' return binary conditional expression to be constant-folded later
Return AnalyzeConversionAndCreateBinaryConditionalExpression(
node,
testExpression:=boundFirstArg,
elseExpression:=boundSecondArg,
constantValueOpt:=constVal,
type:=dominantType,
hasErrors:=False,
diagnostics:=diagnostics)
End If
' NOTE: no constant folding after this point
' By this time Void type will be filtered out in BindValue calls, and empty dominant type reported as an error
Debug.Assert(hasErrors OrElse (dominantType IsNot Nothing AndAlso Not dominantType.IsVoidType()))
' address some cases of Type being Nothing before making an RValue of it
If Not hasErrors AndAlso boundFirstArg.Type Is Nothing Then
If boundFirstArg.IsNothingLiteral Then
' leave Nothing literal unchanged
Else
' convert lambdas, AddressOf, etc. to dominant type
boundFirstArg = Me.ApplyImplicitConversion(node.FirstExpression, dominantType, boundFirstArg, diagnostics)
hasErrors = boundFirstArg.HasErrors
End If
End If
' TODO: Address array initializers, they might need to change type.
' make r-value out of boundFirstArg; this will reclassify property access from Unknown to Get and
' also mark not-nothing expressions without type with errors
If boundFirstArg.IsNothingLiteral Then
' Don't do anything for nothing literal
ElseIf Not hasErrors Then
boundFirstArg = MakeRValue(boundFirstArg, diagnostics)
hasErrors = boundFirstArg.HasErrors
Else
boundFirstArg = MakeRValueAndIgnoreDiagnostics(boundFirstArg)
End If
' Type of the first expression should be set by now
Debug.Assert(hasErrors OrElse boundFirstArg.IsNothingLiteral OrElse boundFirstArg.Type IsNot Nothing)
Dim boundSecondArgWithConversions As BoundExpression = boundSecondArg
If Not hasErrors Then
boundSecondArgWithConversions = Me.ApplyImplicitConversion(node.SecondExpression, dominantType, boundSecondArg, diagnostics)
hasErrors = boundSecondArgWithConversions.HasErrors
Else
boundSecondArgWithConversions = MakeRValueAndIgnoreDiagnostics(boundSecondArg)
End If
' If there are still no errors check the original type of the first argument. First, we check
' the pre-VB 16.0 condition, which is the first operand must be Nothing, a reference type, or
' a nullable value type
If Not hasErrors AndAlso Not (boundFirstArg.IsNothingLiteral OrElse boundFirstArg.Type.IsNullableType OrElse boundFirstArg.Type.IsReferenceType) Then
' VB 16 changed the requirements on the first operand to permit unconstrained type parameters. If we're in that scenario,
' ensure that the feature is enabled and report an error if it is not
If Not boundFirstArg.Type.IsValueType Then
InternalSyntax.Parser.CheckFeatureAvailability(diagnostics,
node.Location,
DirectCast(node.SyntaxTree.Options, VisualBasicParseOptions).LanguageVersion,
InternalSyntax.Feature.UnconstrainedTypeParameterInConditional)
Else
ReportDiagnostic(diagnostics, node.FirstExpression, ERRID.ERR_IllegalCondTypeInIIF)
hasErrors = True
End If
End If
Return AnalyzeConversionAndCreateBinaryConditionalExpression(
node,
testExpression:=boundFirstArg,
elseExpression:=boundSecondArgWithConversions,
constantValueOpt:=Nothing,
type:=If(dominantType, ErrorTypeSymbol.UnknownResultType),
hasErrors:=hasErrors,
diagnostics:=diagnostics)
End Function
Private Function AnalyzeConversionAndCreateBinaryConditionalExpression(
syntax As SyntaxNode,
testExpression As BoundExpression,
elseExpression As BoundExpression,
constantValueOpt As ConstantValue,
type As TypeSymbol,
hasErrors As Boolean,
diagnostics As BindingDiagnosticBag,
Optional explicitConversion As Boolean = False) As BoundExpression
Dim convertedTestExpression As BoundExpression = Nothing
Dim placeholder As BoundRValuePlaceholder = Nothing
If Not hasErrors Then
' Do we need to apply a placeholder?
If Not testExpression.IsConstant Then
Debug.Assert(Not testExpression.IsLValue)
placeholder = New BoundRValuePlaceholder(testExpression.Syntax, testExpression.Type.GetNullableUnderlyingTypeOrSelf())
End If
' apply a conversion
convertedTestExpression = ApplyConversion(testExpression.Syntax, type,
If(placeholder, testExpression),
explicitConversion, diagnostics)
If convertedTestExpression Is If(placeholder, testExpression) Then
convertedTestExpression = Nothing
placeholder = Nothing
End If
End If
Return New BoundBinaryConditionalExpression(syntax,
testExpression:=testExpression,
convertedTestExpression:=convertedTestExpression,
testExpressionPlaceholder:=placeholder,
elseExpression:=elseExpression,
constantValueOpt:=constantValueOpt,
type:=type,
hasErrors:=hasErrors)
End Function
''' <summary> Process the result of dominant type inference, generate diagnostics </summary>
Private Function GenerateDiagnosticsForDominantTypeInferenceInIfExpression(dominantType As TypeSymbol, numCandidates As Integer,
node As ExpressionSyntax, diagnostics As BindingDiagnosticBag) As Boolean
Dim hasErrors As Boolean = False
If dominantType Is Nothing Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_IfNoType)
hasErrors = True
ElseIf numCandidates = 0 Then
' TODO: Is this reachable? Check and add tests
If OptionStrict = VisualBasic.OptionStrict.On Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_IfNoTypeObjectDisallowed)
hasErrors = True
ElseIf OptionStrict = VisualBasic.OptionStrict.Custom Then
ReportDiagnostic(diagnostics, node, ErrorFactory.ErrorInfo(ERRID.WRN_ObjectAssumed1, ErrorFactory.ErrorInfo(ERRID.WRN_IfNoTypeObjectAssumed)))
End If
ElseIf numCandidates > 1 Then
If OptionStrict = VisualBasic.OptionStrict.On Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_IfTooManyTypesObjectDisallowed)
hasErrors = True
ElseIf OptionStrict = VisualBasic.OptionStrict.Custom Then
ReportDiagnostic(diagnostics, node, ErrorFactory.ErrorInfo(ERRID.WRN_ObjectAssumed1, ErrorFactory.ErrorInfo(ERRID.WRN_IfTooManyTypesObjectAssumed)))
End If
End If
Return hasErrors
End Function
''' <summary>
''' True if inside in binding arguments of constructor
''' call with {'Me'/'MyClass'/'MyBase'}.New(...) from another constructor
''' </summary>
Protected Overridable ReadOnly Property IsInsideChainedConstructorCallArguments As Boolean
Get
Return Me.ContainingBinder.IsInsideChainedConstructorCallArguments
End Get
End Property
Private Function IsMeOrMyBaseOrMyClassInSharedContext() As Boolean
' If we are inside an attribute then we are not in an instance context.
If Me.BindingLocation = VisualBasic.BindingLocation.Attribute Then
Return True
End If
Dim containingMember = Me.ContainingMember
If containingMember IsNot Nothing Then
Select Case containingMember.Kind
Case SymbolKind.Method, SymbolKind.Property
Return containingMember.IsShared OrElse
Me.ContainingType.IsModuleType
Case SymbolKind.Field
Return containingMember.IsShared OrElse
Me.ContainingType.IsModuleType OrElse
DirectCast(containingMember, FieldSymbol).IsConst
End Select
End If
Return True
End Function
Private Function CheckMeOrMyBaseOrMyClassInSharedOrDisallowedContext(implicitReference As Boolean, <Out()> ByRef errorId As ERRID) As Boolean
errorId = Nothing
' Any executable statement in a script class can access Me/MyClass/MyBase implicitly but not explicitly.
' No code in a script class is shared.
Dim containingType = Me.ContainingType
If containingType IsNot Nothing AndAlso containingType.IsScriptClass Then
If implicitReference Then
Return True
Else
errorId = ERRID.ERR_KeywordNotAllowedInScript
Return False
End If
End If
If IsMeOrMyBaseOrMyClassInSharedContext() Then
errorId = If(implicitReference,
ERRID.ERR_BadInstanceMemberAccess,
If(containingType IsNot Nothing AndAlso containingType.IsModuleType,
ERRID.ERR_UseOfKeywordFromModule1,
ERRID.ERR_UseOfKeywordNotInInstanceMethod1))
Return False
ElseIf IsInsideChainedConstructorCallArguments Then
errorId = If(implicitReference, ERRID.ERR_InvalidImplicitMeReference, ERRID.ERR_InvalidMeReference)
Return False
End If
Return True
End Function
''' <summary>
''' Can we access MyBase in this location. If False is returned,
''' also returns the error id associated with that.
''' </summary>
Private Function CanAccessMyBase(implicitReference As Boolean, <Out()> ByRef errorId As ERRID) As Boolean
errorId = Nothing
If Not CheckMeOrMyBaseOrMyClassInSharedOrDisallowedContext(implicitReference, errorId) Then
Return False
End If
If ContainingType.IsStructureType Then
errorId = ERRID.ERR_UseOfKeywordFromStructure1
Return False
End If
' TODO: Find a test case for ERRID_UseOfKeywordOutsideClass1
Debug.Assert(ContainingType.IsClassType)
' TODO: Check for closures
Return True
End Function
Private Function CanAccessMeOrMyClass(implicitReference As Boolean, <Out()> ByRef errorId As ERRID) As Boolean
errorId = Nothing
Return CheckMeOrMyBaseOrMyClassInSharedOrDisallowedContext(implicitReference, errorId)
End Function
''' <summary>
''' Can we access Me in this location. If False is returned,
''' also returns the error id associated with that.
''' </summary>
Friend Function CanAccessMe(implicitReference As Boolean, <Out()> ByRef errorId As ERRID) As Boolean
errorId = Nothing
' TODO: Find a test case for ERRID_UseOfKeywordOutsideClass1
Return CanAccessMeOrMyClass(implicitReference, errorId)
End Function
''' <summary>
''' Can we access MyClass in this location. If False is returned,
''' also returns the error id associated with that.
''' </summary>
Private Function CanAccessMyClass(implicitReference As Boolean, <Out()> ByRef errorId As ERRID) As Boolean
errorId = Nothing
If Me.ContainingType IsNot Nothing AndAlso Me.ContainingType.IsModuleType Then
errorId = ERRID.ERR_MyClassNotInClass
Return False
End If
Return CanAccessMeOrMyClass(implicitReference, errorId)
End Function
Private Function BindMeExpression(node As MeExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundMeReference
Dim err As ERRID = Nothing
If Not CanAccessMe(False, err) Then
ReportDiagnostic(diagnostics, node, err, SyntaxFacts.GetText(node.Keyword.Kind))
Return New BoundMeReference(node, If(Me.ContainingType, ErrorTypeSymbol.UnknownResultType), hasErrors:=True)
End If
Return CreateMeReference(node)
End Function
' Create a reference to Me, without error checking.
Private Function CreateMeReference(node As SyntaxNode, Optional isSynthetic As Boolean = False) As BoundMeReference
Dim containingMethod = TryCast(ContainingMember, MethodSymbol)
Dim result = New BoundMeReference(node, If(Me.ContainingType, ErrorTypeSymbol.UnknownResultType))
If isSynthetic Then
result.SetWasCompilerGenerated()
End If
Return result
End Function
Private Function BindMyBaseExpression(node As MyBaseExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundMyBaseReference
Dim err As ERRID = Nothing
If Not CanAccessMyBase(False, err) Then
ReportDiagnostic(diagnostics, node, err, SyntaxFacts.GetText(node.Keyword.Kind))
Return New BoundMyBaseReference(node, If(Me.ContainingType IsNot Nothing, Me.ContainingType.BaseTypeNoUseSiteDiagnostics, ErrorTypeSymbol.UnknownResultType), hasErrors:=True)
End If
Dim containingMethod = TryCast(ContainingMember, MethodSymbol)
Return New BoundMyBaseReference(node, If(Me.ContainingType IsNot Nothing, Me.ContainingType.BaseTypeNoUseSiteDiagnostics, ErrorTypeSymbol.UnknownResultType))
End Function
Private Function BindMyClassExpression(node As MyClassExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundMyClassReference
Dim err As ERRID = Nothing
If Not CanAccessMyClass(False, err) Then
ReportDiagnostic(diagnostics, node, err, SyntaxFacts.GetText(node.Keyword.Kind))
Return New BoundMyClassReference(node, If(Me.ContainingType, ErrorTypeSymbol.UnknownResultType), hasErrors:=True)
End If
Dim containingMethod = TryCast(ContainingMember, MethodSymbol)
Return New BoundMyClassReference(node, If(Me.ContainingType, ErrorTypeSymbol.UnknownResultType))
End Function
' Can the given name syntax be an implicit declared variables. Only some syntactic locations are permissible
' for implicitly declared variables. They are disallowed in:
' target of invocation
' LHS of member access
'
' Also, For, For Each, and Catch can implicitly declare a variable, but that implicit declaration has
' different rules that is handled directly in the binding of those statements. Thus, they are disallowed here.
'
' Finally, Dev10 disallows 3 special names: "Null", "Empty", and "Rnd".
Private Shared Function CanBeImplicitVariableDeclaration(nameSyntax As SimpleNameSyntax) As Boolean
' Disallow generic names.
If nameSyntax.Kind <> SyntaxKind.IdentifierName Then
Return False
End If
Dim parent As VisualBasicSyntaxNode = nameSyntax.Parent
If parent IsNot Nothing Then
Select Case parent.Kind
Case SyntaxKind.SimpleMemberAccessExpression ' intentionally NOT SyntaxKind.DictionaryAccess
If DirectCast(parent, MemberAccessExpressionSyntax).Expression Is nameSyntax Then
Return False
End If
Case SyntaxKind.InvocationExpression
If DirectCast(parent, InvocationExpressionSyntax).Expression Is nameSyntax Then
' Name is the expression part of an invocation.
Return False
End If
Case SyntaxKind.ConditionalAccessExpression
Dim conditionalAccess = DirectCast(parent, ConditionalAccessExpressionSyntax)
If conditionalAccess.Expression Is nameSyntax Then
Dim leaf As ExpressionSyntax = conditionalAccess.GetLeafAccess()
If leaf IsNot Nothing AndAlso
(leaf.Kind = SyntaxKind.SimpleMemberAccessExpression OrElse leaf.Kind = SyntaxKind.InvocationExpression) Then
Return False
End If
End If
Case SyntaxKind.CatchStatement
If DirectCast(parent, CatchStatementSyntax).IdentifierName Is nameSyntax Then
Return False
End If
End Select
End If
' Dev10 disallows implicit variable creation for "Null", "Empty", and "RND".
Dim name As String = MakeHalfWidthIdentifier(nameSyntax.Identifier.ValueText)
If CaseInsensitiveComparison.Equals(name, "Null") OrElse CaseInsensitiveComparison.Equals(name, "Empty") OrElse CaseInsensitiveComparison.Equals(name, "RND") Then
Return False
End If
Return True
End Function
' "isInvocationOrAddressOf" indicates that the name is being bound as the left hand side of an invocation
' or the argument of an AddressOf, and the return value variable should not be bound to.
Private Function BindSimpleName(node As SimpleNameSyntax,
isInvocationOrAddressOf As Boolean,
diagnostics As BindingDiagnosticBag,
Optional skipLocalsAndParameters As Boolean = False) As BoundExpression
Dim name As String
Dim typeArguments As TypeArgumentListSyntax
#If DEBUG Then
If CanBeImplicitVariableDeclaration(node) Then
CheckSimpleNameBindingOrder(node)
End If
#End If
If node.Kind = SyntaxKind.GenericName Then
Dim genericName = DirectCast(node, GenericNameSyntax)
typeArguments = genericName.TypeArgumentList
name = genericName.Identifier.ValueText
Else
Debug.Assert(node.Kind = SyntaxKind.IdentifierName)
typeArguments = Nothing
name = DirectCast(node, IdentifierNameSyntax).Identifier.ValueText
End If
If String.IsNullOrEmpty(name) Then
' Empty string must have been a syntax error.
' Just produce a bad expression and get out without producing any new errors.
Return BadExpression(node, ErrorTypeSymbol.UnknownResultType)
End If
Dim options As LookupOptions = LookupOptions.AllMethodsOfAnyArity ' overload resolution filters methods by arity.
If isInvocationOrAddressOf Then
options = options Or LookupOptions.MustNotBeReturnValueVariable
End If
If skipLocalsAndParameters Then
options = options Or LookupOptions.MustNotBeLocalOrParameter
End If
' Handle a case of being able to refer to System.Int32 through System.Integer.
' Same for other intrinsic types with intrinsic name different from emitted name.
If node.Kind = SyntaxKind.IdentifierName AndAlso DirectCast(node, IdentifierNameSyntax).Identifier.IsBracketed AndAlso
MemberLookup.GetTypeForIntrinsicAlias(name) <> SpecialType.None Then
options = options Or LookupOptions.AllowIntrinsicAliases
End If
Dim arity As Integer = If(typeArguments IsNot Nothing, typeArguments.Arguments.Count, 0)
Dim result As LookupResult = LookupResult.GetInstance()
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Me.Lookup(result, name, arity, options, useSiteInfo)
diagnostics.Add(node, useSiteInfo)
If Not result.IsGoodOrAmbiguous AndAlso
Me.ImplicitVariableDeclarationAllowed AndAlso
Not Me.AllImplicitVariableDeclarationsAreHandled AndAlso
CanBeImplicitVariableDeclaration(node) Then
' Declare an implicit local variable.
Dim implicitLocal As LocalSymbol = DeclareImplicitLocalVariable(DirectCast(node, IdentifierNameSyntax), diagnostics)
result.SetFrom(implicitLocal)
End If
If Not result.HasSymbol Then
' Did not find anything with that name.
result.Free()
' If the name represents an imported XML namespace prefix, report a specific error.
Dim [namespace] As String = Nothing
Dim fromImports = False
If LookupXmlNamespace(name, ignoreXmlNodes:=True, [namespace]:=[namespace], fromImports:=fromImports) Then
Return ReportDiagnosticAndProduceBadExpression(diagnostics, node, ERRID.ERR_XmlPrefixNotExpression, name)
End If
Dim errorInfo As DiagnosticInfo = Nothing
If node.Kind = SyntaxKind.IdentifierName Then
Select Case KeywordTable.TokenOfString(name)
Case SyntaxKind.AwaitKeyword
errorInfo = GetAwaitInNonAsyncError()
End Select
End If
If errorInfo Is Nothing Then
'Check for My and use of VB Embed Runtime usage for different diagnostic
If IdentifierComparison.Equals(MissingRuntimeMemberDiagnosticHelper.MyVBNamespace, name) AndAlso Me.Compilation.Options.EmbedVbCoreRuntime Then
errorInfo = ErrorFactory.ErrorInfo(ERRID.ERR_PlatformDoesntSupport, MissingRuntimeMemberDiagnosticHelper.MyVBNamespace)
Else
errorInfo = ErrorFactory.ErrorInfo(If(Me.IsInQuery, ERRID.ERR_QueryNameNotDeclared, ERRID.ERR_NameNotDeclared1),
name)
End If
End If
Return ReportDiagnosticAndProduceBadExpression(diagnostics, node, errorInfo)
End If
Dim boundExpr As BoundExpression = BindSimpleName(result, node, options, typeArguments, diagnostics)
result.Free()
Return boundExpr
End Function
''' <summary>
''' Second part of BindSimpleName.
''' It is a separate function so that it could be called directly
''' when we have already looked up for the name.
''' </summary>
Private Function BindSimpleName(result As LookupResult,
node As VisualBasicSyntaxNode,
options As LookupOptions,
typeArguments As TypeArgumentListSyntax,
diagnostics As BindingDiagnosticBag) As BoundExpression
' An implicit Me is inserted if we found something in the immediate containing type.
' Note that validation of whether Me can actually be used in this case is deferred until after
' overload resolution determines if we are accessing a static or instance member.
Dim receiver As BoundExpression = Nothing
Dim containingType = Me.ContainingType
If containingType IsNot Nothing Then
If containingType.IsScriptClass Then
Dim memberDeclaringType = result.Symbols(0).ContainingType
If memberDeclaringType IsNot Nothing Then
receiver = TryBindInteractiveReceiver(node, Me.ContainingMember, containingType, memberDeclaringType)
End If
End If
If receiver Is Nothing Then
Dim symbol = result.Symbols(0)
If symbol.IsReducedExtensionMethod() OrElse BindSimpleNameIsMemberOfType(symbol, containingType) Then
receiver = CreateMeReference(node, isSynthetic:=True)
End If
End If
End If
Dim boundExpr As BoundExpression = BindSymbolAccess(node, result, options, receiver, typeArguments, QualificationKind.Unqualified, diagnostics)
Return boundExpr
End Function
Private Shared Function BindSimpleNameIsMemberOfType(member As Symbol, type As NamedTypeSymbol) As Boolean
Debug.Assert(type IsNot Nothing)
Debug.Assert(member IsNot Nothing)
Select Case member.Kind
Case SymbolKind.Field, SymbolKind.Method, SymbolKind.Property, SymbolKind.Event
Dim container = member.ContainingType
If container Is Nothing Then Return False
Dim currentType = type
Do While currentType IsNot Nothing
If container.Equals(currentType) Then
Return True
End If
currentType = currentType.BaseTypeNoUseSiteDiagnostics
Loop
End Select
Return False
End Function
Private Function TryBindInteractiveReceiver(syntax As VisualBasicSyntaxNode, currentMember As Symbol, currentType As NamedTypeSymbol, memberDeclaringType As NamedTypeSymbol) As BoundExpression
If currentType.TypeKind = TYPEKIND.Submission AndAlso Not currentMember.IsShared Then
If memberDeclaringType.TypeKind = TYPEKIND.Submission Then
Return New BoundPreviousSubmissionReference(syntax, currentType, memberDeclaringType)
Else
' TODO (tomat): host object binding
'Dim hostObjectType As TypeSymbol = Compilation.GetHostObjectTypeSymbol()
'If hostObjectType IsNot Nothing AndAlso (hostObjectType = memberDeclaringType OrElse hostObjectType.BaseClassesContain(memberDeclaringType)) Then
' Return New BoundHostObjectMemberReference(syntax, hostObjectType)
'End If
End If
End If
Return Nothing
End Function
Private Function BindMemberAccess(node As MemberAccessExpressionSyntax, eventContext As Boolean, diagnostics As BindingDiagnosticBag) As BoundExpression
Dim leftOpt = node.Expression
Dim boundLeft As BoundExpression = Nothing
Dim rightName As SimpleNameSyntax = node.Name
If leftOpt Is Nothing Then
' 11.6 Member Access Expressions: "1. If E is omitted, then the expression from the
' immediately containing With statement is substituted for E and the member access
' is performed. If there is no containing With statement, a compile-time error occurs."
' NOTE: If there are no enclosing anonymous type creation or With statement, the method below will
' report error ERR_BadWithRef; otherwise 'the closest' binder (either AnonymousTypeCreationBinder
' or WithStatementBinder (to be created)) should handle binding of such expression
Dim wholeMemberAccessExpressionBound As Boolean = False
Dim conditionalAccess As ConditionalAccessExpressionSyntax = node.GetCorrespondingConditionalAccessExpression()
If conditionalAccess IsNot Nothing Then
boundLeft = GetConditionalAccessReceiver(conditionalAccess)
Else
boundLeft = Me.TryBindOmittedLeftForMemberAccess(node, diagnostics, Me, wholeMemberAccessExpressionBound)
End If
If boundLeft Is Nothing Then
Debug.Assert(Not wholeMemberAccessExpressionBound)
Return ReportDiagnosticAndProduceBadExpression(diagnostics, node, ERRID.ERR_BadWithRef)
End If
If wholeMemberAccessExpressionBound Then
' In case TryBindOmittedLeftForMemberAccess bound the whole member
' access expression syntax node just return the result
Return boundLeft
End If
Else
boundLeft = BindLeftOfPotentialColorColorMemberAccess(node, leftOpt, diagnostics)
End If
Return Me.BindMemberAccess(node, boundLeft, rightName, eventContext, diagnostics)
End Function
Private Function BindLeftOfPotentialColorColorMemberAccess(parentNode As MemberAccessExpressionSyntax, leftOpt As ExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
' handle for Color Color case:
'
' ======= 11.6.1 Identical Type and Member Names
' It is not uncommon to name members using the same name as their type. In that situation, however,
' inconvenient name hiding can occur:
'
' Enum Color
' Red
' Green
' Yellow
' End Enum
'
' Class Test
' ReadOnly Property Color() As Color
' Get
' Return Color.Red
' End Get
' End Property
'
' Shared Function DefaultColor() As Color
' Return Color.Green ' Binds to the instance property!
' End Function
' End Class
'
' In the previous example, the simple name Color in DefaultColor binds to the instance property
' instead of the type. Because an instance member cannot be referenced in a shared member,
' this would normally be an error.
'
' However, a special rule allows access to the type in this case. If the base expression
' of a member access expression is a simple name and binds to a constant, field, property,
' local variable or parameter whose type has the same name, then the base expression can refer
' either to the member or the type. This can never result in ambiguity because the members
' that can be accessed off of either one are the same.
'
' In the case that such a base expression binds to an instance member but the binding occurs
' within a context in which "Me" is not accessible, the expression instead binds to the
' type (if applicable).
'
' If the base expression cannot be successfully disambiguated by the context in which it
' occurs, it binds to the member. This can occur in particular in late-bound calls or
' error conditions.
If leftOpt.Kind = SyntaxKind.IdentifierName Then
Dim node = DirectCast(leftOpt, SimpleNameSyntax)
Dim leftDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics)
Dim boundLeft = Me.BindSimpleName(node, False, leftDiagnostics)
Dim boundValue = boundLeft
Dim propertyDiagnostics As BindingDiagnosticBag = Nothing
If boundLeft.Kind = BoundKind.PropertyGroup Then
propertyDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics)
boundValue = Me.AdjustReceiverValue(boundLeft, node, propertyDiagnostics)
End If
Dim leftSymbol = boundValue.ExpressionSymbol
If leftSymbol IsNot Nothing Then
Dim leftType As TypeSymbol
Dim isInstanceMember As Boolean
Select Case leftSymbol.Kind
Case SymbolKind.Field, SymbolKind.Property
Debug.Assert(boundValue.Type IsNot Nothing)
leftType = boundValue.Type
isInstanceMember = Not leftSymbol.IsShared
Case SymbolKind.Local, SymbolKind.Parameter, SymbolKind.RangeVariable
Debug.Assert(boundValue.Type IsNot Nothing)
leftType = boundValue.Type
isInstanceMember = False
Case Else
leftType = Nothing
isInstanceMember = False
End Select
If leftType IsNot Nothing Then
Dim leftName = node.Identifier.ValueText
If CaseInsensitiveComparison.Equals(leftType.Name, leftName) AndAlso leftType.TypeKind <> TYPEKIND.TypeParameter Then
Dim typeDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics)
Dim boundType = Me.BindNamespaceOrTypeExpression(node, typeDiagnostics)
If TypeSymbol.Equals(boundType.Type, leftType, TypeCompareKind.ConsiderEverything) Then
Dim err As ERRID = Nothing
If isInstanceMember AndAlso (Not CanAccessMe(implicitReference:=True, errorId:=err) OrElse Not BindSimpleNameIsMemberOfType(leftSymbol, ContainingType)) Then
diagnostics.AddRange(typeDiagnostics)
leftDiagnostics.Free()
Return boundType
End If
Dim valueDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics)
valueDiagnostics.AddRangeAndFree(leftDiagnostics)
If propertyDiagnostics IsNot Nothing Then
valueDiagnostics.AddRangeAndFree(propertyDiagnostics)
End If
Return New BoundTypeOrValueExpression(leftOpt, New BoundTypeOrValueData(boundValue, valueDiagnostics.ToReadOnlyAndFree(), boundType, typeDiagnostics.ToReadOnlyAndFree()), leftType)
End If
typeDiagnostics.Free()
End If
End If
End If
If propertyDiagnostics IsNot Nothing Then
propertyDiagnostics.Free()
End If
diagnostics.AddRangeAndFree(leftDiagnostics)
Return boundLeft
End If
' Not a Color Color case; just bind the LHS as an expression.
If leftOpt.Kind = SyntaxKind.SimpleMemberAccessExpression Then
Return BindMemberAccess(DirectCast(leftOpt, MemberAccessExpressionSyntax), eventContext:=False, diagnostics:=diagnostics)
Else
Return Me.BindExpression(leftOpt, diagnostics)
End If
End Function
''' <summary>
''' Method binds member access in case when we got hold
''' of a bound node representing the left expression
''' </summary>
''' <remarks>
''' The method is protected, so that it can be called from other
''' binders overriding TryBindMemberAccessWithLeftOmitted
''' </remarks>
Protected Function BindMemberAccess(node As VisualBasicSyntaxNode, left As BoundExpression, right As SimpleNameSyntax, eventContext As Boolean, diagnostics As BindingDiagnosticBag) As BoundExpression
Debug.Assert(node IsNot Nothing)
Debug.Assert(left IsNot Nothing)
Debug.Assert(right IsNot Nothing)
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
' Check if 'left' is of type which is a class or struct and 'name' is "New"
Dim leftTypeSymbol As TypeSymbol = left.Type
If leftTypeSymbol IsNot Nothing AndAlso (right.Kind = SyntaxKind.IdentifierName OrElse right.Kind = SyntaxKind.GenericName) Then
' Get the name syntax token
Dim identifier = If(right.Kind = SyntaxKind.IdentifierName,
DirectCast(right, IdentifierNameSyntax).Identifier,
DirectCast(right, GenericNameSyntax).Identifier)
If Not identifier.IsBracketed AndAlso
CaseInsensitiveComparison.Equals(identifier.ValueText, SyntaxFacts.GetText(SyntaxKind.NewKeyword)) Then
If leftTypeSymbol.IsArrayType() Then
' No instance constructors found. Can't call constructor on an array type.
If (left.HasErrors) Then
Return BadExpression(node, left, ErrorTypeSymbol.UnknownResultType)
End If
Return ReportDiagnosticAndProduceBadExpression(
diagnostics, node, ErrorFactory.ErrorInfo(ERRID.ERR_ConstructorNotFound1, leftTypeSymbol), left)
End If
Dim leftTypeKind As TYPEKIND = leftTypeSymbol.TypeKind
If leftTypeKind = TYPEKIND.Class OrElse leftTypeKind = TYPEKIND.Structure OrElse leftTypeKind = TYPEKIND.Module Then
' Bind to method group representing available instance constructors
Dim namedLeftTypeSymbol = DirectCast(leftTypeSymbol, NamedTypeSymbol)
Dim accessibleConstructors = GetAccessibleConstructors(namedLeftTypeSymbol, useSiteInfo)
diagnostics.Add(node, useSiteInfo)
useSiteInfo = New CompoundUseSiteInfo(Of AssemblySymbol)(useSiteInfo)
If accessibleConstructors.IsEmpty Then
' No instance constructors found
If (left.HasErrors) Then
Return BadExpression(node, left, ErrorTypeSymbol.UnknownResultType)
End If
Return ReportDiagnosticAndProduceBadExpression(
diagnostics, node, ErrorFactory.ErrorInfo(ERRID.ERR_ConstructorNotFound1, namedLeftTypeSymbol), left)
Else
Dim hasErrors As Boolean = left.HasErrors
If Not hasErrors AndAlso right.Kind = SyntaxKind.GenericName Then
' Report error BC30282
ReportDiagnostic(diagnostics, node, ERRID.ERR_InvalidConstructorCall)
hasErrors = True
End If
' Create a method group consisting of all instance constructors
Return New BoundMethodGroup(node, Nothing, accessibleConstructors, LookupResultKind.Good, left,
If(left.Kind = BoundKind.TypeExpression, QualificationKind.QualifiedViaTypeName, QualificationKind.QualifiedViaValue),
hasErrors)
End If
End If
End If
End If
Dim type As TypeSymbol
Dim rightName As String
Dim typeArguments As TypeArgumentListSyntax
If right.Kind = SyntaxKind.GenericName Then
Dim genericName = DirectCast(right, GenericNameSyntax)
typeArguments = genericName.TypeArgumentList
rightName = genericName.Identifier.ValueText
Else
Debug.Assert(right.Kind = SyntaxKind.IdentifierName)
typeArguments = Nothing
rightName = DirectCast(right, IdentifierNameSyntax).Identifier.ValueText
End If
Dim rightArity As Integer = If(typeArguments IsNot Nothing, typeArguments.Arguments.Count, 0)
Dim lookupResult As LookupResult = lookupResult.GetInstance()
Dim options As LookupOptions = LookupOptions.AllMethodsOfAnyArity
Try
If left.Kind = BoundKind.NamespaceExpression Then
If String.IsNullOrEmpty(rightName) Then
' Must have been a syntax error.
Return BadExpression(node, left, ErrorTypeSymbol.UnknownResultType)
End If
Dim ns As NamespaceSymbol = DirectCast(left, BoundNamespaceExpression).NamespaceSymbol
' Handle a case of being able to refer to System.Int32 through System.Integer.
' Same for other intrinsic types with intrinsic name different from emitted name.
If right.Kind = SyntaxKind.IdentifierName AndAlso node.Kind = SyntaxKind.SimpleMemberAccessExpression Then
options = options Or LookupOptions.AllowIntrinsicAliases
End If
MemberLookup.Lookup(lookupResult, ns, rightName, rightArity, options, Me, useSiteInfo) ' overload resolution filters methods by arity.
If lookupResult.HasSymbol Then
Return BindSymbolAccess(node, lookupResult, options, left, typeArguments, QualificationKind.QualifiedViaNamespace, diagnostics)
Else
Return ReportDiagnosticAndProduceBadExpression(diagnostics, node, ErrorFactory.ErrorInfo(ERRID.ERR_NameNotMember2, rightName, ns), left)
End If
ElseIf left.Kind = BoundKind.TypeExpression Then
type = DirectCast(left, BoundTypeExpression).Type
If type.TypeKind = TYPEKIND.TypeParameter Then
Return ReportDiagnosticAndProduceBadExpression(diagnostics, node, ErrorFactory.ErrorInfo(ERRID.ERR_TypeParamQualifierDisallowed), left)
Else
If String.IsNullOrEmpty(rightName) Then
' Must have been a syntax error.
Return BadExpression(node, left, ErrorTypeSymbol.UnknownResultType)
End If
LookupMember(lookupResult, type, rightName, rightArity, options, useSiteInfo) ' overload resolution filters methods by arity.
If lookupResult.HasSymbol Then
Return BindSymbolAccess(node, lookupResult, options, left, typeArguments, QualificationKind.QualifiedViaTypeName, diagnostics)
Else
Return ReportDiagnosticAndProduceBadExpression(diagnostics, node, ErrorFactory.ErrorInfo(ERRID.ERR_NameNotMember2, rightName, type), left)
End If
End If
Else
left = AdjustReceiverValue(left, node, diagnostics)
type = left.Type
If type Is Nothing OrElse type.IsErrorType() Then
Return BadExpression(node, left, ErrorTypeSymbol.UnknownResultType)
End If
If String.IsNullOrEmpty(rightName) Then
' Must have been a syntax error.
Return BadExpression(node, left, ErrorTypeSymbol.UnknownResultType)
End If
Dim effectiveOptions = If(left.Kind <> BoundKind.MyBaseReference, options,
options Or LookupOptions.UseBaseReferenceAccessibility)
If eventContext Then
effectiveOptions = effectiveOptions Or LookupOptions.EventsOnly
End If
LookupMember(lookupResult, type, rightName, rightArity, effectiveOptions, useSiteInfo) ' overload resolution filters methods by arity.
If lookupResult.HasSymbol Then
Return BindSymbolAccess(node, lookupResult, effectiveOptions, left, typeArguments, QualificationKind.QualifiedViaValue, diagnostics)
ElseIf (type.IsObjectType AndAlso Not left.IsMyBaseReference) OrElse type.IsExtensibleInterfaceNoUseSiteDiagnostics Then
Return BindLateBoundMemberAccess(node, rightName, typeArguments, left, type, diagnostics)
ElseIf left.HasErrors Then
Return BadExpression(node, left, ErrorTypeSymbol.UnknownResultType)
Else
If type.IsInterfaceType() Then
' In case IsExtensibleInterfaceNoUseSiteDiagnostics above failed because there were bad inherited interfaces.
type.AllInterfacesWithDefinitionUseSiteDiagnostics(useSiteInfo)
End If
Return ReportDiagnosticAndProduceBadExpression(diagnostics, node, ErrorFactory.ErrorInfo(ERRID.ERR_NameNotMember2, rightName, type), left)
End If
End If
Finally
diagnostics.Add(node, useSiteInfo)
lookupResult.Free()
End Try
End Function
''' <summary>
''' Returns a bound node for left part of member access node with omitted left syntax.
''' In particular it handles member access inside With statement.
'''
''' By default the method delegates the work to it's containing binder or returns Nothing.
''' </summary>
''' <param name="accessingBinder">
''' Specifies the binder which requests an access to the bound node for omitted left.
''' </param>
''' <param name="wholeMemberAccessExpressionBound">
''' NOTE: in some cases, like for binding inside anonymous object creation expression, this
''' method returns bound node for the whole expression rather than only for omitted left part.
''' </param>
Protected Friend Overridable Function TryBindOmittedLeftForMemberAccess(node As MemberAccessExpressionSyntax,
diagnostics As BindingDiagnosticBag,
accessingBinder As Binder,
<Out> ByRef wholeMemberAccessExpressionBound As Boolean) As BoundExpression
Debug.Assert(Me.ContainingBinder IsNot Nothing)
Return Me.ContainingBinder.TryBindOmittedLeftForMemberAccess(node, diagnostics, accessingBinder, wholeMemberAccessExpressionBound)
End Function
Protected Friend Overridable Function TryBindOmittedLeftForXmlMemberAccess(node As XmlMemberAccessExpressionSyntax,
diagnostics As BindingDiagnosticBag,
accessingBinder As Binder) As BoundExpression
Debug.Assert(Me.ContainingBinder IsNot Nothing)
Return Me.ContainingBinder.TryBindOmittedLeftForXmlMemberAccess(node, diagnostics, accessingBinder)
End Function
Private Function IsBindingImplicitlyTypedLocal(symbol As LocalSymbol) As Boolean
For Each s In Me.ImplicitlyTypedLocalsBeingBound
If s = symbol Then
Return True
End If
Next
Return False
End Function
''' <summary>
''' Given a localSymbol and a syntaxNode where the symbol is used, safely return the symbol's type.
''' </summary>
''' <param name="localSymbol">The local symbol</param>
''' <param name="node">The syntax node that references the symbol</param>
''' <param name="diagnostics">diagnostic bag if errors are to be reported</param>
''' <returns>Returns the symbol's type or an ErrorTypeSymbol if the local is referenced before its definition or if the symbol is still being bound.</returns>
''' <remarks>This method safely returns a local symbol's type by checking for circular references or references before declaration.</remarks>
Private Function GetLocalSymbolType(localSymbol As LocalSymbol, node As VisualBasicSyntaxNode, Optional diagnostics As BindingDiagnosticBag = Nothing) As TypeSymbol
Dim localType As TypeSymbol = Nothing
' Check if local symbol is used before it's definition.
' Do span comparison first in order to optimize performance for non-error cases.
If node IsNot Nothing AndAlso
node.SpanStart < localSymbol.IdentifierToken.SpanStart Then
Dim declarationLocation As Location = localSymbol.IdentifierLocation
Dim referenceLocation As Location = node.GetLocation()
If Not localSymbol.IsImplicitlyDeclared AndAlso
declarationLocation.IsInSource AndAlso
referenceLocation IsNot Nothing AndAlso referenceLocation.IsInSource AndAlso
declarationLocation.SourceTree Is referenceLocation.SourceTree Then
localType = localSymbol.UseBeforeDeclarationResultType
If diagnostics IsNot Nothing Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_UseOfLocalBeforeDeclaration1, localSymbol)
End If
End If
ElseIf IsBindingImplicitlyTypedLocal(localSymbol) Then
' We are currently in the process of binding this symbol.
' if constant knows its type, there is no circularity
' Example:
' Const x as Color = x.Red
If localSymbol.IsConst AndAlso localSymbol.ConstHasType Then
Return localSymbol.Type
End If
' NOTE: OptionInfer does not need to be checked before reporting the error
' locals only get to ImplicitlyTypedLocalsBeingBound if we actually infer
' their type, either because Option Infer is On or for other reason,
' we use UnknownResultType for such locals.
If diagnostics IsNot Nothing Then
If localSymbol.IsConst Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_CircularEvaluation1, localSymbol)
Else
ReportDiagnostic(diagnostics, node, ERRID.ERR_CircularInference1, localSymbol)
End If
End If
localType = ErrorTypeSymbol.UnknownResultType
End If
If localType Is Nothing Then
' It is safe to get the type from the symbol.
localType = localSymbol.Type
End If
Return localType
End Function
' Bind access to a symbol, either qualified (LHS.Symbol) or unqualified (Symbol). This kind of qualification is indicated by qualKind.
' receiver is set to a value expression indicating the receiver that the symbol is being accessed off of.
' lookupResult must refer to one or more symbols. If lookupResult has a diagnostic associated with it, that diagnostic is reported.
Private Function BindSymbolAccess(node As VisualBasicSyntaxNode,
lookupResult As LookupResult,
lookupOptionsUsed As LookupOptions,
receiver As BoundExpression,
typeArgumentsOpt As TypeArgumentListSyntax,
qualKind As QualificationKind,
diagnostics As BindingDiagnosticBag) As BoundExpression
Debug.Assert(lookupResult.HasSymbol)
Dim hasError As Boolean = False ' Is there an ERROR (not a warning).
If receiver IsNot Nothing Then
hasError = receiver.HasErrors ' don't report subsequent errors if LHS was already an error.
' If receiver is a namespace group, let's check if we can collapse it to a single or more narrow namespace
receiver = AdjustReceiverNamespace(lookupResult, receiver)
End If
Dim reportedLookupError As Boolean = False
Dim resultKind As LookupResultKind = lookupResult.Kind
If lookupResult.HasDiagnostic AndAlso
((lookupResult.Symbols(0).Kind <> SymbolKind.Method AndAlso lookupResult.Symbols(0).Kind <> SymbolKind.Property) OrElse
resultKind <> LookupResultKind.Inaccessible) Then
Debug.Assert(resultKind <> LookupResultKind.Good)
' Report the diagnostic with the symbol.
Dim di As DiagnosticInfo = lookupResult.Diagnostic
If Not hasError Then
If typeArgumentsOpt IsNot Nothing AndAlso
(lookupResult.Kind = LookupResultKind.WrongArity OrElse lookupResult.Kind = LookupResultKind.WrongArityAndStopLookup) Then
' Arity errors are reported on the type arguments only.
ReportDiagnostic(diagnostics, typeArgumentsOpt, di)
Else
ReportDiagnostic(diagnostics, node, di)
End If
If di.Severity = DiagnosticSeverity.Error Then
hasError = True
reportedLookupError = True
End If
End If
' For non-overloadable symbols (everything but property/method)
' Create a BoundBadExpression to encapsulate all the
' symbols and the result kind.
' The type of the expression is the common type of the symbols, so further Intellisense
' works well if all the symbols are of common type.
' For property/method, we create a BoundMethodGroup/PropertyGroup so that we continue to do overload
' resolution.
Dim symbols As ImmutableArray(Of Symbol)
If TypeOf di Is AmbiguousSymbolDiagnostic Then
' Lookup had an ambiguity between Imports or Modules.
Debug.Assert(lookupResult.Kind = LookupResultKind.Ambiguous)
symbols = DirectCast(di, AmbiguousSymbolDiagnostic).AmbiguousSymbols
Else
symbols = lookupResult.Symbols.ToImmutable()
End If
Return New BoundBadExpression(node,
lookupResult.Kind,
symbols,
If(receiver IsNot Nothing, ImmutableArray.Create(receiver), ImmutableArray(Of BoundExpression).Empty),
GetCommonExpressionTypeForErrorRecovery(node, symbols, ConstantFieldsInProgress), hasErrors:=True)
End If
Select Case lookupResult.Symbols(0).Kind ' all symbols in a lookupResult must be of the same kind.
Case SymbolKind.Method
'TODO: Deal with errors reported by BindTypeArguments. Should we adjust hasError?
Return CreateBoundMethodGroup(
node,
lookupResult,
lookupOptionsUsed,
diagnostics.AccumulatesDependencies,
receiver,
BindTypeArguments(typeArgumentsOpt, diagnostics),
qualKind,
hasError)
Case SymbolKind.Property
' UNDONE: produce error if type arguments were present.
Debug.Assert(lookupResult.Kind = LookupResultKind.Good OrElse lookupResult.Kind = LookupResultKind.Inaccessible)
Return New BoundPropertyGroup(
node,
lookupResult.Symbols.ToDowncastedImmutable(Of PropertySymbol),
lookupResult.Kind,
receiver,
qualKind,
hasErrors:=hasError)
Case SymbolKind.Event
Dim eventSymbol = DirectCast(lookupResult.SingleSymbol, EventSymbol)
If eventSymbol.IsShared And qualKind = QualificationKind.Unqualified Then
receiver = Nothing
End If
If Not reportedLookupError Then
ReportUseSite(diagnostics, node, eventSymbol)
End If
If Not hasError Then
If receiver IsNot Nothing AndAlso receiver.Kind = BoundKind.TypeOrValueExpression Then
receiver = AdjustReceiverTypeOrValue(receiver, node, isShared:=eventSymbol.IsShared, diagnostics:=diagnostics, qualKind:=qualKind)
End If
If Not IsNameOfArgument(node) Then
hasError = CheckSharedSymbolAccess(node, eventSymbol.IsShared, receiver, qualKind, diagnostics)
End If
End If
ReportDiagnosticsIfObsoleteOrNotSupported(diagnostics, eventSymbol, node)
If receiver IsNot Nothing AndAlso receiver.IsPropertyOrXmlPropertyAccess() Then
receiver = MakeRValue(receiver, diagnostics)
End If
Return New BoundEventAccess(
node,
receiver,
eventSymbol,
eventSymbol.Type,
hasErrors:=hasError)
Case SymbolKind.Field
Dim fieldSymbol As FieldSymbol = DirectCast(lookupResult.SingleSymbol, FieldSymbol)
If fieldSymbol.IsShared And qualKind = QualificationKind.Unqualified Then
receiver = Nothing
End If
' TODO: Check if this is a constant field with missing or bad value and report an error.
If Not hasError Then
If receiver IsNot Nothing AndAlso receiver.Kind = BoundKind.TypeOrValueExpression Then
receiver = AdjustReceiverTypeOrValue(receiver, node, isShared:=fieldSymbol.IsShared, diagnostics:=diagnostics, qualKind:=qualKind)
End If
hasError = CheckSharedSymbolAccess(node, fieldSymbol.IsShared, receiver, qualKind, diagnostics)
End If
If Not reportedLookupError Then
If Not ReportUseSite(diagnostics, node, fieldSymbol) Then
CheckMemberTypeAccessibility(diagnostics, node, fieldSymbol)
End If
End If
ReportDiagnosticsIfObsoleteOrNotSupported(diagnostics, fieldSymbol, node)
' const fields may need to determine the type because it's inferred
' This is why using .Type was replaced by .GetInferredType to detect cycles.
Dim fieldAccessType = fieldSymbol.GetInferredType(ConstantFieldsInProgress)
Dim asMemberAccess = TryCast(node, MemberAccessExpressionSyntax)
If asMemberAccess IsNot Nothing AndAlso Not fieldAccessType.IsErrorType() Then
VerifyTypeCharacterConsistency(asMemberAccess.Name, fieldAccessType.GetEnumUnderlyingTypeOrSelf, diagnostics)
End If
If receiver IsNot Nothing AndAlso receiver.IsPropertyOrXmlPropertyAccess() Then
receiver = MakeRValue(receiver, diagnostics)
End If
Return New BoundFieldAccess(node,
receiver,
fieldSymbol,
isLValue:=IsLValueFieldAccess(fieldSymbol, receiver),
suppressVirtualCalls:=False,
constantsInProgressOpt:=Me.ConstantFieldsInProgress,
type:=fieldAccessType,
hasErrors:=hasError OrElse fieldAccessType.IsErrorType)
Case SymbolKind.Local
Dim localSymbol = DirectCast(lookupResult.SingleSymbol, LocalSymbol)
If localSymbol.IsFunctionValue AndAlso Not IsNameOfArgument(node) Then
Dim method = DirectCast(localSymbol.ContainingSymbol, MethodSymbol)
If method.IsAsync OrElse method.IsIterator Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_BadResumableAccessReturnVariable)
Return BadExpression(node, ErrorTypeSymbol.UnknownResultType)
End If
End If
Dim localAccessType As TypeSymbol = GetLocalSymbolType(localSymbol, node, diagnostics)
Dim asSimpleName = TryCast(node, SimpleNameSyntax)
If asSimpleName IsNot Nothing AndAlso Not localAccessType.IsErrorType() Then
VerifyTypeCharacterConsistency(asSimpleName, localAccessType.GetEnumUnderlyingTypeOrSelf, diagnostics)
End If
If localSymbol.IsFor Then
' lifting iteration variable produces a warning
Dim localSymbolContainingSymbol As Symbol = localSymbol.ContainingSymbol
If ContainingMember IsNot localSymbolContainingSymbol Then
' Need to go up the chain of containers and see if the last lambda we see
' is a QueryLambda, before we reach local's container.
If IsTopMostEnclosingLambdaAQueryLambda(ContainingMember, localSymbolContainingSymbol) Then
ReportDiagnostic(diagnostics, node, ERRID.WRN_LiftControlVariableQuery, localSymbol.Name)
Else
ReportDiagnostic(diagnostics, node, ERRID.WRN_LiftControlVariableLambda, localSymbol.Name)
End If
End If
End If
' Debug.Assert(localSymbol.GetUseSiteInfo().DiagnosticInfo Is Nothing) ' Not true in the debugger.
Return New BoundLocal(node, localSymbol, localAccessType, hasErrors:=hasError)
Case SymbolKind.RangeVariable
Dim rangeVariable = DirectCast(lookupResult.SingleSymbol, RangeVariableSymbol)
Debug.Assert(rangeVariable.GetUseSiteInfo().IsEmpty)
Return New BoundRangeVariable(node, rangeVariable, rangeVariable.Type, hasErrors:=hasError)
Case SymbolKind.Parameter
Dim parameterSymbol = DirectCast(lookupResult.SingleSymbol, ParameterSymbol)
Dim parameterType = parameterSymbol.Type
Dim asSimpleName = TryCast(node, SimpleNameSyntax)
If asSimpleName IsNot Nothing AndAlso Not parameterType.IsErrorType() Then
VerifyTypeCharacterConsistency(asSimpleName, parameterType.GetEnumUnderlyingTypeOrSelf, diagnostics)
End If
Debug.Assert(parameterSymbol.GetUseSiteInfo().IsEmpty)
Return New BoundParameter(node, parameterSymbol, parameterType, hasErrors:=hasError)
Case SymbolKind.NamedType, SymbolKind.ErrorType
' Note: arity already checked by lookup process.
' Bind the type arguments.
Dim typeArguments As BoundTypeArguments = Nothing
If typeArgumentsOpt IsNot Nothing Then
' Bind the type arguments and report errors in the current context.
typeArguments = BindTypeArguments(typeArgumentsOpt, diagnostics)
End If
' If I identifies a type, then the result is that type constructed with the given type arguments.
' Construct the type if it is generic! See ConstructAndValidateConstraints().
Dim typeSymbol = TryCast(lookupResult.SingleSymbol, NamedTypeSymbol)
If typeSymbol IsNot Nothing AndAlso typeArguments IsNot Nothing Then
' Construct the type and validate constraints.
Dim constructedType = ConstructAndValidateConstraints(
typeSymbol, typeArguments.Arguments, node, typeArgumentsOpt.Arguments, diagnostics)
' Put the constructed type in. Note that this preserves any error associated with the lookupResult.
lookupResult.ReplaceSymbol(constructedType)
End If
ReportDiagnosticsIfObsoleteOrNotSupported(diagnostics, typeSymbol, node)
If Not hasError Then
receiver = AdjustReceiverTypeOrValue(receiver, node, isShared:=True, diagnostics:=diagnostics, qualKind:=qualKind)
hasError = CheckSharedSymbolAccess(node, True, receiver, qualKind, diagnostics)
End If
If Not reportedLookupError Then
ReportUseSite(diagnostics, node, If(typeSymbol, lookupResult.SingleSymbol))
End If
Dim type As TypeSymbol = DirectCast(lookupResult.SingleSymbol, TypeSymbol)
Dim asSimpleName = TryCast(node, SimpleNameSyntax)
If asSimpleName IsNot Nothing AndAlso Not type.IsErrorType() Then
VerifyTypeCharacterConsistency(asSimpleName, type.GetEnumUnderlyingTypeOrSelf, diagnostics)
End If
Return New BoundTypeExpression(node, receiver, Nothing, type, hasErrors:=hasError)
Case SymbolKind.TypeParameter
' Note: arity already checked by lookup process.
Debug.Assert(lookupResult.SingleSymbol.GetUseSiteInfo().IsEmpty)
Return New BoundTypeExpression(node, DirectCast(lookupResult.SingleSymbol, TypeSymbol), hasErrors:=hasError)
Case SymbolKind.Namespace
' Note: arity already checked by lookup process.
Debug.Assert(lookupResult.SingleSymbol.GetUseSiteInfo().IsEmpty)
Return New BoundNamespaceExpression(node, receiver, DirectCast(lookupResult.SingleSymbol, NamespaceSymbol), hasErrors:=hasError)
Case SymbolKind.Alias
Dim [alias] = DirectCast(lookupResult.SingleSymbol, AliasSymbol)
Debug.Assert([alias].GetUseSiteInfo().IsEmpty)
Dim symbol = [alias].Target
Select Case symbol.Kind
Case SymbolKind.NamedType, SymbolKind.ErrorType
If Not reportedLookupError Then
ReportUseSite(diagnostics, node, symbol)
End If
Return New BoundTypeExpression(node, Nothing, [alias], DirectCast(symbol, TypeSymbol), hasErrors:=hasError)
Case SymbolKind.Namespace
Debug.Assert(symbol.GetUseSiteInfo().IsEmpty)
Return New BoundNamespaceExpression(node, Nothing, [alias], DirectCast(symbol, NamespaceSymbol), hasErrors:=hasError)
Case Else
Throw ExceptionUtilities.UnexpectedValue(symbol.Kind)
End Select
Case Else
Throw ExceptionUtilities.UnexpectedValue(lookupResult.Symbols(0).Kind)
End Select
End Function
Private Function AdjustReceiverNamespace(lookupResult As LookupResult, receiver As BoundExpression) As BoundExpression
If receiver.Kind = BoundKind.NamespaceExpression Then
Dim namespaceReceiver = DirectCast(receiver, BoundNamespaceExpression)
If namespaceReceiver.NamespaceSymbol.NamespaceKind = NamespaceKindNamespaceGroup Then
Dim symbols As ArrayBuilder(Of Symbol) = lookupResult.Symbols
If lookupResult.HasDiagnostic Then
Dim di As DiagnosticInfo = lookupResult.Diagnostic
If TypeOf di Is AmbiguousSymbolDiagnostic Then
' Lookup had an ambiguity
Debug.Assert(lookupResult.Kind = LookupResultKind.Ambiguous)
Dim ambiguous As ImmutableArray(Of Symbol) = DirectCast(di, AmbiguousSymbolDiagnostic).AmbiguousSymbols
symbols = ArrayBuilder(Of Symbol).GetInstance()
symbols.AddRange(ambiguous)
End If
End If
receiver = AdjustReceiverNamespace(namespaceReceiver, symbols)
If symbols IsNot lookupResult.Symbols Then
symbols.Free()
End If
End If
End If
Return receiver
End Function
Private Function AdjustReceiverNamespace(namespaceReceiver As BoundNamespaceExpression, symbols As ArrayBuilder(Of Symbol)) As BoundNamespaceExpression
If symbols.Count > 0 Then
Dim namespaces = New SmallDictionary(Of NamespaceSymbol, Boolean)()
For Each candidate In symbols
If Not AddReceiverNamespaces(namespaces, candidate, Me.Compilation) Then
namespaces = Nothing
Exit For
End If
Next
If namespaces IsNot Nothing AndAlso namespaces.Count < namespaceReceiver.NamespaceSymbol.ConstituentNamespaces.Length Then
Return AdjustReceiverNamespace(namespaceReceiver, DirectCast(namespaceReceiver.NamespaceSymbol, MergedNamespaceSymbol).Shrink(namespaces.Keys))
End If
End If
Return namespaceReceiver
End Function
Friend Shared Function AddReceiverNamespaces(namespaces As SmallDictionary(Of NamespaceSymbol, Boolean), candidate As Symbol, compilation As VisualBasicCompilation) As Boolean
If candidate.Kind = SymbolKind.Namespace AndAlso
DirectCast(candidate, NamespaceSymbol).NamespaceKind = NamespaceKindNamespaceGroup Then
For Each constituent In DirectCast(candidate, NamespaceSymbol).ConstituentNamespaces
If Not AddContainingNamespaces(namespaces, constituent, compilation) Then
Return False
End If
Next
Return True
Else
Return AddContainingNamespaces(namespaces, candidate, compilation)
End If
End Function
Private Shared Function AddContainingNamespaces(namespaces As SmallDictionary(Of NamespaceSymbol, Boolean), candidate As Symbol, compilation As VisualBasicCompilation) As Boolean
If candidate Is Nothing OrElse candidate.Kind = SymbolKind.ErrorType Then
Return False
End If
Dim containingNamespace = candidate.ContainingNamespace
If containingNamespace IsNot Nothing Then
namespaces(compilation.GetCompilationNamespace(containingNamespace)) = False
Else
Debug.Assert(containingNamespace IsNot Nothing)
' Should not get here, I believe.
Return False
End If
Return True
End Function
Private Function AdjustReceiverNamespace(namespaceReceiver As BoundNamespaceExpression, adjustedNamespace As NamespaceSymbol) As BoundNamespaceExpression
If adjustedNamespace IsNot namespaceReceiver.NamespaceSymbol Then
Dim receiver As BoundExpression = namespaceReceiver.UnevaluatedReceiverOpt
If receiver IsNot Nothing AndAlso receiver.Kind = BoundKind.NamespaceExpression Then
Dim parentNamespace = DirectCast(receiver, BoundNamespaceExpression)
If parentNamespace.NamespaceSymbol.NamespaceKind = NamespaceKindNamespaceGroup AndAlso
IsNamespaceGroupIncludesButNotEquivalentTo(parentNamespace.NamespaceSymbol, adjustedNamespace.ContainingNamespace) Then
receiver = AdjustReceiverNamespace(parentNamespace, adjustedNamespace.ContainingNamespace)
End If
End If
Return namespaceReceiver.Update(receiver, namespaceReceiver.AliasOpt, adjustedNamespace)
End If
Return namespaceReceiver
End Function
Private Shared Function IsNamespaceGroupIncludesButNotEquivalentTo(namespaceGroup As NamespaceSymbol, other As NamespaceSymbol) As Boolean
Debug.Assert(namespaceGroup.NamespaceKind = NamespaceKindNamespaceGroup)
Dim result As Boolean
If other.NamespaceKind <> NamespaceKindNamespaceGroup Then
result = namespaceGroup.ConstituentNamespaces.Contains(other)
Else
Dim groupConstituents As ImmutableArray(Of NamespaceSymbol) = namespaceGroup.ConstituentNamespaces
Dim otherConstituents As ImmutableArray(Of NamespaceSymbol) = other.ConstituentNamespaces
If groupConstituents.Length > otherConstituents.Length Then
result = True
Dim lookup = New SmallDictionary(Of NamespaceSymbol, Boolean)()
For Each item In groupConstituents
lookup(item) = False
Next
For Each item In otherConstituents
If Not lookup.TryGetValue(item, Nothing) Then
result = False
Exit For
End If
Next
Else
result = False
End If
End If
Debug.Assert(result)
Return result
End Function
Private Sub CheckMemberTypeAccessibility(diagnostics As BindingDiagnosticBag, node As SyntaxNode, member As Symbol)
' We are not doing this check during lookup due to a performance impact it has on IDE scenarios.
' In any case, an accessible member with inaccessible type is beyond language spec, so we have
' some freedom how to deal with it.
Dim memberType As TypeSymbol
Select Case member.Kind
Case SymbolKind.Method
memberType = DirectCast(member, MethodSymbol).ReturnType
Exit Select
Case SymbolKind.Property
memberType = DirectCast(member, PropertySymbol).Type
Exit Select
Case SymbolKind.Field
' Getting the type of a source field that is a constant can cause infinite
' recursion if that field has an inferred type. Rather than passing in fields
' currently being evaluated to break the recursion, we simply note that inferred
' types can never be inaccessible, so we don't check their types.
Dim fieldSym = DirectCast(member, FieldSymbol)
If fieldSym.HasDeclaredType Then
memberType = fieldSym.Type
Else
Return
End If
Case Else
' Somewhat strangely, event types are not checked.
Throw ExceptionUtilities.UnexpectedValue(member.Kind)
End Select
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If CheckAccessibility(memberType, useSiteInfo, accessThroughType:=Nothing) <> AccessCheckResult.Accessible Then
ReportDiagnostic(diagnostics, node,
New BadSymbolDiagnostic(member,
ERRID.ERR_InaccessibleReturnTypeOfMember2,
CustomSymbolDisplayFormatter.WithContainingType(member)))
End If
diagnostics.Add(node, useSiteInfo)
End Sub
Public Shared Function IsTopMostEnclosingLambdaAQueryLambda(containingMember As Symbol, stopAtContainer As Symbol) As Boolean
Dim topMostEnclosingLambdaIsQueryLambda As Boolean = False
' Need to go up the chain of containers and see if the last lambda we see
' is a QueryLambda, before we reach the stopAtContainer.
Dim currentContainer As Symbol = containingMember
While currentContainer IsNot Nothing AndAlso currentContainer IsNot stopAtContainer
Debug.Assert(currentContainer.IsLambdaMethod OrElse stopAtContainer Is Nothing)
If currentContainer.IsLambdaMethod Then
topMostEnclosingLambdaIsQueryLambda = currentContainer.IsQueryLambdaMethod
Else
Exit While
End If
currentContainer = currentContainer.ContainingSymbol
End While
Return topMostEnclosingLambdaIsQueryLambda
End Function
Public Function BindLabel(node As LabelSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
Dim labelName As String = node.LabelToken.ValueText
Dim result = LookupResult.GetInstance()
Me.Lookup(result, labelName, arity:=0, options:=LookupOptions.LabelsOnly, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
Dim symbol As LabelSymbol = Nothing
Dim hasErrors As Boolean = False
If result.IsGood AndAlso result.HasSingleSymbol Then
symbol = DirectCast(result.Symbols.First(), LabelSymbol)
Else
If result.HasDiagnostic Then
ReportDiagnostic(diagnostics, node, result.Diagnostic)
Else
' The label is undefined
ReportDiagnostic(diagnostics, node, ERRID.ERR_LabelNotDefined1, labelName)
End If
hasErrors = True
End If
result.Free()
If symbol Is Nothing Then
Return New BoundBadExpression(node,
LookupResultKind.Empty,
ImmutableArray(Of Symbol).Empty,
ImmutableArray(Of BoundExpression).Empty,
Nothing,
hasErrors:=True)
Else
Return New BoundLabel(node, symbol, Nothing, hasErrors:=hasErrors)
End If
End Function
Private Function BindTypeArguments(
typeArgumentsOpt As TypeArgumentListSyntax,
diagnostics As BindingDiagnosticBag
) As BoundTypeArguments
If typeArgumentsOpt Is Nothing Then
Return Nothing
End If
Dim arguments = typeArgumentsOpt.Arguments
'TODO: What should we do if count is 0? Can we get in a situation like this?
' Perhaps for a missing type argument case [Goo(Of )].
Dim boundArguments(arguments.Count - 1) As TypeSymbol
For i As Integer = 0 To arguments.Count - 1 Step 1
boundArguments(i) = BindTypeSyntax(arguments(i), diagnostics)
Next
' TODO: Should we set HasError flag if any of the BindTypeSyntax calls report errors?
Return New BoundTypeArguments(typeArgumentsOpt, boundArguments.AsImmutableOrNull())
End Function
''' <summary>
''' Report diagnostics relating to access shared/nonshared symbols. Returns true if an ERROR (but not a warning)
''' was reported. Also replaces receiver as a type with DefaultPropertyInstance when appropriate.
''' </summary>
Private Function CheckSharedSymbolAccess(node As SyntaxNode, isShared As Boolean, <[In], Out> ByRef receiver As BoundExpression, qualKind As QualificationKind, diagnostics As BindingDiagnosticBag) As Boolean
If isShared Then
If qualKind = QualificationKind.QualifiedViaValue AndAlso receiver IsNot Nothing AndAlso
receiver.Kind <> BoundKind.TypeOrValueExpression AndAlso receiver.Kind <> BoundKind.MyBaseReference AndAlso
Not receiver.HasErrors Then
' NOTE: Since using MyBase is the only way to call a method from base type
' in some cases, calls with 'MyBase' receiver should not be marked
' with this WRN_SharedMemberThroughInstance;
' WARNING: This differs from DEV10
' we do not want to report this diagnostic in the case of an initialization of a field/property
' through an object initializer. In that case we will output an error
' "BC30991: Member '{0}' cannot be initialized in an object initializer expression because it is shared."
' instead.
If node.Parent Is Nothing OrElse
node.Parent.Kind <> SyntaxKind.NamedFieldInitializer Then
ReportDiagnostic(diagnostics, node, ERRID.WRN_SharedMemberThroughInstance)
End If
End If
Else
If qualKind = QualificationKind.QualifiedViaTypeName OrElse
(qualKind = QualificationKind.Unqualified AndAlso receiver Is Nothing) Then
If qualKind = QualificationKind.QualifiedViaTypeName AndAlso receiver IsNot Nothing AndAlso
receiver.Kind = BoundKind.TypeExpression Then
' Try default instance property through DefaultInstanceAlias
Dim instance As BoundExpression = TryDefaultInstanceProperty(DirectCast(receiver, BoundTypeExpression), diagnostics)
If instance IsNot Nothing Then
receiver = instance
Return False
End If
End If
' We don't have a valid qualifier for this instance method.
If receiver IsNot Nothing AndAlso receiver.Kind = BoundKind.TypeExpression AndAlso IsReceiverOfNameOfArgument(receiver.Syntax) Then
receiver = New BoundTypeAsValueExpression(receiver.Syntax, DirectCast(receiver, BoundTypeExpression), receiver.Type).MakeCompilerGenerated()
Return False
Else
ReportDiagnostic(diagnostics, node, ERRID.ERR_ObjectReferenceNotSupplied)
Return True
End If
End If
Dim errorId As ERRID = Nothing
If qualKind = QualificationKind.Unqualified AndAlso Not IsNameOfArgument(node) AndAlso Not CanAccessMe(True, errorId) Then
' We can't use implicit Me here.
ReportDiagnostic(diagnostics, node, errorId)
Return True
End If
End If
Return False
End Function
Private Shared Function IsReceiverOfNameOfArgument(syntax As SyntaxNode) As Boolean
Dim parent = syntax.Parent
Return parent IsNot Nothing AndAlso
parent.Kind = SyntaxKind.SimpleMemberAccessExpression AndAlso
DirectCast(parent, MemberAccessExpressionSyntax).Expression Is syntax AndAlso
IsNameOfArgument(parent)
End Function
Private Shared Function IsNameOfArgument(syntax As SyntaxNode) As Boolean
Return syntax.Parent IsNot Nothing AndAlso
syntax.Parent.Kind = SyntaxKind.NameOfExpression AndAlso
DirectCast(syntax.Parent, NameOfExpressionSyntax).Argument Is syntax
End Function
''' <summary>
''' Returns a bound node for left part of dictionary access node with omitted left syntax.
''' In particular it handles dictionary access inside With statement.
'''
''' By default the method delegates the work to it's containing binder or returns Nothing.
''' </summary>
Protected Overridable Function TryBindOmittedLeftForDictionaryAccess(node As MemberAccessExpressionSyntax,
accessingBinder As Binder,
diagnostics As BindingDiagnosticBag) As BoundExpression
Debug.Assert(Me.ContainingBinder IsNot Nothing)
Return Me.ContainingBinder.TryBindOmittedLeftForDictionaryAccess(node, accessingBinder, diagnostics)
End Function
Private Function BindDictionaryAccess(node As MemberAccessExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
Dim leftOpt = node.Expression
Dim left As BoundExpression
If leftOpt Is Nothing Then
' Spec 11.7: "If an exclamation point is specified with no expression, the
' expression from the immediately containing With statement is assumed.
' If there is no containing With statement, a compile-time error occurs."
Dim conditionalAccess As ConditionalAccessExpressionSyntax = node.GetCorrespondingConditionalAccessExpression()
If conditionalAccess IsNot Nothing Then
left = GetConditionalAccessReceiver(conditionalAccess)
Else
left = TryBindOmittedLeftForDictionaryAccess(node, Me, diagnostics)
End If
If left Is Nothing Then
' Didn't find binder that can handle member access with omitted left part
Return BadExpression(
node,
ImmutableArray.Create(
ReportDiagnosticAndProduceBadExpression(diagnostics, node, ERRID.ERR_BadWithRef),
New BoundLiteral(
node.Name,
ConstantValue.Create(node.Name.Identifier.ValueText),
GetSpecialType(SpecialType.System_String, node.Name, diagnostics))),
ErrorTypeSymbol.UnknownResultType)
End If
Else
left = Me.BindExpression(leftOpt, diagnostics)
End If
If Not left.IsLValue AndAlso left.Kind <> BoundKind.LateMemberAccess Then
left = MakeRValue(left, diagnostics)
Debug.Assert(left IsNot Nothing)
End If
Dim type = left.Type
Debug.Assert(type IsNot Nothing)
If Not type.IsErrorType() Then
If type.SpecialType = SpecialType.System_Object OrElse type.IsExtensibleInterfaceNoUseSiteDiagnostics() Then
Dim name = node.Name
Dim arg = New BoundLiteral(name, ConstantValue.Create(node.Name.Identifier.ValueText), GetSpecialType(SpecialType.System_String, name, diagnostics))
Dim boundArguments = ImmutableArray.Create(Of BoundExpression)(arg)
Return BindLateBoundInvocation(node, Nothing, left, boundArguments, Nothing, diagnostics)
End If
If type.IsInterfaceType Then
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
' In case IsExtensibleInterfaceNoUseSiteDiagnostics above failed because there were bad inherited interfaces.
type.AllInterfacesWithDefinitionUseSiteDiagnostics(useSiteInfo)
diagnostics.Add(node, useSiteInfo)
End If
Dim defaultPropertyGroup As BoundExpression = BindDefaultPropertyGroup(node, left, diagnostics)
Debug.Assert(defaultPropertyGroup Is Nothing OrElse defaultPropertyGroup.Kind = BoundKind.PropertyGroup OrElse
defaultPropertyGroup.Kind = BoundKind.MethodGroup OrElse defaultPropertyGroup.HasErrors)
' Dev10 limits Dictionary access to properties.
If defaultPropertyGroup IsNot Nothing AndAlso defaultPropertyGroup.Kind = BoundKind.PropertyGroup Then
Dim name = node.Name
Dim arg = New BoundLiteral(name, ConstantValue.Create(node.Name.Identifier.ValueText), GetSpecialType(SpecialType.System_String, name, diagnostics))
Return BindInvocationExpression(
node,
left.Syntax,
TypeCharacter.None,
DirectCast(defaultPropertyGroup, BoundPropertyGroup),
boundArguments:=ImmutableArray.Create(Of BoundExpression)(arg),
argumentNames:=Nothing,
diagnostics:=diagnostics,
isDefaultMemberAccess:=True,
callerInfoOpt:=node)
ElseIf defaultPropertyGroup Is Nothing OrElse Not defaultPropertyGroup.HasErrors Then
Select Case type.TypeKind
Case TYPEKIND.Array, TYPEKIND.Enum
ReportQualNotObjectRecord(left, diagnostics)
Case TYPEKIND.Class
If type.SpecialType = SpecialType.System_Array Then
ReportDefaultMemberNotProperty(left, diagnostics)
Else
ReportNoDefaultProperty(left, diagnostics)
End If
Case TYPEKIND.TypeParameter, TYPEKIND.Interface
ReportNoDefaultProperty(left, diagnostics)
Case TYPEKIND.Structure
If type.IsIntrinsicValueType() Then
ReportQualNotObjectRecord(left, diagnostics)
Else
ReportNoDefaultProperty(left, diagnostics)
End If
Case Else
ReportDefaultMemberNotProperty(left, diagnostics)
End Select
End If
End If
Return BadExpression(
node,
ImmutableArray.Create(
left,
New BoundLiteral(
node.Name,
ConstantValue.Create(node.Name.Identifier.ValueText),
GetSpecialType(SpecialType.System_String, node.Name, diagnostics))),
ErrorTypeSymbol.UnknownResultType)
End Function
Private Shared Sub ReportNoDefaultProperty(expr As BoundExpression, diagnostics As BindingDiagnosticBag)
Dim type = expr.Type
Dim syntax = expr.Syntax
Select Case type.TypeKind
Case TYPEKIND.Class
' "Class '{0}' cannot be indexed because it has no default property."
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_NoDefaultNotExtend1, type)
Case TYPEKIND.Structure
' "Structure '{0}' cannot be indexed because it has no default property."
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_StructureNoDefault1, type)
Case TYPEKIND.Error
' We should have reported an error elsewhere.
Case Else
' "'{0}' cannot be indexed because it has no default property."
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_InterfaceNoDefault1, type)
End Select
End Sub
Private Shared Sub ReportQualNotObjectRecord(expr As BoundExpression, diagnostics As BindingDiagnosticBag)
' "'!' requires its left operand to have a type parameter, class or interface type, but this operand has the type '{0}'."
ReportDiagnostic(diagnostics, expr.Syntax, ERRID.ERR_QualNotObjectRecord1, expr.Type)
End Sub
Private Shared Sub ReportDefaultMemberNotProperty(expr As BoundExpression, diagnostics As BindingDiagnosticBag)
' "Default member '{0}' is not a property."
' Note: The error argument is the expression type
' rather than the expression text used in Dev10.
ReportDiagnostic(diagnostics, expr.Syntax, ERRID.ERR_DefaultMemberNotProperty1, expr.Type)
End Sub
Private Shared Function GenerateBadExpression(node As InvocationExpressionSyntax, target As BoundExpression, boundArguments As ImmutableArray(Of BoundExpression)) As BoundExpression
Dim children = ArrayBuilder(Of BoundExpression).GetInstance()
children.Add(target)
children.AddRange(boundArguments)
Return BadExpression(node, children.ToImmutableAndFree(), ErrorTypeSymbol.UnknownResultType)
End Function
Private Shared Sub VerifyTypeCharacterConsistency(nodeOrToken As SyntaxNodeOrToken, type As TypeSymbol, typeChar As TypeCharacter, diagnostics As BindingDiagnosticBag)
Dim typeCharacterString As String = Nothing
Dim specialType As SpecialType = GetSpecialTypeForTypeCharacter(typeChar, typeCharacterString)
If specialType <> Microsoft.CodeAnalysis.SpecialType.None Then
If type.IsArrayType() Then
type = DirectCast(type, ArrayTypeSymbol).ElementType
End If
type = type.GetNullableUnderlyingTypeOrSelf()
If type.SpecialType <> specialType Then
ReportDiagnostic(diagnostics, nodeOrToken, ERRID.ERR_TypecharNoMatch2, typeCharacterString, type)
End If
End If
End Sub
Private Shared Sub VerifyTypeCharacterConsistency(name As SimpleNameSyntax, type As TypeSymbol, diagnostics As BindingDiagnosticBag)
Dim typeChar As TypeCharacter = name.Identifier.GetTypeCharacter()
If typeChar = TypeCharacter.None Then
Return
End If
Dim typeCharacterString As String = Nothing
Dim specialType As SpecialType = GetSpecialTypeForTypeCharacter(typeChar, typeCharacterString)
If specialType <> specialType.None Then
If type.IsArrayType() Then
type = DirectCast(type, ArrayTypeSymbol).ElementType
End If
type = type.GetNullableUnderlyingTypeOrSelf()
If type.SpecialType <> specialType Then
ReportDiagnostic(diagnostics, name, ERRID.ERR_TypecharNoMatch2, typeCharacterString, type)
End If
End If
End Sub
Private Function BindArrayAccess(node As InvocationExpressionSyntax, expr As BoundExpression, boundArguments As ImmutableArray(Of BoundExpression), argumentNames As ImmutableArray(Of String), diagnostics As BindingDiagnosticBag) As BoundExpression
Debug.Assert(node IsNot Nothing)
Debug.Assert(expr IsNot Nothing)
If expr.IsLValue Then
expr = expr.MakeRValue()
End If
Dim convertedArguments = ArrayBuilder(Of BoundExpression).GetInstance(boundArguments.Length)
Dim int32Type = GetSpecialType(SpecialType.System_Int32, node.ArgumentList, diagnostics)
For Each argument In boundArguments
convertedArguments.Add(ApplyImplicitConversion(argument.Syntax, int32Type, argument, diagnostics))
Next
boundArguments = convertedArguments.ToImmutableAndFree()
Dim exprType = expr.Type
If exprType Is Nothing Then
Return New BoundArrayAccess(node, expr, boundArguments, Nothing, hasErrors:=True)
End If
If Not argumentNames.IsDefault AndAlso argumentNames.Length > 0 Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_NamedSubscript)
End If
Dim arrayType As ArrayTypeSymbol = DirectCast(expr.Type, ArrayTypeSymbol)
Dim rank As Integer = arrayType.Rank
If boundArguments.Length <> arrayType.Rank Then
Dim err As ERRID
If boundArguments.Length > arrayType.Rank Then
err = ERRID.ERR_TooManyIndices
Else
err = ERRID.ERR_TooFewIndices
End If
ReportDiagnostic(diagnostics, node.ArgumentList, err)
Return New BoundArrayAccess(node, expr, boundArguments, arrayType.ElementType, hasErrors:=True)
End If
Return New BoundArrayAccess(node, expr, boundArguments, arrayType.ElementType)
End Function
' Get the common return type of a set of symbols, or error type if no common return type. Used
' in error cases to give a type in ambiguity situations.
' If we can't find a common type, create an error type. If all the types have a common name,
' that name is used as the type of the error type (useful in ambiguous type lookup situations)
Private Function GetCommonExpressionTypeForErrorRecovery(
symbolReference As VisualBasicSyntaxNode,
symbols As ImmutableArray(Of Symbol),
constantFieldsInProgress As ConstantFieldsInProgress
) As TypeSymbol
Dim commonType As TypeSymbol = Nothing
Dim commonName As String = Nothing
Dim noCommonType As Boolean = False
Dim noCommonName As Boolean = False
Dim discardedDiagnostics = BindingDiagnosticBag.Discarded
For i As Integer = 0 To symbols.Length - 1
Dim expressionType As TypeSymbol = GetExpressionType(symbolReference, symbols(i), constantFieldsInProgress, discardedDiagnostics)
If expressionType IsNot Nothing Then
If commonType Is Nothing Then
commonType = expressionType
ElseIf Not noCommonType AndAlso Not commonType.Equals(expressionType) Then
noCommonType = True
End If
If commonName Is Nothing Then
commonName = expressionType.Name
ElseIf Not noCommonName AndAlso Not CaseInsensitiveComparison.Equals(commonName, expressionType.Name) Then
noCommonName = True
End If
End If
Next
If noCommonType Then
If noCommonName Then
Return ErrorTypeSymbol.UnknownResultType
Else
Return New ExtendedErrorTypeSymbol(Nothing, commonName)
End If
Else
Return commonType
End If
End Function
' Get the "expression type" of a symbol when used in an expression.
Private Function GetExpressionType(
symbolReference As VisualBasicSyntaxNode,
s As Symbol,
constantFieldsInProgress As ConstantFieldsInProgress,
diagnostics As BindingDiagnosticBag
) As TypeSymbol
Select Case s.Kind
Case SymbolKind.Method
Return DirectCast(s, MethodSymbol).ReturnType
Case SymbolKind.Field
' const fields may need to determine the type because it's inferred
' This is why using .Type was replaced by .GetInferredType to detect cycles.
Return DirectCast(s, FieldSymbol).GetInferredType(constantFieldsInProgress)
Case SymbolKind.Property
Return DirectCast(s, PropertySymbol).Type
Case SymbolKind.Parameter
Return DirectCast(s, ParameterSymbol).Type
Case SymbolKind.Local
Return GetLocalSymbolType(DirectCast(s, LocalSymbol), symbolReference, diagnostics)
Case SymbolKind.RangeVariable
Return DirectCast(s, RangeVariableSymbol).Type
Case Else
Dim type = TryCast(s, TypeSymbol)
If type IsNot Nothing Then
Return type
End If
End Select
Return Nothing
End Function
' Given the expression part of a named argument, get the token of it's name. We use this for error reported, and its more efficient
' to calculate it only when needed when reported a diagnostic.
Private Shared Function GetNamedArgumentIdentifier(argumentExpression As SyntaxNode) As SyntaxToken
Dim parent = TryCast(argumentExpression.Parent, SimpleArgumentSyntax)
If parent Is Nothing OrElse Not parent.IsNamed Then
Debug.Assert(False, "Did not found a NamedArgumentSyntax where one should have been")
Return argumentExpression.GetFirstToken() ' since we use this for error reporting, this gives us something close, anyway.
Else
Return parent.NameColonEquals.Name.Identifier
End If
End Function
Private Structure DimensionSize
Public Enum SizeKind As Byte
Unknown
Constant
NotConstant
End Enum
Public ReadOnly Kind As SizeKind
Public ReadOnly Size As Integer
Private Sub New(size As Integer, kind As SizeKind)
Me.Size = size
Me.Kind = kind
End Sub
Public Shared Function ConstantSize(size As Integer) As DimensionSize
Return New DimensionSize(size, SizeKind.Constant)
End Function
Public Shared Function VariableSize() As DimensionSize
Return New DimensionSize(0, SizeKind.NotConstant)
End Function
End Structure
''' <summary>
''' Handle ArrayCreationExpressionSyntax
''' new integer(n)(,) {...}
''' new integer() {...}
''' </summary>
Private Function BindArrayCreationExpression(node As ArrayCreationExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
' Bind the type
Dim baseType = BindTypeSyntax(node.Type, diagnostics)
Dim arrayBoundsOpt = node.ArrayBounds
Dim boundArguments As ImmutableArray(Of BoundExpression) = Nothing
' Get the resulting array type by applying the array rank specifiers and the array bounds
Dim arrayType = CreateArrayOf(baseType, node.RankSpecifiers, arrayBoundsOpt, diagnostics)
Dim knownSizes(arrayType.Rank - 1) As DimensionSize
' Bind the bounds. This returns the known sizes of each dimension from the optional bounds
boundArguments = BindArrayBounds(arrayBoundsOpt, diagnostics, knownSizes)
' Bind the array initializer. This may update the known sizes for dimensions with initializers but that are missing explicit bounds
Dim boundInitializers = BindArrayInitializerList(node.Initializer, arrayType, knownSizes, diagnostics)
'Construct a set of size expressions if we were not given any.
If boundArguments.Length = 0 Then
boundArguments = CreateArrayBounds(node, knownSizes, diagnostics)
End If
Return New BoundArrayCreation(node, boundArguments, boundInitializers, arrayType)
End Function
Private Function BindArrayLiteralExpression(node As CollectionInitializerSyntax,
diagnostics As BindingDiagnosticBag) As BoundExpression
' Inspect the collection initializer to determine the literal's rank
' Per 11.1.1, the array literal is reclassified to a value whose type is an array of rank equal to the level of nesting is used.
Dim rank = ComputeArrayLiteralRank(node)
Dim knownSizes(rank - 1) As DimensionSize
Dim hasDominantType As Boolean
Dim numberOfCandidates As Integer
Dim inferredElementType As TypeSymbol = Nothing
Dim arrayInitializer = BindArrayInitializerList(node, knownSizes, hasDominantType, numberOfCandidates, inferredElementType, diagnostics)
' Similar to ReclassifyArrayLiteralExpression:
' Mark as compiler generated so that semantic model does not select the array initialization bound node.
' The array initialization node is not a real expression and lacks a type.
arrayInitializer.SetWasCompilerGenerated()
Dim inferredArrayType = ArrayTypeSymbol.CreateVBArray(inferredElementType, Nothing, knownSizes.Length, Compilation)
Dim sizes As ImmutableArray(Of BoundExpression) = CreateArrayBounds(node, knownSizes, diagnostics)
Return New BoundArrayLiteral(node, hasDominantType, numberOfCandidates, inferredArrayType, sizes, arrayInitializer, Me)
End Function
Private Function CreateArrayBounds(node As SyntaxNode, knownSizes() As DimensionSize, diagnostics As BindingDiagnosticBag) As ImmutableArray(Of BoundExpression)
Dim rank = knownSizes.Length
Dim sizes = New BoundExpression(rank - 1) {}
Dim Int32Type = GetSpecialType(SpecialType.System_Int32, node, diagnostics)
For i As Integer = 0 To knownSizes.Length - 1
Dim size = knownSizes(i)
'It is possible in error scenarios that some of the bounds were not determined.
'Use default values (0) for those.
Dim sizeExpr = New BoundLiteral(
node,
ConstantValue.Create(size.Size),
Int32Type
)
sizeExpr.SetWasCompilerGenerated()
sizes(i) = sizeExpr
Next
Return sizes.AsImmutableOrNull
End Function
Private Shared Function ComputeArrayLiteralRank(node As CollectionInitializerSyntax) As Integer
Dim rank As Integer = 1
Do
Dim initializers = node.Initializers
If initializers.Count = 0 Then
Exit Do
End If
Dim expr = initializers(0)
node = TryCast(expr, CollectionInitializerSyntax)
If node Is Nothing Then
Exit Do
End If
rank += 1
Loop
Return rank
End Function
''' <summary>
''' Binds CollectionInitializeSyntax. i.e. { expr, ... } from an ArrayCreationExpressionSyntax
''' </summary>
''' <param name="node">The collection initializer syntax</param>
''' <param name="type">The type of array.</param>
''' <param name="knownSizes">This is in/out. It comes in with sizes from explicit bounds but will be updated based on the number of initializers for dimensions without bounds</param>
''' <param name="diagnostics">Where to put errors</param>
Private Function BindArrayInitializerList(node As CollectionInitializerSyntax,
type As ArrayTypeSymbol,
knownSizes As DimensionSize(),
diagnostics As BindingDiagnosticBag) As BoundArrayInitialization
Debug.Assert(type IsNot Nothing)
Dim result = BindArrayInitializerList(node, type, knownSizes, 1, Nothing, diagnostics)
Return result
End Function
''' <summary>
''' Binds CollectionInitializeSyntax. i.e. { expr, ... } from an ArrayCreationExpressionSyntax
''' </summary>
''' <param name="node">The collection initializer syntax</param>
''' <param name="knownSizes">This is in/out. It comes in with sizes from explicit bounds but will be updated based on the number of initializers for dimensions without bounds</param>
''' <param name="hasDominantType">When the inferred type is Object() indicates that the dominant type algorithm computed this type.</param>
''' <param name="numberOfCandidates" >The number of candidates found during inference</param>
''' <param name="inferredElementType" >The inferred element type</param>
''' <param name="diagnostics">Where to put errors</param>
Private Function BindArrayInitializerList(node As CollectionInitializerSyntax,
knownSizes As DimensionSize(),
<Out> ByRef hasDominantType As Boolean,
<Out> ByRef numberOfCandidates As Integer,
<Out> ByRef inferredElementType As TypeSymbol,
diagnostics As BindingDiagnosticBag) As BoundArrayInitialization
' Infer the type for this array literal
Dim initializers As ArrayBuilder(Of BoundExpression) = ArrayBuilder(Of BoundExpression).GetInstance
Dim result = BindArrayInitializerList(node, Nothing, knownSizes, 1, initializers, diagnostics)
inferredElementType = InferDominantTypeOfExpressions(node, initializers, diagnostics, numberOfCandidates)
If inferredElementType Is Nothing Then
' When no dominant type exists, use Object but remember that there wasn't a dominant type.
inferredElementType = GetSpecialType(SpecialType.System_Object, node, diagnostics)
hasDominantType = False
Else
hasDominantType = True
End If
initializers.Free()
Return result
End Function
Private Function BindArrayInitializerList(node As CollectionInitializerSyntax,
type As ArrayTypeSymbol,
knownSizes As DimensionSize(),
dimension As Integer,
allInitializers As ArrayBuilder(Of BoundExpression),
diagnostics As BindingDiagnosticBag) As BoundArrayInitialization
Debug.Assert(type IsNot Nothing OrElse allInitializers IsNot Nothing)
Dim initializers = ArrayBuilder(Of BoundExpression).GetInstance
Dim arrayInitType As TypeSymbol
If dimension = 1 Then
' binding the outer-most initializer list; the result type is the array type being created.
arrayInitType = type
Else
' binding an inner initializer list; the result type is nothing.
arrayInitType = Nothing
End If
Dim rank As Integer = knownSizes.Length
If dimension <> 1 OrElse node.Initializers.Count <> 0 Then
Debug.Assert(type Is Nothing OrElse type.Rank = rank)
If dimension = rank Then
' We are processing the nth dimension of a rank-n array. We expect
' that these will only be values, not array initializers.
Dim elemType As TypeSymbol = If(type IsNot Nothing, type.ElementType, Nothing)
For Each expressionSyntax In node.Initializers
Dim boundExpression As BoundExpression
If expressionSyntax.Kind <> SyntaxKind.CollectionInitializer Then
boundExpression = BindValue(expressionSyntax, diagnostics)
If elemType IsNot Nothing Then
boundExpression = ApplyImplicitConversion(expressionSyntax, elemType, boundExpression, diagnostics)
End If
Else
boundExpression = ReportDiagnosticAndProduceBadExpression(diagnostics, expressionSyntax, ERRID.ERR_ArrayInitializerTooManyDimensions)
End If
initializers.Add(boundExpression)
If allInitializers IsNot Nothing Then
allInitializers.Add(boundExpression)
End If
Next
Else
' Inductive case; we'd better have another array initializer
For Each expr In node.Initializers
Dim init As BoundArrayInitialization = Nothing
If expr.Kind = SyntaxKind.CollectionInitializer Then
init = Me.BindArrayInitializerList(DirectCast(expr, CollectionInitializerSyntax), type, knownSizes, dimension + 1, allInitializers, diagnostics)
Else
ReportDiagnostic(diagnostics, expr, ERRID.ERR_ArrayInitializerTooFewDimensions)
init = New BoundArrayInitialization(expr, ImmutableArray(Of BoundExpression).Empty, arrayInitType, hasErrors:=True)
End If
initializers.Add(init)
Next
End If
Dim curSize = knownSizes(dimension - 1)
If curSize.Kind = DimensionSize.SizeKind.Unknown Then
knownSizes(dimension - 1) = DimensionSize.ConstantSize(initializers.Count)
ElseIf curSize.Kind = DimensionSize.SizeKind.NotConstant Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_ArrayInitializerForNonConstDim)
Return New BoundArrayInitialization(node, initializers.ToImmutableAndFree(), arrayInitType, hasErrors:=True)
ElseIf curSize.Size < initializers.Count Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_InitializerTooManyElements1, initializers.Count - curSize.Size)
Return New BoundArrayInitialization(node, initializers.ToImmutableAndFree(), arrayInitType, hasErrors:=True)
ElseIf curSize.Size > initializers.Count Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_InitializerTooFewElements1, curSize.Size - initializers.Count)
Return New BoundArrayInitialization(node, initializers.ToImmutableAndFree(), arrayInitType, hasErrors:=True)
End If
End If
Return New BoundArrayInitialization(node, initializers.ToImmutableAndFree(), arrayInitType)
End Function
Private Sub CheckRangeArgumentLowerBound(rangeArgument As RangeArgumentSyntax, diagnostics As BindingDiagnosticBag)
Dim lowerBound = BindValue(rangeArgument.LowerBound, diagnostics)
' This check was moved from the parser to the binder. For backwards compatibility with Dev10, the constant must
' be integral. This seems a bit inconsistent because the range argument allows (0 to 5.0) but not (0.0 to 5.0)
Dim lowerBoundConstantValueOpt As ConstantValue = lowerBound.ConstantValueOpt
If lowerBoundConstantValueOpt Is Nothing OrElse Not lowerBoundConstantValueOpt.IsIntegral OrElse Not lowerBoundConstantValueOpt.IsDefaultValue Then
ReportDiagnostic(diagnostics, rangeArgument.LowerBound, ERRID.ERR_OnlyNullLowerBound)
End If
End Sub
''' <summary>
''' Bind the array bounds and return the sizes for each dimension.
''' </summary>
''' <param name="arrayBoundsOpt">The bounds</param>
''' <param name="diagnostics">Where to put the errors</param>
''' <param name="knownSizes">The bounds if they are constants, if argument is not specified this info is not returned </param>
Private Function BindArrayBounds(arrayBoundsOpt As ArgumentListSyntax,
diagnostics As BindingDiagnosticBag,
Optional knownSizes As DimensionSize() = Nothing,
Optional errorOnEmptyBound As Boolean = False) As ImmutableArray(Of BoundExpression)
If arrayBoundsOpt Is Nothing Then
Return s_noArguments
End If
Dim arguments As SeparatedSyntaxList(Of ArgumentSyntax) = arrayBoundsOpt.Arguments
Dim boundArgumentsBuilder As ArrayBuilder(Of BoundExpression) = ArrayBuilder(Of BoundExpression).GetInstance
Dim int32Type = GetSpecialType(SpecialType.System_Int32, arrayBoundsOpt, diagnostics)
' check if there is any nonempty array bound
' in such case we will require all other bounds be nonempty
For Each argumentSyntax In arguments
Select Case argumentSyntax.Kind
Case SyntaxKind.SimpleArgument, SyntaxKind.RangeArgument
errorOnEmptyBound = True
Exit For
End Select
Next
For i As Integer = 0 To arguments.Count - 1
Dim upperBound As BoundExpression = Nothing
Dim upperBoundSyntax As ExpressionSyntax = Nothing
Dim argumentSyntax = arguments(i)
Select Case argumentSyntax.Kind
Case SyntaxKind.SimpleArgument
Dim simpleArgument = DirectCast(argumentSyntax, SimpleArgumentSyntax)
If simpleArgument.NameColonEquals IsNot Nothing Then
ReportDiagnostic(diagnostics, argumentSyntax, ERRID.ERR_NamedSubscript)
End If
upperBoundSyntax = simpleArgument.Expression
Case SyntaxKind.RangeArgument
Dim rangeArgument = DirectCast(argumentSyntax, RangeArgumentSyntax)
CheckRangeArgumentLowerBound(rangeArgument, diagnostics)
upperBoundSyntax = rangeArgument.UpperBound
Case SyntaxKind.OmittedArgument
If errorOnEmptyBound Then
ReportDiagnostic(diagnostics, argumentSyntax, ERRID.ERR_MissingSubscript)
GoTo lElseClause
Else
Continue For
End If
Case Else
lElseClause:
' TODO - What expression should be generated in this case? Note, the parser already generates a syntax error.
' The syntax is a missing Identifier and not an OmittedArgumentSyntax.
upperBound = BadExpression(argumentSyntax, ErrorTypeSymbol.UnknownResultType)
End Select
If upperBoundSyntax IsNot Nothing Then
upperBound = BindValue(upperBoundSyntax, diagnostics)
upperBound = ApplyImplicitConversion(upperBoundSyntax, int32Type, upperBound, diagnostics)
End If
' Add 1 to the upper bound to get the array size
' Dev10 does not consider checked/unchecked here when folding the addition
' in a case of overflow exception will be thrown at run time
Dim upperBoundConstantValueOpt As ConstantValue = upperBound.ConstantValueOpt
If upperBoundConstantValueOpt IsNot Nothing AndAlso Not upperBoundConstantValueOpt.IsBad Then
' -1 is a valid value it means 0 - length array
' anything less is invalid.
If upperBoundConstantValueOpt.Int32Value < -1 Then
ReportDiagnostic(diagnostics, argumentSyntax, ERRID.ERR_NegativeArraySize)
End If
End If
Dim one = New BoundLiteral(argumentSyntax, ConstantValue.Create(1), int32Type)
one.SetWasCompilerGenerated()
' Try folding the size.
Dim integerOverflow As Boolean = False
Dim divideByZero As Boolean = False
' Note: the value may overflow, but we ignore this and use the overflown value.
Dim value = OverloadResolution.TryFoldConstantBinaryOperator(BinaryOperatorKind.Add, upperBound, one, int32Type, integerOverflow, divideByZero, Nothing)
If knownSizes IsNot Nothing Then
If value IsNot Nothing Then
knownSizes(i) = DimensionSize.ConstantSize(value.Int32Value)
Else
knownSizes(i) = DimensionSize.VariableSize
End If
End If
Dim actualSize = New BoundBinaryOperator(
argumentSyntax,
BinaryOperatorKind.Add,
upperBound,
one,
CheckOverflow,
value,
int32Type
)
actualSize.SetWasCompilerGenerated()
boundArgumentsBuilder.Add(actualSize)
Next
Return boundArgumentsBuilder.ToImmutableAndFree
End Function
Private Function BindLiteralConstant(node As LiteralExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundLiteral
Dim value = node.Token.Value
Dim cv As ConstantValue
Dim type As TypeSymbol = Nothing
If value Is Nothing Then
' this is for Null
cv = ConstantValue.Null
Else
Debug.Assert(Not value.GetType().GetTypeInfo().IsEnum)
Dim specialType As SpecialType = SpecialTypeExtensions.FromRuntimeTypeOfLiteralValue(value)
' VB literals can't be of type byte, sbyte
Debug.Assert(specialType <> specialType.None AndAlso
specialType <> specialType.System_Byte AndAlso
specialType <> specialType.System_SByte)
cv = ConstantValue.Create(value, specialType)
type = GetSpecialType(specialType, node, diagnostics)
End If
Return New BoundLiteral(node, cv, type)
End Function
Friend Function InferDominantTypeOfExpressions(
syntax As SyntaxNode,
Expressions As ArrayBuilder(Of BoundExpression),
diagnostics As BindingDiagnosticBag,
ByRef numCandidates As Integer,
Optional ByRef errorReasons As InferenceErrorReasons = InferenceErrorReasons.Other
) As TypeSymbol
' Arguments: "expressions" is a list of expressions from which to infer dominant type
' Output: we might return Nothing / NumCandidates==0 (if we couldn't find a dominant type)
' Or we might return Object / NumCandidates==0 (if we had to assume Object because no dominant type was found)
' Or we might return Object / NumCandidates>=2 (if we had to assume Object because multiple candidates were found)
' Or we might return a real dominant type from one of the candidates / NumCandidates==1
' In the last case, "winner" is set to one of the expressions who proposed that winning dominant type.
' "Winner" information might be useful if you are calculating the dominant type of "{}" and "{Nothing}"
' and you need to know who the winner is so you can report appropriate warnings on him.
' The dominant type of a list of elements means:
' (1) for each element, attempt to classify it as a value in a context where the target
' type is unknown. So unbound lambdas get classified as anonymous delegates, and array literals get
' classified according to their dominant type (if they have one), and Nothing is ignored, and AddressOf too.
' But skip over empty array literals.
' (2) Consider the types of all elements for which we got a type, and feed these into the dominant-type
' algorithm: if there are multiple candidates such that all other types convert to it through identity/widening,
' then pick the dominant type out of this set. Otherwise, if there is a single all-widening/identity candidate,
' pick this. Otherwise, if there is a single all-widening/identity/narrowing candidate, then pick this.
' (3) Otherwise, if the dominant type algorithm has failed and every element was an empty array literal {}
' then pick Object() and report a warning "Object assumed"
' (4) Otherwise, if every element converts to Object, then pick Object and report a warning "Object assumed".
' (5) Otherwise, there is no dominant type; return Nothing and report an error.
numCandidates = 0
Dim count As Integer = 0
Dim countOfEmptyArrays As Integer = 0 ' To detect case (3)
Dim anEmptyArray As BoundArrayLiteral = Nothing ' Used for case (3), so we'll return one of them
Dim allConvertibleToObject As Boolean = True ' To detect case (4)
Dim typeList As New TypeInferenceCollection()
For Each expression As BoundExpression In Expressions
count += 1
Debug.Assert(expression IsNot Nothing)
Debug.Assert(expression.IsValue())
' Dig through parenthesized.
If Not expression.IsNothingLiteral Then
expression = expression.GetMostEnclosedParenthesizedExpression()
End If
Dim expressionKind As BoundKind = expression.Kind
Dim expressionType As TypeSymbol = expression.Type
If expressionKind = BoundKind.UnboundLambda Then
expressionType = DirectCast(expression, UnboundLambda).InferredAnonymousDelegate.Key
typeList.AddType(expressionType, RequiredConversion.Any, expression)
ElseIf expressionKind = BoundKind.TupleLiteral Then
expressionType = DirectCast(expression, BoundTupleLiteral).InferredType
If expressionType IsNot Nothing Then
typeList.AddType(expressionType, RequiredConversion.Any, expression)
End If
ElseIf expressionKind = BoundKind.ArrayLiteral Then
Dim arrayLiteral = DirectCast(expression, BoundArrayLiteral)
' Empty array literals {} should not be constraints on the dominant type algorithm.
' Array's without a dominant type should not be constraints on the dominant type algorithm.
If arrayLiteral.IsEmptyArrayLiteral Then
countOfEmptyArrays += 1
anEmptyArray = arrayLiteral
ElseIf arrayLiteral.HasDominantType Then
expressionType = New ArrayLiteralTypeSymbol(arrayLiteral)
typeList.AddType(expressionType, RequiredConversion.Any, expression)
End If
ElseIf expressionType IsNot Nothing AndAlso Not expressionType.IsVoidType() AndAlso
Not (expressionType.IsArrayType() AndAlso DirectCast(expressionType, ArrayTypeSymbol).ElementType.IsVoidType()) Then
typeList.AddType(expressionType, RequiredConversion.Any, expression)
If expressionType.IsRestrictedType() Then
' this element is a restricted type; not convertible to object
allConvertibleToObject = False
End If
Else
' What else?
Debug.Assert(expressionType Is Nothing)
If Not expression.IsNothingLiteral Then
' NOTE: Some expressions without type are still convertible to System.Object, example: Nothing literal
allConvertibleToObject = False
End If
' this will pick up AddressOf expressions.
End If
Next
' Here we calculate the dominant type.
' Note: if there were no candidate types in the list, this will fail with errorReason = NoneBest.
errorReasons = InferenceErrorReasons.Other
Dim results = ArrayBuilder(Of DominantTypeData).GetInstance()
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
typeList.FindDominantType(results, errorReasons, useSiteInfo)
If diagnostics.Add(syntax, useSiteInfo) Then
' Suppress additional diagnostics
diagnostics = BindingDiagnosticBag.Discarded
End If
Dim dominantType As TypeSymbol
If results.Count = 1 AndAlso errorReasons = InferenceErrorReasons.Other Then
' main case: we succeeded in finding a dominant type
Debug.Assert(Not results(0).ResultType.IsVoidType(), "internal logic error: how could void have won the dominant type algorithm?")
numCandidates = 1
dominantType = results(0).ResultType
ElseIf count = countOfEmptyArrays AndAlso count > 0 Then
' special case: the dominant type of a list of empty arrays is Object(), not Object.
Debug.Assert(anEmptyArray IsNot Nothing, "internal logic error: if we got at least one empty array, then AnEmptyArray should not be null")
numCandidates = 1
' Use the inferred Object() from the array literal. ReclassifyArrayLiteral depends on the array identity for correct error reporting
' of inference errors.
dominantType = anEmptyArray.InferredType
Debug.Assert(dominantType.IsArrayType AndAlso DirectCast(dominantType, ArrayTypeSymbol).Rank = 1 AndAlso DirectCast(dominantType, ArrayTypeSymbol).ElementType.SpecialType = SpecialType.System_Object)
ElseIf allConvertibleToObject AndAlso (errorReasons And InferenceErrorReasons.Ambiguous) <> 0 Then
' special case: there were multiple dominant types, so we fall back to Object
Debug.Assert(results.Count > 1, "internal logic error: if InferenceErrorReasonsAmbiguous, you'd have expected more than 1 candidate")
numCandidates = results.Count
dominantType = GetSpecialType(SpecialType.System_Object, syntax, diagnostics)
ElseIf allConvertibleToObject Then
' fallback case: we didn't find a dominant type, but can fall back to Object
numCandidates = 0
dominantType = GetSpecialType(SpecialType.System_Object, syntax, diagnostics)
Else
numCandidates = 0
dominantType = Nothing
End If
' Ensure that ArrayLiteralType is not returned from the dominant type algorithm
Dim arrayLiteralType = TryCast(dominantType, ArrayLiteralTypeSymbol)
If arrayLiteralType IsNot Nothing Then
dominantType = arrayLiteralType.ArrayLiteral.InferredType
End If
results.Free()
Return dominantType
End Function
Public Function IsInAsyncContext() As Boolean
Return ContainingMember.Kind = SymbolKind.Method AndAlso DirectCast(ContainingMember, MethodSymbol).IsAsync
End Function
Public Function IsInIteratorContext() As Boolean
Return ContainingMember.Kind = SymbolKind.Method AndAlso DirectCast(ContainingMember, MethodSymbol).IsIterator
End Function
Private Function BindAwait(
node As AwaitExpressionSyntax,
diagnostics As BindingDiagnosticBag,
Optional bindAsStatement As Boolean = False
) As BoundExpression
If IsInQuery Then
ReportDiagnostic(diagnostics, node.AwaitKeyword, ERRID.ERR_BadAsyncInQuery)
ElseIf Not IsInAsyncContext() Then
ReportDiagnostic(diagnostics, node.AwaitKeyword, GetAwaitInNonAsyncError())
End If
Dim operand As BoundExpression = BindExpression(node.Expression, diagnostics)
Return BindAwait(node, operand, diagnostics, bindAsStatement)
End Function
Private Function BindAwait(
node As VisualBasicSyntaxNode,
operand As BoundExpression,
diagnostics As BindingDiagnosticBag,
bindAsStatement As Boolean
) As BoundExpression
' If the user tries to do "await f()" or "await expr.f()" where f is an async sub,
' then we'll give a more helpful error message...
If Not operand.HasErrors AndAlso
operand.Type IsNot Nothing AndAlso
operand.Type.IsVoidType() AndAlso
operand.Kind = BoundKind.Call Then
Dim method As MethodSymbol = DirectCast(operand, BoundCall).Method
If method.IsSub AndAlso method.IsAsync Then
ReportDiagnostic(diagnostics, operand.Syntax, ERRID.ERR_CantAwaitAsyncSub1, method.Name)
Return BadExpression(node, operand, ErrorTypeSymbol.UnknownResultType)
End If
End If
operand = MakeRValue(operand, diagnostics)
If operand.IsNothingLiteral() Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_BadAwaitNothing)
Return BadExpression(node, operand, ErrorTypeSymbol.UnknownResultType)
ElseIf operand.Type.IsObjectType() Then
' Late-bound pattern.
If OptionStrict = OptionStrict.On Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_StrictDisallowsLateBinding)
Return BadExpression(node, operand, ErrorTypeSymbol.UnknownResultType)
ElseIf OptionStrict = OptionStrict.Custom Then
ReportDiagnostic(diagnostics, node, ERRID.WRN_LateBindingResolution)
End If
End If
If operand.HasErrors Then
' Disable error reporting going forward.
diagnostics = BindingDiagnosticBag.Discarded
End If
Dim ignoreDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=diagnostics.AccumulatesDependencies)
' Will accumulate all ignored diagnostics in case we want to add them
Dim allIgnoreDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics)
Dim awaitableInstancePlaceholder = New BoundRValuePlaceholder(operand.Syntax, operand.Type).MakeCompilerGenerated()
Dim awaiterInstancePlaceholder As BoundLValuePlaceholder = Nothing
Dim getAwaiter As BoundExpression = Nothing
Dim isCompleted As BoundExpression = Nothing
Dim getResult As BoundExpression = Nothing
If operand.Type.IsObjectType Then
' Late-bound pattern.
getAwaiter = BindLateBoundMemberAccess(node, WellKnownMemberNames.GetAwaiter, Nothing, awaitableInstancePlaceholder, operand.Type,
ignoreDiagnostics, suppressLateBindingResolutionDiagnostics:=True).MakeCompilerGenerated()
getAwaiter = DirectCast(getAwaiter, BoundLateMemberAccess).SetAccessKind(LateBoundAccessKind.Get)
Debug.Assert(getAwaiter.Type.IsObjectType())
awaiterInstancePlaceholder = New BoundLValuePlaceholder(operand.Syntax, getAwaiter.Type).MakeCompilerGenerated()
isCompleted = BindLateBoundMemberAccess(node, WellKnownMemberNames.IsCompleted, Nothing, awaiterInstancePlaceholder, awaiterInstancePlaceholder.Type,
ignoreDiagnostics, suppressLateBindingResolutionDiagnostics:=True).MakeCompilerGenerated()
isCompleted = DirectCast(isCompleted, BoundLateMemberAccess).SetAccessKind(LateBoundAccessKind.Get)
Debug.Assert(isCompleted.Type.IsObjectType())
getResult = BindLateBoundMemberAccess(node, WellKnownMemberNames.GetResult, Nothing, awaiterInstancePlaceholder, awaiterInstancePlaceholder.Type,
ignoreDiagnostics, suppressLateBindingResolutionDiagnostics:=True).MakeCompilerGenerated()
Debug.Assert(getResult.Type.IsObjectType())
getResult = DirectCast(getResult, BoundLateMemberAccess).SetAccessKind(If(bindAsStatement, LateBoundAccessKind.Call, LateBoundAccessKind.Get))
Debug.Assert(operand.Type.IsErrorType() OrElse ignoreDiagnostics.DiagnosticBag.IsEmptyWithoutResolution())
Else
Dim lookupResult As LookupResult = lookupResult.GetInstance()
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
' 11.25 Await Operator
'
'1. C contains an accessible instance or extension method named GetAwaiter which has no arguments and which returns some type E;
LookupMember(lookupResult, awaitableInstancePlaceholder.Type, WellKnownMemberNames.GetAwaiter, 0, LookupOptions.AllMethodsOfAnyArity, useSiteInfo)
Dim methodGroup As BoundMethodGroup = Nothing
If lookupResult.Kind = LookupResultKind.Good AndAlso lookupResult.Symbols(0).Kind = SymbolKind.Method Then
methodGroup = CreateBoundMethodGroup(
node,
lookupResult,
LookupOptions.Default,
ignoreDiagnostics.AccumulatesDependencies,
awaitableInstancePlaceholder,
Nothing,
QualificationKind.QualifiedViaValue).MakeCompilerGenerated()
ignoreDiagnostics.Clear()
getAwaiter = MakeRValue(BindInvocationExpression(node,
operand.Syntax,
TypeCharacter.None,
methodGroup,
ImmutableArray(Of BoundExpression).Empty,
Nothing,
ignoreDiagnostics,
callerInfoOpt:=node).MakeCompilerGenerated(),
ignoreDiagnostics).MakeCompilerGenerated()
' The result we are looking for is:
' 1) a non-latebound call of an instance (extension method is considered instance) method;
' 2) method doesn't have any parameters, optional parameters should be ruled out;
' 3) method is not a Sub;
' 4) result is not Object.
If getAwaiter.HasErrors OrElse
DiagnosticBagHasErrorsOtherThanObsoleteOnes(ignoreDiagnostics.DiagnosticBag) OrElse
getAwaiter.Kind <> BoundKind.Call OrElse
getAwaiter.Type.IsObjectType() Then
getAwaiter = Nothing
Else
allIgnoreDiagnostics.AddRange(ignoreDiagnostics)
Dim method As MethodSymbol = DirectCast(getAwaiter, BoundCall).Method
If method.IsShared OrElse method.ParameterCount <> 0 Then
getAwaiter = Nothing
End If
End If
Debug.Assert(getAwaiter Is Nothing OrElse Not DiagnosticBagHasErrorsOtherThanObsoleteOnes(ignoreDiagnostics.DiagnosticBag))
End If
If getAwaiter IsNot Nothing AndAlso Not getAwaiter.Type.IsErrorType() Then
' 2. E contains a readable instance property named IsCompleted which takes no arguments and has type Boolean;
awaiterInstancePlaceholder = New BoundLValuePlaceholder(operand.Syntax, getAwaiter.Type).MakeCompilerGenerated()
lookupResult.Clear()
LookupMember(lookupResult, awaiterInstancePlaceholder.Type, WellKnownMemberNames.IsCompleted, 0,
LookupOptions.AllMethodsOfAnyArity Or LookupOptions.IgnoreExtensionMethods, useSiteInfo)
If lookupResult.Kind = LookupResultKind.Good AndAlso lookupResult.Symbols(0).Kind = SymbolKind.Property Then
Dim propertyGroup = New BoundPropertyGroup(node,
lookupResult.Symbols.ToDowncastedImmutable(Of PropertySymbol),
lookupResult.Kind,
awaiterInstancePlaceholder,
QualificationKind.QualifiedViaValue).MakeCompilerGenerated()
ignoreDiagnostics.Clear()
isCompleted = MakeRValue(BindInvocationExpression(node,
operand.Syntax,
TypeCharacter.None,
propertyGroup,
ImmutableArray(Of BoundExpression).Empty,
Nothing,
ignoreDiagnostics,
callerInfoOpt:=node).MakeCompilerGenerated(),
ignoreDiagnostics).MakeCompilerGenerated()
' The result we are looking for is:
' 1) a non-latebound get of an instance property;
' 2) property doesn't have any parameters, optional parameters should be ruled out;
' 3) result is Boolean.
If isCompleted.HasErrors OrElse
DiagnosticBagHasErrorsOtherThanObsoleteOnes(ignoreDiagnostics.DiagnosticBag) OrElse
isCompleted.Kind <> BoundKind.PropertyAccess OrElse
Not isCompleted.Type.IsBooleanType() Then
isCompleted = Nothing
Else
allIgnoreDiagnostics.AddRange(ignoreDiagnostics)
Debug.Assert(DirectCast(isCompleted, BoundPropertyAccess).AccessKind = PropertyAccessKind.Get)
Dim prop As PropertySymbol = DirectCast(isCompleted, BoundPropertyAccess).PropertySymbol
If prop.IsShared OrElse prop.ParameterCount <> 0 Then
isCompleted = Nothing
End If
End If
Debug.Assert(isCompleted Is Nothing OrElse Not DiagnosticBagHasErrorsOtherThanObsoleteOnes(ignoreDiagnostics.DiagnosticBag))
End If
' 3. E contains an accessible instance method named GetResult which takes no arguments;
lookupResult.Clear()
LookupMember(lookupResult, awaiterInstancePlaceholder.Type, WellKnownMemberNames.GetResult, 0,
LookupOptions.AllMethodsOfAnyArity Or LookupOptions.IgnoreExtensionMethods, useSiteInfo)
If lookupResult.Kind = LookupResultKind.Good AndAlso lookupResult.Symbols(0).Kind = SymbolKind.Method Then
methodGroup = CreateBoundMethodGroup(
node,
lookupResult,
LookupOptions.Default,
ignoreDiagnostics.AccumulatesDependencies,
awaiterInstancePlaceholder,
Nothing,
QualificationKind.QualifiedViaValue).MakeCompilerGenerated()
ignoreDiagnostics.Clear()
getResult = BindInvocationExpression(node,
operand.Syntax,
TypeCharacter.None,
methodGroup,
ImmutableArray(Of BoundExpression).Empty,
Nothing,
ignoreDiagnostics,
callerInfoOpt:=node).MakeCompilerGenerated()
' The result we are looking for is:
' 1) a non-latebound call of an instance (extension methods ignored) method;
' 2) method doesn't have any parameters, optional parameters should be ruled out;
If getResult.HasErrors OrElse
DiagnosticBagHasErrorsOtherThanObsoleteOnes(ignoreDiagnostics.DiagnosticBag) OrElse
getResult.Kind <> BoundKind.Call Then
getResult = Nothing
Else
allIgnoreDiagnostics.AddRange(ignoreDiagnostics)
Dim method As MethodSymbol = DirectCast(getResult, BoundCall).Method
Debug.Assert(Not method.IsReducedExtensionMethod)
If method.IsShared OrElse method.ParameterCount <> 0 OrElse
(method.IsSub AndAlso method.IsConditional) Then
getResult = Nothing
End If
End If
Debug.Assert(getResult Is Nothing OrElse Not DiagnosticBagHasErrorsOtherThanObsoleteOnes(ignoreDiagnostics.DiagnosticBag))
End If
' 4. E implements either System.Runtime.CompilerServices.INotifyCompletion or ICriticalNotifyCompletion.
Dim notifyCompletion As NamedTypeSymbol = GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_INotifyCompletion, node, diagnostics)
' ICriticalNotifyCompletion inherits from INotifyCompletion, so a check for INotifyCompletion is sufficient.
If Not notifyCompletion.IsErrorType() AndAlso
Not Conversions.IsWideningConversion(Conversions.ClassifyDirectCastConversion(getAwaiter.Type, notifyCompletion, useSiteInfo)) Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_DoesntImplementAwaitInterface2, getAwaiter.Type, notifyCompletion)
End If
End If
diagnostics.Add(node, useSiteInfo)
lookupResult.Free()
End If
Dim hasErrors As Boolean = False
If getAwaiter Is Nothing Then
hasErrors = True
ReportDiagnostic(diagnostics, node, ERRID.ERR_BadGetAwaiterMethod1, operand.Type)
Debug.Assert(isCompleted Is Nothing AndAlso getResult Is Nothing)
getAwaiter = BadExpression(node, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
ElseIf getAwaiter.Type.IsErrorType() Then
hasErrors = True
ElseIf isCompleted Is Nothing OrElse getResult Is Nothing Then
hasErrors = True
ReportDiagnostic(diagnostics, node, ERRID.ERR_BadIsCompletedOnCompletedGetResult2, getAwaiter.Type, operand.Type)
End If
If awaiterInstancePlaceholder Is Nothing Then
Debug.Assert(hasErrors)
awaiterInstancePlaceholder = New BoundLValuePlaceholder(node, getAwaiter.Type).MakeCompilerGenerated()
End If
If isCompleted Is Nothing Then
isCompleted = BadExpression(node, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
End If
If getResult Is Nothing Then
getResult = BadExpression(node, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
End If
If Not hasErrors Then
diagnostics.AddRange(allIgnoreDiagnostics)
End If
allIgnoreDiagnostics.Free()
ignoreDiagnostics.Free()
Return New BoundAwaitOperator(node, operand,
awaitableInstancePlaceholder, getAwaiter,
awaiterInstancePlaceholder, isCompleted, getResult,
type:=getResult.Type, hasErrors)
End Function
Private Shared Function DiagnosticBagHasErrorsOtherThanObsoleteOnes(bag As DiagnosticBag) As Boolean
If bag.IsEmptyWithoutResolution Then
Return False
End If
For Each diag In bag.AsEnumerable()
If diag.Severity = DiagnosticSeverity.Error Then
Select Case diag.Code
Case ERRID.ERR_UseOfObsoletePropertyAccessor2,
ERRID.ERR_UseOfObsoletePropertyAccessor3,
ERRID.ERR_UseOfObsoleteSymbolNoMessage1,
ERRID.ERR_UseOfObsoleteSymbol2
' ignore
Case Else
Return True
End Select
End If
Next
Return False
End Function
Private Function GetAwaitInNonAsyncError() As DiagnosticInfo
If Me.IsInLambda Then
Return ErrorFactory.ErrorInfo(ERRID.ERR_BadAwaitInNonAsyncLambda)
ElseIf ContainingMember.Kind = SymbolKind.Method Then
Dim method = DirectCast(ContainingMember, MethodSymbol)
If method.IsSub Then
Return ErrorFactory.ErrorInfo(ERRID.ERR_BadAwaitInNonAsyncVoidMethod)
Else
Return ErrorFactory.ErrorInfo(ERRID.ERR_BadAwaitInNonAsyncMethod, method.ReturnType)
End If
End If
Return ErrorFactory.ErrorInfo(ERRID.ERR_BadAwaitNotInAsyncMethodOrLambda)
End Function
End Class
End Namespace
|