Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async-iterator method can return IAsyncEnumerator<T> #31114

Merged
merged 3 commits into from
Nov 28, 2018

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Nov 12, 2018

Fixes #31057

Aside from not implementing the IAsyncEnumerable<T> interface (and its GetAsyncEnumerator method), the state machine also does not need to hold proxied/preserved parameters, or to preserve the original threadID.

Also integrated with #31072 (nullability analysis of yield return).

@jcouv jcouv added Area-Compilers PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. New Feature - Async Streams Async Streams labels Nov 12, 2018
@jcouv jcouv added this to the 16.0.P2 milestone Nov 12, 2018
@jcouv jcouv self-assigned this Nov 12, 2018
@jcouv jcouv requested a review from a team as a code owner November 12, 2018 06:48
@jasonmalinowski jasonmalinowski changed the base branch from dev16.0-preview2 to master November 16, 2018 19:40
@jcouv jcouv removed the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Nov 18, 2018
@jcouv
Copy link
Member Author

jcouv commented Nov 19, 2018

@dotnet/roslyn-compiler for review. Thanks
This allows async-iterator methods to return IAsyncEnumerator<T> and in that case, we can produce a simplified state machine (removing some machinery that serves only for enumerable case). #Resolved

@jcouv
Copy link
Member Author

jcouv commented Nov 26, 2018

@dotnet/roslyn-compiler for review. Thanks

async System.Collections.Generic.IAsyncEnumerator<int> M() { await Task.CompletedTask; yield return 3; }
}
";
private void VerifyMissingMember(WellKnownMember member, params DiagnosticDescription[] expected)
Copy link
Member

@cston cston Nov 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private [](start = 8, length = 7)

static #Resolved

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)
Copy link
Member

@cston cston Nov 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private [](start = 8, length = 7)

static #Resolved

VerifyMissingType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T,
VerifyMissingMember(_enumerator, WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator);

VerifyMissingType(_enumerable, WellKnownType.System_Collections_Generic_IAsyncEnumerable_T,
// (5,60): error CS1983: The return type of an async method must be void, Task, Task<T>, a task-like type, or IAsyncEnumerable<T>
Copy link
Member

@cston cston Nov 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or IAsyncEnumerable [](start = 123, length = 22)

Should the message include IAsyncEnumerator<T> as well? #Resolved

comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "Value:0 1 2 Value:3 4 Value:5 Done");

var comp2 = CreateCompilationWithTasksExtensions("", references: new[] { comp.EmitToImageReference() });
Copy link
Member

@cston cston Nov 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateCompilationWithTasksExtensions [](start = 24, length = 36)

Are we creating a second compilation to check PE symbols? If so, consider using CompileAndVerify(..., symbolValidator=...) instead, for consistency with other tests. #Resolved

@@ -1008,6 +1302,37 @@ await using (var enumerator2 = enumerable.GetAsyncEnumerator())
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");

var comp2 = CreateCompilationWithTasksExtensions("", references: new[] { comp.EmitToImageReference() });
Copy link
Member

@cston cston Nov 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateCompilationWithTasksExtensions("", [](start = 24, length = 40)

Consider adding symbolValidator=... to the CompileAndVerify call above instead. #Resolved

@@ -31,8 +31,12 @@ public AsyncStateMachine(VariableSlotAllocator variableAllocatorOpt, TypeCompila
var elementType = TypeMap.SubstituteType(asyncMethod.IteratorElementType).TypeSymbol;
this.IteratorElementType = elementType;

// IAsyncEnumerable<TResult>
interfaces.Add(compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T).Construct(elementType));
bool isEnumerable = asyncMethod.IsIAsyncEnumerableReturningAsync(asyncMethod.DeclaringCompilation);
Copy link
Member

@cston cston Nov 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asyncMethod.DeclaringCompilation [](start = 81, length = 32)

compilation #Resolved

@jcouv
Copy link
Member Author

jcouv commented Nov 27, 2018

@dotnet/roslyn-compiler for second review. Thanks

@@ -9,6 +9,7 @@

namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
using System.Linq;
Copy link
Member

@agocke agocke Nov 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe our style guidelines specify that all usings must go outside the namespace. #Resolved

VerifyMissingMember(_enumerator, WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator);

VerifyMissingType(_enumerable, WellKnownType.System_Collections_Generic_IAsyncEnumerable_T,
// (5,60): error CS1983: The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>
Copy link
Member

@agocke agocke Nov 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this testing what happens when you have something like

using System.Threading.Tasks;
class C
{
    async System.Collections.Generic.IAsyncEnumerable<int> M() { await Task.CompletedTask; yield return 3; }
}

but IAsyncEnumerable<int> is missing? If so, is this error message useful? Should we just skip it if the return type is an error type? #Resolved

Copy link
Member Author

@jcouv jcouv Nov 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's what this tests.
Unfortunately, the MakeTypeMissing infrastructure doesn't fully simulate a type being missing. It only makes the type disappear from GetWellKnownType.

I'll adjust the test to show what users actually would see. #Resolved

Copy link
Member

@agocke agocke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM aside from minor cascading error question

@jcouv jcouv merged commit 259cae0 into dotnet:master Nov 28, 2018
@jcouv jcouv deleted the async-enumerator branch November 28, 2018 02:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants