Skip to content

Commit

Permalink
Revert "Prefer pattern-based over interface-based disposal in await u…
Browse files Browse the repository at this point in the history
…sing (dotnet#72598)"

This reverts commit 2f72b4d.
  • Loading branch information
jcouv committed Apr 1, 2024
1 parent 3d8fee7 commit 05f9df5
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 1,166 deletions.
23 changes: 0 additions & 23 deletions docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,26 +83,3 @@ static class C
public static string M(I2 o, in int x) => "2";
}
```

## Prefer pattern-based over interface-based disposal in async `using`

***Introduced in Visual Studio 2022 version 17.10p3***

An async `using` prefers to bind using a pattern-based `DisposeAsync()` method rather than the interface-based `IAsyncDisposable.DisposeAsync()`.

For instance, the public `DisposeAsync()` method will be picked, rather than the private interface implementation:
```csharp
await using (var x = new C()) { }

public class C : System.IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync() => throw null; // no longer picked
public async ValueTask DisposeAsync()
{
Console.WriteLine("PICKED");
await Task.Yield();
}
}
```

39 changes: 19 additions & 20 deletions src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,31 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo

bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeInfo, out TypeSymbol? awaitableType)
{
TypeSymbol disposableInterface = getDisposableInterface(hasAwait);
Debug.Assert((object)disposableInterface != null);

CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = originalBinder.GetNewCompoundUseSiteInfo(diagnostics);
Conversion iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteInfo);
patternDisposeInfo = null;
awaitableType = null;

diagnostics.Add(syntax, useSiteInfo);

if (iDisposableConversion.IsImplicit)
{
if (hasAwait)
{
awaitableType = originalBinder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask);
}

return !ReportUseSite(disposableInterface, diagnostics, hasAwait ? awaitKeyword : usingKeyword);
}

Debug.Assert(!fromExpression || expressionOpt != null);
TypeSymbol? type = fromExpression ? expressionOpt!.Type : declarationTypeOpt;

// Pattern-based binding
// If this is a ref struct, or we're in a valid asynchronous using, try binding via pattern.
// We won't need to try and bind a second time if it fails, as async dispose can't be pattern based (ref structs are not allowed in async methods)
if (type is object && (type.IsRefLikeType || hasAwait))
{
BoundExpression? receiver = fromExpression
Expand Down Expand Up @@ -232,25 +250,6 @@ bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeI
}
}

// Interface binding
TypeSymbol disposableInterface = getDisposableInterface(hasAwait);
Debug.Assert((object)disposableInterface != null);

CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = originalBinder.GetNewCompoundUseSiteInfo(diagnostics);
Conversion iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteInfo);

diagnostics.Add(syntax, useSiteInfo);

if (iDisposableConversion.IsImplicit)
{
if (hasAwait)
{
awaitableType = originalBinder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask);
}

return !ReportUseSite(disposableInterface, diagnostics, hasAwait ? awaitKeyword : usingKeyword);
}

if (type is null || !type.IsErrorType())
{
// Retry with a different assumption about whether the `using` is async
Expand Down
Loading

0 comments on commit 05f9df5

Please sign in to comment.