-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Implement pattern-matching in top-level scripts #13999
Conversation
…versionFromExpression` (issue ignored during code review)
in which pattern variables become fields. Fixes dotnet#10603
@AlekseyTs @agocke Please review. |
@AlekseyTs @agocke Please review. #Resolved |
@AlekseyTs @agocke Please review. #Resolved |
1 similar comment
@AlekseyTs @agocke Please review. #Resolved |
if (written) NoteWrite(symbol, value, read); | ||
var pattern = (BoundDeclarationPattern)node; | ||
Symbol symbol = pattern.Variable as LocalSymbol; | ||
if (symbol != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it useful to cast to object here for null check?
Does the case were pattern.Variable is a field need to be handled too? If not, consider adding a comment. #Resolved
SetSlotState(slot, assigned: written || !this.State.Reachable); | ||
if (written) NoteWrite(symbol, value, read); | ||
var pattern = (BoundDeclarationPattern)node; | ||
Symbol symbol = pattern.Variable as LocalSymbol; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use var
or LocalSymbol
instead of Symbol
? #Resolved
The tests in this file seem to have been split to multiple files. Is there a way I tell which tests are new or modified with this PR? #Resolved Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs:16 in 59aca3e. [](commit_id = 59aca3e, deletion_comment = False) |
Nevermind. I think it is the test file named "Global" ;-) In reply to: 249940461 [](ancestors = 249940461) Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs:16 in 59aca3e. [](commit_id = 59aca3e, deletion_comment = False) |
@@ -1236,7 +1236,7 @@ public static ISymbol GetDeclaredSymbol(this SemanticModel semanticModel, Variab | |||
/// <summary> | |||
/// Given a declaration pattern syntax, get the corresponding symbol. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider updating comment: "get the corresponding symbol (local or field)". #WontFix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not introduce an inconsistency with all the other places where this comment appears (e.g. for SingleVariableDesignationSyntax and VariableDeclaratorSyntax above)
In reply to: 80752496 [](ancestors = 80752496)
@@ -77,7 +77,12 @@ private void NoteDeclaredPatternVariables(BoundPattern pattern) | |||
if (IsInside && pattern.Kind == BoundKind.DeclarationPattern) | |||
{ | |||
var decl = (BoundDeclarationPattern)pattern; | |||
_variablesDeclared.Add(decl.LocalSymbol); | |||
if (decl.Variable.Kind == SymbolKind.Local) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't recall modifying this file/walker for d-declaration in script. Should I have? #WontFix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends how this walker handles Deconstruction Declaration. It shouldn't try to add fields into _variablesDeclared list. You should definitely consider adding tests for region analysis for Deconstruction Declaration.
In reply to: 80753230 [](ancestors = 80753230)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You didn't modify this file because the relevant APIs were never implemented for deconstruction. I'll submit a bug with a test case.
In reply to: 80756845 [](ancestors = 80756845,80753230)
@@ -1562,12 +1562,14 @@ | |||
</AbstractNode> | |||
|
|||
<Node Name="BoundDeclarationPattern" Base="BoundPattern"> | |||
<Field Name="LocalSymbol" Type="LocalSymbol" Null="disallow"/> | |||
<Field Name="Variable" Type="Symbol" Null="disallow"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[](start = 4, length = 54)
Please add a comment what kind of symbols could end up here. #Closed
I'm not sure I understand your question. These tests correspond 1-to-1 with tests for similar cases involving out variables. In reply to: 249941547 [](ancestors = 249941547) Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_Global.cs:16 in 59aca3e. [](commit_id = 59aca3e, deletion_comment = False) |
All review comments have been addressed. #Resolved |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -1748,11 +1736,13 @@ private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSy | |||
{ | |||
okToDowngradeToNeither = false; | |||
|
|||
if (considerRefKinds) | |||
var argumentKind = node.Kind; | |||
if (considerRefKinds || argumentKind == BoundKind.OutVariablePendingInference || argumentKind == BoundKind.OutDeconstructVarPendingInference) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (considerRefKinds || argumentKind == BoundKind.OutVariablePendingInference || argumentKind == BoundKind.OutDeconstructVarPendingInference) [](start = 12, length = 141)
I believe merging handling of BoundKind.OutVariablePendingInference and BoundKind.OutDeconstructVarPendingInference into this if
complicates the code significantly. The behavior we want for these nodes is very simple and it is better to handle them separately, so that we can quote the spec and have an easy way to match implementation to the spec. It is nearly impossible to match the current implementation to the spec. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The behavior we want arises from the specification for exactly matching expression. I will implement it that way, now that we have that tentative spec language.
In reply to: 80791006 [](ancestors = 80791006)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The behavior we want arises from the specification for exactly matching expression.
Please quote what you think the tentative specification is. I am not able to verify correctness of the change otherwise.
In reply to: 80793171 [](ancestors = 80793171,80791006)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will include the quote in a comment.
In reply to: 80794118 [](ancestors = 80794118,80793171,80791006)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please revert change to this file then.
In reply to: 80800555 [](ancestors = 80800555,80795408,80794118,80793171,80791006)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closed the issue, as it does not occur in the ECMA draft for our next spec. I've updated the spec and the implementation to align as close as the shape of our implementation allows.
In reply to: 80800555 [](ancestors = 80800555,80795408,80794118,80793171,80791006)
{ | ||
var memberModel = this.GetMemberModel(declarationSyntax); | ||
return memberModel == null ? null : memberModel.GetDeclaredSymbol(declarationSyntax, cancellationToken); | ||
return memberModel?.GetDeclaredSymbol(declarationSyntax, cancellationToken) ?? | ||
GetEnclosingBinder(declarationSyntax.Position)?.LookupDeclaredField(declarationSyntax, declarationSyntax.Identifier.ValueText); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
, declarationSyntax.Identifier.ValueText [](start = 101, length = 40)
Consider using an overload that doesn't need this argument. #Closed
@@ -106,7 +106,7 @@ private Symbol GetNodeSymbol(BoundNode node) | |||
{ | |||
case BoundKind.DeclarationPattern: | |||
{ | |||
return ((BoundDeclarationPattern)node).LocalSymbol; | |||
return ((BoundDeclarationPattern)node).Variable as LocalSymbol; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return ((BoundDeclarationPattern)node).Variable as LocalSymbol; [](start = 28, length = 63)
Consider simply delegating to VariableAccess. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VariableAccess isn't a symbol, and doesn't represent the logical symbol represented by this node.
In reply to: 80798952 [](ancestors = 80798952)
@@ -52,12 +52,12 @@ private BoundExpression LowerConstantPattern(BoundConstantPattern pattern, Bound | |||
|
|||
private BoundExpression LowerDeclarationPattern(BoundDeclarationPattern pattern, BoundExpression input) | |||
{ | |||
Debug.Assert(pattern.IsVar || pattern.LocalSymbol.Type == pattern.DeclaredType.Type); | |||
Debug.Assert(pattern.IsVar || pattern.Variable.GetTypeOrReturnType() == pattern.DeclaredType.Type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pattern.IsVar [](start = 25, length = 14)
What is the purpose of IsVar
condition? Shouldn't Variable have type assigned before we get here? #Closed
@@ -320,7 +320,7 @@ private void AddBindings(ArrayBuilder<BoundStatement> sectionBuilder, ImmutableA | |||
{ | |||
var source = kv.Key; | |||
var dest = kv.Value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both expressions should be lowered. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both are required to be of a form that no further local lowering is possible.
In reply to: 80800519 [](ancestors = 80800519)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already had this discussion in context of #13535. There is a simple rule that implementation of LocalRewriter should follow - every node that is a part of the input bound tree should be lowered. Following this simple rule doesn't create any correctness problems, but at the same time prevents us from introducing hard to find bugs and makes local rewriter robust to future changes in the pipeline.
In reply to: 80815417 [](ancestors = 80815417,80800519)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -453,48 +453,6 @@ internal static ImmutableArray<Symbol> GetExplicitInterfaceImplementations(this | |||
} | |||
} | |||
|
|||
internal static TypeSymbol GetTypeOrReturnType(this Symbol member) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
internal static TypeSymbol GetTypeOrReturnType(this Symbol member) [](start = 8, length = 66)
Could you please share the rational behind moving these helpers to SymbolExtensions.cs? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you referring to the rename of the "this" parameter, which was and still is a Symbol? This is applicable to many other methods in this file. Why stop at this two?
In reply to: 80815690 [](ancestors = 80815690,80807640)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am referring to the fact that they operate on symbols that are not members as well as symbols that are members.
In reply to: 80849752 [](ancestors = 80849752,80815690,80807640)
…efactoring the whole thing)
protected override TypeSyntax TypeSyntax => (TypeSyntax)_typeSyntax.GetSyntax(); | ||
protected override SyntaxTokenList ModifiersTokenList => default(SyntaxTokenList); | ||
public override bool HasInitializer => false; | ||
protected override ConstantValue MakeConstantValue(HashSet<SourceFieldSymbolWithSyntaxReference> dependencies, bool earlyDecodingWellKnownAttributes, DiagnosticBag diagnostics) => null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
protected override ConstantValue MakeConstantValue(HashSet dependencies, bool earlyDecodingWellKnownAttributes, DiagnosticBag diagnostics) => null; [](start = 8, length = 185)
This line is too long. Please split it. #Closed
@@ -323,7 +323,7 @@ public sealed override bool HasInitializer | |||
get { return _hasInitializer; } | |||
} | |||
|
|||
public VariableDeclaratorSyntax VariableDeclaratorNode | |||
private VariableDeclaratorSyntax VariableDeclaratorNode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private VariableDeclaratorSyntax VariableDeclaratorNode [](start = 8, length = 55)
Could you please explain the rational behind making this field private? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally, as a matter of software engineering hygiene, it is better to keep API surface area between components as small as possible. This is an example of the application of that principle.
In reply to: 80815616 [](ancestors = 80815616)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally, as a matter of software engineering hygiene, it is better to keep API surface area between components as small as possible.
I agree with that in general. However, SourceFixedFieldSymbol derives from this type and now it has an implicit contract with the base instead of an explicit one. I believe that an explicit contract is better than an implicit one. It makes dependency clear, code more readable and easier to understand. Consider changing accessibility of this method to protected and reverting the change in SourceFixedFieldSymbol.cs
In reply to: 80816186 [](ancestors = 80816186,80815616)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A private member is an explicit contract with code within its containing type.
In reply to: 80850539 [](ancestors = 80850539,80816186,80815616)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am talking about the contract between this class and SourceFixedFieldSymbol, which you made implicit by replacing the use of this property with a type cast.
In reply to: 80959907 [](ancestors = 80959907,80850539,80816186,80815616)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for making the change.
In reply to: 80965746 [](ancestors = 80965746,80959907,80850539,80816186,80815616)
@dotnet-bot test perf_correctness_prtest please #Closed |
// both match exactly, non-lambda case. | ||
// Neither is a better conversion from expression | ||
return BetterResult.Neither; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not comfortable with this change. It also doesn't match the latest specification quoted by Mads at #14117 #Closed
The change in OverloadResolution.cs is way more complicated than the original implementation with no clear benefits. The discussion about the exact spec wording is in flight, there is no good reason to rush making this change right now, you already iterated through at least three different implementations. Overload resolution is a delicate area, let's wait for the dust to settle, i.e. for the LDM to come up with the final wording. Please exclude changes to OverloadResolution.cs and outvar.md from this PR, they are not related to the goal that this PR is trying to achieve. #Closed |
The change in OverloadResolution.cs now implements what the spec actually says, rather than inlining it into the caller. In reply to: 250033678 [](ancestors = 250033678) |
30ef7ad
to
aca145b
Compare
aca145b
to
7a9a8b1
Compare
I'm not comfortable having the implementation of "better conversion from expression" incorrect and partially inlined into a caller. So lets review the fix for that and move on. #Closed |
Given that we are in the middle of an active design discussion about the exact wording that should be used by the language specification to describe interaction of Overload Resolution with Out Variable Declarations and the fact that you haven't demonstrated any correctness issues with the current implementation, I object to making any premature changes both to the outvar.md in that area and to OverloadResolution.cs. Please summarize your concerns in a new, or existing issue (if there is one already) and assign it to me, I will make the necessary changes once design is finalized. Also, please exclude relevant changes from this PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Please revert premature changes to OverloadResolution.cs and changes related to overload resolution in outvar.md
- Please ensure LocalRewriter explicitly lowers bound nodes from the input bound tree.
- Please open an issue to close a test gap for Declaration Pattern in Script. All remaining statements (for, foreach, try, etc.) should be covered.
Those changes either describe what is implemented, or change the description to align with spec terminology. So unless your preexisting implementation is also premature, those changes are intended to remain in this PR. You're welcome to make changes to that spec in the future if/when you change the corresponding implementation, and vice versa. As it stands, the spec and implementation are out of alignment, and these changes are necessary to correct that.
The only nodes you asked to be lowered were those in the decision tree. The decision tree is constructed in locally lowered form, but if you need the double-lowering to pass the review, I'll do that.
I'll check to see what test gap remains. |
LGTM, although I know very little about the semantics in scripting. |
@@ -55,8 +55,7 @@ public sealed override int FixedSize | |||
DiagnosticBag diagnostics = DiagnosticBag.GetInstance(); | |||
int size = 0; | |||
|
|||
VariableDeclaratorSyntax declarator = this.VariableDeclaratorNode; | |||
|
|||
VariableDeclaratorSyntax declarator = VariableDeclaratorNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VariableDeclaratorSyntax declarator = VariableDeclaratorNode; [](start = 20, length = 61)
It looks like there is no meaningful change in this file. Consider reverting it to the previous state to reduce unnecessary churn in the code base.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to wait for discussions to settle on the spec behavior before merging this change.
@jaredpar This is implementing the spec as written before the
This change modifies I'm fine with the spec and implementation changing in lockstep if there is a proposed revision. Until then I prefer we keep them aligned. |
@jaredpar I checked with @MadsTorgersen and the LDM does not intend to produce any further specification text for quite some time. He is OK with a spec that aligns with the implementation, the shape of the current language specification, and the intent of the LDM as previously expressed. We are all OK with any further work on the spec and implementation, as long as they are kept in alignment. |
in which pattern variables become fields.
Also aligns the overload resolution implementation with the spec for out var.
Fixes #10603
@AlekseyTs @agocke Please review.
/cc @dotnet/roslyn-compiler