Extensions: AssociatedExtensionImplementation on constructed symbols#80423
Extensions: AssociatedExtensionImplementation on constructed symbols#80423jcouv merged 4 commits intodotnet:mainfrom
Conversation
|
|
||
| if (_underlying.IsDefinition) | ||
| { | ||
| return implDefinition.GetPublicSymbol(); |
| if (extensionMember is IMethodSymbol { OriginalDefinition.AssociatedExtensionImplementation: { } toShadow }) | ||
| { | ||
| implementationsToHide.Add(toShadow); | ||
| implementationsToHide.Add(toShadow.OriginalDefinition); |
There was a problem hiding this comment.
The previous behavior of the API was to associate a definition with a definition, but now it associates a constructed symbol with a definition.
I'm not sure that's desirable in terms of API design. Maybe it would be better to keep the restriction that the API only works on definitions.
There was a problem hiding this comment.
Given that I didn't hear back from API review on this question, I'm assuming we're proceeding with design as-is. I re-opened the PR for review. @jjonescz Please take a look
| var memberAccess = GetSyntax<MemberAccessExpressionSyntax>(tree, "t.M"); | ||
| var method = (IMethodSymbol)model.GetSymbolInfo(memberAccess).Symbol; | ||
| Assert.True(method.IsDefinition); | ||
| var associated = method.AssociatedExtensionImplementation; |
There was a problem hiding this comment.
If we have a definition, why couldn't its AssociatedExtensionImplementation return definition too? The API review says it should be consistent with AssociatedSymbol and isn't that what it would do?
There was a problem hiding this comment.
Unfortunately, the analogy with AssociatedSymbol breaks down because the situation is different enough.
For AssociatedSymbol, if we have an accessor in a generic type, the associated symbol (ie. the property) will be in the same generic type, with the same substitution. So there's not a situation where the symbols define different type parameters. We can associate C<T>.get_P() with C<T>.P, or C<int>.get_P() with C<int>.P without any issue.
But for generic extension members, the two symbols differ in type parameters.
If the extension method is E.extension<T>(T t).M(), the implementation method is E.M<T'>(T' t) (the prime is added to highlight that they are different type parameters).
If the behavior of the API were to associate E.M<T'>(T' t) (definition) with E.extension<T>(T t).M() (definition), that would be irregular. It is unclear how we would justify it (was it some renaming, some substitution, ...)?
Considering more than one type parameter, what would be expected if you had a partial substitution like E2.M<T1', int>(). Would it associate with E2.extension<T1, int>().M() or E2.extension<T1', int>().M()?
Added you to the thread with API review
There was a problem hiding this comment.
If the behavior of the API were to associate
E.M<T'>(T' t)(definition) withE.extension<T>(T t).M()(definition), that would be irregular.
I see, makes sense, thanks. (I was already in the API review email thread, but this reasoning wasn't clear from the email to me.)
Considering more than one type parameter, what would be expected if you had a partial substitution like
E2.M<T1', int>(). Would it associate withE2.extension<T1, int>().M()orE2.extension<T1', int>().M()?
Do we have a test for that?
There was a problem hiding this comment.
Do we have a test for that?
I'll add one in next PR. I'd like to get this in and get it considered for backport to 18.0 since affects behavior of public API
There was a problem hiding this comment.
I do have a test for partial construction. I'll strengthen it to make it more apparent what type parameter is used.
[Fact]
public void AssociatedExtensionImplementation_08()
{
// not a definition, generic extension and method, partially constructed with type parameters
var src = """
public static class E
{
extension<T>(T t)
{
public void M<U, V>(U u, V v)
{
t.M(42L, "");
}
}
}
""";
var comp = CreateCompilation(src);
comp.VerifyEmitDiagnostics();
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var memberAccess = GetSyntax<MemberAccessExpressionSyntax>(tree, "t.M");
var method = (IMethodSymbol)model.GetSymbolInfo(memberAccess).Symbol;
AssertEx.Equal("void E.M<T, System.Int64, System.String>(this T t, System.Int64 u, System.String v)",
method.AssociatedExtensionImplementation.ToTestDisplayString());
}
|
/backport to release/dev18.0 |
|
Started backporting to release/dev18.0: https://github.com/dotnet/roslyn/actions/runs/18007170510 |
Addresses part of #78957 ("AssociatedExtensionImplementation should work on constructed symbols (currently only definitions)")
Closes #80167
Relates to test plan #76130