Skip to content

Commit

Permalink
Added lowering-time checks that all builder methods with generic type…
Browse files Browse the repository at this point in the history
… parameters have the appropriate constraints on them
  • Loading branch information
ljw1004 committed Apr 23, 2016
1 parent bd1a430 commit fc64b23
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down
77 changes: 77 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TSM>(ref TSM stateMachine) where TSM : I { }
public void AwaitOnCompleted<TA, TSM>(ref TA awaiter, ref TSM stateMachine) { }
public void AwaitUnsafeOnCompleted<TA, TSM>(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<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) { }
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]
Expand Down

0 comments on commit fc64b23

Please sign in to comment.