-
Notifications
You must be signed in to change notification settings - Fork 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
Open issues for collection expression construction #7542
Comments
We should also be flexible enough to support |
This is def a non-goal. Class collection initializers don't do a good job perf-wise, which is what we're trying to avoid here. :) |
We should avoid a temporary buffer if:
In both these cases, we can figure out the correct capacity and initialize the destination accordingly so we do not do anything wasteful. The only times we should make temporary storage are when we have spreads without staticly or dynamically determinable counts. In that case, we can't really do anything better other than at least setting the initial capacity to the number of fixed elements. i.e. if we have 100 fixed elements, no point using default capacity, sicne it will have to double 6 times to reach that size. |
This comment was marked as resolved.
This comment was marked as resolved.
@karakasa deadlines meant it was more important to get a correct impl in first. The team is continuing to work on perf optimizations. And, of course, prs welcome :-) |
thank you. didn't realize that. 😉 |
Open issues: collection expression construction
See #7541.
Use .ctor(int capacity)?
For collection initializer types, should the compiler use a
.ctor(int capacity)
if the length of the collection is known at compile time and the constructor overload exists?Use AddRange() or other patterns?
For collection initializer types, construction involves calling the applicable
Add()
methods which may be instance or extension methods.For spread elements, should the compiler invoke applicable
AddRange()
methods if available? If so, should extension methods be supported or just instance methods? Should the compiler always useAddRange()
if available, or can the compiler choose?Should the compiler use a handful of other common patterns if available on the collection type or spread type?
From construction:
Avoid intermediate buffer if length is known?
For target types other than collection initializer types, construction involves element assignment to an instance of a well-known type (an array, span, or list). Ideally for these cases, the compiler should avoid creating an intermediate buffer during construction when the collection has a known length at runtime – that is, when all spread elements have an appropriate
Length
orCount
property. However, that means evaluating all elements (up to the last spread element) before allocating the collection. For these cases:Should the compiler guarantee that a temporary buffer is avoided when all spread elements have a known length? Or should we use heuristics to avoid creating a temporary buffer under a limited set of conditions (for instance, no more than 8 elements)?
Evaluate all elements before calling Add() or AddRange()?
[Related to the previous questions.]
Should the compiler use
.ctor(int capacity)
for collection initializer types when the collection expression contains spread elements, or only when there are no spread elements? The question is really: ShouldAdd()
andAddRange()
calls be interleaved with element evaluation (which matches classic collection initializer behavior), or should all elements be evaluated before any are added?Type inference: lower bound inference from spread element iteration type
From type inference, a lower bound inference is made from the spread element iteration type.
Confirm this is the expected behavior.
Overload resolution: no fallback to better conversion target
Overload resolution was previously discussed LDM-2023-08-14, with WG follow-up covered in email.
From overload resolution, there is no fallback to a better conversion target if the ref struct and non- ref struct types are ambiguous.
Confirm this is the expected behavior.
Collections and well-defined behavior
Collection expressions are assumed to be well-behaved:
Length
andCount
properties return the number of items in the spread element expression.The compiler is free to change how it emits construction of collection instances assuming collection expressions are well-behaved. If the collection expression is not well-behaved, the result is undefined.
From construction:
Collection expression conversion existence
Currently, the spec says this for when a type is a valid target type for a collection expression:
This is very broad, and introduces a problem that we redesigned interpolated string handlers to avoid: whether or not a conversion exists depends on a successful bind of construction of the type. This makes the presence of the conversion brittle to user errors, both from type authors and from errors user's own code; it also significantly complicates the implementation of the conversion logic. We'd like to propose the following rules instead:
This has a few benefits:
Add
method, for example) now fail for that reason, instead of being silently discarded by overload resolution.IEnumerable<T>
with the calculation forIEnumerable<T>
itself.However, the second rule may be overly broad, and cause ambiguities when old, non-
IEnumerable<T>
APIs are mixed with new APIs. Therefore, another option that we think may be viable is simply removing the second rule; this would mean that collection expressions cannot be used for collection types that implementIEnumerable
but notIEnumerable<T>
and do not haveCollectionBuilderAttribute
applied to them.Nature of the constructor check
If we decide to keep the constructor check, we should decide whether to use a specific lookup or just regular binding.
For instance, if the constructor takes an optional parameter, should we recognize it as a proper constructor?
If we stick with regular binding rules, then any constructor that can be invoked without argument would be recognized. That is equivalent to the user typing
new type()
.If we decide to recognize such optional parameters, there is the question of whether use-site errors on those parameter types should count as a failure to bind the constructor or not. There may also be some scenarios with
[RequiresCompilerFeature]
.The text was updated successfully, but these errors were encountered: