提交 3c041e65 编写于 作者: M Manish Vasani 提交者: GitHub

Merge pull request #21356 from mavasani/QueryOperations

Add IOperation support for query operations.
......@@ -257,6 +257,8 @@ private IOperation CreateInternal(BoundNode boundNode)
return CreateBoundPatternSwitchLabelOperation((BoundPatternSwitchLabel)boundNode);
case BoundKind.IsPatternExpression:
return CreateBoundIsPatternExpressionOperation((BoundIsPatternExpression)boundNode);
case BoundKind.QueryClause:
return CreateBoundQueryClauseOperation((BoundQueryClause)boundNode);
default:
var constantValue = ConvertToOptional((boundNode as BoundExpression)?.ConstantValue);
bool isImplicit = boundNode.WasCompilerGenerated;
......@@ -1584,5 +1586,21 @@ private IIsPatternExpression CreateBoundIsPatternExpressionOperation(BoundIsPatt
bool isImplicit = boundIsPatternExpression.WasCompilerGenerated;
return new LazyIsPatternExpression(expression, pattern, _semanticModel, syntax, type, constantValue, isImplicit);
}
private IOperation CreateBoundQueryClauseOperation(BoundQueryClause boundQueryClause)
{
if (boundQueryClause.Syntax.Kind() != SyntaxKind.QueryExpression)
{
// Currently we have no IOperation APIs for different query clauses or continuation.
return Create(boundQueryClause.Value);
}
Lazy<IOperation> expression = new Lazy<IOperation>(() => Create(boundQueryClause.Value));
SyntaxNode syntax = boundQueryClause.Syntax;
ITypeSymbol type = boundQueryClause.Type;
Optional<object> constantValue = ConvertToOptional(boundQueryClause.ConstantValue);
bool isImplicit = boundQueryClause.WasCompilerGenerated;
return new LazyTranslatedQueryExpression(expression, _semanticModel, syntax, type, constantValue, isImplicit);
}
}
}
......@@ -143,31 +143,26 @@ public void M(List<Customer> customers)
}
";
string expectedOperationTree = @"
IOperation: (OperationKind.None) (Syntax: 'from cust i ... t cust.Name')
Children(1):
IOperation: (OperationKind.None) (Syntax: 'select cust.Name')
Children(1):
IInvocationExpression (System.Collections.Generic.IEnumerable<System.String> System.Linq.Enumerable.Select<Customer, System.String>(this System.Collections.Generic.IEnumerable<Customer> source, System.Func<Customer, System.String> selector)) (OperationKind.InvocationExpression, Type: System.Collections.Generic.IEnumerable<System.String>) (Syntax: 'select cust.Name')
Instance Receiver: null
Arguments(2):
IArgument (ArgumentKind.Explicit, Matching Parameter: source) (OperationKind.Argument) (Syntax: 'from cust in customers')
IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.Collections.Generic.IEnumerable<Customer>) (Syntax: 'from cust in customers')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
Operand: IOperation: (OperationKind.None) (Syntax: 'from cust in customers')
Children(1):
IParameterReferenceExpression: customers (OperationKind.ParameterReferenceExpression, Type: System.Collections.Generic.List<Customer>) (Syntax: 'customers')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgument (ArgumentKind.Explicit, Matching Parameter: selector) (OperationKind.Argument) (Syntax: 'cust.Name')
IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.Func<Customer, System.String>) (Syntax: 'cust.Name')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand: IAnonymousFunctionExpression (Symbol: lambda expression) (OperationKind.AnonymousFunctionExpression, Type: null) (Syntax: 'cust.Name')
IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: 'cust.Name')
IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'cust.Name')
ReturnedValue: IPropertyReferenceExpression: System.String Customer.Name { get; set; } (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'cust.Name')
Instance Receiver: IOperation: (OperationKind.None) (Syntax: 'cust')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
ITranslatedQueryExpression (OperationKind.TranslatedQueryExpression, Type: System.Collections.Generic.IEnumerable<System.String>) (Syntax: 'from cust i ... t cust.Name')
Expression: IInvocationExpression (System.Collections.Generic.IEnumerable<System.String> System.Linq.Enumerable.Select<Customer, System.String>(this System.Collections.Generic.IEnumerable<Customer> source, System.Func<Customer, System.String> selector)) (OperationKind.InvocationExpression, Type: System.Collections.Generic.IEnumerable<System.String>) (Syntax: 'select cust.Name')
Instance Receiver: null
Arguments(2):
IArgument (ArgumentKind.Explicit, Matching Parameter: source) (OperationKind.Argument) (Syntax: 'from cust in customers')
IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.Collections.Generic.IEnumerable<Customer>) (Syntax: 'from cust in customers')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null)
Operand: IParameterReferenceExpression: customers (OperationKind.ParameterReferenceExpression, Type: System.Collections.Generic.List<Customer>) (Syntax: 'customers')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgument (ArgumentKind.Explicit, Matching Parameter: selector) (OperationKind.Argument) (Syntax: 'cust.Name')
IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.Func<Customer, System.String>) (Syntax: 'cust.Name')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand: IAnonymousFunctionExpression (Symbol: lambda expression) (OperationKind.AnonymousFunctionExpression, Type: null) (Syntax: 'cust.Name')
IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: 'cust.Name')
IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'cust.Name')
ReturnedValue: IPropertyReferenceExpression: System.String Customer.Name { get; set; } (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'cust.Name')
Instance Receiver: IOperation: (OperationKind.None) (Syntax: 'cust')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
";
var expectedDiagnostics = DiagnosticDescription.None;
......
......@@ -5866,4 +5866,78 @@ internal sealed partial class LazyCollectionElementInitializerExpression : BaseC
protected override ImmutableArray<IOperation> ArgumentsImpl => _lazyArguments.Value;
}
/// <summary>
/// Represents an unrolled/lowered query expression in C# and VB.
/// For example, for the query expression "from x in set where x.Name != null select x.Name", the Operation tree has the following shape:
/// ITranslatedQueryExpression
/// IInvocationExpression ('Select' invocation for "select x.Name")
/// IInvocationExpression ('Where' invocation for "where x.Name != null")
/// IInvocationExpression ('From' invocation for "from x in set")
/// </summary>
internal abstract partial class BaseTranslatedQueryExpression : Operation, ITranslatedQueryExpression
{
protected BaseTranslatedQueryExpression(SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue, bool isImplicit) :
base(OperationKind.TranslatedQueryExpression, semanticModel, syntax, type, constantValue, isImplicit)
{
}
protected abstract IOperation ExpressionImpl { get; }
/// <summary>
/// Underlying unrolled expression.
/// </summary>
public IOperation Expression => Operation.SetParentOperation(ExpressionImpl, this);
public override IEnumerable<IOperation> Children
{
get
{
yield return Expression;
}
}
public override void Accept(OperationVisitor visitor)
{
visitor.VisitTranslatedQueryExpression(this);
}
public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument)
{
return visitor.VisitTranslatedQueryExpression(this, argument);
}
}
/// <summary>
/// Represents an unrolled/lowered query expression in C# and VB.
/// For example, for the query expression "from x in set where x.Name != null select x.Name", the Operation tree has the following shape:
/// ITranslatedQueryExpression
/// IInvocationExpression ('Select' invocation for "select x.Name")
/// IInvocationExpression ('Where' invocation for "where x.Name != null")
/// IInvocationExpression ('From' invocation for "from x in set")
/// </summary>
internal sealed partial class TranslatedQueryExpression : BaseTranslatedQueryExpression, ITranslatedQueryExpression
{
public TranslatedQueryExpression(IOperation expression, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue, bool isImplicit) :
base(semanticModel, syntax, type, constantValue, isImplicit)
{
ExpressionImpl = expression;
}
protected override IOperation ExpressionImpl { get; }
}
/// <summary>
/// Represents an unrolled/lowered query expression in C# and VB.
/// For example, for the query expression "from x in set where x.Name != null select x.Name", the Operation tree has the following shape:
/// ITranslatedQueryExpression
/// IInvocationExpression ('Select' invocation for "select x.Name")
/// IInvocationExpression ('Where' invocation for "where x.Name != null")
/// IInvocationExpression ('From' invocation for "from x in set")
/// </summary>
internal sealed partial class LazyTranslatedQueryExpression : BaseTranslatedQueryExpression, ITranslatedQueryExpression
{
private readonly Lazy<IOperation> _lazyExpression;
public LazyTranslatedQueryExpression(Lazy<IOperation> expression, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue, bool isImplicit) :
base(semanticModel, syntax, type, constantValue, isImplicit)
{
_lazyExpression = expression ?? throw new System.ArgumentNullException(nameof(expression));
}
protected override IOperation ExpressionImpl => _lazyExpression.Value;
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Semantics
{
/// <summary>
/// Represents an unrolled/lowered query expression in C# and VB.
/// For example, for the query expression "from x in set where x.Name != null select x.Name", the Operation tree has the following shape:
/// ITranslatedQueryExpression
/// IInvocationExpression ('Select' invocation for "select x.Name")
/// IInvocationExpression ('Where' invocation for "where x.Name != null")
/// IInvocationExpression ('From' invocation for "from x in set")
/// </summary>
/// <remarks>
/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface ITranslatedQueryExpression : IOperation
{
/// <summary>
/// Underlying unrolled expression.
/// </summary>
IOperation Expression { get; }
}
}
......@@ -475,5 +475,10 @@ public override IOperation VisitTupleExpression(ITupleExpression operation, obje
{
return new TupleExpression(VisitArray(operation.Elements), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit);
}
public override IOperation VisitTranslatedQueryExpression(ITranslatedQueryExpression operation, object argument)
{
return new TranslatedQueryExpression(Visit(operation.Expression), ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit);
}
}
}
......@@ -151,6 +151,8 @@ public enum OperationKind
DynamicInvocationExpression = 0x127,
/// <summary>Indicates an <see cref="IDynamicIndexerAccessExpression"/>.</summary>
DynamicIndexerAccessExpression = 0x128,
/// <summary>Indicates an <see cref="ITranslatedQueryExpression"/>.</summary>
TranslatedQueryExpression = 0x129,
// Expressions that occur only in C#.
......@@ -179,7 +181,9 @@ public enum OperationKind
/// <summary>Indicates an <see cref="IOmittedArgumentExpression"/>.</summary>
OmittedArgumentExpression = 0x300,
// 0x301 was removed, and is available for use.
// https://github.com/dotnet/roslyn/issues/21294
// /// <summary>Indicates an <see cref="IPlaceholderExpression"/>.</summary>
// PlaceholderExpression = 0x302,
......
......@@ -485,6 +485,11 @@ public virtual void VisitTupleExpression(ITupleExpression operation)
{
DefaultVisit(operation);
}
public virtual void VisitTranslatedQueryExpression(ITranslatedQueryExpression operation)
{
DefaultVisit(operation);
}
}
/// <summary>
......@@ -976,5 +981,10 @@ public virtual TResult VisitTupleExpression(ITupleExpression operation, TArgumen
{
return DefaultVisit(operation, argument);
}
public virtual TResult VisitTranslatedQueryExpression(ITranslatedQueryExpression operation, TArgument argument)
{
return DefaultVisit(operation, argument);
}
}
}
......@@ -114,6 +114,7 @@ Microsoft.CodeAnalysis.OperationKind.SizeOfExpression = 514 -> Microsoft.CodeAna
Microsoft.CodeAnalysis.OperationKind.SwitchCase = 1033 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.SwitchStatement = 4 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.ThrowExpression = 519 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.TranslatedQueryExpression = 297 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.TupleExpression = 292 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.TypeOfExpression = 513 -> Microsoft.CodeAnalysis.OperationKind
Microsoft.CodeAnalysis.OperationKind.TypeParameterObjectCreationExpression = 275 -> Microsoft.CodeAnalysis.OperationKind
......@@ -405,6 +406,8 @@ Microsoft.CodeAnalysis.Semantics.ISymbolInitializer
Microsoft.CodeAnalysis.Semantics.ISymbolInitializer.Value.get -> Microsoft.CodeAnalysis.IOperation
Microsoft.CodeAnalysis.Semantics.IThrowExpression
Microsoft.CodeAnalysis.Semantics.IThrowExpression.Expression.get -> Microsoft.CodeAnalysis.IOperation
Microsoft.CodeAnalysis.Semantics.ITranslatedQueryExpression
Microsoft.CodeAnalysis.Semantics.ITranslatedQueryExpression.Expression.get -> Microsoft.CodeAnalysis.IOperation
Microsoft.CodeAnalysis.Semantics.ITupleExpression
Microsoft.CodeAnalysis.Semantics.ITupleExpression.Elements.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.IOperation>
Microsoft.CodeAnalysis.Semantics.ITypeOfExpression
......@@ -556,6 +559,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSizeOfExpression(
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSwitchCase(Microsoft.CodeAnalysis.Semantics.ISwitchCase operation) -> void
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitSwitchStatement(Microsoft.CodeAnalysis.Semantics.ISwitchStatement operation) -> void
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitThrowExpression(Microsoft.CodeAnalysis.Semantics.IThrowExpression operation) -> void
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTranslatedQueryExpression(Microsoft.CodeAnalysis.Semantics.ITranslatedQueryExpression operation) -> void
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTupleExpression(Microsoft.CodeAnalysis.Semantics.ITupleExpression operation) -> void
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeOfExpression(Microsoft.CodeAnalysis.Semantics.ITypeOfExpression operation) -> void
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitTypeParameterObjectCreationExpression(Microsoft.CodeAnalysis.Semantics.ITypeParameterObjectCreationExpression operation) -> void
......@@ -638,6 +642,7 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.Vi
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitSwitchCase(Microsoft.CodeAnalysis.Semantics.ISwitchCase operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitSwitchStatement(Microsoft.CodeAnalysis.Semantics.ISwitchStatement operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitThrowExpression(Microsoft.CodeAnalysis.Semantics.IThrowExpression operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitTranslatedQueryExpression(Microsoft.CodeAnalysis.Semantics.ITranslatedQueryExpression operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitTupleExpression(Microsoft.CodeAnalysis.Semantics.ITupleExpression operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitTypeOfExpression(Microsoft.CodeAnalysis.Semantics.ITypeOfExpression operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitTypeParameterObjectCreationExpression(Microsoft.CodeAnalysis.Semantics.ITypeParameterObjectCreationExpression operation, TArgument argument) -> TResult
......
......@@ -40,11 +40,47 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim originalMethodOrLambda = Me._currentMethodOrLambda
Me._currentMethodOrLambda = node.LambdaSymbol
PopulateRangeVariableMapForQueryLambdaRewrite(node, _rangeVariableMap, _inExpressionLambda)
Dim save_createSequencePointsForTopLevelNonCompilerGeneratedExpressions = _instrumentTopLevelNonCompilerGeneratedExpressionsInQuery
Dim synthesizedKind As SynthesizedLambdaKind = node.LambdaSymbol.SynthesizedKind
Dim instrumentQueryLambdaBody As Boolean = synthesizedKind = SynthesizedLambdaKind.AggregateQueryLambda OrElse
synthesizedKind = SynthesizedLambdaKind.LetVariableQueryLambda
_instrumentTopLevelNonCompilerGeneratedExpressionsInQuery = Not instrumentQueryLambdaBody
Dim rewrittenBody As BoundExpression = VisitExpressionNode(node.Expression)
Dim returnstmt = CreateReturnStatementForQueryLambdaBody(rewrittenBody, node)
If instrumentQueryLambdaBody AndAlso Instrument Then
returnstmt = _instrumenterOpt.InstrumentQueryLambdaBody(node, returnstmt)
End If
RemoveRangeVariables(node, _rangeVariableMap)
_instrumentTopLevelNonCompilerGeneratedExpressionsInQuery = save_createSequencePointsForTopLevelNonCompilerGeneratedExpressions
Me._hasLambdas = True
Dim result As BoundLambda = RewriteQueryLambda(returnstmt, node)
' Done with lambda body rewrite, restore current lambda.
' END LAMBDA REWRITE
Me._currentMethodOrLambda = originalMethodOrLambda
Return result
End Function
Friend Shared Sub PopulateRangeVariableMapForQueryLambdaRewrite(
node As BoundQueryLambda,
ByRef rangeVariableMap As Dictionary(Of RangeVariableSymbol, BoundExpression),
inExpressionLambda As Boolean)
Dim nodeRangeVariables As ImmutableArray(Of RangeVariableSymbol) = node.RangeVariables
If nodeRangeVariables.Length > 0 Then
If _rangeVariableMap Is Nothing Then
_rangeVariableMap = New Dictionary(Of RangeVariableSymbol, BoundExpression)()
If rangeVariableMap Is Nothing Then
rangeVariableMap = New Dictionary(Of RangeVariableSymbol, BoundExpression)()
End If
Dim firstUnmappedRangeVariable As Integer = 0
......@@ -64,78 +100,40 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
parameter.Type)
If isReservedName AndAlso Not String.Equals(parameterName, StringConstants.Group, StringComparison.Ordinal) Then
' Compound variable.
' Each range variable is an Anonymous Type property.
Debug.Assert(parameterName.Equals(StringConstants.It) OrElse parameterName.Equals(StringConstants.It1) OrElse parameterName.Equals(StringConstants.It2))
PopulateRangeVariableMapForAnonymousType(node.Syntax, paramRef, nodeRangeVariables, firstUnmappedRangeVariable)
If parameter.Type.IsErrorType() Then
' Skip adding variables to the range variable map and bail out for error case.
Return
Else
' Compound variable.
' Each range variable is an Anonymous Type property.
Debug.Assert(parameterName.Equals(StringConstants.It) OrElse parameterName.Equals(StringConstants.It1) OrElse parameterName.Equals(StringConstants.It2))
PopulateRangeVariableMapForAnonymousType(node.Syntax, paramRef, nodeRangeVariables, firstUnmappedRangeVariable, rangeVariableMap, inExpressionLambda)
End If
Else
' Simple case, range variable is a lambda parameter.
Debug.Assert(IdentifierComparison.Equals(parameterName, nodeRangeVariables(firstUnmappedRangeVariable).Name))
_rangeVariableMap.Add(nodeRangeVariables(firstUnmappedRangeVariable), paramRef)
rangeVariableMap.Add(nodeRangeVariables(firstUnmappedRangeVariable), paramRef)
firstUnmappedRangeVariable += 1
End If
Next
Debug.Assert(firstUnmappedRangeVariable = nodeRangeVariables.Length)
End If
End Sub
Dim save_createSequencePointsForTopLevelNonCompilerGeneratedExpressions = _instrumentTopLevelNonCompilerGeneratedExpressionsInQuery
Dim synthesizedKind As SynthesizedLambdaKind = node.LambdaSymbol.SynthesizedKind
Dim instrumentQueryLambdaBody As Boolean = synthesizedKind = SynthesizedLambdaKind.AggregateQueryLambda OrElse
synthesizedKind = SynthesizedLambdaKind.LetVariableQueryLambda
_instrumentTopLevelNonCompilerGeneratedExpressionsInQuery = Not instrumentQueryLambdaBody
Dim returnstmt As BoundStatement = New BoundReturnStatement(node.Syntax,
VisitExpressionNode(node.Expression),
Nothing,
Nothing)
If instrumentQueryLambdaBody AndAlso Instrument Then
returnstmt = _instrumenterOpt.InstrumentQueryLambdaBody(node, returnstmt)
End If
_instrumentTopLevelNonCompilerGeneratedExpressionsInQuery = save_createSequencePointsForTopLevelNonCompilerGeneratedExpressions
For Each rangeVar As RangeVariableSymbol In nodeRangeVariables
_rangeVariableMap.Remove(rangeVar)
Next
Dim lambdaBody = New BoundBlock(node.Syntax,
Nothing,
ImmutableArray(Of LocalSymbol).Empty,
ImmutableArray.Create(returnstmt))
Me._hasLambdas = True
Dim result As BoundLambda = New BoundLambda(node.Syntax,
node.LambdaSymbol,
lambdaBody,
ImmutableArray(Of Diagnostic).Empty,
Nothing,
ConversionKind.DelegateRelaxationLevelNone,
MethodConversionKind.Identity)
result.MakeCompilerGenerated()
' Done with lambda body rewrite, restore current lambda.
' END LAMBDA REWRITE
Me._currentMethodOrLambda = originalMethodOrLambda
Return result
End Function
Private Sub PopulateRangeVariableMapForAnonymousType(
Private Shared Sub PopulateRangeVariableMapForAnonymousType(
syntax As SyntaxNode,
anonymousTypeInstance As BoundExpression,
rangeVariables As ImmutableArray(Of RangeVariableSymbol),
ByRef firstUnmappedRangeVariable As Integer
)
ByRef firstUnmappedRangeVariable As Integer,
rangeVariableMap As Dictionary(Of RangeVariableSymbol, BoundExpression),
inExpressionLambda As Boolean)
Dim anonymousType = DirectCast(anonymousTypeInstance.Type, AnonymousTypeManager.AnonymousTypePublicSymbol)
For Each propertyDef As PropertySymbol In anonymousType.Properties
Dim getCallOrPropertyAccess As BoundExpression = Nothing
If _inExpressionLambda Then
If inExpressionLambda Then
' NOTE: If we are in context of a lambda to be converted to an expression tree we need to use PropertyAccess.
getCallOrPropertyAccess = New BoundPropertyAccess(syntax,
propertyDef,
......@@ -162,16 +160,50 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
If propertyDefName.StartsWith("$"c, StringComparison.Ordinal) AndAlso Not String.Equals(propertyDefName, StringConstants.Group, StringComparison.Ordinal) Then
' Nested compound variable.
Debug.Assert(propertyDefName.Equals(StringConstants.It) OrElse propertyDefName.Equals(StringConstants.It1) OrElse propertyDefName.Equals(StringConstants.It2))
PopulateRangeVariableMapForAnonymousType(syntax, getCallOrPropertyAccess, rangeVariables, firstUnmappedRangeVariable)
PopulateRangeVariableMapForAnonymousType(syntax, getCallOrPropertyAccess, rangeVariables, firstUnmappedRangeVariable, rangeVariableMap, inExpressionLambda)
Else
Debug.Assert(IdentifierComparison.Equals(propertyDefName, rangeVariables(firstUnmappedRangeVariable).Name))
_rangeVariableMap.Add(rangeVariables(firstUnmappedRangeVariable), getCallOrPropertyAccess)
rangeVariableMap.Add(rangeVariables(firstUnmappedRangeVariable), getCallOrPropertyAccess)
firstUnmappedRangeVariable += 1
End If
Next
End Sub
Friend Shared Function CreateReturnStatementForQueryLambdaBody(
rewrittenBody As BoundExpression,
originalNode As BoundQueryLambda) As BoundStatement
Return New BoundReturnStatement(originalNode.Syntax,
rewrittenBody,
Nothing,
Nothing)
End Function
Friend Shared Sub RemoveRangeVariables(originalNode As BoundQueryLambda, rangeVariableMap As Dictionary(Of RangeVariableSymbol, BoundExpression))
For Each rangeVar As RangeVariableSymbol In originalNode.RangeVariables
rangeVariableMap.Remove(rangeVar)
Next
End Sub
Friend Shared Function RewriteQueryLambda(rewrittenBody As BoundStatement, originalNode As BoundQueryLambda) As BoundLambda
Dim lambdaBody = New BoundBlock(originalNode.Syntax,
Nothing,
ImmutableArray(Of LocalSymbol).Empty,
ImmutableArray.Create(rewrittenBody))
Dim result As BoundLambda = New BoundLambda(originalNode.Syntax,
originalNode.LambdaSymbol,
lambdaBody,
ImmutableArray(Of Diagnostic).Empty,
Nothing,
ConversionKind.DelegateRelaxationLevelNone,
MethodConversionKind.Identity)
result.MakeCompilerGenerated()
Return result
End Function
Public Overrides Function VisitRangeVariable(node As BoundRangeVariable) As BoundNode
Return _rangeVariableMap(node.RangeVariable)
End Function
......
......@@ -26,11 +26,12 @@ Namespace Microsoft.CodeAnalysis.Semantics
' this should be removed once this issue is fixed
' https://github.com/dotnet/roslyn/issues/21186
If TypeOf boundNode Is BoundValuePlaceholderBase Then
' since same place holder bound node appears in multiple places in the tree
' https://github.com/dotnet/roslyn/issues/21554
If TypeOf boundNode Is BoundValuePlaceholderBase OrElse
(TypeOf boundNode Is BoundParameter AndAlso boundNode.WasCompilerGenerated) Then
' since same bound node appears in multiple places in the tree
' we can't use bound node to operation map.
' for now, we will just create new operation and return clone but we need to figure out
' what we want to do with place holder node such as just returning nothing
' for now, we will just create new operation and return cloned
Return _semanticModel.CloneOperation(CreateInternal(boundNode))
End If
......@@ -238,6 +239,36 @@ Namespace Microsoft.CodeAnalysis.Semantics
Return Create(DirectCast(boundNode, BoundAnonymousTypeFieldInitializer).Value)
Case BoundKind.AnonymousTypePropertyAccess
Return CreateBoundAnonymousTypePropertyAccessOperation(DirectCast(boundNode, BoundAnonymousTypePropertyAccess))
Case BoundKind.QueryExpression
Return CreateBoundQueryExpressionOperation(DirectCast(boundNode, BoundQueryExpression))
Case BoundKind.QueryClause
' Query clause has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundQueryClause).UnderlyingExpression)
Case BoundKind.QueryableSource
' Queryable source has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundQueryableSource).Source)
Case BoundKind.AggregateClause
' Aggregate clause has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundAggregateClause).UnderlyingExpression)
Case BoundKind.Ordering
' Ordering clause has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundOrdering).UnderlyingExpression)
Case BoundKind.GroupAggregation
' Group aggregation has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundGroupAggregation).Group)
Case BoundKind.QuerySource
' Query source has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundQuerySource).Expression)
Case BoundKind.ToQueryableCollectionConversion
' Queryable collection conversion has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundToQueryableCollectionConversion).ConversionCall)
Case BoundKind.QueryLambda
' Query lambda must be lowered to the regular lambda form for the operation tree.
Dim rewrittenLambda As BoundNode = RewriteQueryLambda(DirectCast(boundNode, BoundQueryLambda))
Return Create(rewrittenLambda)
Case BoundKind.RangeVariableAssignment
' Range variable assignment has no special representation in the IOperation tree
Return Create(DirectCast(boundNode, BoundRangeVariableAssignment).Value)
Case Else
Dim constantValue = ConvertToOptional(TryCast(boundNode, BoundExpression)?.ConstantValueOpt)
Dim isImplicit As Boolean = boundNode.WasCompilerGenerated
......@@ -1351,6 +1382,15 @@ Namespace Microsoft.CodeAnalysis.Semantics
Dim isImplicit As Boolean = boundAnonymousTypePropertyAccess.WasCompilerGenerated
Return New LazyPropertyReferenceExpression([property], instance, [property], argumentsInEvaluationOrder, _semanticModel, syntax, type, constantValue, isImplicit)
End Function
Private Function CreateBoundQueryExpressionOperation(boundQueryExpression As BoundQueryExpression) As IOperation
Dim expression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(boundQueryExpression.LastOperator))
Dim syntax As SyntaxNode = boundQueryExpression.Syntax
Dim type As ITypeSymbol = boundQueryExpression.Type
Dim constantValue As [Optional](Of Object) = ConvertToOptional(boundQueryExpression.ConstantValueOpt)
Dim isImplicit As Boolean = boundQueryExpression.WasCompilerGenerated
Return New LazyTranslatedQueryExpression(expression, _semanticModel, syntax, type, constantValue, isImplicit)
End Function
End Class
End Namespace
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.BoundTreeVisitor
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Namespace Microsoft.CodeAnalysis.Semantics
Partial Friend NotInheritable Class VisualBasicOperationFactory
Private Shared Function RewriteQueryLambda(node As BoundQueryLambda) As BoundNode
' We rewrite query lambda into regular lambda with 2 passes.
' Pass 1 uses helper methods from LocalRewriter to do the lowering. This introduces large number of DAGs.
' Pass 2 walks over the lowered tree and replaces duplicate bound nodes in the tree with their clones - this is a requirement for the Operation tree.
' Note that the rewriter also rewrites all the query lambdas inside the body of this query lambda.
Try
Dim pass1Rewriter As New QueryLambdaRewriterPass1
Dim rewrittenLambda As BoundLambda = DirectCast(pass1Rewriter.VisitQueryLambda(node), BoundLambda)
Dim pass2Rewriter As New QueryLambdaRewriterPass2
Return pass2Rewriter.VisitLambda(rewrittenLambda)
Catch ex As CancelledByStackGuardException
Return node
End Try
End Function
Private NotInheritable Class QueryLambdaRewriterPass1
Inherits BoundTreeRewriterWithStackGuard
Private _rangeVariableMap As Dictionary(Of RangeVariableSymbol, BoundExpression)
Public Sub New()
_rangeVariableMap = Nothing
End Sub
Public Overrides Function VisitQueryLambda(node As BoundQueryLambda) As BoundNode
LocalRewriter.PopulateRangeVariableMapForQueryLambdaRewrite(node, _rangeVariableMap, inExpressionLambda:=True)
Dim rewrittenBody As BoundExpression = VisitExpressionWithStackGuard(node.Expression)
Dim rewrittenStatement As BoundStatement = LocalRewriter.CreateReturnStatementForQueryLambdaBody(rewrittenBody, node)
LocalRewriter.RemoveRangeVariables(node, _rangeVariableMap)
Return LocalRewriter.RewriteQueryLambda(rewrittenStatement, node)
End Function
Public Overrides Function VisitRangeVariable(node As BoundRangeVariable) As BoundNode
Dim expression As BoundExpression = Nothing
If Not _rangeVariableMap.TryGetValue(node.RangeVariable, expression) Then
' _rangeVariableMap might not contain an entry for the range variable for error cases.
Return node
End If
#If DEBUG Then
' Range variable reference should be rewritten to a parameter reference or a property access.
' We clone these bound nodes in QueryLambdaRewriterPass2 to avoid dag in the generated bound tree.
' If the LocalRewriter is changed to generate more kind of bound nodes for range variables, we should handle these in QueryLambdaRewriterPass2.
' Below assert helps us to stay in sync with the LocalRewriter.
Select Case expression.Kind
Case BoundKind.Parameter
Case BoundKind.PropertyAccess
Exit Select
Case Else
Debug.Fail($"Unexpected bound kind '{expression.Kind}' generated for range variable rewrite by method '{NameOf(LocalRewriter.PopulateRangeVariableMapForQueryLambdaRewrite)}'")
End Select
#End If
Return expression
End Function
End Class
Private NotInheritable Class QueryLambdaRewriterPass2
Inherits BoundTreeRewriterWithStackGuard
Private ReadOnly _uniqueNodes As New HashSet(Of BoundParameter)
Public Overrides Function VisitParameter(node As BoundParameter) As BoundNode
If node.ParameterSymbol?.ContainingSymbol.IsQueryLambdaMethod AndAlso Not _uniqueNodes.Add(node) Then
node = New BoundParameter(node.Syntax, node.ParameterSymbol, node.IsLValue, node.SuppressVirtualCalls, node.Type, node.HasErrors)
End If
Return node
End Function
End Class
End Class
End Namespace
......@@ -476,7 +476,7 @@ BC30518: Overload resolution failed because no accessible 'P1' can be called wit
End Sub
<CompilerTrait(CompilerFeature.IOperation)>
<Fact>
<Fact(Skip:="https://github.com/dotnet/roslyn/issues/22224")>
Public Sub TestClone()
Dim sourceCode = TestResource.AllInOneVisualBasicCode
......@@ -491,7 +491,7 @@ BC30518: Overload resolution failed because no accessible 'P1' can be called wit
End Sub
<CompilerTrait(CompilerFeature.IOperation)>
<Fact>
<Fact(Skip:="https://github.com/dotnet/roslyn/issues/22224")>
Public Sub TestParentOperations()
Dim sourceCode = TestResource.AllInOneVisualBasicCode
......
......@@ -1432,6 +1432,14 @@ public override void VisitPatternCaseClause(IPatternCaseClause operation)
Visit(operation.GuardExpression, "Guard Expression");
}
public override void VisitTranslatedQueryExpression(ITranslatedQueryExpression operation)
{
LogString(nameof(ITranslatedQueryExpression));
LogCommonPropertiesAndNewLine(operation);
Visit(operation.Expression, "Expression");
}
#endregion
}
}
......@@ -638,5 +638,10 @@ public override void VisitPatternCaseClause(IPatternCaseClause operation)
base.VisitPatternCaseClause(operation);
}
public override void VisitTranslatedQueryExpression(ITranslatedQueryExpression operation)
{
base.VisitTranslatedQueryExpression(operation);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册