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

Collection expression arguments: initial binding and lowering #76903

Merged
merged 32 commits into from
Feb 4, 2025

Conversation

cston
Copy link
Member

@cston cston commented Jan 24, 2025

Binding and lowering of collection expression arguments for class and struct target types that implement IEnumerable and do not have a [CollectionBuilder] attribute.

See proposals/collection-expression-arguments.md

Relates to test plan #76310

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Jan 24, 2025
@@ -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>
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Jan 24, 2025

Choose a reason for hiding this comment

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

Grrrr #Closed

Copy link
Member Author

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>
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Jan 24, 2025

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)
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Jan 24, 2025

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

Copy link
Member Author

@cston cston Jan 24, 2025

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.

Copy link
Member

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?

Copy link
Member Author

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.

@cston cston marked this pull request as ready for review January 27, 2025 19:11
@@ -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">
Copy link
Member

@jcouv jcouv Jan 30, 2025

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

Copy link
Member Author

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,
Copy link
Member

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;
Copy link
Member

@jcouv jcouv Jan 30, 2025

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

Copy link
Member Author

@cston cston Jan 30, 2025

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">
Copy link
Member

@jcouv jcouv Jan 30, 2025

Choose a reason for hiding this comment

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

BoundUnconvertedCollectionArguments

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);
Copy link
Member

@jcouv jcouv Jan 30, 2025

Choose a reason for hiding this comment

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

allowArglist: true

Consider adding a test to cover this #Closed

Copy link
Member Author

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.

Copy link
Member

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

Copy link
Member Author

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)
Copy link
Member

@jcouv jcouv Jan 30, 2025

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

Copy link
Member Author

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.

Copy link
Member

@jcouv jcouv Jan 31, 2025

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:

  1. Semantically, there's only one object creation in a collection expression. But presumably QuickInfo on each with might show different constructors...
  2. 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.

Copy link
Member Author

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)];

Copy link
Member

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.

Copy link
Member Author

@cston cston Feb 4, 2025

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.

Copy link
Member

@jcouv jcouv left a 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)

@cston cston requested a review from jcouv January 31, 2025 17:56
Copy link
Member

@jcouv jcouv left a 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)

Copy link
Member

@jcouv jcouv left a 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)

src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml Outdated Show resolved Hide resolved

if (collectionCreation.HasErrors)
// Bind collection creation with arguments.
foreach (var element in elements)
Copy link
Member

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/Portable/Binder/Binder_Conversions.cs Outdated 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
Copy link
Member

@333fred 333fred Feb 3, 2025

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

Copy link
Member Author

@cston cston Feb 4, 2025

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

Copy link
Member

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.

@cston cston requested a review from 333fred February 4, 2025 14:49
@cston cston merged commit f5224d8 into dotnet:features/dictionary-expressions Feb 4, 2025
24 checks passed
@cston cston deleted the collection-args branch February 4, 2025 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Feature - Dictionary Expressions untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants