You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add support for using declarations and async using (#672)
* add support or using declarations and async using
* Add IAsyncDisposable to the library
* Update statements.md
* Add example annotation
* add annotation for examples
* Create SupportLocalVarDecl.cs
* fix section number
* fix links
* Update statements.md
* fix header label
* describe async using expansion
Include the rule that the result of `DisposeAsync` must be `await`ed.
* Update standard/statements.md
Co-authored-by: Nigel-Ecma <[email protected]>
* Address language in review comments
This commit address the comments in this review: #672 (review)
except for any grammar changes.
Notes on where the edit differs (slightly) from the recommendation:
- The rule that a `using` statement can't declare an `out` variable applies to all forms. I added that in 13.14.1.
- I added a note that ref structs can't use the form of `await using`. It's a note because it follows from the normative restriction that `ref struct` types can't implement interfaces, but `await using` requires the resource type to implement `IAsyncDisposable`.
- The rules for `goto`, `break`, etc. are a note at the end of 13.14.1. They can be derived from the expanded forms of the `using` statement.
- The rule for a using declaration in a switch block remains. It's specific to a using declaration.
* Update grammar
Update grammar based on #672 (review)
* refactor foreach into common sections
Refactor the common processing for `foreach` and `await foreach`. The common processing is the determination of the iteration type and such.
The lowering is still split into different sections. That could be combined, but as each is specific, I'm not sure it provides a simplification.
* Add attribute section for async iterator cancellation
In addition, add usings to the relevant template
* add normative text for async cancellation
* Address feedback in #606
Post meeting, a number of reviews were left on #606 to be addressed in this PR. All but one are addressed in this commit
* Clarify sync vs. async enumerable objects.
* Add missing note.
* address some feedback
Address some of the feedback items before the upcoming meeting.
* Apply suggestions from code review
Co-authored-by: Jon Skeet <[email protected]>
* Apply suggestions from code review
Co-authored-by: Joseph Musser <[email protected]>
Co-authored-by: Jon Skeet <[email protected]>
* Interim checkin.
* Add language on cancellation
I stopped a bit short here. The more I dove into the cancellation protocol, the more it felt like this was an implementation spec, not a standard. Adding a good illustrative sample would mean showing the implementation of the async state machine for an async iterator. That seems to restricting.
* Remove `using` from duplicated grammar
* Apply suggestions from code review
Co-authored-by: Jon Skeet <[email protected]>
* Apply suggestions from code review
Co-authored-by: Jon Skeet <[email protected]>
* respond to feedback from July 30 meeting
* Apply suggestions from code review
Co-authored-by: Nigel-Ecma <[email protected]>
* Edits, part 1
Respond to review from @Nigel-Ecma on 8/15, *except* the summary comment.
* Address overall review comments
This might still be a bit hand-wavy, but better.
* Apply suggestions from code review
Co-authored-by: Joseph Musser <[email protected]>
* respond to feedback.
* Apply suggestions from code review
Co-authored-by: Joseph Musser <[email protected]>
* Add async dispose specification
* Update sample, fix build issue.
* Apply suggestions from code review
Co-authored-by: Jon Skeet <[email protected]>
* remaining review comments as of 9/2
* Update standard/attributes.md
Co-authored-by: Nigel-Ecma <[email protected]>
* Clarify dispose and finally
Clarify the semantics of DisposeAsync and Dispose for asynchronous enumerators.
* respond to final feedback.
* Apply suggestions from code review
Co-authored-by: Nigel-Ecma <[email protected]>
* Respond to feedback.
Update cancellation example. Make description of `finally` clauses consistent.
* Update sample and fix warnings
Update the sample per Nigel & Joseph's comments.
Also, update anchors that have changed due to the index and range merge.
* Update standard/attributes.md
Co-authored-by: Joseph Musser <[email protected]>
* Update standard/attributes.md
* Update standard/attributes.md
* Update standard/attributes.md
* fix sample
* ensure sample compiles in C# 8
---------
Co-authored-by: Bill Wagner <[email protected]>
Co-authored-by: Nigel-Ecma <[email protected]>
Co-authored-by: Jon Skeet <[email protected]>
Co-authored-by: Joseph Musser <[email protected]>
Copy file name to clipboardExpand all lines: standard/attributes.md
+47Lines changed: 47 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -495,6 +495,7 @@ A number of attributes affect the language in some way. These attributes include
495
495
- `System.ObsoleteAttribute` ([§23.5.4](attributes.md#2354-the-obsolete-attribute)), which is used to mark a member as obsolete.
496
496
- `System.Runtime.CompilerServices.AsyncMethodBuilderAttribute` ([§23.5.5](attributes.md#2355-the-asyncmethodbuilder-attribute)), which is used to establish a task builder for an async method.
497
497
- `System.Runtime.CompilerServices.CallerLineNumberAttribute` ([§23.5.6.2](attributes.md#23562-the-callerlinenumber-attribute)), `System.Runtime.CompilerServices.CallerFilePathAttribute` ([§23.5.6.3](attributes.md#23563-the-callerfilepath-attribute)), and `System.Runtime.CompilerServices.CallerMemberNameAttribute` ([§23.5.6.4](attributes.md#23564-the-callermembername-attribute)), which are used to supply information about the calling context to optional parameters.
498
+
- `System.Runtime.CompilerServices.EnumeratorCancellationAttribute` (§enumerator-cancellation), which is used to specify parameter for the cancellation token in an asynchronous iterator.
498
499
499
500
The Nullable static analysis attributes ([§23.5.7](attributes.md#2357-code-analysis-attributes)) can improve the correctness of warnings generated for nullabilities and null states ([§8.9.5](types.md#895-nullabilities-and-null-states)).
500
501
@@ -1060,6 +1061,52 @@ Specifies that a nullable argument won’t be `null` when the method returns the
1060
1061
>
1061
1062
>*endexample*
1062
1063
1064
+
### §enumerator-cancellation The EnumeratorCancellation attribute
Copy file name to clipboardExpand all lines: standard/classes.md
+20-17Lines changed: 20 additions & 17 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5496,27 +5496,29 @@ This allows the context to keep track of how many `void`-returning async functio
5496
5496
5497
5497
### 15.15.1 General
5498
5498
5499
-
A function member ([§12.6](expressions.md#126-function-members)) or local function ([§13.6.4](statements.md#1364-local-function-declarations)) implemented using an iterator block ([§13.3](statements.md#133-blocks)) is called an ***iterator***. An iterator block may be used as the body of a function member as long as the return type of the corresponding function member is one of the enumerator interfaces ([§15.15.2](classes.md#15152-enumerator-interfaces)) or one of the enumerable interfaces ([§15.15.3](classes.md#15153-enumerable-interfaces)).
5499
+
A function member ([§12.6](expressions.md#126-function-members)) or local function ([§13.6.4](statements.md#1364-local-function-declarations)) implemented using an iterator block ([§13.3](statements.md#133-blocks)) is called an ***iterator***. An iterator block may be used as the body of a function as long as the return type of the corresponding function is one of the enumerator interfaces ([§15.15.2](classes.md#15152-enumerator-interfaces)) or one of the enumerable interfaces ([§15.15.3](classes.md#15153-enumerable-interfaces)).
5500
5500
5501
-
An async function ([§15.14](classes.md#1514-async-functions)) implemented using an iterator block ([§13.3](statements.md#133-blocks)) is called an ***asynchronous iterator***. An asynchronous iterator block may be used as the body of a function member as long as the return type of the corresponding function member is the asynchronous enumerator interfaces ([§15.15.2](classes.md#15152-enumerator-interfaces)) or the asynchronous enumerable interfaces ([§15.15.3](classes.md#15153-enumerable-interfaces)).
5501
+
An async function ([§15.14](classes.md#1514-async-functions)) or local function ([§13.6.4](statements.md#1364-local-function-declarations)) implemented using an iterator block ([§13.3](statements.md#133-blocks)) is called an ***asynchronous iterator***. An asynchronous iterator block may be used as the body of a function as long as the return type of the corresponding function is the asynchronous enumerator interface ([§15.15.2](classes.md#15152-enumerator-interfaces)) or the asynchronous enumerable interface ([§15.15.3](classes.md#15153-enumerable-interfaces)).
5502
5502
5503
5503
An iterator block may occur as a *method_body*, *operator_body* or *accessor_body*, whereas events, instance constructors, static constructors and finalizer shall not be implemented as synchronous or asynchronous iterators.
5504
5504
5505
-
When a function member or local function is implemented using an iterator block, it is a compile-time error for the parameter list of the function member to specify any `in`, `out`, or `ref` parameters, or an parameter of a `ref struct` type.
5505
+
When a function is implemented using an iterator block, it is a compile-time error for the parameter list of the function to specify any `in`, `out`, or `ref` parameters, or a parameter of a `ref struct` type.
5506
+
5507
+
An asynchronous iterator shall support cancellation of the asynchronous operation. This is described in §enumerator-cancellation.
5506
5508
5507
5509
### 15.15.2 Enumerator interfaces
5508
5510
5509
-
The ***enumerator interfaces*** are the non-generic interface `System.Collections.IEnumerator` and all instantiations of the generic interfaces`System.Collections.Generic.IEnumerator<T>`.
5511
+
The ***enumerator interfaces*** are the non-generic interface `System.Collections.IEnumerator` and the generic interface`System.Collections.Generic.IEnumerator<T>`.
5510
5512
5511
-
The ***asynchronous enumerator interfaces***are all instantiations of the generic interface `System.Collections.Generic.IAsyncEnumerator<T>`.
5513
+
The ***asynchronous enumerator interface***is the generic interface `System.Collections.Generic.IAsyncEnumerator<T>`.
5512
5514
5513
5515
For the sake of brevity, in this subclause and its siblings these interfaces are referenced as `IEnumerator`, `IEnumerator<T>`, and `IAsyncEnumerator<T>`, respectively.
5514
5516
5515
5517
### 15.15.3 Enumerable interfaces
5516
5518
5517
-
The ***enumerable interfaces*** are the non-generic interface `System.Collections.IEnumerable` and all instantiations of the generic interfaces `System.Collections.Generic.IEnumerable<T>`.
5519
+
The ***enumerable interfaces*** are the non-generic interface `System.Collections.IEnumerable` and the generic interfaces `System.Collections.Generic.IEnumerable<T>`.
5518
5520
5519
-
The ***asynchronous enumerable interfaces***are all instantiations of the generic interface `System.Collections.Generic.IAsyncEnumerable<T>`.
5521
+
The ***asynchronous enumerable interface***is the generic interface `System.Collections.Generic.IAsyncEnumerable<T>`.
5520
5522
5521
5523
For the sake of brevity, in this subclause and its siblings these interfaces are referenced as `IEnumerable`, `IEnumerable<T>`, and `IAsyncEnumerable<T>`, respectively.
5522
5524
@@ -5531,13 +5533,13 @@ An iterator produces a sequence of values, all of the same type. This type is ca
5531
5533
5532
5534
#### 15.15.5.1 General
5533
5535
5534
-
When a function member or local function returning an enumerator interface type is implemented using an iterator block, invoking the function does not immediately execute the code in the iterator block. Instead, an ***enumerator object*** is created and returned. This object encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object’s `MoveNext` or `MoveNextAsync` method is invoked. An enumerator object has the following characteristics:
5536
+
When a function member or local function returning an enumerator interface type or an asynchronous enumerator interface type is implemented using an iterator block, invoking the function does not immediately execute the code in the iterator block. Instead, an ***enumerator object*** is created and returned. This object encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object’s `MoveNext` or `MoveNextAsync` method is invoked. An enumerator object has the following characteristics:
5535
5537
5536
5538
- It implements `System.IDisposable`, `IEnumerator` and `IEnumerator<T>`, or `System.IAsyncDisposable` and `IAsyncEnumerator<T>`, where `T` is the yield type of the iterator.
5537
-
- It is initialized with a copy of the argument values (if any) and instance value passed to the function member.
5539
+
- It is initialized with a copy of the argument values (if any) and instance value passed to the function.
5538
5540
- It has four potential states, **before**, **running**, **suspended**, and **after**, and is initially in the **before** state.
5539
5541
5540
-
An enumerator object is typically an instance of a compiler-generated enumerator class that encapsulates the code in the iterator block and implements the enumerator interfaces, but other methods of implementation are possible. If an enumerator class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use ([§6.4.3](lexical-structure.md#643-identifiers)).
5542
+
An enumerator object is typically an instance of a compiler-generated enumerator class that encapsulates the code in the iterator block and implements the enumerator interfaces, but other methods of implementation are possible. If an enumerator class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function, it will have private accessibility, and it will have a name reserved for compiler use ([§6.4.3](lexical-structure.md#643-identifiers)).
5541
5543
5542
5544
An enumerator object may implement more interfaces than those specified above.
5543
5545
@@ -5573,6 +5575,8 @@ The precise action performed by `MoveNext` or `MoveNextAsync` depends on the sta
5573
5575
5574
5576
When `MoveNext` executes the iterator block, execution can be interrupted in four ways: By a `yield return` statement, by a `yield break` statement, by encountering the end of the iterator block, and by an exception being thrown and propagated out of the iterator block.
5575
5577
5578
+
> *Note*: `MoveNextAsync` is suspended if it evaluates an `await` expression that awaits a task type that hasn't completed. *end note*
5579
+
5576
5580
- When a `yield return` statement is encountered ([§9.4.4.20](variables.md#94420-yield-statements)):
5577
5581
- The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the `Current` property of the enumerator object.
5578
5582
- Execution of the iterator body is suspended. The values of all local variables and parameters (including `this`) are saved, as is the location of this `yield return` statement. If the `yield return` statement is within one or more `try` blocks, the associated finally blocks are *not* executed at this time.
@@ -5616,21 +5620,20 @@ The `Dispose` or `DisposeAsync` method is used to clean up the iteration by brin
5616
5620
5617
5621
#### 15.15.6.1 General
5618
5622
5619
-
When a function member or local function returning an enumerable interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. Instead, an ***enumerable object*** is created and returned.
5623
+
When a function member or local function returning an enumerable interface type or an async enumerable interface type is implemented using an iterator block, invoking the function does not immediately execute the code in the iterator block. Instead, an ***enumerable object*** is created and returned.
5620
5624
5621
-
The enumerable object’s `GetEnumerator` or `GetAsyncEnumerator` method returns an enumerator object that encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object’s `MoveNext` or `MoveNextAsync` method is invoked. An enumerable object has the following characteristics:
5625
+
A synchronous enumerable object implements `IEnumerable` and `IEnumerable<T>`, where `T` is the yield type of the iterator. Its `GetEnumerator` method returns an enumerator object (§15.15.5). An async enumerable object implements `IAsyncEnumerable<T>` where `T` is the yield type of the iterator. Its `GetAsyncEnumerator` method returns an asynchronous enumerator object (§15.15.5).
5622
5626
5623
-
- It implements `IEnumerable` and `IEnumerable<T>` or `IAsyncEnumerable<T>`, where `T` is the yield type of the iterator.
5624
-
- It is initialized with a copy of the argument values (if any) and instance value passed to the function member.
5627
+
An enumerable object is initialized with a copy of the argument values (if any) and instance value passed to the function.
5625
5628
5626
-
An enumerable object is typically an instance of a compiler-generated enumerable class that encapsulates the code in the iterator block and implements the enumerable interfaces, but other methods of implementation are possible. If an enumerable class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use ([§6.4.3](lexical-structure.md#643-identifiers)).
5629
+
An enumerable object is typically an instance of a compiler-generated enumerable class that encapsulates the code in the iterator block and implements the enumerable interfaces, but other methods of implementation are possible. If an enumerable class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function, it will have private accessibility, and it will have a name reserved for compiler use ([§6.4.3](lexical-structure.md#643-identifiers)).
5627
5630
5628
5631
An enumerable object may implement more interfaces than those specified above.
5629
5632
5630
-
> *Note*: For example, an enumerable object may also implement `IEnumerator` and `IEnumerator<T>`, enabling it to serve as both an enumerable and an enumerator. Typically, such an implementation would return its own instance (to save allocations) from the first call to `GetEnumerator`. Subsequent invocations of `GetEnumerator`, if any, would return a new class instance, typically of the same class, so that calls to different enumerator instances will not affect each other. It cannot return the same instance even if the previous enumerator has already enumerated past the end of the sequence, since all future calls to an exhausted enumerator must throw exceptions. *end note*
5633
+
> *Note*: For example, an enumerable object may also implement `IEnumerator` and `IEnumerator<T>`, enabling it to serve as both an enumerable and an enumerator. Typically, such an implementation would return its own instance (to save allocations) from the first call to `GetEnumerator`. Subsequent invocations of `GetEnumerator`, if any, would return a new class instance, typically of the same class, so that calls to different enumerator instances will not affect each other. *end note*
5631
5634
5632
5635
#### 15.15.6.2 The GetEnumerator or GetAsyncEnumerator method
5633
5636
5634
5637
An enumerable object provides an implementation of the `GetEnumerator` methods of the `IEnumerable` and `IEnumerable<T>` interfaces. The two `GetEnumerator` methods share a common implementation that acquires and returns an available enumerator object. The enumerator object is initialized with the argument values and instance value saved when the enumerable object was initialized, but otherwise the enumerator object functions as described in [§15.15.5](classes.md#15155-enumerator-objects).
5635
5638
5636
-
An asynchronous enumerable object provides an implementation of the `GetAsyncEnumerator` method of the `IAsyncEnumerable<T>` interface. This method returns an available asynchronous enumerator object. The enumerator object is initialized with the argument values and instance value saved when the enumerable object was initialized, but otherwise the enumerator object functions as described in [§15.15.5](classes.md#15155-enumerator-objects).
5639
+
An asynchronous enumerable object provides an implementation of the `GetAsyncEnumerator` method of the `IAsyncEnumerable<T>` interface. This method returns an available asynchronous enumerator object. The enumerator object is initialized with the argument values and instance value saved when the enumerable object was initialized, including the optional cancellation token, but otherwise the enumerator object functions as described in [§15.15.5](classes.md#15155-enumerator-objects). An asynchronous iterator method can mark one parameter as the cancellation token using `System.Runtime.CompilerServices.EnumeratorCancellationAttribute` (§enumerator-cancellation). An implementation shall provide a mechanism to combine cancellation tokens such that an asynchronous iterator is canceled when either cancellation token (the argument to `GetAsyncEnumerator` or the argument attributed with the attribute `System.Runtime.CompilerServices.EnumeratorCancellationAttribute`) requests cancellation.
0 commit comments