Skip to content

Implement async using statement#23961

Merged
jcouv merged 13 commits intodotnet:features/async-streamsfrom
jcouv:async-streams2
Jan 23, 2018
Merged

Implement async using statement#23961
jcouv merged 13 commits intodotnet:features/async-streamsfrom
jcouv:async-streams2

Conversation

@jcouv
Copy link
Copy Markdown
Member

@jcouv jcouv commented Dec 29, 2017

The approach here is to re-use all the binding and lowering logic for using, but extend it to support using await. The BoundUsingStatement gets a new field to hold an optional BoundAwaitExpression which will be used during lowering to do await <expr>.DisposeAsync() instead of <expr>Dispose().
All the elements of the bound tree that relate to the IDisposable interface become overloaded to deal with IDisposableAsync in the async scenario.
I've done a little bit of IDE testing, which prompted me to fix some control flow analysis.

@jcouv jcouv added this to the 16.0 milestone Dec 29, 2017
@jcouv jcouv self-assigned this Dec 29, 2017
@jcouv jcouv changed the title Implement async using statement binding and lowering Implement async using statement Dec 29, 2017
@jcouv jcouv added the Feature - Async Streams Async Streams label Dec 29, 2017
@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Jan 5, 2018

@dotnet/roslyn-compiler for review. Thanks #Resolved

1 similar comment
@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Jan 8, 2018

@dotnet/roslyn-compiler for review. Thanks #Resolved

BoundMultipleLocalDeclarations declarationsOpt = null;
BoundExpression expressionOpt = null;
BoundAwaitExpression boundAwaitOpt = null;
if (!hasErrors)
Copy link
Copy Markdown
Contributor

@cston cston Jan 9, 2018

Choose a reason for hiding this comment

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

Is it necessary to skip binding the expression if IAsyncDisposable has use-site errors? #Resolved

