Skip to content

Clarify binding rules for await foreach#5898

Merged
jcouv merged 7 commits intomainfrom
await-foreach
Mar 11, 2022
Merged

Clarify binding rules for await foreach#5898
jcouv merged 7 commits intomainfrom
await-foreach

Conversation

@jcouv
Copy link
Copy Markdown
Member

@jcouv jcouv commented Mar 8, 2022

Relates to dotnet/roslyn#60022

@jcouv jcouv self-assigned this Mar 8, 2022
@jcouv jcouv marked this pull request as ready for review March 8, 2022 17:56
@jcouv jcouv requested a review from a team as a code owner March 8, 2022 17:56
@jcouv jcouv requested a review from cston March 8, 2022 17:57
- If the type `X` of *expression* is `dynamic` or an array type, then an error is produced and no further steps are taken.
- Otherwise, determine whether the type `X` has an appropriate `GetAsyncEnumerator` method:
- Perform member lookup on the type `X` with identifier `GetAsyncEnumerator` and no type arguments. If the member lookup does not produce a match, or it produces an ambiguity, or produces a match that is not a method group, check for an enumerable interface as described below.
- Perform overload resolution using the resulting method group and an empty argument list. If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, check for an enumerable interface as described below.
Copy link
Copy Markdown
Contributor

@cston cston Mar 9, 2022

Choose a reason for hiding this comment

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

Should a warning be reported if the match is not a method group or is ambiguous or is non-public, similar to the recommendations in foreach-statement? #ByDesign

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.

LDM said that we don't usually spec warnings, so that's why I didn't include this.

If the iterated type doesn't expose the right pattern, the interfaces will be used.
The body of the `finally` block is constructed according to the following steps:
- If the type `E` has an appropriate `DisposeAsync` method:
- Perform member lookup on the type `E` with identifier `DisposeAsync` and no type arguments. If the member lookup does not produce a match, or it produces an ambiguity, or produces a match that is not a method group, check for the disposal interface as described below.
Copy link
Copy Markdown
Contributor

@cston cston Mar 9, 2022

Choose a reason for hiding this comment

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

disposal interface

Extra space between words. And should this be "disposable interface" (here and below) instead? #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 trying to go for parallel with "enumeration interfaces".
"disposal" and "enumeration" are nouns, but "disposable" and "enumerable" are adjectives?

The body of the `finally` block is constructed according to the following steps:
- If the type `E` has an appropriate `DisposeAsync` method:
- Perform member lookup on the type `E` with identifier `DisposeAsync` and no type arguments. If the member lookup does not produce a match, or it produces an ambiguity, or produces a match that is not a method group, check for the disposal interface as described below.
- Perform overload resolution using the resulting method group and an empty argument list. If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, check for the disposal interface as described below.
Copy link
Copy Markdown
Contributor

@cston cston Mar 9, 2022

Choose a reason for hiding this comment

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

results in an ambiguity, or results in a single best method but that method is either static or not public

Report a warning? #ByDesign

}
```
except that if `E` is a value type, or a type parameter instantiated to a value type, then the conversion of `e` to `System.IAsyncDisposable` shall not cause boxing to occur.
- Otherwise, the `finally` clause is expanded to an empty block:
Copy link
Copy Markdown
Contributor

@cston cston Mar 9, 2022

Choose a reason for hiding this comment

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

If E is not a sealed type, should we generate the following, matching foreach-statement?

        System.IAsyncDisposable d = e as System.IAsyncDisposable;
        if (d != null) await d.DisposeAsync();
``` #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 don't think so. I don't remember why we'd decided that, but the code has:
// For async foreach, we don't do the runtime check

             if ((!enumeratorType.IsSealed && !isAsync) ||
                    this.Conversions.ClassifyImplicitConversionFromType(enumeratorType,
                        isAsync ? this.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable) : this.Compilation.GetSpecialType(SpecialType.System_IDisposable),
                        ref useSiteInfo).IsImplicit)
                {
                    builder.NeedsDisposal = true;
                }

@jcouv jcouv requested a review from cston March 10, 2022 17:43
@jcouv jcouv merged commit 82122c1 into main Mar 11, 2022
@jcouv jcouv deleted the await-foreach branch March 11, 2022 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants