Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IOperation support for query operations. #21356

Merged
merged 13 commits into from
Sep 20, 2017

Conversation

mavasani
Copy link
Contributor

@mavasani mavasani commented Aug 8, 2017

Implements the proposal discussed in #17838 (comment).

  1. We will have an IQueryExpression representing the topmost query expression.
  2. This will point to the last query clause (IQueryClause) or continuation (IQueryContinuation) in the unrolled lowered bound tree - we are not got going to reverse the bound tree to match source.
  3. IQueryClause will have a QueryClauseKind field indicating the type of query clause, which should match the syntax/language specification query clause/operator kinds.

Implements the proposal discussed in dotnet#17838 (comment).

1. We will have an `IQueryExpression` representing the topmost query expression.
2. This will point to the last query clause (`IQueryClause`) or continuation (`IQueryContinuation`) in the unrolled lowered bound tree - we are not got going to reverse the tree to match source.
3. `IQueryClause` will have a `QueryClauseKind` field indicating the type of query clause, which should match the syntax/language specification query clause/operator kinds.
@mavasani
Copy link
Contributor Author

mavasani commented Aug 8, 2017

FYI: There are still merge conflicts and I need to merge Heejae's parent pointer change into this branch, so the tests are expected to fail.

I am sending out this PR early to start the review process. #Closed

@mavasani
Copy link
Contributor Author

mavasani commented Aug 8, 2017

All merge conflicts have now been resolved. #Closed

return new LazyJoinIntoQueryClause(underlyingExpression, _semanticModel, syntax, type, constantValue);

default:
throw ExceptionUtilities.Unreachable;
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw UnexpectedValue(queryClauseKind) #Closed


