/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb
4 978 строк
255 KB
Jan Jones
Validate pooled objects in compiler tests (#82784)
15 май 2026, 09:55
Не верифицирован
15 май 2026, 09:55
e141f0d
Код
Авторство
О чём код?
' 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) typeDiagnostics.Free() leftDiagnostics.Free() If propertyDiagnostics IsNot Nothing Then propertyDiagnostics.Free() End If 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