diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md index 76b8741267d6e..9fb06cdb406ab 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md @@ -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(); - } -} -``` - diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index 775774fe0188c..d640fae2ca0dd 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -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 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 @@ -232,25 +250,6 @@ bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeI } } - // Interface binding - TypeSymbol disposableInterface = getDisposableInterface(hasAwait); - Debug.Assert((object)disposableInterface != null); - - CompoundUseSiteInfo 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 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs index c388178fb6771..546d49a7fea95 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs @@ -16,33 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen public class CodeGenAwaitUsingTests : CSharpTestBase { [Fact] - public void TestWithCSharp7_3_01() - { - string source = @" -class C : System.IAsyncDisposable -{ - async System.Threading.Tasks.Task M() - { - await using (var x = new C()) - { - } - } - System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() - { - throw null; - } -} -"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, parseOptions: TestOptions.Regular7_3); - comp.VerifyDiagnostics( - // (6,9): error CS8652: The feature 'asynchronous using' is not available in C# 7.3. Please use language version 8.0 or greater. - // await using (var x = new C()) - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "await").WithArguments("asynchronous using", "8.0").WithLocation(6, 9) - ); - } - - [Fact] - public void TestWithCSharp7_3_02() + public void TestWithCSharp7_3() { string source = @" class C : System.IAsyncDisposable @@ -63,10 +37,7 @@ public System.Threading.Tasks.ValueTask DisposeAsync() comp.VerifyDiagnostics( // (6,9): error CS8652: The feature 'asynchronous using' is not available in C# 7.3. Please use language version 8.0 or greater. // await using (var x = new C()) - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "await").WithArguments("asynchronous using", "8.0").WithLocation(6, 9), - // 0.cs(6,22): error CS8370: Feature 'pattern-based disposal' is not available in C# 7.3. Please use language version 8.0 or greater. - // await using (var x = new C()) - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "var x = new C()").WithArguments("pattern-based disposal", "8.0").WithLocation(6, 22) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "await").WithArguments("asynchronous using", "8.0").WithLocation(6, 9) ); } @@ -276,7 +247,7 @@ public System.Threading.Tasks.ValueTask DisposeAsync() } [Fact] - public void TestWithObsoleteDisposeAsync_01() + public void TestWithObsoleteDisposeAsync() { string source = @" class C : System.IAsyncDisposable @@ -288,7 +259,7 @@ public static async System.Threading.Tasks.Task Main() } } [System.Obsolete] - async System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() + public async System.Threading.Tasks.ValueTask DisposeAsync() { await System.Threading.Tasks.Task.Yield(); } @@ -299,33 +270,6 @@ async System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() // https://github.com/dotnet/roslyn/issues/30257 Confirm whether this behavior is ok (currently matching behavior of obsolete Dispose in non-async using) } - [Fact] - public void TestWithObsoleteDisposeAsync_02() - { - string source = @" -class C : System.IAsyncDisposable -{ - public static async System.Threading.Tasks.Task Main() - { - await using (var x = new C()) - { - } - } - [System.Obsolete] - public async System.Threading.Tasks.ValueTask DisposeAsync() - { - await System.Threading.Tasks.Task.Yield(); - } -} -"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); - comp.VerifyDiagnostics( - // 0.cs(6,22): warning CS0612: 'C.DisposeAsync()' is obsolete - // await using (var x = new C()) - Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "var x = new C()").WithArguments("C.DisposeAsync()").WithLocation(6, 22) - ); - } - [Fact] public void TestWithObsoleteDispose() { @@ -721,7 +665,7 @@ public void Dispose() } [Fact] - public void TestBadDisposeAsync_01() + public void TestBadDisposeAsync() { string source = @" namespace System @@ -738,7 +682,7 @@ async System.Threading.Tasks.Task M() await using (new C()) { } await using (var x = new C()) { return 1; } } - int System.IAsyncDisposable.DisposeAsync() + public int DisposeAsync() { throw null; } @@ -757,47 +701,12 @@ int System.IAsyncDisposable.DisposeAsync() } [Fact] - public void TestBadDisposeAsync_02() - { - string source = @" -namespace System -{ - public interface IAsyncDisposable - { - int DisposeAsync(); // bad return type - } -} -class C : System.IAsyncDisposable -{ - async System.Threading.Tasks.Task M() - { - await using (new C()) { } - await using (var x = new C()) { return 1; } - } - public int DisposeAsync() - { - throw null; - } -} -"; - var comp = CreateCompilationWithTasksExtensions(source); - comp.VerifyDiagnostics( - // (13,22): error CS1061: 'int' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) - // await using (new C()) { } - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "new C()").WithArguments("int", "GetAwaiter").WithLocation(13, 22), - // (14,22): error CS1061: 'int' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) - // await using (var x = new C()) { return 1; } - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "var x = new C()").WithArguments("int", "GetAwaiter").WithLocation(14, 22) - ); - } - - [Fact] - public void TestMissingTaskType_01() + public void TestMissingTaskType() { string lib_cs = @" public class Base : System.IAsyncDisposable { - System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() + public System.Threading.Tasks.ValueTask DisposeAsync() { System.Console.Write(""DisposeAsync""); return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask); @@ -828,39 +737,6 @@ public static async System.Threading.Tasks.Task Main() ); } - [Fact] - public void TestMissingTaskType_02() - { - string lib_cs = @" -public class Base : System.IAsyncDisposable -{ - public System.Threading.Tasks.ValueTask DisposeAsync() - { - System.Console.Write(""DisposeAsync""); - return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask); - } -} -"; - - string comp_cs = @" -public class C : Base -{ - public static async System.Threading.Tasks.Task Main() - { - await using (var x = new C()) - { - System.Console.Write(""body ""); - return 1; - } - } -} -"; - var libComp = CreateCompilationWithTasksExtensions(lib_cs + IAsyncDisposableDefinition); - var comp = CreateCompilationWithTasksExtensions(comp_cs, references: new[] { libComp.EmitToImageReference() }, options: TestOptions.DebugExe); - comp.MakeTypeMissing(WellKnownType.System_Threading_Tasks_ValueTask); - comp.VerifyEmitDiagnostics(); - } - [Fact] public void TestWithDeclaration() { @@ -1076,7 +952,7 @@ public System.Threading.Tasks.ValueTask DisposeAsync() } [Fact] - public void TestWithExpression_01() + public void TestWithExpression() { string source = @" class C : System.IAsyncDisposable @@ -1089,7 +965,7 @@ public static async System.Threading.Tasks.Task Main() return; } } - System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() + public System.Threading.Tasks.ValueTask DisposeAsync() { System.Console.Write(""DisposeAsync""); return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask); @@ -1250,14 +1126,84 @@ .locals init (int V_0, } [Fact] - public void TestWithExpression_02() + public void TestWithNullExpression() + { + string source = @" +class C +{ + public static async System.Threading.Tasks.Task Main() + { + await using (null) + { + System.Console.Write(""body""); + return; + } + } +} +"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "body"); + } + + [Fact] + public void TestWithMethodName() + { + string source = @" +class C +{ + public static async System.Threading.Tasks.Task Main() + { + await using (Main) + { + } + } +} +"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }); + comp.VerifyDiagnostics( + // (6,22): error CS8410: 'method group': type used in an async using statement must be implicitly convertible to 'System.IAsyncDisposable' + // await using (Main) + Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "Main").WithArguments("method group").WithLocation(6, 22) + ); + } + + [Fact] + public void TestWithDynamicExpression() { string source = @" class C : System.IAsyncDisposable { public static async System.Threading.Tasks.Task Main() { - await using (new C()) + dynamic d = new C(); + await using (d) + { + System.Console.Write(""body ""); + return; + } + } + public System.Threading.Tasks.ValueTask DisposeAsync() + { + System.Console.Write(""DisposeAsync""); + return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask); + } +} +"; + var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, references: new[] { CSharpRef }); + comp.VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + } + + [Fact] + public void TestWithStructExpression() + { + string source = @" +struct S : System.IAsyncDisposable +{ + public static async System.Threading.Tasks.Task Main() + { + await using (new S()) { System.Console.Write(""body ""); return; @@ -1273,475 +1219,55 @@ public System.Threading.Tasks.ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); - verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" + verifier.VerifyIL("S.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { - // Code size 306 (0x132) + // Code size 298 (0x12a) .maxstack 3 .locals init (int V_0, object V_1, System.Runtime.CompilerServices.ValueTaskAwaiter V_2, System.Threading.Tasks.ValueTask V_3, - C.
d__0 V_4, + S.
d__0 V_4, System.Exception V_5, int V_6) IL_0000: ldarg.0 - IL_0001: ldfld ""int C.
d__0.<>1__state"" + IL_0001: ldfld ""int S.
d__0.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_000c IL_000a: br.s IL_0011 - IL_000c: br IL_0099 + IL_000c: br IL_0098 IL_0011: nop IL_0012: ldarg.0 - IL_0013: newobj ""C..ctor()"" - IL_0018: stfld ""C C.
d__0.<>s__1"" - IL_001d: ldarg.0 - IL_001e: ldnull - IL_001f: stfld ""object C.
d__0.<>s__2"" - IL_0024: ldarg.0 - IL_0025: ldc.i4.0 - IL_0026: stfld ""int C.
d__0.<>s__3"" + IL_0013: ldflda ""S S.
d__0.<>s__1"" + IL_0018: initobj ""S"" + IL_001e: ldarg.0 + IL_001f: ldnull + IL_0020: stfld ""object S.
d__0.<>s__2"" + IL_0025: ldarg.0 + IL_0026: ldc.i4.0 + IL_0027: stfld ""int S.
d__0.<>s__3"" .try { - IL_002b: nop - IL_002c: ldstr ""body "" - IL_0031: call ""void System.Console.Write(string)"" - IL_0036: nop - IL_0037: br.s IL_0039 - IL_0039: ldarg.0 - IL_003a: ldc.i4.1 - IL_003b: stfld ""int C.
d__0.<>s__3"" - IL_0040: leave.s IL_004c + IL_002c: nop + IL_002d: ldstr ""body "" + IL_0032: call ""void System.Console.Write(string)"" + IL_0037: nop + IL_0038: br.s IL_003a + IL_003a: ldarg.0 + IL_003b: ldc.i4.1 + IL_003c: stfld ""int S.
d__0.<>s__3"" + IL_0041: leave.s IL_004d } catch object { - IL_0042: stloc.1 - IL_0043: ldarg.0 - IL_0044: ldloc.1 - IL_0045: stfld ""object C.
d__0.<>s__2"" - IL_004a: leave.s IL_004c - } - IL_004c: ldarg.0 - IL_004d: ldfld ""C C.
d__0.<>s__1"" - IL_0052: brfalse.s IL_00bd - IL_0054: ldarg.0 - IL_0055: ldfld ""C C.
d__0.<>s__1"" - IL_005a: callvirt ""System.Threading.Tasks.ValueTask C.DisposeAsync()"" - IL_005f: stloc.3 - IL_0060: ldloca.s V_3 - IL_0062: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()"" - IL_0067: stloc.2 - IL_0068: ldloca.s V_2 - IL_006a: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get"" - IL_006f: brtrue.s IL_00b5 - IL_0071: ldarg.0 - IL_0072: ldc.i4.0 - IL_0073: dup - IL_0074: stloc.0 - IL_0075: stfld ""int C.
d__0.<>1__state"" - IL_007a: ldarg.0 - IL_007b: ldloc.2 - IL_007c: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.
d__0.<>u__1"" - IL_0081: ldarg.0 - IL_0082: stloc.s V_4 - IL_0084: ldarg.0 - IL_0085: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder"" - IL_008a: ldloca.s V_2 - IL_008c: ldloca.s V_4 - IL_008e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.
d__0)"" - IL_0093: nop - IL_0094: leave IL_0131 - IL_0099: ldarg.0 - IL_009a: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.
d__0.<>u__1"" - IL_009f: stloc.2 - IL_00a0: ldarg.0 - IL_00a1: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.
d__0.<>u__1"" - IL_00a6: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter"" - IL_00ac: ldarg.0 - IL_00ad: ldc.i4.m1 - IL_00ae: dup - IL_00af: stloc.0 - IL_00b0: stfld ""int C.
d__0.<>1__state"" - IL_00b5: ldloca.s V_2 - IL_00b7: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()"" - IL_00bc: nop - IL_00bd: ldarg.0 - IL_00be: ldfld ""object C.
d__0.<>s__2"" - IL_00c3: stloc.1 - IL_00c4: ldloc.1 - IL_00c5: brfalse.s IL_00e2 - IL_00c7: ldloc.1 - IL_00c8: isinst ""System.Exception"" - IL_00cd: stloc.s V_5 - IL_00cf: ldloc.s V_5 - IL_00d1: brtrue.s IL_00d5 - IL_00d3: ldloc.1 - IL_00d4: throw - IL_00d5: ldloc.s V_5 - IL_00d7: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)"" - IL_00dc: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()"" - IL_00e1: nop - IL_00e2: ldarg.0 - IL_00e3: ldfld ""int C.
d__0.<>s__3"" - IL_00e8: stloc.s V_6 - IL_00ea: ldloc.s V_6 - IL_00ec: ldc.i4.1 - IL_00ed: beq.s IL_00f1 - IL_00ef: br.s IL_00f3 - IL_00f1: leave.s IL_011d - IL_00f3: ldarg.0 - IL_00f4: ldnull - IL_00f5: stfld ""object C.
d__0.<>s__2"" - IL_00fa: ldarg.0 - IL_00fb: ldnull - IL_00fc: stfld ""C C.
d__0.<>s__1"" - IL_0101: leave.s IL_011d - } - catch System.Exception - { - IL_0103: stloc.s V_5 - IL_0105: ldarg.0 - IL_0106: ldc.i4.s -2 - IL_0108: stfld ""int C.
d__0.<>1__state"" - IL_010d: ldarg.0 - IL_010e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder"" - IL_0113: ldloc.s V_5 - IL_0115: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" - IL_011a: nop - IL_011b: leave.s IL_0131 - } - IL_011d: ldarg.0 - IL_011e: ldc.i4.s -2 - IL_0120: stfld ""int C.
d__0.<>1__state"" - IL_0125: ldarg.0 - IL_0126: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder"" - IL_012b: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" - IL_0130: nop - IL_0131: ret -} -"); - } - - [Fact] - public void TestWithExpression_03() - { - string source = @" -class C -{ - public static async System.Threading.Tasks.Task Main() - { - await using (new C()) - { - System.Console.Write(""body ""); - return; - } - } - public System.Threading.Tasks.ValueTask DisposeAsync() - { - System.Console.Write(""DisposeAsync""); - return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask); - } -} -"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); - verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" -{ - // Code size 306 (0x132) - .maxstack 3 - .locals init (int V_0, - object V_1, - System.Runtime.CompilerServices.ValueTaskAwaiter V_2, - System.Threading.Tasks.ValueTask V_3, - C.
d__0 V_4, - System.Exception V_5, - int V_6) - IL_0000: ldarg.0 - IL_0001: ldfld ""int C.
d__0.<>1__state"" - IL_0006: stloc.0 - .try - { - IL_0007: ldloc.0 - IL_0008: brfalse.s IL_000c - IL_000a: br.s IL_0011 - IL_000c: br IL_0099 - IL_0011: nop - IL_0012: ldarg.0 - IL_0013: newobj ""C..ctor()"" - IL_0018: stfld ""C C.
d__0.<>s__1"" - IL_001d: ldarg.0 - IL_001e: ldnull - IL_001f: stfld ""object C.
d__0.<>s__2"" - IL_0024: ldarg.0 - IL_0025: ldc.i4.0 - IL_0026: stfld ""int C.
d__0.<>s__3"" - .try - { - IL_002b: nop - IL_002c: ldstr ""body "" - IL_0031: call ""void System.Console.Write(string)"" - IL_0036: nop - IL_0037: br.s IL_0039 - IL_0039: ldarg.0 - IL_003a: ldc.i4.1 - IL_003b: stfld ""int C.
d__0.<>s__3"" - IL_0040: leave.s IL_004c - } - catch object - { - IL_0042: stloc.1 - IL_0043: ldarg.0 - IL_0044: ldloc.1 - IL_0045: stfld ""object C.
d__0.<>s__2"" - IL_004a: leave.s IL_004c - } - IL_004c: ldarg.0 - IL_004d: ldfld ""C C.
d__0.<>s__1"" - IL_0052: brfalse.s IL_00bd - IL_0054: ldarg.0 - IL_0055: ldfld ""C C.
d__0.<>s__1"" - IL_005a: callvirt ""System.Threading.Tasks.ValueTask C.DisposeAsync()"" - IL_005f: stloc.3 - IL_0060: ldloca.s V_3 - IL_0062: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()"" - IL_0067: stloc.2 - IL_0068: ldloca.s V_2 - IL_006a: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get"" - IL_006f: brtrue.s IL_00b5 - IL_0071: ldarg.0 - IL_0072: ldc.i4.0 - IL_0073: dup - IL_0074: stloc.0 - IL_0075: stfld ""int C.
d__0.<>1__state"" - IL_007a: ldarg.0 - IL_007b: ldloc.2 - IL_007c: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.
d__0.<>u__1"" - IL_0081: ldarg.0 - IL_0082: stloc.s V_4 - IL_0084: ldarg.0 - IL_0085: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder"" - IL_008a: ldloca.s V_2 - IL_008c: ldloca.s V_4 - IL_008e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.
d__0)"" - IL_0093: nop - IL_0094: leave IL_0131 - IL_0099: ldarg.0 - IL_009a: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.
d__0.<>u__1"" - IL_009f: stloc.2 - IL_00a0: ldarg.0 - IL_00a1: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.
d__0.<>u__1"" - IL_00a6: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter"" - IL_00ac: ldarg.0 - IL_00ad: ldc.i4.m1 - IL_00ae: dup - IL_00af: stloc.0 - IL_00b0: stfld ""int C.
d__0.<>1__state"" - IL_00b5: ldloca.s V_2 - IL_00b7: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()"" - IL_00bc: nop - IL_00bd: ldarg.0 - IL_00be: ldfld ""object C.
d__0.<>s__2"" - IL_00c3: stloc.1 - IL_00c4: ldloc.1 - IL_00c5: brfalse.s IL_00e2 - IL_00c7: ldloc.1 - IL_00c8: isinst ""System.Exception"" - IL_00cd: stloc.s V_5 - IL_00cf: ldloc.s V_5 - IL_00d1: brtrue.s IL_00d5 - IL_00d3: ldloc.1 - IL_00d4: throw - IL_00d5: ldloc.s V_5 - IL_00d7: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)"" - IL_00dc: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()"" - IL_00e1: nop - IL_00e2: ldarg.0 - IL_00e3: ldfld ""int C.
d__0.<>s__3"" - IL_00e8: stloc.s V_6 - IL_00ea: ldloc.s V_6 - IL_00ec: ldc.i4.1 - IL_00ed: beq.s IL_00f1 - IL_00ef: br.s IL_00f3 - IL_00f1: leave.s IL_011d - IL_00f3: ldarg.0 - IL_00f4: ldnull - IL_00f5: stfld ""object C.
d__0.<>s__2"" - IL_00fa: ldarg.0 - IL_00fb: ldnull - IL_00fc: stfld ""C C.
d__0.<>s__1"" - IL_0101: leave.s IL_011d - } - catch System.Exception - { - IL_0103: stloc.s V_5 - IL_0105: ldarg.0 - IL_0106: ldc.i4.s -2 - IL_0108: stfld ""int C.
d__0.<>1__state"" - IL_010d: ldarg.0 - IL_010e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder"" - IL_0113: ldloc.s V_5 - IL_0115: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" - IL_011a: nop - IL_011b: leave.s IL_0131 - } - IL_011d: ldarg.0 - IL_011e: ldc.i4.s -2 - IL_0120: stfld ""int C.
d__0.<>1__state"" - IL_0125: ldarg.0 - IL_0126: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder"" - IL_012b: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" - IL_0130: nop - IL_0131: ret -} -"); - } - - [Fact] - public void TestWithNullExpression() - { - string source = @" -class C -{ - public static async System.Threading.Tasks.Task Main() - { - await using (null) - { - System.Console.Write(""body""); - return; - } - } -} -"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body"); - } - - [Fact] - public void TestWithMethodName() - { - string source = @" -class C -{ - public static async System.Threading.Tasks.Task Main() - { - await using (Main) - { - } - } -} -"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }); - comp.VerifyDiagnostics( - // (6,22): error CS8410: 'method group': type used in an async using statement must be implicitly convertible to 'System.IAsyncDisposable' - // await using (Main) - Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "Main").WithArguments("method group").WithLocation(6, 22) - ); - } - - [Fact] - public void TestWithDynamicExpression() - { - string source = @" -class C : System.IAsyncDisposable -{ - public static async System.Threading.Tasks.Task Main() - { - dynamic d = new C(); - await using (d) - { - System.Console.Write(""body ""); - return; - } - } - public System.Threading.Tasks.ValueTask DisposeAsync() - { - System.Console.Write(""DisposeAsync""); - return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask); - } -} -"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, references: new[] { CSharpRef }); - comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); - } - - [Fact] - public void TestWithStructExpression_01() - { - string source = @" -struct S : System.IAsyncDisposable -{ - public static async System.Threading.Tasks.Task Main() - { - await using (new S()) - { - System.Console.Write(""body ""); - return; - } - } - System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() - { - System.Console.Write(""DisposeAsync""); - return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask); - } -} -"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); - verifier.VerifyIL("S.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" -{ - // Code size 298 (0x12a) - .maxstack 3 - .locals init (int V_0, - object V_1, - System.Runtime.CompilerServices.ValueTaskAwaiter V_2, - System.Threading.Tasks.ValueTask V_3, - S.
d__0 V_4, - System.Exception V_5, - int V_6) - IL_0000: ldarg.0 - IL_0001: ldfld ""int S.
d__0.<>1__state"" - IL_0006: stloc.0 - .try - { - IL_0007: ldloc.0 - IL_0008: brfalse.s IL_000c - IL_000a: br.s IL_0011 - IL_000c: br IL_0098 - IL_0011: nop - IL_0012: ldarg.0 - IL_0013: ldflda ""S S.
d__0.<>s__1"" - IL_0018: initobj ""S"" - IL_001e: ldarg.0 - IL_001f: ldnull - IL_0020: stfld ""object S.
d__0.<>s__2"" - IL_0025: ldarg.0 - IL_0026: ldc.i4.0 - IL_0027: stfld ""int S.
d__0.<>s__3"" - .try - { - IL_002c: nop - IL_002d: ldstr ""body "" - IL_0032: call ""void System.Console.Write(string)"" - IL_0037: nop - IL_0038: br.s IL_003a - IL_003a: ldarg.0 - IL_003b: ldc.i4.1 - IL_003c: stfld ""int S.
d__0.<>s__3"" - IL_0041: leave.s IL_004d - } - catch object - { - IL_0043: stloc.1 - IL_0044: ldarg.0 - IL_0045: ldloc.1 - IL_0046: stfld ""object S.
d__0.<>s__2"" - IL_004b: leave.s IL_004d + IL_0043: stloc.1 + IL_0044: ldarg.0 + IL_0045: ldloc.1 + IL_0046: stfld ""object S.
d__0.<>s__2"" + IL_004b: leave.s IL_004d } IL_004d: ldarg.0 IL_004e: ldflda ""S S.
d__0.<>s__1"" @@ -1838,174 +1364,6 @@ .locals init (int V_0, }"); } - [Fact] - public void TestWithStructExpression_02() - { - string source = @" -struct S : System.IAsyncDisposable -{ - public static async System.Threading.Tasks.Task Main() - { - await using (new S()) - { - System.Console.Write(""body ""); - return; - } - } - public System.Threading.Tasks.ValueTask DisposeAsync() - { - System.Console.Write(""DisposeAsync""); - return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask); - } -} -"; - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); - verifier.VerifyIL("S.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" -{ - // Code size 292 (0x124) - .maxstack 3 - .locals init (int V_0, - object V_1, - System.Runtime.CompilerServices.ValueTaskAwaiter V_2, - System.Threading.Tasks.ValueTask V_3, - S.
d__0 V_4, - System.Exception V_5, - int V_6) - IL_0000: ldarg.0 - IL_0001: ldfld ""int S.
d__0.<>1__state"" - IL_0006: stloc.0 - .try - { - IL_0007: ldloc.0 - IL_0008: brfalse.s IL_000c - IL_000a: br.s IL_0011 - IL_000c: br IL_0092 - IL_0011: nop - IL_0012: ldarg.0 - IL_0013: ldflda ""S S.
d__0.<>s__1"" - IL_0018: initobj ""S"" - IL_001e: ldarg.0 - IL_001f: ldnull - IL_0020: stfld ""object S.
d__0.<>s__2"" - IL_0025: ldarg.0 - IL_0026: ldc.i4.0 - IL_0027: stfld ""int S.
d__0.<>s__3"" - .try - { - IL_002c: nop - IL_002d: ldstr ""body "" - IL_0032: call ""void System.Console.Write(string)"" - IL_0037: nop - IL_0038: br.s IL_003a - IL_003a: ldarg.0 - IL_003b: ldc.i4.1 - IL_003c: stfld ""int S.
d__0.<>s__3"" - IL_0041: leave.s IL_004d - } - catch object - { - IL_0043: stloc.1 - IL_0044: ldarg.0 - IL_0045: ldloc.1 - IL_0046: stfld ""object S.
d__0.<>s__2"" - IL_004b: leave.s IL_004d - } - IL_004d: ldarg.0 - IL_004e: ldflda ""S S.
d__0.<>s__1"" - IL_0053: call ""System.Threading.Tasks.ValueTask S.DisposeAsync()"" - IL_0058: stloc.3 - IL_0059: ldloca.s V_3 - IL_005b: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()"" - IL_0060: stloc.2 - IL_0061: ldloca.s V_2 - IL_0063: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get"" - IL_0068: brtrue.s IL_00ae - IL_006a: ldarg.0 - IL_006b: ldc.i4.0 - IL_006c: dup - IL_006d: stloc.0 - IL_006e: stfld ""int S.
d__0.<>1__state"" - IL_0073: ldarg.0 - IL_0074: ldloc.2 - IL_0075: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter S.
d__0.<>u__1"" - IL_007a: ldarg.0 - IL_007b: stloc.s V_4 - IL_007d: ldarg.0 - IL_007e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder S.
d__0.<>t__builder"" - IL_0083: ldloca.s V_2 - IL_0085: ldloca.s V_4 - IL_0087: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref S.
d__0)"" - IL_008c: nop - IL_008d: leave IL_0123 - IL_0092: ldarg.0 - IL_0093: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter S.
d__0.<>u__1"" - IL_0098: stloc.2 - IL_0099: ldarg.0 - IL_009a: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter S.
d__0.<>u__1"" - IL_009f: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter"" - IL_00a5: ldarg.0 - IL_00a6: ldc.i4.m1 - IL_00a7: dup - IL_00a8: stloc.0 - IL_00a9: stfld ""int S.
d__0.<>1__state"" - IL_00ae: ldloca.s V_2 - IL_00b0: call ""void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()"" - IL_00b5: nop - IL_00b6: ldarg.0 - IL_00b7: ldfld ""object S.
d__0.<>s__2"" - IL_00bc: stloc.1 - IL_00bd: ldloc.1 - IL_00be: brfalse.s IL_00db - IL_00c0: ldloc.1 - IL_00c1: isinst ""System.Exception"" - IL_00c6: stloc.s V_5 - IL_00c8: ldloc.s V_5 - IL_00ca: brtrue.s IL_00ce - IL_00cc: ldloc.1 - IL_00cd: throw - IL_00ce: ldloc.s V_5 - IL_00d0: call ""System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)"" - IL_00d5: callvirt ""void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()"" - IL_00da: nop - IL_00db: ldarg.0 - IL_00dc: ldfld ""int S.
d__0.<>s__3"" - IL_00e1: stloc.s V_6 - IL_00e3: ldloc.s V_6 - IL_00e5: ldc.i4.1 - IL_00e6: beq.s IL_00ea - IL_00e8: br.s IL_00ec - IL_00ea: leave.s IL_010f - IL_00ec: ldarg.0 - IL_00ed: ldnull - IL_00ee: stfld ""object S.
d__0.<>s__2"" - IL_00f3: leave.s IL_010f - } - catch System.Exception - { - IL_00f5: stloc.s V_5 - IL_00f7: ldarg.0 - IL_00f8: ldc.i4.s -2 - IL_00fa: stfld ""int S.
d__0.<>1__state"" - IL_00ff: ldarg.0 - IL_0100: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder S.
d__0.<>t__builder"" - IL_0105: ldloc.s V_5 - IL_0107: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" - IL_010c: nop - IL_010d: leave.s IL_0123 - } - IL_010f: ldarg.0 - IL_0110: ldc.i4.s -2 - IL_0112: stfld ""int S.
d__0.<>1__state"" - IL_0117: ldarg.0 - IL_0118: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder S.
d__0.<>t__builder"" - IL_011d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" - IL_0122: nop - IL_0123: ret -}"); - } - [Fact] public void Struct_ExplicitImplementation() { @@ -2475,9 +1833,12 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() [Fact] [WorkItem(32316, "https://github.com/dotnet/roslyn/issues/32316")] - [WorkItem("https://github.com/dotnet/roslyn/issues/72573")] - public void TestPatternBasedDisposal_InterfaceNotPreferredOverInstanceMethod() + public void TestPatternBasedDisposal_InterfacePreferredOverInstanceMethod() { + // SPEC: In the situation where a type can be implicitly converted to IDisposable and also fits the disposable pattern, + // then IDisposable will be preferred. + // https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/using.md#pattern-based-using + string source = @" public class C : System.IAsyncDisposable { @@ -2491,14 +1852,14 @@ public static async System.Threading.Tasks.Task Main() System.Console.Write(""return""); return 1; } - System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() - => throw null; - public async System.Threading.Tasks.ValueTask DisposeAsync() + async System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() { System.Console.Write($""dispose_start ""); await System.Threading.Tasks.Task.Yield(); System.Console.Write($""dispose_end ""); } + public System.Threading.Tasks.ValueTask DisposeAsync() + => throw null; } "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenUsingDeclarationTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenUsingDeclarationTests.cs index 7c29ca0f33e1a..d21d6821db8fa 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenUsingDeclarationTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenUsingDeclarationTests.cs @@ -1320,14 +1320,14 @@ static async Task Main() } [Fact] - public void UsingDeclarationAsyncMissingValueTask_01() + public void UsingDeclarationAsyncMissingValueTask() { var source = @" using System; using System.Threading.Tasks; class C1 : IAsyncDisposable { - ValueTask IAsyncDisposable.DisposeAsync() + public ValueTask DisposeAsync() { return new ValueTask(Task.CompletedTask); } @@ -1350,33 +1350,6 @@ static async Task Main() ); } - [Fact] - public void UsingDeclarationAsyncMissingValueTask_02() - { - var source = @" -using System; -using System.Threading.Tasks; -class C1 : IAsyncDisposable -{ - public ValueTask DisposeAsync() - { - return new ValueTask(Task.CompletedTask); - } -} - -class C2 -{ - static async Task Main() - { - await using C1 c1 = new C1(); - } -}"; - - var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }); - comp.MakeTypeMissing(WellKnownType.System_Threading_Tasks_ValueTask); - comp.VerifyEmitDiagnostics(); - } - [Fact] public void UsingDeclarationAsync_WithOptionalParameter() { diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IUsingStatement.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IUsingStatement.cs index 766404d3df5d2..f50818c83dbbd 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IUsingStatement.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IUsingStatement.cs @@ -93,7 +93,7 @@ public static async Task M1(IAsyncDisposable disposable) } "; string expectedOperationTree = @" -IUsingOperation (IsAsynchronous) (DisposeMethod: System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Using, Type: null) (Syntax: 'await using ... }') +IUsingOperation (IsAsynchronous) (OperationKind.Using, Type: null) (Syntax: 'await using ... }') Locals: Local_1: System.IAsyncDisposable c Resources: IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'var c = disposable') @@ -126,124 +126,6 @@ public static async Task M1(IAsyncDisposable disposable) VerifyOperationTreeAndDiagnosticsForTest(source + s_IAsyncEnumerable + IOperationTests_IForEachLoopStatement.s_ValueTask, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.AsyncStreams)] - [Fact, WorkItem(30362, "https://github.com/dotnet/roslyn/issues/30362")] - public void IUsingAwaitStatement_SimpleAwaitUsing_PatternBased() - { - string source = @" -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -class C -{ - public async Task M1() - { - /**/await using (var c = this) - { - Console.WriteLine(c.ToString()); - }/**/ - } - - public async System.Threading.Tasks.ValueTask DisposeAsync() - { - await Task.Yield(); - } -} -"; - string expectedOperationTree = @" -IUsingOperation (IsAsynchronous) (DisposeMethod: System.Threading.Tasks.ValueTask C.DisposeAsync()) (OperationKind.Using, Type: null) (Syntax: 'await using ... }') - Locals: Local_1: C c - Resources: - IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'var c = this') - IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'var c = this') - Declarators: - IVariableDeclaratorOperation (Symbol: C c) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'c = this') - Initializer: - IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= this') - IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this') - Initializer: - null - Body: - IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Wri ... oString());') - Expression: - IInvocationOperation (void System.Console.WriteLine(System.String value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Wri ... ToString())') - Instance Receiver: - null - Arguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'c.ToString()') - IInvocationOperation (virtual System.String System.Object.ToString()) (OperationKind.Invocation, Type: System.String) (Syntax: 'c.ToString()') - Instance Receiver: - ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') - Arguments(0) - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) -"; - - var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source + IOperationTests_IForEachLoopStatement.s_ValueTask, expectedOperationTree, expectedDiagnostics); - } - - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.AsyncStreams)] - [Fact, WorkItem(30362, "https://github.com/dotnet/roslyn/issues/30362")] - public void IUsingAwaitStatement_SimpleAwaitUsing_InterfaceBased() - { - string source = @" -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -class C : IAsyncDisposable -{ - public async Task M1() - { - /**/await using (var c = this) - { - Console.WriteLine(c.ToString()); - }/**/ - } - - async ValueTask IAsyncDisposable.DisposeAsync() - { - await Task.Yield(); - } -} -"; - string expectedOperationTree = @" -IUsingOperation (IsAsynchronous) (OperationKind.Using, Type: null) (Syntax: 'await using ... }') - Locals: Local_1: C c - Resources: - IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'var c = this') - IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'var c = this') - Declarators: - IVariableDeclaratorOperation (Symbol: C c) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'c = this') - Initializer: - IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= this') - IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this') - Initializer: - null - Body: - IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Wri ... oString());') - Expression: - IInvocationOperation (void System.Console.WriteLine(System.String value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Wri ... ToString())') - Instance Receiver: - null - Arguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'c.ToString()') - IInvocationOperation (virtual System.String System.Object.ToString()) (OperationKind.Invocation, Type: System.String) (Syntax: 'c.ToString()') - Instance Receiver: - ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') - Arguments(0) - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) -"; - - var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source + s_IAsyncEnumerable + IOperationTests_IForEachLoopStatement.s_ValueTask, expectedOperationTree, expectedDiagnostics); - } - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.AsyncStreams)] [Fact, WorkItem(30362, "https://github.com/dotnet/roslyn/issues/30362")] public void UsingFlow_SimpleAwaitUsing() @@ -277,9 +159,9 @@ public static async Task M1(IAsyncDisposable disposable) Predecessors: [B0] Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.IAsyncDisposable, IsImplicit) (Syntax: 'c = disposable') - Left: + Left: ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: System.IAsyncDisposable, IsImplicit) (Syntax: 'c = disposable') - Right: + Right: IParameterReferenceOperation: disposable (OperationKind.ParameterReference, Type: System.IAsyncDisposable) (Syntax: 'disposable') Next (Regular) Block[B2] Entering: {R2} {R3} @@ -289,14 +171,14 @@ public static async Task M1(IAsyncDisposable disposable) Predecessors: [B1] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Wri ... oString());') - Expression: + Expression: IInvocationOperation (void System.Console.WriteLine(System.String value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Wri ... ToString())') - Instance Receiver: + Instance Receiver: null Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'c.ToString()') IInvocationOperation (virtual System.String System.Object.ToString()) (OperationKind.Invocation, Type: System.String) (Syntax: 'c.ToString()') - Instance Receiver: + Instance Receiver: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: System.IAsyncDisposable) (Syntax: 'c') Arguments(0) InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) @@ -312,16 +194,16 @@ public static async Task M1(IAsyncDisposable disposable) Statements (0) Jump if True (Regular) to Block[B5] IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'c = disposable') - Operand: + Operand: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: System.IAsyncDisposable, IsImplicit) (Syntax: 'c = disposable') Next (Regular) Block[B4] Block[B4] - Block Predecessors: [B3] Statements (1) IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'c = disposable') - Expression: - IInvocationOperation ( System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'c = disposable') - Instance Receiver: + Expression: + IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'c = disposable') + Instance Receiver: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: System.IAsyncDisposable, IsImplicit) (Syntax: 'c = disposable') Arguments(0) Next (Regular) Block[B5] @@ -340,212 +222,6 @@ public static async Task M1(IAsyncDisposable disposable) VerifyFlowGraphAndDiagnosticsForTest(source + s_IAsyncEnumerable + IOperationTests_IForEachLoopStatement.s_ValueTask, expectedGraph, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.AsyncStreams)] - [Fact, WorkItem(30362, "https://github.com/dotnet/roslyn/issues/30362")] - public void UsingFlow_SimpleAwaitUsing_PatternBased() - { - string source = @" -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -class C -{ - public async Task M1() - /**/{ - await using (var c = this) - { - Console.WriteLine(c.ToString()); - } - }/**/ - - public async ValueTask DisposeAsync() - { - await System.Threading.Tasks.Task.Yield(); - } -} -"; - - string expectedGraph = @" -Block[B0] - Entry - Statements (0) - Next (Regular) Block[B1] - Entering: {R1} -.locals {R1} -{ - Locals: [C c] - Block[B1] - Block - Predecessors: [B0] - Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C, IsImplicit) (Syntax: 'c = this') - Left: - ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: C, IsImplicit) (Syntax: 'c = this') - Right: - IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this') - Next (Regular) Block[B2] - Entering: {R2} {R3} - .try {R2, R3} - { - Block[B2] - Block - Predecessors: [B1] - Statements (1) - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Wri ... oString());') - Expression: - IInvocationOperation (void System.Console.WriteLine(System.String value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Wri ... ToString())') - Instance Receiver: - null - Arguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'c.ToString()') - IInvocationOperation (virtual System.String System.Object.ToString()) (OperationKind.Invocation, Type: System.String) (Syntax: 'c.ToString()') - Instance Receiver: - ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') - Arguments(0) - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Next (Regular) Block[B6] - Finalizing: {R4} - Leaving: {R3} {R2} {R1} - } - .finally {R4} - { - Block[B3] - Block - Predecessors (0) - Statements (0) - Jump if True (Regular) to Block[B5] - IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'c = this') - Operand: - ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C, IsImplicit) (Syntax: 'c = this') - Next (Regular) Block[B4] - Block[B4] - Block - Predecessors: [B3] - Statements (1) - IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'c = this') - Expression: - IInvocationOperation ( System.Threading.Tasks.ValueTask C.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'c = this') - Instance Receiver: - ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C, IsImplicit) (Syntax: 'c = this') - Arguments(0) - Next (Regular) Block[B5] - Block[B5] - Block - Predecessors: [B3] [B4] - Statements (0) - Next (StructuredExceptionHandling) Block[null] - } -} -Block[B6] - Exit - Predecessors: [B2] - Statements (0) -"; - var expectedDiagnostics = DiagnosticDescription.None; - - VerifyFlowGraphAndDiagnosticsForTest(source + s_IAsyncEnumerable + IOperationTests_IForEachLoopStatement.s_ValueTask, expectedGraph, expectedDiagnostics); - } - - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.AsyncStreams)] - [Fact, WorkItem(30362, "https://github.com/dotnet/roslyn/issues/30362")] - public void UsingFlow_SimpleAwaitUsing_InterfaceBased() - { - string source = @" -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -class C : IAsyncDisposable -{ - public async Task M1() - /**/{ - await using (var c = this) - { - Console.WriteLine(c.ToString()); - } - }/**/ - - async ValueTask IAsyncDisposable.DisposeAsync() - { - await System.Threading.Tasks.Task.Yield(); - } -} -"; - - string expectedGraph = @" -Block[B0] - Entry - Statements (0) - Next (Regular) Block[B1] - Entering: {R1} -.locals {R1} -{ - Locals: [C c] - Block[B1] - Block - Predecessors: [B0] - Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C, IsImplicit) (Syntax: 'c = this') - Left: - ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: C, IsImplicit) (Syntax: 'c = this') - Right: - IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C) (Syntax: 'this') - Next (Regular) Block[B2] - Entering: {R2} {R3} - .try {R2, R3} - { - Block[B2] - Block - Predecessors: [B1] - Statements (1) - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Wri ... oString());') - Expression: - IInvocationOperation (void System.Console.WriteLine(System.String value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Wri ... ToString())') - Instance Receiver: - null - Arguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'c.ToString()') - IInvocationOperation (virtual System.String System.Object.ToString()) (OperationKind.Invocation, Type: System.String) (Syntax: 'c.ToString()') - Instance Receiver: - ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') - Arguments(0) - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Next (Regular) Block[B6] - Finalizing: {R4} - Leaving: {R3} {R2} {R1} - } - .finally {R4} - { - Block[B3] - Block - Predecessors (0) - Statements (0) - Jump if True (Regular) to Block[B5] - IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'c = this') - Operand: - ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C, IsImplicit) (Syntax: 'c = this') - Next (Regular) Block[B4] - Block[B4] - Block - Predecessors: [B3] - Statements (1) - IAwaitOperation (OperationKind.Await, Type: System.Void, IsImplicit) (Syntax: 'c = this') - Expression: - IInvocationOperation (virtual System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()) (OperationKind.Invocation, Type: System.Threading.Tasks.ValueTask, IsImplicit) (Syntax: 'c = this') - Instance Receiver: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.IAsyncDisposable, IsImplicit) (Syntax: 'c = this') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) - (ImplicitReference) - Operand: - ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C, IsImplicit) (Syntax: 'c = this') - Arguments(0) - Next (Regular) Block[B5] - Block[B5] - Block - Predecessors: [B3] [B4] - Statements (0) - Next (StructuredExceptionHandling) Block[null] - } -} -Block[B6] - Exit - Predecessors: [B2] - Statements (0) -"; - var expectedDiagnostics = DiagnosticDescription.None; - - VerifyFlowGraphAndDiagnosticsForTest(source + s_IAsyncEnumerable + IOperationTests_IForEachLoopStatement.s_ValueTask, expectedGraph, expectedDiagnostics); - } - [CompilerTrait(CompilerFeature.IOperation)] [Fact] public void IUsingStatement_MultipleNewVariable() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UsingDeclarationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UsingDeclarationTests.cs index e3885e0761e42..2e6adae448c0c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UsingDeclarationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UsingDeclarationTests.cs @@ -867,10 +867,7 @@ static async Task Main() "; var expected = new[] { - // 0.cs(8,9): error CS8370: Feature 'pattern-based disposal' is not available in C# 7.3. Please use language version 8.0 or greater. - // await using IAsyncDisposable x = null; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "await using IAsyncDisposable x = null;").WithArguments("pattern-based disposal", "8.0").WithLocation(8, 9), - // 0.cs(8,15): error CS8370: Feature 'using declarations' is not available in C# 7.3. Please use language version 8.0 or greater. + // (8,15): error CS8652: The feature 'using declarations' is not available in C# 7.3. Please use language version 8.0 or greater. // await using IAsyncDisposable x = null; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "using").WithArguments("using declarations", "8.0").WithLocation(8, 15) };