-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Collection expression arguments: initial binding and lowering #76903
Collection expression arguments: initial binding and lowering #76903
Conversation
…s' into collection-args
…s' into collection-args
@@ -6841,6 +6841,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ | |||
<data name="IDS_FeatureCollectionExpressions" xml:space="preserve"> | |||
<value>collection expressions</value> | |||
</data> | |||
<data name="IDS_FeatureCollectionExpressionArguments" xml:space="preserve"> | |||
<value>collection expression arguments</value> | |||
</data> |
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.
Grrrr #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.
Yes, these two resource strings are essentially the ones you added and removed from #76003.
@@ -6880,6 +6883,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ | |||
<data name="ERR_CollectionExpressionKeyValuePairNotSupported" xml:space="preserve"> | |||
<value>Collection expression type '{0}' does not support key-value pair elements.</value> | |||
</data> | |||
<data name="ERR_CollectionArgumentsMustBeFirst" xml:space="preserve"> | |||
<value>Collection arguments must be the first element.</value> | |||
</data> |
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.
Grr * 2.
Note i think this should be called "collection argument element" or just "'with' element. Saying the arguments (plural) must be the first element (singular) reads oddly to me. #Closed
{ | ||
internal partial class BoundUnconvertedCollectionArguments | ||
{ | ||
internal void GetArguments(AnalyzedArguments analyzedArguments) |
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.
fwiw, havin thtis be called GetArguments and not return something reads oddly. Consider .AddArgumentsTo, which makes it much clearer that the receiver is adding the arguments to the passed in value. #ByDesign
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 like to leave this as is. The intended use is to populate the AnalyzedArguments
collection with just the arguments, rather than to add to an existing collection (we assert the collection is empty initially), so renaming to AddArgumentsTo
might make the intent less clear.
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.
Would "BuildArguments" or "CollectArguments" solve the problem?
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.
When we support collection builder types, where the factory method has an additional, implicit ReadOnlySpan<T> elements
argument, it may make sense to rename this to AddArguments()
or something similar. For now though, GetArguments()
seems appropriate to me.
9a04311
to
b41f88f
Compare
f211702
to
19d5fa0
Compare
@@ -6841,6 +6841,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ | |||
<data name="IDS_FeatureCollectionExpressions" xml:space="preserve"> | |||
<value>collection expressions</value> | |||
</data> | |||
<data name="IDS_FeatureCollectionExpressionArguments" xml:space="preserve"> |
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.
nit: Why do we want a new feature flag? Seems IDS_FeatureDictionaryExpressions
would be sufficient #ByDesign
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 motivating scenario for collection arguments is dictionary key comparer, but collection arguments can be used with non-dictionary types. The additional id allows the feature to be reported "collection expression arguments" rather than "dictionary expressions" for the error reported with earlier language versions.
@@ -2360,6 +2360,9 @@ internal enum ErrorCode | |||
ERR_ImplicitlyTypedParamsParameter = 9272, | |||
ERR_VariableDeclarationNamedField = 9273, | |||
ERR_CollectionExpressionKeyValuePairNotSupported = 9274, | |||
ERR_CollectionArgumentsMustBeFirst = 9275, |
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.
nit: You may want to leave a gap between errorcodes in main
branch and those in dictionary expression branch, so that you don't have to re-number too much (hopefully only once)
// We only get here if the conversion of the collection expression to the | ||
// target type failed. In this case, simply visit each argument. | ||
VisitArgumentsEvaluate(collectionArguments.Arguments, collectionArguments.ArgumentRefKindsOpt, parameterAnnotationsOpt: default, defaultArguments: default); | ||
break; |
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 leaving a PROTOTYPE note or tracking a follow-up elsewhere to implement nullability analysis on happy cases (where the conversion succeeds) #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.
Included in the test plan: #76310
@@ -1958,6 +1958,13 @@ | |||
<Field Name="Value" Type="BoundExpression"/> | |||
</Node> | |||
|
|||
<Node Name="BoundUnconvertedCollectionArguments" Base="BoundNode"> |
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.
nit: consider "BoundWithElement" since we already use the "...Element" naming convention for various collection expression elements (spread, key-value pair) and it would line up with the name of the syntax node ("WithElementSyntax")
Also, there's no "Converted" version of this node, so "Unconverted" feels odd #Closed
MessageID.IDS_FeatureCollectionExpressionArguments.CheckFeatureAvailability(diagnostics, syntax.WithKeyword); | ||
|
||
var arguments = AnalyzedArguments.GetInstance(); | ||
BindArgumentsAndNames(syntax.ArgumentList, diagnostics, arguments, allowArglist: true); |
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.
Good catch, thanks, I've added tests.
That said, we should consider not supporting __arglist
.
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 be fine not supporting :-) I just assumed you wanted anything that is supported in explicit object creation syntax to be allowed
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.
Added an open question in dotnet/csharplang#9115.
|
||
if (collectionCreation.HasErrors) | ||
// Bind collection creation with arguments. | ||
foreach (var element in elements) |
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 looks like we're converting the arguments in the with-element even when the with-element is not in the right position, or there are multiple. Feels like we should only convert the one that is in the right position, and do some error binding (bind to natural type?) for the error ones (but not updating collectionCreation
) #ByDesign
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 seems useful to me to bind with(...)
as arguments to the best candidate method regardless of whether the arguments are in the first position, since presumably that is the intent.
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.
Binding more than one with
element still makes me uncomfortable. I could be convinced to bind a single with
in the wrong position.
Two reasons:
- Semantically, there's only one object creation in a collection expression. But presumably QuickInfo on each
with
might show different constructors... - When there are multiple
with
elements, any diagnostics reported on those in the wrong position are guaranteed to be cascading diagnostics. Do we have a test for that?
It may be good to get a second opinion and/or discuss after standup.
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.
When there are multiple with elements, any diagnostics reported on those in the wrong position are guaranteed to be cascading diagnostics.
Overload resolution for the constructor is independent of the position of the with()
, so one does not affect the other.
Do we have a test for that?
Yes, CollectionExpressionArguments.LanguageVersion_03()
tests List<int> l = [with(x: 1), with(y: 2)];
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 agree with Julien. I'm not comfortable binding multiple constructors for collection expressions. I'm fine with out of position with
elements, but multiple constructors will have potential cascading effects, not just in diagnostics, but also in other IDE features, as well as forcing handling of multiple constructors elsewhere in our code. I think we should just turn secondary withs into BoundBadExpressions
, bind the expressions for error recovery, and be done with it.
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.
Added a PROTOTYPE comment to revisit this later, likely when implementing IOperation
support.
src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionArgumentTests.cs
Show resolved
Hide 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.
Done with review pass (iteration 23)
src/Compilers/CSharp/Portable/BoundTree/BoundUnconvertedCollectionArguments.cs
Outdated
Show resolved
Hide 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.
Done with review pass (iteration 27)
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 Thanks (iteration 28)
|
||
if (collectionCreation.HasErrors) | ||
// Bind collection creation with arguments. | ||
foreach (var element in elements) |
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 agree with Julien. I'm not comfortable binding multiple constructors for collection expressions. I'm fine with out of position with
elements, but multiple constructors will have potential cascading effects, not just in diagnostics, but also in other IDE features, as well as forcing handling of multiple constructors elsewhere in our code. I think we should just turn secondary withs into BoundBadExpressions
, bind the expressions for error recovery, and be done with it.
src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionArgumentTests.cs
Show resolved
Hide resolved
"""; | ||
var comp = CreateCompilation([sourceA, sourceB]); | ||
comp.VerifyEmitDiagnostics( | ||
// (7,13): error CS0417: 'U': cannot provide arguments when creating an instance of a variable 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.
I'll be honest: I've reread this error 5 times and still have no idea what it's tell me. Is the issue that we can't find an appropriate constructor? #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.
This is the error reported when constructing an instance of the type parameter with arguments: new U(t)
. sharplab.io
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.
Good lord that error is awful.
f5224d8
into
dotnet:features/dictionary-expressions
Binding and lowering of collection expression arguments for
class
andstruct
target types that implementIEnumerable
and do not have a[CollectionBuilder]
attribute.See proposals/collection-expression-arguments.md
Relates to test plan #76310