-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"Arbitrary async returns" should use attribute #13405
Changes from all commits
b82a3f9
1c806ae
abc0efd
6ecd515
424da4a
416cf98
1d6580a
3d82d1a
8b8d09d
d7076e8
62a472f
01eafc2
0ddec05
bcbd12f
33e65fb
c9633b0
eef0c70
0e30e90
f2cdba8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1273,9 +1273,8 @@ internal static bool IsNonGenericTaskType(this TypeSymbol type, CSharpCompilatio | |
{ | ||
return false; | ||
} | ||
NamedTypeSymbol builderType; | ||
MethodSymbol createBuilderMethod; | ||
return namedType.IsCustomTaskType(out builderType, out createBuilderMethod); | ||
object builderArgument; | ||
return namedType.IsCustomTaskType(out builderArgument); | ||
} | ||
|
||
internal static bool IsGenericTaskType(this TypeSymbol type, CSharpCompilation compilation) | ||
|
@@ -1289,51 +1288,42 @@ internal static bool IsGenericTaskType(this TypeSymbol type, CSharpCompilation c | |
{ | ||
return true; | ||
} | ||
NamedTypeSymbol builderType; | ||
MethodSymbol createBuilderMethod; | ||
return namedType.IsCustomTaskType(out builderType, out createBuilderMethod); | ||
object builderArgument; | ||
return namedType.IsCustomTaskType(out builderArgument); | ||
} | ||
|
||
/// <summary> | ||
/// Returns true if the type is generic or non-generic task-like type. If so, the async | ||
/// method builder type is returned along with the method to construct that type. | ||
/// Returns true if the type is generic or non-generic custom task-like type due to the | ||
/// [AsyncBuilder(typeof(B))] attribute. It returns the "B". | ||
/// </summary> | ||
internal static bool IsCustomTaskType(this NamedTypeSymbol type, out NamedTypeSymbol builderType, out MethodSymbol createBuilderMethod) | ||
/// <remarks> | ||
/// For the Task types themselves, this method might return true or false depending on mscorlib. | ||
/// The definition of "custom task-like type" is one that has an [AsyncBuilder(typeof(B))] attribute, | ||
/// no more, no less. Validation of builder type B is left for elsewhere. This method returns B | ||
/// without validation of any kind. | ||
/// </remarks> | ||
internal static bool IsCustomTaskType(this NamedTypeSymbol type, out object builderArgument) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went back and forth on this. If you write Now I said that I'm looking specifically for the So if anyone wants to report errors on what the argument was, then returning it just as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not? :) I figure it could have gone either way. There is a spectrum of possibilities...
I picked the first option because (A) the extremes seem more defensible than the middle grounds, (B) it felt a touch more efficient because places like overload resolution and type inference which might potentially be used a lot only do the minimal checking, (C) I think it's a better user experience while you're developing your custom tasklike if it remains tasklike even while you're in the process of editing+fixing the code of your builder. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should allow And is returning Returning an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like using extension methods when the target type is defined in the same assembly and namespace. It's just harder to find all the places where applicable extension methods are defined. Especially since this method is not even defined in Not sure what you mean by public API -- If the method was implemented using the true public API, e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure where to go with this...
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I vote for an instance method on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have very few methods that operate on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case, "leave as is" :) |
||
{ | ||
Debug.Assert((object)type != null); | ||
Debug.Assert(type.SpecialType != SpecialType.System_Void); | ||
|
||
var arity = type.Arity; | ||
if (arity < 2) | ||
{ | ||
// Find the public static CreateAsyncMethodBuilder method. | ||
var members = type.GetMembers(WellKnownMemberNames.CreateAsyncMethodBuilder); | ||
foreach (var member in members) | ||
// Find the AsyncBuilder attribute. | ||
foreach (var attr in type.GetAttributes()) | ||
{ | ||
if (member.Kind != SymbolKind.Method) | ||
{ | ||
continue; | ||
} | ||
var method = (MethodSymbol)member; | ||
if ((method.DeclaredAccessibility == Accessibility.Public) && | ||
method.IsStatic && | ||
(method.ParameterCount == 0) && | ||
!method.IsGenericMethod) | ||
if (attr.IsTargetAttribute(type, AttributeDescription.AsyncBuilderAttribute) | ||
&& attr.CommonConstructorArguments.Length == 1 | ||
&& attr.CommonConstructorArguments[0].Kind == TypedConstantKind.Type) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we testing constructors with multiple arguments or single argument but not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added unit tests in 33e65fb |
||
{ | ||
var returnType = method.ReturnType as NamedTypeSymbol; | ||
if ((object)returnType == null || returnType.Arity != arity) | ||
{ | ||
break; | ||
} | ||
builderType = returnType; | ||
createBuilderMethod = method; | ||
builderArgument = attr.CommonConstructorArguments[0].Value; | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
builderType = null; | ||
createBuilderMethod = null; | ||
builderArgument = null; | ||
return false; | ||
} | ||
|
||
|
@@ -1412,9 +1402,8 @@ private static bool NormalizeTaskTypesInNamedType(CSharpCompilation compilation, | |
typeArgumentsBuilder.Free(); | ||
} | ||
|
||
NamedTypeSymbol builderType; | ||
MethodSymbol createBuilderMethod; | ||
if (type.OriginalDefinition.IsCustomTaskType(out builderType, out createBuilderMethod)) | ||
object builderArgument; | ||
if (type.OriginalDefinition.IsCustomTaskType(out builderArgument)) | ||
{ | ||
int arity = type.Arity; | ||
Debug.Assert(arity < 2); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider changing
desiredArity
tobool
and remove this case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps inline
isArityOk
checks in theif
. It might be slightly harder to read but would require fewer checks ofbuilderType != null
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in f2cdba8#diff-995d1bf33206eef8c980dbd08b8601b0R267 - I changed to bool, and streamlined the number of null checks.