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 expressions: emit Array.Empty<T>() for [] with target type T[] or IEnumerable<T> #69355

Merged
merged 4 commits into from
Aug 9, 2023

Conversation

cston
Copy link
Member

@cston cston commented Aug 3, 2023

Emit Array.Empty<T>() for an empty collection expression when the target type is:

  • T[]
  • IEnumerable<T>
  • IReadOnlyCollection<T>
  • IReadOnlyList<T>

Relates to test plan #66418

@cston cston requested a review from a team as a code owner August 3, 2023 06:42
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Aug 3, 2023
@cston cston requested review from 333fred and RikkiGibson August 3, 2023 06:43
@Sergio0694
Copy link
Contributor

Question: would it make sense to emit Enumerable.Empty<T>() instead when the target is IEnumerable<T>? Wouldn't that be more efficient, in that it would avoid the allocation of the enumerator (as the returned enumerable is also an enumerator)?

@CyrusNajmabadi
Copy link
Member

Does array.empty not optimize the enumerator it returns either?

var collectionType = (NamedTypeSymbol)node.Type;
BoundExpression? arrayOrList;

// For [] with target type IEnumerable<T>, use Array.Empty<T>() rather than List<T>.
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 3, 2023

Choose a reason for hiding this comment

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

could also do for IReadOnlyList/IReadOnlyCollection. #Resolved

IL_0000: call "string[] System.Array.Empty<string>()"
IL_0005: newobj "System.ReadOnlySpan<string>..ctor(string[])"
IL_000a: call "MyCollection<string> MyCollectionBuilder.Create<string>(System.ReadOnlySpan<string>)"
IL_000f: ret
}
""");
verifier.VerifyIL("Program.F1",
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 3, 2023

Choose a reason for hiding this comment

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

add test where the destination is an int*[][] this should be able to use Array.Empty<int*[]> (inner type is pointer, outer is array). #Resolved

static ICollection<string> EmptyICollection() => [];
static IList<string> EmptyIList() => [];
static IReadOnlyCollection<string> EmptyIReadOnlyCollection() => [];
static IReadOnlyList<string> EmptyIReadOnlyList() => [];
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Aug 3, 2023

Choose a reason for hiding this comment

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

pity about these last two. #Resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like we are calling Array.Empty for these. Did I miss something?

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 cases were added in commit 2 response to #69355 (comment).

@cston
Copy link
Member Author

cston commented Aug 3, 2023

Does array.empty not optimize the enumerator it returns either?

It looks like Array.Empty<T>().GetEnumerator() returns a unique instance each time (see sharplab.io).

@stephentoub
Copy link
Member

stephentoub commented Aug 3, 2023

Does array.empty not optimize the enumerator it returns either?

It looks like Array.Empty<T>().GetEnumerator() returns a unique instance each time (see sharplab.io).

Array.Empty<T>()'s IEnumerable<T>.GetEnumerator returns a singleton. It does not allocate.
SharpLab

@jcouv jcouv self-assigned this Aug 4, 2023
@cston
Copy link
Member Author

cston commented Aug 4, 2023

/azp run roslyn-CI

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@alrz
Copy link
Member

alrz commented Aug 4, 2023

Is it recommended to use [] for immutable array as well?

@CyrusNajmabadi
Copy link
Member

Is it recommended to use [] for immutable array as well?

Yes.

var collectionType = (NamedTypeSymbol)node.Type;
BoundExpression arrayOrList;

// Use Array.Empty<T>() rather than List<T> for an empty collection expression when
Copy link
Contributor

@RikkiGibson RikkiGibson Aug 8, 2023

Choose a reason for hiding this comment

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

I know things are in flux here but the implementation seems fine.

static ICollection<string> EmptyICollection() => [];
static IList<string> EmptyIList() => [];
static IReadOnlyCollection<string> EmptyIReadOnlyCollection() => [];
static IReadOnlyList<string> EmptyIReadOnlyList() => [];
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like we are calling Array.Empty for these. Did I miss something?

@cston cston requested a review from a team August 8, 2023 17:23
{
return arrayEmpty;
}
return new BoundArrayCreation(
Copy link
Member

@jcouv jcouv Aug 9, 2023

Choose a reason for hiding this comment

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

nit: consider adding equivalent pseudo-code // new ArrayType[0] #Closed

type: arrayEmpty.ReturnType);
}

return null;
Copy link
Member

@jcouv jcouv Aug 9, 2023

Choose a reason for hiding this comment

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

nit: consider inverting above if #Closed

IL_0000: ldc.i4.0
IL_0001: newarr "int"
IL_0006: ret
IL_0000: call "int[] System.Array.Empty<int>()"
Copy link
Member

@jcouv jcouv Aug 9, 2023

Choose a reason for hiding this comment

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

Do we have a test with missing Array.Empty API? #Closed

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 3). Minor nits only

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 4)

@cston cston merged commit d838fac into dotnet:main Aug 9, 2023
@ghost ghost added this to the Next milestone Aug 9, 2023
@cston cston deleted the array-empty branch August 9, 2023 17:46
dibarbet added a commit to dibarbet/roslyn that referenced this pull request Aug 15, 2023
…get type T[] or IEnumerable<T> (dotnet#69355)"

This reverts commit d838fac.
@dibarbet dibarbet modified the milestones: Next, 17.8 P2 Aug 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Feature - Collection 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.

9 participants