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
F
associated with the method groupM
:
- If
F
is non-generic,F
is a candidate when:
M
has no type argument list, andF
is applicable with respect toA
.- If
F
is generic andM
has no type argument list,F
is 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.