{
var syntaxForAwait = (SyntaxNode)expressionSyntax ?? declarationSyntax;
BoundExpression placeholder = new BoundAwaitableValuePlaceholder(syntaxForAwait, taskType);
boundAwaitOpt = BindAwait(placeholder, syntaxForAwait, diagnostics).MakeCompilerGenerated();
Copy link
Copy Markdown
Contributor

@cston cston Jan 9, 2018

Choose a reason for hiding this comment

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

We're marking the BoundAwaitExpression as compiler generated here, but not in existing await scenarios. Why the difference? #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We're marking the BoundAwaitExpression as compiler generated here, but not in existing await scenarios. Why the difference?

I guess because there is no corresponding AwaitExpressionSyntax.


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

Copy link
Copy Markdown
Member

@VSadov VSadov Jan 10, 2018

Choose a reason for hiding this comment

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

Because in a case of Using, the await expression is not isomorphic to any expression in the syntax.
We are using a bound node just as a storage for method/property symbols found in binding. The node has no other structure or nesting (like in deconstruct).
I think AwaitableInfo, which is not a bound node, would make things simpler. #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The main reason why I marked it as compiler-generated is that AsyncMethodToStateMachineRewriter.VisitAwaitExpression expects that a BoundAwaitExpression is attached to some await syntax.
For async-using, we have no await syntax, so I modified the assertion as shown below.

Also, I think it makes sense into terms of bound tree having the same shape as the syntax tree.

        private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace)
        {
            ...
            Debug.Assert(node.Syntax.IsKind(SyntaxKind.AwaitExpression) || node.WasCompilerGenerated);

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


<!--
This node is used to represent an awaitable expression of a certain type, when binding an using-await statement.
It will be replaced by an actual bound node during lowering.
Copy link
Copy Markdown
Contributor

@cston cston Jan 9, 2018

Choose a reason for hiding this comment

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

Minor point: an async using or a using-await. #Resolved

@@ -311,7 +312,7 @@ protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByS

/// <summary>
/// A pending branch. There are created for a return, break, continue, goto statement,
Copy link
Copy Markdown
Contributor

@cston cston Jan 9, 2018

Choose a reason for hiding this comment

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

These are ...? #Resolved

/// <summary>
/// A pending branch. There are created for a return, break, continue, goto statement,
/// yield return, yield break, await expression, and if PreciseAbstractFlowPass.trackExceptions
/// yield return, yield break, await expression, foreach/using await, and if PreciseAbstractFlowPass.trackExceptions
Copy link
Copy Markdown
Contributor

@cston cston Jan 9, 2018

Choose a reason for hiding this comment

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

Are there pending branches for foreach? #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not yet, but that will be coming shortly with foreach-await. I updated the comment pro-actively, as I feared I'd forget to update it in the foreach-await PR. I can replace with PROTOTYPE if you prefer.


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

using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
Copy link
Copy Markdown
Contributor

@cston cston Jan 9, 2018

Choose a reason for hiding this comment

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

Are there tests for using and using await for types that implement IDisposable and IAsyncDisposable? #Resolved

Copy link
Copy Markdown
Contributor

@cston cston Jan 10, 2018

Choose a reason for hiding this comment

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

Consider testing using await (null) { }, using await (MethodName) { }. #Resolved

IDS_FeatureAttributesOnBackingFields = MessageBase + 12731,

IDS_FeatureAsyncStreams = MessageBase + 12777, // PROTOTYPE(async-streams)
IDS_FeatureAsyncStreams = MessageBase + 12777, // PROTOTYPE(async-streams) Compat IDs
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 10, 2018

Choose a reason for hiding this comment

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

Compat [](start = 83, length = 6)

Did you mean "Compact"? #Closed

if (hasAwait)
{
iDisposable = this.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable);
hasErrors |= ReportUseSiteDiagnostics(iDisposable, diagnostics, _syntax.AwaitKeyword.GetLocation());
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 10, 2018

Choose a reason for hiding this comment

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

hasErrors |= ReportUseSiteDiagnostics(iDisposable, diagnostics, _syntax.AwaitKeyword.GetLocation()); [](start = 16, length = 100)

Is there a reason to differ from regular IDisposable in regard to reporting use-site diagnostics? #Closed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The comment made me think it was unnecessary. But it looks like there was a bug for IDisposable case.


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

BoundMultipleLocalDeclarations declarationsOpt = null;
BoundExpression expressionOpt = null;
BoundAwaitExpression boundAwaitOpt = null;
if (!hasErrors)
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 10, 2018

Choose a reason for hiding this comment

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

if (!hasErrors) [](start = 12, length = 15)

We should keep binding expression/declarations, if not for error reporting, then for SemanticModel/IOperation benefits. #Closed

hasErrors = true;
if (!hasErrors && hasAwait)
{
TypeSymbol taskType = this.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 10, 2018

Choose a reason for hiding this comment

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

GetWellKnownType [](start = 59, length = 16)

I think Binder has a helper that gets the type and reports use-site diagnostics. #Closed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

They don't accept a SyntaxToken or a Location. I don't want to multiple overloads, so I just added one to ReportUseSiteDiagnostics.


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

if (!hasErrors)
{
var syntaxForAwait = (SyntaxNode)expressionSyntax ?? declarationSyntax;
BoundExpression placeholder = new BoundAwaitableValuePlaceholder(syntaxForAwait, taskType);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 10, 2018

Choose a reason for hiding this comment

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

BoundAwaitableValuePlaceholder [](start = 58, length = 30)

MakeCompilerGenerated()? #Closed

<Field Name="IDisposableConversion" Type="Conversion" />
<Field Name="Body" Type="BoundStatement"/>
<!-- A bound await, but with a placeholder instead of an awaited expression -->
<Field Name="AwaitOpt" Type="BoundAwaitExpression" Null="allow"/>
Copy link
Copy Markdown
Member

@VSadov VSadov Jan 10, 2018

Choose a reason for hiding this comment

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

I wonder if it is better to factor out {getAwaiter, isCompleted, getResult} as an AwaitableInfo and use it in both BoundAwait and in BoundUsing?
GetAwaitableExpressionInfo is basically returning such info (although via bunch of out params).

Such approach might scale better at representing implicit awaitability than carrying around await nodes on a side. #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm taking a look at this and will get back to you.


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

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Pushed a commit that refactors BoundAwaitExpression and extracts an AwaitableInfo.


In reply to: 161033778 [](ancestors = 161033778,160565220)

/// <summary>
/// Lower "using (ResourceType resource = expression) statement" to a try-finally block.
/// Lower "using (ResourceType resource = expression) statement" and
/// "using await (ResourceType resuorce = expression) statement" to a try-finally block.
Copy link
Copy Markdown
Contributor

@cston cston Jan 10, 2018

Choose a reason for hiding this comment

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

Typo: resource #Resolved

Copy link
Copy Markdown
Contributor

@cston cston Jan 10, 2018

Choose a reason for hiding this comment

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

Perhaps short the comment to Lower "using [await] (ResourceType resource = expression) statement" to a try-finally block. Similar comment for previous method. #Resolved

// local.Dispose()
disposeCall = BoundCall.Synthesized(syntax, disposedExpression, disposeMethodSymbol);
}
else if (awaitOpt != null && TryGetWellKnownTypeMember(syntax, WellKnownMember.System_IAsyncDisposable__DisposeAsync, out MethodSymbol disposeAsyncMethodSymbol))
Copy link
Copy Markdown
Contributor

@cston cston Jan 10, 2018

Choose a reason for hiding this comment

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

If we're reporting use-site diagnostics here, could we skip reporting the use-site diagnostic for IAsyncDisposable in binding? #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The checks here don't handle missing IDisposable, only missing IDisposable.Dipose().
I agree with you that it's weird to split the use-site diagnostics between here and initial binding. I'll look into this more.


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

Copy link
Copy Markdown
Contributor

@cston cston Jan 11, 2018

Choose a reason for hiding this comment

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

Doesn't TryGetWellKnownTypeMember report a diagnostic if the type is missing? #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If I remove the reporting of use-site diagnostics in binding (for missing IDisposable), then execution does not reach lowering, even when I ask for emit diagnostics.
I think that's because there are errors reported from binding anyways. Namely:

                // (6,16): error CS1674: 'C': type used in a using statement must be implicitly convertible to 'System.IDisposable'
                //         using (new C())
                Diagnostic(ErrorCode.ERR_NoConvToIDisp, "new C()").WithArguments("C").WithLocation(6, 16),
                // (9,16): error CS1674: 'C': type used in a using statement must be implicitly convertible to 'System.IDisposable'
                //         using (var x = new C())
                Diagnostic(ErrorCode.ERR_NoConvToIDisp, "var x = new C()").WithArguments("C").WithLocation(9, 16)

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe all error reporting for missing members and such should be done in binding. IIRC, the local rewriter can assume that these exist here. Example in: https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StackAlloc.cs when getting the span type


In reply to: 161024454 [](ancestors = 161024454,160863521)

Copy link
Copy Markdown
Member Author

@jcouv jcouv Jan 13, 2018

Choose a reason for hiding this comment

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

Initial binding doesn't currently check for existing of IDisposable.Dispose().
There are other examples of well-known types and members which are only checked during rewriting, such as AsyncRewriter.VerifyPresenceOfRequiredAPIs().

I don't see anything wrong with that, especially since getting in a situation where IDisposable.Dispose() is missing, but IDisposable exists wouldn't seem common. The same applies to async counterpart APIs. #Resolved

{
System.Console.Write(""body "");
return;
}
Copy link
Copy Markdown
Contributor

@cston cston Jan 10, 2018

Choose a reason for hiding this comment

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

Consider testing S? s = null; using await (s) { ... }. #Resolved

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks for the test suggestions (good ideas). Added them


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

@jcouv jcouv requested a review from a team as a code owner January 11, 2018 00:27
@jcouv jcouv requested a review from OmarTawfik January 11, 2018 05:22
@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Jan 19, 2018

@AlekseyTs @cston Any other feedback? Thanks #Resolved


internal AwaitableInfo Update(MethodSymbol newGetAwaiter, PropertySymbol newIsCompleted, MethodSymbol newGetResult)
{
if (ReferenceEquals(GetAwaiter, newGetAwaiter) && ReferenceEquals(IsCompleted, newIsCompleted) && ReferenceEquals(GetResult, newGetResult))
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

ReferenceEquals [](start = 16, length = 15)

ReferenceEquals is fine, but we usually use cast to object and regular == operator to compare references #Closed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Leaving as-is if that's ok.


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

return null;
}

public abstract bool AsyncUsingAddsPendingBranch { get; }
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

AsyncUsingAddsPendingBranch [](start = 29, length = 27)

I am not sure why do we need this for using, but regular await expressions don't use similar mechanism. #Closed

Copy link
Copy Markdown
Member Author

@jcouv jcouv Jan 19, 2018

Choose a reason for hiding this comment

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

The reason is that ControlFlowPass doesn't dig into expressions, so it doesn't add pending branches for awaits (implemented in base class).
I want to get the same behavior for async-using, but without cloning the implementation of VisitUsingStatement.

// from ControlFlowPass
        public override BoundNode Visit(BoundNode node)
        {
            // there is no need to scan the contents of an expression, as expressions
            // do not contribute to reachability analysis (except for constants, which
            // are handled by the caller).
            if (!(node is BoundExpression))
            {
                return base.Visit(node);
            }

            return null;
        }
// from PreciseAbstractFlowPass<LocalState> 
        public override BoundNode VisitAwaitExpression(BoundAwaitExpression node)
        {
            VisitRvalue(node.Expression);
            _pendingBranches.Add(new PendingBranch(node, this.State));
            if (_trackExceptions) NotePossibleException(node);
            return null;
        }
``` #Resolved

}

#if DEBUG
LocalRewritingValidator.Validate(loweredStatement);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

LocalRewritingValidator.Validate(loweredStatement); [](start = 16, length = 51)

I am concerned that this introduces a new source of stack overflow exception and only for debug. Some test will be testing different code path debug vs. release. Perhaps we should catch and swallow the internal stack overflow exception thrown by this validator. #Closed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added try/catch inside that method


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

comp.VerifyDiagnostics(
// (6,22): 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'.
// using await (var x = new C())
Diagnostic(ErrorCode.ERR_BadAwaitWithoutVoidAsyncMethod, "var x = new C()").WithLocation(6, 22)
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

var x = new C() [](start = 74, length = 15)

I think it would be better if the location for the error was the await keyword. It would point precisely to the problem and the part between parenthesis could be quite long, event could span multiple lines. #Closed

";
var comp = CreateCompilationWithMscorlib46(source + s_interfaces, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "C body DisposeAsync");
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

"C body DisposeAsync" [](start = 51, length = 21)

It would be good to test that the task is actually awaited, which I believe this test doesn't confirm. #Closed

comp.VerifyDiagnostics(
// (8,26): error CS4004: Cannot await in an unsafe context
// using await (var x = new C())
Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "var x = new C()").WithLocation(8, 26)
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

var x = new C() [](start = 64, length = 15)

Same comment for location #Closed

comp.VerifyDiagnostics(
// (8,26): error CS1996: Cannot await in the body of a lock statement
// using await (var x = new C())
Diagnostic(ErrorCode.ERR_BadAwaitInLock, "var x = new C()").WithLocation(8, 26)
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

var x = new C() [](start = 58, length = 15)

Same comment for location #Closed

{
System.Action a = () =>
{
System.Action b = async () => { await local(); }; // this await doesn't count towards Main method
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

await local() [](start = 44, length = 13)

Shouldn't we be testing using await here? #Closed


async System.Threading.Tasks.Task local()
{
await local(); // neither does this one
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

await local(); [](start = 12, length = 14)

Shouldn't we be testing using await here? #Closed

{
System.Action lambda1 = async () =>
{
System.Action b = async () => { await local(); }; // this await doesn't count towards lambda
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

await local() [](start = 44, length = 13)

Shouldn't we be testing using await here? #Closed

System.Action b = async () => { await local(); }; // this await doesn't count towards lambda
};

System.Action lambda2 = async () => await local2(); // this await counts towards lambda
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

await local2() [](start = 44, length = 14)

Shouldn't we be testing using await here? #Closed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We can't here. The purpose for this lambda is to have an expression body, but async using is a statement.


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


async System.Threading.Tasks.Task innerLocal()
{
await local(); // this await doesn't count towards local function
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

await local() [](start = 16, length = 13)

Shouldn't we be testing using await here? #Closed

finally
{
System.Console.Write(""before "");
await System.Threading.Tasks.Task.CompletedTask;
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

await System.Threading.Tasks.Task.CompletedTask [](start = 12, length = 47)

Does completed task actually suspend execution? #Closed

comp.VerifyEmitDiagnostics(
// (13,9): error CS0656: Missing compiler required member 'System.IAsyncDisposable.DisposeAsync'
// using await (new C()) { }
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "using await (new C()) { }").WithArguments("System.IAsyncDisposable", "DisposeAsync").WithLocation(13, 9),
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

using await (new C()) { } [](start = 67, length = 25)

It feels like we should narrow location of this error. #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No response for this comment.


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

}

[Fact]
public void TestWithMultipleResources()
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 19, 2018

Choose a reason for hiding this comment

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

TestWithMultipleResources [](start = 20, length = 25)

Is this a dupe of TestWithMultipleDeclarations? #Closed

Copy link
Copy Markdown
Member Author

@jcouv jcouv Jan 20, 2018

Choose a reason for hiding this comment

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

Close enough. I'll remove TestWithMultipleDeclarations which doesn't check order of evaluation of resources.
Thanks


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

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Jan 19, 2018

Done with review pass (iteration 11) #Closed

AwaitableInfo info = node.AwaitableInfo;
return node.Update(
expression,
node.AwaitableInfo.Update(VisitMethodSymbol(info.GetAwaiter), VisitPropertySymbol(info.IsCompleted), VisitMethodSymbol(info.GetResult)),
Copy link
Copy Markdown
Contributor

@cston cston Jan 20, 2018

Choose a reason for hiding this comment

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

info.Update(...) #Resolved

// (11,9): error CS0165: Use of unassigned local variable 'z'
// z++;
Diagnostic(ErrorCode.ERR_UseDefViolation, "z").WithArguments("z").WithLocation(11, 9)
);
Copy link
Copy Markdown
Contributor

@cston cston Jan 20, 2018

Choose a reason for hiding this comment

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

Do we report an error for await L1(); ...; y++;?
#Resolved

Copy link
Copy Markdown
Member Author

@jcouv jcouv Jan 20, 2018

Choose a reason for hiding this comment

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

See #24267 for that scenario (reports same errors) and another related one


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

";
var comp = CreateCompilationWithMscorlib46(source + s_interfaces, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
var verifier = CompileAndVerify(comp, expectedOutput: "ctor1 ctor2 body dispose2 dispose1 caught");
Copy link
Copy Markdown
Contributor

@cston cston Jan 20, 2018

Choose a reason for hiding this comment

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

Are we disposing of s1 if an exception is thrown in new S(2)? #Resolved

public Task DisposeAsync()
{
System.Console.Write(""DisposeAsync "");
return Task.Delay(10);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Jan 20, 2018

Choose a reason for hiding this comment

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

return Task.Delay(10); [](start = 8, length = 22)

How do we know that this task is awaited after this method is called? #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here and in all other similar tests we should await for something inside DisposeAsync and then print something after that. This will confirm that we aren't just calling the method, but also awaiting the task that we are getting from it.


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

Copy link
Copy Markdown
Member Author

@jcouv jcouv Jan 20, 2018

Choose a reason for hiding this comment

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

Thanks for the clarification. #Resolved

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Jan 20, 2018

Done with review pass (iteration 12) #Closed

@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Jan 22, 2018

@AlekseyTs Could you take another look when you get the chance? Thanks

Btw, I added a work item to umbrella issue (#24037) to deal with IOperation too.

Copy link
Copy Markdown
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 13)

@jcouv
Copy link
Copy Markdown
Member Author

jcouv commented Jan 23, 2018

Awesome. Thanks!

@jcouv jcouv merged commit 8bc67d6 into dotnet:features/async-streams Jan 23, 2018
@jcouv jcouv deleted the async-streams2 branch January 23, 2018 20:30
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.

8 participants