未验证 提交 8d00d4fb 编写于 作者: D dotnet-automerge-bot 提交者: GitHub

Merge pull request #32321 from dotnet/merges/dev16.0-preview2-to-master

Merge dev16.0-preview2 to master
......@@ -67,14 +67,16 @@ An asynchronous `using` is lowered just like a regular `using`, except that `Dis
### Detailed design for `await foreach` statement
An `await foreach` is lowered just like a regular `foreach`, except that:
- `GetEnumerator()` is replaced with `await GetAsyncEnumerator(default)`
- `GetEnumerator()` is replaced with `await GetAsyncEnumerator()`
- `MoveNext()` is replaced with `await MoveNextAsync()`
- `Dispose()` is replaced with `await DisposeAsync()`
Note that pattern-based lookup for `GetAsyncEnumerator` and `MoveNextAsync` do not place particular requirements on those methods,
as long as they could be invoked without arguments.
Asynchronous foreach loops are disallowed on collections of type dynamic,
as there is no asynchronous equivalent of the non-generic `IEnumerable` interface.
The `CancellationToken` is always passed as `default` by the `await foreach` statement.
But wrapper types can pass non-default values (see `.WithCancellation(CancellationToken)` extension method),
thereby allowing consumers of async-streams to control cancellation.
A producer of async-streams can make use of the cancellation token by writing an
......@@ -308,7 +310,7 @@ But if the suspension was a `yield return` (-N), you could also call DisposeAsyn
When in dispose mode, MoveNext continues to suspend (N) and resume (-1) until the end of the method is reached (-2).
The result of invoking `DisposeAsync` from states -1 or N is unspecified. This compiler throws `NotSupportException` for those cases.
The result of invoking `DisposeAsync` from states -1 or N is unspecified. This compiler generates `throw new NotSupportException()` for those cases.
```
DisposeAsync await
......
......@@ -900,19 +900,7 @@ private bool SatisfiesGetEnumeratorPattern(ref ForEachEnumeratorInfo.Builder bui
{
var lookupResult = LookupResult.GetInstance();
string methodName = isAsync ? GetAsyncEnumeratorMethodName : GetEnumeratorMethodName;
ImmutableArray<BoundExpression> arguments;
if (isAsync)
{
var cancellationTokenType = Compilation.GetWellKnownType(WellKnownType.System_Threading_CancellationToken);
arguments = ImmutableArray.Create<BoundExpression>(new BoundAwaitableValuePlaceholder(_syntax, cancellationTokenType));
}
else
{
arguments = ImmutableArray<BoundExpression>.Empty;
}
MethodSymbol getEnumeratorMethod = FindForEachPatternMethod(collectionExprType, methodName, lookupResult, warningsOnly: true, diagnostics: diagnostics, isAsync: isAsync, arguments);
MethodSymbol getEnumeratorMethod = FindForEachPatternMethod(collectionExprType, methodName, lookupResult, warningsOnly: true, diagnostics: diagnostics, isAsync: isAsync);
lookupResult.Free();
builder.GetEnumeratorMethod = getEnumeratorMethod;
......@@ -929,10 +917,9 @@ private bool SatisfiesGetEnumeratorPattern(ref ForEachEnumeratorInfo.Builder bui
/// <param name="warningsOnly">True if failures should result in warnings; false if they should result in errors.</param>
/// <param name="diagnostics">Populated with binding diagnostics.</param>
/// <returns>The desired method or null.</returns>
private MethodSymbol FindForEachPatternMethod(TypeSymbol patternType, string methodName, LookupResult lookupResult, bool warningsOnly, DiagnosticBag diagnostics, bool isAsync, ImmutableArray<BoundExpression> arguments)
private MethodSymbol FindForEachPatternMethod(TypeSymbol patternType, string methodName, LookupResult lookupResult, bool warningsOnly, DiagnosticBag diagnostics, bool isAsync)
{
Debug.Assert(lookupResult.IsClear);
Debug.Assert(!arguments.IsDefault);
// Not using LookupOptions.MustBeInvocableMember because we don't want the corresponding lookup error.
// We filter out non-methods below.
......@@ -977,13 +964,16 @@ private MethodSymbol FindForEachPatternMethod(TypeSymbol patternType, string met
// some custom logic in ExpressionBinder.BindGrpToParams. The biggest difference
// we've found (so far) is that it only considers methods with expected number of parameters
// (i.e. doesn't work with "params" or optional parameters).
if (method.ParameterCount == arguments.Length)
// Note: for pattern-based lookup for `await foreach` we accept `GetAsyncEnumerator` and
// `MoveNextAsync` methods with optional/params parameters.
if (method.ParameterCount == 0 || isAsync)
{
candidateMethods.Add((MethodSymbol)member);
}
}
MethodSymbol patternMethod = PerformForEachPatternOverloadResolution(patternType, candidateMethods, warningsOnly, diagnostics, isAsync, arguments);
MethodSymbol patternMethod = PerformForEachPatternOverloadResolution(patternType, candidateMethods, warningsOnly, diagnostics, isAsync);
candidateMethods.Free();
......@@ -994,12 +984,9 @@ private MethodSymbol FindForEachPatternMethod(TypeSymbol patternType, string met
/// The overload resolution portion of FindForEachPatternMethod.
/// If no arguments are passed in, then an empty argument list will be used.
/// </summary>
private MethodSymbol PerformForEachPatternOverloadResolution(TypeSymbol patternType, ArrayBuilder<MethodSymbol> candidateMethods, bool warningsOnly, DiagnosticBag diagnostics, bool isAsync, ImmutableArray<BoundExpression> arguments)
private MethodSymbol PerformForEachPatternOverloadResolution(TypeSymbol patternType, ArrayBuilder<MethodSymbol> candidateMethods, bool warningsOnly, DiagnosticBag diagnostics, bool isAsync)
{
Debug.Assert(!arguments.IsDefault);
var analyzedArguments = AnalyzedArguments.GetInstance();
analyzedArguments.Arguments.AddRange(arguments);
var typeArguments = ArrayBuilder<TypeSymbolWithAnnotations>.GetInstance();
var overloadResolutionResult = OverloadResolutionResult<MethodSymbol>.GetInstance();
......@@ -1152,7 +1139,7 @@ private bool SatisfiesForEachPattern(ref ForEachEnumeratorInfo.Builder builder,
MethodSymbol moveNextMethodCandidate = FindForEachPatternMethod(enumeratorType,
isAsync ? MoveNextAsyncMethodName : MoveNextMethodName,
lookupResult, warningsOnly: false, diagnostics: diagnostics, isAsync: isAsync, arguments: ImmutableArray<BoundExpression>.Empty);
lookupResult, warningsOnly: false, diagnostics: diagnostics, isAsync: isAsync);
if ((object)moveNextMethodCandidate == null ||
moveNextMethodCandidate.IsStatic || moveNextMethodCandidate.DeclaredAccessibility != Accessibility.Public ||
......
......@@ -137,28 +137,32 @@ internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, D
// and deduce an iterator element type from the return type. If we didn't do this, the
// TypeInfo.ConvertedType of the yield statement would always be an error type. However, we will
// not mutate any state (i.e. we won't store the result).
return GetIteratorElementTypeFromReturnType(refKind, returnType, node, diagnostics) ?? CreateErrorType();
return GetIteratorElementTypeFromReturnType(refKind, returnType, node, diagnostics).elementType ?? CreateErrorType();
}
if (_iteratorInfo == IteratorInfo.Empty)
{
TypeSymbol elementType = null;
DiagnosticBag elementTypeDiagnostics = DiagnosticBag.GetInstance();
elementType = GetIteratorElementTypeFromReturnType(refKind, returnType, node, elementTypeDiagnostics);
(TypeSymbol elementType, bool asyncInterface) = GetIteratorElementTypeFromReturnType(refKind, returnType, node, elementTypeDiagnostics);
Location errorLocation = _methodSymbol.Locations[0];
if ((object)elementType == null)
{
if (refKind != RefKind.None)
{
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturnRef, _methodSymbol.Locations[0], _methodSymbol);
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturnRef, errorLocation, _methodSymbol);
}
else if (!returnType.IsErrorType())
{
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, _methodSymbol.Locations[0], _methodSymbol, returnType);
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, errorLocation, _methodSymbol, returnType);
}
elementType = CreateErrorType();
}
else if (asyncInterface && !_methodSymbol.IsAsync)
{
Error(elementTypeDiagnostics, ErrorCode.ERR_IteratorMustBeAsync, errorLocation, _methodSymbol, returnType);
}
var info = new IteratorInfo(elementType, elementTypeDiagnostics.ToReadOnlyAndFree());
......@@ -175,12 +179,15 @@ internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, D
return _iteratorInfo.ElementType;
}
private TypeSymbol GetIteratorElementTypeFromReturnType(RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
private (TypeSymbol elementType, bool asyncInterface) GetIteratorElementTypeFromReturnType(RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
{
return GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, errorLocationNode, diagnostics).TypeSymbol;
(TypeSymbolWithAnnotations elementType, bool asyncInterface) = GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, errorLocationNode, diagnostics);
return (elementType.TypeSymbol, asyncInterface);
}
internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(CSharpCompilation compilation, RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
// If an element type is found, we also return whether the interface is meant to be used with async.
internal static (TypeSymbolWithAnnotations elementType, bool asyncInterface) GetIteratorElementTypeFromReturnType(CSharpCompilation compilation,
RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
{
if (refKind == RefKind.None && returnType.Kind == SymbolKind.NamedType)
{
......@@ -194,17 +201,17 @@ internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(C
{
ReportUseSiteDiagnostics(objectType, diagnostics, errorLocationNode);
}
return TypeSymbolWithAnnotations.Create(objectType);
return (TypeSymbolWithAnnotations.Create(objectType), false);
case SpecialType.System_Collections_Generic_IEnumerable_T:
case SpecialType.System_Collections_Generic_IEnumerator_T:
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
return (((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0], false);
}
if (TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T), TypeCompareKind.ConsiderEverything2) ||
TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T), TypeCompareKind.ConsiderEverything2))
{
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
return (((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0], true);
}
}
......
......@@ -1503,7 +1503,7 @@ private bool ExactOrBoundsNullableInference(ExactOrBoundsKind kind, TypeSymbolWi
return true;
}
if (s_isNullableOnly(source) && s_isNullableOnly(target))
if (isNullableOnly(source) && isNullableOnly(target))
{
ExactOrBoundsInference(kind, source.AsNotNullableReferenceType(), target.AsNotNullableReferenceType(), ref useSiteDiagnostics);
return true;
......@@ -1512,7 +1512,7 @@ private bool ExactOrBoundsNullableInference(ExactOrBoundsKind kind, TypeSymbolWi
return false;
// True if the type is nullable but not an unconstrained type parameter.
bool s_isNullableOnly(TypeSymbolWithAnnotations type)
bool isNullableOnly(TypeSymbolWithAnnotations type)
=> type.NullableAnnotation.IsAnyNullable() && !type.TypeSymbol.IsTypeParameterDisallowingAnnotation();
}
......
......@@ -6306,6 +6306,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Method &apos;{0}&apos; with an iterator block must be &apos;async&apos; to return &apos;{1}&apos;.
/// </summary>
internal static string ERR_IteratorMustBeAsync {
get {
return ResourceManager.GetString("ERR_IteratorMustBeAsync", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No such label &apos;{0}&apos; within the scope of the goto statement.
/// </summary>
......
......@@ -2794,6 +2794,9 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep
<data name="ERR_BadYieldInFinally" xml:space="preserve">
<value>Cannot yield in the body of a finally clause</value>
</data>
<data name="ERR_IteratorMustBeAsync" xml:space="preserve">
<value>Method '{0}' with an iterator block must be 'async' to return '{1}'</value>
</data>
<data name="ERR_BadYieldInTryOfCatch" xml:space="preserve">
<value>Cannot yield a value in the body of a try block with a catch clause</value>
</data>
......
......@@ -1583,6 +1583,7 @@ internal enum ErrorCode
ERR_FeatureNotAvailableInVersion8 = 8400,
ERR_AltInterpolatedVerbatimStringsNotAvailable = 8401,
WRN_DefaultLiteralConvertedToNullIsNotIntended = 8402,
ERR_IteratorMustBeAsync = 8403,
ERR_NoConvToIAsyncDisp = 8410,
ERR_AwaitForEachMissingMember = 8411,
......
......@@ -5143,7 +5143,9 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no
return null;
}
var method = (MethodSymbol)_symbol;
TypeSymbolWithAnnotations elementType = InMethodBinder.GetIteratorElementTypeFromReturnType(compilation, RefKind.None, method.ReturnType.TypeSymbol, errorLocationNode: null, diagnostics: null);
TypeSymbolWithAnnotations elementType = InMethodBinder.GetIteratorElementTypeFromReturnType(compilation, RefKind.None,
method.ReturnType.TypeSymbol, errorLocationNode: null, diagnostics: null).elementType;
VisitOptionalImplicitConversion(expr, elementType, useLegacyWarnings: false, AssignmentKind.Return);
return null;
}
......
......@@ -135,7 +135,7 @@ protected override BoundBinaryOperator ShouldEnterFinallyBlock()
/// <summary>
/// Lower the body, adding an entry state (-3) at the start,
/// so that we can differentiate a async-iterator that was never moved forward with MoveNextAsync()
/// so that we can differentiate an async-iterator that was never moved forward with MoveNextAsync()
/// from one that is running (-1).
/// Then we can guard against some bad usages of DisposeAsync.
/// </summary>
......
......@@ -6,6 +6,7 @@
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -85,7 +86,7 @@ private bool CanRewriteForEachAsFor(SyntaxNode forEachSyntax, TypeSymbol nodeExp
/// Lower a foreach loop that will enumerate a collection using an enumerator.
///
/// <![CDATA[
/// E e = ((C)(x)).GetEnumerator() OR ((C)(x)).GetAsyncEnumerator(default)
/// E e = ((C)(x)).GetEnumerator() OR ((C)(x)).GetAsyncEnumerator()
/// try {
/// while (e.MoveNext()) OR while (await e.MoveNextAsync())
/// {
......@@ -101,6 +102,7 @@ private bool CanRewriteForEachAsFor(SyntaxNode forEachSyntax, TypeSymbol nodeExp
private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement node)
{
var forEachSyntax = (CommonForEachStatementSyntax)node.Syntax;
bool isAsync = node.AwaitOpt != null;
ForEachEnumeratorInfo enumeratorInfo = node.EnumeratorInfoOpt;
Debug.Assert(enumeratorInfo != null);
......@@ -119,11 +121,12 @@ private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement n
// Reference to e.
BoundLocal boundEnumeratorVar = MakeBoundLocal(forEachSyntax, enumeratorVar, enumeratorType);
// ((C)(x)).GetEnumerator(); OR (x).GetEnumerator(); OR async variants
var arguments = (node.AwaitOpt != null)
? ImmutableArray.Create<BoundExpression>(new BoundDefaultExpression(forEachSyntax, getEnumeratorMethod.Parameters[0].Type.TypeSymbol))
: ImmutableArray<BoundExpression>.Empty;
BoundExpression enumeratorVarInitValue = SynthesizeCall(forEachSyntax, rewrittenExpression, getEnumeratorMethod, enumeratorInfo.CollectionConversion, enumeratorInfo.CollectionType, arguments);
// ((C)(x)).GetEnumerator(); OR (x).GetEnumerator(); OR async variants (which fill-in arguments for optional parameters)
BoundExpression enumeratorVarInitValue = SynthesizeCall(
forEachSyntax,
ConvertReceiverForInvocation(forEachSyntax, rewrittenExpression, getEnumeratorMethod, enumeratorInfo.CollectionConversion, enumeratorInfo.CollectionType),
getEnumeratorMethod,
allowExtensionAndOptionalParameters: isAsync);
// E e = ((C)(x)).GetEnumerator();
BoundStatement enumeratorVarDecl = MakeLocalDeclaration(forEachSyntax, enumeratorVar, enumeratorVarInitValue);
......@@ -160,12 +163,12 @@ private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement n
// }
var rewrittenBodyBlock = CreateBlockDeclaringIterationVariables(iterationVariables, iterationVarDecl, rewrittenBody, forEachSyntax);
BoundExpression rewrittenCondition = BoundCall.Synthesized(
BoundExpression rewrittenCondition = SynthesizeCall(
syntax: forEachSyntax,
receiverOpt: boundEnumeratorVar,
method: enumeratorInfo.MoveNextMethod);
if (node.AwaitOpt != null)
receiver: boundEnumeratorVar,
method: enumeratorInfo.MoveNextMethod,
allowExtensionAndOptionalParameters: isAsync);
if (isAsync)
{
rewrittenCondition = RewriteAwaitExpression(forEachSyntax, rewrittenCondition, node.AwaitOpt, node.AwaitOpt.GetResult.ReturnType.TypeSymbol, used: true);
}
......@@ -248,7 +251,12 @@ private bool TryGetDisposeMethod(CommonForEachStatementSyntax forEachSyntax, For
Conversion.ImplicitReference;
// ((IDisposable)e).Dispose() or e.Dispose() or await ((IAsyncDisposable)e).DisposeAsync() or await e.DisposeAsync()
BoundExpression disposeCall = SynthesizeCall(forEachSyntax, boundEnumeratorVar, disposeMethod, receiverConversion, idisposableTypeSymbol, arguments: ImmutableArray<BoundExpression>.Empty);
// Note: pattern-based async disposal is not allowed (cannot use ref structs in async methods), so the arguments are known to be empty even for async case
BoundExpression disposeCall = BoundCall.Synthesized(
forEachSyntax,
ConvertReceiverForInvocation(forEachSyntax, boundEnumeratorVar, disposeMethod, receiverConversion, idisposableTypeSymbol),
disposeMethod,
ImmutableArray<BoundExpression>.Empty);
BoundStatement disposeCallStatement;
if (disposeAwaitableInfoOpt != null)
{
......@@ -391,10 +399,10 @@ private BoundStatement WrapWithAwait(CommonForEachStatementSyntax forEachSyntax,
}
/// <summary>
/// Synthesize a call to a given method, possibly applying a conversion to the receiver.
///
/// If the receiver is of struct type and the method is an interface method, then skip the conversion
/// and just call the interface method directly - the code generator will detect this and generate a
/// Optionally apply a conversion to the receiver.
///
/// If the receiver is of struct type and the method is an interface method, then skip the conversion.
/// When we call the interface method directly - the code generator will detect it and generate a
/// constrained virtual call.
/// </summary>
/// <param name="syntax">A syntax node to attach to the synthesized bound node.</param>
......@@ -402,9 +410,9 @@ private BoundStatement WrapWithAwait(CommonForEachStatementSyntax forEachSyntax,
/// <param name="method">Method to invoke.</param>
/// <param name="receiverConversion">Conversion to be applied to the receiver if not calling an interface method on a struct.</param>
/// <param name="convertedReceiverType">Type of the receiver after applying the conversion.</param>
/// <returns>A BoundExpression representing the call.</returns>
private BoundExpression SynthesizeCall(CSharpSyntaxNode syntax, BoundExpression receiver, MethodSymbol method, Conversion receiverConversion, TypeSymbol convertedReceiverType, ImmutableArray<BoundExpression> arguments)
private BoundExpression ConvertReceiverForInvocation(CSharpSyntaxNode syntax, BoundExpression receiver, MethodSymbol method, Conversion receiverConversion, TypeSymbol convertedReceiverType)
{
Debug.Assert(!method.IsExtensionMethod);
if (!receiver.Type.IsReferenceType && method.ContainingType.IsInterface)
{
Debug.Assert(receiverConversion.IsImplicit && !receiverConversion.IsUserDefined);
......@@ -427,25 +435,35 @@ private BoundExpression SynthesizeCall(CSharpSyntaxNode syntax, BoundExpression
// We're invoking the interface method directly on the struct (which may have a private
// explicit implementation). The code generator knows how to handle it though.
// receiver.InterfaceMethod() OR receiver.InterfaceMethod(default)
return BoundCall.Synthesized(syntax, receiver, method, arguments);
// receiver.InterfaceMethod()
}
else
{
// ((Interface)receiver).InterfaceMethod() OR ((Interface)receiver).InterfaceMethod(default)
// ((Interface)receiver).InterfaceMethod()
Debug.Assert(!receiverConversion.IsNumeric);
return BoundCall.Synthesized(
receiver = MakeConversionNode(
syntax: syntax,
receiverOpt: MakeConversionNode(
syntax: syntax,
rewrittenOperand: receiver,
conversion: receiverConversion,
@checked: false,
rewrittenType: convertedReceiverType),
method: method,
arguments);
rewrittenOperand: receiver,
conversion: receiverConversion,
@checked: false,
rewrittenType: convertedReceiverType);
}
return receiver;
}
private BoundExpression SynthesizeCall(CSharpSyntaxNode syntax, BoundExpression receiver, MethodSymbol method, bool allowExtensionAndOptionalParameters)
{
Debug.Assert(!method.IsExtensionMethod);
if (allowExtensionAndOptionalParameters)
{
// Generate a call with zero explicit arguments, but with implicit arguments for optional and params parameters.
return MakeCallWithNoExplicitArgument(syntax, receiver, method);
}
// Generate a call with literally zero arguments
return BoundCall.Synthesized(syntax, receiver, method, arguments: ImmutableArray<BoundExpression>.Empty);
}
/// <summary>
......
......@@ -422,29 +422,7 @@ private BoundExpression GenerateDisposeCall(SyntaxNode syntax, BoundExpression d
}
else
{
var receiver = methodOpt.IsExtensionMethod
? null
: disposedExpression;
var args = methodOpt.IsExtensionMethod
? ImmutableArray.Create(disposedExpression)
: ImmutableArray<BoundExpression>.Empty;
var refs = methodOpt.IsExtensionMethod && !methodOpt.ParameterRefKinds.IsDefaultOrEmpty
? ImmutableArray.Create(methodOpt.ParameterRefKinds[0])
: default;
disposeCall = MakeCall(syntax: syntax,
rewrittenReceiver: receiver,
method: methodOpt,
rewrittenArguments: args,
argumentRefKindsOpt: refs,
expanded: methodOpt.HasParamsParameter(),
invokedAsExtensionMethod: methodOpt.IsExtensionMethod,
argsToParamsOpt: default,
resultKind: LookupResultKind.Viable,
type: methodOpt.ReturnType.TypeSymbol,
nodeOpt: null);
disposeCall = MakeCallWithNoExplicitArgument(syntax, disposedExpression, methodOpt);
if (!(awaitOpt is null))
{
......@@ -458,5 +436,34 @@ private BoundExpression GenerateDisposeCall(SyntaxNode syntax, BoundExpression d
return disposeCall;
}
/// <summary>
/// Synthesize a call `expression.Method()`, but with some extra smarts to handle extension methods, and to fill-in optional and params parameters.
/// </summary>
private BoundExpression MakeCallWithNoExplicitArgument(SyntaxNode syntax, BoundExpression expression, MethodSymbol method)
{
var receiver = method.IsExtensionMethod ? null : expression;
var args = method.IsExtensionMethod
? ImmutableArray.Create(expression)
: ImmutableArray<BoundExpression>.Empty;
var refKinds = method.IsExtensionMethod && !method.ParameterRefKinds.IsDefaultOrEmpty
? ImmutableArray.Create(method.ParameterRefKinds[0])
: default;
BoundExpression disposeCall = MakeCall(syntax: syntax,
rewrittenReceiver: receiver,
method: method,
rewrittenArguments: args,
argumentRefKindsOpt: refKinds,
expanded: method.HasParamsParameter(),
invokedAsExtensionMethod: method.IsExtensionMethod,
argsToParamsOpt: default,
resultKind: LookupResultKind.Viable,
type: method.ReturnType.TypeSymbol);
return disposeCall;
}
}
}
......@@ -409,10 +409,13 @@ protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSy
thenBuilder.Add(extraReset);
}
thenBuilder.Add(
method.IsStatic || method.ThisParameter.Type.IsReferenceType ? // if this is a reference type, no need to copy it since it is not assignable
F.Goto(thisInitialized) : // goto thisInitialized
(BoundStatement)F.StatementList());
if (method.IsStatic || method.ThisParameter.Type.IsReferenceType)
{
// if this is a reference type, no need to copy it since it is not assignable
thenBuilder.Add(
// goto thisInitialized;
F.Goto(thisInitialized));
}
makeIterator = F.If(
// if (this.state == -2 && this.initialThreadId == Thread.CurrentThread.ManagedThreadId)
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -1168,6 +1168,7 @@ static async System.Collections.Generic.IAsyncEnumerator<int> M(int value)
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
[WorkItem(31113, "https://github.com/dotnet/roslyn/issues/31113")]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void AsyncIteratorReturningEnumerator_WithoutAsync()
{
string source = @"
......@@ -1181,6 +1182,9 @@ static System.Collections.Generic.IAsyncEnumerator<int> M(int value)
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
// (4,61): error CS8403: Method 'C.M(int)' with an iterator block must be 'async' to return 'IAsyncEnumerator<int>'
// static System.Collections.Generic.IAsyncEnumerator<int> M(int value)
Diagnostic(ErrorCode.ERR_IteratorMustBeAsync, "M").WithArguments("C.M(int)", "System.Collections.Generic.IAsyncEnumerator<int>").WithLocation(4, 61),
// (7,9): error CS4032: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task<IAsyncEnumerator<int>>'.
// await System.Threading.Tasks.Task.CompletedTask;
Diagnostic(ErrorCode.ERR_BadAwaitWithoutAsyncMethod, "await System.Threading.Tasks.Task.CompletedTask").WithArguments("System.Collections.Generic.IAsyncEnumerator<int>").WithLocation(7, 9)
......@@ -1343,6 +1347,94 @@ public static async System.Collections.Generic.IAsyncEnumerable<int> M()
);
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void AsyncIterator_WithoutAwait()
{
string source = @"
public class C
{
public static async System.Collections.Generic.IAsyncEnumerable<int> M()
{
yield return 1;
}
}";
var comp = CreateCompilationWithAsyncIterator(new[] { Run(iterations: 2), source }, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (4,74): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
// public static async System.Collections.Generic.IAsyncEnumerable<int> M()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74)
);
CompileAndVerify(comp, expectedOutput: "1 END DISPOSAL DONE");
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void AsyncIterator_WithoutAwait_WithoutAsync()
{
string source = @"
class C
{
static System.Collections.Generic.IAsyncEnumerable<int> M()
{
yield return 1;
}
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
// (4,61): error CS8403: Method 'C.M()' with an iterator block must be 'async' to return 'IAsyncEnumerable<int>'
// static System.Collections.Generic.IAsyncEnumerable<int> M()
Diagnostic(ErrorCode.ERR_IteratorMustBeAsync, "M").WithArguments("C.M()", "System.Collections.Generic.IAsyncEnumerable<int>").WithLocation(4, 61)
);
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void AsyncIterator_WithoutAwait_WithoutAsync_LocalFunction()
{
string source = @"
class C
{
void M()
{
_ = local();
static System.Collections.Generic.IAsyncEnumerator<int> local()
{
yield break;
}
}
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
// (7,65): error CS8403: Method 'local()' with an iterator block must be 'async' to return 'IAsyncEnumerator<int>'
// static System.Collections.Generic.IAsyncEnumerator<int> local()
Diagnostic(ErrorCode.ERR_IteratorMustBeAsync, "local").WithArguments("local()", "System.Collections.Generic.IAsyncEnumerator<int>").WithLocation(7, 65)
);
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void Iterator_WithAsync()
{
string source = @"
class C
{
static async System.Collections.Generic.IEnumerable<int> M()
{
yield return 1;
}
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
// (4,62): error CS1983: The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>
// static async System.Collections.Generic.IEnumerable<int> M()
Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(4, 62),
// (4,62): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
// static async System.Collections.Generic.IEnumerable<int> M()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 62)
);
}
[Fact]
[WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")]
public void AsyncIteratorReturningEnumerator_WithoutBody()
......
......@@ -229,7 +229,7 @@ await foreach (var i in new C())
{
}
}
internal Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
internal Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
......@@ -261,7 +261,7 @@ await foreach (var i in new C())
}
}
[System.Obsolete]
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
......@@ -303,7 +303,7 @@ async System.Threading.Tasks.Task M()
{
await foreach (var i in new C()) { }
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator()
=> throw null;
public sealed class Enumerator
{
......@@ -317,9 +317,9 @@ public int Current
}";
var comp = CreateCompilationWithMscorlib46(source);
comp.VerifyDiagnostics(
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator(CancellationToken)' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator()' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// await foreach (var i in new C()) { }
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(6, 33)
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator()").WithLocation(6, 33)
);
}
......@@ -335,7 +335,7 @@ await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token) => throw null;
public Enumerator GetAsyncEnumerator() => throw null;
public sealed class Enumerator
{
public System.Threading.Tasks.Task<object> MoveNextAsync() => throw null; // returns Task<object>
......@@ -344,9 +344,9 @@ public sealed class Enumerator
}";
var comp = CreateCompilationWithMscorlib46(source);
comp.VerifyDiagnostics(
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator(CancellationToken)' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator()' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// await foreach (var i in new C())
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(6, 33)
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator()").WithLocation(6, 33)
);
}
......@@ -362,7 +362,7 @@ await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
......@@ -396,7 +396,7 @@ async System.Threading.Tasks.Task M()
{
await foreach (var i in new C()) { }
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator()
=> throw null;
public sealed class Enumerator
{
......@@ -410,9 +410,9 @@ public static int Current
}";
var comp = CreateCompilationWithMscorlib46(source);
comp.VerifyDiagnostics(
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator(CancellationToken)' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator()' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// await foreach (var i in new C()) { }
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(6, 33)
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator()").WithLocation(6, 33)
);
}
......@@ -426,7 +426,7 @@ async System.Threading.Tasks.Task M()
{
await foreach (var i in new C()) { }
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator()
=> throw null;
public sealed class Enumerator
{
......@@ -438,9 +438,9 @@ public int Current
}";
var comp = CreateCompilationWithMscorlib46(source);
comp.VerifyDiagnostics(
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator(CancellationToken)' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator()' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// await foreach (var i in new C()) { }
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(6, 33)
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator()").WithLocation(6, 33)
);
}
......@@ -454,7 +454,7 @@ async System.Threading.Tasks.Task M()
{
await foreach (var i in new C()) { }
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> throw null;
public sealed class Enumerator
{
......@@ -487,7 +487,7 @@ async System.Threading.Tasks.Task M()
{
await foreach (var i in new C()) { }
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator()
=> throw null;
public sealed class Enumerator
{
......@@ -502,9 +502,9 @@ public int Current
}";
var comp = CreateCompilationWithMscorlib46(source);
comp.VerifyDiagnostics(
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator(CancellationToken)' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator()' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// await foreach (var i in new C()) { }
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(6, 33)
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator()").WithLocation(6, 33)
);
}
......@@ -518,7 +518,7 @@ async System.Threading.Tasks.Task M()
{
await foreach (var i in new C()) { }
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> throw null;
public sealed class Enumerator
{
......@@ -550,7 +550,7 @@ await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
......@@ -586,7 +586,7 @@ await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
......@@ -614,23 +614,25 @@ public int Current
public void TestMoveNextAsync_WithOptionalParameter()
{
string source = @"
class C
public class C
{
async System.Threading.Tasks.Task M()
public static async System.Threading.Tasks.Task Main()
{
await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
return new Enumerator();
}
public sealed class Enumerator
{
public System.Threading.Tasks.Task<bool> MoveNextAsync(int bad = 0)
public async System.Threading.Tasks.Task<bool> MoveNextAsync(int ok = 0)
{
throw null;
System.Console.Write(""MoveNextAsync"");
await System.Threading.Tasks.Task.Yield();
return false;
}
public int Current
{
......@@ -638,12 +640,44 @@ public int Current
}
}
}";
var comp = CreateCompilationWithMscorlib46(source);
comp.VerifyDiagnostics(
// (6,33): error CS8412: Asynchronous foreach requires that the return type 'C.Enumerator' of 'C.GetAsyncEnumerator(CancellationToken)' must have a suitable public 'MoveNextAsync' method and public 'Current' property
// await foreach (var i in new C())
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(6, 33)
);
var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "MoveNextAsync");
}
[Fact]
public void TestMoveNextAsync_WithParamsParameter()
{
string source = @"
public class C
{
public static async System.Threading.Tasks.Task Main()
{
await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
return new Enumerator();
}
public sealed class Enumerator
{
public async System.Threading.Tasks.Task<bool> MoveNextAsync(params int[] ok)
{
System.Console.Write(""MoveNextAsync"");
await System.Threading.Tasks.Task.Yield();
return false;
}
public int Current
{
get => throw null;
}
}
}";
var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "MoveNextAsync");
}
[Fact]
......@@ -658,7 +692,7 @@ await foreach (string i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator()
=> throw null;
public sealed class Enumerator
{
......@@ -682,7 +716,7 @@ public int Current
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.Equal("C.Enumerator C.GetAsyncEnumerator(System.Threading.CancellationToken token)", info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal("C.Enumerator C.GetAsyncEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal("System.Threading.Tasks.Task<System.Boolean> C.Enumerator.MoveNextAsync()", info.MoveNextMethod.ToTestDisplayString());
Assert.Equal("System.Int32 C.Enumerator.Current { get; }", info.CurrentProperty.ToTestDisplayString());
Assert.Null(info.DisposeMethod);
......@@ -704,7 +738,7 @@ await foreach (Element i in new C())
{
}
}
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> throw null;
public sealed class AsyncEnumerator
{
......@@ -744,7 +778,7 @@ await foreach (Element i in new C())
Write($""Got({i}) "");
}
}
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
return new AsyncEnumerator();
}
......@@ -806,7 +840,7 @@ await foreach (var i in new C())
}
f();
}
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
return new AsyncEnumerator();
}
......@@ -852,7 +886,7 @@ await foreach (var i in new C<IntContainer>())
}
class C<T> where T : IntContainer, new()
{
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
return new AsyncEnumerator();
}
......@@ -911,7 +945,7 @@ await foreach (var i in new C())
Write(e.Message);
}
}
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> throw new System.ArgumentException(""exception"");
public sealed class AsyncEnumerator : System.IAsyncDisposable
{
......@@ -951,7 +985,7 @@ await foreach (var i in new C())
Write(e.Message);
}
}
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> new AsyncEnumerator();
public sealed class AsyncEnumerator : System.IAsyncDisposable
{
......@@ -994,7 +1028,7 @@ await foreach (var i in new C())
Write(e.Message);
}
}
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator()
=> new AsyncEnumerator();
public sealed class AsyncEnumerator : System.IAsyncDisposable
{
......@@ -1041,7 +1075,7 @@ await foreach (var i in new C())
Write(e.Message);
}
}
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator()
=> new AsyncEnumerator();
public sealed class AsyncEnumerator : System.IAsyncDisposable
{
......@@ -1116,7 +1150,7 @@ namespace System.Collections.Generic
{
public interface IAsyncEnumerable<out T>
{
IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken token);
IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken token = default);
}
public interface IAsyncEnumerator<out T>
......@@ -1152,7 +1186,7 @@ namespace System.Collections.Generic
{
public interface IAsyncEnumerable<out T>
{
IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken token);
IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken token = default);
}
public interface IAsyncEnumerator<out T>
......@@ -1191,6 +1225,7 @@ async System.Threading.Tasks.Task M()
await foreach (var i in new C())
{
}
_ = (new C()).GetAsyncEnumerator();
}
public sealed class Enumerator
{
......@@ -1203,6 +1238,7 @@ public static C.Enumerator GetAsyncEnumerator(this C c)
throw null;
}
}";
// Pattern-based lookup does not bind extension methods at the moment
var comp = CreateCompilationWithMscorlib46(source);
comp.VerifyDiagnostics(
// (6,33): error CS8411: Async foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public definition for 'GetAsyncEnumerator'
......@@ -1222,6 +1258,7 @@ void M()
foreach (var i in new C())
{
}
_ = (new C()).GetEnumerator();
}
public sealed class Enumerator
{
......@@ -1233,6 +1270,7 @@ public static class Extensions
{
public static C.Enumerator GetEnumerator(this C self) => throw null;
}";
// Pattern-based lookup does not bind extension methods at the moment
var comp = CreateCompilationWithMscorlib46(source);
comp.VerifyDiagnostics(
// (6,27): error CS1579: foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public definition for 'GetEnumerator'
......@@ -1253,7 +1291,7 @@ await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
......@@ -1364,7 +1402,7 @@ void M()
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token) => throw null;
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default) => throw null;
public sealed class Enumerator
{
public System.Threading.Tasks.Task<bool> MoveNextAsync() => throw null;
......@@ -1438,7 +1476,7 @@ void M()
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> throw null;
public sealed class Enumerator
{
......@@ -1456,32 +1494,33 @@ public System.Threading.Tasks.Task<bool> MoveNextAsync()
}
[Fact]
public void TestPatternBased_MissingCancellationToken()
public void TestPatternBased_GetEnumeratorWithoutCancellationToken()
{
string source = @"
class C
public class C
{
async System.Threading.Tasks.Task M()
public static async System.Threading.Tasks.Task Main()
{
await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator() // missing parameter
=> throw null;
public Enumerator GetAsyncEnumerator() // no parameter
=> new Enumerator();
public sealed class Enumerator
{
public System.Threading.Tasks.Task<bool> MoveNextAsync()
=> throw null;
public int Current { get => throw null; }
public async System.Threading.Tasks.Task<bool> MoveNextAsync()
{
System.Console.Write(""MoveNextAsync"");
await System.Threading.Tasks.Task.Yield();
return false;
}
public int Current => throw null;
}
}";
var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable });
comp.VerifyDiagnostics(
// (6,33): error CS8411: Asynchronous foreach statement cannot operate on variables of type 'C' because 'C' does not contain a suitable public instance definition for 'GetAsyncEnumerator'
// await foreach (var i in new C())
Diagnostic(ErrorCode.ERR_AwaitForEachMissingMember, "new C()").WithArguments("C", "GetAsyncEnumerator").WithLocation(6, 33)
);
var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "MoveNextAsync");
}
[Fact]
......@@ -1525,7 +1564,7 @@ await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
......@@ -1549,7 +1588,7 @@ public int Current
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.Equal("C.Enumerator C.GetAsyncEnumerator(System.Threading.CancellationToken token)", info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal("C.Enumerator C.GetAsyncEnumerator([System.Threading.CancellationToken token = default(System.Threading.CancellationToken)])", info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal("System.Threading.Tasks.Task<System.Boolean> C.Enumerator.MoveNextAsync()", info.MoveNextMethod.ToTestDisplayString());
Assert.Equal("System.Int32 C.Enumerator.Current { get; }", info.CurrentProperty.ToTestDisplayString());
Assert.Null(info.DisposeMethod);
......@@ -1575,7 +1614,7 @@ await foreach (ref var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> throw null;
public sealed class Enumerator
{
......@@ -1608,7 +1647,7 @@ await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token) => throw null;
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default) => throw null;
public sealed class Enumerator
{
public System.Threading.Tasks.Task<bool> MoveNextAsync() => throw null;
......@@ -1679,7 +1718,7 @@ await foreach (var i in new D())
}
class D
{
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> throw null;
public sealed class Enumerator
{
......@@ -1717,7 +1756,7 @@ async System.Threading.Tasks.Task M()
}
class D
{
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> throw null;
public sealed class Enumerator
{
......@@ -1754,7 +1793,7 @@ async System.Threading.Tasks.Task M()
}
class D
{
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator()
=> throw null;
public sealed class Enumerator
{
......@@ -1764,9 +1803,9 @@ public sealed class Enumerator
}";
var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable);
comp.VerifyDiagnostics(
// (6,33): error CS8412: Async foreach requires that the return type 'D.Enumerator' of 'D.GetAsyncEnumerator(System.Threading.CancellationToken)' must have a suitable public MoveNextAsync method and public Current property
// (6,33): error CS8412: Async foreach requires that the return type 'D.Enumerator' of 'D.GetAsyncEnumerator()' must have a suitable public MoveNextAsync method and public Current property
// await foreach (var i in new D()) { }
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new D()").WithArguments("D.Enumerator", "D.GetAsyncEnumerator(System.Threading.CancellationToken)").WithLocation(6, 33)
Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new D()").WithArguments("D.Enumerator", "D.GetAsyncEnumerator()").WithLocation(6, 33)
);
var tree = comp.SyntaxTrees.Single();
......@@ -1791,7 +1830,7 @@ await foreach (var s in new C())
}
Write(""Done"");
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token) => new Enumerator();
public Enumerator GetAsyncEnumerator() => new Enumerator();
public sealed class Enumerator : System.IAsyncDisposable
{
int i = 0;
......@@ -1838,7 +1877,7 @@ await foreach (var s in new C())
}
Write(""Done"");
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token) => new Enumerator();
public Enumerator GetAsyncEnumerator() => new Enumerator();
public sealed class Enumerator
{
int i = 0;
......@@ -1887,7 +1926,7 @@ await foreach (var i in new C())
i = 1;
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
=> throw null;
public sealed class Enumerator
{
......@@ -1920,7 +1959,7 @@ await foreach (var i in new C())
}
Write($""Done"");
}
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator()
{
return new AsyncEnumerator(0);
}
......@@ -1974,7 +2013,7 @@ await foreach (var i in new C())
}
Write($""Done"");
}
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
return new AsyncEnumerator(0);
}
......@@ -2036,7 +2075,7 @@ await foreach (var i in new C())
Write($""Got({i}) "");
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator()
{
return new Enumerator();
}
......@@ -2097,7 +2136,7 @@ await foreach (var i in new C())
Write($""Got({i}) "");
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
return new DerivedEnumerator();
}
......@@ -2398,7 +2437,7 @@ await foreach (var i in new C())
Write($""Got({i}) "");
}
}
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public Enumerator GetAsyncEnumerator()
{
return new Enumerator();
}
......@@ -2449,7 +2488,7 @@ public void TestWithPattern_WithIAsyncDisposableUseSiteError()
using System.Threading.Tasks;
public class C
{
public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token) => throw null;
public Enumerator GetAsyncEnumerator() => throw null;
public sealed class Enumerator : System.IAsyncDisposable
{
public int Current { get => throw null; }
......@@ -2552,12 +2591,13 @@ IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(System.Threading.
public void TestWithInterface()
{
string source = @"
using static System.Console;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static System.Console;
class C : IAsyncEnumerable<int>
{
static async System.Threading.Tasks.Task Main()
static async Task Main()
{
await foreach (var i in new C())
{
......@@ -2571,7 +2611,7 @@ IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(System.Threading.
sealed class AsyncEnumerator : IAsyncEnumerator<int>
{
int i = 0;
public int Current
int IAsyncEnumerator<int>.Current
{
get
{
......@@ -2579,13 +2619,13 @@ public int Current
return i;
}
}
public async ValueTask<bool> MoveNextAsync()
async ValueTask<bool> IAsyncEnumerator<int>.MoveNextAsync()
{
Write($""NextAsync({i}) "");
i++;
return await Task.FromResult(i < 4);
}
public async ValueTask DisposeAsync()
async ValueTask IAsyncDisposable.DisposeAsync()
{
Write($""Disp"");
await Task.Yield();
......@@ -2593,12 +2633,12 @@ public async ValueTask DisposeAsync()
}
}
}";
var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe);
var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)", verify: Verification.Skipped);
var tree = comp.SyntaxTrees.Single();
var tree = comp.SyntaxTrees.First();
var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false);
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
......@@ -2620,6 +2660,273 @@ public async ValueTask DisposeAsync()
Assert.True(internalInfo.NeedsDisposeMethod);
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
public void TestWithInterface_OnStruct_ImplicitInterfaceImplementation()
{
string source = @"
using static System.Console;
using System.Collections.Generic;
using System.Threading.Tasks;
struct C : IAsyncEnumerable<int>
{
static async System.Threading.Tasks.Task Main()
{
await foreach (var i in new C())
{
Write($""Got({i}) "");
}
}
public IAsyncEnumerator<int> GetAsyncEnumerator(System.Threading.CancellationToken token)
{
return new AsyncEnumerator();
}
class AsyncEnumerator : IAsyncEnumerator<int>
{
public int i;
public int Current
{
get
{
Write($""Current({i}) "");
return i;
}
}
public async ValueTask<bool> MoveNextAsync()
{
Write($""NextAsync({i}) "");
i++;
return await Task.FromResult(i < 4);
}
public async ValueTask DisposeAsync()
{
Write($""Disp"");
await Task.Yield();
Write($""ose({i}) "");
}
}
}";
// Note: the enumerator type should not be a struct, otherwise you will loop forever
var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
var verifier = CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)");
// The thing to notice here is that the call to GetAsyncEnumerator is a constrained call (we're not boxing to `IAsyncEnumerable<int>`)
verifier.VerifyIL("C.<Main>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"
{
// Code size 490 (0x1ea)
.maxstack 3
.locals init (int V_0,
C V_1,
System.Threading.CancellationToken V_2,
System.Runtime.CompilerServices.ValueTaskAwaiter<bool> V_3,
System.Threading.Tasks.ValueTask<bool> V_4,
C.<Main>d__0 V_5,
object V_6,
System.Runtime.CompilerServices.ValueTaskAwaiter V_7,
System.Threading.Tasks.ValueTask V_8,
System.Exception V_9)
IL_0000: ldarg.0
IL_0001: ldfld ""int C.<Main>d__0.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0048
IL_000a: br.s IL_000c
IL_000c: ldloc.0
IL_000d: ldc.i4.1
IL_000e: beq IL_0156
IL_0013: br.s IL_0015
IL_0015: nop
IL_0016: nop
IL_0017: ldarg.0
IL_0018: ldloca.s V_1
IL_001a: dup
IL_001b: initobj ""C""
IL_0021: ldloca.s V_2
IL_0023: initobj ""System.Threading.CancellationToken""
IL_0029: ldloc.2
IL_002a: constrained. ""C""
IL_0030: callvirt ""System.Collections.Generic.IAsyncEnumerator<int> System.Collections.Generic.IAsyncEnumerable<int>.GetAsyncEnumerator(System.Threading.CancellationToken)""
IL_0035: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>s__1""
IL_003a: ldarg.0
IL_003b: ldnull
IL_003c: stfld ""object C.<Main>d__0.<>s__2""
IL_0041: ldarg.0
IL_0042: ldc.i4.0
IL_0043: stfld ""int C.<Main>d__0.<>s__3""
IL_0048: nop
.try
{
IL_0049: ldloc.0
IL_004a: brfalse.s IL_00c4
IL_004c: br.s IL_004e
IL_004e: br.s IL_007e
IL_0050: ldarg.0
IL_0051: ldarg.0
IL_0052: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>s__1""
IL_0057: callvirt ""int System.Collections.Generic.IAsyncEnumerator<int>.Current.get""
IL_005c: stfld ""int C.<Main>d__0.<i>5__4""
IL_0061: nop
IL_0062: ldstr ""Got({0}) ""
IL_0067: ldarg.0
IL_0068: ldfld ""int C.<Main>d__0.<i>5__4""
IL_006d: box ""int""
IL_0072: call ""string string.Format(string, object)""
IL_0077: call ""void System.Console.Write(string)""
IL_007c: nop
IL_007d: nop
IL_007e: ldarg.0
IL_007f: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>s__1""
IL_0084: callvirt ""System.Threading.Tasks.ValueTask<bool> System.Collections.Generic.IAsyncEnumerator<int>.MoveNextAsync()""
IL_0089: stloc.s V_4
IL_008b: ldloca.s V_4
IL_008d: call ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> System.Threading.Tasks.ValueTask<bool>.GetAwaiter()""
IL_0092: stloc.3
IL_0093: ldloca.s V_3
IL_0095: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.IsCompleted.get""
IL_009a: brtrue.s IL_00e0
IL_009c: ldarg.0
IL_009d: ldc.i4.0
IL_009e: dup
IL_009f: stloc.0
IL_00a0: stfld ""int C.<Main>d__0.<>1__state""
IL_00a5: ldarg.0
IL_00a6: ldloc.3
IL_00a7: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Main>d__0.<>u__1""
IL_00ac: ldarg.0
IL_00ad: stloc.s V_5
IL_00af: ldarg.0
IL_00b0: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_00b5: ldloca.s V_3
IL_00b7: ldloca.s V_5
IL_00b9: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, C.<Main>d__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter<bool>, ref C.<Main>d__0)""
IL_00be: nop
IL_00bf: leave IL_01e9
IL_00c4: ldarg.0
IL_00c5: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Main>d__0.<>u__1""
IL_00ca: stloc.3
IL_00cb: ldarg.0
IL_00cc: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool> C.<Main>d__0.<>u__1""
IL_00d1: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter<bool>""
IL_00d7: ldarg.0
IL_00d8: ldc.i4.m1
IL_00d9: dup
IL_00da: stloc.0
IL_00db: stfld ""int C.<Main>d__0.<>1__state""
IL_00e0: ldarg.0
IL_00e1: ldloca.s V_3
IL_00e3: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter<bool>.GetResult()""
IL_00e8: stfld ""bool C.<Main>d__0.<>s__5""
IL_00ed: ldarg.0
IL_00ee: ldfld ""bool C.<Main>d__0.<>s__5""
IL_00f3: brtrue IL_0050
IL_00f8: leave.s IL_0106
}
catch object
{
IL_00fa: stloc.s V_6
IL_00fc: ldarg.0
IL_00fd: ldloc.s V_6
IL_00ff: stfld ""object C.<Main>d__0.<>s__2""
IL_0104: leave.s IL_0106
}
IL_0106: ldarg.0
IL_0107: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>s__1""
IL_010c: brfalse.s IL_017b
IL_010e: ldarg.0
IL_010f: ldfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>s__1""
IL_0114: callvirt ""System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()""
IL_0119: stloc.s V_8
IL_011b: ldloca.s V_8
IL_011d: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
IL_0122: stloc.s V_7
IL_0124: ldloca.s V_7
IL_0126: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
IL_012b: brtrue.s IL_0173
IL_012d: ldarg.0
IL_012e: ldc.i4.1
IL_012f: dup
IL_0130: stloc.0
IL_0131: stfld ""int C.<Main>d__0.<>1__state""
IL_0136: ldarg.0
IL_0137: ldloc.s V_7
IL_0139: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__2""
IL_013e: ldarg.0
IL_013f: stloc.s V_5
IL_0141: ldarg.0
IL_0142: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_0147: ldloca.s V_7
IL_0149: ldloca.s V_5
IL_014b: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.ValueTaskAwaiter, C.<Main>d__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.<Main>d__0)""
IL_0150: nop
IL_0151: leave IL_01e9
IL_0156: ldarg.0
IL_0157: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__2""
IL_015c: stloc.s V_7
IL_015e: ldarg.0
IL_015f: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.<Main>d__0.<>u__2""
IL_0164: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
IL_016a: ldarg.0
IL_016b: ldc.i4.m1
IL_016c: dup
IL_016d: stloc.0
IL_016e: stfld ""int C.<Main>d__0.<>1__state""
IL_0173: ldloca.s V_7
IL_0175: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
IL_017a: nop
IL_017b: ldarg.0
IL_017c: ldfld ""object C.<Main>d__0.<>s__2""
IL_0181: stloc.s V_6
IL_0183: ldloc.s V_6
IL_0185: brfalse.s IL_01a4
IL_0187: ldloc.s V_6
IL_0189: isinst ""System.Exception""
IL_018e: stloc.s V_9
IL_0190: ldloc.s V_9
IL_0192: brtrue.s IL_0197
IL_0194: ldloc.s V_6
IL_0196: throw
IL_0197: ldloc.s V_9
IL_0199: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)""
IL_019e: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()""
IL_01a3: nop
IL_01a4: ldarg.0
IL_01a5: ldfld ""int C.<Main>d__0.<>s__3""
IL_01aa: pop
IL_01ab: ldarg.0
IL_01ac: ldnull
IL_01ad: stfld ""object C.<Main>d__0.<>s__2""
IL_01b2: ldarg.0
IL_01b3: ldnull
IL_01b4: stfld ""System.Collections.Generic.IAsyncEnumerator<int> C.<Main>d__0.<>s__1""
IL_01b9: leave.s IL_01d5
}
catch System.Exception
{
IL_01bb: stloc.s V_9
IL_01bd: ldarg.0
IL_01be: ldc.i4.s -2
IL_01c0: stfld ""int C.<Main>d__0.<>1__state""
IL_01c5: ldarg.0
IL_01c6: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_01cb: ldloc.s V_9
IL_01cd: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
IL_01d2: nop
IL_01d3: leave.s IL_01e9
}
IL_01d5: ldarg.0
IL_01d6: ldc.i4.s -2
IL_01d8: stfld ""int C.<Main>d__0.<>1__state""
IL_01dd: ldarg.0
IL_01de: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder""
IL_01e3: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
IL_01e8: nop
IL_01e9: ret
}");
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
public void TestWithInterface_WithEarlyCompletion1()
{
......@@ -3456,7 +3763,7 @@ await foreach (var i in new C())
}
}
[System.Obsolete]
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token)
public AsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
......@@ -3500,7 +3807,7 @@ await foreach (var i in c)
{
}
}
public IAsyncEnumerator<int> GetAsyncEnumerator(System.Threading.CancellationToken token) => throw null;
public IAsyncEnumerator<int> GetAsyncEnumerator(System.Threading.CancellationToken token = default) => throw null;
}";
var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable);
comp.VerifyDiagnostics(
......@@ -3523,7 +3830,7 @@ void M()
{
}
}
public IAsyncEnumerator<int> GetAsyncEnumerator(System.Threading.CancellationToken token)
public IAsyncEnumerator<int> GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
throw null;
}
......@@ -3619,7 +3926,7 @@ public void TestWithInterfaceImplementingPattern()
public interface ICollection<T>
{
IMyAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken token);
IMyAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken token = default);
}
public interface IMyAsyncEnumerator<T>
{
......@@ -3629,7 +3936,7 @@ public interface IMyAsyncEnumerator<T>
public class Collection<T> : ICollection<T>
{
public IMyAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken token)
public IMyAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken token = default)
{
return new MyAsyncEnumerator<T>();
}
......@@ -3674,7 +3981,7 @@ await foreach (var i in c)
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);
Assert.Equal("IMyAsyncEnumerator<System.Int32> ICollection<System.Int32>.GetAsyncEnumerator(System.Threading.CancellationToken token)",
Assert.Equal("IMyAsyncEnumerator<System.Int32> ICollection<System.Int32>.GetAsyncEnumerator([System.Threading.CancellationToken token = default(System.Threading.CancellationToken)])",
info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal("System.Threading.Tasks.Task<System.Boolean> IMyAsyncEnumerator<System.Int32>.MoveNextAsync()",
info.MoveNextMethod.ToTestDisplayString());
......@@ -3930,37 +4237,89 @@ .maxstack 3
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
public void CancellationTokenIsDefault()
public void GetAsyncEnumerator_CancellationTokenMustBeOptional()
{
string source = @"
using System.Collections.Generic;
using System.Threading.Tasks;
using static System.Console;
class C
{
public static async Task Main()
{
try
await foreach (var i in new C())
{
await foreach (var i in new C())
{
}
}
catch { }
}
public IAsyncEnumerator<int> GetAsyncEnumerator(System.Threading.CancellationToken token)
{
if (token == default)
throw null;
}
}";
var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable });
comp.VerifyDiagnostics(
// (8,33): error CS8411: Asynchronous foreach statement cannot operate on variables of type 'C' because 'C' does not contain a suitable public instance definition for 'GetAsyncEnumerator'
// await foreach (var i in new C())
Diagnostic(ErrorCode.ERR_AwaitForEachMissingMember, "new C()").WithArguments("C", "GetAsyncEnumerator").WithLocation(8, 33)
);
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
public void GetAsyncEnumerator_WithOptionalParameter()
{
string source = @"
using System.Collections.Generic;
using System.Threading.Tasks;
class C
{
public static async Task Main()
{
await foreach (var i in new C())
{
Write(""correct token value"");
}
}
public IAsyncEnumerator<int> GetAsyncEnumerator(int opt = 0)
{
throw null;
}
}";
var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe);
var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable });
comp.VerifyDiagnostics();
}
CompileAndVerify(comp, expectedOutput: "correct token value", verify: Verification.Skipped);
[ConditionalFact(typeof(WindowsDesktopOnly))]
public void GetAsyncEnumerator_WithParams()
{
string source = @"
using System.Threading.Tasks;
class C
{
public static async Task Main()
{
await foreach (var i in new C())
{
}
}
public Enumerator GetAsyncEnumerator(params int[] x)
{
return new Enumerator();
}
public sealed class Enumerator
{
public async System.Threading.Tasks.Task<bool> MoveNextAsync()
{
System.Console.Write(""MoveNextAsync"");
await System.Threading.Tasks.Task.Yield();
return false;
}
public int Current
{
get => throw null;
}
}
}";
var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "MoveNextAsync");
}
}
}
......@@ -273,7 +273,11 @@ public ImmutableArray<U> ToDowncastedImmutable<U>()
public ImmutableArray<T> ToImmutableAndFree()
{
ImmutableArray<T> result;
if (_builder.Capacity == Count)
if (Count == 0)
{
result = ImmutableArray<T>.Empty;
}
else if (_builder.Capacity == Count)
{
result = _builder.MoveToImmutable();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册