diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxUtilities.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxUtilities.cs index 9ab4e935614027eb27efbcbbb294fe3bcc9a4263..00eba3940073c9f1aa5bd17f8385be2fbc775f67 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxUtilities.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxUtilities.cs @@ -81,7 +81,7 @@ internal static bool IsClosureScope(SyntaxNode node) } // TODO: EE expression - if (node is ExpressionSyntax && node.Parent.Parent == null) + if (node is ExpressionSyntax && node.Parent != null && node.Parent.Parent == null) { return true; } diff --git a/src/Compilers/VisualBasic/Portable/Lowering/LambdaRewriter/LambdaFrame.vb b/src/Compilers/VisualBasic/Portable/Lowering/LambdaRewriter/LambdaFrame.vb index 76372c60349c01da3fa313d60847362c6dbe40b4..22267542a5a0d6ea4ebdace0a8aafd838027f739 100644 --- a/src/Compilers/VisualBasic/Portable/Lowering/LambdaRewriter/LambdaFrame.vb +++ b/src/Compilers/VisualBasic/Portable/Lowering/LambdaRewriter/LambdaFrame.vb @@ -66,7 +66,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic _scopeSyntaxOpt = scopeSyntaxOpt End If - AssertIsLambdaScopeSyntax(_scopeSyntaxOpt) + If Not isDelegateRelaxationFrame Then + AssertIsClosureScopeSyntax(_scopeSyntaxOpt) + End If Me._typeParameters = SynthesizedClonedTypeParameterSymbol.MakeTypeParameters(topLevelMethod.TypeParameters, Me, CreateTypeParameter) Me.TypeMap = TypeSubstitution.Create(topLevelMethod, topLevelMethod.TypeParameters, Me.TypeArgumentsNoUseSiteDiagnostics) @@ -106,8 +108,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function - Private Shared Sub AssertIsLambdaScopeSyntax(syntaxOpt As VisualBasicSyntaxNode) - 'TODO: Add checks for possible syntax + Private Shared Sub AssertIsClosureScopeSyntax(syntaxOpt As VisualBasicSyntaxNode) + ' static lambdas technically have the class scope so the scope syntax is nothing + If syntaxOpt Is Nothing Then + Return + End If + + If SyntaxUtilities.IsClosureScope(syntaxOpt) Then + Return + End If + + Select Case syntaxOpt.Kind() + Case SyntaxKind.ObjectMemberInitializer + ' TODO: Closure capturing a synthesized "with" variable + Return + End Select + + ExceptionUtilities.UnexpectedValue(syntaxOpt.Kind()) End Sub Public ReadOnly Property ScopeSyntax As VisualBasicSyntaxNode diff --git a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxUtilities.vb b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxUtilities.vb index 996f3e32ad2daa608f723a070edf3ca8dcf6aaad..0bbea01e8d97a9884633bdff6fbad1b35778cb22 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/SyntaxUtilities.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/SyntaxUtilities.vb @@ -54,5 +54,100 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Throw ExceptionUtilities.Unreachable End Select End Function + + ''' + ''' Returns true if the specified node can represent a closure scope -- that is a scope of a captured variable. + ''' Doesn't validate whether or not the node actually declares any captured variable. + ''' + Friend Shared Function IsClosureScope(node As VisualBasicSyntaxNode) As Boolean + Select Case node.Kind() + Case SyntaxKind.MultiLineSubLambdaExpression, + SyntaxKind.MultiLineFunctionLambdaExpression, + SyntaxKind.SingleLineSubLambdaExpression, + SyntaxKind.SingleLineFunctionLambdaExpression + ' lambda parameters, variables defined in lambda body + Return True + + Case SyntaxKind.SubBlock, + SyntaxKind.FunctionBlock, + SyntaxKind.ConstructorBlock, + SyntaxKind.OperatorBlock, + SyntaxKind.GetAccessorBlock, + SyntaxKind.SetAccessorBlock, + SyntaxKind.AddHandlerAccessorBlock, + SyntaxKind.RemoveHandlerAccessorBlock, + SyntaxKind.RaiseEventAccessorBlock + ' parameters, variables defined in method body + ' Note: property parameters, accessor parameters and variables defined in an accessor have all the same scope (the accessor scope). + Return True + + Case SyntaxKind.WhileBlock, + SyntaxKind.ForBlock, + SyntaxKind.ForEachBlock, + SyntaxKind.SimpleDoLoopBlock, + SyntaxKind.DoWhileLoopBlock, + SyntaxKind.DoUntilLoopBlock, + SyntaxKind.DoLoopWhileBlock, + SyntaxKind.DoLoopUntilBlock, + SyntaxKind.UsingBlock, + SyntaxKind.SyncLockBlock, + SyntaxKind.WithBlock, + SyntaxKind.CaseBlock, + SyntaxKind.CaseElseBlock, + SyntaxKind.SingleLineIfStatement, + SyntaxKind.SingleLineElseClause, + SyntaxKind.MultiLineIfBlock, + SyntaxKind.ElseIfBlock, + SyntaxKind.ElseBlock, + SyntaxKind.TryBlock, + SyntaxKind.CatchBlock, + SyntaxKind.FinallyBlock + ' variable declared in a statement block + Return True + + Case SyntaxKind.SelectClause, + SyntaxKind.SimpleJoinClause, + SyntaxKind.GroupJoinClause, + SyntaxKind.GroupByClause, + SyntaxKind.AggregateClause + ' range variable captured by the clause + Return True + + Case Else + Dim parent = node.Parent + + If TypeOf node IsNot ExpressionSyntax OrElse parent Is Nothing Then + Return False + End If + + Select Case parent.Kind() + Case SyntaxKind.WhereClause, + SyntaxKind.TakeWhileClause, + SyntaxKind.SkipWhileClause, + SyntaxKind.AscendingOrdering, + SyntaxKind.DescendingOrdering + ' captured range variable by the clause + Return True + + Case SyntaxKind.FunctionAggregation, + SyntaxKind.GroupAggregation + ' range variable captured by IntoClause + Return True + + Case SyntaxKind.ExpressionRangeVariable + ' range variable captured by Let clause + Return parent.Parent IsNot Nothing AndAlso parent.Parent.IsKind(SyntaxKind.LetClause) + + End Select + + ' TODO: EE expression + If parent.Parent IsNot Nothing AndAlso + parent.Parent.Parent Is Nothing Then + Return True + End If + + Return False + End Select + End Function End Class End Namespace \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenClosureLambdaTests.vb b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenClosureLambdaTests.vb index 04e701325da753b0beec48cffead757961ede515..5e5825fdbb4fbe7de3dc235adc588ba8f0481335 100644 --- a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenClosureLambdaTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenClosureLambdaTests.vb @@ -3596,6 +3596,271 @@ End Class }, c.GetMembers().Select(Function(member) member.ToString())) End Sub) End Sub + + + Public Sub DeclarationBlockClosures() + Dim source = + + +Imports System + +Class C + Sub New(a As Integer) + Dim f = Function() a + End Sub + + Sub F1(a As Integer) + Dim f = Function() a + End Sub + + Function F2(a As Integer) As Integer + Dim f = Function() a + Return 1 + End Function + + Property F3(a As Integer) As Integer + Get + Dim f = Function() a + Return 1 + End Get + + Set(value As Integer) + Dim f1 = Function() a + Dim f2 = Function() value + End Set + End Property + + Custom Event F4 As Action + AddHandler(value As Action) + Dim f1 = Function() value + End AddHandler + + RemoveHandler(value As Action) + Dim f1 = Function() value + End RemoveHandler + + RaiseEvent() + Dim x = 1 + Dim f1 = Function() x + End RaiseEvent + End Event + + Shared Operator *(a As C, b As C) As C + Dim f1 = Function() a + Return a + End Operator + + Shared Widening Operator CType(a As C) As Integer + Dim f1 = Function() a + Return 1 + End Operator +End Class + + + CompileAndVerify(source) + End Sub + + + Public Sub StatementBlockClosures() + Dim source = + + +Imports System + +Class D + Public Q As Integer + + Shared Function Z(Of T)(f As Func(Of T)) As T + Return f() + End Function + + Sub F() + While True + Dim a = 0 + Dim f1 = Function() a + End While + + For x As Integer = 0 To 1 + Dim a = 0 + Dim f1 = Function() a + Next + + For Each x In {1} + Dim a = 0 + Dim f1 = Function() a + Next + + Do + Dim a = 0 + Dim f1 = Function() a + Loop + + Do + Dim a = 0 + Dim f1 = Function() a + Loop While True + + Do + Dim a = 0 + Dim f1 = Function() a + Loop Until True + + Do While True + Dim a = 0 + Dim f1 = Function() a + Loop + + Do Until True + Dim a = 0 + Dim f1 = Function() a + Loop + + Dim u As IDisposable = Nothing + Using u + Dim a = 0 + Dim f1 = Function() a + End Using + + SyncLock u + Dim a = 0 + Dim f1 = Function() a + End SyncLock + + With u + Dim a = 0 + Dim f1 = Function() a + End With + + Select Case Q + Case 1 + Dim a = 0 + Dim f1 = Function() a + + Case 2 + Dim a = 0 + Dim f1 = Function() a + + Case Else + Dim a = 0 + Dim f1 = Function() a + End Select + + If True Then _ + Dim a As Integer = Z(Function() a) _ + Else Dim a As Integer = Z(Function() a) + + If True Then + Dim a = 0 + Dim f1 = Function() a + ElseIf False + Dim a = 0 + Dim f1 = Function() a + Else + Dim a = 0 + Dim f1 = Function() a + End If + + Try + Dim a = 0 + Dim f1 = Function() a + Catch ex As InvalidOperationException When Z(Function() ex) IsNot Nothing + Dim a = 0 + Dim f1 = Function() a + Catch + Dim a = 0 + Dim f1 = Function() a + Finally + Dim a = 0 + Dim f1 = Function() a + End Try + End Sub +End Class + + + + CompileAndVerify(source) + End Sub + + + Public Sub ObjectMemberInitializerClosure() + Dim source = + + +Imports System + +Class C + Public Q As Integer + + Shared Function Z(Of T)(f As Func(Of T)) As T + Return f() + End Function + + Sub F() + Dim obj = New C With {.Q = Z(Function() .Q)} + End Sub +End Class + + + + CompileAndVerify(source) + End Sub + + + Public Sub QueryRangeVariableClosures() + Dim source = + + +Imports System +Imports System.Linq + +Class C + Function G(Of T)(f As Func(Of T)) As T + Return f() + End Function + + Sub F() + Dim result = From c1 In {1}, c2 In {2} + Join c3 In {3} On G(Function() c3) Equals G(Function() c1) And G(Function() c3) Equals G(Function() c2) + Join c4 In {4} On G(Function() c4) Equals G(Function() c1) And G(Function() c4) Equals G(Function() c2) + Group Join c5 In {5} On G(Function() c5) Equals G(Function() c4) Into a1 = Count(G(Function() c1)), Group + Let e3 = G(Function() a1), e4 = G(Function() Group.First()) + Group e4 = G(Function() e3), e5 = G(Function() e4 + 1) By e6 = G(Function() e3), e7 = G(Function() e4 + 2) Into a2 = Count(G(Function() e4 + 3)), a3 = LongCount(G(Function() e4 + 4)) + Aggregate c6 In {6}, c7 In {7} From c8 In {8} Select G(Function() c6 + c7 + c8) Into a4 = Sum(G(Function() e6 + 9)) + Where G(Function() e6) > 0 + Take While G(Function() e6) > 0 + Skip While G(Function() e6) > 0 + Order By G(Function() e6), G(Function() e7) + Select e8 = G(Function() a2), e9 = G(Function() a3), e10 = G(Function() a4) + End Sub +End Class + + + + CompileAndVerify(source) + End Sub + + + Public Sub QueryRangeVariableClosures_Aggregate() + Dim source = + + +Imports System +Imports System.Linq + +Class C + Function G(Of T)(f As Func(Of T)) As T + Return f() + End Function + + Sub F() + Dim result = From x In {1} Aggregate y In {2} Into Sum(x + y), z2 = Sum(x + y) + End Sub +End Class + + + + CompileAndVerify(source) + End Sub End Class End Namespace