Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Oct 6, 2025

Followup to #80554.

Adds support for the collection-builder pattern.

Relates to test plan #80613

@CyrusNajmabadi CyrusNajmabadi requested a review from jcouv October 15, 2025 15: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 (commit 328)

src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs Outdated Show resolved Hide resolved

var argsToParams = projectionCall.ArgsToParamsOpt;
if (!argsToParams.IsDefault)
argsToParams = argsToParams.Add(collectionBuilderMethod.ParameterCount - 1);
Copy link
Member

Choose a reason for hiding this comment

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

A collection builder method is not going to be a new extension method, right? That would make some of this projection stuff extra painful.

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah. that's not supported now. it's supposed to point at a normal static method.

type: collectionBuilderMethod.ReturnType).MakeCompilerGenerated();

// Wrap in a conversion if necessary. Note that GetCollectionBuilderMethods guarantees that either
// return and target type are identical, or that a valid implicit conversion exists between them.
Copy link
Member

Choose a reason for hiding this comment

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

Consider asserting this (potentially in follow up PR)

Copy link
Member Author

Choose a reason for hiding this comment

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

can do.

Debug.Assert(!collectionBuilderMethod.GetIsNewExtensionMember());

return collectionBuilderMethod;
Debug.Assert(!collectionBuilderMethod.GetIsNewExtensionMember());
Copy link
Member

Choose a reason for hiding this comment

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

whew.

else if (targetType is TypeParameterSymbol typeParameter)
{
collectionCreation = BindTypeParameterCreationExpression(syntax, typeParameter, analyzedArguments, initializerOpt: null, typeSyntax: syntax, wasTargetTyped: true, diagnostics);
collectionCreation = BindTypeParameterCreationExpression(syntax, typeParameter, analyzedArguments, initializerOpt: null, typeSyntax: syntax, wasTargetTyped: false, diagnostics);
Copy link
Member

Choose a reason for hiding this comment

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

It looks like wasTargetTyped: true was just the wrong thing to be using here? I am assuming this is for when the target collection type is a type parameter constrained to something which allows us to know what its construction methods are?

Copy link
Member Author

Choose a reason for hiding this comment

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

yup


var withArgumentsBuilder = ArrayBuilder<BoundExpression>.GetInstance(withArguments.Length);
foreach (var argument in withArguments)
withArgumentsBuilder.Add(BindToNaturalType(argument, diagnostics, reportNoTargetType: !targetType.IsErrorType()));
Copy link
Member

Choose a reason for hiding this comment

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

nit: perhaps !targetType.IsErrorType() should be extracted. can happen in later PR or never. up to you.

Copy link
Member Author

Choose a reason for hiding this comment

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

src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml Outdated Show resolved Hide resolved
Co-authored-by: Rikki Gibson <rikkigibson@gmail.com>
static void Main()
{
string? s = null;
Identity<int>([with((s = "").Length)]);
Copy link
Member

Choose a reason for hiding this comment

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

It would be good to test a scenario like the following (can be done in follow up)

public static MyCollection<T> Create<T>(T value, ReadOnlySpan<T> items) => new(items);
MyCollection<T> Identity<T>(MyCollection<T> collection) => collection;

string? x = null;
var collection = M1([with(x), "a"]);
collection[0].ToString(); // should this warn or not?

Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "params MyCollection<T> c").WithArguments("MyBuilder.Create<T>(System.ReadOnlySpan<T>)", "T", "T").WithLocation(12, 51),
// (14,53): error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'MyBuilder.Create<T>(ReadOnlySpan<T>)'
// static MyCollection<T> ClassConstraintParams<T>(params MyCollection<T> c) where T : class => c;
Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "params MyCollection<T> c").WithArguments("MyBuilder.Create<T>(System.ReadOnlySpan<T>)", "T", "T").WithLocation(14, 53));
Copy link
Member

Choose a reason for hiding this comment

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

It would be useful to also know what error is reported for empty with() here.

Copy link
Member

@RikkiGibson RikkiGibson left a comment

Choose a reason for hiding this comment

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

more comments on tests incoming, but, I don't think any will be blocking, so approving to merge

@CyrusNajmabadi CyrusNajmabadi enabled auto-merge (squash) October 15, 2025 19:39
@RikkiGibson
Copy link
Member

RikkiGibson commented Oct 15, 2025

I'm having issues with the review tool (both vscode and github side review tools). I have the following comments which I will indicate here with the test name associated with the comment. These are non-blocking comments. Sorry for the inconvenience.

Test CollectionBuilder_RefParameter_A: It would be interesting to have a Create method which assigns the ref and that we can observe the result.
- Similarly, a ref parameter like [NotNull] string? param which updates the flow state of the argument
Test CollectionBuilder_OutParameter_A: Same comment as for RefParameter_A case
Test CollectionBuilder_SpreadElement_BoxingConversion: Warning CS8620 looks bogus. It looks like nullable analysis is trying to convert T to ReadOnlySpan<T?>, and concluding from the conversion failure, that there is a nullability issue. Could you please add a prototype comment to address this.

@CyrusNajmabadi CyrusNajmabadi merged commit 621cc77 into dotnet:features/collection-expression-arguments Oct 15, 2025
23 of 24 checks passed
@CyrusNajmabadi CyrusNajmabadi deleted the withElementCreate branch October 15, 2025 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.