From f2cdba83c83d34ddac4875def41674d352fc5b7e Mon Sep 17 00:00:00 2001 From: Lucian Wischik Date: Thu, 1 Sep 2016 12:13:05 -0700 Subject: [PATCH] Minor tweaks. Made ValidateBuilderType method a bit simpler+cleaner. Added more unit tests for bad builder, and for interface tasklike. --- .../AsyncMethodBuilderMemberCollection.cs | 51 +++----- .../Test/Emit/CodeGen/CodeGenAsyncTests.cs | 113 ++++++++++++++---- 2 files changed, 108 insertions(+), 56 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs index 155e9ed445b54..716d28c878943 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs @@ -144,7 +144,7 @@ internal static bool TryCreate(SyntheticBoundNodeFactory F, MethodSymbol method, bool customBuilder = returnType.IsCustomTaskType(out builderArgument); if (customBuilder) { - builderType = ValidateBuilderType(F, builderArgument, returnType.DeclaredAccessibility, desiredArity:0); + builderType = ValidateBuilderType(F, builderArgument, returnType.DeclaredAccessibility, isGeneric:false); if ((object)builderType != null) { taskProperty = GetCustomTaskProperty(F, builderType); @@ -204,7 +204,7 @@ internal static bool TryCreate(SyntheticBoundNodeFactory F, MethodSymbol method, resultType = typeMap.SubstituteType(resultType).Type; } returnType = returnType.ConstructedFrom.Construct(resultType); - NamedTypeSymbol builderType = null; + NamedTypeSymbol builderType; MethodSymbol createBuilderMethod = null; PropertySymbol taskProperty = null; @@ -212,7 +212,7 @@ internal static bool TryCreate(SyntheticBoundNodeFactory F, MethodSymbol method, bool customBuilder = returnType.IsCustomTaskType(out builderArgument); if (customBuilder) { - builderType = ValidateBuilderType(F, builderArgument, returnType.DeclaredAccessibility, 1); + builderType = ValidateBuilderType(F, builderArgument, returnType.DeclaredAccessibility, isGeneric:true); if ((object)builderType != null) { builderType = builderType.ConstructedFrom.Construct(resultType); @@ -264,40 +264,26 @@ internal static bool TryCreate(SyntheticBoundNodeFactory F, MethodSymbol method, throw ExceptionUtilities.UnexpectedValue(method); } - private static NamedTypeSymbol ValidateBuilderType(SyntheticBoundNodeFactory F, object builderAttributeArgument, Accessibility desiredAccessibility, int desiredArity) + private static NamedTypeSymbol ValidateBuilderType(SyntheticBoundNodeFactory F, object builderAttributeArgument, Accessibility desiredAccessibility, bool isGeneric) { var builderType = builderAttributeArgument as NamedTypeSymbol; - bool isArityOk; - if (desiredArity == 0) - { - isArityOk = builderType?.IsGenericType == false; - } - else if (desiredArity == 1) - { - isArityOk = builderType?.IsUnboundGenericType == true && - builderType?.ContainingType?.IsGenericType != true; - } - else + if ((object)builderType != null && + !builderType.IsErrorType() && + builderType.SpecialType != SpecialType.System_Void && + builderType.DeclaredAccessibility == desiredAccessibility) { - isArityOk = false; - Debug.Assert(false, "expected arity 0 or 1"); + bool isArityOk = isGeneric + ? builderType.IsUnboundGenericType && builderType.ContainingType?.IsGenericType != true + : !builderType.IsGenericType; + if (isArityOk) + { + return builderType; + } } - - if ((object)builderType == null || - builderType.IsErrorType() == true || - builderType.SpecialType == SpecialType.System_Void || - builderType.DeclaredAccessibility != desiredAccessibility || - !isArityOk) - { - F.Diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, F.Syntax.Location); - return null; - } - else - { - return builderType; - } + F.Diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, F.Syntax.Location); + return null; } private static bool TryCreate( @@ -397,9 +383,6 @@ private static MethodSymbol GetCustomCreateMethod( { // The Create method's return type is expected to be builderType. // The WellKnownMembers routines aren't able to enforce that, which is why this method exists. - // - // TODO: consider removing WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder*__Create - // and using GetCustomCreateMethod for the Task types as well as for custom tasklikes. const string methodName = "Create"; var members = builderType.GetMembers(methodName); foreach (var member in members) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 5b683e0954046..38c430c421a80 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -3216,13 +3216,13 @@ class C Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> 3").WithArguments("System.Runtime.CompilerServices.IAsyncStateMachine", "SetStateMachine").WithLocation(4, 25)); } - public string AsyncBuilderCode(string builderTypeName, string tasklikeTypeName, string genericTypeParameter = null, bool isStruct = false) + private static string AsyncBuilderCode(string builderTypeName, string tasklikeTypeName, string genericTypeParameter = null, bool isStruct = false) { string ofT = genericTypeParameter == null ? "" : "<" + genericTypeParameter + ">"; return $@" public {(isStruct ? "struct" : "class")} {builderTypeName}{ofT} {{ - public static {builderTypeName} Create() => default({builderTypeName}{ofT}); + public static {builderTypeName}{ofT} Create() => default({builderTypeName}{ofT}); public {tasklikeTypeName}{ofT} Task {{ get; }} public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine {{ }} public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine {{ }} @@ -3504,6 +3504,30 @@ namespace System.Runtime.CompilerServices { class AsyncBuilderAttribute : System ); } + public void AsyncTasklikeBadAttributeArgument4() + { + var source = @" +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +[AsyncBuilder(null)] class T { } + +class Program { + static void Main() { } + async T f() => await Task.Delay(1); +} + +namespace System.Runtime.CompilerServices { class AsyncBuilderAttribute : System.Attribute { public AsyncBuilderAttribute(System.Type t) { } } } +"; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( + // (9,17): error CS1983: The return type of an async method must be void, Task or Task + // async T f() => await Task.Delay(1); + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.Delay(1)").WithLocation(9, 17) + ); + } + [Fact] public void AsyncTasklikeMissingBuilderType() { @@ -3548,12 +3572,28 @@ public void AsyncTasklikeCreateMethod() using System.Runtime.CompilerServices; using System.Threading.Tasks; +class Program {{ + static void Main() {{ }} + async T0 f0() => await Task.Delay(0); + async T1 f1() => await Task.Delay(1); + async T2 f2() => await Task.Delay(2); + async T3 f3() => await Task.Delay(3); + async T4 f4() => await Task.Delay(4); + async T5 f5() => await Task.Delay(5); + async T6 f6() => await Task.Delay(6); + async T7 f7() => await Task.Delay(7); + async T8 f8() => await Task.Delay(8); +}} + [AsyncBuilder(typeof(B0))] public class T0 {{ }} [AsyncBuilder(typeof(B1))] public class T1 {{ }} [AsyncBuilder(typeof(B2))] public class T2 {{ }} [AsyncBuilder(typeof(B3))] public class T3 {{ }} [AsyncBuilder(typeof(B4))] public class T4 {{ }} [AsyncBuilder(typeof(B5))] public class T5 {{ }} +[AsyncBuilder(typeof(B6))] public class T6 {{ }} +[AsyncBuilder(typeof(B7))] public class T7 {{ }} +[AsyncBuilder(typeof(B8))] public class T8 {{ }} {AsyncBuilderCode("B0", "T0").Replace("public static B0 Create()", "public static B0 Create()")} {AsyncBuilderCode("B1", "T1").Replace("public static B1 Create()", "private static B1 Create()")} @@ -3561,37 +3601,66 @@ public void AsyncTasklikeCreateMethod() {AsyncBuilderCode("B3", "T3").Replace("public static B3 Create() => default(B3);", "public static B1 Create() => default(B1);")} {AsyncBuilderCode("B4", "T4").Replace("public static B4 Create()", "public static B4 Create(int i)")} {AsyncBuilderCode("B5", "T5").Replace("public static B5 Create()", "public static B5 Create()")} - -class Program {{ - static void Main() {{ }} - async T0 f0() => await Task.Delay(0); - async T1 f1() => await Task.Delay(1); - async T2 f2() => await Task.Delay(2); - async T3 f3() => await Task.Delay(3); - async T4 f4() => await Task.Delay(4); - async T5 f5() => await Task.Delay(5); -}} +{AsyncBuilderCode("B6", "T6").Replace("public static B6 Create()", "public static B6 Create(object arg = null)")} +{AsyncBuilderCode("B7", "T7").Replace("public static B7 Create()", "public static B7 Create(params object[] arg)")} +{AsyncBuilderCode("B8", "T8").Replace("public static B8 Create()", "public B8 Create()")} namespace System.Runtime.CompilerServices {{ class AsyncBuilderAttribute : System.Attribute {{ public AsyncBuilderAttribute(System.Type t) {{ }} }} }} "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (94,19): error CS0656: Missing compiler required member 'B1.Create' + // (8,19): error CS0656: Missing compiler required member 'B1.Create' // async T1 f1() => await Task.Delay(1); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(1)").WithArguments("B1", "Create").WithLocation(94, 19), - // (95,19): error CS0656: Missing compiler required member 'B2.Create' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(1)").WithArguments("B1", "Create").WithLocation(8, 19), + // (9,19): error CS0656: Missing compiler required member 'B2.Create' // async T2 f2() => await Task.Delay(2); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(2)").WithArguments("B2", "Create").WithLocation(95, 19), - // (96,19): error CS0656: Missing compiler required member 'B3.Create' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(2)").WithArguments("B2", "Create").WithLocation(9, 19), + // (10,19): error CS0656: Missing compiler required member 'B3.Create' // async T3 f3() => await Task.Delay(3); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(3)").WithArguments("B3", "Create").WithLocation(96, 19), - // (97,19): error CS0656: Missing compiler required member 'B4.Create' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(3)").WithArguments("B3", "Create").WithLocation(10, 19), + // (11,19): error CS0656: Missing compiler required member 'B4.Create' // async T4 f4() => await Task.Delay(4); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(4)").WithArguments("B4", "Create").WithLocation(97, 19), - // (98,19): error CS0656: Missing compiler required member 'B5.Create' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(4)").WithArguments("B4", "Create").WithLocation(11, 19), + // (12,19): error CS0656: Missing compiler required member 'B5.Create' // async T5 f5() => await Task.Delay(5); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(5)").WithArguments("B5", "Create").WithLocation(98, 19) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(5)").WithArguments("B5", "Create").WithLocation(12, 19), + // (13,19): error CS0656: Missing compiler required member 'B6.Create' + // async T6 f6() => await Task.Delay(6); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(6)").WithArguments("B6", "Create").WithLocation(13, 19), + // (14,19): error CS0656: Missing compiler required member 'B7.Create' + // async T7 f7() => await Task.Delay(7); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(7)").WithArguments("B7", "Create").WithLocation(14, 19), + // (15,19): error CS0656: Missing compiler required member 'B8.Create' + // async T8 f8() => await Task.Delay(8); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.Delay(8)").WithArguments("B8", "Create").WithLocation(15, 19) + ); + } + + [Fact] + public void AsyncInterfaceTasklike() + { + var source = $@" +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +class Program {{ + static void Main() {{ }} + async I0 f0() => await Task.Delay(0); + async I1 f1() {{ await Task.Delay(1); return 1; }} +}} + +[AsyncBuilder(typeof(B0))] public interface I0 {{ }} +[AsyncBuilder(typeof(B1<>))] public interface I1 {{ }} + +{AsyncBuilderCode("B0", "I0", genericTypeParameter: null)} +{AsyncBuilderCode("B1", "I1", genericTypeParameter: "T")} + +namespace System.Runtime.CompilerServices {{ class AsyncBuilderAttribute : System.Attribute {{ public AsyncBuilderAttribute(System.Type t) {{ }} }} }} +"; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( ); }