Skip to content

Collection expression arguments: initial binding and lowering#76903

Merged
cston merged 32 commits intodotnet:features/dictionary-expressionsfrom
cston:collection-args
Feb 4, 2025
Merged

Collection expression arguments: initial binding and lowering#76903
cston merged 32 commits intodotnet:features/dictionary-expressionsfrom
cston:collection-args

Conversation

@cston
Copy link
Contributor

@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

@ghost ghost added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Jan 24, 2025
</data>
<data name="IDS_FeatureCollectionExpressionArguments" xml:space="preserve">
<value>collection expression arguments</value>
</data>
Copy link
Contributor

@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
Contributor 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.

</data>
<data name="ERR_CollectionArgumentsMustBeFirst" xml:space="preserve">
<value>Collection arguments must be the first element.</value>
</data>
Copy link
Contributor

@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
Contributor

@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
Contributor 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
Contributor 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
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
Contributor 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

<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
Contributor 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
Contributor 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.

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)

""";
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
Contributor 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
var comp = CreateCompilation([sourceA, sourceB]);
comp.VerifyEmitDiagnostics(
// (5,5): error CS9223: Creation of params collection 'MyCollection<int>' results in an infinite chain of invocation of constructor 'MyCollection<T>.MyCollection()'.
// c = [with(1)];

Choose a reason for hiding this comment

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

@cston I may be wrong, but is it really an error? We will pass 1 to MyCollection<T>.MyCollection(params MyCollection<T>), then will create empty instance of MyCollection<T> by parameterless constructor and just add this 1 via .Add method. If we unwrap withElement sharplab then there is no error before

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.

5 participants