|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics
{
public class BindingAsyncTasklikeTests : CompilingTestBase
{
[Fact]
public void AsyncTasklikeFromBuilderMethod()
{
var source = @"
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
class C {
async ValueTask f() { await (Task)null; }
async ValueTask<int> g() { await (Task)null; return 1; }
}
[AsyncMethodBuilder(typeof(string))]
struct ValueTask { }
[AsyncMethodBuilder(typeof(Task<>))]
struct ValueTask<T> { }
namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } }
";
var compilation = CreateCompilationWithMscorlib461(source).VerifyDiagnostics();
var methodf = compilation.GetMember<MethodSymbol>("C.f");
var methodg = compilation.GetMember<MethodSymbol>("C.g");
Assert.True(methodf.IsAsync);
Assert.True(methodg.IsAsync);
}
[Fact]
public void AsyncTasklikeNotFromDelegate()
{
var source = @"
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
class Program
{
static void Main()
{
}
static async Task f()
{
await new Awaitable();
await new Unawaitable(); // error: GetAwaiter must be a field not a delegate
}
static async Tasklike g()
{
await (Task)null;
}
}
class Awaitable
{
public TaskAwaiter GetAwaiter() => (Task.FromResult(1) as Task).GetAwaiter();
}
class Unawaitable
{
public Func<TaskAwaiter> GetAwaiter = () => (Task.FromResult(1) as Task).GetAwaiter();
}
[AsyncMethodBuilder(typeof(TasklikeMethodBuilder))]
public class Tasklike { }
public class TasklikeMethodBuilder
{
public static TasklikeMethodBuilder Create() => null;
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { }
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
public void SetResult() { }
public void SetException(Exception exception) { }
private void EnsureTaskBuilder() { }
public Tasklike Task => null;
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { }
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { }
}
namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } }
";
CreateCompilationWithMscorlib461(source).VerifyDiagnostics(
// (15,9): error CS0118: 'GetAwaiter' is a field but is used like a method
// await new Unawaitable(); // error: GetAwaiter must be a field not a delegate
Diagnostic(ErrorCode.ERR_BadSKknown, "await new Unawaitable()").WithArguments("GetAwaiter", "field", "method").WithLocation(15, 9)
);
}
private bool VerifyTaskOverloads(string arg, string betterOverload, string worseOverload, bool implicitConversionToTask = false, bool isError = false)
{
var source =
@"using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
class Program
{
#pragma warning disable CS1998
static void Main()
{
var s = (<<arg>>);
Console.Write(s);
}
<<betterOverload>>
<<worseOverload>>
}
[AsyncMethodBuilder(typeof(ValueTaskMethodBuilder))]
struct ValueTask
{
<<implicitConversionToTask>>
internal Awaiter GetAwaiter() => new Awaiter();
internal class Awaiter : INotifyCompletion
{
public void OnCompleted(Action a) { }
internal bool IsCompleted => true;
internal void GetResult() { }
}
}
[AsyncMethodBuilder(typeof(ValueTaskMethodBuilder<>))]
struct ValueTask<T>
{
internal T _result;
<<implicitConversionToTaskT>>
public T Result => _result;
internal Awaiter GetAwaiter() => new Awaiter(this);
internal class Awaiter : INotifyCompletion
{
private ValueTask<T> _task;
internal Awaiter(ValueTask<T> task) { _task = task; }
public void OnCompleted(Action a) { }
internal bool IsCompleted => true;
internal T GetResult() => _task.Result;
}
}
sealed class ValueTaskMethodBuilder
{
private ValueTask _task = new ValueTask();
public static ValueTaskMethodBuilder Create() => new ValueTaskMethodBuilder();
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
stateMachine.MoveNext();
}
public void SetException(Exception e) { }
public void SetResult() { }
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { }
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { }
public ValueTask Task => _task;
}
sealed class ValueTaskMethodBuilder<T>
{
private ValueTask<T> _task = new ValueTask<T>();
public static ValueTaskMethodBuilder<T> Create() => null;
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
stateMachine.MoveNext();
}
public void SetException(Exception e) { }
public void SetResult(T t) { _task._result = t; }
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { }
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { }
public ValueTask<T> Task => _task;
}
namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } }
";
source = source.Replace("<<arg>>", arg);
source = source.Replace("<<betterOverload>>", (betterOverload != null) ? "static string " + betterOverload + " => \"better\";" : "");
source = source.Replace("<<worseOverload>>", (worseOverload != null) ? "static string " + worseOverload + " => \"worse\";" : "");
source = source.Replace("<<implicitConversionToTask>>", implicitConversionToTask ? "public static implicit operator Task(ValueTask t) => Task.FromResult(0);" : "");
source = source.Replace("<<implicitConversionToTaskT>>", implicitConversionToTask ? "public static implicit operator Task<T>(ValueTask<T> t) => Task.FromResult<T>(t._result);" : "");
if (isError)
{
var compilation = CreateCompilationWithMscorlib461(source);
var diagnostics = compilation.GetDiagnostics();
Assert.True(diagnostics.Length == 1);
Assert.True(diagnostics.First().Code == (int)ErrorCode.ERR_AmbigCall);
}
else
{
CompileAndVerify(source, targetFramework: TargetFramework.Empty, references: new[] { MscorlibRef_v4_0_30316_17626 }, expectedOutput: "better");
}
return true;
}
[Fact]
public void TasklikeA3() => VerifyTaskOverloads("f(async () => 3)",
"f(Func<ValueTask<int>> lambda)",
null);
[Fact]
public void TasklikeA3n() => VerifyTaskOverloads("f(async () => {})",
"f(Func<ValueTask> lambda)",
null);
[Fact]
public void TasklikeA4() => VerifyTaskOverloads("f(async () => 3)",
"f<T>(Func<ValueTask<T>> labda)",
null);
[Fact]
public void TasklikeA5s() => VerifyTaskOverloads("f(() => 3)",
"f<T>(Func<T> lambda)",
"f<T>(Func<ValueTask<T>> lambda)");
[Fact]
public void TasklikeA5a() => VerifyTaskOverloads("f(async () => 3)",
"f<T>(Func<ValueTask<T>> lambda)",
"f<T>(Func<T> lambda)");
[Fact]
public void TasklikeA6() => VerifyTaskOverloads("f(async () => 3)",
"f(Func<ValueTask<int>> lambda)",
"f(Func<ValueTask<double>> lambda)");
[Fact]
public void TasklikeA7() => VerifyTaskOverloads("f(async () => 3)",
"f(Func<ValueTask<byte>> lambda)",
"f(Func<ValueTask<short>> lambda)");
[Fact]
public void TasklikeA8() => VerifyTaskOverloads("f(async () => {})",
"f(Func<ValueTask> lambda)",
"f(Action lambda)");
[Fact]
public void TasklikeB7_ic0() => VerifyTaskOverloads("f(async () => 3)",
"f(Func<ValueTask<int>> lambda)",
"f(Func<Task<int>> lambda)",
isError: true);
[Fact]
public void TasklikeB7_ic1() => VerifyTaskOverloads("f(async () => 3)",
"f(Func<ValueTask<int>> lambda)",
"f(Func<Task<int>> lambda)",
implicitConversionToTask: true);
[Fact]
public void TasklikeB7g_ic0() => VerifyTaskOverloads("f(async () => 3)",
"f<T>(Func<ValueTask<T>> lambda)",
"f<T>(Func<Task<T>> lambda)",
isError: true);
[Fact]
public void TasklikeB7g_ic1() => VerifyTaskOverloads("f(async () => 3)",
"f<T>(Func<ValueTask<T>> lambda)",
"f<T>(Func<Task<T>> lambda)",
implicitConversionToTask: true);
[Fact]
public void TasklikeB7n_ic0() => VerifyTaskOverloads("f(async () => {})",
"f(Func<ValueTask> lambda)",
"f(Func<Task> lambda)",
isError: true);
[Fact]
public void TasklikeB7n_ic1() => VerifyTaskOverloads("f(async () => {})",
"f(Func<ValueTask> lambda)",
"f(Func<Task> lambda)",
implicitConversionToTask: true);
[Fact]
public void TasklikeC1() => VerifyTaskOverloads("f(async () => 3)",
"f(Func<ValueTask<int>> lambda)",
"f(Func<Task<double>> lambda)");
[Fact]
public void TasklikeC2() => VerifyTaskOverloads("f(async () => 3)",
"f(Func<ValueTask<byte>> lambda)",
"f(Func<Task<short>> lambda)");
[Fact]
public void TasklikeC5() => VerifyTaskOverloads("f(async () => 3)",
"f(Func<ValueTask<int>> lambda)",
"f<T>(Func<Task<T>> lambda)");
[Fact]
public void AsyncTasklikeMethod()
{
var source = @"
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
class C {
async ValueTask f() { await Task.Delay(0); }
async ValueTask<int> g() { await Task.Delay(0); return 1; }
}
[AsyncMethodBuilder(typeof(ValueTaskMethodBuilder))]
struct ValueTask { }
[AsyncMethodBuilder(typeof(ValueTaskMethodBuilder<>))]
struct ValueTask<T> { }
class ValueTaskMethodBuilder {}
class ValueTaskMethodBuilder<T> {}
namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } }
";
var compilation = CreateCompilationWithMscorlib461(source).VerifyDiagnostics();
var methodf = compilation.GetMember<MethodSymbol>("C.f");
var methodg = compilation.GetMember<MethodSymbol>("C.g");
Assert.True(methodf.IsAsync);
Assert.True(methodg.IsAsync);
}
[Fact]
public void NotTasklike()
{
var source1 = @"
using System.Threading.Tasks;
class C
{
static void Main() { }
async MyTask f() { await (Task)null; }
}
public class MyTask { }
";
CreateCompilationWithMscorlib461(source1).VerifyDiagnostics(
// (6,18): error CS1983: The return type of an async method must be void, Task or Task<T>
// async MyTask f() { await (Task)null; }
Diagnostic(ErrorCode.ERR_BadAsyncReturn, "f").WithLocation(6, 18),
// (6,18): error CS0161: 'C.f()': not all code paths return a value
// async MyTask f() { await (Task)null; }
Diagnostic(ErrorCode.ERR_ReturnExpected, "f").WithArguments("C.f()").WithLocation(6, 18)
);
var source2 = @"
using System.Threading.Tasks;
class C
{
static void Main() { }
async MyTask f() { await (Task)null; }
}
public class MyTask { }
";
CreateCompilationWithMscorlib461(source2).VerifyDiagnostics(
// (6,18): error CS1983: The return type of an async method must be void, Task or Task<T>
// async MyTask f() { await (Task)null; }
Diagnostic(ErrorCode.ERR_BadAsyncReturn, "f").WithLocation(6, 18),
// (6,18): error CS0161: 'C.f()': not all code paths return a value
// async MyTask f() { await (Task)null; }
Diagnostic(ErrorCode.ERR_ReturnExpected, "f").WithArguments("C.f()").WithLocation(6, 18)
);
var source3 = @"
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
class C
{
static void Main() { }
async MyTask f() { await (Task)null; }
}
[AsyncMethodBuilder(typeof(MyTaskBuilder))]
public class MyTask { }
public class MyTaskBuilder
{
public static MyTaskBuilder Create() => null;
}
namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } }
";
CreateCompilationWithMscorlib461(source3).VerifyDiagnostics();
}
[Fact]
public void AsyncTasklikeOverloadLambdas()
{
var source = @"
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
class C {
static void Main() {
f(async () => { await (Task)null; return 1; });
h(async () => { await (Task)null; });
}
static void f<T>(Func<MyTask<T>> lambda) { }
static void f<T>(Func<T> lambda) { }
static void f<T>(T arg) { }
static void h(Func<MyTask> lambda) { }
static void h(Func<Task> lambda) { }
}
[AsyncMethodBuilder(typeof(MyTaskBuilder<>))]
public class MyTask<T> { }
public class MyTaskBuilder<T> {
public static MyTaskBuilder<T> Create() => null;
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { }
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
public void SetResult(T result) { }
public void SetException(Exception exception) { }
public MyTask<T> Task => default(MyTask<T>);
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { }
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { }
}
[AsyncMethodBuilder(typeof(MyTaskBuilder))]
public class MyTask { }
public class MyTaskBuilder
{
public static MyTaskBuilder Create() => null;
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { }
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
public void SetResult() { }
public void SetException(Exception exception) { }
public MyTask Task => default(MyTask);
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { }
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine { }
}
namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } }
";
CreateCompilationWithMscorlib461(source).VerifyDiagnostics(
// (8,9): error CS0121: The call is ambiguous between the following methods or properties: 'C.h(Func<MyTask>)' and 'C.h(Func<Task>)'
// h(async () => { await (Task)null; });
Diagnostic(ErrorCode.ERR_AmbigCall, "h").WithArguments("C.h(System.Func<MyTask>)", "C.h(System.Func<System.Threading.Tasks.Task>)").WithLocation(8, 9)
);
}
[Fact]
public void AsyncTasklikeInadmissibleArity()
{
var source = @"
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
class C {
async Mismatch2<int,int> g() { await Task.Delay(0); return 1; }
}
[AsyncMethodBuilder(typeof(Mismatch2MethodBuilder<>))]
struct Mismatch2<T,U> { }
class Mismatch2MethodBuilder<T> {}
namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } }
";
var comp = CreateCompilationWithMscorlib461(source);
comp.VerifyEmitDiagnostics(
// (5,30): error CS1983: The return type of an async method must be void, Task or Task<T>
// async Mismatch2<int,int> g() { await Task.Delay(0); return 1; }
Diagnostic(ErrorCode.ERR_BadAsyncReturn, "g").WithLocation(5, 30)
);
}
[Fact]
public void AsyncTasklikeOverloadInvestigations()
{
var source = @"
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
class Program
{
static Task<int> arg1 = null;
static int arg2 = 0;
static int f1(MyTask<int> t) => 0;
static int f1(Task<int> t) => 1;
static int r1 = f1(arg1); // 1
static void Main()
{
Console.Write(r1);
}
}
[AsyncMethodBuilder(typeof(ValueTaskBuilder<>))]
class ValueTask<T>
{
public static implicit operator ValueTask<T>(Task<T> task) => null;
}
[AsyncMethodBuilder(typeof(MyTaskBuilder<>))]
class MyTask<T>
{
public static implicit operator MyTask<T>(Task<T> task) => null;
public static implicit operator Task<T>(MyTask<T> mytask) => null;
}
class ValueTaskBuilder<T>
{
public static ValueTaskBuilder<T> Create() => null;
public void Start<TSM>(ref TSM sm) where TSM : IAsyncStateMachine { }
public void SetStateMachine(IAsyncStateMachine sm) { }
public void SetResult(T r) { }
public void SetException(Exception ex) { }
public ValueTask<T> Task => null;
public void AwaitOnCompleted<TA, TSM>(ref TA a, ref TSM sm) where TA : INotifyCompletion where TSM : IAsyncStateMachine { }
public void AwaitUnsafeOnCompleted<TA, TSM>(ref TA a, ref TSM sm) where TA : ICriticalNotifyCompletion where TSM : IAsyncStateMachine { }
}
class MyTaskBuilder<T>
{
public static MyTaskBuilder<T> Create() => null;
public void Start<TSM>(ref TSM sm) where TSM : IAsyncStateMachine { }
public void SetStateMachine(IAsyncStateMachine sm) { }
public void SetResult(T r) { }
public void SetException(Exception ex) { }
public ValueTask<T> Task => null;
public void AwaitOnCompleted<TA, TSM>(ref TA a, ref TSM sm) where TA : INotifyCompletion where TSM : IAsyncStateMachine { }
public void AwaitUnsafeOnCompleted<TA, TSM>(ref TA a, ref TSM sm) where TA : ICriticalNotifyCompletion where TSM : IAsyncStateMachine { }
}
namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } }
";
CompileAndVerify(source, targetFramework: TargetFramework.Empty, references: new[] { MscorlibRef_v4_0_30316_17626 }, expectedOutput: "1");
}
[Fact]
public void AsyncTasklikeBetterness()
{
var source = @"
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
class Program
{
static char f1(Func<ValueTask<short>> lambda) => 's';
static char f1(Func<Task<byte>> lambda) => 'b';
static char f2(Func<Task<short>> lambda) => 's';
static char f2(Func<ValueTask<byte>> lambda) => 'b';
static char f3(Func<Task<short>> lambda) => 's';
static char f3(Func<Task<byte>> lambda) => 'b';
static char f4(Func<ValueTask<short>> lambda) => 's';
static char f4(Func<ValueTask<byte>> lambda) => 'b';
static void Main()
{
Console.Write(f1(async () => { await (Task)null; return 9; }));
Console.Write(f2(async () => { await (Task)null; return 9; }));
Console.Write(f3(async () => { await (Task)null; return 9; }));
Console.Write(f4(async () => { await (Task)null; return 9; }));
}
}
[AsyncMethodBuilder(typeof(ValueTaskBuilder<>))]
public class ValueTask<T> { }
public class ValueTaskBuilder<T>
{
public static ValueTaskBuilder<T> Create() => null;
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
public void Start<TSM>(ref TSM stateMachine) where TSM : IAsyncStateMachine { }
public void AwaitOnCompleted<TA, TSM>(ref TA awaiter, ref TSM stateMachine) where TA : INotifyCompletion where TSM : IAsyncStateMachine { }
public void AwaitUnsafeOnCompleted<TA, TSM>(ref TA awaiter, ref TSM stateMachine) where TA : ICriticalNotifyCompletion where TSM : IAsyncStateMachine { }
public void SetResult(T result) { }
public void SetException(Exception ex) { }
public ValueTask<T> Task => null;
}
namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } }
";
CompileAndVerify(source, targetFramework: TargetFramework.Empty, references: new[] { MscorlibRef_v4_0_30316_17626 }, expectedOutput: "bbbb");
}
}
}
|