未验证 提交 de6c369a 编写于 作者: J Julien Couvreur 提交者: GitHub

[EnumeratorCancellation] combines tokens (#35326)

上级 74d697a3
......@@ -3,7 +3,7 @@ async-streams (C# 8.0)
Async-streams are asynchronous variants of enumerables, where getting the next element may involve an asynchronous operation. They are types that implement `IAsyncEnumerable<T>`.
```C#
```csharp
// Those interfaces will ship as part of .NET Core 3
namespace System.Collections.Generic
{
......@@ -40,7 +40,7 @@ An async-iterator method is a method that:
3. uses both `await` syntax (`await` expression, `await foreach` or `await using` statements) and `yield` statements (`yield return`, `yield break`).
For example:
```C#
```csharp
async IAsyncEnumerable<int> GetValuesFromServer()
{
while (true)
......@@ -86,7 +86,7 @@ thereby allowing consumers of async-streams to control cancellation.
A producer of async-streams can make use of the cancellation token by writing an
`IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken)` async-iterator method in a custom type.
```C#
```csharp
E e = ((C)(x)).GetAsyncEnumerator(default);
try
{
......@@ -117,7 +117,8 @@ But it contains additional state:
- a promise of a value-or-end,
- a current yielded value of type `T`,
- an `int` capturing the id of the thread that created it,
- a `bool` flag indicating "dispose mode".
- a `bool` flag indicating "dispose mode",
- a `CancellationTokenSource` for combining tokens (in enumerables).
The central method of the state machine is `MoveNext()`. It gets run by `MoveNextAsync()`, or as a background continuation initiated from these from an `await` in the method.
......@@ -133,8 +134,8 @@ Compared to the state machine for a regular async method, the `MoveNext()` for a
- to support handling a `yield return` statement, which saves the current value and fulfills the promise with result `true`,
- to support handling a `yield break` statement, which sets the dispose mode on and jumps to the enclosing `finally` or exit,
- to dispatch execution to `finally` blocks (when disposing),
- to exit the method, which fulfills the promise with result `false`,
- to catch exceptions, which set the exception into the promise.
- to exit the method, which disposes the `CancellationTokenSource` (if any) and fulfills the promise with result `false`,
- to catch exceptions, which disposes the `CancellationTokenSource` (if any) and sets the exception into the promise.
(The handling of an `await` is unchanged)
This is reflected in the implementation, which extends the lowering machinery for async methods to:
......@@ -143,7 +144,7 @@ This is reflected in the implementation, which extends the lowering machinery fo
3. produce additional state and logic for the promise itself (see `AsyncIteratorRewriter`, which produces various other members: `MoveNextAsync`, `Current`, `DisposeAsync`,
and some members supporting the resettable `ValueTask` behavior, namely `GetResult`, `SetStatus`, `OnCompleted`).
```C#
```csharp
ValueTask<bool> MoveNextAsync()
{
if (state == StateMachineStates.FinishedStateMachine)
......@@ -162,13 +163,13 @@ ValueTask<bool> MoveNextAsync()
}
```
```C#
```csharp
T Current => current;
```
The kick-off method and the initialization of the state machine for an async-iterator method follows those for regular iterator methods.
In particular, the synthesized `GetAsyncEnumerator()` method is like `GetEnumerator()` except that it sets the initial state to to StateMachineStates.NotStartedStateMachine (-1):
```C#
```csharp
IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken token)
{
{StateMachineType} result;
......@@ -182,13 +183,30 @@ IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken token)
{
result = new {StateMachineType}(InitialState);
}
/* copy each parameter proxy, or copy the token parameter if it's not default for any parameter marked with [EnumeratorCancellation] attribute */
/* copy each parameter proxy, or in the case of the parameter marked with [EnumeratorCancellation] combine it with `GetAsyncEnumerator`'s `token` parameter */
}
```
For the parameter with `[EnumeratorCancellation]`, `GetAsyncEnumerator` initializes it by combining the two available tokens:
```csharp
if (this.parameterProxy.Equals(default))
{
result.parameter = token;
}
else if (token.Equals(this.parameterProxy) || token.Equals(default))
{
result.parameter = this.parameterProxy;
}
else
{
result.combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(this.parameterProxy, token);
result.parameter = combinedTokens.Token;
}
```
For a discussion of the threadID check, see https://github.com/dotnet/corefx/issues/3481
Similarly, the kick-off method is much like those of regular iterator methods:
```C#
```csharp
{
{StateMachineType} result = new {StateMachineType}(StateMachineStates.FinishedStateMachine); // -2
/* save parameters into parameter proxies */
......@@ -227,7 +245,7 @@ Looking at disposal from the perspective of a given `finally` block, the code in
- in dispose mode, following a nested `finally`.
A `yield return` is lowered as:
```C#
```csharp
_current = expression;
_state = <next_state>;
goto <exprReturnTruelabel>; // which does _valueOrEndPromise.SetResult(true); return;
......@@ -239,12 +257,12 @@ if (disposeMode) /* jump to enclosing finally or exit */
```
A `yield break` is lowered as:
```C#
```csharp
disposeMode = true;
/* jump to enclosing finally or exit */
```
```C#
```csharp
ValueTask IAsyncDisposable.DisposeAsync()
{
if (state >= StateMachineStates.NotStartedStateMachine /* -1 */)
......@@ -266,7 +284,7 @@ ValueTask IAsyncDisposable.DisposeAsync()
##### Regular versus extracted finally
When the `finally` clause contains no `await` expressions, a `try/finally` is lowered as:
```C#
```csharp
try
{
...
......@@ -280,7 +298,7 @@ if (disposeMode) /* jump to enclosing finally or exit */
```
When a `finally` contains `await` expressions, it is extracted before async rewriting (by AsyncExceptionHandlerRewriter). In those cases, we get:
```C#
```csharp
try
{
...
......@@ -325,7 +343,7 @@ The result of invoking `DisposeAsync` from states -1 or N is unspecified. This c
^ | | |
| done and disposed | | yield return |
+-----------------------------------+ +-----------------------> -N
| | |
| or exception thrown | |
| | |
| yield | |
| break | DisposeAsync |
......
......@@ -5901,6 +5901,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="WRN_UndecoratedCancellationTokenParameter_Title" xml:space="preserve">
<value>Async-iterator member has one or more parameters of type 'CancellationToken' but none of them is decorated with the 'EnumeratorCancellation' attribute, so the cancellation token parameter from the generated 'IAsyncEnumerable&lt;>.GetAsyncEnumerator' will be unconsumed</value>
</data>
<data name="ERR_MultipleEnumeratorCancellationAttributes" xml:space="preserve">
<value>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</value>
</data>
<data name="ERR_OverrideRefConstraintNotSatisfied" xml:space="preserve">
<value>Method '{0}' specifies a 'class' constraint for type parameter '{1}', but corresponding type parameter '{2}' of overridden or explicitly implemented method '{3}' is not a reference type.</value>
</data>
......
......@@ -1601,6 +1601,7 @@ internal enum ErrorCode
ERR_AttributeNotOnEventAccessor = 8423,
WRN_UnconsumedEnumeratorCancellationAttributeUsage = 8424,
WRN_UndecoratedCancellationTokenParameter = 8425,
ERR_MultipleEnumeratorCancellationAttributes = 8426,
// available range
#region diagnostics introduced for recursive patterns
......
......@@ -12,6 +12,9 @@ internal sealed class AsyncIteratorInfo
// This `ManualResetValueTaskSourceCore<bool>` struct implements the `IValueTaskSource` logic
internal FieldSymbol PromiseOfValueOrEndField { get; }
// This `CancellationTokenSource` field helps combine two cancellation tokens
internal FieldSymbol CombinedTokensField { get; }
// Stores the current/yielded value
internal FieldSymbol CurrentField { get; }
......@@ -24,10 +27,11 @@ internal sealed class AsyncIteratorInfo
// Method to fulfill the promise with an exception: `void ManualResetValueTaskSourceCore<T>.SetException(Exception error)`
internal MethodSymbol SetExceptionMethod { get; }
public AsyncIteratorInfo(FieldSymbol promiseOfValueOrEndField, FieldSymbol currentField, FieldSymbol disposeModeField,
public AsyncIteratorInfo(FieldSymbol promiseOfValueOrEndField, FieldSymbol combinedTokensField, FieldSymbol currentField, FieldSymbol disposeModeField,
MethodSymbol setResultMethod, MethodSymbol setExceptionMethod)
{
PromiseOfValueOrEndField = promiseOfValueOrEndField;
CombinedTokensField = combinedTokensField;
CurrentField = currentField;
DisposeModeField = disposeModeField;
SetResultMethod = setResultMethod;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeGen;
......@@ -66,6 +67,7 @@ protected override BoundStatement GenerateSetResultCall()
// ... _exprReturnLabel: ...
// ... this.state = FinishedState; ...
// if (this.combinedTokens != null) { this.combinedTokens.Dispose(); this.combinedTokens = null; } // for enumerables only
// this.promiseOfValueOrEnd.SetResult(false);
// return;
// _exprReturnLabelTrue:
......@@ -74,7 +76,12 @@ protected override BoundStatement GenerateSetResultCall()
// ... _exitLabel: ...
// ... return; ...
return F.Block(
var builder = ArrayBuilder<BoundStatement>.GetInstance();
// if (this.combinedTokens != null) { this.combinedTokens.Dispose(); this.combinedTokens = null; } // for enumerables only
AddDisposeCombinedTokensIfNeeded(builder);
builder.AddRange(
// this.promiseOfValueOrEnd.SetResult(false);
generateSetResultOnPromise(false),
F.Return(),
......@@ -82,6 +89,8 @@ protected override BoundStatement GenerateSetResultCall()
// this.promiseOfValueOrEnd.SetResult(true);
generateSetResultOnPromise(true));
return F.Block(builder.ToImmutableAndFree());
BoundExpressionStatement generateSetResultOnPromise(bool result)
{
// Produce:
......@@ -91,13 +100,36 @@ BoundExpressionStatement generateSetResultOnPromise(bool result)
}
}
private void AddDisposeCombinedTokensIfNeeded(ArrayBuilder<BoundStatement> builder)
{
// if (this.combinedTokens != null) { this.combinedTokens.Dispose(); this.combinedTokens = null; } // for enumerables only
if (_asyncIteratorInfo.CombinedTokensField is object)
{
var combinedTokens = F.Field(F.This(), _asyncIteratorInfo.CombinedTokensField);
TypeSymbol combinedTokensType = combinedTokens.Type;
builder.Add(
F.If(F.ObjectNotEqual(combinedTokens, F.Null(combinedTokensType)),
thenClause: F.Block(
F.ExpressionStatement(F.Call(combinedTokens, F.WellKnownMethod(WellKnownMember.System_Threading_CancellationTokenSource__Dispose))),
F.Assignment(combinedTokens, F.Null(combinedTokensType)))));
}
}
protected override BoundStatement GenerateSetExceptionCall(LocalSymbol exceptionLocal)
{
var builder = ArrayBuilder<BoundStatement>.GetInstance();
// if (this.combinedTokens != null) { this.combinedTokens.Dispose(); this.combinedTokens = null; } // for enumerables only
AddDisposeCombinedTokensIfNeeded(builder);
// _promiseOfValueOrEnd.SetException(ex);
return F.ExpressionStatement(F.Call(
builder.Add(F.ExpressionStatement(F.Call(
F.InstanceField(_asyncIteratorInfo.PromiseOfValueOrEndField),
_asyncIteratorInfo.SetExceptionMethod,
F.Local(exceptionLocal)));
F.Local(exceptionLocal))));
return F.Block(builder.ToImmutableAndFree());
}
private BoundStatement GenerateJumpToCurrentFinallyOrExit()
......
......@@ -208,7 +208,7 @@ protected BoundCatchBlock GenerateExceptionHandling(LocalSymbol exceptionLocal)
// catch (Exception ex)
// {
// _state = finishedState;
// builder.SetException(ex); OR _promiseOfValueOrEnd.SetException(ex); /* for async-iterator method */
// builder.SetException(ex); OR if (this.combinedTokens != null) this.combinedTokens.Dispose(); _promiseOfValueOrEnd.SetException(ex); /* for async-iterator method */
// return;
// }
......@@ -216,7 +216,7 @@ protected BoundCatchBlock GenerateExceptionHandling(LocalSymbol exceptionLocal)
BoundStatement assignFinishedState =
F.ExpressionStatement(F.AssignmentExpression(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)));
// builder.SetException(ex); OR _promiseOfValueOrEnd.SetException(ex);
// builder.SetException(ex); OR if (this.combinedTokens != null) this.combinedTokens.Dispose(); _promiseOfValueOrEnd.SetException(ex);
BoundStatement callSetException = GenerateSetExceptionCall(exceptionLocal);
return new BoundCatchBlock(
......@@ -234,6 +234,8 @@ protected BoundCatchBlock GenerateExceptionHandling(LocalSymbol exceptionLocal)
protected virtual BoundStatement GenerateSetExceptionCall(LocalSymbol exceptionLocal)
{
Debug.Assert(!CurrentMethod.IsIterator); // an override handles async-iterators
// builder.SetException(ex);
return F.ExpressionStatement(
F.Call(
......
......@@ -2,6 +2,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
......@@ -18,6 +19,7 @@ private sealed class AsyncIteratorRewriter : AsyncRewriter
private FieldSymbol _promiseOfValueOrEndField; // this struct implements the IValueTaskSource logic
private FieldSymbol _currentField; // stores the current/yielded value
private FieldSymbol _disposeModeField; // whether the state machine is in dispose mode (ie. skipping all logic except that in `catch` and `finally`, yielding no new elements)
private FieldSymbol _combinedTokensField; // CancellationTokenSource for combining tokens
// true if the iterator implements IAsyncEnumerable<T>,
// false if it implements IAsyncEnumerator<T>
......@@ -46,7 +48,12 @@ protected override void VerifyPresenceOfRequiredAPIs(DiagnosticBag bag)
if (_isEnumerable)
{
EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, bag);
EnsureWellKnownMember(WellKnownMember.System_Threading_CancellationToken__Equals, bag);
EnsureWellKnownMember(WellKnownMember.System_Threading_CancellationTokenSource__CreateLinkedTokenSource, bag);
EnsureWellKnownMember(WellKnownMember.System_Threading_CancellationTokenSource__Token, bag);
EnsureWellKnownMember(WellKnownMember.System_Threading_CancellationTokenSource__Dispose, bag);
}
EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__MoveNextAsync, bag);
EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__get_Current, bag);
......@@ -124,6 +131,14 @@ protected override void GenerateControlFields()
// Add a field: bool disposeMode
_disposeModeField = F.StateMachineField(boolType, GeneratedNames.MakeDisposeModeFieldName());
if (_isEnumerable && this.method.Parameters.Any(p => p is SourceComplexParameterSymbol { HasEnumeratorCancellationAttribute: true }))
{
// Add a field: CancellationTokenSource combinedTokens
_combinedTokensField = F.StateMachineField(
F.WellKnownType(WellKnownType.System_Threading_CancellationTokenSource),
GeneratedNames.MakeAsyncIteratorCombinedTokensFieldName());
}
}
protected override void GenerateConstructor()
......@@ -176,6 +191,57 @@ protected override void InitializeStateMachine(ArrayBuilder<BoundStatement> body
F.New(stateMachineType.Constructor.AsMember(frameType), F.Literal(initialState))));
}
protected override BoundStatement InitializeParameterField(MethodSymbol getEnumeratorMethod, ParameterSymbol parameter, BoundExpression resultParameter, BoundExpression parameterProxy)
{
BoundStatement result;
if (_combinedTokensField is object &&
parameter is SourceComplexParameterSymbol { HasEnumeratorCancellationAttribute: true })
{
// For the parameter with [EnumeratorCancellation]
// if (this.parameterProxy.Equals(default))
// {
// result.parameter = token;
// }
// else if (token.Equals(this.parameterProxy) || token.Equals(default))
// {
// result.parameter = this.parameterProxy;
// }
// else
// {
// result.combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(this.parameterProxy, token);
// result.parameter = combinedTokens.Token;
// }
BoundParameter tokenParameter = F.Parameter(getEnumeratorMethod.Parameters[0]);
BoundFieldAccess combinedTokens = F.Field(F.This(), _combinedTokensField);
result = F.If(
// if (this.parameterProxy.Equals(default))
F.Call(parameterProxy, WellKnownMember.System_Threading_CancellationToken__Equals, F.Default(parameterProxy.Type)),
// result.parameter = token;
thenClause: F.Assignment(resultParameter, tokenParameter),
elseClauseOpt: F.If(
// else if (token.Equals(this.parameterProxy) || token.Equals(default))
F.LogicalOr(
F.Call(tokenParameter, WellKnownMember.System_Threading_CancellationToken__Equals, parameterProxy),
F.Call(tokenParameter, WellKnownMember.System_Threading_CancellationToken__Equals, F.Default(tokenParameter.Type))),
// result.parameter = this.parameterProxy;
thenClause: F.Assignment(resultParameter, parameterProxy),
elseClauseOpt: F.Block(
// result.combinedTokens = CancellationTokenSource.CreateLinkedTokenSource(this.parameterProxy, token);
F.Assignment(combinedTokens, F.StaticCall(WellKnownMember.System_Threading_CancellationTokenSource__CreateLinkedTokenSource, parameterProxy, tokenParameter)),
// result.parameter = result.combinedTokens.Token;
F.Assignment(resultParameter, F.Property(combinedTokens, WellKnownMember.System_Threading_CancellationTokenSource__Token)))));
}
else
{
// For parameters that don't have [EnumeratorCancellation], initialize their parameter fields
// result.parameter = this.parameterProxy;
result = F.Assignment(resultParameter, parameterProxy);
}
return result;
}
protected override BoundStatement GenerateStateMachineCreation(LocalSymbol stateMachineVariable, NamedTypeSymbol frameType)
{
// return local;
......@@ -590,7 +656,7 @@ protected override void GenerateMoveNext(SynthesizedImplementationMethod moveNex
method: method,
methodOrdinal: _methodOrdinal,
asyncMethodBuilderMemberCollection: _asyncMethodBuilderMemberCollection,
asyncIteratorInfo: new AsyncIteratorInfo(_promiseOfValueOrEndField, _currentField, _disposeModeField, setResultMethod, setExceptionMethod),
asyncIteratorInfo: new AsyncIteratorInfo(_promiseOfValueOrEndField, _combinedTokensField, _currentField, _disposeModeField, setResultMethod, setExceptionMethod),
F: F,
state: stateField,
builder: _builderField,
......
......@@ -375,10 +375,7 @@ protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSy
// result = new {StateMachineType}({initialState});
// }
//
// // Initialize each parameter fields
// result.parameter = this.parameterProxy;
// OR
// if (token.Equals(default)) { result.parameter = this.parameterProxy; } else { result.parameter = token; } // for async-enumerable parameters marked with [EnumeratorCancellation]
// result.parameter = this.parameterProxy; // OR more complex initialization for async-iterator parameter marked with [EnumeratorCancellation]
// The implementation doesn't depend on the method body of the iterator method.
// Only on its parameters and staticness.
......@@ -455,29 +452,11 @@ protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSy
CapturedSymbolReplacement proxy;
if (copyDest.TryGetValue(parameter, out proxy))
{
// result.parameter = this.parameterProxy;
BoundExpression left = proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable));
BoundStatement copy = F.Assignment(
left,
copySrc[parameter].Replacement(F.Syntax, stateMachineType => F.This()));
if (this.method.IsAsync && parameter is SourceComplexParameterSymbol { HasEnumeratorCancellationAttribute: true })
{
ParameterSymbol tokenParameter = getEnumeratorMethod.Parameters[0];
// For any async-enumerable parameter marked with [EnumeratorCancellation] attribute, conditionally copy GetAsyncEnumerator's cancellation token parameter instead
// if (token.Equals(default))
// result.parameter = this.parameterProxy;
// else
// result.parameter = token;
copy = F.If(
// if (token.Equals(default))
F.Call(F.Parameter(tokenParameter), WellKnownMember.System_Threading_CancellationToken__Equals, F.Default(tokenParameter.Type)),
// result.parameter = this.parameterProxy;
copy,
// result.parameter = token;
F.Assignment(left, F.Parameter(tokenParameter)));
}
// result.parameter
BoundExpression resultParameter = proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable));
// this.parameterProxy
BoundExpression parameterProxy = copySrc[parameter].Replacement(F.Syntax, stateMachineType => F.This());
BoundStatement copy = InitializeParameterField(getEnumeratorMethod, parameter, resultParameter, parameterProxy);
bodyBuilder.Add(copy);
}
......@@ -488,6 +467,14 @@ protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSy
return getEnumerator;
}
protected virtual BoundStatement InitializeParameterField(MethodSymbol getEnumeratorMethod, ParameterSymbol parameter, BoundExpression resultParameter, BoundExpression parameterProxy)
{
Debug.Assert(!method.IsIterator || !method.IsAsync); // an override handles async-iterators
// result.parameter = this.parameterProxy;
return F.Assignment(resultParameter, parameterProxy);
}
/// <summary>
/// Async-iterator methods use a GetAsyncEnumerator method just like the GetEnumerator of iterator methods.
/// But they need to do a bit more work (to reset the dispose mode).
......
......@@ -184,16 +184,25 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB
var location = this.Locations[0];
if (IsAsync)
{
// Warn for CancellationToken parameters in async-iterators with no parameter decorated with [EnumeratorCancellation]
var cancellationTokenType = DeclaringCompilation.GetWellKnownType(WellKnownType.System_Threading_CancellationToken);
var iAsyncEnumerableType = DeclaringCompilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T);
var enumeratorCancellationCount = Parameters.Count(p => p is SourceComplexParameterSymbol { HasEnumeratorCancellationAttribute: true });
if (ReturnType.OriginalDefinition.Equals(iAsyncEnumerableType) &&
(Bodies.blockBody != null || Bodies.arrowBody != null) &&
ParameterTypesWithAnnotations.Any(p => p.Type.Equals(cancellationTokenType)) &&
!Parameters.Any(p => p is SourceComplexParameterSymbol { HasEnumeratorCancellationAttribute: true }))
(Bodies.blockBody != null || Bodies.arrowBody != null))
{
// There could be more than one parameter that could be decorated with [EnumeratorCancellation] so we warn on the method instead
diagnostics.Add(ErrorCode.WRN_UndecoratedCancellationTokenParameter, location, this);
if (enumeratorCancellationCount == 0 &&
ParameterTypesWithAnnotations.Any(p => p.Type.Equals(cancellationTokenType)))
{
// Warn for CancellationToken parameters in async-iterators with no parameter decorated with [EnumeratorCancellation]
// There could be more than one parameter that could be decorated with [EnumeratorCancellation] so we warn on the method instead
diagnostics.Add(ErrorCode.WRN_UndecoratedCancellationTokenParameter, location, this);
}
if (enumeratorCancellationCount > 1)
{
// The [EnumeratorCancellation] attribute can only be used on one parameter
diagnostics.Add(ErrorCode.ERR_MultipleEnumeratorCancellationAttributes, location);
}
}
}
......
......@@ -39,7 +39,8 @@ internal enum GeneratedNameKind
DynamicCallSiteContainerType = 'o',
DynamicCallSiteField = 'p',
AsyncIteratorPromiseOfValueOrEndBackingField = 'v',
DisposeModeField = 'w', // last
DisposeModeField = 'w',
CombinedTokensField = 'x', // last
// Deprecated - emitted by Dev12, but not by Roslyn.
// Don't reuse the values because the debugger might encounter them when consuming old binaries.
......
......@@ -436,6 +436,12 @@ internal static string MakeAsyncIteratorPromiseOfValueOrEndFieldName()
return "<>v__promiseOfValueOrEnd";
}
internal static string MakeAsyncIteratorCombinedTokensFieldName()
{
Debug.Assert((char)GeneratedNameKind.CombinedTokensField == 'x');
return "<>x__combinedTokens";
}
internal static string MakeIteratorCurrentFieldName()
{
Debug.Assert((char)GeneratedNameKind.IteratorCurrentBackingField == '2');
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">Omezení new() nejde používat s omezením unmanaged.</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">Die new()-Einschränkung kann nicht mit der unmanaged-Einschränkung verwendet werden.</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">La restricción "new()" no se puede utilizar con la restricción "unmanaged"</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">La contrainte 'new()' ne peut pas être utilisée avec la contrainte 'unmanaged'</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">Non è possibile usare il vincolo 'new()' con il vincolo 'unmanaged'</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">new()' 制約は 'unmanaged' 制約と一緒には使用できません</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">new()' 제약 조건은 'unmanaged' 제약 조건과 함께 사용할 수 없습니다.</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">Ograniczenie „new()” nie może być używane z ograniczeniem „unmanaged”</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">A restrição 'new()' não pode ser usada com a restrição 'unmanaged'</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">Ограничение "new()" невозможно использовать вместе с ограничением "unmanaged"</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">'new()' kısıtlaması, 'unmanaged' kısıtlamasıyla kullanılamaz</target>
......
......@@ -282,6 +282,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">"new()" 约束不能与 "unmanaged" 约束一起使用</target>
......
......@@ -257,6 +257,11 @@
<target state="new">Multiple analyzer config files cannot be in the same directory ('{0}').</target>
<note />
</trans-unit>
<trans-unit id="ERR_MultipleEnumeratorCancellationAttributes">
<source>The attribute [EnumeratorCancellation] cannot be used on multiple parameters</source>
<target state="new">The attribute [EnumeratorCancellation] cannot be used on multiple parameters</target>
<note />
</trans-unit>
<trans-unit id="ERR_NewBoundWithUnmanaged">
<source>The 'new()' constraint cannot be used with the 'unmanaged' constraint</source>
<target state="translated">new()' 條件約束不能和 'unmanaged' 條件約束一起使用</target>
......
......@@ -492,6 +492,9 @@ internal enum WellKnownMember
System_Runtime_CompilerServices_SwitchExpressionException__ctorObject,
System_Threading_CancellationToken__Equals,
System_Threading_CancellationTokenSource__CreateLinkedTokenSource,
System_Threading_CancellationTokenSource__Token,
System_Threading_CancellationTokenSource__Dispose,
Count
......
......@@ -3396,6 +3396,29 @@ static WellKnownMembers()
1, // Method Signature
(byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, // Return Type
(byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_CancellationToken - WellKnownType.ExtSentinel), // Argument: CancellationToken
// System_Threading_CancellationTokenSource__CreateLinkedTokenSource
(byte)(MemberFlags.Method | MemberFlags.Static), // Flags
(byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_CancellationTokenSource - WellKnownType.ExtSentinel), // DeclaringTypeId
0, // Arity
2, // Method Signature
(byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_CancellationTokenSource - WellKnownType.ExtSentinel), // Return Type
(byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_CancellationToken - WellKnownType.ExtSentinel), // Argument: CancellationToken
(byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_CancellationToken - WellKnownType.ExtSentinel), // Argument: CancellationToken
// System_Threading_CancellationTokenSource__Token
(byte)MemberFlags.Property, // Flags
(byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_CancellationTokenSource - WellKnownType.ExtSentinel), // DeclaringTypeId
0, // Arity
0, // Method Signature
(byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_CancellationToken - WellKnownType.ExtSentinel), // Return Type
// System_Threading_CancellationTokenSource__Dispose
(byte)MemberFlags.Method, // Flags
(byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Threading_CancellationTokenSource - WellKnownType.ExtSentinel), // DeclaringTypeId
0, // Arity
0, // Method Signature
(byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type
};
string[] allNames = new string[(int)WellKnownMember.Count]
......@@ -3823,6 +3846,9 @@ static WellKnownMembers()
".ctor", // System_Runtime_CompilerServices_SwitchExpressionException__ctor
".ctor", // System_Runtime_CompilerServices_SwitchExpressionException__ctorObject
"Equals", // System_Threading_CancellationToken__Equals
"CreateLinkedTokenSource", // System_Threading_CancellationTokenSource__CreateLinkedTokenSource
"Token", // System_Threading_CancellationTokenSource__Token
"Dispose", // System_Threading_CancellationTokenSource__Dispose
};
s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames);
......
......@@ -297,6 +297,7 @@ internal enum WellKnownType
System_Threading_Tasks_ValueTask,
System_Runtime_CompilerServices_AsyncIteratorMethodBuilder,
System_Threading_CancellationToken,
System_Threading_CancellationTokenSource,
System_InvalidOperationException,
System_Runtime_CompilerServices_SwitchExpressionException,
......@@ -594,6 +595,7 @@ internal static class WellKnownTypes
"System.Threading.Tasks.ValueTask",
"System.Runtime.CompilerServices.AsyncIteratorMethodBuilder",
"System.Threading.CancellationToken",
"System.Threading.CancellationTokenSource",
"System.InvalidOperationException",
"System.Runtime.CompilerServices.SwitchExpressionException"
......
......@@ -350,6 +350,17 @@ private static string DumpRec(this MetadataReader reader, EntityHandle handle)
TypeReference type = reader.GetTypeReference((TypeReferenceHandle)handle);
return getQualifiedName(type.Namespace, type.Name);
}
case HandleKind.FieldDefinition:
{
FieldDefinition field = reader.GetFieldDefinition((FieldDefinitionHandle)handle);
var name = reader.GetString(field.Name);
var blob = reader.GetBlobReader(field.Signature);
var decoder = new SignatureDecoder<string, object>(ConstantSignatureVisualizer.Instance, reader, genericContext: null);
var type = decoder.DecodeFieldSignature(ref blob);
return $"{type} {name}";
}
default:
return null;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册