Skip to content

Add followup async public apis#80455

Merged
333fred merged 7 commits intodotnet:mainfrom
333fred:await-apis
Oct 7, 2025
Merged

Add followup async public apis#80455
333fred merged 7 commits intodotnet:mainfrom
333fred:await-apis

Conversation

@333fred
Copy link
Member

@333fred 333fred commented Sep 24, 2025

Adds the followup APIs for await using and await foreach. Closes #80409.

Relates to test plan #75960

@333fred 333fred requested review from a team as code owners September 24, 2025 17:00
@dotnet-policy-service dotnet-policy-service bot added the Needs API Review Needs to be reviewed by the API review council label Sep 24, 2025
@333fred
Copy link
Member Author

333fred commented Sep 24, 2025

@dotnet/roslyn-compiler for review. Suggest reviewing commit-by-commit: the first commit consolidates await information into the same location as the rest of the enumeration info, the second actually adds the API.

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 (commit 3)

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 (commit 4)

@333fred
Copy link
Member Author

333fred commented Sep 29, 2025

@jcouv @dotnet/roslyn-compiler for review

@jcouv
Copy link
Member

jcouv commented Sep 29, 2025

I've already approved the latest commit. It looks like there's a merge conflict to resolve though

@333fred
Copy link
Member Author

333fred commented Sep 29, 2025

I've already approved the latest commit. It looks like there's a merge conflict to resolve though

Whoops, sorry. Apparently github didn't load that comment, I'd only seen the commit 3 comments.


public override AwaitExpressionInfo GetAwaitExpressionInfo(LocalDeclarationStatementSyntax node)
{
return GetEnclosingMemberModel(node).GetAwaitExpressionInfo(node);
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 2, 2025

Choose a reason for hiding this comment

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

return GetEnclosingMemberModel(node).GetAwaitExpressionInfo(node);

Is this code path covered by a test? #Closed


public override AwaitExpressionInfo GetAwaitExpressionInfo(UsingStatementSyntax node)
{
return GetEnclosingMemberModel(node).GetAwaitExpressionInfo(node);
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 2, 2025

Choose a reason for hiding this comment

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

return GetEnclosingMemberModel(node).GetAwaitExpressionInfo(node);

Is this code path covered by a test? #Closed

public override AwaitExpressionInfo GetAwaitExpressionInfo(LocalDeclarationStatementSyntax node)
{
MemberSemanticModel memberModel = GetMemberModel(node);
return memberModel == null ? default(AwaitExpressionInfo) : memberModel.GetAwaitExpressionInfo(node);
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 2, 2025

Choose a reason for hiding this comment

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

memberModel.GetAwaitExpressionInfo(node)

Is this code path covered by a test? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, the existing tests go through here.

public override AwaitExpressionInfo GetAwaitExpressionInfo(UsingStatementSyntax node)
{
MemberSemanticModel memberModel = GetMemberModel(node);
return memberModel == null ? default(AwaitExpressionInfo) : memberModel.GetAwaitExpressionInfo(node);
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 2, 2025

Choose a reason for hiding this comment

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

memberModel.GetAwaitExpressionInfo(node)

Is this code path covered by a test? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, the existing tests go through here.

return RewriteInlineArrayForEachStatementAsFor(node);
}
else if (node.AwaitOpt is null && CanRewriteForEachAsFor(node.Syntax, nodeExpressionType, out var indexerGet, out var lengthGetter))
else if (node.EnumeratorInfoOpt is { MoveNextAwaitableInfo: null } && CanRewriteForEachAsFor(node.Syntax, nodeExpressionType, out var indexerGet, out var lengthGetter))
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 2, 2025

Choose a reason for hiding this comment

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

node.EnumeratorInfoOpt is { MoveNextAwaitableInfo: null }

It looks like semantically equivalent replacement is node.EnumeratorInfoOpt?.MoveNextAwaitableInfo is null #Closed

SyntaxNode syntax = boundForEachStatement.Syntax;
bool isImplicit = boundForEachStatement.WasCompilerGenerated;
bool isAsynchronous = boundForEachStatement.AwaitOpt != null;
bool isAsynchronous = boundForEachStatement.EnumeratorInfoOpt is { IsAsync: true };
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 2, 2025

Choose a reason for hiding this comment

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

EnumeratorInfoOpt is { IsAsync: true }

The rewrite doesn't look semantically equivalent. #Closed


var expectedOperationTree = @"
IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }')
IForEachLoopOperation (LoopKind.ForEach, IsAsynchronous, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null) (Syntax: 'await forea ... }')
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 2, 2025

Choose a reason for hiding this comment

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

IsAsynchronous

This change looks unexpected #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

As discussed offline, this is because of the error scenario that causes EnumeratorInfoOpt to not be populated. I'll investigate a followup change after to base this flag on just whether the loop is await foreach.

{
if (semanticModel is CSharpSemanticModel csmodel)
{
return csmodel.GetAwaitExpressionInfo(awaitUsingDeclaration);
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 2, 2025

Choose a reason for hiding this comment

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

return csmodel.GetAwaitExpressionInfo(awaitUsingDeclaration);

Is this code path covered by a test? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, TestAwaitUsingDeclarationAwaitInfo

{
if (semanticModel is CSharpSemanticModel csmodel)
{
return csmodel.GetAwaitExpressionInfo(awaitUsingStatement);
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 2, 2025

Choose a reason for hiding this comment

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

return csmodel.GetAwaitExpressionInfo(awaitUsingStatement);

Is this code path covered by a test? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, TestAwaitUsingStatementAwaitInfo

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Oct 2, 2025

Done with review pass (commit 4) #Closed

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 (commit 5)

* upstream/main: (149 commits)
  Fix
  Simplify and add additional asserts
  Update proposal adjuster to acquire feature flags from VS (dotnet#80541)
  Remove First/Last
  Docs
  Docs
  Deprecate method and use workaround
  Remove support for ccreating EmbeddedStrings
  Remove methods
  Move more into extensions
  Move more into extensions
  Proper slice
  Proper slice
  Proper slice
  Move to slices
  Extract extensions
  Downstream
  Simplify the virtual char api
  Add telemetry indicating when file-based programs are used (dotnet#80538)
  Fix thread safety issue in BuildServerConnection.TryCreateServer environment variable handling (dotnet#80498)
  ...
@333fred 333fred enabled auto-merge (squash) October 3, 2025 23:58
* upstream/main:
  Extensions: Close some tracked follow-ups (dotnet#80527)
  Fix outdated 17.15 to 18.0 (dotnet#80570)
  Update dependencies from https://github.com/dotnet/dotnet build 285582 (dotnet#80551)
  Fix all-in-one tests
  [main] Update dependencies from dotnet/arcade (dotnet#80559)
  Improve error recovery when object initializer uses ':' instead of '=' (dotnet#80553)
  Support `field` keyword in EE. (dotnet#80515)
  Log a debug message for ContentModified exceptions. (dotnet#80549)
  Update dependencies from https://github.com/dotnet/arcade build 20251002.2 (dotnet#80550)
@333fred 333fred merged commit 1017e4a into dotnet:main Oct 7, 2025
27 of 28 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Oct 7, 2025
333fred added a commit to 333fred/roslyn that referenced this pull request Oct 7, 2025
* upstream/main: (252 commits)
  Move Watch EA to a separate assembly Microsoft.CodeAnalysis.ExternalAccess.HotReload (dotnet#80556)
  Enable long paths for Windows DartLab CI (dotnet#80581)
  Ensure that CS8659 is reported on partial properties (dotnet#80573)
  Fix a wrong relative link in a doc (dotnet#80567)
  [main] Source code updates from dotnet/dotnet (dotnet#80578)
  Update dependencies from https://github.com/dotnet/arcade build 20251006.2 (dotnet#80577)
  Update main configs after VS snap (dotnet#80523)
  Add followup async public apis (dotnet#80455)
  Reduce allocations in CSharpSyntaxNode.GetStructure (dotnet#80562)
  Extensions: Close some tracked follow-ups (dotnet#80527)
  Fix outdated 17.15 to 18.0 (dotnet#80570)
  Update dependencies from https://github.com/dotnet/dotnet build 285582 (dotnet#80551)
  Fix all-in-one tests
  [main] Update dependencies from dotnet/arcade (dotnet#80559)
  Improve error recovery when object initializer uses ':' instead of '=' (dotnet#80553)
  Support `field` keyword in EE. (dotnet#80515)
  Log a debug message for ContentModified exceptions. (dotnet#80549)
  Update dependencies from https://github.com/dotnet/arcade build 20251002.2 (dotnet#80550)
  Fix
  Simplify and add additional asserts
  ...
@333fred 333fred deleted the await-apis branch October 8, 2025 18:47
@davidwengier davidwengier modified the milestones: Next, 18.3 Jan 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Compilers Feature - Runtime Async Needs API Review Needs to be reviewed by the API review council

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add public API implementation for getting await info from await using and await foreach

4 participants