From 4bf785cbf715579d8fe124bd99ce3529e1ab2d9c Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 1 Oct 2018 19:14:13 -0700 Subject: [PATCH] Fix lowering for async-iterator method with type parameter (#30105) --- docs/features/async-streams.md | 10 +- .../Portable/Binder/ForEachLoopBinder.cs | 12 +- .../Portable/CSharpResources.Designer.cs | 13 +- .../CSharp/Portable/CSharpResources.resx | 9 +- .../Compilation/ForEachStatementInfo.cs | 5 +- .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../FlowAnalysis/PreciseAbstractFlowPass.cs | 1 - .../AsyncRewriter.AsyncIteratorRewriter.cs | 21 +- .../Lowering/AsyncRewriter/AsyncRewriter.cs | 2 - .../AsyncRewriter/AsyncStateMachine.cs | 5 +- .../IteratorRewriter/IteratorRewriter.cs | 23 +- .../StateMachineRewriter.cs | 28 ++ .../Source/SourceMemberMethodSymbol.cs | 51 ++- .../Portable/xlf/CSharpResources.cs.xlf | 13 +- .../Portable/xlf/CSharpResources.de.xlf | 13 +- .../Portable/xlf/CSharpResources.es.xlf | 13 +- .../Portable/xlf/CSharpResources.fr.xlf | 13 +- .../Portable/xlf/CSharpResources.it.xlf | 13 +- .../Portable/xlf/CSharpResources.ja.xlf | 13 +- .../Portable/xlf/CSharpResources.ko.xlf | 13 +- .../Portable/xlf/CSharpResources.pl.xlf | 13 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 13 +- .../Portable/xlf/CSharpResources.ru.xlf | 13 +- .../Portable/xlf/CSharpResources.tr.xlf | 13 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 13 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 13 +- .../Emit/CodeGen/CodeGenAsyncForeachTests.cs | 366 +++++++++++++++++- .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 215 +++++++++- .../Compilation/ForEachStatementInfoTests.cs | 44 ++- 29 files changed, 815 insertions(+), 160 deletions(-) diff --git a/docs/features/async-streams.md b/docs/features/async-streams.md index 7a4324b92fe..beeec268d30 100644 --- a/docs/features/async-streams.md +++ b/docs/features/async-streams.md @@ -59,6 +59,9 @@ async IAsyncEnumerable GetValuesFromServer() ### Detailed design for async `foreach` statement PROTOTYPE(async-streams): TODO +Async foreach is disallowed on collections of type dynamic, as there is no async equivalent of the non-generic `IEnumerable` interface. + + ```C# E e = ((C)(x)).GetAsyncEnumerator() try @@ -81,13 +84,18 @@ finally { await e.DisposeAsync(); } ### Detailed design for async-iterator methods +An async-iterator method is replaced by a kick-off method, which initializes a state machine. It does not start running the state machine (unlike kick-off methods for regular async method). +The kick-off method method is marked with both `AsyncStateMachineAttribute` and `IteratorStateMachineAttribute`. + The state machine for an async-iterator method primarily implements `IAsyncEnumerable` and `IAsyncEnumerator`. -It is similar to a state machine produced for an async method. It contains builder and awaiter fields, used to run the state machine in the background (when an `await` is reached in the async-iterator). +It is similar to a state machine produced for an async method. It contains builder and awaiter fields, used to run the state machine in the background (when an `await` is reached in the async-iterator). It also captures parameter values (if any) or `this` (if needed). But it contains additional state: - a promise of a value-or-end, - a `bool` flag indicating whether the promise is active or not, - a current yielded value of type `T`. +The central method of the state machine is `MoveNext()`. It gets run by `WaitForNextAsync()` and `TryGetNext()`, or as a background continuation initiated from these from an `await` in the method. + The promise of a value-or-end is returned from `WaitForNextAsync`. It can be fulfilled with either: - `true` (when a value becomes available following background execution of the state machine), - `false` (if the end is reached), diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index a3ccf2267e2..8de02e0820e 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -262,10 +262,12 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, if (IsDirectlyInIterator) { diagnostics.Add(ErrorCode.ERR_BadIteratorLocalType, local.IdentifierToken.GetLocation()); + hasErrors = true; } else if (IsInAsyncMethod()) { diagnostics.Add(ErrorCode.ERR_BadAsyncLocalType, local.IdentifierToken.GetLocation()); + hasErrors = true; } } @@ -369,8 +371,8 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, { return new BoundForEachStatement( _syntax, - null, // can't be sure that it's complete - default(Conversion), + enumeratorInfoOpt: null, // can't be sure that it's complete + elementConversion: default, boundIterationVariableType, iterationVariables, iterationErrorExpression, @@ -683,6 +685,12 @@ private EnumeratorResult GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder bui return EnumeratorResult.FailedAndReported; } + if (collectionExprType.Kind == SymbolKind.DynamicType && IsAsync) + { + diagnostics.Add(ErrorCode.ERR_BadDynamicAsyncForEach, _syntax.Expression.Location); + return EnumeratorResult.FailedAndReported; + } + // The spec specifically lists the collection, enumerator, and element types for arrays and dynamic. if (collectionExprType.Kind == SymbolKind.ArrayType || collectionExprType.Kind == SymbolKind.DynamicType) { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 0dd40726106..ecaa61ae115 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -701,7 +701,7 @@ internal class CSharpResources { } /// - /// Looks up a localized string similar to Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. + /// Looks up a localized string similar to Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. /// internal static string ERR_AsyncForEachMissingMember { get { @@ -710,7 +710,7 @@ internal class CSharpResources { } /// - /// Looks up a localized string similar to Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'?. + /// Looks up a localized string similar to Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'?. /// internal static string ERR_AsyncForEachMissingMemberWrongAsync { get { @@ -1420,6 +1420,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to Cannot use a collection of dynamic type in an asynchronous foreach. + /// + internal static string ERR_BadDynamicAsyncForEach { + get { + return ResourceManager.GetString("ERR_BadDynamicAsyncForEach", resourceCulture); + } + } + /// /// Looks up a localized string similar to '{0}': user-defined conversions to or from the dynamic type are not allowed. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index efd0e302617..857be3b9ad4 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2633,13 +2633,13 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? Invalid type for parameter {0} in XML comment cref attribute: '{1}' @@ -5390,4 +5390,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ unconstrained type parameters in null coalescing operator - \ No newline at end of file + + Cannot use a collection of dynamic type in an asynchronous foreach + + diff --git a/src/Compilers/CSharp/Portable/Compilation/ForEachStatementInfo.cs b/src/Compilers/CSharp/Portable/Compilation/ForEachStatementInfo.cs index 5899ef3b5bc..e22bd0a1b64 100644 --- a/src/Compilers/CSharp/Portable/Compilation/ForEachStatementInfo.cs +++ b/src/Compilers/CSharp/Portable/Compilation/ForEachStatementInfo.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -114,8 +113,8 @@ public override int GetHashCode() Hash.Combine(ElementType, Hash.Combine(ElementConversion.GetHashCode(), Hash.Combine(CurrentConversion.GetHashCode(), - Hash.Combine(WaitForNextAsyncMethod.GetHashCode(), - TryGetNextMethod.GetHashCode())))))))); + Hash.Combine(WaitForNextAsyncMethod, + TryGetNextMethod?.GetHashCode() ?? 0)))))))); } } } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 3347aa4e29c..d702199dff3 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1592,6 +1592,7 @@ internal enum ErrorCode ERR_MultipleIAsyncEnumOfT = 9003, ERR_ForEachMissingMemberWrongAsync = 9004, ERR_AsyncForEachMissingMemberWrongAsync = 9005, + ERR_BadDynamicAsyncForEach = 9006, #endregion diagnostics introduced for C# 8.0 // Note: you will need to re-generate compiler code after adding warnings (build\scripts\generate-compiler-code.cmd) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs index efaf7012f0b..1a882efdb26 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs @@ -2157,7 +2157,6 @@ public override BoundNode VisitForEachStatement(BoundForEachStatement node) { _pendingBranches.Add(new PendingBranch(node, this.State)); } - //if (_trackExceptions) NotePossibleException(node); // PROTOTYPE(async-streams) return null; } diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs index 289bff5999b..cb39c854dc6 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.AsyncIteratorRewriter.cs @@ -30,8 +30,6 @@ private sealed class AsyncIteratorRewriter : AsyncRewriter : base(body, method, methodOrdinal, stateMachineType, slotAllocatorOpt, compilationState, diagnostics) { Debug.Assert(method.IteratorElementType != null); - - // PROTOTYPE(async-streams): Why does AsyncRewriter have logic to ignore accessibility? } protected override void VerifyPresenceOfRequiredAPIs(DiagnosticBag bag) @@ -101,8 +99,11 @@ protected override void GenerateControlFields() boolType, GeneratedNames.MakeAsyncIteratorPromiseIsActiveFieldName(), isPublic: true); + // the element type may contain method type parameters, which are now alpha-renamed into type parameters of the generated class + TypeSymbol elementType = ((AsyncStateMachine)stateMachineType).IteratorElementType; + // Add a field: T current - _currentField = F.StateMachineField(method.IteratorElementType, GeneratedNames.MakeIteratorCurrentFieldName()); + _currentField = F.StateMachineField(elementType, GeneratedNames.MakeIteratorCurrentFieldName()); } /// @@ -110,8 +111,6 @@ protected override void GenerateControlFields() /// protected override BoundStatement GenerateStateMachineCreation(LocalSymbol stateMachineVariable, NamedTypeSymbol frameType) { - // PROTOTYPE(async-streams): TODO review this (what is this error case at the start?) - // If the async method's result type is a type parameter of the method, then the AsyncTaskMethodBuilder // needs to use the method's type parameters inside the rewritten method body. All other methods generated // during async rewriting are members of the synthesized state machine struct, and use the type parameters @@ -154,7 +153,7 @@ protected override BoundStatement GenerateStateMachineCreation(LocalSymbol state F.Field(F.Local(stateMachineVariable), _promiseIsActiveField.AsMember(frameType)), F.Literal(true))); - // return local.$stateField; + // return local; bodyBuilder.Add(F.Return(F.Local(stateMachineVariable))); return F.Block( @@ -225,7 +224,7 @@ private void GenerateIAsyncEnumeratorImplementation_TryGetNext() // Produce the implementation for `T TryGetNext(out bool success)`: // if (this._promiseIsActive) // { - // if (_valueOrEndPromise.GetStatus(_valueOrEndPromise.Version) == ValueTaskSourceStatus.Pending) throw new Exception(); // PROTOTYPE(NullableReferenceTypes): Add this safeguard code + // if (_valueOrEndPromise.GetStatus(_valueOrEndPromise.Version) == ValueTaskSourceStatus.Pending) throw new Exception(); // https://github.com/dotnet/roslyn/issues/30109 Add this safeguard code // _promiseIsActive = false; // } // else @@ -264,7 +263,7 @@ private void GenerateIAsyncEnumeratorImplementation_TryGetNext() // if (this._promiseIsActive) // { // if (_valueOrEndPromise.GetStatus(_valueOrEndPromise.Version) == ValueTaskSourceStatus.Pending) throw new Exception(); - // if (State == StateMachineStates.NotStartedStateMachine) throw new Exception("You should call WaitForNextAsync first"); // PROTOTYPE(NullableReferenceTypes): Add this safeguard code + // if (State == StateMachineStates.NotStartedStateMachine) throw new Exception("You should call WaitForNextAsync first"); // https://github.com/dotnet/roslyn/issues/30109 Add this safeguard code // _promiseIsActive = false; // } // else @@ -504,13 +503,9 @@ private void GenerateIAsyncEnumerableImplementation_GetAsyncEnumerator() F.WellKnownMethod(WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator) .AsMember(IAsyncEnumerableOfElementType); - // PROTOTYPE(async-streams): TODO - // result = this; - // result.parameter = this.parameterProxy; // copy all of the parameter proxies // PROTOTYPE(async-streams): No sure what this is for - // The implementation doesn't depend on the method body of the iterator method. // Generates IAsyncEnumerator IAsyncEnumerable.GetEnumerator() - OpenMethodImplementation( IAsyncEnumerableOfElementType_GetEnumerator, hasMethodBodyDependency: false); + OpenMethodImplementation(IAsyncEnumerableOfElementType_GetEnumerator, hasMethodBodyDependency: false); // PROTOTYPE(async-streams): 0 is not the proper state to start with F.CloseMethod(F.Block( diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs index 039ae704a41..a2a30318da8 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncRewriter.cs @@ -12,7 +12,6 @@ internal partial class AsyncRewriter : StateMachineRewriter private readonly AsyncMethodBuilderMemberCollection _asyncMethodBuilderMemberCollection; private readonly bool _constructedSuccessfully; private readonly int _methodOrdinal; - private readonly bool _ignoreAccessibility; private FieldSymbol _builderField; @@ -28,7 +27,6 @@ internal partial class AsyncRewriter : StateMachineRewriter { _constructedSuccessfully = AsyncMethodBuilderMemberCollection.TryCreate(F, method, this.stateMachineType.TypeMap, out _asyncMethodBuilderMemberCollection); _methodOrdinal = methodOrdinal; - _ignoreAccessibility = compilationState.ModuleBuilderOpt.IgnoreAccessibility; } /// diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs index f12d73a2368..35692150748 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncStateMachine.cs @@ -16,6 +16,7 @@ internal sealed class AsyncStateMachine : StateMachineTypeSymbol private readonly TypeKind _typeKind; private readonly MethodSymbol _constructor; private readonly ImmutableArray _interfaces; + internal readonly TypeSymbol IteratorElementType; // only for async-iterators public AsyncStateMachine(VariableSlotAllocator variableAllocatorOpt, TypeCompilationState compilationState, MethodSymbol asyncMethod, int asyncMethodOrdinal, TypeKind typeKind) : base(variableAllocatorOpt, compilationState, asyncMethod, asyncMethodOrdinal) @@ -27,8 +28,8 @@ public AsyncStateMachine(VariableSlotAllocator variableAllocatorOpt, TypeCompila if (asyncMethod.IsIterator) { - var elementType = asyncMethod.IteratorElementType; - //this.ElementType = TypeMap.SubstituteType(elementType).Type; // PROTOTYPE(async-streams): TODO + var elementType = TypeMap.SubstituteType(asyncMethod.IteratorElementType).Type; + this.IteratorElementType = elementType; // IAsyncEnumerable interfaces.Add(compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T).Construct(elementType)); diff --git a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs index 8e04aad718e..de5fcef9cc2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs @@ -174,10 +174,7 @@ protected override void GenerateControlFields() // if it is an enumerable, and either Environment.CurrentManagedThreadId or Thread.ManagedThreadId are available // add a field: int initialThreadId - bool addInitialThreadId = - _isEnumerable && - ((object)F.WellKnownMember(WellKnownMember.System_Threading_Thread__ManagedThreadId, isOptional: true) != null || - (object)F.WellKnownMember(WellKnownMember.System_Environment__CurrentManagedThreadId, isOptional: true) != null); + bool addInitialThreadId = _isEnumerable && CanGetThreadId(); _initialThreadIdField = addInitialThreadId ? F.StateMachineField(F.SpecialType(SpecialType.System_Int32), GeneratedNames.MakeIteratorCurrentThreadIdFieldName()) @@ -284,23 +281,7 @@ private void GenerateEnumerableImplementation(ref BoundExpression managedThreadI if ((object)_initialThreadIdField != null) { - MethodSymbol currentManagedThreadIdMethod = null; - - PropertySymbol currentManagedThreadIdProperty = F.WellKnownMember(WellKnownMember.System_Environment__CurrentManagedThreadId, isOptional: true) as PropertySymbol; - - if ((object)currentManagedThreadIdProperty != null) - { - currentManagedThreadIdMethod = currentManagedThreadIdProperty.GetMethod; - } - - if ((object)currentManagedThreadIdMethod != null) - { - managedThreadId = F.Call(null, currentManagedThreadIdMethod); - } - else - { - managedThreadId = F.Property(F.Property(WellKnownMember.System_Threading_Thread__CurrentThread), WellKnownMember.System_Threading_Thread__ManagedThreadId); - } + managedThreadId = MakeCurrentThreadId(); makeIterator = F.If( condition: F.LogicalAnd( // if (this.state == -2 && this.initialThreadId == Thread.CurrentThread.ManagedThreadId) diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs index 00bddd42a3f..5c9c6e5b9e3 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs @@ -325,5 +325,33 @@ protected SynthesizedImplementationMethod OpenMoveNextMethodImplementation(Metho F.CurrentFunction = result; return result; } + + /// + /// Produce Environment.CurrentManagedThreadId if available, otherwise CurrentThread.ManagedThreadId + /// + protected BoundExpression MakeCurrentThreadId() + { + Debug.Assert(CanGetThreadId()); + var currentManagedThreadIdProperty = (PropertySymbol)F.WellKnownMember(WellKnownMember.System_Environment__CurrentManagedThreadId, isOptional: true); + if ((object)currentManagedThreadIdProperty != null) + { + MethodSymbol currentManagedThreadIdMethod = currentManagedThreadIdProperty.GetMethod; + if ((object)currentManagedThreadIdMethod != null) + { + return F.Call(null, currentManagedThreadIdMethod); + } + } + + return F.Property(F.Property(WellKnownMember.System_Threading_Thread__CurrentThread), WellKnownMember.System_Threading_Thread__ManagedThreadId); + } + + /// + /// Returns true if either Thread.ManagedThreadId or Environment.CurrentManagedThreadId are available + /// + protected bool CanGetThreadId() + { + return (object)F.WellKnownMember(WellKnownMember.System_Threading_Thread__ManagedThreadId, isOptional: true) != null || + (object)F.WellKnownMember(WellKnownMember.System_Environment__CurrentManagedThreadId, isOptional: true) != null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 6fe41571f2c..3c1761e27f3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -1613,35 +1613,44 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - if (this.IsAsync || this.IsIterator) + bool isAsync = this.IsAsync; + bool isIterator = this.IsIterator; + if (!isAsync && !isIterator) { - var compilation = this.DeclaringCompilation; - - // PROTOTYPE(async-streams): Need to review this + return; + } - // The async state machine type is not synthesized until the async method body is rewritten. If we are - // only emitting metadata the method body will not have been rewritten, and the async state machine - // type will not have been created. In this case, omit the attribute. - NamedTypeSymbol stateMachineType; - if (moduleBuilder.CompilationState.TryGetStateMachineType(this, out stateMachineType)) - { - WellKnownMember ctor = this.IsAsync ? - WellKnownMember.System_Runtime_CompilerServices_AsyncStateMachineAttribute__ctor : - WellKnownMember.System_Runtime_CompilerServices_IteratorStateMachineAttribute__ctor; + var compilation = this.DeclaringCompilation; - var arg = new TypedConstant(compilation.GetWellKnownType(WellKnownType.System_Type), TypedConstantKind.Type, stateMachineType.GetUnboundGenericTypeOrSelf()); + // The async state machine type is not synthesized until the async method body is rewritten. If we are + // only emitting metadata the method body will not have been rewritten, and the async state machine + // type will not have been created. In this case, omit the attribute. + if (moduleBuilder.CompilationState.TryGetStateMachineType(this, out NamedTypeSymbol stateMachineType)) + { + var arg = new TypedConstant(compilation.GetWellKnownType(WellKnownType.System_Type), + TypedConstantKind.Type, stateMachineType.GetUnboundGenericTypeOrSelf()); - AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(ctor, ImmutableArray.Create(arg))); + if (isAsync) + { + AddSynthesizedAttribute(ref attributes, + compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_AsyncStateMachineAttribute__ctor, + ImmutableArray.Create(arg))); } - - if (this.IsAsync) + if (isIterator) { - // Async kick-off method calls MoveNext, which contains user code. - // This means we need to emit DebuggerStepThroughAttribute in order - // to have correct stepping behavior during debugging. - AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDebuggerStepThroughAttribute()); + AddSynthesizedAttribute(ref attributes, + compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IteratorStateMachineAttribute__ctor, + ImmutableArray.Create(arg))); } } + + if (isAsync && !isIterator) + { + // Regular async (not async-iterator) kick-off method calls MoveNext, which contains user code. + // This means we need to emit DebuggerStepThroughAttribute in order + // to have correct stepping behavior during debugging. + AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDebuggerStepThroughAttribute()); + } } /// diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index fdcb32f1ca7..4372ecd2268 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8813,8 +8818,8 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 8b6c4e44a3b..3a3940c7817 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8826,8 +8831,8 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 7aba88e0ce8..a38d0c9ff61 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8800,8 +8805,8 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index f5e3c9374ea..f6c5bd624a7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8801,8 +8806,8 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 0f79a624361..a647bba1418 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8802,8 +8807,8 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index eadc9818f27..f0835bc3c6b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8826,8 +8831,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 2afb4db8a23..5e8e802724a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8826,8 +8831,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 1dbf8a98d55..eb84db7025c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8805,8 +8810,8 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 4e9adeed40a..7d3fe6969eb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8806,8 +8811,8 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index cf6ba851d97..2e516e27938 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8808,8 +8813,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 90db6b8e5a3..e16e7a633b2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8814,8 +8819,8 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 59812a6681b..14bc56cbae8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8826,8 +8831,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 54db40b3eb1..8800cba616f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -18,8 +18,13 @@ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}'. Did you mean 'foreach' rather than 'foreach await'? + + + + Cannot use a collection of dynamic type in an asynchronous foreach + Cannot use a collection of dynamic type in an asynchronous foreach @@ -8826,8 +8831,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' - Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' + Async foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance definition for '{1}' diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncForeachTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncForeachTests.cs index 81342ba2a0f..306c9daf2a8 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncForeachTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncForeachTests.cs @@ -54,7 +54,7 @@ async System.Threading.Tasks.Task M() }"; var comp = CreateCompilationWithMscorlib46(source); comp.VerifyDiagnostics( - // (6,33): error CS9001: Async foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public definition for 'GetAsyncEnumerator' + // (6,33): error CS9001: Async foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public instance definition for 'GetAsyncEnumerator' // foreach await (var i in new C()) Diagnostic(ErrorCode.ERR_AsyncForEachMissingMember, "new C()").WithArguments("C", "GetAsyncEnumerator").WithLocation(6, 33) ); @@ -82,7 +82,7 @@ public sealed class Enumerator }"; var comp = CreateCompilationWithMscorlib46(source); comp.VerifyDiagnostics( - // (6,33): error CS9001: Async foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public definition for 'GetAsyncEnumerator' + // (6,33): error CS9001: Async foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public instance definition for 'GetAsyncEnumerator' // foreach await (var i in new C()) Diagnostic(ErrorCode.ERR_AsyncForEachMissingMember, "new C()").WithArguments("C", "GetAsyncEnumerator").WithLocation(6, 33) ); @@ -701,6 +701,26 @@ class Element "Next(26) Convert(0) NextAsync(26) Dispose(37)", verify: Verification.Skipped); } + [Fact] + public void TestWithDynamicCollection() + { + string source = @" +class C +{ + public static async System.Threading.Tasks.Task Main() + { + foreach await (var i in (dynamic)new C()) + { + } + } +}"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, s_interfaces }); + comp.VerifyDiagnostics( + // (6,33): error CS9006: Cannot use a collection of dynamic type in an asynchronous foreach + // foreach await (var i in (dynamic)new C()) + Diagnostic(ErrorCode.ERR_BadDynamicAsyncForEach, "(dynamic)new C()").WithLocation(6, 33)); + } + [Fact] public void TestWithIncompleteInterface() { @@ -1079,7 +1099,7 @@ void M(IEnumerable collection) }"; var comp = CreateCompilationWithTasksExtensions(source + s_interfaces); comp.VerifyDiagnostics( - // (7,33): error CS9005: Async foreach statement cannot operate on variables of type 'IEnumerable' because 'IEnumerable' does not contain a public definition for 'GetAsyncEnumerator'. Did you mean 'foreach' rather than 'foreach await'? + // (7,33): error CS9005: Async foreach statement cannot operate on variables of type 'IEnumerable' because 'IEnumerable' does not contain a public instance definition for 'GetAsyncEnumerator'. Did you mean 'foreach' rather than 'foreach await'? // foreach await (var i in collection) Diagnostic(ErrorCode.ERR_AsyncForEachMissingMemberWrongAsync, "collection").WithArguments("System.Collections.Generic.IEnumerable", "GetAsyncEnumerator").WithLocation(7, 33), // (7,17): error CS4033: 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'. @@ -1141,7 +1161,7 @@ public bool MoveNext() }"; var comp = CreateCompilationWithTasksExtensions(source + s_interfaces); comp.VerifyDiagnostics( - // (6,33): error CS9005: Async foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public definition for 'GetAsyncEnumerator'. Did you mean 'foreach' rather than 'foreach await'? + // (6,33): error CS9005: Async foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public instance definition for 'GetAsyncEnumerator'. Did you mean 'foreach' rather than 'foreach await'? // foreach await (var i in new C()) Diagnostic(ErrorCode.ERR_AsyncForEachMissingMemberWrongAsync, "new C()").WithArguments("C", "GetAsyncEnumerator").WithLocation(6, 33) ); @@ -1199,6 +1219,327 @@ public int TryGetNext(out bool success) Assert.False(internalInfo.NeedsDisposeMethod); } + [Fact] + public void TestWithPattern_Ref() + { + string source = @" +class C +{ + async System.Threading.Tasks.Task M() + { + foreach await (ref var i in new C()) + { + } + } + public Enumerator GetAsyncEnumerator() + => throw null; + public sealed class Enumerator + { + public System.Threading.Tasks.Task WaitForNextAsync() + => throw null; + public int TryGetNext(out bool success) + => throw null; + } +}"; + var comp = CreateCompilationWithTasksExtensions(source + s_interfaces); + comp.VerifyDiagnostics( + // (6,32): error CS8177: Async methods cannot have by-reference locals + // foreach await (ref var i in new C()) + Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "i").WithLocation(6, 32)); + + var tree = comp.SyntaxTrees.Single(); + var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); + var foreachSyntax = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(default, model.GetForEachStatementInfo(foreachSyntax)); + } + + [Fact] + public void TestWithPattern_PointerType() + { + string source = @" +unsafe class C +{ + async System.Threading.Tasks.Task M() + { + foreach await (var i in new C()) + { + } + } + public Enumerator GetAsyncEnumerator() + => throw null; + public sealed class Enumerator + { + public System.Threading.Tasks.Task WaitForNextAsync() + => throw null; + public int* TryGetNext(out bool success) + => throw null; + } +}"; + var comp = CreateCompilationWithTasksExtensions(source + s_interfaces, options: TestOptions.UnsafeDebugDll); + comp.VerifyDiagnostics( + // (6,17): error CS4004: Cannot await in an unsafe context + // foreach await (var i in new C()) + Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await").WithLocation(6, 17)); + + var tree = comp.SyntaxTrees.Single(); + var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); + var foreachSyntax = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(default, model.GetForEachStatementInfo(foreachSyntax)); + } + + [Fact] + public void TestWithPattern_InaccessibleGetAsyncEnumerator() + { + string source = @" +class C +{ + async System.Threading.Tasks.Task M() + { + foreach await (var i in new D()) + { + } + } +} +class D +{ + private Enumerator GetAsyncEnumerator() + => throw null; + public sealed class Enumerator + { + public System.Threading.Tasks.Task WaitForNextAsync() + => throw null; + public int TryGetNext(out bool success) + => throw null; + } +}"; + var comp = CreateCompilationWithTasksExtensions(source + s_interfaces); + comp.VerifyDiagnostics( + // (6,33): error CS9001: Async foreach statement cannot operate on variables of type 'D' because 'D' does not contain a public definition for 'GetAsyncEnumerator' + // foreach await (var i in new D()) + Diagnostic(ErrorCode.ERR_AsyncForEachMissingMember, "new D()").WithArguments("D", "GetAsyncEnumerator").WithLocation(6, 33) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); + var foreachSyntax = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(default, model.GetForEachStatementInfo(foreachSyntax)); + } + + [Fact] + public void TestWithPattern_InaccessibleWaitForNextAsync() + { + string source = @" +class C +{ + async System.Threading.Tasks.Task M() + { + foreach await (var i in new D()) + { + } + } +} +class D +{ + public Enumerator GetAsyncEnumerator() + => throw null; + public sealed class Enumerator + { + private System.Threading.Tasks.Task WaitForNextAsync() + => throw null; + public int TryGetNext(out bool success) + => throw null; + } +}"; + var comp = CreateCompilationWithTasksExtensions(source + s_interfaces); + comp.VerifyDiagnostics( + // (6,33): error CS0122: 'D.Enumerator.WaitForNextAsync()' is inaccessible due to its protection level + // foreach await (var i in new D()) + Diagnostic(ErrorCode.ERR_BadAccess, "new D()").WithArguments("D.Enumerator.WaitForNextAsync()").WithLocation(6, 33), + // (6,33): error CS9002: Async foreach requires that the return type 'D.Enumerator' of 'D.GetAsyncEnumerator()' must have suitable public WaitForNextAsync and TryGetNext methods + // foreach await (var i in new D()) + Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new D()").WithArguments("D.Enumerator", "D.GetAsyncEnumerator()").WithLocation(6, 33) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); + var foreachSyntax = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(default, model.GetForEachStatementInfo(foreachSyntax)); + } + + [Fact] + public void TestWithPattern_InaccessibleTryGetNext() + { + string source = @" +class C +{ + async System.Threading.Tasks.Task M() + { + foreach await (var i in new D()) + { + } + } +} +class D +{ + public Enumerator GetAsyncEnumerator() + => throw null; + public sealed class Enumerator + { + public System.Threading.Tasks.Task WaitForNextAsync() + => throw null; + private int TryGetNext(out bool success) + => throw null; + } +}"; + var comp = CreateCompilationWithTasksExtensions(source + s_interfaces); + comp.VerifyDiagnostics( + // (6,33): error CS0122: 'D.Enumerator.TryGetNext(out bool)' is inaccessible due to its protection level + // foreach await (var i in new D()) + Diagnostic(ErrorCode.ERR_BadAccess, "new D()").WithArguments("D.Enumerator.TryGetNext(out bool)").WithLocation(6, 33), + // (6,33): error CS9002: Async foreach requires that the return type 'D.Enumerator' of 'D.GetAsyncEnumerator()' must have suitable public WaitForNextAsync and TryGetNext methods + // foreach await (var i in new D()) + Diagnostic(ErrorCode.ERR_BadGetAsyncEnumerator, "new D()").WithArguments("D.Enumerator", "D.GetAsyncEnumerator()").WithLocation(6, 33) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); + var foreachSyntax = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(default, model.GetForEachStatementInfo(foreachSyntax)); + } + + [Fact] + public void TestWithPattern_RefStruct() + { + string source = @" +using static System.Console; +using System.Threading.Tasks; +public class C +{ + public static async System.Threading.Tasks.Task Main() + { + foreach await (var s in new C()) + { + Write($""{s.ToString()} ""); + } + Write(""Done""); + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public sealed class Enumerator + { + int i = -1; + public S TryGetNext(out bool success) + { + i++; + success = (i % 10 % 3 != 0); + return new S(i); + } + public async Task WaitForNextAsync() + { + i = i + 11; + bool more = await Task.FromResult(i < 20); + return more; + } + } +} +public ref struct S +{ + int i; + public S(int i) + { + this.i = i; + } + public override string ToString() + => i.ToString(); +} +"; + var comp = CreateCompilationWithTasksExtensions(source + s_interfaces, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "11 12 Done"); + } + + [Fact] + public void TestWithPattern_RefReturningTryGetNext() + { + string source = @" +using static System.Console; +using System.Threading.Tasks; +public class C +{ + public static async System.Threading.Tasks.Task Main() + { + foreach await (var s in new C()) + { + Write($""{s.ToString()} ""); + } + Write(""Done""); + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public sealed class Enumerator + { + int i = -1; + S current; + public ref S TryGetNext(out bool success) + { + i++; + success = (i % 10 % 3 != 0); + current = new S(i); + return ref current; + } + public async Task WaitForNextAsync() + { + i = i + 11; + bool more = await Task.FromResult(i < 20); + return more; + } + } +} +public struct S +{ + int i; + public S(int i) + { + this.i = i; + } + public override string ToString() + => i.ToString(); +} +"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, s_interfaces }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "11 12 Done", verify: Verification.Fails); + } + + [Fact] + public void TestWithPattern_IterationVariableIsReadOnly() + { + string source = @" +class C +{ + async System.Threading.Tasks.Task M() + { + foreach await (var i in new C()) + { + i = 1; + } + } + public Enumerator GetAsyncEnumerator() + => throw null; + public sealed class Enumerator + { + public System.Threading.Tasks.Task WaitForNextAsync() + => throw null; + public int TryGetNext(out bool success) + => throw null; + } +}"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, s_interfaces }); + comp.VerifyDiagnostics( + // (8,13): error CS1656: Cannot assign to 'i' because it is a 'foreach iteration variable' + // i = 1; + Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "i").WithArguments("i", "foreach iteration variable").WithLocation(8, 13) + ); + } + [Fact] public void TestWithPattern_WithStruct_WaitForNextAsyncReturnsTask() { @@ -3397,14 +3738,7 @@ .maxstack 3 ", sequencePoints: "C+
d__0.MoveNext", source: source); } - [Fact] - public void TestFoEachStatementInfo_IEquatable() - { - // PROTOTYPE(async-streams) test ForEachStatementInfo equality and such - } - // PROTOTYPE(async-streams) More test ideas - // block dynamic // test with captures: // int[] values = { 7, 9, 13 }; @@ -3415,10 +3749,7 @@ public void TestFoEachStatementInfo_IEquatable() //} // f(); - // pointer element type - // verify that the foreach variables are readonly // nested deconstruction or tuple - // pattern with inaccessible methods // review instrumentation // test various things that could go wrong with binding the await for DisposeAsync // throwing exception from enumerator, or from inside the async-foreach @@ -3428,15 +3759,10 @@ public void TestFoEachStatementInfo_IEquatable() // IOperation // IDE // scripting? - // foreach on restricted type (like ref struct) in async or iterator method - // foreach on restricted type in a regular method - - // Also try with ref-returning TryGetNext. With both ref iteration variable and ordinary one. Not sure what spec says here, but technically either could work. Especially the ordinary byval case - it could even do implicit conversions. Ref-returning methods can work as rvalues too. - // Also, if ref iteration variables are allowed, check readonliness mismatches. I.E. if method returns a readonly ref, but iterator var is an ordinary ref nd the other way around. + // expression trees // Misc other test ideas: // Verify that async-dispose doesn't have a similar bug with struct resource - // cleanup: use statement lists for async-using, instead of blocks // IAsyncEnumerable has an 'out' type parameter, any tests I need to do related to that? // spec: struct case should be blocked? // spec: extension methods don't contribute diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 25c0595fdfd..87b5a14448b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -1,13 +1,14 @@ // 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.Text; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - using Roslyn.Test.Utilities; using static Instruction; internal enum Instruction { @@ -21,23 +22,21 @@ internal enum Instruction [CompilerTrait(CompilerFeature.AsyncStreams)] public class CodeGenAsyncIteratorTests : EmitMetadataTestBase { - // PROTOTYPE(async-streams) - // Test missing remaining types/members once BCL APIs are finalized (MRVTSL, IStrongBox, IValueTaskSource) - // test missing AsyncTaskMethodBuilder or missing members Create(), Task, ... + // PROTOTYPE(async-streams) Add more tests: // Test with yield or await in try/catch/finally // More tests with exception thrown // There is a case in GetIteratorElementType with IsDirectlyInIterator that relates to speculation, needs testing // yield break disallowed in finally and top-level script (see BindYieldBreakStatement); same for yield return (see BindYieldReturnStatement) // binding for yield return (BindYieldReturnStatement) validates escape rules, needs testing // test yield in async lambda (still error) - // test exception handling (should capture and return the exception via the promise) - // test IAsyncEnumerable M() ... // test with IAsyncEnumerable // other tests with dynamic? // test should cover both case with AwaitOnCompleted and AwaitUnsafeOnCompleted // test `async IAsyncEnumerable M() { return TaskLike(); }` // Can we avoid making IAsyncEnumerable special from the start? Making mark it with an attribute like we did for task-like? // Do some manual validation on debugging scenarios, including with exceptions (thrown after yield and after await). + // Test with one or both or the threadID APIs missing. + // Enable remaining windows/desktop-only to run on Core private void VerifyMissingMember(WellKnownMember member, params DiagnosticDescription[] expected) { @@ -51,9 +50,9 @@ class C async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; } } "; - var comp = CreateCompilationWithTasksExtensions(source, references: new[] { lib_ref }); - comp.MakeMemberMissing(member); - comp.VerifyEmitDiagnostics(expected); + var comp = CreateCompilationWithTasksExtensions(source, references: new[] { lib_ref }); + comp.MakeMemberMissing(member); + comp.VerifyEmitDiagnostics(expected); } private void VerifyMissingType(WellKnownType type, params DiagnosticDescription[] expected) @@ -68,9 +67,60 @@ class C async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; } } "; - var comp = CreateCompilationWithTasksExtensions(source, references: new[] { lib_ref }); - comp.MakeTypeMissing(type); - comp.VerifyEmitDiagnostics(expected); + var comp = CreateCompilationWithTasksExtensions(source, references: new[] { lib_ref }); + comp.MakeTypeMissing(type); + comp.VerifyEmitDiagnostics(expected); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + public void RefStructElementType() + { + string source = @" +class C +{ + static async System.Collections.Generic.IAsyncEnumerable M() + { + await System.Threading.Tasks.Task.CompletedTask; + yield return new S(); + } + static async System.Threading.Tasks.Task Main() + { + foreach await (var s in M()) + { + } + } +} +ref struct S +{ +}"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (4,65): error CS0306: The type 'S' may not be used as a type argument + // static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.ERR_BadTypeArgument, "M").WithArguments("S").WithLocation(4, 65) + ); + } + + [Fact] + 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 = CreateCompilationWithTasksExtensions(new[] { source, s_common }, 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] @@ -189,6 +239,16 @@ public void MissingTypeAndMembers_IValueTaskSource() ); } + [Fact] + public void MissingMember_AsyncVoidMethodBuilder() + { + VerifyMissingMember(WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__Create, + // (5,64): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create' + // async System.Collections.Generic.IAsyncEnumerable M() { await Task.CompletedTask; yield return 3; } + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ await Task.CompletedTask; yield return 3; }").WithArguments("System.Runtime.CompilerServices.AsyncVoidMethodBuilder", "Create").WithLocation(5, 64) + ); + } + [Fact] public void MissingTypeAndMembers_IAsyncStateMachine() { @@ -1097,6 +1157,134 @@ .maxstack 3 } } + [ConditionalFact(typeof(WindowsDesktopOnly))] + public void AsyncIteratorWithGenericReturn() + { + string source = @" +using static System.Console; +class C +{ + static async System.Collections.Generic.IAsyncEnumerable M(T value) + { + Write(""1 ""); + await System.Threading.Tasks.Task.CompletedTask; + Write(""2 ""); + yield return value; + Write("" 4 ""); + } + static async System.Threading.Tasks.Task Main() + { + Write(""0 ""); + foreach await (var i in M(3)) + { + Write(i); + } + Write(""5""); + } +}"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 5"); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + public void AsyncIteratorWithGenericReturnFromContainingType() + { + string source = @" +using static System.Console; +public class C +{ + public static async System.Collections.Generic.IAsyncEnumerable M(T value) + { + Write(""1 ""); + await System.Threading.Tasks.Task.CompletedTask; + Write(""2 ""); + yield return value; + Write("" 4 ""); + } +} +class D +{ + static async System.Threading.Tasks.Task Main() + { + Write(""0 ""); + foreach await (var i in C.M(3)) + { + Write(i); + } + Write(""5""); + } +}"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 5"); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + public void AsyncIteratorWithParameter() + { + string source = @" +using static System.Console; +class C +{ + static async System.Collections.Generic.IAsyncEnumerable M(int parameter) + { + Write($""p:{parameter} ""); + parameter++; + await System.Threading.Tasks.Task.Delay(10); + Write($""p:{parameter} ""); + parameter++; + yield return 42; + Write($""p:{parameter} ""); + } + static async System.Threading.Tasks.Task Main() + { + Write(""Start ""); + foreach await (var i in M(10)) + { + Write(""Value ""); + } + Write(""End""); + } +}"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "Start p:10 p:11 Value p:12 End"); + } + + [ConditionalFact(typeof(WindowsDesktopOnly))] + public void AsyncIteratorWithThis() + { + string source = @" +using static System.Console; +class C +{ + int field = 10; + async System.Collections.Generic.IAsyncEnumerable M() + { + Write($""f:{this.field} ""); + this.field++; + await System.Threading.Tasks.Task.Delay(10); + Write($""f:{this.field} ""); + this.field++; + yield return 42; + Write($""f:{this.field} ""); + } + static async System.Threading.Tasks.Task Main() + { + Write(""Start ""); + foreach await (var i in new C().M()) + { + Write(""Value ""); + } + Write(""End""); + } +}"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + var v = CompileAndVerify(comp, expectedOutput: "Start f:10 f:11 Value f:12 End"); + } + [Fact] public void AsyncIteratorWithReturn() { @@ -1629,7 +1817,7 @@ static void Assert(bool b) }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, s_common }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - // PROTOTYPE(async-streams): need to implement the exception + // https://github.com/dotnet/roslyn/issues/30109 need to implement the guard/exception //CompileAndVerify(comp, expectedOutput: "Done"); } @@ -1730,7 +1918,6 @@ static void Assert(bool b) CompileAndVerify(comp, expectedOutput: "Done"); } - // PROTOTYPE(async-streams): Consider moving this common test code to TestSources.cs private static readonly string s_common = @" namespace System.Collections.Generic { diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/ForEachStatementInfoTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/ForEachStatementInfoTests.cs index d12366ce3f4..0f853248ae1 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/ForEachStatementInfoTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/ForEachStatementInfoTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp { public class ForEachStatementInfoTests : CSharpTestBase { - [Fact(Skip = "PROTOTYPE(async-streams)")] + [Fact] public void Equality() { var c = CreateCompilation(@" @@ -19,7 +19,7 @@ class E1 public E GetEnumerator() { return null; } public bool MoveNext() { return false; } public object Current { get; } - public void Dispose() { } + public void Dispose() { } } class E2 @@ -27,7 +27,21 @@ class E2 public E GetEnumerator() { return null; } public bool MoveNext() { return false; } public object Current { get; } - public void Dispose() { } + public void Dispose() { } +} +class E3 +{ + public E GetAsyncEnumerator() => throw null; + public ValueTask WaitForNextAsync() => throw null; + public object TryGetNext(out bool success) => throw null; + public ValueTask DisposeAsync() => throw null; +} +class E4 +{ + public E GetAsyncEnumerator() => throw null; + public ValueTask WaitForNextAsync() => throw null; + public object TryGetNext(out bool success) => throw null; + public ValueTask DisposeAsync() => throw null; } "); var e1 = (TypeSymbol)c.GlobalNamespace.GetMembers("E1").Single(); @@ -52,6 +66,30 @@ class E2 EqualityTesting.AssertNotEqual(new ForEachStatementInfo(ge1, mn1, cur1, disp2, e1, conv1, conv1), new ForEachStatementInfo(ge1, mn1, cur1, disp1, e1, conv1, conv1)); EqualityTesting.AssertNotEqual(new ForEachStatementInfo(ge1, mn1, cur1, disp1, e1, conv2, conv1), new ForEachStatementInfo(ge1, mn1, cur1, disp1, e1, conv1, conv1)); EqualityTesting.AssertNotEqual(new ForEachStatementInfo(ge1, mn1, cur1, disp1, e1, conv1, conv2), new ForEachStatementInfo(ge1, mn1, cur1, disp1, e1, conv1, conv1)); + + var e3 = (TypeSymbol)c.GlobalNamespace.GetMembers("E3").Single(); + var gae3 = (MethodSymbol)e3.GetMembers("GetAsyncEnumerator").Single(); + var wfna3 = (MethodSymbol)e3.GetMembers("WaitForNextAsync").Single(); + var tgn3 = (MethodSymbol)e3.GetMembers("TryGetNext").Single(); + var disp3 = (MethodSymbol)e3.GetMembers("DisposeAsync").Single(); + var conv3 = Conversion.NoConversion; + + var e4 = (TypeSymbol)c.GlobalNamespace.GetMembers("E4").Single(); + var gae4 = (MethodSymbol)e4.GetMembers("GetAsyncEnumerator").Single(); + var wfna4 = (MethodSymbol)e4.GetMembers("WaitForNextAsync").Single(); + var tgn4 = (MethodSymbol)e4.GetMembers("TryGetNext").Single(); + var disp4 = (MethodSymbol)e4.GetMembers("DisposeAsync").Single(); + var conv4 = Conversion.NoConversion; + + EqualityTesting.AssertEqual( + new ForEachStatementInfo(gae3, moveNextMethod: null, currentProperty: null, disp3, e3, conv3, conv3, wfna3, tgn3), + new ForEachStatementInfo(gae3, moveNextMethod: null, currentProperty: null, disp3, e3, conv3, conv3, wfna3, tgn3)); + EqualityTesting.AssertNotEqual( + new ForEachStatementInfo(gae3, moveNextMethod: null, currentProperty: null, disp3, e3, conv3, conv3, wfna3, tgn3), + new ForEachStatementInfo(gae3, moveNextMethod: null, currentProperty: null, disp3, e3, conv3, conv3, wfna4, tgn3)); + EqualityTesting.AssertNotEqual( + new ForEachStatementInfo(gae3, moveNextMethod: null, currentProperty: null, disp3, e3, conv3, conv3, wfna3, tgn3), + new ForEachStatementInfo(gae3, moveNextMethod: null, currentProperty: null, disp3, e3, conv3, conv3, wfna3, tgn4)); } } } -- GitLab