private IOperation CreateBoundQueryOrContinuationOrOrderExpressionOperation(BoundQueryClause boundQueryClause)
{
switch(boundQueryClause.Syntax.Kind())
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does one use the syntax kind, and the other uses a different kind? #Closed

return CreateBoundOrderingExpressionOperation(boundQueryClause, OrderKind.Descending);

case SyntaxKind.QueryBody:
return boundQueryClause.Value != null ? Create((BoundQueryClause)boundQueryClause.Value) : null;
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do you get nul lhere? #Closed

Copy link
Contributor Author

@mavasani mavasani Aug 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was just a defensive guard, I'll remove it unless any test breaks. #Closed

return boundQueryClause.Value != null ? Create((BoundQueryClause)boundQueryClause.Value) : null;

default:
throw ExceptionUtilities.Unreachable;
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw UnexpecteValue #Closed


private QueryClauseKind? GetQueryClauseKind(BoundQueryClause boundQueryClause)
{
switch(boundQueryClause.Syntax.Kind())
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you use map some syntax kins to query clause kinds here. why not map them all? #Closed

Copy link
Contributor Author

@mavasani mavasani Aug 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will pull all of the syntax kind comparisons into a single switch statement. #Closed

/// <summary>
/// Underlying reduced expression for the query clause. This is normally the invocation expression for the underlying linq call.
/// </summary>
IOperation ReducedExpression { get; }
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider 'TranslatedExpression' #Closed

Copy link
Contributor Author

@mavasani mavasani Aug 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will change. #Closed

/// For example, for the query expression "from x in set where x.Name != null select x.Name", the select clause is the last clause of the unrolled query expression,
/// with the where clause as one of its descendant, and the from clause as the descendant of the where clause.
/// </summary>
IOperation LastClauseOrContinuation { get; }
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you might want to give an example with a continuation. #Closed

/// <summary>
/// Represents no ordering for an <see cref="IOrderingExpression"/>.
/// </summary>
None = 0x0,
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this possible? seems unlikely. if not, i would recommend removing enum and just having a bool "IsDescending" #Closed

Copy link
Contributor Author

@mavasani mavasani Aug 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have matched the API of other XXXKind enums for the IOperation APIs. Let me open a separate issue to tackle all the None kinds. #Closed

Ascending = 0x1,

/// <summary>
/// Represents an ascending ordering for an <see cref="IOrderingExpression"/>.
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'descending ordering' #Closed

public enum QueryClauseKind
{
/// <summary> Indicates an invalid clause kind.</summary>
None = 0x00,
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this possible? #Closed

@mavasani
Copy link
Contributor Author

mavasani commented Aug 21, 2017

ping @dotnet/roslyn-compiler for review #Closed

1 similar comment
@mavasani
Copy link
Contributor Author

mavasani commented Aug 23, 2017

ping @dotnet/roslyn-compiler for review #Closed

/// <summary>Indicates an <see cref="IQueryExpression"/>.</summary>
QueryExpression = 0x127,
/// <summary>Indicates an <see cref="IOrderingExpression"/> in an <see cref="IOrderByQueryClause"/>.</summary>
OrderingExpression = 0x128,
Copy link
Contributor

@AlekseyTs AlekseyTs Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OrderingExpression [](start = 8, length = 18)

I am not sure why Ordering is singled out as an expression. What makes it special by comparison to other clauses? Can ordering be stand-alone, without query expression? #Closed

@@ -216,5 +221,10 @@ public enum OperationKind

/// <summary>Indicates an <see cref="IDefaultCaseClause"/>.</summary>
DefaultCaseClause = 0x412,

/// <summary>Indicates an <see cref="IQueryClause"/>.</summary>
QueryClause = 0x413,
Copy link
Contributor

@AlekseyTs AlekseyTs Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QueryClause = 0x413, [](start = 8, length = 20)

Is this kind ever used? #Closed

/// This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.
/// </remarks>
public interface IQueryContinuation : IOperation
Copy link
Contributor

@AlekseyTs AlekseyTs Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IQueryContinuation [](start = 21, length = 18)

Should we treat IQueryContinuation as just another query clause? Then we might be able to strongly type QueryBody as IQueryClause and probably can do the same for IQueryExpression.LastClauseOrContinuation. #Closed

QueryClauseKind ClauseKind { get; }

/// <summary>
/// Underlying reduced expression for the query clause. This is normally the invocation expression for the underlying linq call.
Copy link
Contributor

@AlekseyTs AlekseyTs Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Underlying reduced expression for the query clause. This is normally the invocation expression for the underlying linq call. [](start = 12, length = 124)

Perhaps it is worth mentioning that that call refers to other query clauses. #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Sep 18, 2017

@mavasani It looks like there are some legitimate test failures. #Closed

@mavasani
Copy link
Contributor Author

mavasani commented Sep 18, 2017

@AlekseyTs Following 2 tests are failing due to current GetOperation implementation:

Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics.IOperationTests.TestClone
Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics.IOperationTests.TestParentOperations

I expect them to pass as soon as @heejaechang PR for changes to GetOperation gets merged in. #Pending

PopulateRangeVariableMapForAnonymousType(node.Syntax, paramRef, nodeRangeVariables, firstUnmappedRangeVariable)
If parameter.Type.IsErrorType() Then
' Skip adding to the range variable map for error case.
firstUnmappedRangeVariable += 1
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

firstUnmappedRangeVariable += 1 [](start = 28, length = 31)

It doesn't look like it is safe to assume that only one range variable should be skipped in this case. Probably we should simply exit the loop. I'd like to take a closer look at the specific scenarios, for which we are taking this code path. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed offline, I have changed the code to exit the function at this point and also removed the assert in the rewriter.


In reply to: 139533872 [](ancestors = 139533872)

Dim pass2Rewriter As New QueryLambdaRewriterPass2
Return pass2Rewriter.VisitLambda(rewrittenLambda)
Catch ex As CancelledByStackGuardException
Return node
Copy link
Contributor

@AlekseyTs AlekseyTs Sep 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return node [](start = 16, length = 11)

Handling potential stack overflow in this manner can be a source of hard to repro bug reports. Let's open a follow up issue to confirm whether we want to use the guard (BoundTreeRewriterWithStackGuard) at all. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #22193


In reply to: 139535884 [](ancestors = 139535884)

@AlekseyTs
Copy link
Contributor

Operand: ITranslatedQueryExpression (OperationKind.TranslatedQueryExpression, Type: QueryAble) (Syntax: 'From s In q ... ere Two > 0')

It seems correct that we are getting ITranslatedQueryExpression for a query expression syntax. Can you please clarify why you think otherwise?

We are requesting IOperation for a single clause From s In q, It feels unexpected to me to get the entire query, not to mention an implicit conversion on top of it.


In reply to: 328924294 [](ancestors = 328924294,327889585)


Refers to: src/Compilers/VisualBasic/Test/Semantic/Semantics/QueryExpressions.vb:2181 in b63c6e6. [](commit_id = b63c6e6, deletion_comment = False)

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Sep 18, 2017

Done with review pass (iteration 8) #Closed

@mavasani
Copy link
Contributor Author

Operand: ITranslatedQueryExpression (OperationKind.TranslatedQueryExpression, Type: QueryAble) (Syntax: 'From s In q ... ere Two > 0')

The comment 'BIND:"From s In q" is causing the confusion as it just contains the first line of annotated syntax - the test is actually requesting the operation tree for the entire QueryExpression. See line 2221 below: VerifyOperationTreeAndDiagnosticsForTest(Of QueryExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics).


In reply to: 330358284 [](ancestors = 330358284,328924294,327889585)


Refers to: src/Compilers/VisualBasic/Test/Semantic/Semantics/QueryExpressions.vb:2181 in b63c6e6. [](commit_id = b63c6e6, deletion_comment = False)

@mavasani
Copy link
Contributor Author

@AlekseyTs Addressed your feedback.

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Private ReadOnly _uniqueNodes As New HashSet(Of BoundParameter)

Public Overrides Function VisitParameter(node As BoundParameter) As BoundNode
node = DirectCast(MyBase.VisitParameter(node), BoundParameter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the call to MyBase.VisitParameter() necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct, I will remove it.

@mavasani
Copy link
Contributor Author

@dotnet-bot retest windows_debug_unit32_prtest please

Module Module1
Sub Main()
Dim q As New QueryAble()
Dim q1 As Object = From s In q Where s > 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is q1 used?

Module Module1
Sub Main()
Dim q As New QueryAble2()
Dim q1 As Object = From s In q
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is q1 used?

@mavasani mavasani requested a review from a team as a code owner September 20, 2017 20:24
@mavasani mavasani changed the base branch from features/ioperation to master September 20, 2017 20:24
@mavasani
Copy link
Contributor Author

@dotnet-bot retest windows_release_unit64_prtest please

@mavasani
Copy link
Contributor Author

@dotnet-bot retest windows_release_unit32_prtest please

1 similar comment
@mavasani
Copy link
Contributor Author

@dotnet-bot retest windows_release_unit32_prtest please

@mavasani mavasani merged commit 3c041e6 into dotnet:master Sep 20, 2017
@mavasani mavasani deleted the QueryOperations branch September 20, 2017 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants