diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 4a8ff5348bebf..8fb295e4e7ef2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -2482,10 +2482,16 @@ protected bool IsGenericTaskReturningAsyncMethod() return symbol?.Kind == SymbolKind.Method && ((MethodSymbol)symbol).IsGenericTaskReturningAsync(this.Compilation); } - protected bool IsIAsyncEnumerableReturningAsyncMethod() + protected bool IsIAsyncEnumerableOrIAsyncEnumeratorReturningAsyncMethod() { var symbol = this.ContainingMemberOrLambda; - return symbol?.Kind == SymbolKind.Method && ((MethodSymbol)symbol).IsIAsyncEnumerableReturningAsync(this.Compilation); + if (symbol?.Kind == SymbolKind.Method) + { + var method = (MethodSymbol)symbol; + return method.IsIAsyncEnumerableReturningAsync(this.Compilation) || + method.IsIAsyncEnumeratorReturningAsync(this.Compilation); + } + return false; } protected virtual TypeSymbol GetCurrentReturnType(out RefKind refKind) @@ -2547,7 +2553,7 @@ private BoundStatement BindReturn(ReturnStatementSyntax syntax, DiagnosticBag di diagnostics.Add(ErrorCode.ERR_MustNotHaveRefReturn, syntax.ReturnKeyword.GetLocation()); hasErrors = true; } - else if (IsIAsyncEnumerableReturningAsyncMethod()) + else if (IsIAsyncEnumerableOrIAsyncEnumeratorReturningAsyncMethod()) { diagnostics.Add(ErrorCode.ERR_ReturnInIterator, syntax.ReturnKeyword.GetLocation()); hasErrors = true; diff --git a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs index 6a76ef8c943d8..2ab1dd8218a19 100644 --- a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs @@ -155,7 +155,7 @@ internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, D } else if (!returnType.IsErrorType()) { - Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, _methodSymbol.Locations[0], _methodSymbol, returnType); + Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, _methodSymbol.Locations[0], _methodSymbol, returnType); } elementType = CreateErrorType(); } @@ -184,7 +184,8 @@ internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(C { if (refKind == RefKind.None && returnType.Kind == SymbolKind.NamedType) { - switch (returnType.OriginalDefinition.SpecialType) + TypeSymbol originalDefinition = returnType.OriginalDefinition; + switch (originalDefinition.SpecialType) { case SpecialType.System_Collections_IEnumerable: case SpecialType.System_Collections_IEnumerator: @@ -200,7 +201,8 @@ internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(C return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0]; } - if (returnType.OriginalDefinition == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T)) + if (originalDefinition == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T) || + originalDefinition == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T)) { return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0]; } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index db7454443320b..8813a3867fdf6 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -1024,7 +1024,7 @@ internal static string ERR_BadAsyncMethodBuilderTaskProperty { } /// - /// Looks up a localized string similar to The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T>. + /// Looks up a localized string similar to The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>. /// internal static string ERR_BadAsyncReturn { get { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index f1c2a94a0ec54..ddd5e292f2391 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3630,7 +3630,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi Since '{0}' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'? - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> Cannot return an expression of type 'void' diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs index 2c300afb05e72..d65901f2909e6 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs @@ -18,6 +18,10 @@ private sealed class AsyncIteratorRewriter : AsyncRewriter private FieldSymbol _promiseOfValueOrEndField; // this struct implements the IValueTaskSource logic private FieldSymbol _currentField; // stores the current/yielded value + // true if the iterator implements IAsyncEnumerable, + // false if it implements IAsyncEnumerator + private readonly bool _isEnumerable; + internal AsyncIteratorRewriter( BoundStatement body, MethodSymbol method, @@ -29,14 +33,21 @@ internal AsyncIteratorRewriter( : base(body, method, methodOrdinal, stateMachineType, slotAllocatorOpt, compilationState, diagnostics) { Debug.Assert(method.IteratorElementType != null); + + _isEnumerable = method.IsIAsyncEnumerableReturningAsync(method.DeclaringCompilation); } protected override void VerifyPresenceOfRequiredAPIs(DiagnosticBag bag) { base.VerifyPresenceOfRequiredAPIs(bag); - EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, bag); + + if (_isEnumerable) + { + EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, bag); + } EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__MoveNextAsync, bag); EnsureWellKnownMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerator_T__get_Current, bag); + EnsureWellKnownMember(WellKnownMember.System_IAsyncDisposable__DisposeAsync, bag); EnsureWellKnownMember(WellKnownMember.System_Threading_Tasks_ValueTask_T__ctor, bag); @@ -62,8 +73,11 @@ protected override void GenerateMethodImplementations() // IAsyncStateMachine methods and constructor base.GenerateMethodImplementations(); - // IAsyncEnumerable - GenerateIAsyncEnumerableImplementation_GetAsyncEnumerator(); + if (_isEnumerable) + { + // IAsyncEnumerable + GenerateIAsyncEnumerableImplementation_GetAsyncEnumerator(); + } // IAsyncEnumerator GenerateIAsyncEnumeratorImplementation_MoveNextAsync(); @@ -81,6 +95,9 @@ protected override void GenerateMethodImplementations() GenerateIAsyncDisposable_DisposeAsync(); } + protected override bool PreserveInitialParameterValuesAndThreadId + => _isEnumerable; + protected override void GenerateControlFields() { // the fields are initialized from entry-point method (which replaces the async-iterator method), so they need to be public @@ -153,8 +170,8 @@ protected override void GenerateConstructor() protected override void InitializeStateMachine(ArrayBuilder bodyBuilder, NamedTypeSymbol frameType, LocalSymbol stateMachineLocal) { - // var stateMachineLocal = new {StateMachineType}(FinishedStateMachine) - int initialState = StateMachineStates.FinishedStateMachine; + // var stateMachineLocal = new {StateMachineType}({initialState}) + int initialState = _isEnumerable ? StateMachineStates.FinishedStateMachine : StateMachineStates.NotStartedStateMachine; bodyBuilder.Add( F.Assignment( F.Local(stateMachineLocal), diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs index cfef83c811223..af41d21af7650 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs @@ -106,9 +106,8 @@ private Symbol EnsureWellKnownMember(WellKnownMember member, DiagnosticBag bag) return Binder.GetWellKnownTypeMember(F.Compilation, member, bag, body.Syntax.Location); } - // Should only be true for async-enumerables, not async-enumerators. Tracked by https://github.com/dotnet/roslyn/issues/31057 protected override bool PreserveInitialParameterValuesAndThreadId - => method.IsIterator; + => false; protected override void GenerateControlFields() { diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs index b6cf218295b5a..71e3b99196605 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs @@ -31,8 +31,12 @@ public AsyncStateMachine(VariableSlotAllocator variableAllocatorOpt, TypeCompila var elementType = TypeMap.SubstituteType(asyncMethod.IteratorElementType).TypeSymbol; this.IteratorElementType = elementType; - // IAsyncEnumerable - interfaces.Add(compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T).Construct(elementType)); + bool isEnumerable = asyncMethod.IsIAsyncEnumerableReturningAsync(compilation); + if (isEnumerable) + { + // IAsyncEnumerable + interfaces.Add(compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T).Construct(elementType)); + } // IAsyncEnumerator interfaces.Add(compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T).Construct(elementType)); diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs index 506f064dbe1b5..b2d106aa98d46 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs @@ -314,6 +314,15 @@ public static bool IsIAsyncEnumerableReturningAsync(this MethodSymbol method, CS && method.ReturnType.TypeSymbol.IsIAsyncEnumerableType(compilation); } + /// + /// Returns whether this method is async and returns an IAsyncEnumerator`1. + /// + public static bool IsIAsyncEnumeratorReturningAsync(this MethodSymbol method, CSharpCompilation compilation) + { + return method.IsAsync + && method.ReturnType.TypeSymbol.IsIAsyncEnumeratorType(compilation); + } + internal static CSharpSyntaxNode ExtractReturnTypeSyntax(this MethodSymbol method) { method = method.PartialDefinitionPart ?? method; diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 7aea1fa8835a7..540922ae00d44 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -1450,6 +1450,17 @@ internal static bool IsIAsyncEnumerableType(this TypeSymbol type, CSharpCompilat return (object)namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T); } + internal static bool IsIAsyncEnumeratorType(this TypeSymbol type, CSharpCompilation compilation) + { + var namedType = type as NamedTypeSymbol; + if ((object)namedType == null || namedType.Arity != 1) + { + return false; + } + + return (object)namedType.ConstructedFrom == compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T); + } + /// /// Returns true if the type is generic or non-generic custom task-like type due to the /// [AsyncMethodBuilder(typeof(B))] attribute. It returns the "B". @@ -1694,10 +1705,12 @@ private static bool IsWellKnownInteropServicesTopLevelType(this ITypeSymbol type public static bool IsBadAsyncReturn(this TypeSymbol returnType, CSharpCompilation declaringCompilation) { // Note: we're passing the return type explicitly (rather than using `method.ReturnType`) to avoid cycles - return returnType.SpecialType != SpecialType.System_Void && + return !returnType.IsErrorType() && + returnType.SpecialType != SpecialType.System_Void && !returnType.IsNonGenericTaskType(declaringCompilation) && !returnType.IsGenericTaskType(declaringCompilation) && - !returnType.IsIAsyncEnumerableType(declaringCompilation); + !returnType.IsIAsyncEnumerableType(declaringCompilation) && + !returnType.IsIAsyncEnumeratorType(declaringCompilation); } } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 50b905a808ba7..d04b5c5127770 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -6419,7 +6419,7 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> Návratový typ asynchronní metody musí být void, Task nebo Task<T>. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 20591719507d5..e0ab6798ef4e2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -6419,7 +6419,7 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> Der Rückgabetyp einer Async-Methode muss "void", "Task" oder "Task<T>" sein diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index a23c2fcd9d482..78765d70cef30 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -6419,7 +6419,7 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> El tipo de valor devuelto de un método asincrónico debe ser void, Task o Task<T> diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 23861e4fd7810..2ec3556c8c656 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -6419,7 +6419,7 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> Le type de retour d'une méthode async doit être void, Task ou Task<T> diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8848fc114f0d4..1684911d88272 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -6419,7 +6419,7 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> Il tipo restituito di un metodo asincrono deve essere void, Task o Task<T> diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 68b5e9f55f87f..c5fb4fd5d9887 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -6419,7 +6419,7 @@ C# では out と ref を区別しますが、CLR では同じと認識します - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> 非同期メソッドの戻り値の型は、void、Task、または Task<T> であることが必要です diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 5d216143297fb..132164cf5d46e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -6419,7 +6419,7 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간 - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> 비동기 메서드의 반환 형식은 void, Task 또는 Task<T>여야 합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 009233f4572f7..07e60d8e867f1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -6419,7 +6419,7 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> Zwracany typ metody asynchronicznej musi mieć wartość „void”, „Task” lub „Task<T>”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index a123e2c4389c8..510ec43043681 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -6419,7 +6419,7 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> O tipo de retorno de um método assíncrono deve ser void, Task ou Task<T> diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index a8aaa8e0f0a87..8d6b23691aece 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -6419,7 +6419,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> Возвращаемым типом асинхронного метода должен быть void, Task или Task<T> diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index f8cc406b21e99..71cdfa69594cc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -6419,7 +6419,7 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> Zaman uyumsuz bir yöntemin dönüş türü void, Task ve Task<T> olmalıdır> diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 50196a365e89e..0f200082e6370 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -6419,7 +6419,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> 异步方法的返回类型必须为 void、Task 或 Task<T> diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 27bfbbc758277..9c4342c924718 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -6419,7 +6419,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi - The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T> + The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> 非同步方法的傳回類型必須為 void、Task 或 Task<T> diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 69c0a8d88c03d..9a06fc1620134 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -1,15 +1,16 @@ // 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.Linq; using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; +using static Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen.Instruction; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - using static Instruction; internal enum Instruction { Write, @@ -22,35 +23,49 @@ internal enum Instruction [CompilerTrait(CompilerFeature.AsyncStreams)] public class CodeGenAsyncIteratorTests : EmitMetadataTestBase { - private void VerifyMissingMember(WellKnownMember member, params DiagnosticDescription[] expected) - { - var lib = CreateCompilationWithAsyncIterator(""); - var lib_ref = lib.EmitToImageReference(); - - string source = @" + private const string _enumerable = @" using System.Threading.Tasks; class C { async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; } } "; + private const string _enumerator = @" +using System.Threading.Tasks; +class C +{ + async System.Collections.Generic.IAsyncEnumerator M() { await Task.CompletedTask; yield return 3; } +} +"; + private static void VerifyMissingMember(WellKnownMember member, params DiagnosticDescription[] expected) + { + foreach (var source in new[] { _enumerable, _enumerator }) + { + VerifyMissingMember(source, member, expected); + } + } + + private static void VerifyMissingMember(string source, WellKnownMember member, params DiagnosticDescription[] expected) + { + var lib = CreateCompilationWithTasksExtensions(AsyncStreamsTypes); + var lib_ref = lib.EmitToImageReference(); var comp = CreateCompilationWithTasksExtensions(source, references: new[] { lib_ref }); comp.MakeMemberMissing(member); comp.VerifyEmitDiagnostics(expected); } - private void VerifyMissingType(WellKnownType type, params DiagnosticDescription[] expected) + private static void VerifyMissingType(WellKnownType type, params DiagnosticDescription[] expected) { - var lib = CreateCompilationWithAsyncIterator(""); - var lib_ref = lib.EmitToImageReference(); + foreach (var source in new[] { _enumerable, _enumerator }) + { + VerifyMissingType(source, type, expected); + } + } - string source = @" -using System.Threading.Tasks; -class C -{ - async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; } -} -"; + private static void VerifyMissingType(string source, WellKnownType type, params DiagnosticDescription[] expected) + { + var lib = CreateCompilationWithTasksExtensions(AsyncStreamsTypes); + var lib_ref = lib.EmitToImageReference(); var comp = CreateCompilationWithTasksExtensions(source, references: new[] { lib_ref }); comp.MakeTypeMissing(type); comp.VerifyEmitDiagnostics(expected); @@ -320,9 +335,6 @@ static async System.Collections.Generic.IAsyncEnumerable M() // (4,45): error CS0234: The type or namespace name 'IAsyncEnumerable<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?) // static async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(4, 45), - // (4,67): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable - // static async System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(4, 67), // (4,67): error CS8370: Feature 'async streams' is not available in C# 7.3. Please use language version 8.0 or greater. // static async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M").WithArguments("async streams", "8.0").WithLocation(4, 67), @@ -389,49 +401,73 @@ static async System.Collections.Generic.IAsyncEnumerable M() Assert.True(m.IsIterator); } - [ConditionalFact(typeof(WindowsDesktopOnly))] - public void AttributesSynthesized() + [Fact] + public void ReturnAfterAwait() { string source = @" -public class C +class C { - public static async System.Collections.Generic.IAsyncEnumerable M() + async System.Collections.Generic.IAsyncEnumerable M() { await System.Threading.Tasks.Task.CompletedTask; - yield return 4; + return null; } }"; var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugDll); - comp.VerifyDiagnostics(); - CompileAndVerify(comp, symbolValidator: module => - { - var method = module.GlobalNamespace.GetMember("C.M"); - AssertEx.SetEqual(new[] { "AsyncStateMachineAttribute", "IteratorStateMachineAttribute" }, - GetAttributeNames(method.GetAttributes())); - }); + comp.VerifyDiagnostics( + // (7,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. + // return null; + Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(7, 9) + ); } [Fact] - public void ReturnIAsyncEnumerator() + public void AwaitAfterReturn() { string source = @" -public class C +class C { - public static async System.Collections.Generic.IAsyncEnumerator M() + async System.Collections.Generic.IAsyncEnumerable M() { + return null; await System.Threading.Tasks.Task.CompletedTask; - yield return 4; } }"; var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( - // (4,74): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable - // public static async System.Collections.Generic.IAsyncEnumerator M() - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(4, 74), - // (4,74): error CS1624: The body of 'C.M()' cannot be an iterator block because 'IAsyncEnumerator' is not an iterator interface type - // public static async System.Collections.Generic.IAsyncEnumerator M() - Diagnostic(ErrorCode.ERR_BadIteratorReturn, "M").WithArguments("C.M()", "System.Collections.Generic.IAsyncEnumerator").WithLocation(4, 74) + // (6,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. + // return null; + Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(6, 9), + // (7,9): warning CS0162: Unreachable code detected + // await System.Threading.Tasks.Task.CompletedTask; + Diagnostic(ErrorCode.WRN_UnreachableCode, "await").WithLocation(7, 9) ); + + var m = comp.GlobalNamespace.GetMember("C.M"); + Assert.True(m.IsAsync); + Assert.False(m.IsIterator); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + public void AttributesSynthesized() + { + string source = @" +public class C +{ + public static async System.Collections.Generic.IAsyncEnumerable M() + { + await System.Threading.Tasks.Task.CompletedTask; + yield return 4; + } +}"; + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugDll); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, symbolValidator: module => + { + var method = module.GlobalNamespace.GetMember("C.M"); + AssertEx.SetEqual(new[] { "AsyncStateMachineAttribute", "IteratorStateMachineAttribute" }, + GetAttributeNames(method.GetAttributes())); + }); } [Fact] @@ -601,19 +637,46 @@ public void MissingTypeAndMembers_IAsyncStateMachine() [Fact] public void MissingTypeAndMembers_IAsyncEnumerable() { - VerifyMissingMember(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, + VerifyMissingMember(_enumerable, WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, // (5,64): error CS0656: Missing compiler required member 'System.Collections.Generic.IAsyncEnumerable`1.GetAsyncEnumerator' // async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ await Task.CompletedTask; yield return 3; }").WithArguments("System.Collections.Generic.IAsyncEnumerable`1", "GetAsyncEnumerator").WithLocation(5, 64) ); - VerifyMissingType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T, - // (5,60): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable - // async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; } - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(5, 60), - // (5,60): error CS1624: The body of 'C.M()' cannot be an iterator block because 'IAsyncEnumerable' is not an iterator interface type + VerifyMissingMember(_enumerator, WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator); + + // Since MakeTypeMissing doesn't fully simulate a type being absent (it only makes it disappear from GetWellKnownType), we specially verify missing IAsyncEnumerable since it appears in source + var comp1 = CreateCompilation(_enumerable); + comp1.VerifyDiagnostics( + // (5,38): error CS0234: The type or namespace name 'IAsyncEnumerable<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?) // async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; } - Diagnostic(ErrorCode.ERR_BadIteratorReturn, "M").WithArguments("C.M()", "System.Collections.Generic.IAsyncEnumerable").WithLocation(5, 60) + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(5, 38) + ); + + // Also verify on local functions + var comp2 = CreateCompilation(@" +using System.Threading.Tasks; +class C +{ + void M() + { + _ = local(); + async System.Collections.Generic.IAsyncEnumerable local() { await Task.CompletedTask; yield return 3; } + } +} +"); + comp2.VerifyDiagnostics( + // (8,42): error CS0234: The type or namespace name 'IAsyncEnumerable<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?) + // async System.Collections.Generic.IAsyncEnumerable local() { await Task.CompletedTask; yield return 3; } + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(8, 42) + ); + + // And missing IAsyncEnumerator + var comp3 = CreateCompilation(_enumerator); + comp3.VerifyDiagnostics( + // (5,38): error CS0234: The type or namespace name 'IAsyncEnumerator<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?) + // async System.Collections.Generic.IAsyncEnumerator M() { await Task.CompletedTask; yield return 3; } + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerator").WithArguments("IAsyncEnumerator<>", "System.Collections.Generic").WithLocation(5, 38) ); } @@ -680,10 +743,7 @@ public interface IAsyncDisposable comp.VerifyDiagnostics( // (8,42): error CS0234: The type or namespace name 'IAsyncEnumerable<>' does not exist in the namespace 'System.Collections.Generic' (are you missing an assembly reference?) // async System.Collections.Generic.IAsyncEnumerable local() - Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(8, 42), - // (8,64): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable - // async System.Collections.Generic.IAsyncEnumerable local() - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "local").WithLocation(8, 64) + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "IAsyncEnumerable").WithArguments("IAsyncEnumerable<>", "System.Collections.Generic").WithLocation(8, 42) ); } @@ -702,7 +762,16 @@ public void MissingTypeAndMembers_IAsyncEnumerator() Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ await Task.CompletedTask; yield return 3; }").WithArguments("System.Collections.Generic.IAsyncEnumerator`1", "get_Current").WithLocation(5, 64) ); - VerifyMissingType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T, + VerifyMissingType(_enumerator, WellKnownType.System_Collections_Generic_IAsyncEnumerator_T, + // (5,60): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable + // async System.Collections.Generic.IAsyncEnumerator M() { await Task.CompletedTask; yield return 3; } + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(5, 60), + // (5,60): error CS1624: The body of 'C.M()' cannot be an iterator block because 'IAsyncEnumerator' is not an iterator interface type + // async System.Collections.Generic.IAsyncEnumerator M() { await Task.CompletedTask; yield return 3; } + Diagnostic(ErrorCode.ERR_BadIteratorReturn, "M").WithArguments("C.M()", "System.Collections.Generic.IAsyncEnumerator").WithLocation(5, 60) + ); + + VerifyMissingType(_enumerable, WellKnownType.System_Collections_Generic_IAsyncEnumerator_T, // (5,64): error CS0656: Missing compiler required member 'System.Collections.Generic.IAsyncEnumerable`1.GetAsyncEnumerator' // async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ await Task.CompletedTask; yield return 3; }").WithArguments("System.Collections.Generic.IAsyncEnumerable`1", "GetAsyncEnumerator").WithLocation(5, 64), @@ -932,6 +1001,250 @@ async System.Collections.Generic.IAsyncEnumerable M2() ); } + [ConditionalFact(typeof(WindowsDesktopOnly))] + [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] + public void AsyncIteratorReturningEnumerator() + { + string source = @" +using static System.Console; +class C +{ + static async System.Collections.Generic.IAsyncEnumerator M(int value) + { + yield return value; + value = 5; + Write(""1 ""); + await System.Threading.Tasks.Task.CompletedTask; + Write(""2 ""); + yield return 3; + Write(""4 ""); + yield return value; + } + static async System.Threading.Tasks.Task Main() + { + await using (var enumerator = M(0)) + { + if (!await enumerator.MoveNextAsync()) throw null; + Write($""Value:{enumerator.Current} ""); + if (!await enumerator.MoveNextAsync()) throw null; + Write($""Value:{enumerator.Current} ""); + if (!await enumerator.MoveNextAsync()) throw null; + Write($""Value:{enumerator.Current} ""); + if (await enumerator.MoveNextAsync()) throw null; + if (await enumerator.MoveNextAsync()) throw null; + + Write(""Done""); + } + } +}"; + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "Value:0 1 2 Value:3 4 Value:5 Done", symbolValidator: verifyMembersAndInterfaces); + + void verifyMembersAndInterfaces(ModuleSymbol module) + { + var type = (NamedTypeSymbol)module.GlobalNamespace.GetMember("C.d__0"); + AssertEx.SetEqual(new[] { + "System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.System.Runtime.CompilerServices.IStrongBox>.Value { get; }", + "System.Runtime.CompilerServices.AsyncVoidMethodBuilder C.d__0.<>t__builder", + "System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.<>v__promiseOfValueOrEnd", + "System.Int32 C.d__0.value", + "C.d__0..ctor(System.Int32 <>1__state)", + "void C.d__0.MoveNext()", + "void C.d__0.SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine)", + "System.Threading.Tasks.ValueTask C.d__0.System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()", + "System.Int32 C.d__0.System.Collections.Generic.IAsyncEnumerator.Current.get", + "System.Boolean C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(System.Int16 token)", + "System.Threading.Tasks.Sources.ValueTaskSourceStatus C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(System.Int16 token)", + "void C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.OnCompleted(System.Action continuation, System.Object state, System.Int16 token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)", + "ref System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.System.Runtime.CompilerServices.IStrongBox>.get_Value()", + "System.Threading.Tasks.ValueTask C.d__0.System.IAsyncDisposable.DisposeAsync()", + "System.Int32 C.d__0.System.Collections.Generic.IAsyncEnumerator.Current { get; }", + "System.Int32 C.d__0.<>1__state" }, + type.GetMembersUnordered().Select(m => m.ToTestDisplayString())); + + AssertEx.SetEqual(new[] { + "System.Runtime.CompilerServices.IStrongBox>", + "System.Runtime.CompilerServices.IAsyncStateMachine", + "System.IAsyncDisposable", + "System.Threading.Tasks.Sources.IValueTaskSource", + "System.Collections.Generic.IAsyncEnumerator" }, + type.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.Select(m => m.ToTestDisplayString())); + } + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] + public void AsyncIteratorReturningEnumerator_CSharp73() + { + string source = @" +class C +{ + async System.Collections.Generic.IAsyncEnumerator M() + { + yield return 1; + await System.Threading.Tasks.Task.CompletedTask; + } +}"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, parseOptions: TestOptions.Regular7_3); + comp.VerifyDiagnostics( + // (4,60): error CS8370: Feature 'async streams' is not available in C# 7.3. Please use language version 8.0 or greater. + // async System.Collections.Generic.IAsyncEnumerator M() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M").WithArguments("async streams", "8.0").WithLocation(4, 60), + // (23,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater. + // #nullable disable + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(23, 2) + ); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] + public void AsyncIteratorReturningEnumerator_WithReturnOnly() + { + string source = @" +class C +{ + async System.Collections.Generic.IAsyncEnumerator M() + { + return null; + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (4,60): 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. + // async System.Collections.Generic.IAsyncEnumerator M() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 60), + // (6,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. + // return null; + Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(6, 9) + ); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] + public void ReturningIAsyncEnumerator_WithReturn() + { + string source = @" +class C +{ + System.Collections.Generic.IAsyncEnumerator M() + { + return null; + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics(); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] + public void AsyncIteratorReturningEnumerator_WithReturnAndAwait() + { + string source = @" +class C +{ + static async System.Collections.Generic.IAsyncEnumerator M(int value) + { + return value; + await System.Threading.Tasks.Task.CompletedTask; + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (6,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. + // return value; + Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(6, 9), + // (7,9): warning CS0162: Unreachable code detected + // await System.Threading.Tasks.Task.CompletedTask; + Diagnostic(ErrorCode.WRN_UnreachableCode, "await").WithLocation(7, 9) + ); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] + [WorkItem(31113, "https://github.com/dotnet/roslyn/issues/31113")] + public void AsyncIteratorReturningEnumerator_WithoutAsync() + { + string source = @" +class C +{ + static System.Collections.Generic.IAsyncEnumerator M(int value) + { + yield return value; + await System.Threading.Tasks.Task.CompletedTask; + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (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>'. + // await System.Threading.Tasks.Task.CompletedTask; + Diagnostic(ErrorCode.ERR_BadAwaitWithoutAsyncMethod, "await System.Threading.Tasks.Task.CompletedTask").WithArguments("System.Collections.Generic.IAsyncEnumerator").WithLocation(7, 9) + ); + + // This error message is rather poor. Tracked by https://github.com/dotnet/roslyn/issues/31113 + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] + public void AsyncIteratorReturningEnumerator_WithReturnAfterAwait() + { + string source = @" +class C +{ + static async System.Collections.Generic.IAsyncEnumerator M(int value) + { + await System.Threading.Tasks.Task.CompletedTask; + return value; + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (7,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. + // return value; + Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(7, 9) + ); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] + public void AsyncIteratorReturningEnumerator_WithoutAwait() + { + string source = @" +class C +{ + static async System.Collections.Generic.IAsyncEnumerator M(int value) + { + yield return value; + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (4,67): 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.IAsyncEnumerator M(int value) + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 67) + ); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + [WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")] + public void AsyncIteratorReturningEnumerator_WithoutYield() + { + string source = @" +class C +{ + static async System.Collections.Generic.IAsyncEnumerator M(int value) + { + await System.Threading.Tasks.Task.CompletedTask; + } +}"; + var comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyDiagnostics( + // (4,67): error CS0161: 'C.M(int)': not all code paths return a value + // static async System.Collections.Generic.IAsyncEnumerator M(int value) + Diagnostic(ErrorCode.ERR_ReturnExpected, "M").WithArguments("C.M(int)").WithLocation(4, 67) + ); + } + [ConditionalFact(typeof(WindowsDesktopOnly))] public void CallingMoveNextAsyncTwice() { @@ -1007,7 +1320,40 @@ static async System.Threading.Tasks.Task Main() }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "1 2 Stream1:3 1 2 Stream2:3 4 2 4 2 Done"); + CompileAndVerify(comp, expectedOutput: "1 2 Stream1:3 1 2 Stream2:3 4 2 4 2 Done", symbolValidator: verifyMembersAndInterfaces); + + void verifyMembersAndInterfaces(ModuleSymbol module) + { + var type = (NamedTypeSymbol)module.GlobalNamespace.GetMember("C.d__0"); + AssertEx.SetEqual(new[] { + "System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.System.Runtime.CompilerServices.IStrongBox>.Value { get; }", + "System.Runtime.CompilerServices.AsyncVoidMethodBuilder C.d__0.<>t__builder", + "System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.<>v__promiseOfValueOrEnd", + "System.Int32 C.d__0.<>3__value", + "C.d__0..ctor(System.Int32 <>1__state)", + "void C.d__0.MoveNext()", + "void C.d__0.SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine)", + "System.Collections.Generic.IAsyncEnumerator C.d__0.System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator()", + "System.Threading.Tasks.ValueTask C.d__0.System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()", + "System.Int32 C.d__0.System.Collections.Generic.IAsyncEnumerator.Current.get", + "System.Boolean C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(System.Int16 token)", + "System.Threading.Tasks.Sources.ValueTaskSourceStatus C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(System.Int16 token)", + "void C.d__0.System.Threading.Tasks.Sources.IValueTaskSource.OnCompleted(System.Action continuation, System.Object state, System.Int16 token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags)", + "ref System.Threading.Tasks.ManualResetValueTaskSourceLogic C.d__0.System.Runtime.CompilerServices.IStrongBox>.get_Value()", + "System.Threading.Tasks.ValueTask C.d__0.System.IAsyncDisposable.DisposeAsync()", + "System.Int32 C.d__0.System.Collections.Generic.IAsyncEnumerator.Current { get; }", + "System.Int32 C.d__0.<>1__state" }, + type.GetMembersUnordered().Select(m => m.ToTestDisplayString())); + + AssertEx.SetEqual(new[] { + "System.Runtime.CompilerServices.IStrongBox>", + "System.Runtime.CompilerServices.IAsyncStateMachine", + "System.IAsyncDisposable", + "System.Threading.Tasks.Sources.IValueTaskSource", + "System.Collections.Generic.IAsyncEnumerable", + "System.Collections.Generic.IAsyncEnumerator" }, + type.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.Select(m => m.ToTestDisplayString())); + } } [ConditionalFact(typeof(WindowsDesktopOnly))] @@ -2271,10 +2617,7 @@ async Unknown local() comp.VerifyDiagnostics( // (8,15): error CS0246: The type or namespace name 'Unknown' could not be found (are you missing a using directive or an assembly reference?) // async Unknown local() - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Unknown").WithArguments("Unknown").WithLocation(8, 15), - // (8,23): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, or IAsyncEnumerable - // async Unknown local() - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "local").WithLocation(8, 23) + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Unknown").WithArguments("Unknown").WithLocation(8, 15) ); } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index d7bb7251df5d4..3371b5639e361 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -28086,7 +28086,7 @@ async IAsyncEnumerable local() ); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/31057"), CompilerTrait(CompilerFeature.AsyncStreams)] + [Fact, CompilerTrait(CompilerFeature.AsyncStreams)] public void Yield_IAsyncEnumerator() { var source = @" @@ -28125,7 +28125,8 @@ async IAsyncEnumerator local() } } }"; - CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: WithNonNullTypesTrue()).VerifyDiagnostics( + var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: WithNonNullTypesTrue()); + comp.VerifyDiagnostics( // (8,22): warning CS8603: Possible null reference return. // yield return null; // 1 Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(8, 22),