-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Skip user-defined conversions when checking extension method 'this' argument #19511
Conversation
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.
LGTM
da9be0a
to
567f3a0
Compare
@@ -54,67 +52,6 @@ public override Conversion GetMethodGroupConversion(BoundMethodGroup source, Typ | |||
return conversion; | |||
} | |||
|
|||
protected override Conversion GetImplicitTupleLiteralConversion(BoundTupleLiteral source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics) |
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.
Methods have moved to ConversionsBase
since the implementation did not depend on the derived class.
@@ -1340,14 +1391,15 @@ public static bool HasIdentityConversionToAny<T>(T type, ArrayBuilder<T> targetT | |||
public Conversion ConvertExtensionMethodThisArg(TypeSymbol parameterType, TypeSymbol thisType, ref HashSet<DiagnosticInfo> useSiteDiagnostics) | |||
{ | |||
Debug.Assert((object)thisType != null); | |||
var conversion = this.ClassifyImplicitConversionFromType(thisType, parameterType, ref useSiteDiagnostics); | |||
var conversion = this.ClassifyImplicitExtensionMethodThisArgConversion(null, thisType, parameterType, ref useSiteDiagnostics); | |||
return IsValidExtensionMethodThisArgConversion(conversion) ? conversion : Conversion.NoConversion; | |||
} | |||
|
|||
// Spec 7.6.5.2: "An extension method ... is eligible if ... [an] implicit identity, reference, | |||
// or boxing conversion exists from expr to the type of the first parameter" | |||
public static bool IsValidExtensionMethodThisArgConversion(Conversion conversion) |
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.
This method should be replaced by an assert.
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.
Yes. I did not notice your comment and was puzzled at why IsValidExtensionMethodThisArgConversion still needs to be called.
|
||
if (conversion.IsImplicit) |
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.
Implicit conversions are no longer generated for this
argument.
@dotnet/roslyn, @AlekseyTs please review. |
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.
LGTM
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.
LGTM
@MeiChin-Tsai for approval |
@dotnet-bot test windows_release_vs-integration_prtest please |
return Conversion.NoConversion; | ||
} | ||
|
||
// It should be possible to remove IsValidExtensionMethodThisArgConversion |
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.
File follow-up issue?
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.
Logged #19622
|
||
if ((object)sourceType != null) | ||
{ | ||
var tupleConversion = ClassifyTupleConversion( |
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.
I didn't quite follow. How do we know that we're looking at a tuple at this point?
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.
We don't know we have a tuple at this point but ClassifyTupleConversion
returns NoConversion
if either the source or destination is not a tuple. This follows the pattern used elsewhere in ConversionsBase
. See the call to ClassifyImplicitTupleConversion
in ClassifyStandardImplicitConversion
for instance.
TypeSymbol destination, | ||
ref HashSet<DiagnosticInfo> useSiteDiagnostics, | ||
ConversionKind kind, | ||
ClassifyConversionFromExpressionDelegate classifyConversion, |
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.
Would it be simpler to pass options, rather than a delegate? Seems like there are three cases: implicit, explicit and implicit-for-this-in-extension. Maybe two bools, or a small enum?
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.
Unfortunately the code didn't seem any clearer since it added a switch in each method and meant each method took two enum values, one for ConversionKind
and one for new enum.
I have changed the delegates to take an additional ConversionsBase
argument though, so that the delegate instances are static and cached.
TypeSymbol destination, | ||
ref HashSet<DiagnosticInfo> useSiteDiagnostics, | ||
ConversionKind kind, | ||
ClassifyConversionFromTypeDelegate classifyConversion, |
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.
Same question here.
Thanks for the factoring of common code, btw.
@@ -1986,6 +1986,138 @@ static void Main() | |||
Diagnostic(ErrorCode.ERR_NoImplicitConv, "0").WithArguments("int", "R")); | |||
} | |||
|
|||
[Fact] | |||
[WorkItem(434957, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=434957")] | |||
public void SkipUserDefinedConversionsForThisArgument() |
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.
Just to confirm: this test (and the following 2 or three) already produced errors (just different error code) before your change?
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.
Yes, these tests also (correctly) produced errors before this change. The tests made more sense with an earlier iteration of this change and are now likely redundant, and I considered removing them.
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.
LGTM
Please make sure all checks are successful. Do we want to add more tests given that the risk is medium? Approved since it is a regression. thx. |
@dotnet-bot retest windows_release_vs-integration_prtest please. |
@dotnet-bot retest windows_release_vs-integration_prtest please |
@MeiChin-Tsai, added tests for more conversion cases. |
@dotnet-bot retest Ubuntu_16_debug_prtest please |
Customer scenario
Invoke extension methods where the receiver type has user-defined conversion operators. This change affects performance only, not correctness.
Bugs this fixes:
434957
Workarounds, if any
None
Risk
Medium. The change avoids looking for user-defined conversion operators that were already being ignored by the caller for the
this
argument, although that argument conversion is now handled by a new specific method.Performance impact
Improves performance
Is this a regression from a previous update?
Yes, a regression from VS2013.
How was the bug found?
Customer reported