Support implicit indexer access in object initializers#70649
Support implicit indexer access in object initializers#70649jcouv merged 19 commits intodotnet:mainfrom
Conversation
a3c76d7 to
a716dee
Compare
a716dee to
acfbe40
Compare
| <value>The diagnosticId argument to the 'Experimental' attribute must be a valid identifier</value> | ||
| </data> | ||
| <data name="IDS_ImplicitIndexInitializer" xml:space="preserve"> | ||
| <value>implicit Index initializer</value> |
| MessageID.IDS_ImplicitIndexInitializer.CheckFeatureAvailability(diagnostics, implicitIndexer.Syntax); | ||
|
|
||
| hasErrors |= isRhsNestedInitializer | ||
| && !CheckNestedObjectInitializerPropertySymbol(GetPropertySymbol(implicitIndexer, out _, out _), leftSyntax, diagnostics, hasErrors, ref resultKind); |
| hasErrors |= isRhsNestedInitializer | ||
| && !CheckNestedObjectInitializerPropertySymbol(GetPropertySymbol(implicitIndexer, out _, out _), leftSyntax, diagnostics, hasErrors, ref resultKind); | ||
|
|
||
| return hasErrors ? boundMember : CheckValue(boundMember, valueKind, diagnostics); |
There was a problem hiding this comment.
The regular indexer case (BoundKind.IndexerAccess) does this after the switch (line 5429 below).
The effect is shown in tests like InObjectInitializer_Readonly and InObjectInitializer_Readonly.
| if (assignment.Left.Kind != BoundKind.PointerElementAccess) | ||
| // Do not lower pointer or implicit indexer access yet, we'll do them later. | ||
| BoundKind leftKind = assignment.Left.Kind; | ||
| if (leftKind != BoundKind.PointerElementAccess && leftKind != BoundKind.ImplicitIndexerAccess) |
There was a problem hiding this comment.
It is not obvious why this is necessary. Why regular way of doing things works for regular indexer and doesn't work for an implicit indexer? It is quite possible that I would be more comfortable with adjusting existing code paths instead of introducing completely new case below. #Closed
There was a problem hiding this comment.
The regular indexer case is handled inside VisitObjectInitializerMember which takes a BoundObjectInitializerMember. So it doesn't seem very suitable for sharing.
I could convert the ternary expression below into a switch and essentially do rewrittenLeft = VisitImplicitIndexerAccess((BoundImplicitIndexerAccess)assignment.Left, ...); here (instead of rewrittenAccess = VisitImplicitIndexerAccess(...) below) if that feels more comfortable, but I don't think we'll be able to share.
| } | ||
| } | ||
| """; | ||
| var comp = CreateCompilationWithIndex(source, parseOptions: TestOptions.RegularNext); |
| } | ||
|
|
||
| [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67533")] | ||
| public void InObjectInitializer_Nested() |
| """; | ||
| var comp = CreateCompilationWithIndexAndRange(source); | ||
| comp.VerifyDiagnostics( | ||
| // (2,1): error CS0131: The left-hand side of an assignment must be a variable, property or indexer |
| Buffer10 b = default; | ||
| b[..][0] = 1; | ||
|
|
||
| _ = new Buffer10() { [..][0] = 2 }; // 1 |
| Buffer10 b = default; | ||
| b[..] = null; // 1 | ||
|
|
||
| _ = new Buffer10() { [..] = null }; // 2 |
|
Done with review pass (commit 1). |
d1a0cbb to
a9a2e26
Compare
a9a2e26 to
e4c79c2
Compare
| void addIndexes(IMemberInitializerOperation memberInitializer) | ||
| { | ||
| var lhs = memberInitializer.InitializedMember; | ||
| // If we have an element access of the form `[arguments] = { ... }`, we'll evaluate `arguments` only |
There was a problem hiding this comment.
No, there's possibly some nested initializers in there. As long as the leaves are empty initializers, the optimization kicks in and we only need to evaluate the indexer arguments.
| cacheAllArgumentsOnly: true, | ||
| result, temps); | ||
|
|
||
| if (rewrittenAccess is BoundIndexerAccess indexerAccess1) |
|
Done with review pass (commit 17) |
We only had CFG validation. Expanded the relevant tests In reply to: 1839590554 Refers to: src/Compilers/CSharp/Test/Emit2/CodeGen/IndexAndRangeTests.cs:4220 in cb65f3f. [](commit_id = cb65f3f, deletion_comment = False) |
Will this be changed to And currently, it looks like Refers to: src/Compilers/CSharp/Test/Emit2/CodeGen/IndexAndRangeTests.cs:57 in 87eb9a1. [](commit_id = 87eb9a1, deletion_comment = False) |
Are we testing this case with an empty initializer (with Refers to: src/Compilers/CSharp/Test/Emit2/CodeGen/IndexAndRangeTests.cs:1199 in 87eb9a1. [](commit_id = 87eb9a1, deletion_comment = False) |
Are we testing an indexer that returns In reply to: 1841587538 Refers to: src/Compilers/CSharp/Test/Emit2/CodeGen/IndexAndRangeTests.cs:1199 in 87eb9a1. [](commit_id = 87eb9a1, deletion_comment = False) |
Yes, In reply to: 1841558134 Refers to: src/Compilers/CSharp/Test/Emit2/CodeGen/IndexAndRangeTests.cs:57 in 87eb9a1. [](commit_id = 87eb9a1, deletion_comment = False) |
Added some tests. Was that was you had in mind? In reply to: 1841590554 Refers to: src/Compilers/CSharp/Test/Emit2/CodeGen/IndexAndRangeTests.cs:1199 in 87eb9a1. [](commit_id = 87eb9a1, deletion_comment = False) |
|
@AlekseyTs I believe all feedback was addressed. Thanks |
AlekseyTs
left a comment
There was a problem hiding this comment.
LGTM(commit 19), assuming CI is passing
|
/azp run |
|
Azure Pipelines successfully started running 2 pipeline(s). |
Fixes #67533
Since it's been a while since the Ranges feature was released, I put the fix behind a LangVer/feature check.