diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs index 18b3675cf21ad..f95c9c6d93afd 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs @@ -250,8 +250,14 @@ private static bool TryCreate( TryGetWellKnownMethodAsMember(F, awaitOnCompleted, builderType, requireWellKnownType, out awaitOnCompletedMethod) && TryGetWellKnownMethodAsMember(F, awaitUnsafeOnCompleted, builderType, requireWellKnownType, out awaitUnsafeOnCompletedMethod) && TryGetWellKnownMethodAsMember(F, start, builderType, requireWellKnownType, out startMethod) && - TryGetWellKnownMethodAsMember(F, setStateMachine, builderType, requireWellKnownType, out setStateMachineMethod)) + TryGetWellKnownMethodAsMember(F, setStateMachine, builderType, requireWellKnownType, out setStateMachineMethod) && + HasOnlyConstraint(F, startMethod.TypeParameters[0], WellKnownType.System_Runtime_CompilerServices_IAsyncStateMachine) && + HasOnlyConstraint(F, awaitOnCompletedMethod.TypeParameters[0], WellKnownType.System_Runtime_CompilerServices_INotifyCompletion) && + HasOnlyConstraint(F, awaitOnCompletedMethod.TypeParameters[1], WellKnownType.System_Runtime_CompilerServices_IAsyncStateMachine) && + HasOnlyConstraint(F, awaitUnsafeOnCompletedMethod.TypeParameters[0], WellKnownType.System_Runtime_CompilerServices_ICriticalNotifyCompletion) && + HasOnlyConstraint(F, awaitUnsafeOnCompletedMethod.TypeParameters[1], WellKnownType.System_Runtime_CompilerServices_IAsyncStateMachine)) { + collection = new AsyncMethodBuilderMemberCollection( builderType, resultType, @@ -270,6 +276,22 @@ private static bool TryCreate( return false; } + private static bool HasOnlyConstraint(SyntheticBoundNodeFactory F, TypeParameterSymbol typeParameter, WellKnownType constraint) + { + if (!typeParameter.HasConstructorConstraint && + !typeParameter.HasValueTypeConstraint && + !typeParameter.HasReferenceTypeConstraint && + typeParameter.ConstraintTypes.Length == 1 && + typeParameter.ConstraintTypes[0] == F.WellKnownType(constraint)) + { + return true; + } + + object[] args = new object[] { typeParameter.MetadataName, F.WellKnownType(constraint).MetadataName }; + CSDiagnostic error = new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, args), F.Syntax.Location, false); + throw new SyntheticBoundNodeFactory.MissingPredefinedMember(error); + } + private static bool TryGetWellKnownMethodAsMember(SyntheticBoundNodeFactory F, WellKnownMember wellKnownMethod, NamedTypeSymbol containingType, bool requireWellKnownType, out MethodSymbol methodSymbol) { if (requireWellKnownType) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 303e50931b3fa..8508a20fc96cf 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -3375,6 +3375,83 @@ namespace System.Runtime.CompilerServices { class TasklikeAttribute : Attribute ); } + [Fact] + public void AsyncTasklikeBuilderConstraints() + { + var source1 = @" +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +class C +{ + static void Main() { } + async MyTask f() { await (Task)null; } +} + +[Tasklike(typeof(MyTaskBuilder))] +class MyTask { } + +interface I { } + +class MyTaskBuilder +{ + public static MyTaskBuilder Create() => null; + public void SetStateMachine(IAsyncStateMachine stateMachine) { } + public void Start(ref TSM stateMachine) where TSM : I { } + public void AwaitOnCompleted(ref TA awaiter, ref TSM stateMachine) { } + public void AwaitUnsafeOnCompleted(ref TA awaiter, ref TSM stateMachine) { } + public void SetResult() { } + public void SetException(Exception ex) { } + public MyTask Task => null; +} + +namespace System.Runtime.CompilerServices { public class TasklikeAttribute : Attribute { public TasklikeAttribute(Type builder) { } } } +"; + + var comp1 = CreateCompilation(source1, options: TestOptions.DebugExe); + comp1.VerifyEmitDiagnostics( + // (8,22): error CS0656: Missing compiler required member 'TSM.IAsyncStateMachine' + // async MyTask f() { await (Task)null; } + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ await (Task)null; }").WithArguments("TSM", "IAsyncStateMachine").WithLocation(8, 22) + ); + + var source2 = @" +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +class C +{ + static void Main() { } + async MyTask f() { await (Task)null; } +} + +[Tasklike(typeof(MyTaskBuilder))] +class MyTask { } + +class MyTaskBuilder +{ + public static MyTaskBuilder Create() => null; + public void SetStateMachine(IAsyncStateMachine stateMachine) { } + public void Start(ref TSM stateMachine) where TSM : IAsyncStateMachine { } + public void AwaitOnCompleted(ref TA awaiter, ref TSM stateMachine) where TA : INotifyCompletion where TSM : IAsyncStateMachine { } + public void AwaitUnsafeOnCompleted(ref TA awaiter, ref TSM stateMachine) { } + public void SetResult() { } + public void SetException(Exception ex) { } + public MyTask Task => null; +} + +namespace System.Runtime.CompilerServices { public class TasklikeAttribute : Attribute { public TasklikeAttribute(Type builder) { } } } +"; + + var comp2 = CreateCompilation(source2, options: TestOptions.DebugExe); + comp2.VerifyEmitDiagnostics( + // (8,22): error CS0656: Missing compiler required member 'TA.ICriticalNotifyCompletion' + // async MyTask f() { await (Task)null; } + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ await (Task)null; }").WithArguments("TA", "ICriticalNotifyCompletion").WithLocation(8, 22) + ); + + } + [Fact]