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

Support EnumeratorCancellationAttribute in local functions #40959

Conversation

RikkiGibson
Copy link
Contributor

Related to #38801

@RikkiGibson RikkiGibson added this to the Compiler.Next milestone Jan 14, 2020
@RikkiGibson RikkiGibson requested a review from a team as a code owner January 14, 2020 20:13
@@ -227,35 +230,39 @@ public override ImmutableArray<CustomModifier> RefCustomModifiers

private sealed class SynthesizedComplexParameterSymbol : SynthesizedParameterSymbolBase
{
private readonly ImmutableArray<CustomModifier> _refCustomModifiers;
private readonly ImmutableArray<CSharpAttributeData> _attributes;
private readonly ParameterSymbol _baseParameter;
Copy link
Member

@jcouv jcouv Jan 14, 2020

Choose a reason for hiding this comment

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

nit: consider enabling nullability analysis in this file, or asserting that baseParameter is not null in constructor. #Resolved

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

Done with review pass (iteration 1). Thanks!

@jcouv jcouv self-assigned this Jan 14, 2020
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jan 15, 2020

@RikkiGibson Do you know the reason for CI failures? #Closed

@RikkiGibson
Copy link
Contributor Author

RikkiGibson commented Jan 15, 2020

EE was using the old signatures of the internal SynthesizedParameterSymbol.Create methods. Working on how to rationalize the design/hierarchy of the synthesized parameter symbols in light of this. #Resolved

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jan 15, 2020

Done with review pass (iteration 1). #Closed

@@ -833,6 +834,34 @@ internal sealed override void PostDecodeWellKnownAttributes(ImmutableArray<CShar
base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart, decodedData);
}

// Called after all method attributes, return attributes, and parameter attributes have been initialized on this method.
protected void PostDecodeAllMethodAttributes(DiagnosticBag diagnostics)
Copy link
Member

@jcouv jcouv Jan 17, 2020

Choose a reason for hiding this comment

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

PostDecodeAllMethodAttributes [](start = 23, length = 29)

nit: could be given a more specific name at the moment, since it only checks EnumeratorCancellation attribute #Resolved

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM Thanks (iteration 4)

@RikkiGibson RikkiGibson reopened this Jan 17, 2020
if (enumeratorCancellationCount == 0 &&
ParameterTypesWithAnnotations.Any(p => p.Type.Equals(cancellationTokenType)))
{
// Warn for CancellationToken parameters in async-iterators with no parameter decorated with [EnumeratorCancellation]
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 17, 2020

Choose a reason for hiding this comment

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

async-iterators [](start = 68, length = 15)

@jcouv I assume you added this code originally, where do we make sure this is an iterator?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems like we just check that we have an IAsyncEnumerable return type. Would we want to warn when the method uses that return type and [EnumeratorCancellation] on a parameter, but isn't actually an iterator? @jcouv


In reply to: 368069236 [](ancestors = 368069236)

Copy link
Member

Choose a reason for hiding this comment

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

@AlekseyTs From our discussion, there are three factors to make an async-iterator: (1) async, (2) the return type, and (3) a yield in the body.
But we enforce that you cannot have (1) and (2) without having (3). We produce an error in that case (see test below). So in other places in the code, we can just check for (1) and (2).
I have a vague recollection that there was some technical benefit to this approach, but I'm not longer sure.

@RikkiGibson Would you mind adding a comment in the code that you're moving to capture this? I'd add it in master, but that would only create a conflict for when you'll merge...

        [Fact]
        [WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")]
        public void AsyncIterator_WithThrowOnly()
        {
            string source = @"
class C
{
    public static async System.Collections.Generic.IAsyncEnumerable<int> M()
    {
        throw new System.NotImplementedException();
    }
}";
            var comp = CreateCompilationWithAsyncIterator(source);
            comp.VerifyDiagnostics(
                // (4,74): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
                //     public static async System.Collections.Generic.IAsyncEnumerable<int> M()
                Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74)
                );
            comp.VerifyEmitDiagnostics(
                // (4,74): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
                //     public static async System.Collections.Generic.IAsyncEnumerable<int> M()
                Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74),
                // (4,74): error CS8420: The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement.
                //     public static async System.Collections.Generic.IAsyncEnumerable<int> M()
                Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithArguments("System.Collections.Generic.IAsyncEnumerable<int>").WithLocation(4, 74)
                );

            var m = comp.SourceModule.GlobalNamespace.GetMember<MethodSymbol>("C.M");
            Assert.False(m.IsIterator);
            Assert.True(m.IsAsync);
        }

}
}

PostDecodeAllMethodAttributes(diagnostics);
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 17, 2020

Choose a reason for hiding this comment

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

PostDecodeAllMethodAttributes [](start = 12, length = 29)

It feels like "AfterReturnTypeBoundAndParametersAreCreated" is more precise name. However, I don't think we need a dedicated method for this operation. In fact, I think that this is not the right place for this kind of checks. We haven't finalized the signature of the method yet and are likely to mutate it below. It feels like this check belongs in LazyAsyncMethodChecks, which is called in the right time. Also, it looks like for local functions the checks performed by LazyAsyncMethodChecks are duplicated. I think it would be good to consolidate that and avoid code duplication. #Closed

@@ -125,6 +125,8 @@ internal void GetDeclarationDiagnostics(DiagnosticBag addTo)
GetAttributes();
GetReturnTypeAttributes();

PostDecodeAllMethodAttributes(_declarationDiagnostics);
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 17, 2020

Choose a reason for hiding this comment

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

PostDecodeAllMethodAttributes(_declarationDiagnostics); [](start = 12, length = 55)

Should consolidate with other async-related checks performed for local functions and probably share code with regular methods so that there was one, well defined place where we perform async-related checks. #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jan 17, 2020

Done with review pass (iteration 5) #Closed

@RikkiGibson
Copy link
Contributor Author

@AlekseyTs Please let me know if I have adequately addressed your comments.

{
foreach (var parameter in Parameters)
{
var loc = parameter.Locations.FirstOrDefault() ?? location;
Copy link
Contributor

Choose a reason for hiding this comment

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

parameter.Locations.FirstOrDefault() ?? location [](start = 26, length = 48)

Consider calculating this lazily, i.e. only when we have something to report.

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (iteration 7)

@RikkiGibson RikkiGibson merged commit 885617c into dotnet:features/local-function-attributes Jan 21, 2020
@RikkiGibson RikkiGibson deleted the lfa-synthesized-params branch January 21, 2020 22:04
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.

4 participants