File: Binding\Binder_Query.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Partial Friend Class Binder
 
        Private Function CreateQueryLambdaSymbol(syntaxNode As VisualBasicSyntaxNode,
                                                 kind As SynthesizedLambdaKind,
                                                 parameters As ImmutableArray(Of BoundLambdaParameterSymbol)) As SynthesizedLambdaSymbol
 
            Debug.Assert(kind.IsQueryLambda)
 
            Return New SynthesizedLambdaSymbol(kind,
                                               syntaxNode,
                                               parameters,
                                               LambdaSymbol.ReturnTypePendingDelegate,
                                               Me)
        End Function
 
        Private Shared Function CreateBoundQueryLambda(queryLambdaSymbol As SynthesizedLambdaSymbol,
                                                       rangeVariables As ImmutableArray(Of RangeVariableSymbol),
                                                       expression As BoundExpression,
                                                       exprIsOperandOfConditionalBranch As Boolean) As BoundQueryLambda
            Return New BoundQueryLambda(queryLambdaSymbol.Syntax, queryLambdaSymbol, rangeVariables, expression, exprIsOperandOfConditionalBranch)
        End Function
 
        Friend Overridable Function BindGroupAggregationExpression(group As GroupAggregationSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
            ' Only special query binders that have enough context can bind GroupAggregationSyntax.
            ' TODO: Do we need to report any diagnostic?
            Debug.Assert(False, "Binding out of context is unsupported!")
            Return BadExpression(group, ErrorTypeSymbol.UnknownResultType)
        End Function
 
        Friend Overridable Function BindFunctionAggregationExpression([function] As FunctionAggregationSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
            ' Only special query binders that have enough context can bind FunctionAggregationSyntax.
            ' TODO: Do we need to report any diagnostic?
            Debug.Assert(False, "Binding out of context is unsupported!")
            Return BadExpression([function], ErrorTypeSymbol.UnknownResultType)
        End Function
 
        ''' <summary>
        ''' Bind a Query Expression.
        ''' This is the entry point.
        ''' </summary>
        Private Function BindQueryExpression(
            query As QueryExpressionSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundExpression
 
            If query.Clauses.Count < 1 Then
                ' Syntax error must have been reported
                Return BadExpression(query, ErrorTypeSymbol.UnknownResultType)
            End If
 
            Dim operators As SyntaxList(Of QueryClauseSyntax).Enumerator = query.Clauses.GetEnumerator()
            Dim moveResult = operators.MoveNext()
            Debug.Assert(moveResult)
 
            Dim current As QueryClauseSyntax = operators.Current
 
            Select Case current.Kind
                Case SyntaxKind.FromClause
                    Return BindFromQueryExpression(query, operators, diagnostics)
 
                Case SyntaxKind.AggregateClause
                    Return BindAggregateQueryExpression(query, operators, diagnostics)
 
                Case Else
                    ' Syntax error must have been reported
                    Return BadExpression(query, ErrorTypeSymbol.UnknownResultType)
            End Select
        End Function
 
        ''' <summary>
        ''' Given a result of binding of initial set of collection range variables, the source,
        ''' bind the rest of the operators in the enumerator.
        '''
        ''' There is a special method to bind an operator of each kind, the common thing among them is that
        ''' all of them take the result we have so far, the source, and return result of an application
        ''' of one or two following operators.
        ''' Some of the methods also take operators enumerator in order to be able to do a necessary look-ahead
        ''' and in some cases even to advance the enumerator themselves.
        ''' Join and From operators absorb following Select or Let, that is when the process of binding of
        ''' a single operator actually handles two and advances the enumerator.
        ''' </summary>
        Private Function BindSubsequentQueryOperators(
            source As BoundQueryClauseBase,
            operators As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClauseBase
            Debug.Assert(source IsNot Nothing)
 
            While operators.MoveNext()
                Dim current As QueryClauseSyntax = operators.Current
 
                Select Case current.Kind
                    Case SyntaxKind.FromClause
                        ' Note, this call can advance [operators] enumerator if it absorbs the following Let or Select.
                        source = BindFromClause(source, DirectCast(current, FromClauseSyntax), operators, diagnostics)
 
                    Case SyntaxKind.SelectClause
                        source = BindSelectClause(source, DirectCast(current, SelectClauseSyntax), operators, diagnostics)
 
                    Case SyntaxKind.LetClause
                        source = BindLetClause(source, DirectCast(current, LetClauseSyntax), operators, diagnostics)
 
                    Case SyntaxKind.WhereClause
                        source = BindWhereClause(source, DirectCast(current, WhereClauseSyntax), diagnostics)
 
                    Case SyntaxKind.SkipWhileClause
                        source = BindSkipWhileClause(source, DirectCast(current, PartitionWhileClauseSyntax), diagnostics)
 
                    Case SyntaxKind.TakeWhileClause
                        source = BindTakeWhileClause(source, DirectCast(current, PartitionWhileClauseSyntax), diagnostics)
 
                    Case SyntaxKind.DistinctClause
                        source = BindDistinctClause(source, DirectCast(current, DistinctClauseSyntax), diagnostics)
 
                    Case SyntaxKind.SkipClause
                        source = BindSkipClause(source, DirectCast(current, PartitionClauseSyntax), diagnostics)
 
                    Case SyntaxKind.TakeClause
                        source = BindTakeClause(source, DirectCast(current, PartitionClauseSyntax), diagnostics)
 
                    Case SyntaxKind.OrderByClause
                        source = BindOrderByClause(source, DirectCast(current, OrderByClauseSyntax), diagnostics)
 
                    Case SyntaxKind.SimpleJoinClause
                        ' Note, this call can advance [operators] enumerator if it absorbs the following Let or Select.
                        source = BindInnerJoinClause(source, DirectCast(current, SimpleJoinClauseSyntax), Nothing, operators, diagnostics)
 
                    Case SyntaxKind.GroupJoinClause
                        source = BindGroupJoinClause(source, DirectCast(current, GroupJoinClauseSyntax), Nothing, operators, diagnostics)
 
                    Case SyntaxKind.GroupByClause
                        source = BindGroupByClause(source, DirectCast(current, GroupByClauseSyntax), diagnostics)
 
                    Case SyntaxKind.AggregateClause
                        source = BindAggregateClause(source, DirectCast(current, AggregateClauseSyntax), operators, diagnostics)
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(current.Kind)
                End Select
            End While
 
            Return source
        End Function
 
        ''' <summary>
        ''' Bind query expression that starts with From keyword, as opposed to the one that starts with Aggregate.
        '''
        '''     From {collection range variables} [{other operators}]
        ''' </summary>
        Private Function BindFromQueryExpression(
            query As QueryExpressionSyntax,
            operators As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryExpression
            ' Note, this call can advance [operators] enumerator if it absorbs the following Let or Select.
            Dim source As BoundQueryClauseBase = BindFromClause(Nothing, DirectCast(operators.Current, FromClauseSyntax), operators, diagnostics)
 
            source = BindSubsequentQueryOperators(source, operators, diagnostics)
 
            If Not source.Type.IsErrorType() AndAlso source.Kind = BoundKind.QueryableSource AndAlso
               DirectCast(source, BoundQueryableSource).Source.Kind = BoundKind.QuerySource Then
                ' Need to apply implicit Select.
                source = BindFinalImplicitSelectClause(source, diagnostics)
            End If
 
            Return New BoundQueryExpression(query, source, source.Type)
 
        End Function
 
        ''' <summary>
        ''' Bind query expression that starts with Aggregate keyword, as opposed to the one that starts with From.
        '''
        '''     Aggregate {collection range variables} [{other operators}] Into {aggregation range variables}
        '''
        ''' If Into clause has one item, a single value is produced. If it has multiple items, values are
        ''' combined into an instance of an Anonymous Type.
        ''' </summary>
        Private Function BindAggregateQueryExpression(
            query As QueryExpressionSyntax,
            operators As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryExpression
            Dim aggregate = DirectCast(operators.Current, AggregateClauseSyntax)
 
            Dim malformedSyntax As Boolean = operators.MoveNext()
 
            Debug.Assert(Not malformedSyntax, "Malformed syntax tree. Parser shouldn't produce a tree like that.")
 
            operators = aggregate.AdditionalQueryOperators.GetEnumerator()
 
            ' Note, this call can advance [operators] enumerator if it absorbs the following Let or Select.
            Dim source As BoundQueryClauseBase = BindCollectionRangeVariables(aggregate, Nothing, aggregate.Variables, operators, diagnostics)
            source = BindSubsequentQueryOperators(source, operators, diagnostics)
 
            Dim aggregationVariables As SeparatedSyntaxList(Of AggregationRangeVariableSyntax) = aggregate.AggregationVariables
            Dim aggregationVariablesCount As Integer = aggregationVariables.Count
 
            Select Case aggregationVariablesCount
                Case 0
                    Debug.Assert(aggregationVariables.Count > 0, "Malformed syntax tree.")
                    Dim intoBinder As New IntoClauseDisallowGroupReferenceBinder(Me, source, source.RangeVariables, source.CompoundVariableType, source.RangeVariables)
 
                    source = New BoundAggregateClause(aggregate, Nothing, Nothing,
                                                      BadExpression(aggregate, source, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated(),
                                                      ImmutableArray(Of RangeVariableSymbol).Empty,
                                                      ErrorTypeSymbol.UnknownResultType,
                                                      ImmutableArray.Create(Of Binder)(intoBinder),
                                                      ErrorTypeSymbol.UnknownResultType)
 
                Case 1
                    ' Simple case, one item in the [Into] clause, source is our group.
                    Dim intoBinder As New IntoClauseDisallowGroupReferenceBinder(Me, source, source.RangeVariables, source.CompoundVariableType, source.RangeVariables)
 
                    Dim aggregationSelector As BoundExpression = Nothing
                    intoBinder.BindAggregationRangeVariable(aggregationVariables(0),
                                                            Nothing,
                                                            aggregationSelector,
                                                            diagnostics)
 
                    source = New BoundAggregateClause(aggregate, Nothing, Nothing,
                                                      aggregationSelector,
                                                      ImmutableArray(Of RangeVariableSymbol).Empty,
                                                      aggregationSelector.Type,
                                                      ImmutableArray.Create(Of Binder)(intoBinder),
                                                      aggregationSelector.Type)
 
                Case Else
                    ' Complex case, need to build an instance of an Anonymous Type.
                    Dim declaredNames As HashSet(Of String) = CreateSetOfDeclaredNames()
 
                    Dim selectors = New BoundExpression(aggregationVariablesCount - 1) {}
                    Dim fields = New AnonymousTypeField(selectors.Length - 1) {}
 
                    Dim groupReference = New BoundRValuePlaceholder(aggregate, source.Type).MakeCompilerGenerated()
                    Dim intoBinder As New IntoClauseDisallowGroupReferenceBinder(Me, groupReference, source.RangeVariables, source.CompoundVariableType, source.RangeVariables)
 
                    For i As Integer = 0 To aggregationVariablesCount - 1
                        Dim rangeVar As RangeVariableSymbol = intoBinder.BindAggregationRangeVariable(aggregationVariables(i),
                                                                                                      declaredNames, selectors(i),
                                                                                                      diagnostics)
 
                        Debug.Assert(rangeVar IsNot Nothing)
                        fields(i) = New AnonymousTypeField(rangeVar.Name, rangeVar.Type, rangeVar.Syntax.GetLocation(), isKeyOrByRef:=True)
                    Next
 
                    Dim result As BoundExpression = BindAnonymousObjectCreationExpression(aggregate,
                                                             New AnonymousTypeDescriptor(fields.AsImmutableOrNull(),
                                                                                         aggregate.IntoKeyword.GetLocation(),
                                                                                         True),
                                                             selectors.AsImmutableOrNull(),
                                                             diagnostics).MakeCompilerGenerated()
 
                    source = New BoundAggregateClause(aggregate, source, groupReference,
                                                      result,
                                                      ImmutableArray(Of RangeVariableSymbol).Empty,
                                                      result.Type,
                                                      ImmutableArray.Create(Of Binder)(intoBinder),
                                                      result.Type)
            End Select
 
            Debug.Assert(Not source.Binders.IsDefault AndAlso source.Binders.Length = 1 AndAlso source.Binders(0) IsNot Nothing)
 
            Return New BoundQueryExpression(query, source, If(malformedSyntax, ErrorTypeSymbol.UnknownResultType, source.Type), hasErrors:=malformedSyntax)
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Aggregate operator.
        '''
        '''     {Preceding query operators} Aggregate {collection range variables} [{other operators}] Into {aggregation range variables}
        '''
        ''' Depending on how many items we have in the INTO clause,
        ''' we will interpret Aggregate operator as follows:
        '''
        ''' FROM a in AA              FROM a in AA
        ''' AGGREGATE b in a.BB  =>   LET count = (FROM b IN a.BB).Count()
        ''' INTO Count()
        '''
        ''' FROM a in AA              FROM a in AA
        ''' AGGREGATE b in a.BB  =>   LET Group = (FROM b IN a.BB)
        ''' INTO Count(),             Select a, Count=Group.Count(), Sum=Group.Sum(b=>b)
        '''      Sum(b)
        '''
        ''' </summary>
        Private Function BindAggregateClause(
            source As BoundQueryClauseBase,
            aggregate As AggregateClauseSyntax,
            operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag
        ) As BoundAggregateClause
            Debug.Assert(operatorsEnumerator.Current Is aggregate)
 
            ' Let's interpret our group.
            ' Create LambdaSymbol for the shape of the Let-selector lambda.
            Dim letSelectorParam As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(source.RangeVariables), 0,
                                                                                                  source.CompoundVariableType,
                                                                                                  aggregate, source.RangeVariables)
 
            ' The lambda that will contain the nested query.
            Dim letSelectorLambdaSymbol = Me.CreateQueryLambdaSymbol(LambdaUtilities.GetAggregateLambdaBody(aggregate),
                                                                     SynthesizedLambdaKind.AggregateQueryLambda,
                                                                     ImmutableArray.Create(letSelectorParam))
 
            ' Create binder for the [Let] selector.
            Dim letSelectorBinder As New QueryLambdaBinder(letSelectorLambdaSymbol, source.RangeVariables)
 
            Dim declaredRangeVariables As ImmutableArray(Of RangeVariableSymbol) = Nothing
            Dim group As BoundQueryClauseBase = Nothing
            Dim intoBinder As IntoClauseDisallowGroupReferenceBinder = Nothing
            Dim letSelector As BoundExpression = letSelectorBinder.BindAggregateClauseFirstSelector(aggregate, operatorsEnumerator,
                                                                                                    source.RangeVariables,
                                                                                                    ImmutableArray(Of RangeVariableSymbol).Empty,
                                                                                                    declaredRangeVariables,
                                                                                                    group,
                                                                                                    intoBinder,
                                                                                                    diagnostics)
 
            Dim letSelectorLambda As BoundQueryLambda
 
            letSelectorLambda = CreateBoundQueryLambda(letSelectorLambdaSymbol,
                                                       source.RangeVariables,
                                                       letSelector,
                                                       exprIsOperandOfConditionalBranch:=False)
 
            letSelectorLambdaSymbol.SetQueryLambdaReturnType(letSelector.Type)
            letSelectorLambda.SetWasCompilerGenerated()
 
            ' Now bind the [Let] operator call.
            Dim underlyingExpression As BoundExpression
 
            If source.Type.IsErrorType() Then
                underlyingExpression = BadExpression(aggregate, ImmutableArray.Create(Of BoundExpression)(source, letSelectorLambda),
                                                         ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
            Else
                Dim callDiagnostics As BindingDiagnosticBag = diagnostics
 
                If ShouldSuppressDiagnostics(letSelectorLambda) Then
                    ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                    callDiagnostics = BindingDiagnosticBag.Discarded
                End If
 
                underlyingExpression = BindQueryOperatorCall(aggregate, source,
                                                             StringConstants.SelectMethod,
                                                             ImmutableArray.Create(Of BoundExpression)(letSelectorLambda),
                                                             aggregate.AggregateKeyword.Span,
                                                             callDiagnostics)
            End If
 
            Return CompleteAggregateClauseBinding(aggregate,
                                                  operatorsEnumerator,
                                                  source.RangeVariables,
                                                  ImmutableArray(Of RangeVariableSymbol).Empty,
                                                  underlyingExpression,
                                                  letSelectorBinder,
                                                  declaredRangeVariables,
                                                  letSelectorLambda.Expression.Type,
                                                  group,
                                                  intoBinder,
                                                  diagnostics)
        End Function
 
        Private Function CompleteAggregateClauseBinding(
            aggregate As AggregateClauseSyntax,
            operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
            sourceRangeVariablesPart1 As ImmutableArray(Of RangeVariableSymbol),
            sourceRangeVariablesPart2 As ImmutableArray(Of RangeVariableSymbol),
            firstSelectExpression As BoundExpression,
            firstSelectSelectorBinder As QueryLambdaBinder,
            firstSelectDeclaredRangeVariables As ImmutableArray(Of RangeVariableSymbol),
            firstSelectCompoundVariableType As TypeSymbol,
            group As BoundQueryClauseBase,
            intoBinder As IntoClauseDisallowGroupReferenceBinder,
            diagnostics As BindingDiagnosticBag
        ) As BoundAggregateClause
            Debug.Assert((sourceRangeVariablesPart1.Length = 0) = (sourceRangeVariablesPart2 = firstSelectSelectorBinder.RangeVariables))
            Debug.Assert((sourceRangeVariablesPart2.Length = 0) = (sourceRangeVariablesPart1 = firstSelectSelectorBinder.RangeVariables))
            Debug.Assert(firstSelectSelectorBinder.RangeVariables.Length = sourceRangeVariablesPart1.Length + sourceRangeVariablesPart2.Length)
 
            Dim result As BoundAggregateClause
            Dim aggregationVariables As SeparatedSyntaxList(Of AggregationRangeVariableSyntax) = aggregate.AggregationVariables
 
            If aggregationVariables.Count <= 1 Then
                ' Simple case
                Debug.Assert(intoBinder IsNot Nothing)
                Debug.Assert(firstSelectDeclaredRangeVariables.Length <= 1)
                result = New BoundAggregateClause(aggregate, Nothing, Nothing,
                                                  firstSelectExpression,
                                                  firstSelectSelectorBinder.RangeVariables.Concat(firstSelectDeclaredRangeVariables),
                                                  firstSelectCompoundVariableType,
                                                  ImmutableArray.Create(Of Binder)(firstSelectSelectorBinder, intoBinder),
                                                  firstSelectExpression.Type)
            Else
 
                ' Complex case, apply the [Select].
                Debug.Assert(intoBinder Is Nothing)
                Debug.Assert(firstSelectDeclaredRangeVariables.Length = 1)
 
                Dim suppressCallDiagnostics As Boolean = (firstSelectExpression.Kind = BoundKind.BadExpression)
 
                If Not suppressCallDiagnostics AndAlso firstSelectExpression.HasErrors AndAlso firstSelectExpression.Kind = BoundKind.QueryClause Then
                    Dim query = DirectCast(firstSelectExpression, BoundQueryClause)
                    suppressCallDiagnostics = query.UnderlyingExpression.Kind = BoundKind.BadExpression
                End If
 
                Dim letOperator = New BoundQueryClause(aggregate,
                                                       firstSelectExpression,
                                                       firstSelectSelectorBinder.RangeVariables.Concat(firstSelectDeclaredRangeVariables),
                                                       firstSelectCompoundVariableType,
                                                       ImmutableArray(Of Binder).Empty,
                                                       firstSelectExpression.Type).MakeCompilerGenerated()
 
                Dim selectSelectorParam As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(letOperator.RangeVariables), 0,
                                                                                                         letOperator.CompoundVariableType,
                                                                                                         aggregate, letOperator.RangeVariables)
 
                ' This lambda only contains non-user code. We associate it with the aggregate clause itself.
                Debug.Assert(LambdaUtilities.IsNonUserCodeQueryLambda(aggregate))
                Dim selectSelectorLambdaSymbol = Me.CreateQueryLambdaSymbol(aggregate,
                                                                            SynthesizedLambdaKind.AggregateNonUserCodeQueryLambda,
                                                                            ImmutableArray.Create(selectSelectorParam))
 
                ' Create new binder for the [Select] selector.
                Dim groupRangeVar As RangeVariableSymbol = firstSelectDeclaredRangeVariables(0)
                Dim selectSelectorBinder As New QueryLambdaBinder(selectSelectorLambdaSymbol, ImmutableArray(Of RangeVariableSymbol).Empty)
                Dim groupReference = New BoundRangeVariable(groupRangeVar.Syntax, groupRangeVar, groupRangeVar.Type).MakeCompilerGenerated()
                intoBinder = New IntoClauseDisallowGroupReferenceBinder(selectSelectorBinder,
                                                                        groupReference, group.RangeVariables, group.CompoundVariableType,
                                                                        firstSelectSelectorBinder.RangeVariables.Concat(group.RangeVariables))
 
                ' Compound range variable after the first [Let] has shape { [<compound key part1>, ][<compound key part2>, ]<group> }.
                Dim compoundKeyReferencePart1 As BoundExpression
                Dim keysRangeVariablesPart1 As ImmutableArray(Of RangeVariableSymbol)
                Dim compoundKeyReferencePart2 As BoundExpression
                Dim keysRangeVariablesPart2 As ImmutableArray(Of RangeVariableSymbol)
 
                If sourceRangeVariablesPart1.Length > 0 Then
                    ' So we need to get a reference to the first property of selector's parameter.
                    Dim anonymousType = DirectCast(selectSelectorParam.Type, AnonymousTypeManager.AnonymousTypePublicSymbol)
                    Dim keyProperty = anonymousType.Properties(0)
 
                    Debug.Assert(keyProperty.Type Is firstSelectSelectorBinder.LambdaSymbol.Parameters(0).Type)
 
                    compoundKeyReferencePart1 = New BoundPropertyAccess(aggregate,
                                                                        keyProperty,
                                                                        Nothing,
                                                                        PropertyAccessKind.Get,
                                                                        False,
                                                                        New BoundParameter(selectSelectorParam.Syntax,
                                                                                           selectSelectorParam, False,
                                                                                           selectSelectorParam.Type).MakeCompilerGenerated(),
                                                                        ImmutableArray(Of BoundExpression).Empty).MakeCompilerGenerated()
 
                    keysRangeVariablesPart1 = sourceRangeVariablesPart1
 
                    If sourceRangeVariablesPart2.Length > 0 Then
                        keyProperty = anonymousType.Properties(1)
 
                        Debug.Assert(keyProperty.Type Is firstSelectSelectorBinder.LambdaSymbol.Parameters(1).Type)
 
                        ' We need to get a reference to the second property of selector's parameter.
                        compoundKeyReferencePart2 = New BoundPropertyAccess(aggregate,
                                                                            keyProperty,
                                                                            Nothing,
                                                                            PropertyAccessKind.Get,
                                                                            False,
                                                                            New BoundParameter(selectSelectorParam.Syntax,
                                                                                               selectSelectorParam, False,
                                                                                               selectSelectorParam.Type).MakeCompilerGenerated(),
                                                                            ImmutableArray(Of BoundExpression).Empty).MakeCompilerGenerated()
 
                        keysRangeVariablesPart2 = sourceRangeVariablesPart2
                    Else
                        compoundKeyReferencePart2 = Nothing
                        keysRangeVariablesPart2 = ImmutableArray(Of RangeVariableSymbol).Empty
                    End If
                ElseIf sourceRangeVariablesPart2.Length > 0 Then
                    ' So we need to get a reference to the first property of selector's parameter.
                    Dim anonymousType = DirectCast(selectSelectorParam.Type, AnonymousTypeManager.AnonymousTypePublicSymbol)
                    Dim keyProperty = anonymousType.Properties(0)
 
                    Debug.Assert(keyProperty.Type Is firstSelectSelectorBinder.LambdaSymbol.Parameters(1).Type)
 
                    compoundKeyReferencePart1 = New BoundPropertyAccess(aggregate,
                                                                        keyProperty,
                                                                        Nothing,
                                                                        PropertyAccessKind.Get,
                                                                        False,
                                                                        New BoundParameter(selectSelectorParam.Syntax,
                                                                                           selectSelectorParam, False,
                                                                                           selectSelectorParam.Type).MakeCompilerGenerated(),
                                                                        ImmutableArray(Of BoundExpression).Empty).MakeCompilerGenerated()
 
                    keysRangeVariablesPart1 = sourceRangeVariablesPart2
                    compoundKeyReferencePart2 = Nothing
                    keysRangeVariablesPart2 = ImmutableArray(Of RangeVariableSymbol).Empty
                Else
                    compoundKeyReferencePart1 = Nothing
                    keysRangeVariablesPart1 = ImmutableArray(Of RangeVariableSymbol).Empty
                    compoundKeyReferencePart2 = Nothing
                    keysRangeVariablesPart2 = ImmutableArray(Of RangeVariableSymbol).Empty
                End If
 
                Dim declaredRangeVariables As ImmutableArray(Of RangeVariableSymbol) = Nothing
                Dim selectSelector As BoundExpression = intoBinder.BindIntoSelector(aggregate,
                                                                                    firstSelectSelectorBinder.RangeVariables,
                                                                                    compoundKeyReferencePart1,
                                                                                    keysRangeVariablesPart1,
                                                                                    compoundKeyReferencePart2,
                                                                                    keysRangeVariablesPart2,
                                                                                    Nothing,
                                                                                    aggregationVariables,
                                                                                    MustProduceFlatCompoundVariable(operatorsEnumerator),
                                                                                    declaredRangeVariables,
                                                                                    diagnostics)
 
                Dim selectSelectorLambda = CreateBoundQueryLambda(selectSelectorLambdaSymbol,
                                                                  letOperator.RangeVariables,
                                                                  selectSelector,
                                                                  exprIsOperandOfConditionalBranch:=False)
 
                selectSelectorLambdaSymbol.SetQueryLambdaReturnType(selectSelector.Type)
                selectSelectorLambda.SetWasCompilerGenerated()
 
                Dim underlyingExpression As BoundExpression
 
                If letOperator.Type.IsErrorType() Then
                    underlyingExpression = BadExpression(aggregate, ImmutableArray.Create(Of BoundExpression)(letOperator, selectSelectorLambda),
                                                             ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                Else
                    Dim callDiagnostics As BindingDiagnosticBag = diagnostics
 
                    If suppressCallDiagnostics OrElse ShouldSuppressDiagnostics(selectSelectorLambda) Then
                        ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                        callDiagnostics = BindingDiagnosticBag.Discarded
                    End If
 
                    underlyingExpression = BindQueryOperatorCall(aggregate, letOperator,
                                                                     StringConstants.SelectMethod,
                                                                     ImmutableArray.Create(Of BoundExpression)(selectSelectorLambda),
                                                                     aggregate.AggregateKeyword.Span,
                                                                     callDiagnostics)
                End If
 
                result = New BoundAggregateClause(aggregate, Nothing, Nothing,
                                                  underlyingExpression,
                                                  firstSelectSelectorBinder.RangeVariables.Concat(declaredRangeVariables),
                                                  selectSelectorLambda.Expression.Type,
                                                  ImmutableArray.Create(Of Binder)(firstSelectSelectorBinder, intoBinder),
                                                  underlyingExpression.Type)
            End If
 
            Debug.Assert(Not result.Binders.IsDefault AndAlso result.Binders.Length = 2 AndAlso
                         result.Binders(0) IsNot Nothing AndAlso result.Binders(1) IsNot Nothing)
 
            Return result
        End Function
 
        ''' <summary>
        ''' Apply implicit Select operator at the end of the query to
        ''' ensure that at least one query operator is called.
        '''
        ''' Basically makes query like:
        '''     From a In AA
        ''' into:
        '''     From a In AA Select a
        ''' </summary>
        Private Function BindFinalImplicitSelectClause(
            source As BoundQueryClauseBase,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
            Debug.Assert(Not source.Type.IsErrorType())
 
            Dim fromClauseSyntax = DirectCast(source.Syntax.Parent, FromClauseSyntax)
 
            ' Create LambdaSymbol for the shape of the selector.
            Dim param As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(source.RangeVariables), 0,
                                                                                       source.CompoundVariableType,
                                                                                       fromClauseSyntax, source.RangeVariables)
 
            ' An implicit selector is an identity (x => x) function that doesn't contain any user code.
            LambdaUtilities.IsNonUserCodeQueryLambda(fromClauseSyntax)
            Dim lambdaSymbol = Me.CreateQueryLambdaSymbol(fromClauseSyntax,
                                                          SynthesizedLambdaKind.FromNonUserCodeQueryLambda,
                                                          ImmutableArray.Create(param))
 
            lambdaSymbol.SetQueryLambdaReturnType(source.CompoundVariableType)
 
            Dim selector As BoundExpression = New BoundParameter(param.Syntax,
                                                                 param,
                                                                 isLValue:=False,
                                                                 type:=param.Type).MakeCompilerGenerated()
 
            Debug.Assert(Not selector.HasErrors)
 
            Dim selectorLambda = CreateBoundQueryLambda(lambdaSymbol,
                                                        ImmutableArray(Of RangeVariableSymbol).Empty,
                                                        selector,
                                                        exprIsOperandOfConditionalBranch:=False)
            selectorLambda.SetWasCompilerGenerated()
 
            Debug.Assert(Not selectorLambda.HasErrors)
 
            If param.Type.IsErrorType() Then
                diagnostics = BindingDiagnosticBag.Discarded
            End If
 
            Dim boundCallOrBadExpression As BoundExpression
            boundCallOrBadExpression = BindQueryOperatorCall(source.Syntax.Parent, source,
                                                             StringConstants.SelectMethod,
                                                             ImmutableArray.Create(Of BoundExpression)(selectorLambda),
                                                             source.Syntax.Span,
                                                             diagnostics)
 
            Debug.Assert(boundCallOrBadExpression.WasCompilerGenerated)
 
            Return New BoundQueryClause(source.Syntax.Parent,
                                        boundCallOrBadExpression,
                                        ImmutableArray(Of RangeVariableSymbol).Empty,
                                        source.CompoundVariableType,
                                        ImmutableArray(Of Binder).Empty,
                                        boundCallOrBadExpression.Type).MakeCompilerGenerated()
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Select operator.
        '''
        '''     {Preceding query operators} Select {expression range variables}
        '''
        ''' From a In AA Select b  ==> AA.Select(Function(a) b)
        '''
        ''' From a In AA Select b, c  ==> AA.Select(Function(a) New With {b, c})
        ''' </summary>
        Private Function BindSelectClause(
            source As BoundQueryClauseBase,
            clauseSyntax As SelectClauseSyntax,
            operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
            Debug.Assert(clauseSyntax Is operatorsEnumerator.Current)
 
            ' Create LambdaSymbol for the shape of the selector.
            Dim param As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(source.RangeVariables), 0,
                                                                                       source.CompoundVariableType,
                                                                                       clauseSyntax, source.RangeVariables)
 
            Dim lambdaSymbol = Me.CreateQueryLambdaSymbol(LambdaUtilities.GetSelectLambdaBody(clauseSyntax),
                                                          SynthesizedLambdaKind.SelectQueryLambda,
                                                          ImmutableArray.Create(param))
 
            ' Create binder for the selector.
            Dim selectorBinder As New QueryLambdaBinder(lambdaSymbol, source.RangeVariables)
 
            Dim declaredRangeVariables As ImmutableArray(Of RangeVariableSymbol) = Nothing
            Dim selector As BoundExpression = selectorBinder.BindSelectClauseSelector(clauseSyntax,
                                                                                      operatorsEnumerator,
                                                                                      declaredRangeVariables,
                                                                                      diagnostics)
 
            Dim selectorLambda = CreateBoundQueryLambda(lambdaSymbol,
                                                        source.RangeVariables,
                                                        selector,
                                                        exprIsOperandOfConditionalBranch:=False)
 
            lambdaSymbol.SetQueryLambdaReturnType(selector.Type)
            selectorLambda.SetWasCompilerGenerated()
 
            ' Now bind the call.
            Dim boundCallOrBadExpression As BoundExpression
 
            If source.Type.IsErrorType() Then
                boundCallOrBadExpression = BadExpression(clauseSyntax, ImmutableArray.Create(Of BoundExpression)(source, selectorLambda),
                                                         ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
            Else
                If ShouldSuppressDiagnostics(selectorLambda) Then
                    ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                    diagnostics = BindingDiagnosticBag.Discarded
                End If
 
                boundCallOrBadExpression = BindQueryOperatorCall(clauseSyntax, source,
                                                                 StringConstants.SelectMethod,
                                                                 ImmutableArray.Create(Of BoundExpression)(selectorLambda),
                                                                 clauseSyntax.SelectKeyword.Span,
                                                                 diagnostics)
            End If
 
            Return New BoundQueryClause(clauseSyntax,
                                        boundCallOrBadExpression,
                                        declaredRangeVariables,
                                        selectorLambda.Expression.Type,
                                        ImmutableArray.Create(Of Binder)(selectorBinder),
                                        boundCallOrBadExpression.Type)
        End Function
 
        Private Shared Function ShouldSuppressDiagnostics(lambda As BoundQueryLambda) As Boolean
            If lambda.HasErrors Then
                Return True
            End If
 
            For Each param As ParameterSymbol In lambda.LambdaSymbol.Parameters
                If param.Type.IsErrorType() Then
                    Return True
                End If
            Next
 
            Dim bodyType As TypeSymbol = lambda.Expression.Type
            Return bodyType IsNot Nothing AndAlso bodyType.IsErrorType()
        End Function
 
        Private Shared Function ShadowsRangeVariableInTheChildScope(
            childScopeBinder As Binder,
            rangeVar As RangeVariableSymbol
        ) As Boolean
            Dim lookup = LookupResult.GetInstance()
 
            childScopeBinder.LookupInSingleBinder(lookup, rangeVar.Name, 0, Nothing, childScopeBinder, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
 
            Dim result As Boolean = (lookup.IsGood AndAlso lookup.Symbols(0).Kind = SymbolKind.RangeVariable)
 
            lookup.Free()
 
            Return result
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Let operator.
        '''
        '''     {Preceding query operators} Let {expression range variables}
        '''
        ''' Ex: From a In AA Let b  ==> AA.Select(Function(a) New With {a, b})
        '''
        ''' Ex: From a In AA Let b, c  ==> AA.Select(Function(a) New With {a, b}).Select(Function({a, b}) New With {a, b, c})
        '''
        ''' Note, that preceding Select operator can introduce unnamed range variable, which is dropped by the Let
        '''
        ''' Ex: From a In AA Select a + 1 Let b ==> AA.Select(Function(a) a + 1).Select(Function(unnamed) b)
        '''
        ''' Also, depending on the amount of expression range variables declared by the Let, and the following query operators,
        ''' translation can produce a nested, as opposed to flat, compound variable.
        '''
        ''' Ex: From a In AA Let b, c, d ==> AA.Select(Function(a) New With {a, b}).
        '''                                     Select(Function({a, b}) New With {{a, b}, c}).
        '''                                     Select(Function({{a, b}, c}) New With {a, b, c, d})
        ''' </summary>
        Private Function BindLetClause(
            source As BoundQueryClauseBase,
            clauseSyntax As LetClauseSyntax,
            operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag,
            Optional skipFirstVariable As Boolean = False
        ) As BoundQueryClause
            Debug.Assert(clauseSyntax Is operatorsEnumerator.Current)
 
            Dim callDiagnostics As BindingDiagnosticBag = diagnostics
 
            Dim variables As SeparatedSyntaxList(Of ExpressionRangeVariableSyntax) = clauseSyntax.Variables
            Debug.Assert(variables.Count > 0, "Malformed syntax tree.")
 
            If variables.Count = 0 Then
                ' Handle malformed tree gracefully.
                Debug.Assert(Not skipFirstVariable)
                Return New BoundQueryClause(clauseSyntax,
                                            BadExpression(clauseSyntax, source, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated(),
                                            source.RangeVariables.Add(RangeVariableSymbol.CreateForErrorRecovery(Me,
                                                                                                                 clauseSyntax,
                                                                                                                 ErrorTypeSymbol.UnknownResultType)),
                                            ErrorTypeSymbol.UnknownResultType,
                                            ImmutableArray.Create(Me),
                                            ErrorTypeSymbol.UnknownResultType,
                                            hasErrors:=True)
            End If
 
            Debug.Assert(Not skipFirstVariable OrElse source.Syntax Is variables.First)
 
            For i = If(skipFirstVariable, 1, 0) To variables.Count - 1
 
                Dim variable As ExpressionRangeVariableSyntax = variables(i)
 
                ' Create LambdaSymbol for the shape of the selector lambda.
                Dim param As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(source.RangeVariables), 0,
                                                                                           source.CompoundVariableType,
                                                                                           variable, source.RangeVariables)
 
                Dim lambdaSymbol = Me.CreateQueryLambdaSymbol(LambdaUtilities.GetLetVariableLambdaBody(variable),
                                                              SynthesizedLambdaKind.LetVariableQueryLambda,
                                                              ImmutableArray.Create(param))
 
                ' Create binder for a variable expression.
                Dim selectorBinder As New QueryLambdaBinder(lambdaSymbol, source.RangeVariables)
 
                Dim declaredRangeVariables As ImmutableArray(Of RangeVariableSymbol) = Nothing
                Dim selector As BoundExpression = selectorBinder.BindLetClauseVariableSelector(variable,
                                                                                               operatorsEnumerator,
                                                                                               declaredRangeVariables,
                                                                                               diagnostics)
 
                Dim selectorLambda = CreateBoundQueryLambda(lambdaSymbol,
                                                            source.RangeVariables,
                                                            selector,
                                                            exprIsOperandOfConditionalBranch:=False)
 
                lambdaSymbol.SetQueryLambdaReturnType(selector.Type)
                selectorLambda.SetWasCompilerGenerated()
 
                ' Now bind the call.
                Dim boundCallOrBadExpression As BoundExpression
 
                If source.Type.IsErrorType() Then
                    boundCallOrBadExpression = BadExpression(variable, ImmutableArray.Create(Of BoundExpression)(source, selectorLambda),
                                                             ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                Else
                    If callDiagnostics IsNot BindingDiagnosticBag.Discarded AndAlso ShouldSuppressDiagnostics(selectorLambda) Then
                        ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                        callDiagnostics = BindingDiagnosticBag.Discarded
                    End If
 
                    Dim operatorNameLocation As TextSpan
 
                    If i = 0 Then
                        ' This is the first variable.
                        operatorNameLocation = clauseSyntax.LetKeyword.Span
                    Else
                        operatorNameLocation = variables.GetSeparator(i - 1).Span
                    End If
 
                    boundCallOrBadExpression = BindQueryOperatorCall(variable, source,
                                                                     StringConstants.SelectMethod,
                                                                     ImmutableArray.Create(Of BoundExpression)(selectorLambda),
                                                                     operatorNameLocation,
                                                                     callDiagnostics)
                End If
 
                source = New BoundQueryClause(variable,
                                              boundCallOrBadExpression,
                                              source.RangeVariables.Concat(declaredRangeVariables),
                                              selectorLambda.Expression.Type,
                                              ImmutableArray.Create(Of Binder)(selectorBinder),
                                              boundCallOrBadExpression.Type)
            Next
 
            Return DirectCast(source, BoundQueryClause)
        End Function
 
        ''' <summary>
        ''' In some scenarios, it is safe to leave compound variable in nested form when there is an
        ''' operator down the road that does its own projection (Select, Group By, ...).
        ''' All following operators have to take an Anonymous Type in both cases and, since there is no way to
        ''' restrict the shape of the Anonymous Type in method's declaration, the operators should be
        ''' insensitive to the shape of the Anonymous Type.
        ''' </summary>
        Private Shared Function MustProduceFlatCompoundVariable(
            operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator
        ) As Boolean
 
            While operatorsEnumerator.MoveNext()
                Select Case operatorsEnumerator.Current.Kind
                    Case SyntaxKind.SimpleJoinClause,
                         SyntaxKind.GroupJoinClause,
                         SyntaxKind.SelectClause,
                         SyntaxKind.LetClause,
                         SyntaxKind.FromClause,
                         SyntaxKind.AggregateClause
                        Return False
 
                    Case SyntaxKind.GroupByClause
                        ' If [Group By] doesn't have selector for a group's element, we must produce flat result.
                        ' Element of the group can be observed through result of the query.
                        Dim groupBy = DirectCast(operatorsEnumerator.Current, GroupByClauseSyntax)
                        Return groupBy.Items.Count = 0
                End Select
            End While
 
            Return True
        End Function
 
        ''' <summary>
        ''' In some scenarios, it is safe to leave compound variable in nested form when there is an
        ''' operator down the road that does its own projection (Select, Group By, ...).
        ''' All following operators have to take an Anonymous Type in both cases and, since there is no way to
        ''' restrict the shape of the Anonymous Type in method's declaration, the operators should be
        ''' insensitive to the shape of the Anonymous Type.
        ''' </summary>
        Private Shared Function MustProduceFlatCompoundVariable(
            groupOrInnerJoin As JoinClauseSyntax,
            operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator
        ) As Boolean
            Select Case groupOrInnerJoin.Parent.Kind
                Case SyntaxKind.SimpleJoinClause
                    ' If we are nested into an Inner Join, it is safe to not flatten.
                    ' Parent join will take care of flattening.
                    Return False
 
                Case SyntaxKind.GroupJoinClause
                    Dim groupJoin = DirectCast(groupOrInnerJoin.Parent, GroupJoinClauseSyntax)
 
                    ' If we are nested into a Group Join, we are building the group for it.
                    ' It is safe to not flatten, if there is another nested join after this one,
                    ' the last nested join will take care of flattening.
                    Return groupOrInnerJoin Is groupJoin.AdditionalJoins.LastOrDefault
 
                Case Else
                    Return MustProduceFlatCompoundVariable(operatorsEnumerator)
            End Select
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, if any, bind the following From operator.
        '''
        '''     [{Preceding query operators}] From {collection range variables}
        '''
        ''' Ex: From a In AA  ==> AA
        '''
        ''' Ex: From a In AA, b in BB  ==> AA.SelectMany(Function(a) BB, Function(a, b) New With {a, b})
        '''
        ''' Ex: {source with range variable 'd'} From a In AA, b in BB  ==> source.SelectMany(Function(d) AA, Function(d, a) New With {d, a}).
        '''                                                                        SelectMany(Function({d, a}) BB,
        '''                                                                                   Function({d, a}, b) New With {d, a, b})
        '''
        ''' Note, that preceding Select operator can introduce unnamed range variable, which is dropped by the From
        '''
        ''' Ex: From a In AA Select a + 1 From b in BB ==> AA.Select(Function(a) a + 1).
        '''                                                   SelectMany(Function(unnamed) BB,
        '''                                                              Function(unnamed, b) b)
        '''
        ''' Also, depending on the amount of collection range variables declared by the From, and the following query operators,
        ''' translation can produce a nested, as opposed to flat, compound variable.
        '''
        ''' Ex: From a In AA From b In BB, c In CC, d In DD ==> AA.SelectMany(Function(a) BB, Function(a, b) New With {a, b}).
        '''                                                        SelectMany(Function({a, b}) CC, Function({a, b}, c) New With {{a, b}, c}).
        '''                                                        SelectMany(Function({{a, b}, c}) DD,
        '''                                                                   Function({{a, b}, c}, d) New With {a, b, c, d})
        '''
        ''' If From operator translation results in a SelectMany call and the From is immediately followed by a Select or a Let operator,
        ''' they are absorbed by the From translation. When this happens, operatorsEnumerator is advanced appropriately.
        '''
        ''' Ex: From a In AA From b In BB Select a + b ==> AA.SelectMany(Function(a) BB, Function(a, b) a + b)
        '''
        ''' Ex: From a In AA From b In BB Let c ==> AA.SelectMany(Function(a) BB, Function(a, b) new With {a, b, c})
        '''
        ''' </summary>
        Private Function BindFromClause(
            sourceOpt As BoundQueryClauseBase,
            from As FromClauseSyntax,
            ByRef operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClauseBase
            Debug.Assert(from Is operatorsEnumerator.Current)
            Return BindCollectionRangeVariables(from, sourceOpt, from.Variables, operatorsEnumerator, diagnostics)
        End Function
 
        ''' <summary>
        ''' See comments for BindFromClause method, this method actually does all the work.
        ''' </summary>
        Private Function BindCollectionRangeVariables(
            clauseSyntax As QueryClauseSyntax,
            sourceOpt As BoundQueryClauseBase,
            variables As SeparatedSyntaxList(Of CollectionRangeVariableSyntax),
            ByRef operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClauseBase
 
            Debug.Assert(clauseSyntax.IsKind(SyntaxKind.AggregateClause) OrElse clauseSyntax.IsKind(SyntaxKind.FromClause))
            Debug.Assert(variables.Count > 0, "Malformed syntax tree.")
 
            ' Handle malformed tree gracefully.
            If variables.Count = 0 Then
                Dim rangeVar = RangeVariableSymbol.CreateForErrorRecovery(Me, clauseSyntax, ErrorTypeSymbol.UnknownResultType)
 
                If sourceOpt Is Nothing Then
                    Return New BoundQueryableSource(clauseSyntax,
                                                    New BoundQuerySource(BadExpression(clauseSyntax, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()).MakeCompilerGenerated(),
                                                    Nothing,
                                                    ImmutableArray.Create(rangeVar),
                                                    ErrorTypeSymbol.UnknownResultType,
                                                    ImmutableArray.Create(Me),
                                                    ErrorTypeSymbol.UnknownResultType,
                                                    hasErrors:=True)
                Else
                    Return New BoundQueryClause(clauseSyntax,
                                                BadExpression(clauseSyntax, sourceOpt, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated(),
                                                sourceOpt.RangeVariables.Add(rangeVar),
                                                ErrorTypeSymbol.UnknownResultType,
                                                ImmutableArray.Create(Me),
                                                ErrorTypeSymbol.UnknownResultType,
                                                hasErrors:=True)
                End If
            End If
 
            Dim source As BoundQueryClauseBase = sourceOpt
 
            If source Is Nothing Then
                ' We are at the beginning of the query.
                ' Let's go ahead and process the first collection range variable then.
                source = BindCollectionRangeVariable(variables(0), True, Nothing, diagnostics)
                Debug.Assert(source.RangeVariables.Length = 1)
            End If
 
            Dim callDiagnostics As BindingDiagnosticBag = diagnostics
 
            For i = If(source Is sourceOpt, 0, 1) To variables.Count - 1
 
                Dim variable As CollectionRangeVariableSyntax = variables(i)
 
                ' Create LambdaSymbol for the shape of the many-selector lambda.
                Dim manySelectorParam As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(source.RangeVariables), 0,
                                                                                                       source.CompoundVariableType,
                                                                                                       variable, source.RangeVariables)
 
                Dim manySelectorLambdaSymbol = Me.CreateQueryLambdaSymbol(LambdaUtilities.GetFromOrAggregateVariableLambdaBody(variable),
                                                                          SynthesizedLambdaKind.FromOrAggregateVariableQueryLambda,
                                                                          ImmutableArray.Create(manySelectorParam))
 
                ' Create binder for the many selector.
                Dim manySelectorBinder As New QueryLambdaBinder(manySelectorLambdaSymbol, source.RangeVariables)
 
                Dim manySelector As BoundQueryableSource = manySelectorBinder.BindCollectionRangeVariable(variable, False, Nothing, diagnostics)
                Debug.Assert(manySelector.RangeVariables.Length = 1)
 
                Dim manySelectorLambda = CreateBoundQueryLambda(manySelectorLambdaSymbol,
                                                                source.RangeVariables,
                                                                manySelector,
                                                                exprIsOperandOfConditionalBranch:=False)
 
                ' Note, we are not setting return type for the manySelectorLambdaSymbol because
                ' we want it to be taken from the target delegate type. We don't care what it is going to be
                ' because it doesn't affect types of range variables after this operator.
                manySelectorLambda.SetWasCompilerGenerated()
 
                ' Create LambdaSymbol for the shape of the join-selector lambda.
                Dim joinSelectorParamLeft As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterNameLeft(source.RangeVariables), 0,
                                                                                                           source.CompoundVariableType,
                                                                                                           variable, source.RangeVariables)
 
                Dim joinSelectorParamRight As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterNameRight(manySelector.RangeVariables), 1,
                                                                                                            manySelector.CompoundVariableType,
                                                                                                            variable, manySelector.RangeVariables)
                Dim lambdaBinders As ImmutableArray(Of Binder)
 
                ' If this is the last collection range variable, see if the next operator is
                ' a Select or a Let. If it is, we should absorb it by putting its selector
                ' in the join lambda.
                Dim absorbNextOperator As QueryClauseSyntax = Nothing
 
                If i = variables.Count - 1 Then
                    absorbNextOperator = JoinShouldAbsorbNextOperator(operatorsEnumerator)
                End If
 
                Dim sourceRangeVariables = source.RangeVariables
                Dim joinSelectorRangeVariables As ImmutableArray(Of RangeVariableSymbol) = sourceRangeVariables.Concat(manySelector.RangeVariables)
                Dim joinSelectorDeclaredRangeVariables As ImmutableArray(Of RangeVariableSymbol)
                Dim joinSelector As BoundExpression
                Dim group As BoundQueryClauseBase = Nothing
                Dim intoBinder As IntoClauseDisallowGroupReferenceBinder = Nothing
                Dim joinSelectorBinder As QueryLambdaBinder = Nothing
 
                Dim joinSelectorLambdaKind As SynthesizedLambdaKind = Nothing
                Dim joinSelectorSyntax As VisualBasicSyntaxNode = Nothing
                GetAbsorbingJoinSelectorLambdaKindAndSyntax(clauseSyntax, absorbNextOperator, joinSelectorLambdaKind, joinSelectorSyntax)
 
                Dim joinSelectorLambdaSymbol = Me.CreateQueryLambdaSymbol(joinSelectorSyntax,
                                                                          joinSelectorLambdaKind,
                                                                          ImmutableArray.Create(joinSelectorParamLeft, joinSelectorParamRight))
 
                If absorbNextOperator IsNot Nothing Then
 
                    ' Absorb selector of the next operator.
                    joinSelectorBinder = New QueryLambdaBinder(joinSelectorLambdaSymbol, joinSelectorRangeVariables)
 
                    joinSelectorDeclaredRangeVariables = Nothing
                    joinSelector = joinSelectorBinder.BindAbsorbingJoinSelector(absorbNextOperator,
                                                                                operatorsEnumerator,
                                                                                sourceRangeVariables,
                                                                                manySelector.RangeVariables,
                                                                                joinSelectorDeclaredRangeVariables,
                                                                                group,
                                                                                intoBinder,
                                                                                diagnostics)
 
                    lambdaBinders = ImmutableArray.Create(Of Binder)(manySelectorBinder, joinSelectorBinder)
                Else
                    joinSelectorDeclaredRangeVariables = ImmutableArray(Of RangeVariableSymbol).Empty
 
                    If sourceRangeVariables.Length > 0 Then
                        ' Need to build an Anonymous Type.
                        joinSelectorBinder = New QueryLambdaBinder(joinSelectorLambdaSymbol, joinSelectorRangeVariables)
 
                        ' If it is not the last variable in the list, we simply combine source's
                        ' compound variable (an instance of its Anonymous Type) with our new variable,
                        ' creating new compound variable of nested Anonymous Type.
 
                        ' In some scenarios, it is safe to leave compound variable in nested form when there is an
                        ' operator down the road that does its own projection (Select, Group By, ...).
                        ' All following operators have to take an Anonymous Type in both cases and, since there is no way to
                        ' restrict the shape of the Anonymous Type in method's declaration, the operators should be
                        ' insensitive to the shape of the Anonymous Type.
                        joinSelector = joinSelectorBinder.BuildJoinSelector(variable,
                                                                            (i = variables.Count - 1 AndAlso
                                                                                MustProduceFlatCompoundVariable(operatorsEnumerator)),
                                                                            diagnostics)
                    Else
                        ' Easy case, no need to build an Anonymous Type.
                        Debug.Assert(sourceRangeVariables.Length = 0)
                        joinSelector = New BoundParameter(joinSelectorParamRight.Syntax, joinSelectorParamRight,
                                                           False, joinSelectorParamRight.Type).MakeCompilerGenerated()
                    End If
 
                    lambdaBinders = ImmutableArray.Create(Of Binder)(manySelectorBinder)
                End If
 
                ' Join selector is either associated with absorbed select/let/aggregate clause
                ' or it doesn't contain user code (just pairs outer with inner into an anonymous type or is an identity).
                Dim joinSelectorLambda = CreateBoundQueryLambda(joinSelectorLambdaSymbol,
                                                                joinSelectorRangeVariables,
                                                                joinSelector,
                                                                exprIsOperandOfConditionalBranch:=False)
 
                joinSelectorLambdaSymbol.SetQueryLambdaReturnType(joinSelector.Type)
                joinSelectorLambda.SetWasCompilerGenerated()
 
                ' Now bind the call.
                Dim boundCallOrBadExpression As BoundExpression
 
                If source.Type.IsErrorType() Then
                    boundCallOrBadExpression = BadExpression(variable, ImmutableArray.Create(Of BoundExpression)(source, manySelectorLambda, joinSelectorLambda),
                                                             ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                Else
                    If callDiagnostics IsNot BindingDiagnosticBag.Discarded AndAlso
                       (ShouldSuppressDiagnostics(manySelectorLambda) OrElse ShouldSuppressDiagnostics(joinSelectorLambda)) Then
                        ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                        callDiagnostics = BindingDiagnosticBag.Discarded
                    End If
 
                    Dim operatorNameLocation As TextSpan
 
                    If i = 0 Then
                        ' This is the first variable.
                        operatorNameLocation = clauseSyntax.GetFirstToken().Span
                    Else
                        operatorNameLocation = variables.GetSeparator(i - 1).Span
                    End If
 
                    boundCallOrBadExpression = BindQueryOperatorCall(variable, source,
                                                                     StringConstants.SelectManyMethod,
                                                                     ImmutableArray.Create(Of BoundExpression)(manySelectorLambda, joinSelectorLambda),
                                                                     operatorNameLocation,
                                                                     callDiagnostics)
                End If
 
                source = New BoundQueryClause(variable,
                                              boundCallOrBadExpression,
                                              joinSelectorRangeVariables,
                                              joinSelectorLambda.Expression.Type,
                                              lambdaBinders,
                                              boundCallOrBadExpression.Type)
 
                If absorbNextOperator IsNot Nothing Then
                    Debug.Assert(i = variables.Count - 1)
                    source = AbsorbOperatorFollowingJoin(DirectCast(source, BoundQueryClause),
                                                         absorbNextOperator, operatorsEnumerator,
                                                         joinSelectorDeclaredRangeVariables,
                                                         joinSelectorBinder,
                                                         sourceRangeVariables,
                                                         manySelector.RangeVariables,
                                                         group,
                                                         intoBinder,
                                                         diagnostics)
                    Exit For
                End If
            Next
 
            Return source
        End Function
 
        Private Shared Sub GetAbsorbingJoinSelectorLambdaKindAndSyntax(
            clauseSyntax As QueryClauseSyntax,
            absorbNextOperator As QueryClauseSyntax,
            <Out> ByRef lambdaKind As SynthesizedLambdaKind,
            <Out> ByRef lambdaSyntax As VisualBasicSyntaxNode)
 
            ' Join selector is either associated with absorbed select/let/aggregate clause
            ' or it doesn't contain user code (just pairs outer with inner into an anonymous type or is an identity).
 
            If absorbNextOperator Is Nothing Then
                Select Case clauseSyntax.Kind
                    Case SyntaxKind.SimpleJoinClause
                        lambdaKind = SynthesizedLambdaKind.JoinNonUserCodeQueryLambda
 
                    Case SyntaxKind.FromClause
                        lambdaKind = SynthesizedLambdaKind.FromNonUserCodeQueryLambda
 
                    Case SyntaxKind.AggregateClause
                        lambdaKind = SynthesizedLambdaKind.FromNonUserCodeQueryLambda
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(clauseSyntax.Kind)
                End Select
 
                lambdaSyntax = clauseSyntax
                Debug.Assert(LambdaUtilities.IsNonUserCodeQueryLambda(lambdaSyntax))
            Else
                Select Case absorbNextOperator.Kind
                    Case SyntaxKind.AggregateClause
                        Dim firstVariable = DirectCast(absorbNextOperator, AggregateClauseSyntax).Variables.First
                        lambdaSyntax = LambdaUtilities.GetFromOrAggregateVariableLambdaBody(firstVariable)
                        lambdaKind = SynthesizedLambdaKind.AggregateQueryLambda
 
                    Case SyntaxKind.LetClause
                        Dim firstVariable = DirectCast(absorbNextOperator, LetClauseSyntax).Variables.First
                        lambdaSyntax = LambdaUtilities.GetLetVariableLambdaBody(firstVariable)
                        lambdaKind = SynthesizedLambdaKind.LetVariableQueryLambda
 
                    Case SyntaxKind.SelectClause
                        Dim selectClause = DirectCast(absorbNextOperator, SelectClauseSyntax)
                        lambdaSyntax = LambdaUtilities.GetSelectLambdaBody(selectClause)
                        lambdaKind = SynthesizedLambdaKind.SelectQueryLambda
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(absorbNextOperator.Kind)
                End Select
 
                Debug.Assert(LambdaUtilities.IsLambdaBody(lambdaSyntax))
            End If
        End Sub
 
        Private Shared Function JoinShouldAbsorbNextOperator(
            ByRef operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator
        ) As QueryClauseSyntax
            Dim copyOfOperatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator = operatorsEnumerator
 
            If copyOfOperatorsEnumerator.MoveNext() Then
                Dim nextOperator As QueryClauseSyntax = copyOfOperatorsEnumerator.Current
 
                Select Case nextOperator.Kind
                    Case SyntaxKind.LetClause
                        If DirectCast(nextOperator, LetClauseSyntax).Variables.Count > 0 Then
                            ' Absorb Let
                            operatorsEnumerator = copyOfOperatorsEnumerator
                            Return nextOperator
                        Else
                            ' Malformed tree.
                            Debug.Assert(DirectCast(nextOperator, LetClauseSyntax).Variables.Count > 0, "Malformed syntax tree.")
                        End If
 
                    Case SyntaxKind.SelectClause
                        ' Absorb Select
                        operatorsEnumerator = copyOfOperatorsEnumerator
                        Return nextOperator
 
                    Case SyntaxKind.AggregateClause
                        ' Absorb Aggregate
                        operatorsEnumerator = copyOfOperatorsEnumerator
                        Return nextOperator
 
                End Select
            End If
 
            Return Nothing
        End Function
 
        Private Function AbsorbOperatorFollowingJoin(
            absorbingJoin As BoundQueryClause,
            absorbNextOperator As QueryClauseSyntax,
            operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
            joinSelectorDeclaredRangeVariables As ImmutableArray(Of RangeVariableSymbol),
            joinSelectorBinder As QueryLambdaBinder,
            leftRangeVariables As ImmutableArray(Of RangeVariableSymbol),
            rightRangeVariables As ImmutableArray(Of RangeVariableSymbol),
            group As BoundQueryClauseBase,
            intoBinder As IntoClauseDisallowGroupReferenceBinder,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClauseBase
            Debug.Assert(absorbNextOperator Is operatorsEnumerator.Current)
            Debug.Assert(absorbingJoin.Binders.Length > 1)
 
            Select Case absorbNextOperator.Kind
                Case SyntaxKind.SelectClause
                    ' Absorb Select.
                    Return New BoundQueryClause(absorbNextOperator,
                                                absorbingJoin, joinSelectorDeclaredRangeVariables,
                                                absorbingJoin.CompoundVariableType,
                                                ImmutableArray.Create(absorbingJoin.Binders.Last),
                                                absorbingJoin.Type)
 
                Case SyntaxKind.LetClause
                    ' Absorb Let.
                    ' First expression range variable was handled by the join selector,
                    ' create node for it.
                    Dim [let] = DirectCast(absorbNextOperator, LetClauseSyntax)
                    Debug.Assert([let].Variables.Count > 0)
                    Dim firstVariable As ExpressionRangeVariableSyntax = [let].Variables.First
                    Dim absorbedLet As New BoundQueryClause(firstVariable,
                                                            absorbingJoin,
                                                            absorbingJoin.RangeVariables.Concat(joinSelectorDeclaredRangeVariables),
                                                            absorbingJoin.CompoundVariableType,
                                                            ImmutableArray.Create(absorbingJoin.Binders.Last),
                                                            absorbingJoin.Type)
 
                    ' Handle the rest of the variables.
                    Return BindLetClause(absorbedLet, [let], operatorsEnumerator, diagnostics, skipFirstVariable:=True)
 
                Case SyntaxKind.AggregateClause
                    ' Absorb Aggregate.
 
                    Return CompleteAggregateClauseBinding(DirectCast(absorbNextOperator, AggregateClauseSyntax),
                                                          operatorsEnumerator,
                                                          leftRangeVariables,
                                                          rightRangeVariables,
                                                          absorbingJoin,
                                                          joinSelectorBinder,
                                                          joinSelectorDeclaredRangeVariables,
                                                          absorbingJoin.CompoundVariableType,
                                                          group,
                                                          intoBinder,
                                                          diagnostics)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(absorbNextOperator.Kind)
 
            End Select
 
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the outer, bind the following Join operator.
        '''
        '''     [{Preceding query operators}] Join {collection range variable}
        '''                                        [{additional joins}]
        '''                                   On {condition}
        '''
        ''' Ex: From a In AA Join b in BB On Key(a) Equals Key(b)  ==> AA.Join(BB, Function(a) Key(a), Function(b) Key(b),
        '''                                                                    Function(a, b) New With {a, b})
        '''
        ''' Ex: From a In AA                       AA.Join(
        '''     Join b in BB                               BB.Join(CC, Function(b) Key(b), Function(c) Key(c),
        '''          Join c in CC             ==>                  Function(b, c) New With {b, c}),
        '''          On Key(c) Equals Key(b)               Function(a) Key(a), Function({b, c}) Key(b),
        '''     On Key(a) Equals Key(b)                    Function(a, {b, c}) New With {a, b, c})
        '''
        '''
        ''' Also, depending on the amount of collection range variables in scope, and the following query operators,
        ''' translation can produce a nested, as opposed to flat, compound variable.
        '''
        ''' Ex: From a In AA                       AA.Join(BB, Function(a) Key(a), Function(b) Key(b),
        '''     Join b in BB                               Function(a, b) New With {a, b}).
        '''     On Key(a) Equals Key(b)               Join(CC, Function({a, b}) Key(a, b), Function(c) Key(c),
        '''     Join c in CC             ==>               Function({a, b}, c) New With {{a, b}, c}).
        '''     On Key(c) Equals Key(a, b)            Join(DD, Function({{a, b}, c}) Key(a, b, c), Function(d) Key(d),
        '''     Join d in DD                               Function({{a, b}, c}, d) New With {a, b, c, d})
        '''     On Key(a, b, c) Equals Key(d)
        '''
        ''' If Join is immediately followed by a Select or a Let operator, they are absorbed by the translation.
        ''' When this happens, operatorsEnumerator is advanced appropriately.
        '''
        ''' Ex: From a In AA Join b in BB On Key(a) Equals Key(b)  ==> AA.Join(BB, Function(a) Key(a), Function(b) Key(b),
        '''     Select a + b                                                   Function(a, b) a + b)
        '''
        ''' Ex: From a In AA Join b in BB On Key(a) Equals Key(b)  ==> AA.Join(BB, Function(a) Key(a), Function(b) Key(b),
        '''     Let c                                                   Function(a, b) New With {a, b, c})
        '''
        ''' </summary>
        Private Function BindInnerJoinClause(
            outer As BoundQueryClauseBase,
            join As SimpleJoinClauseSyntax,
            declaredNames As HashSet(Of String),
            ByRef operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClauseBase
            Debug.Assert(join.Kind = SyntaxKind.SimpleJoinClause)
            Debug.Assert((declaredNames IsNot Nothing) = (join.Parent.Kind = SyntaxKind.SimpleJoinClause OrElse join.Parent.Kind = SyntaxKind.GroupJoinClause))
 
            Dim isNested As Boolean
 
            If declaredNames Is Nothing Then
                Debug.Assert(join Is operatorsEnumerator.Current)
                isNested = False
                declaredNames = CreateSetOfDeclaredNames(outer.RangeVariables)
            Else
                isNested = True
                AssertDeclaredNames(declaredNames, outer.RangeVariables)
            End If
 
            Debug.Assert(join.JoinedVariables.Count = 1, "Malformed syntax tree.")
 
            Dim inner As BoundQueryClauseBase
 
            If join.JoinedVariables.Count = 0 Then
                ' Malformed tree.
                Dim rangeVar = RangeVariableSymbol.CreateForErrorRecovery(Me, join, ErrorTypeSymbol.UnknownResultType)
 
                inner = New BoundQueryableSource(join,
                                                 New BoundQuerySource(BadExpression(join, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()).MakeCompilerGenerated(),
                                                 Nothing,
                                                 ImmutableArray.Create(rangeVar),
                                                 ErrorTypeSymbol.UnknownResultType,
                                                 ImmutableArray(Of Binder).Empty,
                                                 ErrorTypeSymbol.UnknownResultType,
                                                 hasErrors:=True).MakeCompilerGenerated()
            Else
                inner = BindCollectionRangeVariable(join.JoinedVariables(0), False, declaredNames, diagnostics)
            End If
 
            For Each additionalJoin As JoinClauseSyntax In join.AdditionalJoins
                Select Case additionalJoin.Kind
                    Case SyntaxKind.SimpleJoinClause
                        inner = BindInnerJoinClause(inner, DirectCast(additionalJoin, SimpleJoinClauseSyntax), declaredNames, Nothing, diagnostics)
                    Case SyntaxKind.GroupJoinClause
                        inner = BindGroupJoinClause(inner, DirectCast(additionalJoin, GroupJoinClauseSyntax), declaredNames, Nothing, diagnostics)
                End Select
            Next
 
            AssertDeclaredNames(declaredNames, inner.RangeVariables)
 
            ' Bind keys.
            Dim outerKeyLambda As BoundQueryLambda = Nothing
            Dim innerKeyLambda As BoundQueryLambda = Nothing
            Dim outerKeyBinder As QueryLambdaBinder = Nothing
            Dim innerKeyBinder As QueryLambdaBinder = Nothing
            Dim joinSelectorRangeVariables As ImmutableArray(Of RangeVariableSymbol) = outer.RangeVariables.Concat(inner.RangeVariables)
 
            QueryLambdaBinder.BindJoinKeys(Me, join, outer, inner,
                                           joinSelectorRangeVariables,
                                           outerKeyLambda, outerKeyBinder,
                                           innerKeyLambda, innerKeyBinder,
                                           diagnostics)
 
            ' Create LambdaSymbol for the shape of the join-selector lambda.
            Dim joinSelectorParamLeft As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterNameLeft(outer.RangeVariables), 0,
                                                                                                       outer.CompoundVariableType,
                                                                                                       join, outer.RangeVariables)
 
            Dim joinSelectorParamRight As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterNameRight(inner.RangeVariables), 1,
                                                                                                        inner.CompoundVariableType,
                                                                                                        join, inner.RangeVariables)
 
            Dim lambdaBinders As ImmutableArray(Of Binder)
 
            ' If the next operator is a Select or a Let, we should absorb it by putting its selector
            ' in the join lambda.
            Dim absorbNextOperator As QueryClauseSyntax = Nothing
 
            If Not isNested Then
                absorbNextOperator = JoinShouldAbsorbNextOperator(operatorsEnumerator)
            End If
 
            Dim joinSelectorDeclaredRangeVariables As ImmutableArray(Of RangeVariableSymbol)
            Dim joinSelector As BoundExpression
            Dim group As BoundQueryClauseBase = Nothing
            Dim intoBinder As IntoClauseDisallowGroupReferenceBinder = Nothing
 
            Dim joinSelectorLambdaKind As SynthesizedLambdaKind = Nothing
            Dim joinSelectorSyntax As VisualBasicSyntaxNode = Nothing
            GetAbsorbingJoinSelectorLambdaKindAndSyntax(join, absorbNextOperator, joinSelectorLambdaKind, joinSelectorSyntax)
 
            Dim joinSelectorLambdaSymbol = Me.CreateQueryLambdaSymbol(joinSelectorSyntax,
                                                                      joinSelectorLambdaKind,
                                                                      ImmutableArray.Create(joinSelectorParamLeft, joinSelectorParamRight))
 
            Dim joinSelectorBinder As New QueryLambdaBinder(joinSelectorLambdaSymbol, joinSelectorRangeVariables)
 
            If absorbNextOperator IsNot Nothing Then
 
                ' Absorb selector of the next operator.
                joinSelectorDeclaredRangeVariables = Nothing
                joinSelectorSyntax = Nothing
                joinSelector = joinSelectorBinder.BindAbsorbingJoinSelector(absorbNextOperator,
                                                                            operatorsEnumerator,
                                                                            outer.RangeVariables,
                                                                            inner.RangeVariables,
                                                                            joinSelectorDeclaredRangeVariables,
                                                                            group,
                                                                            intoBinder,
                                                                            diagnostics)
 
                lambdaBinders = ImmutableArray.Create(Of Binder)(outerKeyBinder, innerKeyBinder, joinSelectorBinder)
            Else
                Debug.Assert(outer.RangeVariables.Length > 0 AndAlso inner.RangeVariables.Length > 0)
                joinSelectorDeclaredRangeVariables = ImmutableArray(Of RangeVariableSymbol).Empty
 
                ' Need to build an Anonymous Type.
 
                ' In some scenarios, it is safe to leave compound variable in nested form when there is an
                ' operator down the road that does its own projection (Select, Group By, ...).
                ' All following operators have to take an Anonymous Type in both cases and, since there is no way to
                ' restrict the shape of the Anonymous Type in method's declaration, the operators should be
                ' insensitive to the shape of the Anonymous Type.
                joinSelector = joinSelectorBinder.BuildJoinSelector(join,
                                                                    MustProduceFlatCompoundVariable(join, operatorsEnumerator),
                                                                    diagnostics)
 
                ' Not including joinSelectorBinder because there is no syntax behind this joinSelector,
                ' it is purely synthetic.
                lambdaBinders = ImmutableArray.Create(Of Binder)(outerKeyBinder, innerKeyBinder)
            End If
 
            Dim joinSelectorLambda = CreateBoundQueryLambda(joinSelectorLambdaSymbol,
                                                            joinSelectorRangeVariables,
                                                            joinSelector,
                                                            exprIsOperandOfConditionalBranch:=False)
 
            joinSelectorLambdaSymbol.SetQueryLambdaReturnType(joinSelector.Type)
            joinSelectorLambda.SetWasCompilerGenerated()
 
            ' Now bind the call.
            Dim boundCallOrBadExpression As BoundExpression
 
            If outer.Type.IsErrorType() Then
                boundCallOrBadExpression = BadExpression(join, ImmutableArray.Create(Of BoundExpression)(outer, inner, outerKeyLambda, innerKeyLambda, joinSelectorLambda),
                                                         ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
            Else
                Dim callDiagnostics As BindingDiagnosticBag = diagnostics
 
                If inner.HasErrors OrElse inner.Type.IsErrorType() OrElse
                   ShouldSuppressDiagnostics(outerKeyLambda) OrElse
                   ShouldSuppressDiagnostics(innerKeyLambda) OrElse
                   ShouldSuppressDiagnostics(joinSelectorLambda) Then
                    ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                    callDiagnostics = BindingDiagnosticBag.Discarded
                End If
 
                boundCallOrBadExpression = BindQueryOperatorCall(join, outer,
                                                                 StringConstants.JoinMethod,
                                                                 ImmutableArray.Create(Of BoundExpression)(inner, outerKeyLambda, innerKeyLambda, joinSelectorLambda),
                                                                 join.JoinKeyword.Span,
                                                                 callDiagnostics)
            End If
 
            Dim result As BoundQueryClauseBase = New BoundQueryClause(join,
                                                                      boundCallOrBadExpression,
                                                                      joinSelectorRangeVariables,
                                                                      joinSelectorLambda.Expression.Type,
                                                                      lambdaBinders,
                                                                      boundCallOrBadExpression.Type)
 
            If absorbNextOperator IsNot Nothing Then
                Debug.Assert(Not isNested)
                result = AbsorbOperatorFollowingJoin(DirectCast(result, BoundQueryClause),
                                                     absorbNextOperator, operatorsEnumerator,
                                                     joinSelectorDeclaredRangeVariables,
                                                     joinSelectorBinder,
                                                     outer.RangeVariables,
                                                     inner.RangeVariables,
                                                     group,
                                                     intoBinder,
                                                     diagnostics)
            End If
 
            Return result
        End Function
 
        Private Shared Function CreateSetOfDeclaredNames() As HashSet(Of String)
            Return New HashSet(Of String)(CaseInsensitiveComparison.Comparer)
        End Function
 
        Private Shared Function CreateSetOfDeclaredNames(rangeVariables As ImmutableArray(Of RangeVariableSymbol)) As HashSet(Of String)
            Dim declaredNames As New HashSet(Of String)(CaseInsensitiveComparison.Comparer)
 
            For Each rangeVar As RangeVariableSymbol In rangeVariables
                declaredNames.Add(rangeVar.Name)
            Next
 
            Return declaredNames
        End Function
 
        <Conditional("DEBUG")>
        Private Shared Sub AssertDeclaredNames(declaredNames As HashSet(Of String), rangeVariables As ImmutableArray(Of RangeVariableSymbol))
#If DEBUG Then
            For Each rangeVar As RangeVariableSymbol In rangeVariables
                If Not rangeVar.Name.StartsWith("$"c, StringComparison.Ordinal) Then
                    Debug.Assert(declaredNames.Contains(rangeVar.Name))
                End If
            Next
#End If
        End Sub
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the outer, bind the following Group Join operator.
        '''
        '''     [{Preceding query operators}] Group Join {collection range variable}
        '''                                              [{additional joins}]
        '''                                   On {condition}
        '''                                   Into {aggregation range variables}
        '''
        ''' Ex: From a In AA Group Join b in BB          AA.GroupJoin(BB, Function(a) Key(a), Function(b) Key(b),
        '''                  On Key(a) Equals Key(b) ==>              Function(a, group_b) New With {a, group_b.Count()})
        '''                  Into Count()
        '''
        ''' Also, depending on the amount of collection range variables in scope, and the following query operators,
        ''' translation can produce a nested, as opposed to flat, compound variable (see BindInnerJoinClause for an example).
        '''
        ''' Note, that type of the group must be inferred from the set of available GroupJoin operators in order to be able to
        ''' interpret the aggregation range variables.
        ''' </summary>
        Private Function BindGroupJoinClause(
            outer As BoundQueryClauseBase,
            groupJoin As GroupJoinClauseSyntax,
            declaredNames As HashSet(Of String),
            operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
            Debug.Assert((declaredNames IsNot Nothing) = (groupJoin.Parent.Kind = SyntaxKind.SimpleJoinClause OrElse groupJoin.Parent.Kind = SyntaxKind.GroupJoinClause))
 
            ' Shadowing rules for range variables declared by [Group Join] are a little
            ' bit tricky:
            '  1) Range variables declared within the inner source (JoinedVariables + AdditionalJoins)
            '     can survive only up until the [On] clause, after that, even those in scope, move out of scope
            '     into the Group. Other range variables in scope in the [On] are the outer's range variables.
            '     Range variables from outer's outer are not in scope and, therefore, are never shadowed by the
            '     same-named inner source range variables. Range variables declared within the inner source that
            '     go out of scope before interpretation reaches the [On] clause do not shadow even same-named
            '     outer's range variables.
            '
            '  2) Range variables declared in the [Into] clause must not shadow outer's range variables simply
            '     because they are merged into the same Anonymous Type by the [Into] selector. They also must
            '     not shadow outer's outer range variables (possibly throughout the whole hierarchy),
            '     with which they will later get into the same scope within an [On] clause. Note, that declaredNames
            '     parameter, when passed, includes the names of all such range variables and this function will add
            '     to this set.
 
            Dim namesInScopeInOnClause As HashSet(Of String) = CreateSetOfDeclaredNames(outer.RangeVariables)
 
            If declaredNames Is Nothing Then
                Debug.Assert(groupJoin Is operatorsEnumerator.Current)
                declaredNames = CreateSetOfDeclaredNames(outer.RangeVariables)
            Else
                AssertDeclaredNames(declaredNames, outer.RangeVariables)
            End If
 
            Debug.Assert(groupJoin.JoinedVariables.Count = 1, "Malformed syntax tree.")
 
            Dim inner As BoundQueryClauseBase
 
            If groupJoin.JoinedVariables.Count = 0 Then
                ' Malformed tree.
                Dim rangeVar = RangeVariableSymbol.CreateForErrorRecovery(Me, groupJoin, ErrorTypeSymbol.UnknownResultType)
 
                inner = New BoundQueryableSource(groupJoin,
                                                 New BoundQuerySource(BadExpression(groupJoin, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()).MakeCompilerGenerated(),
                                                 Nothing,
                                                 ImmutableArray.Create(rangeVar),
                                                 ErrorTypeSymbol.UnknownResultType,
                                                 ImmutableArray(Of Binder).Empty,
                                                 ErrorTypeSymbol.UnknownResultType,
                                                 hasErrors:=True).MakeCompilerGenerated()
            Else
                inner = BindCollectionRangeVariable(groupJoin.JoinedVariables(0), False, namesInScopeInOnClause, diagnostics)
            End If
 
            For Each additionalJoin As JoinClauseSyntax In groupJoin.AdditionalJoins
                Select Case additionalJoin.Kind
                    Case SyntaxKind.SimpleJoinClause
                        inner = BindInnerJoinClause(inner, DirectCast(additionalJoin, SimpleJoinClauseSyntax), namesInScopeInOnClause, Nothing, diagnostics)
                    Case SyntaxKind.GroupJoinClause
                        inner = BindGroupJoinClause(inner, DirectCast(additionalJoin, GroupJoinClauseSyntax), namesInScopeInOnClause, Nothing, diagnostics)
                End Select
            Next
 
            Debug.Assert(outer.RangeVariables.Length > 0 AndAlso inner.RangeVariables.Length > 0)
            AssertDeclaredNames(namesInScopeInOnClause, inner.RangeVariables)
 
            ' Bind keys.
            Dim outerKeyLambda As BoundQueryLambda = Nothing
            Dim innerKeyLambda As BoundQueryLambda = Nothing
            Dim outerKeyBinder As QueryLambdaBinder = Nothing
            Dim innerKeyBinder As QueryLambdaBinder = Nothing
 
            QueryLambdaBinder.BindJoinKeys(Me, groupJoin, outer, inner,
                                           outer.RangeVariables.Concat(inner.RangeVariables),
                                           outerKeyLambda, outerKeyBinder,
                                           innerKeyLambda, innerKeyBinder,
                                           diagnostics)
 
            ' Infer type of the resulting group.
            Dim methodGroup As BoundMethodGroup = Nothing
            Dim groupType As TypeSymbol = InferGroupType(outer, inner, groupJoin, outerKeyLambda, innerKeyLambda, methodGroup, diagnostics)
 
            ' Bind the INTO selector.
            Dim intoBinder As IntoClauseBinder = Nothing
            Dim intoRangeVariables As ImmutableArray(Of RangeVariableSymbol) = Nothing
            Dim intoLambda As BoundQueryLambda = BindIntoSelectorLambda(groupJoin, outer.RangeVariables, outer.CompoundVariableType,
                                                                        True, declaredNames,
                                                                        groupType, inner.RangeVariables, inner.CompoundVariableType,
                                                                        groupJoin.AggregationVariables,
                                                                        MustProduceFlatCompoundVariable(groupJoin, operatorsEnumerator),
                                                                        diagnostics, intoBinder, intoRangeVariables)
 
            ' Now bind the call.
            Dim boundCallOrBadExpression As BoundExpression
 
            If outer.Type.IsErrorType() OrElse methodGroup Is Nothing Then
                boundCallOrBadExpression = BadExpression(groupJoin, ImmutableArray.Create(Of BoundExpression)(outer, inner, outerKeyLambda, innerKeyLambda, intoLambda),
                                                         ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
            Else
                Dim callDiagnostics As BindingDiagnosticBag = diagnostics
 
                If inner.HasErrors OrElse inner.Type.IsErrorType() OrElse
                   ShouldSuppressDiagnostics(outerKeyLambda) OrElse
                   ShouldSuppressDiagnostics(innerKeyLambda) OrElse
                   ShouldSuppressDiagnostics(intoLambda) Then
 
                    ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                    callDiagnostics = BindingDiagnosticBag.Discarded
                End If
 
                ' Reusing method group that we got while inferring group type, this way we can avoid doing name lookup again.
                boundCallOrBadExpression = BindQueryOperatorCall(groupJoin, outer,
                                                               StringConstants.GroupJoinMethod,
                                                               methodGroup,
                                                               ImmutableArray.Create(Of BoundExpression)(inner, outerKeyLambda, innerKeyLambda, intoLambda),
                                                               groupJoin.JoinKeyword.Span,
                                                               callDiagnostics)
            End If
 
            Return New BoundQueryClause(groupJoin,
                                        boundCallOrBadExpression,
                                        outer.RangeVariables.Concat(intoRangeVariables),
                                        intoLambda.Expression.Type,
                                        ImmutableArray.Create(Of Binder)(outerKeyBinder, innerKeyBinder, intoBinder),
                                        boundCallOrBadExpression.Type)
 
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Group By operator.
        '''
        '''     [{Preceding query operators}] Group [{items expression range variables}]
        '''                                   By {keys expression range variables}
        '''                                   Into {aggregation range variables}
        '''
        ''' Ex: From a In AA Group By Key(a)          AA.GroupBy(Function(a) Key(a),
        '''                  Into Count()     ==>                Function(key, group_a) New With {key, group_a.Count()})
        '''
        ''' Ex: From a In AA Group Item(a)            AA.GroupBy(Function(a) Key(a),
        '''                  By Key(a)        ==>                Function(a) Item(a),
        '''                  Into Count()                        Function(key, group_a) New With {key, group_a.Count()})
        '''
        ''' Note, that type of the group must be inferred from the set of available GroupBy operators in order to be able to
        ''' interpret the aggregation range variables.
        ''' </summary>
        Private Function BindGroupByClause(
            source As BoundQueryClauseBase,
            groupBy As GroupByClauseSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
 
            ' Handle group items.
            Dim itemsLambdaBinder As QueryLambdaBinder = Nothing
            Dim itemsRangeVariables As ImmutableArray(Of RangeVariableSymbol) = Nothing
            Dim itemsLambda As BoundQueryLambda = BindGroupByItems(source, groupBy, itemsLambdaBinder, itemsRangeVariables, diagnostics)
 
            ' Handle grouping keys.
            Dim keysLambdaBinder As QueryLambdaBinder = Nothing
            Dim keysRangeVariables As ImmutableArray(Of RangeVariableSymbol) = Nothing
            Dim keysLambda As BoundQueryLambda = BindGroupByKeys(source, groupBy, keysLambdaBinder, keysRangeVariables, diagnostics)
            Debug.Assert(keysLambda IsNot Nothing)
 
            ' Infer type of the resulting group.
            Dim methodGroup As BoundMethodGroup = Nothing
            Dim groupType As TypeSymbol = InferGroupType(source, groupBy, itemsLambda, keysLambda, keysRangeVariables, methodGroup, diagnostics)
 
            ' Bind the INTO selector.
            Dim groupRangeVariables As ImmutableArray(Of RangeVariableSymbol)
            Dim groupCompoundVariableType As TypeSymbol
 
            If itemsLambda Is Nothing Then
                groupRangeVariables = source.RangeVariables
                groupCompoundVariableType = source.CompoundVariableType
            Else
                groupRangeVariables = itemsRangeVariables
                groupCompoundVariableType = itemsLambda.Expression.Type
            End If
 
            Dim intoBinder As IntoClauseBinder = Nothing
            Dim intoRangeVariables As ImmutableArray(Of RangeVariableSymbol) = Nothing
            Dim intoLambda As BoundQueryLambda = BindIntoSelectorLambda(groupBy, keysRangeVariables, keysLambda.Expression.Type, False, Nothing,
                                                                        groupType, groupRangeVariables, groupCompoundVariableType,
                                                                        groupBy.AggregationVariables, True,
                                                                        diagnostics, intoBinder, intoRangeVariables)
 
            ' Now bind the call.
            Dim groupByArguments() As BoundExpression
            Dim lambdaBinders As ImmutableArray(Of Binder)
 
            Debug.Assert((itemsLambda Is Nothing) = (itemsLambdaBinder Is Nothing))
 
            If itemsLambda Is Nothing Then
                groupByArguments = {keysLambda, intoLambda}
                lambdaBinders = ImmutableArray.Create(Of Binder)(keysLambdaBinder, intoBinder)
            Else
                groupByArguments = {keysLambda, itemsLambda, intoLambda}
                lambdaBinders = ImmutableArray.Create(Of Binder)(keysLambdaBinder, itemsLambdaBinder, intoBinder)
            End If
 
            Dim boundCallOrBadExpression As BoundExpression
 
            If source.Type.IsErrorType() OrElse methodGroup Is Nothing Then
                boundCallOrBadExpression = BadExpression(groupBy,
                                                         ImmutableArray.Create(Of BoundExpression)(source).AddRange(groupByArguments),
                                                         ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
            Else
                Dim callDiagnostics As BindingDiagnosticBag = diagnostics
 
                If ShouldSuppressDiagnostics(keysLambda) OrElse ShouldSuppressDiagnostics(intoLambda) OrElse
                   (itemsLambda IsNot Nothing AndAlso ShouldSuppressDiagnostics(itemsLambda)) Then
 
                    ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                    callDiagnostics = BindingDiagnosticBag.Discarded
                End If
 
                ' Reusing method group that we got while inferring group type, this way we can avoid doing name lookup again.
                boundCallOrBadExpression = BindQueryOperatorCall(groupBy, source,
                                                               StringConstants.GroupByMethod,
                                                               methodGroup,
                                                               groupByArguments.AsImmutableOrNull(),
                                                               GetGroupByOperatorNameSpan(groupBy),
                                                               callDiagnostics)
            End If
 
            Return New BoundQueryClause(groupBy,
                                        boundCallOrBadExpression,
                                        keysRangeVariables.Concat(intoRangeVariables),
                                        intoLambda.Expression.Type,
                                        lambdaBinders,
                                        boundCallOrBadExpression.Type)
        End Function
 
        Private Shared Function GetGroupByOperatorNameSpan(groupBy As GroupByClauseSyntax) As TextSpan
            If groupBy.Items.Count = 0 Then
                Return GetQueryOperatorNameSpan(groupBy.GroupKeyword, groupBy.ByKeyword)
            Else
                Return groupBy.GroupKeyword.Span
            End If
        End Function
 
        ''' <summary>
        ''' Returns Nothing if items were omitted.
        ''' </summary>
        Private Function BindGroupByItems(
            source As BoundQueryClauseBase,
            groupBy As GroupByClauseSyntax,
            <Out()> ByRef itemsLambdaBinder As QueryLambdaBinder,
            <Out()> ByRef itemsRangeVariables As ImmutableArray(Of RangeVariableSymbol),
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryLambda
            Debug.Assert(itemsLambdaBinder Is Nothing)
            Debug.Assert(itemsRangeVariables.IsDefault)
 
            ' Handle group items.
            Dim itemsLambda As BoundQueryLambda = Nothing
            Dim items As SeparatedSyntaxList(Of ExpressionRangeVariableSyntax) = groupBy.Items
 
            If items.Count > 0 Then
                Dim itemsParam As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(source.RangeVariables), 0,
                                                                                                source.CompoundVariableType,
                                                                                                groupBy, source.RangeVariables)
 
                Dim itemsLambdaSymbol = Me.CreateQueryLambdaSymbol(LambdaUtilities.GetGroupByItemsLambdaBody(groupBy),
                                                                   SynthesizedLambdaKind.GroupByItemsQueryLambda,
                                                                   ImmutableArray.Create(itemsParam))
 
                ' Create binder for the selector.
                itemsLambdaBinder = New QueryLambdaBinder(itemsLambdaSymbol, source.RangeVariables)
 
                Dim itemsSelector = itemsLambdaBinder.BindExpressionRangeVariables(items, False, groupBy,
                                                                                   itemsRangeVariables, diagnostics)
 
                itemsLambda = CreateBoundQueryLambda(itemsLambdaSymbol,
                                                     source.RangeVariables,
                                                     itemsSelector,
                                                     exprIsOperandOfConditionalBranch:=False)
 
                itemsLambdaSymbol.SetQueryLambdaReturnType(itemsSelector.Type)
                itemsLambda.SetWasCompilerGenerated()
            Else
                itemsLambdaBinder = Nothing
                itemsRangeVariables = ImmutableArray(Of RangeVariableSymbol).Empty
            End If
 
            Debug.Assert((itemsLambda Is Nothing) = (itemsLambdaBinder Is Nothing))
            Debug.Assert(Not itemsRangeVariables.IsDefault)
            Debug.Assert(itemsLambda IsNot Nothing OrElse itemsRangeVariables.Length = 0)
            Return itemsLambda
        End Function
 
        Private Function BindGroupByKeys(
            source As BoundQueryClauseBase,
            groupBy As GroupByClauseSyntax,
            <Out()> ByRef keysLambdaBinder As QueryLambdaBinder,
            <Out()> ByRef keysRangeVariables As ImmutableArray(Of RangeVariableSymbol),
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryLambda
            Debug.Assert(keysLambdaBinder Is Nothing)
            Debug.Assert(keysRangeVariables.IsDefault)
 
            Dim keys As SeparatedSyntaxList(Of ExpressionRangeVariableSyntax) = groupBy.Keys
 
            Dim keysParam As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(source.RangeVariables), 0,
                                                                                           source.CompoundVariableType,
                                                                                           groupBy, source.RangeVariables)
 
            Dim keysLambdaSymbol = Me.CreateQueryLambdaSymbol(LambdaUtilities.GetGroupByKeysLambdaBody(groupBy),
                                                              SynthesizedLambdaKind.GroupByKeysQueryLambda,
                                                              ImmutableArray.Create(keysParam))
 
            ' Create binder for the selector.
            keysLambdaBinder = New QueryLambdaBinder(keysLambdaSymbol, source.RangeVariables)
 
            Dim keysSelector = keysLambdaBinder.BindExpressionRangeVariables(keys, True, groupBy,
                                                                             keysRangeVariables, diagnostics)
 
            Dim keysLambda = CreateBoundQueryLambda(keysLambdaSymbol,
                                                    source.RangeVariables,
                                                    keysSelector,
                                                    exprIsOperandOfConditionalBranch:=False)
 
            keysLambdaSymbol.SetQueryLambdaReturnType(keysSelector.Type)
            keysLambda.SetWasCompilerGenerated()
 
            Return keysLambda
        End Function
 
        ''' <summary>
        ''' Infer type of the group for a Group By operator from the set of available GroupBy methods.
        '''
        ''' In short, given already bound itemsLambda and keysLambda, this method performs overload
        ''' resolution over the set of available GroupBy operator methods using fake Into lambda:
        '''     Function(key, group As typeToBeInferred) New With {group}
        '''
        ''' If resolution succeeds, the type inferred for the best candidate is our result.
        ''' </summary>
        Private Function InferGroupType(
            source As BoundQueryClauseBase,
            groupBy As GroupByClauseSyntax,
            itemsLambda As BoundQueryLambda,
            keysLambda As BoundQueryLambda,
            keysRangeVariables As ImmutableArray(Of RangeVariableSymbol),
            <Out()> ByRef methodGroup As BoundMethodGroup,
            diagnostics As BindingDiagnosticBag
        ) As TypeSymbol
            Debug.Assert(methodGroup Is Nothing)
 
            Dim groupType As TypeSymbol = ErrorTypeSymbol.UnknownResultType
 
            If Not source.Type.IsErrorType() Then
 
                methodGroup = LookupQueryOperator(groupBy, source, StringConstants.GroupByMethod, Nothing, diagnostics)
                Debug.Assert(methodGroup Is Nothing OrElse methodGroup.ResultKind = LookupResultKind.Good OrElse methodGroup.ResultKind = LookupResultKind.Inaccessible)
 
                If methodGroup Is Nothing Then
                    ReportDiagnostic(diagnostics, Location.Create(groupBy.SyntaxTree, GetGroupByOperatorNameSpan(groupBy)), ERRID.ERR_QueryOperatorNotFound, StringConstants.GroupByMethod)
 
                ElseIf Not (ShouldSuppressDiagnostics(keysLambda) OrElse
                         (itemsLambda IsNot Nothing AndAlso ShouldSuppressDiagnostics(itemsLambda))) Then
 
                    Dim inferenceLambda As New GroupTypeInferenceLambda(groupBy, Me,
                                                                        (New ParameterSymbol() {
                                    CreateQueryLambdaParameterSymbol(GeneratedNameConstants.It1,
                                                                   0,
                                                                   keysLambda.Expression.Type,
                                                                   groupBy, keysRangeVariables),
                                    CreateQueryLambdaParameterSymbol(GeneratedNameConstants.It2,
                                                                   1,
                                                                   Nothing,
                                                                   groupBy)}).AsImmutableOrNull(),
                                                                        Compilation)
 
                    Dim groupByArguments() As BoundExpression
 
                    If itemsLambda Is Nothing Then
                        groupByArguments = {keysLambda, inferenceLambda}
                    Else
                        groupByArguments = {keysLambda, itemsLambda, inferenceLambda}
                    End If
 
                    Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
                    Dim results As OverloadResolution.OverloadResolutionResult = OverloadResolution.QueryOperatorInvocationOverloadResolution(methodGroup,
                                                                                                                                              groupByArguments.AsImmutableOrNull(), Me,
                                                                                                                                              useSiteInfo)
 
                    diagnostics.Add(groupBy, useSiteInfo)
 
                    If results.BestResult.HasValue Then
                        Dim method = DirectCast(results.BestResult.Value.Candidate.UnderlyingSymbol, MethodSymbol)
                        Dim resultSelector As TypeSymbol = method.Parameters(groupByArguments.Length - 1).Type
 
                        groupType = resultSelector.DelegateOrExpressionDelegate(Me).DelegateInvokeMethod.Parameters(1).Type
 
                    ElseIf Not source.HasErrors Then
                        ReportDiagnostic(diagnostics, Location.Create(groupBy.SyntaxTree, GetGroupByOperatorNameSpan(groupBy)), ERRID.ERR_QueryOperatorNotFound, StringConstants.GroupByMethod)
                    End If
                End If
            End If
 
            Debug.Assert(groupType IsNot Nothing)
            Return groupType
        End Function
 
        ''' <summary>
        ''' Infer type of the group for a Group Join operator from the set of available GroupJoin methods.
        '''
        ''' In short, given already bound inner source and the join key lambdas, this method performs overload
        ''' resolution over the set of available GroupJoin operator methods using fake Into lambda:
        '''     Function(outerVar, group As typeToBeInferred) New With {group}
        '''
        ''' If resolution succeeds, the type inferred for the best candidate is our result.
        ''' </summary>
        Private Function InferGroupType(
            outer As BoundQueryClauseBase,
            inner As BoundQueryClauseBase,
            groupJoin As GroupJoinClauseSyntax,
            outerKeyLambda As BoundQueryLambda,
            innerKeyLambda As BoundQueryLambda,
            <Out()> ByRef methodGroup As BoundMethodGroup,
            diagnostics As BindingDiagnosticBag
        ) As TypeSymbol
            Debug.Assert(methodGroup Is Nothing)
 
            Dim groupType As TypeSymbol = ErrorTypeSymbol.UnknownResultType
 
            If Not outer.Type.IsErrorType() Then
 
                ' If outer's type is "good", we should always do a lookup, even if we won't attempt the inference
                ' because BindGroupJoinClause still expects to have a group, unless the lookup fails.
                methodGroup = LookupQueryOperator(groupJoin, outer, StringConstants.GroupJoinMethod, Nothing, diagnostics)
                Debug.Assert(methodGroup Is Nothing OrElse methodGroup.ResultKind = LookupResultKind.Good OrElse methodGroup.ResultKind = LookupResultKind.Inaccessible)
 
                If methodGroup Is Nothing Then
                    ReportDiagnostic(diagnostics,
                                     Location.Create(groupJoin.SyntaxTree, GetQueryOperatorNameSpan(groupJoin.GroupKeyword, groupJoin.JoinKeyword)),
                                     ERRID.ERR_QueryOperatorNotFound, StringConstants.GroupJoinMethod)
 
                ElseIf Not ShouldSuppressDiagnostics(innerKeyLambda) AndAlso Not ShouldSuppressDiagnostics(outerKeyLambda) AndAlso
                       Not inner.HasErrors AndAlso Not inner.Type.IsErrorType() Then
 
                    Dim inferenceLambda As New GroupTypeInferenceLambda(groupJoin, Me,
                                                                        (New ParameterSymbol() {
                                    CreateQueryLambdaParameterSymbol(GeneratedNameConstants.It1,
                                                                   0,
                                                                   outer.CompoundVariableType,
                                                                   groupJoin, outer.RangeVariables),
                                    CreateQueryLambdaParameterSymbol(GeneratedNameConstants.It2,
                                                                   1,
                                                                   Nothing,
                                                                   groupJoin)}).AsImmutableOrNull(),
                                                                        Compilation)
 
                    Dim groupJoinArguments() As BoundExpression = {inner, outerKeyLambda, innerKeyLambda, inferenceLambda}
 
                    Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
                    Dim results As OverloadResolution.OverloadResolutionResult = OverloadResolution.QueryOperatorInvocationOverloadResolution(methodGroup,
                                                                                                                                              groupJoinArguments.AsImmutableOrNull(), Me,
                                                                                                                                              useSiteInfo)
 
                    diagnostics.Add(groupJoin, useSiteInfo)
 
                    If results.BestResult.HasValue Then
                        Dim method = DirectCast(results.BestResult.Value.Candidate.UnderlyingSymbol, MethodSymbol)
                        Dim resultSelector As TypeSymbol = method.Parameters(groupJoinArguments.Length - 1).Type
 
                        Dim resultSelectorDelegate = resultSelector.DelegateOrExpressionDelegate(Me)
                        groupType = resultSelectorDelegate.DelegateInvokeMethod.Parameters(1).Type
 
                    ElseIf Not outer.HasErrors Then
                        ReportDiagnostic(diagnostics,
                                         Location.Create(groupJoin.SyntaxTree, GetQueryOperatorNameSpan(groupJoin.GroupKeyword, groupJoin.JoinKeyword)),
                                         ERRID.ERR_QueryOperatorNotFound, StringConstants.GroupJoinMethod)
                    End If
                End If
            End If
 
            Debug.Assert(groupType IsNot Nothing)
            Return groupType
        End Function
 
        ''' <summary>
        ''' This is a helper method to create a BoundQueryLambda for an Into clause
        ''' of a Group By or a Group Join operator.
        ''' </summary>
        Private Function BindIntoSelectorLambda(
            clauseSyntax As QueryClauseSyntax,
            keysRangeVariables As ImmutableArray(Of RangeVariableSymbol),
            keysCompoundVariableType As TypeSymbol,
            addKeysInScope As Boolean,
            declaredNames As HashSet(Of String),
            groupType As TypeSymbol,
            groupRangeVariables As ImmutableArray(Of RangeVariableSymbol),
            groupCompoundVariableType As TypeSymbol,
            aggregationVariables As SeparatedSyntaxList(Of AggregationRangeVariableSyntax),
            mustProduceFlatCompoundVariable As Boolean,
            diagnostics As BindingDiagnosticBag,
            <Out()> ByRef intoBinder As IntoClauseBinder,
            <Out()> ByRef intoRangeVariables As ImmutableArray(Of RangeVariableSymbol)
        ) As BoundQueryLambda
            Debug.Assert(clauseSyntax.Kind = SyntaxKind.GroupByClause OrElse clauseSyntax.Kind = SyntaxKind.GroupJoinClause)
            Debug.Assert(mustProduceFlatCompoundVariable OrElse clauseSyntax.Kind = SyntaxKind.GroupJoinClause)
            Debug.Assert((declaredNames IsNot Nothing) = (clauseSyntax.Kind = SyntaxKind.GroupJoinClause))
            Debug.Assert(keysRangeVariables.Length > 0)
            Debug.Assert(intoBinder Is Nothing)
            Debug.Assert(intoRangeVariables.IsDefault)
 
            Dim keyParam As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(keysRangeVariables), 0,
                                                                                          keysCompoundVariableType,
                                                                                          clauseSyntax, keysRangeVariables)
 
            Dim groupParam As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GeneratedNameConstants.ItAnonymous, 1,
                                                                                            groupType, clauseSyntax)
 
            Debug.Assert(LambdaUtilities.IsNonUserCodeQueryLambda(clauseSyntax))
            Dim intoLambdaSymbol = Me.CreateQueryLambdaSymbol(clauseSyntax,
                                                              SynthesizedLambdaKind.GroupNonUserCodeQueryLambda,
                                                              ImmutableArray.Create(keyParam, groupParam))
 
            ' Create binder for the INTO lambda.
            Dim intoLambdaBinder As New QueryLambdaBinder(intoLambdaSymbol, ImmutableArray(Of RangeVariableSymbol).Empty)
            Dim groupReference = New BoundParameter(groupParam.Syntax, groupParam, False, groupParam.Type).MakeCompilerGenerated()
 
            intoBinder = New IntoClauseBinder(intoLambdaBinder,
                                              groupReference, groupRangeVariables, groupCompoundVariableType,
                                              If(addKeysInScope, keysRangeVariables.Concat(groupRangeVariables), groupRangeVariables))
 
            Dim intoSelector As BoundExpression = intoBinder.BindIntoSelector(clauseSyntax,
                                                                              keysRangeVariables,
                                                                              New BoundParameter(keyParam.Syntax, keyParam, False, keyParam.Type).MakeCompilerGenerated(),
                                                                              keysRangeVariables,
                                                                              Nothing,
                                                                              ImmutableArray(Of RangeVariableSymbol).Empty,
                                                                              declaredNames,
                                                                              aggregationVariables,
                                                                              mustProduceFlatCompoundVariable,
                                                                              intoRangeVariables,
                                                                              diagnostics)
 
            Dim intoLambda = CreateBoundQueryLambda(intoLambdaSymbol,
                                                    keysRangeVariables,
                                                    intoSelector,
                                                    exprIsOperandOfConditionalBranch:=False)
 
            intoLambdaSymbol.SetQueryLambdaReturnType(intoSelector.Type)
            intoLambda.SetWasCompilerGenerated()
 
            Return intoLambda
        End Function
 
        Private Sub VerifyRangeVariableName(rangeVar As RangeVariableSymbol, identifier As SyntaxToken, diagnostics As BindingDiagnosticBag)
            Debug.Assert(identifier.Parent Is rangeVar.Syntax)
 
            If identifier.GetTypeCharacter() <> TypeCharacter.None Then
                ReportDiagnostic(diagnostics, identifier, ERRID.ERR_QueryAnonymousTypeDisallowsTypeChar)
            End If
 
            If Compilation.ObjectType.GetMembers(rangeVar.Name).Length > 0 Then
                ReportDiagnostic(diagnostics, identifier, ERRID.ERR_QueryInvalidControlVariableName1)
            Else
                VerifyNameShadowingInMethodBody(rangeVar, identifier, identifier, diagnostics)
            End If
        End Sub
 
        Private Shared Function GetQueryLambdaParameterSyntax(syntaxNode As VisualBasicSyntaxNode, rangeVariables As ImmutableArray(Of RangeVariableSymbol)) As VisualBasicSyntaxNode
            If rangeVariables.Length = 1 Then
                Return rangeVariables(0).Syntax
            End If
 
            Return syntaxNode
        End Function
 
        Private Function CreateQueryLambdaParameterSymbol(
            name As String,
            ordinal As Integer,
            type As TypeSymbol,
            syntaxNode As VisualBasicSyntaxNode,
            rangeVariables As ImmutableArray(Of RangeVariableSymbol)
        ) As BoundLambdaParameterSymbol
            syntaxNode = GetQueryLambdaParameterSyntax(syntaxNode, rangeVariables)
            Dim param = New BoundLambdaParameterSymbol(name, ordinal, type, isByRef:=False, syntaxNode:=syntaxNode, location:=syntaxNode.GetLocation())
            Return param
        End Function
 
        Private Shared Function CreateQueryLambdaParameterSymbol(
            name As String,
            ordinal As Integer,
            type As TypeSymbol,
            syntaxNode As VisualBasicSyntaxNode
        ) As BoundLambdaParameterSymbol
            Dim param = New BoundLambdaParameterSymbol(name, ordinal, type, isByRef:=False, syntaxNode:=syntaxNode, location:=syntaxNode.GetLocation())
            Return param
        End Function
 
        Private Shared Function GetQueryLambdaParameterName(rangeVariables As ImmutableArray(Of RangeVariableSymbol)) As String
            Select Case rangeVariables.Length
                Case 0
                    Return GeneratedNameConstants.ItAnonymous
                Case 1
                    Return rangeVariables(0).Name
                Case Else
                    Return GeneratedNameConstants.It
            End Select
        End Function
 
        Private Shared Function GetQueryLambdaParameterNameLeft(rangeVariables As ImmutableArray(Of RangeVariableSymbol)) As String
            Select Case rangeVariables.Length
                Case 0
                    Return GeneratedNameConstants.ItAnonymous
                Case 1
                    Return rangeVariables(0).Name
                Case Else
                    Return GeneratedNameConstants.It1
            End Select
        End Function
 
        Private Shared Function GetQueryLambdaParameterNameRight(rangeVariables As ImmutableArray(Of RangeVariableSymbol)) As String
            Select Case rangeVariables.Length
                Case 0
                    Throw ExceptionUtilities.UnexpectedValue(rangeVariables.Length)
                Case 1
                    Return rangeVariables(0).Name
                Case Else
                    Return GeneratedNameConstants.It2
            End Select
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Where operator.
        '''
        '''     {Preceding query operators} Where {expression}
        '''
        ''' Ex: From a In AA Where a > 0 ==> AA.Where(Function(a) a > b)
        '''
        ''' </summary>
        Private Function BindWhereClause(
            source As BoundQueryClauseBase,
            where As WhereClauseSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
            Return BindFilterQueryOperator(source, where,
                                              StringConstants.WhereMethod, where.WhereKeyword.Span,
                                              where.Condition, diagnostics)
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Skip While operator.
        '''
        '''     {Preceding query operators} Skip While {expression}
        '''
        ''' Ex: From a In AA Skip While a > 0 ==> AA.SkipWhile(Function(a) a > b)
        '''
        ''' </summary>
        Private Function BindSkipWhileClause(
            source As BoundQueryClauseBase,
            skipWhile As PartitionWhileClauseSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
            Return BindFilterQueryOperator(source, skipWhile,
                                              StringConstants.SkipWhileMethod,
                                              GetQueryOperatorNameSpan(skipWhile.SkipOrTakeKeyword, skipWhile.WhileKeyword),
                                              skipWhile.Condition, diagnostics)
        End Function
 
        Private Shared Function GetQueryOperatorNameSpan(ByRef left As SyntaxToken, ByRef right As SyntaxToken) As TextSpan
            Dim operatorNameSpan As TextSpan = left.Span
 
            If right.ValueText.Length > 0 Then
                operatorNameSpan = TextSpan.FromBounds(operatorNameSpan.Start, right.Span.End)
            End If
 
            Return operatorNameSpan
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Take While operator.
        '''
        '''     {Preceding query operators} Take While {expression}
        '''
        ''' Ex: From a In AA Skip While a > 0 ==> AA.TakeWhile(Function(a) a > b)
        '''
        ''' </summary>
        Private Function BindTakeWhileClause(
            source As BoundQueryClauseBase,
            takeWhile As PartitionWhileClauseSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
            Return BindFilterQueryOperator(source, takeWhile,
                                              StringConstants.TakeWhileMethod,
                                              GetQueryOperatorNameSpan(takeWhile.SkipOrTakeKeyword, takeWhile.WhileKeyword),
                                              takeWhile.Condition, diagnostics)
        End Function
 
        ''' <summary>
        ''' This helper method does all the work to bind Where, Take While and Skip While query operators.
        ''' </summary>
        Private Function BindFilterQueryOperator(
            source As BoundQueryClauseBase,
            operatorSyntax As QueryClauseSyntax,
            operatorName As String,
            operatorNameLocation As TextSpan,
            condition As ExpressionSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
 
            ' Create LambdaSymbol for the shape of the filter lambda.
 
            Dim param As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(source.RangeVariables), 0,
                                                                                       source.CompoundVariableType,
                                                                                       condition, source.RangeVariables)
 
            Debug.Assert(LambdaUtilities.IsLambdaBody(condition))
            Dim lambdaSymbol = Me.CreateQueryLambdaSymbol(condition,
                                                          SynthesizedLambdaKind.FilterConditionQueryLambda,
                                                          ImmutableArray.Create(param))
 
            ' Create binder for a filter condition.
            Dim filterBinder As New QueryLambdaBinder(lambdaSymbol, source.RangeVariables)
 
            ' Bind condition as a value, conversion should take care of the rest (making it an RValue, etc.).
            Dim predicate As BoundExpression = filterBinder.BindValue(condition, diagnostics)
 
            ' Need to verify result type of the condition and enforce ExprIsOperandOfConditionalBranch for possible future conversions.
            ' In order to do verification, we simply attempt conversion to boolean in the same manner as BindBooleanExpression.
            Dim conversionDiagnostic = BindingDiagnosticBag.GetInstance(withDiagnostics:=True, withDependencies:=diagnostics.AccumulatesDependencies)
 
            Dim boolSymbol As NamedTypeSymbol = GetSpecialType(SpecialType.System_Boolean, condition, diagnostics)
 
            ' If predicate has type Object we will keep result of conversion, otherwise we drop it.
            Dim predicateType As TypeSymbol = predicate.Type
            Dim keepConvertedPredicate As Boolean = False
 
            If predicateType Is Nothing Then
                If predicate.IsNothingLiteral() Then
                    keepConvertedPredicate = True
                End If
            ElseIf predicateType.IsObjectType() Then
                keepConvertedPredicate = True
            End If
 
            Dim convertedToBoolean As BoundExpression = filterBinder.ApplyImplicitConversion(condition,
                                                                                             boolSymbol, predicate,
                                                                                             conversionDiagnostic, isOperandOfConditionalBranch:=True)
 
            ' If we don't keep result of the conversion, keep diagnostic if conversion failed.
            If keepConvertedPredicate Then
                predicate = convertedToBoolean
                diagnostics.AddRange(conversionDiagnostic)
 
            ElseIf convertedToBoolean.HasErrors AndAlso conversionDiagnostic.HasAnyErrors() Then
                diagnostics.AddRange(conversionDiagnostic)
                ' Suppress any additional diagnostic, otherwise we might end up with duplicate errors.
                diagnostics = BindingDiagnosticBag.Discarded
            Else
                diagnostics.AddDependencies(conversionDiagnostic)
            End If
 
            conversionDiagnostic.Free()
 
            ' Bind the Filter
            Dim filterLambda = CreateBoundQueryLambda(lambdaSymbol,
                                                      source.RangeVariables,
                                                      predicate,
                                                      exprIsOperandOfConditionalBranch:=True)
 
            filterLambda.SetWasCompilerGenerated()
 
            ' Now bind the call.
            Dim boundCallOrBadExpression As BoundExpression
 
            If source.Type.IsErrorType() Then
                boundCallOrBadExpression = BadExpression(operatorSyntax, ImmutableArray.Create(Of BoundExpression)(source, filterLambda),
                                                         ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
            Else
                If diagnostics IsNot BindingDiagnosticBag.Discarded AndAlso ShouldSuppressDiagnostics(filterLambda) Then
                    ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                    diagnostics = BindingDiagnosticBag.Discarded
                End If
 
                boundCallOrBadExpression = BindQueryOperatorCall(operatorSyntax, source,
                                                                 operatorName,
                                                                 ImmutableArray.Create(Of BoundExpression)(filterLambda),
                                                                 operatorNameLocation,
                                                                 diagnostics)
            End If
 
            Return New BoundQueryClause(operatorSyntax,
                                        boundCallOrBadExpression,
                                        source.RangeVariables,
                                        source.CompoundVariableType,
                                        ImmutableArray.Create(Of Binder)(filterBinder),
                                        boundCallOrBadExpression.Type)
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Distinct operator.
        '''
        '''     {Preceding query operators} Distinct
        '''
        ''' Ex: From a In AA Distinct ==> AA.Distinct()
        '''
        ''' </summary>
        Private Function BindDistinctClause(
            source As BoundQueryClauseBase,
            distinct As DistinctClauseSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
 
            Dim boundCallOrBadExpression As BoundExpression
 
            If source.Type.IsErrorType() Then
                ' Operator BindQueryClauseCall will fail, let's not bother.
                boundCallOrBadExpression = BadExpression(distinct, source,
                                                         ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
            Else
                boundCallOrBadExpression = BindQueryOperatorCall(distinct, source,
                                                                 StringConstants.DistinctMethod,
                                                                 ImmutableArray(Of BoundExpression).Empty,
                                                                 distinct.DistinctKeyword.Span,
                                                                 diagnostics)
            End If
 
            Return New BoundQueryClause(distinct,
                                        boundCallOrBadExpression,
                                        source.RangeVariables,
                                        source.CompoundVariableType,
                                        ImmutableArray(Of Binder).Empty,
                                        boundCallOrBadExpression.Type)
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Skip operator.
        '''
        '''     {Preceding query operators} Skip {expression}
        '''
        ''' Ex: From a In AA Skip 10 ==> AA.Skip(10)
        '''
        ''' </summary>
        Private Function BindSkipClause(
            source As BoundQueryClauseBase,
            skip As PartitionClauseSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
            Return BindPartitionClause(source, skip, StringConstants.SkipMethod, diagnostics)
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Take operator.
        '''
        '''     {Preceding query operators} Take {expression}
        '''
        ''' Ex: From a In AA Take 10 ==> AA.Take(10)
        '''
        ''' </summary>
        Private Function BindTakeClause(
            source As BoundQueryClauseBase,
            take As PartitionClauseSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
            Return BindPartitionClause(source, take, StringConstants.TakeMethod, diagnostics)
        End Function
 
        ''' <summary>
        ''' This helper method does all the work to bind Take and Skip query operators.
        ''' </summary>
        Private Function BindPartitionClause(
            source As BoundQueryClauseBase,
            partition As PartitionClauseSyntax,
            operatorName As String,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
 
            ' Bind the Count expression as a value, conversion should take care of the rest (making it an RValue, etc.).
            Dim boundCount As BoundExpression = Me.BindValue(partition.Count, diagnostics)
 
            ' Now bind the call.
            Dim boundCallOrBadExpression As BoundExpression
 
            If source.Type.IsErrorType() Then
                boundCallOrBadExpression = BadExpression(partition, ImmutableArray.Create(source, boundCount),
                                                         ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
            Else
                If boundCount.HasErrors OrElse (boundCount.Type IsNot Nothing AndAlso boundCount.Type.IsErrorType()) Then
                    ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                    diagnostics = BindingDiagnosticBag.Discarded
                End If
 
                boundCallOrBadExpression = BindQueryOperatorCall(partition, source,
                                                               operatorName,
                                                               ImmutableArray.Create(boundCount),
                                                               partition.SkipOrTakeKeyword.Span,
                                                               diagnostics)
            End If
 
            Return New BoundQueryClause(partition,
                                        boundCallOrBadExpression,
                                        source.RangeVariables,
                                        source.CompoundVariableType,
                                        ImmutableArray(Of Binder).Empty,
                                        boundCallOrBadExpression.Type)
        End Function
 
        ''' <summary>
        ''' Given result of binding preceding query operators, the source, bind the following Order By operator.
        '''
        '''     {Preceding query operators} Order By {orderings}
        '''
        ''' Ex: From a In AA Order By a ==> AA.OrderBy(Function(a) a)
        '''
        ''' Ex: From a In AA Order By a.Key1, a.Key2 Descending ==> AA.OrderBy(Function(a) a.Key1).ThenByDescending(Function(a) a.Key2)
        '''
        ''' </summary>
        Private Function BindOrderByClause(
            source As BoundQueryClauseBase,
            orderBy As OrderByClauseSyntax,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryClause
            Dim suppressDiagnostics As DiagnosticBag = Nothing
            Dim callDiagnostics As BindingDiagnosticBag = diagnostics
 
            Dim lambdaParameterName As String = GetQueryLambdaParameterName(source.RangeVariables)
 
            Dim sourceOrPreviousOrdering As BoundQueryPart = source
            Dim orderByOrderings As SeparatedSyntaxList(Of OrderingSyntax) = orderBy.Orderings
 
            If orderByOrderings.Count = 0 Then
                ' Malformed tree.
                Debug.Assert(orderByOrderings.Count > 0, "Malformed syntax tree.")
                Return New BoundQueryClause(orderBy,
                                            BadExpression(orderBy, source, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated(),
                                            source.RangeVariables,
                                            source.CompoundVariableType,
                                            ImmutableArray.Create(Me),
                                            ErrorTypeSymbol.UnknownResultType,
                                            hasErrors:=True)
            End If
 
            Dim keyBinder As QueryLambdaBinder = Nothing
 
            For i = 0 To orderByOrderings.Count - 1
                Dim ordering As OrderingSyntax = orderByOrderings(i)
 
                ' Create LambdaSymbol for the shape of the key lambda.
                Dim param As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(lambdaParameterName, 0,
                                                                                           source.CompoundVariableType,
                                                                                           ordering.Expression, source.RangeVariables)
 
                Dim lambdaSymbol = Me.CreateQueryLambdaSymbol(LambdaUtilities.GetOrderingLambdaBody(ordering),
                                                              SynthesizedLambdaKind.OrderingQueryLambda,
                                                              ImmutableArray.Create(param))
 
                ' Create binder for a key expression.
                keyBinder = New QueryLambdaBinder(lambdaSymbol, source.RangeVariables)
 
                ' Bind expression as a value, conversion during overload resolution should take care of the rest (making it an RValue, etc.).
                Dim key As BoundExpression = keyBinder.BindValue(ordering.Expression, diagnostics)
 
                Dim keyLambda = CreateBoundQueryLambda(lambdaSymbol,
                                                       source.RangeVariables,
                                                       key,
                                                       exprIsOperandOfConditionalBranch:=False)
 
                keyLambda.SetWasCompilerGenerated()
 
                ' Now bind the call.
                Dim boundCallOrBadExpression As BoundExpression
 
                If sourceOrPreviousOrdering.Type.IsErrorType() Then
                    boundCallOrBadExpression = BadExpression(ordering, ImmutableArray.Create(Of BoundExpression)(sourceOrPreviousOrdering, keyLambda),
                                                             ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                Else
                    If callDiagnostics IsNot BindingDiagnosticBag.Discarded AndAlso ShouldSuppressDiagnostics(keyLambda) Then
                        ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                        callDiagnostics = BindingDiagnosticBag.Discarded
                    End If
 
                    Dim operatorName As String
                    Dim operatorNameLocation As TextSpan
 
                    If i = 0 Then
                        ' This is the first ordering.
                        If ordering.Kind = SyntaxKind.AscendingOrdering Then
                            operatorName = StringConstants.OrderByMethod
                        Else
                            Debug.Assert(ordering.Kind = SyntaxKind.DescendingOrdering)
                            operatorName = StringConstants.OrderByDescendingMethod
                        End If
 
                        operatorNameLocation = GetQueryOperatorNameSpan(orderBy.OrderKeyword, orderBy.ByKeyword)
                    Else
                        If ordering.Kind = SyntaxKind.AscendingOrdering Then
                            operatorName = StringConstants.ThenByMethod
                        Else
                            Debug.Assert(ordering.Kind = SyntaxKind.DescendingOrdering)
                            operatorName = StringConstants.ThenByDescendingMethod
                        End If
 
                        operatorNameLocation = orderByOrderings.GetSeparator(i - 1).Span
                    End If
 
                    boundCallOrBadExpression = BindQueryOperatorCall(ordering, sourceOrPreviousOrdering,
                                                                   operatorName,
                                                                   ImmutableArray.Create(Of BoundExpression)(keyLambda),
                                                                   operatorNameLocation,
                                                                   callDiagnostics)
                End If
 
                sourceOrPreviousOrdering = New BoundOrdering(ordering, boundCallOrBadExpression, boundCallOrBadExpression.Type)
            Next
 
            If suppressDiagnostics IsNot Nothing Then
                suppressDiagnostics.Free()
            End If
 
            Debug.Assert(sourceOrPreviousOrdering IsNot source)
            Debug.Assert(keyBinder IsNot Nothing)
 
            Return New BoundQueryClause(orderBy,
                                        sourceOrPreviousOrdering,
                                        source.RangeVariables,
                                        source.CompoundVariableType,
                                        ImmutableArray.Create(Of Binder)(keyBinder),
                                        sourceOrPreviousOrdering.Type)
        End Function
 
        ''' <summary>
        ''' This is a top level binder used to bind bodies of query lambdas.
        ''' It also contains a bunch of helper methods to bind bodies of a particular kind.
        ''' </summary>
        Private NotInheritable Class QueryLambdaBinder
            Inherits BlockBaseBinder(Of RangeVariableSymbol) ' BlockBaseBinder implements various lookup methods as we need.
 
            Private ReadOnly _lambdaSymbol As LambdaSymbol
            Private ReadOnly _rangeVariables As ImmutableArray(Of RangeVariableSymbol)
 
            Public Sub New(lambdaSymbol As LambdaSymbol, rangeVariables As ImmutableArray(Of RangeVariableSymbol))
                MyBase.New(lambdaSymbol.ContainingBinder)
                _lambdaSymbol = lambdaSymbol
                _rangeVariables = rangeVariables
            End Sub
 
            Friend Overrides ReadOnly Property Locals As ImmutableArray(Of RangeVariableSymbol)
                Get
                    Return _rangeVariables
                End Get
            End Property
 
            Public ReadOnly Property RangeVariables As ImmutableArray(Of RangeVariableSymbol)
                Get
                    Return _rangeVariables
                End Get
            End Property
 
            Public Overrides ReadOnly Property ContainingMember As Symbol
                Get
                    Return _lambdaSymbol
                End Get
            End Property
 
            Public Overrides ReadOnly Property AdditionalContainingMembers As ImmutableArray(Of Symbol)
                Get
                    Return ImmutableArray(Of Symbol).Empty
                End Get
            End Property
 
            Public ReadOnly Property LambdaSymbol As LambdaSymbol
                Get
                    Return _lambdaSymbol
                End Get
            End Property
 
            Public Overrides ReadOnly Property IsInQuery As Boolean
                Get
                    Return True
                End Get
            End Property
 
            ''' <summary>
            ''' Bind body of a lambda representing Select operator selector in context of this binder.
            ''' </summary>
            Public Function BindSelectClauseSelector(
                [select] As SelectClauseSyntax,
                operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
                <Out()> ByRef declaredRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                diagnostics As BindingDiagnosticBag
            ) As BoundExpression
                Debug.Assert(declaredRangeVariables.IsDefault)
 
                Dim selectVariables As SeparatedSyntaxList(Of ExpressionRangeVariableSyntax) = [select].Variables
                Dim requireRangeVariable As Boolean
 
                If selectVariables.Count = 1 Then
                    requireRangeVariable = False
 
                    ' If there is a Join or a GroupJoin ahead, this operator must
                    ' add a range variable into the scope.
                    While operatorsEnumerator.MoveNext()
                        Select Case operatorsEnumerator.Current.Kind
                            Case SyntaxKind.SimpleJoinClause, SyntaxKind.GroupJoinClause
                                requireRangeVariable = True
                                Exit While
 
                            Case SyntaxKind.SelectClause, SyntaxKind.LetClause, SyntaxKind.FromClause
                                Exit While
 
                            Case SyntaxKind.AggregateClause
                                Exit While
 
                            Case SyntaxKind.GroupByClause
                                Exit While
                        End Select
                    End While
                Else
                    requireRangeVariable = True
                End If
 
                Return BindExpressionRangeVariables(selectVariables,
                                                    requireRangeVariable,
                                                    [select],
                                                    declaredRangeVariables,
                                                    diagnostics)
            End Function
 
            ''' <summary>
            ''' Bind Select like selector based on the set of expression range variables in context of this binder.
            ''' </summary>
            Public Function BindExpressionRangeVariables(
                selectVariables As SeparatedSyntaxList(Of ExpressionRangeVariableSyntax),
                requireRangeVariable As Boolean,
                selectorSyntax As QueryClauseSyntax,
                <Out()> ByRef declaredRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                diagnostics As BindingDiagnosticBag
            ) As BoundExpression
                Debug.Assert(selectorSyntax IsNot Nothing AndAlso declaredRangeVariables.IsDefault)
 
                Dim selector As BoundExpression
 
                If selectVariables.Count = 1 Then
                    ' Simple case, one item in the projection.
                    Dim item As ExpressionRangeVariableSyntax = selectVariables(0)
                    selector = Nothing ' Suppress definite assignment error.
 
                    Debug.Assert(item.NameEquals Is Nothing OrElse item.NameEquals.AsClause Is Nothing)
 
                    ' Using enclosing binder for shadowing check, because range variables brought in scope by the current binder
                    ' are no longer in scope after the Select. So, it is fine to shadow them.
                    Dim rangeVar As RangeVariableSymbol = Me.BindExpressionRangeVariable(item, requireRangeVariable, Me.ContainingBinder, Nothing, selector, diagnostics)
 
                    If rangeVar IsNot Nothing Then
                        declaredRangeVariables = ImmutableArray.Create(Of RangeVariableSymbol)(rangeVar)
                    Else
                        Debug.Assert(Not requireRangeVariable)
                        declaredRangeVariables = ImmutableArray(Of RangeVariableSymbol).Empty
                    End If
 
                ElseIf selectVariables.Count = 0 Then
                    ' Malformed tree
                    Debug.Assert(selectVariables.Count > 0, "Malformed syntax tree.")
                    selector = BadExpression(selectorSyntax, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                    declaredRangeVariables = ImmutableArray.Create(RangeVariableSymbol.CreateForErrorRecovery(Me, selectorSyntax, selector.Type))
                Else
                    ' Need to build an Anonymous Type
                    Dim selectors = New BoundExpression(selectVariables.Count - 1) {}
                    Dim declaredNames As HashSet(Of String) = CreateSetOfDeclaredNames()
                    Dim rangeVariables(selectVariables.Count - 1) As RangeVariableSymbol
 
                    For i As Integer = 0 To selectVariables.Count - 1
                        Debug.Assert(selectVariables(i).NameEquals Is Nothing OrElse selectVariables(i).NameEquals.AsClause Is Nothing)
 
                        ' Using enclosing binder for shadowing check, because range variables brought in scope by the current binder
                        ' are no longer in scope after the Select. So, it is fine to shadow them.
                        Dim rangeVar As RangeVariableSymbol = Me.BindExpressionRangeVariable(selectVariables(i), True, Me.ContainingBinder, declaredNames, selectors(i), diagnostics)
                        Debug.Assert(rangeVar IsNot Nothing)
                        rangeVariables(i) = rangeVar
                    Next
 
                    AssertDeclaredNames(declaredNames, rangeVariables.AsImmutableOrNull())
 
                    Dim fields = New AnonymousTypeField(selectVariables.Count - 1) {}
 
                    For i As Integer = 0 To rangeVariables.Length - 1
                        Dim rangeVar As RangeVariableSymbol = rangeVariables(i)
                        fields(i) = New AnonymousTypeField(
                            rangeVar.Name, rangeVar.Type, rangeVar.Syntax.GetLocation(), isKeyOrByRef:=True)
                    Next
 
                    selector = BindAnonymousObjectCreationExpression(selectorSyntax,
                                                                     New AnonymousTypeDescriptor(fields.AsImmutableOrNull(),
                                                                                                 selectorSyntax.QueryClauseKeywordOrRangeVariableIdentifier.GetLocation(),
                                                                                                 True),
                                                                     selectors.AsImmutableOrNull(),
                                                                     diagnostics).MakeCompilerGenerated()
 
                    declaredRangeVariables = rangeVariables.AsImmutableOrNull()
                End If
 
                Debug.Assert(Not declaredRangeVariables.IsDefault AndAlso selectorSyntax IsNot Nothing)
                Return selector
            End Function
 
            ''' <summary>
            ''' Bind ExpressionRangeVariableSyntax in context of this binder.
            ''' </summary>
            Private Function BindExpressionRangeVariable(
                item As ExpressionRangeVariableSyntax,
                requireRangeVariable As Boolean,
                shadowingCheckBinder As Binder,
                declaredNames As HashSet(Of String),
                <Out()> ByRef selector As BoundExpression,
                diagnostics As BindingDiagnosticBag
            ) As RangeVariableSymbol
                Debug.Assert(selector Is Nothing)
 
                Dim variableName As VariableNameEqualsSyntax = item.NameEquals
 
                ' Figure out the name of the new range variable
                Dim rangeVarName As String = Nothing
                Dim rangeVarNameSyntax As SyntaxToken = Nothing
                Dim targetVariableType As TypeSymbol = Nothing
 
                If variableName IsNot Nothing Then
                    rangeVarNameSyntax = variableName.Identifier.Identifier
                    rangeVarName = rangeVarNameSyntax.ValueText
 
                    ' Deal with AsClauseOpt and various modifiers.
                    If variableName.AsClause IsNot Nothing Then
                        targetVariableType = DecodeModifiedIdentifierType(variableName.Identifier,
                                                                          variableName.AsClause,
                                                                          Nothing,
                                                                          Nothing,
                                                                          diagnostics,
                                                                          ModifiedIdentifierTypeDecoderContext.LocalType Or
                                                                          ModifiedIdentifierTypeDecoderContext.QueryRangeVariableType)
 
                    ElseIf variableName.Identifier.Nullable.Node IsNot Nothing Then
                        ReportDiagnostic(diagnostics, variableName.Identifier.Nullable, ERRID.ERR_NullableTypeInferenceNotSupported)
                    End If
                Else
                    ' Infer the name from expression
                    Dim failedToInferFromXmlName As XmlNameSyntax = Nothing
                    Dim nameToken As SyntaxToken = item.Expression.ExtractAnonymousTypeMemberName(failedToInferFromXmlName)
 
                    If nameToken.Kind <> SyntaxKind.None Then
                        rangeVarNameSyntax = nameToken
                        rangeVarName = rangeVarNameSyntax.ValueText
                    ElseIf requireRangeVariable Then
                        If failedToInferFromXmlName IsNot Nothing Then
                            ReportDiagnostic(diagnostics, failedToInferFromXmlName.LocalName, ERRID.ERR_QueryAnonTypeFieldXMLNameInference)
                        Else
                            ReportDiagnostic(diagnostics, item.Expression, ERRID.ERR_QueryAnonymousTypeFieldNameInference)
                        End If
                    End If
                End If
 
                If rangeVarName IsNot Nothing AndAlso rangeVarName.Length = 0 Then
                    ' Empty string must have been a syntax error.
                    rangeVarName = Nothing
                End If
 
                selector = BindValue(item.Expression, diagnostics)
 
                If targetVariableType Is Nothing Then
                    selector = MakeRValue(selector, diagnostics)
                Else
                    selector = ApplyImplicitConversion(item.Expression, targetVariableType, selector, diagnostics)
                End If
 
                Dim rangeVar As RangeVariableSymbol = Nothing
 
                If rangeVarName IsNot Nothing Then
 
                    rangeVar = RangeVariableSymbol.Create(Me, rangeVarNameSyntax, selector.Type)
 
                    ' Note what we are doing here:
                    ' We are creating BoundRangeVariableAssignment before doing any shadowing checks
                    ' so that SemanticModel can find the declared symbol, but, if the variable will conflict with another
                    ' variable in the same child scope, we will not add it to the scope. Instead, we create special
                    ' error recovery range variable symbol and add it to the scope at the same place, making sure
                    ' that the earlier declared range variable wins during name lookup.
                    ' As an alternative, we could still add the original range variable symbol to the scope and then,
                    ' while we are binding the rest of the query in error recovery mode, references to the name would
                    ' cause ambiguity. However, this could negatively affect IDE experience. Also, as we build an
                    ' Anonymous Type for the compound range variables, we would end up with a type with duplicate members,
                    ' which could cause problems elsewhere.
 
                    selector = New BoundRangeVariableAssignment(item, rangeVar, selector, selector.Type)
 
                    Dim doErrorRecovery As Boolean = False
 
                    If declaredNames IsNot Nothing AndAlso Not declaredNames.Add(rangeVarName) Then
                        Debug.Assert(item.Parent.Kind = SyntaxKind.SelectClause OrElse item.Parent.Kind = SyntaxKind.GroupByClause)
                        ReportDiagnostic(diagnostics, rangeVarNameSyntax, ERRID.ERR_QueryDuplicateAnonTypeMemberName1, rangeVarName)
                        doErrorRecovery = True  ' Shouldn't add to the scope.
                    Else
                        shadowingCheckBinder.VerifyRangeVariableName(rangeVar, rangeVarNameSyntax, diagnostics)
 
                        If item.Parent.Kind = SyntaxKind.LetClause AndAlso ShadowsRangeVariableInTheChildScope(shadowingCheckBinder, rangeVar) Then
                            ' We are about to add this range variable to the current child scope.
                            ' Shadowing error was reported earlier.
                            doErrorRecovery = True  ' Shouldn't add to the scope.
                        End If
                    End If
 
                    If doErrorRecovery Then
                        If requireRangeVariable Then
                            rangeVar = RangeVariableSymbol.CreateForErrorRecovery(Me, rangeVar.Syntax, selector.Type)
                        Else
                            rangeVar = Nothing
                        End If
                    End If
 
                ElseIf requireRangeVariable Then
                    Debug.Assert(rangeVar Is Nothing)
                    rangeVar = RangeVariableSymbol.CreateForErrorRecovery(Me, item, selector.Type)
                End If
 
                Debug.Assert(selector IsNot Nothing)
                Debug.Assert(Not requireRangeVariable OrElse rangeVar IsNot Nothing)
                Return rangeVar
            End Function
 
            ''' <summary>
            ''' Bind Let operator selector for a particular ExpressionRangeVariableSyntax.
            ''' Takes care of "carrying over" of previously declared range variables as well as introduction of the new one.
            ''' </summary>
            Public Function BindLetClauseVariableSelector(
                variable As ExpressionRangeVariableSyntax,
                operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
                <Out()> ByRef declaredRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                diagnostics As BindingDiagnosticBag
            ) As BoundExpression
                Debug.Assert(declaredRangeVariables.IsDefault)
 
                Dim selector As BoundExpression = Nothing
 
                ' Using selector binder for shadowing checks because all its range variables stay in scope
                ' after the operator.
                Dim rangeVar As RangeVariableSymbol = Me.BindExpressionRangeVariable(variable, True, Me, Nothing, selector, diagnostics)
                Debug.Assert(rangeVar IsNot Nothing)
 
                declaredRangeVariables = ImmutableArray.Create(rangeVar)
 
                If _rangeVariables.Length > 0 Then
                    ' Need to build an Anonymous Type.
 
                    ' If it is not the last variable in the list, we simply combine source's
                    ' compound variable (an instance of its Anonymous Type) with our new variable,
                    ' creating new compound variable of nested Anonymous Type.
 
                    ' In some scenarios, it is safe to leave compound variable in nested form when there is an
                    ' operator down the road that does its own projection (Select, Group By, ...).
                    ' All following operators have to take an Anonymous Type in both cases and, since there is no way to
                    ' restrict the shape of the Anonymous Type in method's declaration, the operators should be
                    ' insensitive to the shape of the Anonymous Type.
                    selector = BuildJoinSelector(variable,
                                                 (variable Is DirectCast(operatorsEnumerator.Current, LetClauseSyntax).Variables.Last() AndAlso
                                                      MustProduceFlatCompoundVariable(operatorsEnumerator)),
                                                 diagnostics,
                                                 rangeVar, selector)
 
                Else
                    ' Easy case, no need to build an Anonymous Type.
                    Debug.Assert(_rangeVariables.Length = 0)
                End If
 
                Debug.Assert(Not declaredRangeVariables.IsDefault)
                Return selector
            End Function
 
            ''' <summary>
            ''' Bind body of a lambda representing first Select operator selector for an aggregate clause in context of this binder.
            ''' </summary>
            Public Function BindAggregateClauseFirstSelector(
                aggregate As AggregateClauseSyntax,
                operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
                rangeVariablesPart1 As ImmutableArray(Of RangeVariableSymbol),
                rangeVariablesPart2 As ImmutableArray(Of RangeVariableSymbol),
                <Out()> ByRef declaredRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                <Out()> ByRef group As BoundQueryClauseBase,
                <Out()> ByRef intoBinder As IntoClauseDisallowGroupReferenceBinder,
                diagnostics As BindingDiagnosticBag
            ) As BoundExpression
                Debug.Assert(operatorsEnumerator.Current Is aggregate)
                Debug.Assert(declaredRangeVariables.IsDefault)
                Debug.Assert((rangeVariablesPart1.Length = 0) = (rangeVariablesPart2 = _rangeVariables))
                Debug.Assert((rangeVariablesPart2.Length = 0) = (rangeVariablesPart1 = _rangeVariables))
                Debug.Assert(_lambdaSymbol.ParameterCount = If(rangeVariablesPart2.Length = 0, 1, 2))
                Debug.Assert(_rangeVariables.Length = rangeVariablesPart1.Length + rangeVariablesPart2.Length)
 
                ' Let's interpret our group.
                Dim groupAdditionalOperators As SyntaxList(Of QueryClauseSyntax).Enumerator = aggregate.AdditionalQueryOperators.GetEnumerator()
 
                ' Note, this call can advance [groupAdditionalOperators] enumerator if it absorbs the following Let or Select.
                group = BindCollectionRangeVariables(aggregate, Nothing, aggregate.Variables, groupAdditionalOperators, diagnostics)
                group = BindSubsequentQueryOperators(group, groupAdditionalOperators, diagnostics)
 
                ' Now, deal with aggregate functions.
                Dim aggregationVariables As SeparatedSyntaxList(Of AggregationRangeVariableSyntax) = aggregate.AggregationVariables
                Dim letSelector As BoundExpression
                Dim groupRangeVar As RangeVariableSymbol = Nothing
 
                Select Case aggregationVariables.Count
                    Case 0
                        ' Malformed syntax tree.
                        Debug.Assert(aggregationVariables.Count > 0, "Malformed syntax tree.")
                        declaredRangeVariables = ImmutableArray.Create(Of RangeVariableSymbol)(
                                       RangeVariableSymbol.CreateForErrorRecovery(Me,
                                                                                  aggregate,
                                                                                  ErrorTypeSymbol.UnknownResultType))
 
                        letSelector = BadExpression(aggregate, group, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
 
                        intoBinder = New IntoClauseDisallowGroupReferenceBinder(New QueryLambdaBinder(_lambdaSymbol,
                                                                                                      ImmutableArray(Of RangeVariableSymbol).Empty),
                                                                                group, group.RangeVariables, group.CompoundVariableType,
                                                                                _rangeVariables.Concat(group.RangeVariables))
 
                    Case 1
                        ' Simple case - one aggregate function.
                        '
                        ' FROM a in AA              FROM a in AA
                        ' AGGREGATE b in a.BB  =>   LET count = (FROM b IN a.BB).Count()
                        ' INTO Count()
                        '
                        intoBinder = New IntoClauseDisallowGroupReferenceBinder(New QueryLambdaBinder(_lambdaSymbol,
                                                                                                      ImmutableArray(Of RangeVariableSymbol).Empty),
                                                                                group, group.RangeVariables, group.CompoundVariableType,
                                                                                _rangeVariables.Concat(group.RangeVariables))
 
                        Dim compoundKeyReferencePart1 As BoundExpression
                        Dim keysRangeVariablesPart1 As ImmutableArray(Of RangeVariableSymbol)
                        Dim compoundKeyReferencePart2 As BoundExpression
                        Dim keysRangeVariablesPart2 As ImmutableArray(Of RangeVariableSymbol)
                        Dim letSelectorParam As BoundLambdaParameterSymbol
 
                        If rangeVariablesPart1.Length > 0 Then
                            letSelectorParam = DirectCast(_lambdaSymbol.Parameters(0), BoundLambdaParameterSymbol)
                            compoundKeyReferencePart1 = New BoundParameter(letSelectorParam.Syntax, letSelectorParam, False, letSelectorParam.Type).MakeCompilerGenerated()
                            keysRangeVariablesPart1 = rangeVariablesPart1
 
                            If rangeVariablesPart2.Length > 0 Then
                                letSelectorParam = DirectCast(_lambdaSymbol.Parameters(1), BoundLambdaParameterSymbol)
                                compoundKeyReferencePart2 = New BoundParameter(letSelectorParam.Syntax, letSelectorParam, False, letSelectorParam.Type).MakeCompilerGenerated()
                                keysRangeVariablesPart2 = rangeVariablesPart2
                            Else
                                compoundKeyReferencePart2 = Nothing
                                keysRangeVariablesPart2 = ImmutableArray(Of RangeVariableSymbol).Empty
                            End If
                        ElseIf rangeVariablesPart2.Length > 0 Then
                            letSelectorParam = DirectCast(_lambdaSymbol.Parameters(1), BoundLambdaParameterSymbol)
                            compoundKeyReferencePart1 = New BoundParameter(letSelectorParam.Syntax, letSelectorParam, False, letSelectorParam.Type).MakeCompilerGenerated()
                            keysRangeVariablesPart1 = rangeVariablesPart2
                            compoundKeyReferencePart2 = Nothing
                            keysRangeVariablesPart2 = ImmutableArray(Of RangeVariableSymbol).Empty
                        Else
                            compoundKeyReferencePart1 = Nothing
                            keysRangeVariablesPart1 = ImmutableArray(Of RangeVariableSymbol).Empty
                            compoundKeyReferencePart2 = Nothing
                            keysRangeVariablesPart2 = ImmutableArray(Of RangeVariableSymbol).Empty
                        End If
 
                        letSelector = intoBinder.BindIntoSelector(aggregate,
                                                                  _rangeVariables,
                                                                  compoundKeyReferencePart1,
                                                                  keysRangeVariablesPart1,
                                                                  compoundKeyReferencePart2,
                                                                  keysRangeVariablesPart2,
                                                                  Nothing,
                                                                  aggregationVariables,
                                                                  MustProduceFlatCompoundVariable(operatorsEnumerator),
                                                                  declaredRangeVariables,
                                                                  diagnostics)
 
                    Case Else
 
                        ' Complex case:
                        '
                        ' FROM a in AA              FROM a in AA
                        ' AGGREGATE b in a.BB  =>   LET Group = (FROM b IN a.BB)
                        ' INTO Count(),             Select a, Count=Group.Count(), Sum=Group.Sum(b=>b)
                        '      Sum(b)
                        '
 
                        ' Handle selector for the Let.
                        groupRangeVar = RangeVariableSymbol.CreateCompilerGenerated(Me, aggregate, GeneratedNameConstants.Group, group.Type)
 
                        If _rangeVariables.Length = 0 Then
                            letSelector = group
                        Else
                            letSelector = BuildJoinSelector(aggregate,
                                                            mustProduceFlatCompoundVariable:=False, diagnostics:=diagnostics,
                                                            rangeVarOpt:=groupRangeVar, rangeVarValueOpt:=group)
                        End If
 
                        declaredRangeVariables = ImmutableArray.Create(Of RangeVariableSymbol)(groupRangeVar)
                        intoBinder = Nothing
                End Select
 
                Return letSelector
            End Function
 
            ''' <summary>
            ''' Bind Join/From selector that absorbs following Select/Let in context of this binder.
            ''' </summary>
            Public Function BindAbsorbingJoinSelector(
                absorbNextOperator As QueryClauseSyntax,
                operatorsEnumerator As SyntaxList(Of QueryClauseSyntax).Enumerator,
                leftRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                rightRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                <Out> ByRef joinSelectorDeclaredRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                <Out> ByRef group As BoundQueryClauseBase,
                <Out> ByRef intoBinder As IntoClauseDisallowGroupReferenceBinder,
                diagnostics As BindingDiagnosticBag
            ) As BoundExpression
                Debug.Assert(joinSelectorDeclaredRangeVariables.IsDefault)
                Debug.Assert(absorbNextOperator Is operatorsEnumerator.Current)
 
                group = Nothing
                intoBinder = Nothing
 
                Dim joinSelector As BoundExpression
 
                Select Case absorbNextOperator.Kind
                    Case SyntaxKind.SelectClause
                        ' Absorb Select
                        Dim [select] = DirectCast(absorbNextOperator, SelectClauseSyntax)
                        joinSelector = BindSelectClauseSelector([select],
                                                                operatorsEnumerator,
                                                                joinSelectorDeclaredRangeVariables,
                                                                diagnostics)
 
                    Case SyntaxKind.LetClause
                        ' Absorb Let
                        Dim [let] = DirectCast(absorbNextOperator, LetClauseSyntax)
                        Debug.Assert([let].Variables.Count > 0)
 
                        joinSelector = BindLetClauseVariableSelector([let].Variables.First,
                                                                     operatorsEnumerator,
                                                                     joinSelectorDeclaredRangeVariables,
                                                                     diagnostics)
 
                    Case SyntaxKind.AggregateClause
                        ' Absorb Aggregate
                        Dim aggregate = DirectCast(absorbNextOperator, AggregateClauseSyntax)
                        joinSelector = BindAggregateClauseFirstSelector(aggregate,
                                                                        operatorsEnumerator,
                                                                        leftRangeVariables,
                                                                        rightRangeVariables,
                                                                        joinSelectorDeclaredRangeVariables,
                                                                        group,
                                                                        intoBinder,
                                                                        diagnostics)
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(absorbNextOperator.Kind)
 
                End Select
 
                Return joinSelector
            End Function
 
            ''' <summary>
            ''' Bind Join/Let like and mixed selector in context of this binder.
            '''
            ''' Join like selector: Function(a, b) New With {a, b}
            '''
            ''' Let like selector: Function(a) New With {a, letExpressionRangeVariable}
            '''
            ''' Mixed selector: Function(a, b) New With {a, b, letExpressionRangeVariable}
            ''' </summary>
            Public Function BuildJoinSelector(
                syntax As VisualBasicSyntaxNode,
                mustProduceFlatCompoundVariable As Boolean,
                diagnostics As BindingDiagnosticBag,
                Optional rangeVarOpt As RangeVariableSymbol = Nothing,
                Optional rangeVarValueOpt As BoundExpression = Nothing
            ) As BoundExpression
                Debug.Assert(_rangeVariables.Length > 0)
                Debug.Assert((rangeVarOpt Is Nothing) = (rangeVarValueOpt Is Nothing))
                Debug.Assert(rangeVarOpt IsNot Nothing OrElse _lambdaSymbol.ParameterCount > 1)
 
                Dim selectors As BoundExpression()
                Dim fields As AnonymousTypeField()
 
                ' In some scenarios, it is safe to leave compound variable in nested form when there is an
                ' operator down the road that does its own projection (Select, Group By, ...).
                ' All following operators have to take an Anonymous Type in both cases and, since there is no way to
                ' restrict the shape of the Anonymous Type in method's declaration, the operators should be
                ' insensitive to the shape of the Anonymous Type.
                Dim lastIndex As Integer
                Dim sizeIncrease As Integer = If(rangeVarOpt Is Nothing, 0, 1)
 
                Debug.Assert(sizeIncrease + _rangeVariables.Length > 1)
 
                ' Note, the special case for [sourceRangeVariables.Count = 1] helps in the
                ' following scenario:
                '     From x In Xs Select x+1 From y In Ys Let z= Zz, ...
                ' Selector lambda should be:
                '     Function(unnamed, y) New With {y, .z=Zz}
                ' The lambda has two parameters, but we have only one range variable that should be carried over.
                ' If we were simply copying lambda's parameters to the Anonymous Type instance, we would
                ' copy data that aren't needed (value of the first parameter should be dropped).
                If _rangeVariables.Length = 1 OrElse mustProduceFlatCompoundVariable Then
                    ' Need to flatten
                    lastIndex = _rangeVariables.Length + sizeIncrease - 1
                    selectors = New BoundExpression(lastIndex) {}
                    fields = New AnonymousTypeField(lastIndex) {}
 
                    For j As Integer = 0 To _rangeVariables.Length - 1
                        Dim leftVar As RangeVariableSymbol = _rangeVariables(j)
                        selectors(j) = New BoundRangeVariable(leftVar.Syntax, leftVar, leftVar.Type).MakeCompilerGenerated()
                        fields(j) = New AnonymousTypeField(leftVar.Name, leftVar.Type, leftVar.Syntax.GetLocation(), isKeyOrByRef:=True)
                    Next
                Else
                    ' Nesting ...
                    Debug.Assert(_rangeVariables.Length > 1)
 
                    Dim parameters As ImmutableArray(Of BoundLambdaParameterSymbol) = _lambdaSymbol.Parameters.As(Of BoundLambdaParameterSymbol)
 
                    lastIndex = parameters.Length + sizeIncrease - 1
                    selectors = New BoundExpression(lastIndex) {}
                    fields = New AnonymousTypeField(lastIndex) {}
 
                    For j As Integer = 0 To parameters.Length - 1
                        Dim param As BoundLambdaParameterSymbol = parameters(j)
                        selectors(j) = New BoundParameter(param.Syntax, param, False, param.Type).MakeCompilerGenerated()
                        fields(j) = New AnonymousTypeField(param.Name, param.Type, param.Syntax.GetLocation(), isKeyOrByRef:=True)
                    Next
                End If
 
                If rangeVarOpt IsNot Nothing Then
                    selectors(lastIndex) = rangeVarValueOpt
                    fields(lastIndex) = New AnonymousTypeField(
                        rangeVarOpt.Name, rangeVarOpt.Type, rangeVarOpt.Syntax.GetLocation(), isKeyOrByRef:=True)
                End If
 
                Return BindAnonymousObjectCreationExpression(syntax,
                                                             New AnonymousTypeDescriptor(fields.AsImmutableOrNull(),
                                                                                         syntax.QueryClauseKeywordOrRangeVariableIdentifier.GetLocation(),
                                                                                         True),
                                                             selectors.AsImmutableOrNull(),
                                                             diagnostics).MakeCompilerGenerated()
            End Function
 
            ''' <summary>
            ''' Bind key selectors for a Join/Group Join operator.
            ''' </summary>
            Public Shared Sub BindJoinKeys(
                parentBinder As Binder,
                join As JoinClauseSyntax,
                outer As BoundQueryClauseBase,
                inner As BoundQueryClauseBase,
                joinSelectorRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                <Out()> ByRef outerKeyLambda As BoundQueryLambda,
                <Out()> ByRef outerKeyBinder As QueryLambdaBinder,
                <Out()> ByRef innerKeyLambda As BoundQueryLambda,
                <Out()> ByRef innerKeyBinder As QueryLambdaBinder,
                diagnostics As BindingDiagnosticBag
            )
                Debug.Assert(outerKeyLambda Is Nothing)
                Debug.Assert(outerKeyBinder Is Nothing)
                Debug.Assert(innerKeyLambda Is Nothing)
                Debug.Assert(innerKeyBinder Is Nothing)
                Debug.Assert(joinSelectorRangeVariables.SequenceEqual(outer.RangeVariables.Concat(inner.RangeVariables)))
 
                Dim outerKeyParam As BoundLambdaParameterSymbol = parentBinder.CreateQueryLambdaParameterSymbol(
                                                                                                   GetQueryLambdaParameterName(outer.RangeVariables), 0,
                                                                                                   outer.CompoundVariableType,
                                                                                                   join, outer.RangeVariables)
 
                Dim outerKeyLambdaSymbol = parentBinder.CreateQueryLambdaSymbol(LambdaUtilities.GetJoinLeftLambdaBody(join),
                                                                                SynthesizedLambdaKind.JoinLeftQueryLambda,
                                                                                ImmutableArray.Create(outerKeyParam))
                outerKeyBinder = New QueryLambdaBinder(outerKeyLambdaSymbol, joinSelectorRangeVariables)
 
                Dim innerKeyParam As BoundLambdaParameterSymbol = parentBinder.CreateQueryLambdaParameterSymbol(
                                                                                                   GetQueryLambdaParameterName(inner.RangeVariables), 0,
                                                                                                   inner.CompoundVariableType,
                                                                                                   join, inner.RangeVariables)
 
                Dim innerKeyLambdaSymbol = parentBinder.CreateQueryLambdaSymbol(LambdaUtilities.GetJoinRightLambdaBody(join),
                                                                                SynthesizedLambdaKind.JoinRightQueryLambda,
                                                                                ImmutableArray.Create(innerKeyParam))
 
                innerKeyBinder = New QueryLambdaBinder(innerKeyLambdaSymbol, joinSelectorRangeVariables)
 
                Dim sideDeterminator As New JoinConditionSideDeterminationVisitor(outer.RangeVariables, inner.RangeVariables)
 
                Dim joinConditions As SeparatedSyntaxList(Of JoinConditionSyntax) = join.JoinConditions
                Dim outerKey As BoundExpression
                Dim innerKey As BoundExpression
                Dim keysAreGood As Boolean
 
                If joinConditions.Count = 0 Then
                    ' Malformed tree.
                    Debug.Assert(joinConditions.Count > 0, "Malformed syntax tree.")
                    outerKey = BadExpression(join, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                    innerKey = BadExpression(join, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                    keysAreGood = False
 
                ElseIf joinConditions.Count = 1 Then
                    ' Simple case, no need to build an Anonymous Type.
                    outerKey = Nothing
                    innerKey = Nothing
                    keysAreGood = BindJoinCondition(joinConditions(0),
                                                    outerKeyBinder,
                                                    outer.RangeVariables,
                                                    outerKey,
                                                    innerKeyBinder,
                                                    inner.RangeVariables,
                                                    innerKey,
                                                    sideDeterminator,
                                                    diagnostics)
 
                    If Not keysAreGood Then
                        If outerKey.Type IsNot ErrorTypeSymbol.UnknownResultType Then
                            outerKey = BadExpression(outerKey.Syntax, outerKey, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                        End If
                        If innerKey.Type IsNot ErrorTypeSymbol.UnknownResultType Then
                            innerKey = BadExpression(innerKey.Syntax, innerKey, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                        End If
                    End If
                Else
                    ' Need to build a compound key as an instance of an Anonymous Type.
                    Dim outerKeys(joinConditions.Count - 1) As BoundExpression
                    Dim innerKeys(joinConditions.Count - 1) As BoundExpression
                    keysAreGood = True
 
                    For i As Integer = 0 To joinConditions.Count - 1
                        If Not BindJoinCondition(joinConditions(i),
                                                 outerKeyBinder,
                                                 outer.RangeVariables,
                                                 outerKeys(i),
                                                 innerKeyBinder,
                                                 inner.RangeVariables,
                                                 innerKeys(i),
                                                 sideDeterminator,
                                                 diagnostics) Then
                            keysAreGood = False
                        End If
                    Next
 
                    If keysAreGood Then
                        Dim fields(joinConditions.Count - 1) As AnonymousTypeField
 
                        For i As Integer = 0 To joinConditions.Count - 1
                            fields(i) = New AnonymousTypeField(
                                "Key" & i.ToString(), outerKeys(i).Type, joinConditions(i).GetLocation(), isKeyOrByRef:=True)
                        Next
 
                        Dim descriptor As New AnonymousTypeDescriptor(fields.AsImmutableOrNull(),
                                                                      join.OnKeyword.GetLocation(),
                                                                      True)
 
                        outerKey = outerKeyBinder.BindAnonymousObjectCreationExpression(join, descriptor, outerKeys.AsImmutableOrNull(),
                                                                                        diagnostics)
                        innerKey = innerKeyBinder.BindAnonymousObjectCreationExpression(join, descriptor, innerKeys.AsImmutableOrNull(),
                                                                                        diagnostics)
                    Else
                        outerKey = BadExpression(join, outerKeys.AsImmutableOrNull(), ErrorTypeSymbol.UnknownResultType)
                        innerKey = BadExpression(join, innerKeys.AsImmutableOrNull(), ErrorTypeSymbol.UnknownResultType)
                    End If
 
                    outerKey.MakeCompilerGenerated()
                    innerKey.MakeCompilerGenerated()
                End If
 
                outerKeyLambda = CreateBoundQueryLambda(outerKeyLambdaSymbol,
                                                        outer.RangeVariables,
                                                        outerKey,
                                                        exprIsOperandOfConditionalBranch:=False)
 
                outerKeyLambda.SetWasCompilerGenerated()
 
                innerKeyLambda = CreateBoundQueryLambda(innerKeyLambdaSymbol,
                                                        inner.RangeVariables,
                                                        innerKey,
                                                        exprIsOperandOfConditionalBranch:=False)
 
                innerKeyLambda.SetWasCompilerGenerated()
 
                Debug.Assert(outerKeyLambda IsNot Nothing)
                Debug.Assert(innerKeyLambda IsNot Nothing)
            End Sub
 
            Private Shared Function BindJoinCondition(
                joinCondition As JoinConditionSyntax,
                outerKeyBinder As QueryLambdaBinder,
                outerRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                <Out()> ByRef outerKey As BoundExpression,
                innerKeyBinder As QueryLambdaBinder,
                innerRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                <Out()> ByRef innerKey As BoundExpression,
                sideDeterminator As JoinConditionSideDeterminationVisitor,
                diagnostics As BindingDiagnosticBag
            ) As Boolean
                Debug.Assert(outerKey Is Nothing AndAlso innerKey Is Nothing)
 
                ' For a JoinConditionSyntax (<left expr> Equals <right expr>), we are going to bind
                ' left side with outerKeyBinder, which has all range variables from outer and from inner
                ' in scope. Then, we try to figure out to which join side the left key actually belongs.
                ' If it turns out that it belongs to the inner side, we will bind the left again,
                ' but now using the innerKeyBinder. The reason why we need to rebind expressions is that
                ' innerKeyBinder, outerKeyBinder are each associated with different lambda symbols and
                ' the binding process might capture the lambda in the tree or in symbols referenced by
                ' the tree, so we can't safely move bound nodes between lambdas. Usually expressions are
                ' small and very simple, so rebinding them shouldn't create a noticeable performance impact.
                ' If that will not turn out to be the case, we might try to optimize by tracking if someone
                ' requested ContainingMember from outerKeyBinder while an expression is being bound and
                ' rebind only in that case.
                ' Similarly, in some scenarios we bind right key with innerKeyBinder first and then rebind
                ' it with outerKeyBinder.
 
                Dim keysAreGood As Boolean
 
                Dim left As BoundExpression = outerKeyBinder.BindRValue(joinCondition.Left, diagnostics)
 
                Dim leftSide As JoinConditionSideDeterminationVisitor.Result
                leftSide = sideDeterminator.DetermineTheSide(left, diagnostics)
 
                If leftSide = JoinConditionSideDeterminationVisitor.Result.Outer Then
                    outerKey = left
                    innerKey = innerKeyBinder.BindRValue(joinCondition.Right, diagnostics)
                    keysAreGood = innerKeyBinder.VerifyJoinKeys(outerKey, outerRangeVariables, leftSide,
                                                                innerKey, innerRangeVariables, sideDeterminator.DetermineTheSide(innerKey, diagnostics),
                                                                diagnostics)
                ElseIf leftSide = JoinConditionSideDeterminationVisitor.Result.Inner Then
                    ' Rebind with the inner binder.
                    innerKey = innerKeyBinder.BindRValue(joinCondition.Left, BindingDiagnosticBag.Discarded)
                    Debug.Assert(leftSide = sideDeterminator.DetermineTheSide(innerKey, diagnostics))
                    outerKey = outerKeyBinder.BindRValue(joinCondition.Right, diagnostics)
                    keysAreGood = innerKeyBinder.VerifyJoinKeys(outerKey, outerRangeVariables, sideDeterminator.DetermineTheSide(outerKey, diagnostics),
                                                                innerKey, innerRangeVariables, leftSide,
                                                                diagnostics)
                Else
                    Dim right As BoundExpression = innerKeyBinder.BindRValue(joinCondition.Right, diagnostics)
 
                    Dim rightSide As JoinConditionSideDeterminationVisitor.Result
                    rightSide = sideDeterminator.DetermineTheSide(right, diagnostics)
 
                    If rightSide = JoinConditionSideDeterminationVisitor.Result.Outer Then
                        ' Rebind with the outer binder.
                        outerKey = outerKeyBinder.BindRValue(joinCondition.Right, BindingDiagnosticBag.Discarded)
                        innerKey = innerKeyBinder.BindRValue(joinCondition.Left, BindingDiagnosticBag.Discarded)
                        keysAreGood = innerKeyBinder.VerifyJoinKeys(outerKey, outerRangeVariables, rightSide,
                                                                    innerKey, innerRangeVariables, leftSide,
                                                                    diagnostics)
                    Else
                        outerKey = left
                        innerKey = right
 
                        keysAreGood = innerKeyBinder.VerifyJoinKeys(outerKey, outerRangeVariables, leftSide,
                                                                    innerKey, innerRangeVariables, rightSide,
                                                                    diagnostics)
                    End If
 
                    Debug.Assert(Not keysAreGood)
                End If
 
                If keysAreGood Then
                    keysAreGood = Not (outerKey.Type.IsErrorType() OrElse innerKey.Type.IsErrorType())
                End If
 
                If keysAreGood AndAlso Not outerKey.Type.IsSameTypeIgnoringAll(innerKey.Type) Then
                    ' Apply conversion if available.
                    Dim targetType As TypeSymbol = Nothing
                    Dim intrinsicOperatorType As SpecialType = SpecialType.None
                    Dim useSiteInfo = outerKeyBinder.GetNewCompoundUseSiteInfo(diagnostics)
 
                    Dim operatorKind As BinaryOperatorKind = OverloadResolution.ResolveBinaryOperator(BinaryOperatorKind.Equals,
                                                                                                      outerKey, innerKey,
                                                                                                      innerKeyBinder,
                                                                                                      considerUserDefinedOrLateBound:=False,
                                                                                                      intrinsicOperatorType:=intrinsicOperatorType,
                                                                                                      userDefinedOperator:=Nothing,
                                                                                                      useSiteInfo:=useSiteInfo)
 
                    If (operatorKind And BinaryOperatorKind.Equals) <> 0 AndAlso
                       (operatorKind And Not (BinaryOperatorKind.Equals Or BinaryOperatorKind.Lifted)) = 0 AndAlso
                       intrinsicOperatorType <> SpecialType.None Then
                        ' There is an intrinsic (=) operator, use its argument type.
                        Debug.Assert(useSiteInfo.Diagnostics.IsNullOrEmpty)
                        diagnostics.AddDependencies(useSiteInfo)
                        targetType = innerKeyBinder.GetSpecialTypeForBinaryOperator(joinCondition, outerKey.Type, innerKey.Type, intrinsicOperatorType,
                                                                                    (operatorKind And BinaryOperatorKind.Lifted) <> 0, diagnostics)
                    Else
                        ' Use dominant type.
                        Dim inferenceCollection = New TypeInferenceCollection()
                        inferenceCollection.AddType(outerKey.Type, RequiredConversion.Any, outerKey)
                        inferenceCollection.AddType(innerKey.Type, RequiredConversion.Any, innerKey)
 
                        Dim resultList = ArrayBuilder(Of DominantTypeData).GetInstance()
                        inferenceCollection.FindDominantType(resultList, Nothing, useSiteInfo)
 
                        If diagnostics.Add(joinCondition, useSiteInfo) Then
                            ' Suppress additional diagnostics
                            diagnostics = BindingDiagnosticBag.Discarded
                        End If
 
                        If resultList.Count = 1 Then
                            targetType = resultList(0).ResultType
                        End If
 
                        resultList.Free()
                    End If
 
                    If targetType Is Nothing Then
                        ReportDiagnostic(diagnostics, joinCondition, ERRID.ERR_EqualsTypeMismatch, outerKey.Type, innerKey.Type)
                        keysAreGood = False
                    Else
                        outerKey = outerKeyBinder.ApplyImplicitConversion(outerKey.Syntax, targetType, outerKey, diagnostics, False)
                        innerKey = innerKeyBinder.ApplyImplicitConversion(innerKey.Syntax, targetType, innerKey, diagnostics, False)
                    End If
                End If
 
                Debug.Assert(outerKey IsNot Nothing AndAlso innerKey IsNot Nothing)
                Return keysAreGood
            End Function
 
            Private Function VerifyJoinKeys(
                outerKey As BoundExpression,
                outerRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                outerSide As JoinConditionSideDeterminationVisitor.Result,
                innerKey As BoundExpression,
                innerRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                innerSide As JoinConditionSideDeterminationVisitor.Result,
                diagnostics As BindingDiagnosticBag
            ) As Boolean
                If outerSide = JoinConditionSideDeterminationVisitor.Result.Outer AndAlso
                   innerSide = JoinConditionSideDeterminationVisitor.Result.Inner Then
                    Return True
                End If
 
                If outerKey.HasErrors OrElse innerKey.HasErrors Then
                    Return False
                End If
 
                Dim builder = PooledStringBuilder.GetInstance()
 
                Dim outerNames As String = BuildEqualsOperandIsBadErrorArgument(builder.Builder, outerRangeVariables)
                Dim innerNames As String = BuildEqualsOperandIsBadErrorArgument(builder.Builder, innerRangeVariables)
 
                builder.Free()
 
                If outerNames Is Nothing OrElse innerNames Is Nothing Then
                    ' Syntax errors were earlier reported.
                    Return False
                End If
 
                Dim errorInfo = ErrorFactory.ErrorInfo(ERRID.ERR_EqualsOperandIsBad, outerNames, innerNames)
 
                Select Case outerSide
                    Case JoinConditionSideDeterminationVisitor.Result.Both,
                         JoinConditionSideDeterminationVisitor.Result.Inner
                        ' Report errors on references to inner.
                        EqualsOperandIsBadErrorVisitor.Report(Me, errorInfo, innerRangeVariables, outerKey, diagnostics)
 
                    Case JoinConditionSideDeterminationVisitor.Result.None
                        ReportDiagnostic(diagnostics, outerKey.Syntax, errorInfo)
 
                    Case JoinConditionSideDeterminationVisitor.Result.Outer
                        ' This side is good.
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(outerSide)
                End Select
 
                Select Case innerSide
                    Case JoinConditionSideDeterminationVisitor.Result.Both,
                         JoinConditionSideDeterminationVisitor.Result.Outer
                        ' Report errors on references to outer.
                        EqualsOperandIsBadErrorVisitor.Report(Me, errorInfo, outerRangeVariables, innerKey, diagnostics)
 
                    Case JoinConditionSideDeterminationVisitor.Result.None
                        ReportDiagnostic(diagnostics, innerKey.Syntax, errorInfo)
 
                    Case JoinConditionSideDeterminationVisitor.Result.Inner
                        ' This side is good.
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(innerSide)
                End Select
 
                Return False
            End Function
 
            Private Shared Function BuildEqualsOperandIsBadErrorArgument(
                builder As System.Text.StringBuilder,
                rangeVariables As ImmutableArray(Of RangeVariableSymbol)
            ) As String
                builder.Clear()
 
                Dim i As Integer
 
                For i = 0 To rangeVariables.Length - 1
                    If Not rangeVariables(i).Name.StartsWith("$"c, StringComparison.Ordinal) Then
                        builder.Append("'"c)
                        builder.Append(rangeVariables(i).Name)
                        builder.Append("'"c)
                        i += 1
                        Exit For
                    End If
                Next
 
                For i = i To rangeVariables.Length - 1
                    If Not rangeVariables(i).Name.StartsWith("$"c, StringComparison.Ordinal) Then
                        builder.Append(","c)
                        builder.Append(" "c)
                        builder.Append("'"c)
                        builder.Append(rangeVariables(i).Name)
                        builder.Append("'"c)
                    End If
                Next
 
                If builder.Length = 0 Then
                    Return Nothing
                End If
 
                Return builder.ToString()
            End Function
 
            ''' <summary>
            ''' Helper visitor to determine what join sides are referenced by an expression.
            ''' </summary>
            Private Class JoinConditionSideDeterminationVisitor
                Inherits BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
 
                <Flags()>
                Public Enum Result
                    None = 0
                    Outer = 1
                    Inner = 2
                    Both = Outer Or Inner
                End Enum
 
                Private ReadOnly _outerRangeVariables As ImmutableArray(Of Object) 'ImmutableArray(Of RangeVariableSymbol)
                Private ReadOnly _innerRangeVariables As ImmutableArray(Of Object) 'ImmutableArray(Of RangeVariableSymbol)
                Private _side As Result
 
                Public Sub New(
                    outerRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                    innerRangeVariables As ImmutableArray(Of RangeVariableSymbol)
                )
                    _outerRangeVariables = StaticCast(Of Object).From(outerRangeVariables)
                    _innerRangeVariables = StaticCast(Of Object).From(innerRangeVariables)
                End Sub
 
                Public Function DetermineTheSide(node As BoundExpression, diagnostics As BindingDiagnosticBag) As Result
                    _side = Result.None
                    Try
                        Visit(node)
                    Catch ex As CancelledByStackGuardException
                        ex.AddAnError(diagnostics)
                    End Try
 
                    Return _side
                End Function
 
                Public Overrides Function VisitRangeVariable(node As BoundRangeVariable) As BoundNode
                    Dim rangeVariable As RangeVariableSymbol = node.RangeVariable
 
                    If _outerRangeVariables.IndexOf(rangeVariable, ReferenceEqualityComparer.Instance) >= 0 Then
                        _side = _side Or Result.Outer
                    ElseIf _innerRangeVariables.IndexOf(rangeVariable, ReferenceEqualityComparer.Instance) >= 0 Then
                        _side = _side Or Result.Inner
                    End If
 
                    Return node
                End Function
            End Class
 
            ''' <summary>
            ''' Helper visitor to report query specific errors for an operand of an Equals expression.
            ''' </summary>
            Private Class EqualsOperandIsBadErrorVisitor
                Inherits BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
 
                Private ReadOnly _binder As Binder
                Private ReadOnly _errorInfo As DiagnosticInfo
                Private ReadOnly _diagnostics As BindingDiagnosticBag
                Private ReadOnly _badRangeVariables As ImmutableArray(Of Object) 'ImmutableArray(Of RangeVariableSymbol)
 
                Private Sub New(
                    binder As Binder,
                    errorInfo As DiagnosticInfo,
                    badRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                    diagnostics As BindingDiagnosticBag
                )
                    _badRangeVariables = StaticCast(Of Object).From(badRangeVariables)
                    _binder = binder
                    _diagnostics = diagnostics
                    _errorInfo = errorInfo
                End Sub
 
                Public Shared Sub Report(
                    binder As Binder,
                    errorInfo As DiagnosticInfo,
                    badRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                    node As BoundExpression,
                    diagnostics As BindingDiagnosticBag
                )
                    Dim v As New EqualsOperandIsBadErrorVisitor(binder, errorInfo, badRangeVariables, diagnostics)
                    Try
                        v.Visit(node)
                    Catch ex As CancelledByStackGuardException
                        ex.AddAnError(diagnostics)
                    End Try
                End Sub
 
                Public Overrides Function VisitRangeVariable(node As BoundRangeVariable) As BoundNode
                    Dim rangeVariable As RangeVariableSymbol = node.RangeVariable
 
                    If _badRangeVariables.IndexOf(rangeVariable, ReferenceEqualityComparer.Instance) >= 0 Then
                        ReportDiagnostic(_diagnostics, node.Syntax, _errorInfo)
                    End If
 
                    Return node
                End Function
 
            End Class
        End Class
 
        ''' <summary>
        ''' Knows how to bind FunctionAggregationSyntax and GroupAggregationSyntax
        ''' within particular [Into] clause.
        '''
        ''' Also implements Lookup/LookupNames methods to make sure that lookup without
        ''' container type, uses type of the group as the container type.
        ''' </summary>
        Private Class IntoClauseBinder
            Inherits Binder
 
            Protected ReadOnly m_GroupReference As BoundExpression
            Private ReadOnly _groupRangeVariables As ImmutableArray(Of RangeVariableSymbol)
            Private ReadOnly _groupCompoundVariableType As TypeSymbol
            Private ReadOnly _aggregationArgumentRangeVariables As ImmutableArray(Of RangeVariableSymbol)
 
            Public Sub New(
                parent As Binder,
                groupReference As BoundExpression,
                groupRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                groupCompoundVariableType As TypeSymbol,
                aggregationArgumentRangeVariables As ImmutableArray(Of RangeVariableSymbol)
            )
                MyBase.New(parent)
                m_GroupReference = groupReference
                _groupRangeVariables = groupRangeVariables
                _groupCompoundVariableType = groupCompoundVariableType
                _aggregationArgumentRangeVariables = aggregationArgumentRangeVariables
            End Sub
 
            Friend Overrides Function BindGroupAggregationExpression(
                group As GroupAggregationSyntax,
                diagnostics As BindingDiagnosticBag
            ) As BoundExpression
                Return New BoundGroupAggregation(group, m_GroupReference, m_GroupReference.Type)
            End Function
 
            ''' <summary>
            ''' Given aggregationVariables, bind Into selector in context of this binder.
            ''' </summary>
            Public Function BindIntoSelector(
                syntaxNode As QueryClauseSyntax,
                keysRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                compoundKeyReferencePart1 As BoundExpression,
                keysRangeVariablesPart1 As ImmutableArray(Of RangeVariableSymbol),
                compoundKeyReferencePart2 As BoundExpression,
                keysRangeVariablesPart2 As ImmutableArray(Of RangeVariableSymbol),
                declaredNames As HashSet(Of String),
                aggregationVariables As SeparatedSyntaxList(Of AggregationRangeVariableSyntax),
                mustProduceFlatCompoundVariable As Boolean,
                <Out()> ByRef declaredRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                diagnostics As BindingDiagnosticBag
            ) As BoundExpression
                Debug.Assert(declaredRangeVariables.IsDefault)
                Debug.Assert(compoundKeyReferencePart2 Is Nothing OrElse compoundKeyReferencePart1 IsNot Nothing)
                Debug.Assert((compoundKeyReferencePart1 Is Nothing) = (keysRangeVariablesPart1.Length = 0))
                Debug.Assert((compoundKeyReferencePart2 Is Nothing) = (keysRangeVariablesPart2.Length = 0))
                Debug.Assert((compoundKeyReferencePart2 Is Nothing) = (keysRangeVariables = keysRangeVariablesPart1))
                Debug.Assert(compoundKeyReferencePart1 Is Nothing OrElse keysRangeVariables.Length = keysRangeVariablesPart1.Length + keysRangeVariablesPart2.Length)
 
                Dim keys As Integer = keysRangeVariables.Length
                Dim selectors As BoundExpression()
                Dim fields As AnonymousTypeField()
 
                If declaredNames Is Nothing Then
                    declaredNames = CreateSetOfDeclaredNames(keysRangeVariables)
                Else
                    AssertDeclaredNames(declaredNames, keysRangeVariables)
                End If
 
                Dim intoSelector As BoundExpression
                Dim rangeVariables() As RangeVariableSymbol
 
                Dim fieldsToReserveForAggregationVariables As Integer = Math.Max(aggregationVariables.Count, 1)
 
                If keys + fieldsToReserveForAggregationVariables > 1 Then
                    ' Need to build an Anonymous Type
                    ' Add keys first.
                    If keys > 1 AndAlso Not mustProduceFlatCompoundVariable Then
                        If compoundKeyReferencePart2 Is Nothing Then
                            selectors = New BoundExpression(fieldsToReserveForAggregationVariables + 1 - 1) {}
                            fields = New AnonymousTypeField(selectors.Length - 1) {}
 
                            ' Using syntax of the first range variable in the source, this shouldn't create any problems.
                            fields(0) = New AnonymousTypeField(GetQueryLambdaParameterName(keysRangeVariablesPart1),
                                                               compoundKeyReferencePart1.Type,
                                                               keysRangeVariables(0).Syntax.GetLocation(), isKeyOrByRef:=True)
                            selectors(0) = compoundKeyReferencePart1
                            keys = 1
                        Else
                            selectors = New BoundExpression(fieldsToReserveForAggregationVariables + 2 - 1) {}
                            fields = New AnonymousTypeField(selectors.Length - 1) {}
 
                            ' Using syntax of the first range variable in the source, this shouldn't create any problems.
                            fields(0) = New AnonymousTypeField(GetQueryLambdaParameterNameLeft(keysRangeVariablesPart1),
                                                               compoundKeyReferencePart1.Type,
                                                               keysRangeVariablesPart1(0).Syntax.GetLocation(),
                                                               isKeyOrByRef:=True)
                            selectors(0) = compoundKeyReferencePart1
 
                            fields(1) = New AnonymousTypeField(GetQueryLambdaParameterNameRight(keysRangeVariablesPart2),
                                                               compoundKeyReferencePart2.Type,
                                                               keysRangeVariablesPart2(0).Syntax.GetLocation(),
                                                               isKeyOrByRef:=True)
                            selectors(1) = compoundKeyReferencePart2
 
                            keys = 2
                        End If
                    Else
                        selectors = New BoundExpression(keys + fieldsToReserveForAggregationVariables - 1) {}
                        fields = New AnonymousTypeField(selectors.Length - 1) {}
 
                        For i As Integer = 0 To keys - 1
                            Dim rangeVar As RangeVariableSymbol = keysRangeVariables(i)
                            fields(i) = New AnonymousTypeField(rangeVar.Name, rangeVar.Type, rangeVar.Syntax.GetLocation(), isKeyOrByRef:=True)
                            selectors(i) = New BoundRangeVariable(rangeVar.Syntax, rangeVar, rangeVar.Type).MakeCompilerGenerated()
                        Next
                    End If
 
                    ' Now add aggregation variables.
                    rangeVariables = New RangeVariableSymbol(fieldsToReserveForAggregationVariables - 1) {}
 
                    If aggregationVariables.Count = 0 Then
                        ' Malformed syntax tree.
                        Debug.Assert(aggregationVariables.Count > 0, "Malformed syntax tree.")
                        Dim rangeVar = RangeVariableSymbol.CreateForErrorRecovery(Me, syntaxNode, ErrorTypeSymbol.UnknownResultType)
 
                        rangeVariables(0) = rangeVar
                        selectors(keys) = BadExpression(syntaxNode, m_GroupReference, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                        fields(keys) = New AnonymousTypeField(rangeVar.Name, rangeVar.Type, rangeVar.Syntax.GetLocation(), isKeyOrByRef:=True)
                    Else
                        For i As Integer = 0 To aggregationVariables.Count - 1
                            Dim selector As BoundExpression = Nothing
                            Dim rangeVar As RangeVariableSymbol = BindAggregationRangeVariable(aggregationVariables(i),
                                                                                               declaredNames, selector,
                                                                                               diagnostics)
 
                            Debug.Assert(rangeVar IsNot Nothing)
                            rangeVariables(i) = rangeVar
 
                            selectors(keys + i) = selector
                            fields(keys + i) = New AnonymousTypeField(
                                rangeVar.Name, rangeVar.Type, rangeVar.Syntax.GetLocation(), isKeyOrByRef:=True)
                        Next
                    End If
 
                    Debug.Assert(selectors.Length > 1)
                    intoSelector = BindAnonymousObjectCreationExpression(syntaxNode,
                                                                     New AnonymousTypeDescriptor(fields.AsImmutableOrNull(),
                                                                                                 syntaxNode.QueryClauseKeywordOrRangeVariableIdentifier.GetLocation(),
                                                                                                 True),
                                                                     selectors.AsImmutableOrNull(),
                                                                     diagnostics).MakeCompilerGenerated()
                Else
                    Debug.Assert(keys = 0)
 
                    Dim rangeVar As RangeVariableSymbol
 
                    If aggregationVariables.Count = 0 Then
                        ' Malformed syntax tree.
                        Debug.Assert(aggregationVariables.Count > 0, "Malformed syntax tree.")
                        rangeVar = RangeVariableSymbol.CreateForErrorRecovery(Me, syntaxNode, ErrorTypeSymbol.UnknownResultType)
                        intoSelector = BadExpression(syntaxNode, m_GroupReference, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                    Else
                        Debug.Assert(aggregationVariables.Count = 1)
                        intoSelector = Nothing
                        rangeVar = BindAggregationRangeVariable(aggregationVariables(0),
                                                                declaredNames, intoSelector,
                                                                diagnostics)
                        Debug.Assert(rangeVar IsNot Nothing)
                    End If
 
                    rangeVariables = {rangeVar}
                End If
 
                AssertDeclaredNames(declaredNames, rangeVariables.AsImmutableOrNull())
 
                declaredRangeVariables = rangeVariables.AsImmutableOrNull()
                Return intoSelector
            End Function
 
            Friend Overrides Function BindFunctionAggregationExpression(
                functionAggregationSyntax As FunctionAggregationSyntax,
                diagnostics As BindingDiagnosticBag
            ) As BoundExpression
                If functionAggregationSyntax.FunctionName.GetTypeCharacter() <> TypeCharacter.None Then
                    ReportDiagnostic(diagnostics, functionAggregationSyntax.FunctionName, ERRID.ERR_TypeCharOnAggregation)
                End If
 
                Dim aggregationParam As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(GetQueryLambdaParameterName(_groupRangeVariables), 0,
                                                                                                      _groupCompoundVariableType,
                                                                                                      If(functionAggregationSyntax.Argument, functionAggregationSyntax),
                                                                                                      _groupRangeVariables)
 
                ' Note [Binder:=Me.ContainingBinder] below. We are excluding this binder from the chain of
                ' binders for an argument of the function. Do not want it to interfere in any way given
                ' its special binding and Lookup/LookupNames behavior.
 
                Dim aggregationLambdaSymbol = Me.ContainingBinder.CreateQueryLambdaSymbol(
                    If(LambdaUtilities.GetAggregationLambdaBody(functionAggregationSyntax), functionAggregationSyntax),
                    SynthesizedLambdaKind.AggregationQueryLambda,
                    ImmutableArray.Create(aggregationParam))
 
                ' Create binder for the aggregation.
                Dim aggregationBinder As New QueryLambdaBinder(aggregationLambdaSymbol, _aggregationArgumentRangeVariables)
 
                Dim arguments As ImmutableArray(Of BoundExpression)
                Dim aggregationLambda As BoundQueryLambda = Nothing
 
                If functionAggregationSyntax.Argument Is Nothing Then
                    arguments = ImmutableArray(Of BoundExpression).Empty
                Else
                    ' Bind argument as a value, conversion during overload resolution should take care of the rest (making it an RValue, etc.).
                    Dim aggregationSelector = aggregationBinder.BindValue(functionAggregationSyntax.Argument, diagnostics)
 
                    aggregationLambda = CreateBoundQueryLambda(aggregationLambdaSymbol,
                                                               _groupRangeVariables,
                                                               aggregationSelector,
                                                               exprIsOperandOfConditionalBranch:=False)
 
                    ' Note, we are not setting ReturnType for aggregationLambdaSymbol to allow
                    ' additional conversions. This type doesn't affect type of any range variable
                    ' in the query.
                    aggregationLambda.SetWasCompilerGenerated()
 
                    arguments = ImmutableArray.Create(Of BoundExpression)(aggregationLambda)
                End If
 
                ' Now bind the aggregation call.
                Dim boundCallOrBadExpression As BoundExpression
 
                If m_GroupReference.Type.IsErrorType() OrElse String.IsNullOrEmpty(functionAggregationSyntax.FunctionName.ValueText) Then
                    boundCallOrBadExpression = BadExpression(functionAggregationSyntax,
                                                             ImmutableArray.Create(m_GroupReference).AddRange(arguments),
                                                             ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated()
                Else
                    Dim callDiagnostics As BindingDiagnosticBag = diagnostics
 
                    If aggregationLambda IsNot Nothing AndAlso ShouldSuppressDiagnostics(aggregationLambda) Then
                        ' Operator BindQueryClauseCall will fail, let's suppress any additional errors it will report.
                        callDiagnostics = BindingDiagnosticBag.Discarded
                    End If
 
                    boundCallOrBadExpression = BindQueryOperatorCall(functionAggregationSyntax, m_GroupReference,
                                                                   functionAggregationSyntax.FunctionName.ValueText,
                                                                   arguments,
                                                                   functionAggregationSyntax.FunctionName.Span,
                                                                   callDiagnostics)
                End If
 
                Return New BoundQueryClause(functionAggregationSyntax, boundCallOrBadExpression,
                                            ImmutableArray(Of RangeVariableSymbol).Empty,
                                            boundCallOrBadExpression.Type,
                                            ImmutableArray.Create(Of Binder)(aggregationBinder),
                                            boundCallOrBadExpression.Type)
            End Function
 
            Public Overrides Sub AddLookupSymbolsInfo(nameSet As LookupSymbolsInfo, options As LookupOptions)
                If (options And (LookupOptionExtensions.ConsiderationMask Or LookupOptions.MustNotBeInstance)) <> 0 Then
                    Return
                End If
 
                ' Should look for group's methods only.
                AddMemberLookupSymbolsInfo(nameSet,
                                  m_GroupReference.Type,
                                  options Or CType(LookupOptions.MethodsOnly Or LookupOptions.MustBeInstance, LookupOptions))
            End Sub
 
            Public Overrides Sub Lookup(lookupResult As LookupResult, name As String, arity As Integer, options As LookupOptions, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol))
                If (options And (LookupOptionExtensions.ConsiderationMask Or LookupOptions.MustNotBeInstance)) <> 0 Then
                    Return
                End If
 
                ' Should look for group's methods only.
                LookupMember(lookupResult,
                             m_GroupReference.Type,
                             name,
                             arity,
                             options Or CType(LookupOptions.MethodsOnly Or LookupOptions.MustBeInstance, LookupOptions),
                             useSiteInfo)
            End Sub
 
            ''' <summary>
            ''' Bind AggregationRangeVariableSyntax in context of this binder.
            ''' </summary>
            Public Function BindAggregationRangeVariable(
                item As AggregationRangeVariableSyntax,
                declaredNames As HashSet(Of String),
                <Out()> ByRef selector As BoundExpression,
                diagnostics As BindingDiagnosticBag
            ) As RangeVariableSymbol
                Debug.Assert(selector Is Nothing)
 
                Dim variableName As VariableNameEqualsSyntax = item.NameEquals
 
                ' Figure out the name of the new range variable
                Dim rangeVarName As String = Nothing
                Dim rangeVarNameSyntax As SyntaxToken = Nothing
 
                If variableName IsNot Nothing Then
                    rangeVarNameSyntax = variableName.Identifier.Identifier
                    rangeVarName = rangeVarNameSyntax.ValueText
                    Debug.Assert(variableName.AsClause Is Nothing)
 
                Else
                    ' Infer the name from expression
                    Select Case item.Aggregation.Kind
                        Case SyntaxKind.GroupAggregation
                            ' AggregateClause doesn't support GroupAggregation.
                            If item.Parent.Kind <> SyntaxKind.AggregateClause Then
                                rangeVarNameSyntax = DirectCast(item.Aggregation, GroupAggregationSyntax).GroupKeyword
                                rangeVarName = rangeVarNameSyntax.ValueText
                            End If
                        Case SyntaxKind.FunctionAggregation
                            rangeVarNameSyntax = DirectCast(item.Aggregation, FunctionAggregationSyntax).FunctionName
                            rangeVarName = rangeVarNameSyntax.ValueText
                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(item.Aggregation.Kind)
                    End Select
                End If
 
                ' Bind the value.
                selector = BindRValue(item.Aggregation, diagnostics)
 
                If rangeVarName IsNot Nothing AndAlso rangeVarName.Length = 0 Then
                    ' Empty string must have been a syntax error.
                    rangeVarName = Nothing
                End If
 
                Dim rangeVar As RangeVariableSymbol = Nothing
 
                If rangeVarName IsNot Nothing Then
 
                    rangeVar = RangeVariableSymbol.Create(Me, rangeVarNameSyntax, selector.Type)
 
                    ' Note what we are doing here:
                    ' We are creating BoundRangeVariableAssignment before doing any shadowing checks
                    ' so that SemanticModel can find the declared symbol, but, if the variable will conflict with another
                    ' variable in the same child scope, we will not add it to the scope. Instead, we create special
                    ' error recovery range variable symbol and add it to the scope at the same place, making sure
                    ' that the earlier declared range variable wins during name lookup.
                    ' As an alternative, we could still add the original range variable symbol to the scope and then,
                    ' while we are binding the rest of the query in error recovery mode, references to the name would
                    ' cause ambiguity. However, this could negatively affect IDE experience. Also, as we build an
                    ' Anonymous Type for the compound range variables, we would end up with a type with duplicate members,
                    ' which could cause problems elsewhere.
                    selector = New BoundRangeVariableAssignment(item, rangeVar, selector, selector.Type)
                    Dim doErrorRecovery As Boolean = False
 
                    If declaredNames IsNot Nothing AndAlso Not declaredNames.Add(rangeVarName) Then
                        ReportDiagnostic(diagnostics, rangeVarNameSyntax, ERRID.ERR_QueryDuplicateAnonTypeMemberName1, rangeVarName)
                        doErrorRecovery = True  ' Shouldn't add to the scope.
                    Else
                        Me.VerifyRangeVariableName(rangeVar, rangeVarNameSyntax, diagnostics)
                    End If
 
                    If doErrorRecovery Then
                        rangeVar = RangeVariableSymbol.CreateForErrorRecovery(Me, rangeVar.Syntax, selector.Type)
                    End If
 
                Else
                    Debug.Assert(rangeVar Is Nothing)
                    rangeVar = RangeVariableSymbol.CreateForErrorRecovery(Me, item, selector.Type)
                End If
 
                Debug.Assert(selector IsNot Nothing)
 
                Return rangeVar
            End Function
 
        End Class
 
        ''' <summary>
        ''' Same as IntoClauseBinder, but disallows references to GroupAggregationSyntax.
        ''' </summary>
        Private Class IntoClauseDisallowGroupReferenceBinder
            Inherits IntoClauseBinder
 
            Public Sub New(
                parent As Binder,
                groupReference As BoundExpression,
                groupRangeVariables As ImmutableArray(Of RangeVariableSymbol),
                groupCompoundVariableType As TypeSymbol,
                aggregationArgumentRangeVariables As ImmutableArray(Of RangeVariableSymbol)
            )
                MyBase.New(parent, groupReference, groupRangeVariables, groupCompoundVariableType, aggregationArgumentRangeVariables)
            End Sub
 
            Friend Overrides Function BindGroupAggregationExpression(group As GroupAggregationSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
                ' Parser should have reported an error.
                Return BadExpression(group, m_GroupReference, ErrorTypeSymbol.UnknownResultType)
            End Function
        End Class
 
        ''' <summary>
        ''' Bind CollectionRangeVariableSyntax, applying AsQueryable/AsEnumerable/Cast(Of Object) calls and
        ''' Select with implicit type conversion as appropriate.
        ''' </summary>
        Private Function BindCollectionRangeVariable(
            syntax As CollectionRangeVariableSyntax,
            beginsTheQuery As Boolean,
            declaredNames As HashSet(Of String),
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryableSource
            Debug.Assert(declaredNames Is Nothing OrElse syntax.Parent.Kind = SyntaxKind.SimpleJoinClause OrElse syntax.Parent.Kind = SyntaxKind.GroupJoinClause)
 
            Dim source As BoundQueryPart = New BoundQuerySource(BindRValue(syntax.Expression, diagnostics))
 
            Dim variableType As TypeSymbol = Nothing
            Dim queryable As BoundExpression = ConvertToQueryableType(source, diagnostics, variableType)
 
            Dim sourceIsNotQueryable As Boolean = False
 
            If variableType Is Nothing Then
                If Not source.HasErrors Then
                    ReportDiagnostic(diagnostics, syntax.Expression, ERRID.ERR_ExpectedQueryableSource, source.Type)
                End If
 
                sourceIsNotQueryable = True
 
            ElseIf source IsNot queryable Then
                source = New BoundToQueryableCollectionConversion(DirectCast(queryable, BoundCall)).MakeCompilerGenerated()
            End If
 
            ' Deal with AsClauseOpt and various modifiers.
            Dim targetVariableType As TypeSymbol = Nothing
 
            If syntax.AsClause IsNot Nothing Then
                targetVariableType = DecodeModifiedIdentifierType(syntax.Identifier,
                                                                  syntax.AsClause,
                                                                  Nothing,
                                                                  Nothing,
                                                                  diagnostics,
                                                                  ModifiedIdentifierTypeDecoderContext.LocalType Or
                                                                  ModifiedIdentifierTypeDecoderContext.QueryRangeVariableType)
            ElseIf syntax.Identifier.Nullable.Node IsNot Nothing Then
                ReportDiagnostic(diagnostics, syntax.Identifier.Nullable, ERRID.ERR_NullableTypeInferenceNotSupported)
            End If
 
            If variableType Is Nothing Then
                Debug.Assert(sourceIsNotQueryable)
 
                If targetVariableType Is Nothing Then
                    variableType = ErrorTypeSymbol.UnknownResultType
                Else
                    variableType = targetVariableType
                End If
 
            ElseIf targetVariableType IsNot Nothing AndAlso
                   Not targetVariableType.IsSameTypeIgnoringAll(variableType) Then
                Debug.Assert(Not sourceIsNotQueryable AndAlso syntax.AsClause IsNot Nothing)
                ' Need to apply implicit Select that converts variableType to targetVariableType.
                source = ApplyImplicitCollectionConversion(syntax, source, variableType, targetVariableType, diagnostics)
                variableType = targetVariableType
            End If
 
            Dim variable As RangeVariableSymbol = Nothing
            Dim rangeVarName As String = syntax.Identifier.Identifier.ValueText
            Dim rangeVariableOpt As RangeVariableSymbol = Nothing
 
            If rangeVarName IsNot Nothing AndAlso rangeVarName.Length = 0 Then
                ' Empty string must have been a syntax error.
                rangeVarName = Nothing
            End If
 
            If rangeVarName IsNot Nothing Then
                variable = RangeVariableSymbol.Create(Me, syntax.Identifier.Identifier, variableType)
 
                ' Note what we are doing here:
                ' We are capturing rangeVariableOpt before doing any shadowing checks
                ' so that SemanticModel can find the declared symbol, but, if the variable will conflict with another
                ' variable in the same child scope, we will not add it to the scope. Instead, we create special
                ' error recovery range variable symbol and add it to the scope at the same place, making sure
                ' that the earlier declared range variable wins during name lookup.
                ' As an alternative, we could still add the original range variable symbol to the scope and then,
                ' while we are binding the rest of the query in error recovery mode, references to the name would
                ' cause ambiguity. However, this could negatively affect IDE experience. Also, as we build an
                ' Anonymous Type for the compound range variables, we would end up with a type with duplicate members,
                ' which could cause problems elsewhere.
                rangeVariableOpt = variable
 
                Dim doErrorRecovery As Boolean = False
 
                If declaredNames IsNot Nothing AndAlso Not declaredNames.Add(rangeVarName) Then
                    ReportDiagnostic(diagnostics, syntax.Identifier.Identifier, ERRID.ERR_QueryDuplicateAnonTypeMemberName1, rangeVarName)
                    doErrorRecovery = True  ' Shouldn't add to the scope.
                Else
 
                    ' Check shadowing etc.
                    VerifyRangeVariableName(variable, syntax.Identifier.Identifier, diagnostics)
 
                    If Not beginsTheQuery AndAlso declaredNames Is Nothing Then
                        Debug.Assert(syntax.Parent.Kind = SyntaxKind.FromClause OrElse syntax.Parent.Kind = SyntaxKind.AggregateClause)
                        ' We are about to add this range variable to the current child scope.
                        If ShadowsRangeVariableInTheChildScope(Me, variable) Then
                            ' Shadowing error was reported earlier.
                            doErrorRecovery = True  ' Shouldn't add to the scope.
                        End If
                    End If
                End If
 
                If doErrorRecovery Then
                    variable = RangeVariableSymbol.CreateForErrorRecovery(Me, variable.Syntax, variableType)
                End If
 
            Else
                Debug.Assert(variable Is Nothing)
                variable = RangeVariableSymbol.CreateForErrorRecovery(Me, syntax, variableType)
            End If
 
            Dim result As New BoundQueryableSource(syntax, source, rangeVariableOpt,
                                                   ImmutableArray.Create(variable), variableType,
                                                   ImmutableArray(Of Binder).Empty,
                                                   If(sourceIsNotQueryable, ErrorTypeSymbol.UnknownResultType, source.Type),
                                                   hasErrors:=sourceIsNotQueryable)
 
            Return result
        End Function
 
        ''' <summary>
        ''' Apply "conversion" to the source based on the target AsClause Type of the CollectionRangeVariableSyntax.
        ''' Returns implicit BoundQueryClause or the source, in case of an early failure.
        ''' </summary>
        Private Function ApplyImplicitCollectionConversion(
            syntax As CollectionRangeVariableSyntax,
            source As BoundQueryPart,
            variableType As TypeSymbol,
            targetVariableType As TypeSymbol,
            diagnostics As BindingDiagnosticBag
        ) As BoundQueryPart
            If source.Type.IsErrorType() Then
                ' If the source is already a "bad" type, we know that we will not be able to bind to the Select.
                ' Let's just report errors for the conversion between types, if any.
                Dim sourceValue As New BoundRValuePlaceholder(syntax.AsClause, variableType)
                ApplyImplicitConversion(syntax.AsClause, targetVariableType, sourceValue, diagnostics)
            Else
                ' Create LambdaSymbol for the shape of the selector.
                Dim param As BoundLambdaParameterSymbol = CreateQueryLambdaParameterSymbol(syntax.Identifier.Identifier.ValueText, 0,
                                                                                           variableType,
                                                                                           syntax.AsClause)
 
                Debug.Assert(LambdaUtilities.IsNonUserCodeQueryLambda(syntax.AsClause))
                Dim lambdaSymbol = Me.CreateQueryLambdaSymbol(syntax.AsClause,
                                                              SynthesizedLambdaKind.ConversionNonUserCodeQueryLambda,
                                                              ImmutableArray.Create(param))
 
                lambdaSymbol.SetQueryLambdaReturnType(targetVariableType)
 
                Dim selectorBinder As New QueryLambdaBinder(lambdaSymbol, ImmutableArray(Of RangeVariableSymbol).Empty)
 
                Dim selector As BoundExpression = selectorBinder.ApplyImplicitConversion(syntax.AsClause, targetVariableType,
                                                                                         New BoundParameter(param.Syntax,
                                                                                                            param,
                                                                                                            isLValue:=False,
                                                                                                            type:=variableType).MakeCompilerGenerated(),
                                                                                         diagnostics)
 
                Dim selectorLambda = CreateBoundQueryLambda(lambdaSymbol,
                                                            ImmutableArray(Of RangeVariableSymbol).Empty,
                                                            selector,
                                                            exprIsOperandOfConditionalBranch:=False)
                selectorLambda.SetWasCompilerGenerated()
 
                If ShouldSuppressDiagnostics(selectorLambda) Then
                    ' If the selector is already "bad", we know that we will not be able to bind to the Select.
                    ' Let's suppress additional errors.
                    diagnostics = BindingDiagnosticBag.Discarded
                End If
 
                Dim boundCallOrBadExpression As BoundExpression
                boundCallOrBadExpression = BindQueryOperatorCall(syntax.AsClause, source,
                                                                 StringConstants.SelectMethod,
                                                                 ImmutableArray.Create(Of BoundExpression)(selectorLambda),
                                                                 syntax.AsClause.Span,
                                                                 diagnostics)
 
                Debug.Assert(boundCallOrBadExpression.WasCompilerGenerated)
 
                Return New BoundQueryClause(source.Syntax,
                                            boundCallOrBadExpression,
                                            ImmutableArray(Of RangeVariableSymbol).Empty,
                                            targetVariableType,
                                            ImmutableArray.Create(Of Binder)(selectorBinder),
                                            boundCallOrBadExpression.Type).MakeCompilerGenerated()
            End If
 
            Return source
        End Function
 
        ''' <summary>
        ''' Convert source expression to queryable type by inferring control variable type
        ''' and applying AsQueryable/AsEnumerable or Cast(Of Object) calls.
        '''
        ''' In case of success, returns possibly "converted" source and non-Nothing controlVariableType.
        ''' In case of failure, returns passed in source and Nothing as controlVariableType.
        ''' </summary>
        Private Function ConvertToQueryableType(
            source As BoundExpression,
            diagnostics As BindingDiagnosticBag,
            <Out()> ByRef controlVariableType As TypeSymbol
        ) As BoundExpression
            controlVariableType = Nothing
 
            If Not source.IsValue OrElse source.Type.IsErrorType Then
                Return source
            End If
 
            ' 11.21.2 Queryable Types
            ' A queryable collection type must satisfy one of the following conditions, in order of preference:
            ' -	It must define a conforming Select method.
            ' -	It must have one of the following methods
            ' Function AsEnumerable() As CT
            ' Function AsQueryable() As CT
            ' which can be called to obtain a queryable collection. If both methods are provided, AsQueryable is preferred over AsEnumerable.
            ' -	It must have a method
            ' Function Cast(Of T)() As CT
            ' which can be called with the type of the range variable to produce a queryable collection.
 
            ' Does it define a conforming Select method?
            Dim inferredType As TypeSymbol = InferControlVariableType(source, diagnostics)
 
            If inferredType IsNot Nothing Then
                controlVariableType = inferredType
                Return source
            End If
 
            Dim result As BoundExpression = Nothing
            Dim additionalDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics)
 
            ' Does it have Function AsQueryable() As CT returning queryable collection?
            Dim asQueryable As BoundExpression = BindQueryOperatorCall(source.Syntax, source, StringConstants.AsQueryableMethod,
                                                                       ImmutableArray(Of BoundExpression).Empty,
                                                                       source.Syntax.Span, additionalDiagnostics)
 
            If Not asQueryable.HasErrors AndAlso asQueryable.Kind = BoundKind.Call Then
                inferredType = InferControlVariableType(asQueryable, diagnostics)
 
                If inferredType IsNot Nothing Then
                    controlVariableType = inferredType
                    result = asQueryable
                    diagnostics.AddRange(additionalDiagnostics)
                End If
            End If
 
            If result Is Nothing Then
                additionalDiagnostics.Clear()
 
                ' Does it have Function AsEnumerable() As CT returning queryable collection?
                Dim asEnumerable As BoundExpression = BindQueryOperatorCall(source.Syntax, source, StringConstants.AsEnumerableMethod,
                                                                           ImmutableArray(Of BoundExpression).Empty,
                                                                           source.Syntax.Span, additionalDiagnostics)
 
                If Not asEnumerable.HasErrors AndAlso asEnumerable.Kind = BoundKind.Call Then
                    inferredType = InferControlVariableType(asEnumerable, diagnostics)
 
                    If inferredType IsNot Nothing Then
                        controlVariableType = inferredType
                        result = asEnumerable
                        diagnostics.AddRange(additionalDiagnostics)
                    End If
                End If
            End If
 
            If result Is Nothing Then
                additionalDiagnostics.Clear()
 
                ' If it has Function Cast(Of T)() As CT, call it with T == Object and assume Object is control variable type.
                inferredType = GetSpecialType(SpecialType.System_Object, source.Syntax, additionalDiagnostics)
 
                Dim cast As BoundExpression = BindQueryOperatorCall(source.Syntax, source, StringConstants.CastMethod,
                                                                    New BoundTypeArguments(source.Syntax,
                                                                                           ImmutableArray.Create(Of TypeSymbol)(inferredType)),
                                                                    ImmutableArray(Of BoundExpression).Empty,
                                                                    source.Syntax.Span, additionalDiagnostics)
 
                If Not cast.HasErrors AndAlso cast.Kind = BoundKind.Call Then
                    controlVariableType = inferredType
                    result = cast
                    diagnostics.AddRange(additionalDiagnostics)
                End If
            End If
 
            additionalDiagnostics.Free()
 
            Debug.Assert((result Is Nothing) = (controlVariableType Is Nothing))
            Return If(result Is Nothing, source, result)
        End Function
 
        ''' <summary>
        ''' Given query operator source, infer control variable type from available
        ''' 'Select' methods.
        '''
        ''' Returns inferred type or Nothing.
        ''' </summary>
        Private Function InferControlVariableType(source As BoundExpression, diagnostics As BindingDiagnosticBag) As TypeSymbol
            Debug.Assert(source.IsValue)
 
            Dim result As TypeSymbol = Nothing
 
            ' Look for Select methods available for the source.
            Dim lookupResult As LookupResult = LookupResult.GetInstance()
            Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
            LookupMember(lookupResult, source.Type, StringConstants.SelectMethod, 0, QueryOperatorLookupOptions, useSiteInfo)
 
            If lookupResult.IsGood Then
 
                Dim failedDueToAnAmbiguity As Boolean = False
 
                ' Name lookup does not look for extension methods if it found a suitable
                ' instance method, which is a good thing because according to language spec:
                '
                ' 11.21.2 Queryable Types
                ' ... , when determining the element type of a collection if there
                ' are instance methods that match well-known methods, then any extension methods
                ' that match well-known methods are ignored.
                Debug.Assert((QueryOperatorLookupOptions And LookupOptions.EagerlyLookupExtensionMethods) = 0)
 
                result = InferControlVariableType(lookupResult.Symbols, failedDueToAnAmbiguity)
 
                If result Is Nothing AndAlso Not failedDueToAnAmbiguity AndAlso Not lookupResult.Symbols(0).IsReducedExtensionMethod() Then
                    ' We tried to infer from instance methods and there were no suitable 'Select' method,
                    ' let's try to infer from extension methods.
                    lookupResult.Clear()
                    Me.LookupExtensionMethods(lookupResult, source.Type, StringConstants.SelectMethod, 0, QueryOperatorLookupOptions, useSiteInfo)
 
                    If lookupResult.IsGood Then
                        result = InferControlVariableType(lookupResult.Symbols, failedDueToAnAmbiguity)
                    End If
                End If
            End If
 
            diagnostics.Add(source, useSiteInfo)
            lookupResult.Free()
 
            Return result
        End Function
 
        ''' <summary>
        ''' Given a set of 'Select' methods, infer control variable type.
        '''
        ''' Returns inferred type or Nothing.
        ''' </summary>
        Private Function InferControlVariableType(
            methods As ArrayBuilder(Of Symbol),
            <Out()> ByRef failedDueToAnAmbiguity As Boolean
        ) As TypeSymbol
            Dim result As TypeSymbol = Nothing
            failedDueToAnAmbiguity = False
 
            For Each method As MethodSymbol In methods
                Dim inferredType As TypeSymbol = InferControlVariableType(method)
 
                If inferredType IsNot Nothing Then
                    If inferredType.ReferencesMethodsTypeParameter(method) Then
                        failedDueToAnAmbiguity = True
                        Return Nothing
                    End If
 
                    If result Is Nothing Then
                        result = inferredType
                    ElseIf Not result.IsSameTypeIgnoringAll(inferredType) Then
                        failedDueToAnAmbiguity = True
                        Return Nothing
                    End If
                End If
            Next
 
            Return result
        End Function
 
        ''' <summary>
        ''' Given a method, infer control variable type.
        '''
        ''' Returns inferred type or Nothing.
        ''' </summary>
        Private Function InferControlVariableType(method As MethodSymbol) As TypeSymbol
            ' Ignore Subs
            If method.IsSub Then
                Return Nothing
            End If
 
            ' Only methods taking exactly one parameter are acceptable.
            If method.ParameterCount <> 1 Then
                Return Nothing
            End If
 
            Dim selectParameter As ParameterSymbol = method.Parameters(0)
 
            If selectParameter.IsByRef Then
                Return Nothing
            End If
 
            Dim parameterType As TypeSymbol = selectParameter.Type
 
            ' We are expecting a delegate type with the following shape:
            '     Function Selector (element as ControlVariableType) As AType
 
            ' The delegate type, directly converted to or argument of Expression(Of T)
            Dim delegateType As NamedTypeSymbol = parameterType.DelegateOrExpressionDelegate(Me)
 
            If delegateType Is Nothing Then
                Return Nothing
            End If
 
            Dim invoke As MethodSymbol = delegateType.DelegateInvokeMethod
 
            If invoke Is Nothing OrElse invoke.IsSub OrElse invoke.ParameterCount <> 1 Then
                Return Nothing
            End If
 
            Dim invokeParameter As ParameterSymbol = invoke.Parameters(0)
 
            ' Do not allow Optional, ParamArray and ByRef.
            If invokeParameter.IsOptional OrElse invokeParameter.IsByRef OrElse invokeParameter.IsParamArray Then
                Return Nothing
            End If
 
            Dim controlVariableType As TypeSymbol = invokeParameter.Type
 
            Return If(controlVariableType.IsErrorType(), Nothing, controlVariableType)
        End Function
 
        ''' <summary>
        ''' Return method group or Nothing in case nothing was found.
        ''' Note, returned group might have ResultKind = "Inaccessible".
        ''' </summary>
        Private Function LookupQueryOperator(
            node As SyntaxNode,
            source As BoundExpression,
            operatorName As String,
            typeArgumentsOpt As BoundTypeArguments,
            diagnostics As BindingDiagnosticBag
        ) As BoundMethodGroup
            Dim lookupResult As LookupResult = LookupResult.GetInstance()
            Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
            LookupMember(lookupResult, source.Type, operatorName, 0, QueryOperatorLookupOptions, useSiteInfo)
 
            Dim methodGroup As BoundMethodGroup = Nothing
 
            ' NOTE: Lookup may return Kind = LookupResultKind.Inaccessible or LookupResultKind.MustBeInstance;
            '
            '       It looks we intentionally pass LookupResultKind.Inaccessible to CreateBoundMethodGroup(...)
            '       causing BC30390 to be generated instead of BC36594 reported by Dev11 (more accurate message?)
            '
            '       As CreateBoundMethodGroup(...) only expects Kind = LookupResultKind.Good or
            '       LookupResultKind.Inaccessible in all other cases we just skip calling this method
            '       so that BC36594 is generated which what seems to what Dev11 does.
            If Not lookupResult.IsClear AndAlso (lookupResult.Kind = LookupResultKind.Good OrElse lookupResult.Kind = LookupResultKind.Inaccessible) Then
                methodGroup = CreateBoundMethodGroup(
                            node,
                            lookupResult,
                            QueryOperatorLookupOptions,
                            diagnostics.AccumulatesDependencies,
                            source,
                            typeArgumentsOpt,
                            QualificationKind.QualifiedViaValue).MakeCompilerGenerated()
            End If
 
            diagnostics.Add(node, useSiteInfo)
            lookupResult.Free()
 
            Return methodGroup
        End Function
 
        Private Function BindQueryOperatorCall(
            node As SyntaxNode,
            source As BoundExpression,
            operatorName As String,
            arguments As ImmutableArray(Of BoundExpression),
            operatorNameLocation As TextSpan,
            diagnostics As BindingDiagnosticBag
        ) As BoundExpression
            Return BindQueryOperatorCall(node,
                                         source,
                                         operatorName,
                                         LookupQueryOperator(node, source, operatorName, Nothing, diagnostics),
                                         arguments,
                                         operatorNameLocation,
                                         diagnostics)
        End Function
 
        Private Function BindQueryOperatorCall(
            node As SyntaxNode,
            source As BoundExpression,
            operatorName As String,
            typeArgumentsOpt As BoundTypeArguments,
            arguments As ImmutableArray(Of BoundExpression),
            operatorNameLocation As TextSpan,
            diagnostics As BindingDiagnosticBag
        ) As BoundExpression
            Return BindQueryOperatorCall(node,
                                         source,
                                         operatorName,
                                         LookupQueryOperator(node, source, operatorName, typeArgumentsOpt, diagnostics),
                                         arguments,
                                         operatorNameLocation,
                                         diagnostics)
        End Function
 
        ''' <summary>
        ''' [methodGroup] can be Nothing if lookup didn't find anything.
        ''' </summary>
        Private Function BindQueryOperatorCall(
            node As SyntaxNode,
            source As BoundExpression,
            operatorName As String,
            methodGroup As BoundMethodGroup,
            arguments As ImmutableArray(Of BoundExpression),
            operatorNameLocation As TextSpan,
            diagnostics As BindingDiagnosticBag
        ) As BoundExpression
            Debug.Assert(source.IsValue)
            Debug.Assert(methodGroup Is Nothing OrElse
                         (methodGroup.ReceiverOpt Is source AndAlso
                            (methodGroup.ResultKind = LookupResultKind.Good OrElse methodGroup.ResultKind = LookupResultKind.Inaccessible)))
 
            Dim boundCall As BoundExpression = Nothing
 
            If methodGroup IsNot Nothing Then
                Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
                Dim results As OverloadResolution.OverloadResolutionResult = OverloadResolution.QueryOperatorInvocationOverloadResolution(methodGroup,
                                                                                                                                          arguments, Me,
                                                                                                                                          useSiteInfo)
 
                If diagnostics.Add(node, useSiteInfo) Then
                    If methodGroup.ResultKind <> LookupResultKind.Inaccessible Then
                        ' Suppress additional diagnostics
                        diagnostics = BindingDiagnosticBag.Discarded
                    End If
                End If
 
                If Not results.BestResult.HasValue Then
                    ' Create and report the diagnostic.
                    If results.Candidates.Length = 0 Then
                        results = OverloadResolution.QueryOperatorInvocationOverloadResolution(methodGroup, arguments, Me, includeEliminatedCandidates:=True,
                                                                                               useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
                    End If
 
                    If results.Candidates.Length > 0 Then
                        boundCall = ReportOverloadResolutionFailureAndProduceBoundNode(node, methodGroup, arguments, Nothing, results,
                                                                                       diagnostics, callerInfoOpt:=Nothing, queryMode:=True,
                                                                                       diagnosticLocationOpt:=Location.Create(node.SyntaxTree, operatorNameLocation))
                    End If
                Else
                    boundCall = CreateBoundCallOrPropertyAccess(node, node, TypeCharacter.None, methodGroup,
                                                                arguments, results.BestResult.Value,
                                                                results.AsyncLambdaSubToFunctionMismatch,
                                                                diagnostics)
 
                    ' May need to update return type for LambdaSymbols associated with query lambdas.
                    For i As Integer = 0 To arguments.Length - 1
                        Dim arg As BoundExpression = arguments(i)
 
                        If arg.Kind = BoundKind.QueryLambda Then
                            Dim queryLambda = DirectCast(arg, BoundQueryLambda)
 
                            If queryLambda.LambdaSymbol.ReturnType Is LambdaSymbol.ReturnTypePendingDelegate Then
                                Dim delegateReturnType As TypeSymbol = DirectCast(boundCall, BoundCall).Method.Parameters(i).Type.DelegateOrExpressionDelegate(Me).DelegateInvokeMethod.ReturnType
                                queryLambda.LambdaSymbol.SetQueryLambdaReturnType(delegateReturnType)
                            End If
                        End If
                    Next
                End If
            End If
 
            If boundCall Is Nothing Then
                Dim childBoundNodes As ImmutableArray(Of BoundExpression)
 
                If arguments.IsEmpty Then
                    childBoundNodes = ImmutableArray.Create(If(methodGroup, source))
                Else
                    Dim builder = ArrayBuilder(Of BoundExpression).GetInstance()
                    builder.Add(If(methodGroup, source))
                    builder.AddRange(arguments)
                    childBoundNodes = builder.ToImmutableAndFree()
                End If
 
                If methodGroup Is Nothing Then
                    boundCall = BadExpression(node, childBoundNodes, ErrorTypeSymbol.UnknownResultType)
                Else
                    Dim symbols = ArrayBuilder(Of Symbol).GetInstance()
                    methodGroup.GetExpressionSymbols(symbols)
 
                    Dim resultKind = LookupResultKind.OverloadResolutionFailure
                    If methodGroup.ResultKind < resultKind Then
                        resultKind = methodGroup.ResultKind
                    End If
 
                    boundCall = New BoundBadExpression(node, resultKind, symbols.ToImmutableAndFree(), childBoundNodes, ErrorTypeSymbol.UnknownResultType, hasErrors:=True)
                End If
            End If
 
            If boundCall.HasErrors AndAlso Not source.HasErrors Then
                ReportDiagnostic(diagnostics, Location.Create(node.SyntaxTree, operatorNameLocation), ERRID.ERR_QueryOperatorNotFound, operatorName)
            End If
 
            boundCall.SetWasCompilerGenerated()
 
            Return boundCall
        End Function
 
    End Class
 
End Namespace