Skip to content
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

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

Open
jcouv opened this issue Nov 12, 2024 · 0 comments

Comments

@jcouv
Copy link
Member

jcouv commented Nov 12, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant