dotnet / dotnet/csharpstandard
How to explain extension method invocation when the `this` parameter involves type parameters?
- Dominant language
- C#
- Stars
- 815
- Forks
- 99
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 16
Description
There is a divergence between the standard and the roslyn implementation regarding [extension method invocation](https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#128103-extension-method-invocations) with generic extension methods.
I'm filing this as a spec bug (rather than an implementation bug) because it is unclear in the spec how scenarios with a type parameter in the `this` parameter should work.
Based on the spec, I would expect both extension invocation scenarios below to fail.
But I believe the intent is for the first one to succeed, at least. I'm not sure about the second one.
Here's the most relevant paragraph:
```md
An extension method `Cᵢ.Mₑ` is ***eligible*** if:
- `Cᵢ` is a non-generic, non-nested class
- The name of `Mₑ` is *identifier*
- `Mₑ` is accessible and applicable when applied to the arguments as a static method as shown above
- An implicit identity, reference or boxing conversion exists from *expr* to the type of the first parameter of `Mₑ`.
```
In this first scenario, the extension method should not be considered eligible because there is no conversion from `T` to `string`:
```
public class C
{
public void M(string s)
{
s.M();
}
}
public static class E
{
public static void M(this T t) { }
}
```
If the first scenario is expected to work (ie. the receiver contributes to method type inference for the extension method), then it raises the question of whether this second scenario should work too. If so, how do we expect subsequent arguments to also contribute to method type inference?
```
public class C
{
public void M(I i, out object o)
{
i.M(out o); // infers E.M
}
}
public static class E
{
public static T M(this I i, out T t) { t = default(T); return t; }
}
public interface I { }
```
FWIW, the roslyn compiler collects candidates from each scope and only checks for accessibility (see `CheckViability` call in `LookupExtensionMethodsInSingleBinder`) then once candidates are found we run overload resolution on them (see `MethodInvocationOverloadResolution` call in `BindExtensionMethod`).
Contributor guide
Assessment
This issue has not been assessed yet.