-
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
Collection expressions: inline collections in spreads and foreach #7864
Open
cston
wants to merge
17
commits into
dotnet:main
Choose a base branch
from
cston:spread-conditional
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
5afdd36
Collection expressions: spread conditional expression
cston 6b7a720
Refactor rules
cston 81e151a
Renames
cston bf15073
Move proposal to separate doc
cston 55a9f3f
Add foreach
cston 5ef984e
Misc.
cston f317fb3
Misc.
cston adca12d
Add compile time *collection_type*
cston 8a9d513
Add natural type alternative
cston 7df30e1
Merge remote-tracking branch 'upstream/main' into spread-conditional
cston fc81418
Misc
cston 17a690f
More detail
cston 681611d
Update table
cston fafd194
Update table
cston c242f78
Updates
cston 053a2d7
Merge remote-tracking branch 'upstream/main' into spread-conditional
cston 527ed1a
Merge remote-tracking branch 'upstream/main' into spread-conditional
cston File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,9 +118,16 @@ An implicit *collection expression conversion* exists from a collection expressi | |
* `System.Collections.Generic.ICollection<T>` | ||
* `System.Collections.Generic.IList<T>` | ||
|
||
The implicit conversion exists if the type has an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `U` where for each *element* `Eᵢ` in the collection expression: | ||
* If `Eᵢ` is an *expression element*, there is an implicit conversion from `Eᵢ` to `U`. | ||
* If `Eᵢ` is an *spread element* `Sᵢ`, there is an implicit conversion from the *iteration type* of `Sᵢ` to `U`. | ||
The implicit conversion exists if the type has an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Tₑ` and for each *element* `Eᵢ` in the collection expression one of the following holds: | ||
* `Eᵢ` is an *expression element* and there is an implicit conversion from `Eᵢ` to `Tₑ`. | ||
* `Eᵢ` is a *spread element* `..s` and `s` is *spreadable* as values of type `Tₑ`. | ||
|
||
An expression `E` is *spreadable* as values of type `Tₑ` if one of the following holds: | ||
* `E` is a *collection expression* and for each element `Eᵢ` in the collection expression there is an implicit conversion to `Tₑ`. | ||
* `E` is a [*conditional expression*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/expressions.md#1115-conditional-operator) `b ? x : y`, and `x` and `y` are *spreadable* as values of type `Tₑ`. | ||
* `E` has an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Eₑ` and there is an implicit conversion from `Eₑ` to `Tₑ`. | ||
|
||
*Should `switch` expressions be supported in spread elements?* | ||
|
||
There is no *collection expression conversion* from a collection expression to a multi dimensional *array type*. | ||
|
||
|
@@ -228,6 +235,8 @@ If the target type is a *struct* or *class type* that implements `System.Collect | |
* For each element in order: | ||
* If the element is an *expression element*, the applicable `Add` instance or extension method is invoked with the element *expression* as the argument. (Unlike classic [*collection initializer behavior*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/expressions.md#117154-collection-initializers), element evaluation and `Add` calls are not necessarily interleaved.) | ||
* If the element is a *spread element* then one of the following is used: | ||
* If the spread element expression is a *collection expression*, then the collection expression elements are evaluated in order as if the elements were in the containing collection expression. | ||
* If the spread element expression is a *conditional expression*, then the condition is evaluated and if `true` the second-, otherwise the third-operand is evaluated as if the operand was an element in the containing collection expression. | ||
* An applicable `GetEnumerator` instance or extension method is invoked on the *spread element expression* and for each item from the enumerator the applicable `Add` instance or extension method is invoked on the *collection instance* with the item as the argument. If the enumerator implements `IDisposable`, then `Dispose` will be called after enumeration, regardless of exceptions. | ||
* An applicable `AddRange` instance or extension method is invoked on the *collection instance* with the spread element *expression* as the argument. | ||
* An applicable `CopyTo` instance or extension method is invoked on the *spread element expression* with the collection instance and `int` index as arguments. | ||
|
@@ -250,6 +259,8 @@ If the target type is an *array*, a *span*, a type with a *[create method](#crea | |
* For each element in order: | ||
* If the element is an *expression element*, the initialization instance *indexer* is invoked to add the evaluated expression at the current index. | ||
* If the element is a *spread element* then one of the following is used: | ||
* If the spread element expression is a *collection expression*, then the collection expression elements are evaluated in order as if the elements were in the containing collection expression. | ||
* If the spread element expression is a *conditional expression*, then the condition is evaluated and if `true` the second-, otherwise the third-operand is evaluated as if the operand was an element in the containing collection expression. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if we did not specify treating conditional expressions differently during construction than other expressions, and leave compiler optimizations discretionary? |
||
* A member of a well-known interface or type is invoked to copy items from the spread element expression to the initialization instance. | ||
* An applicable `GetEnumerator` instance or extension method is invoked on the *spread element expression* and for each item from the enumerator, the initialization instance *indexer* is invoked to add the item at the current index. If the enumerator implements `IDisposable`, then `Dispose` will be called after enumeration, regardless of exceptions. | ||
* An applicable `CopyTo` instance or extension method is invoked on the *spread element expression* with the initialization instance and `int` index as arguments. | ||
|
@@ -377,18 +388,25 @@ The existing rules for the [*first phase*](https://github.com/dotnet/csharpstand | |
> | ||
> An *input type inference* is made *from* an expression `E` *to* a type `T` in the following way: | ||
> | ||
> * If `E` is a *collection expression* with elements `Eᵢ`, and `T` is a type with an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Tₑ` or `T` is a *nullable value type* `T0?` and `T0` has an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Tₑ`, then for each `Eᵢ`: | ||
> * If `Eᵢ` is an *expression element*, then an *input type inference* is made *from* `Eᵢ` *to* `Tₑ`. | ||
> * If `Eᵢ` is an *spread element* with an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Sᵢ`, then a [*lower-bound inference*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/expressions.md#116310-lower-bound-inferences) is made *from* `Sᵢ` *to* `Tₑ`. | ||
> * If `E` is a *collection expression* with elements `Eᵢ`, and `T` is a type with an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Tₑ` or `T` is a *nullable value type* `T0?` and `T0` has an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Tₑ`, then for each `Eᵢ` a *collection element inference* is made *from* `Eᵢ` *to* `Tₑ`. | ||
> * *[existing rules from first phase]* ... | ||
> | ||
> A *collection element inference* is made *from* a collection expression element `Eᵢ` *to* an *iteration type* `Tₑ` as follows: | ||
> * If `Eᵢ` is an *expression element*, then an *input type inference* is made *from* `Eᵢ` *to* `Tₑ`. | ||
> * If `Eᵢ` is a *spread element* `..s`, then a *spread element inference* is made *from* `s` *to* `Tₑ`. | ||
> | ||
> A *spread element inference* is made *from* an expression `E` to a collection expression *iteration type* `Tₑ` as follows: | ||
> * If `E` is a *collection expression* with elements `Eᵢ`, then for each `Eᵢ` a *collection element inference* is made *from* `Eᵢ` *to* `Tₑ`. | ||
> * If `E` is a [*conditional expression*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/expressions.md#1115-conditional-operator) `b ? x : y`, then a *spread element inference* is made *from* `x` *to* `Tₑ` and a *spread element inference* is made *from* `y` *to* `Tₑ`. | ||
> * If `E` has an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Eₑ`, then a [*lower-bound inference*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/expressions.md#116310-lower-bound-inferences) is made *from* `Eₑ` *to* `Tₑ`. | ||
|
||
> 11.6.3.7 Output type inferences | ||
> | ||
> An *output type inference* is made *from* an expression `E` *to* a type `T` in the following way: | ||
> | ||
> * If `E` is a *collection expression* with elements `Eᵢ`, and `T` is a type with an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Tₑ` or `T` is a *nullable value type* `T0?` and `T0` has an [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/statements.md#1295-the-foreach-statement) `Tₑ`, then for each `Eᵢ`: | ||
> * If `Eᵢ` is an *expression element*, then an *output type inference* is made *from* `Eᵢ` *to* `Tₑ`. | ||
> * If `Eᵢ` is an *spread element*, no inference is made from `Eᵢ`. | ||
> * If `Eᵢ` is a *spread element*, no inference is made from `Eᵢ`. | ||
> * *[existing rules from output type inferences]* ... | ||
|
||
## Extension methods | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only curious: Is there anywhere in the spec that states this in the opposite form, for purposes like target-typed new inside a conditional expression looking outward? Could both directions be unified somehow in some definition of type inference for any language constructs that support "seeing through" them?