csharpstandard icon indicating copy to clipboard operation
csharpstandard copied to clipboard

How to explain extension method invocation when the `this` parameter involves type parameters?

Open jcouv opened this issue 1 year ago • 1 comments

There is a divergence between the standard and the roslyn implementation regarding extension method invocation 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:

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<T>(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<string> i, out object o)
    {
        i.M(out o); // infers E.M<object>
    }
}
public static class E
{
   public static T M<T>(this I<T> i, out T t) { t = default(T); return t; }
}
public interface I<out T> { }

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).

jcouv avatar Nov 12 '24 22:11 jcouv

It looks like 'eligibility' must be defined in a way that doesn't depend on type inference, since type inference is part of overload resolution and 'eligibility' is being used to define the inputs to overload resolution.

If that fourth bullet were simply removed, would the spec come into alignment with the compiler? What would the motivation be to keep that bullet?

jnm2 avatar Feb 09 '25 20:02 jnm2