This code is not compiled:
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
public static Bar<TOut> Do<TOut>(Func<Bar<TOut>> f) => null;
}
public class Bar
{
}
public class Bar<TOut>
{
public static implicit operator Bar<TOut>(TOut i) => null;
}
// Here compiler complains:
// CS0029 Cannot implicitly convert type 'int' to 'Bar'
// CS1662 Cannot convert lambda expression to intended delegate type
// because some of the return types in the block
// are not implicitly convertible to the delegate return type
Foo.Do(() => 1);
My expectation would be that compiler sees the return type of the lambda and that no valid overload can be selected unless int is converted to Bar<int>.
However, I see that compiler resolves to the first method.
Which part of spec defines this behavior?
CodePudding user response:
This is specified in Method Invocations, when the spec is talking about what method declarations count as a candidate for overload resolution, for an invocation of the form M(A):
The set of candidate methods for the method invocation is constructed. For each method
Fassociated with the method groupM:
- If
Fis non-generic,Fis a candidate when:
Mhas no type argument list, andFis applicable with respect toA.- If
Fis generic andMhas no type argument list,Fis a candidate when:
- Type inference succeeds, inferring a list of type arguments for the call, and
- [...]
Just from those rules, we can see that the non-generic Do is a candidate, and the generic Do is not, because type inference fails. Try commenting out the non-generic Do, and you will see that it says something like "type arguments cannot be inferred".
CodePudding user response:
I do not have the full answer, but I have some observations:
Observation 1: With removing the non-generic version of Do:
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
}
public class Bar
{
public static implicit operator Bar(int i) => null;
}
// CS0411: The type arguments for method 'Foo.Do<TOut>(Func<Bar<TOut>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Foo.Do(() => 1);
, still the compiler can't resolve Func<Bar<TOut>> f. So it does not seem that the issue is about picking the wrong overload, but rather compiler not being able to match () => 1 to Func<Bar<TOut>> f implicitly at all.
Observation 2: The code below works
static class Foo
{
public static Bar Do(Func<Bar> f) => null;
}
public class Bar
{
public static implicit operator Bar(int i) => null;
}
Foo.Do(() => 1);
which shows the compiler is checking implicit conversions.
Observation 3: Making the implicit cast operator to take int as in input, eventhough makes the operator not practically usable as TOut will not be resolved, makes the compiler find the operator:
static class Foo
{
public static Bar<TOut> Do<TOut>(Func<Bar<TOut>> f) => null;
}
public class Bar<TOut>
{
public static implicit operator Bar<TOut>(int i) => null; // Input is `int` now
}
// CS0411: The type arguments for method 'Foo.Do<TOut>(Func<Bar<TOut>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Foo.Do(() => 1);
So all of this combined makes me think that the compiler is just not trying to which generic types would cause the implicit conversion to work.
