csharplang
csharplang copied to clipboard
Champion "Improved overload candidates" (15.7)
- [ ] Proposal added
- [x] Discussed in LDM
- [ ] Decision in LDM
- [ ] Finalized (done, rejected, inactive)
- [ ] Spec'ed
see https://github.com/dotnet/roslyn/issues/250
The overload resolution rules have been updated in nearly every C# language update to improve the experience for programmers, making ambiguous invocations select the "obvious" choice. This has to be done carefully to preserve backward compatibility, but since we are usually resolving what would otherwise be error cases, these enhancements usually work out nicely.
There are three cases we might consider improving in C# 7:
- When a method group contains both instance and static members, we discard the instance members if invoked without an instance receiver or context, and discard the static members if invoked with an instance receiver. When there is no receiver, we include only static members in a static context, otherwise both static and instance members. When the receiver is ambiguously an instance or type due to a color-color situation, we include both. A static context, where an implicit
this
instance receiver cannot be used, includes the body of members where nothis
is defined, such as static members, as well as places wherethis
cannot be used, such as field initializers and constructor-initializers. - When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
- For a method group conversion, candidate methods whose return type doesn't match up with the delegate's return type are removed from the set.
There are probably additional improvements we could consider.
We should also look whether we should do something similar for the lambda conversion (say the conversion doesn't exist when the return type is wrong). See https://github.com/dotnet/roslyn/issues/8942
@gafter Per LDM today, since it not technically a change to overload resolution, but rather the steps before (weeding out candidates) and after (removing any redundant logic to final verification). Should we rename the issue?
@jcouv I think keeping the name stable helps us recall what we were talking about. But I don't feel strongly about it one way or another. Do you have a suggested name? "Better method candidate selection"?
This likely deserves a whole other discussion, but: overload resolution based on generic constraints would be so wonderful.
@scalablecory This would have the side-effect of permitting overload resolution based on generic constraints. You would still have to ensure that the method signatures are different enough that they would not clash in IL.
FYI, I'm implementing this now.
the "smallish feature" tag on this blows my mind. there should be a sign next to anything related to overload resolution that reads "KEEP OUT".
Is there any plan to change the behavior describe here: https://github.com/dotnet/roslyn/issues/250#issuecomment-73037092
Resolving the overload with the float
overload seems wrong, unless the reason was that sizeof(float)
and sizeof(int)
are both 4 bytes. But in that case, I would expect the resolution with long
to be with the double
overload. Unfortunately, it still resolves to the float
overload.
Things get weirder when a third overload with decimal
comes into play: now we have overload ambiguity. One can wonder why we didn't have it with float
and double
in the first place.
As soon as a decimal
overload exists, it is always ambiguous, even if we remove either float
or 1decimal` overload. What is the rational with that? IMHO the rule should change to always be ambiguous and rejected by the compiler, so that the developer has to cast the value to the desired type. So many mistakes could be avoided that way.
void Test()
{
Method(char.MaxValue);
Method(byte.MaxValue);
Method(short.MaxValue);
Method(int.MaxValue);
Method(long.MaxValue);
Method(float.MaxValue);
}
void Method(float value) { } // all calls resolved to that overload
void Method(double value) { }
//void Method(decimal value) { }
Changing the behavior where the code currently compiles would be a breaking change and could adversely affect existing programs. IIRC all of the improvements above were to eliminate overload candidates in situations where the code would fail to compile due to an ambiguity but where some of the candidates would not be invocable.
@Kryptos-FR No, there are no such plans.
@HaloFour It could be an opt-in compilation switch to enforce "stricter" rules regarding overload resolution. In that mode the above examples would fail to compile with a meaningful error message but without that switch (default behavior) it will compile as usual. At the very least the Roslyn analyzers should be updated to display a warning in such cases.
@Kryptos-FR
It could be an opt-in compilation switch
That would create a dialect in the language which the team would be very unlikely to consider.
@richard-fine It has been shipped in C# 7.3, as you can see here, which comes with Visual Studio 15.7.
The language spec is stuck at version 6 while we work with ECMA to plan how we're going to evolve it going forward. Having said that, you can find specs of varying degrees of precision for recently shipped features
Any news about that?
This has been implemented since C# 7.3.
@Nintynuts https://github.com/dotnet/roslyn/issues/26077 was closed because it doesn't repro as of C# 7.3. Please file a bug with a code snippet/repro if you think this feature was not implemented as spec'ed in C# 7.3.
@jcouv I tried to build an example to demonstrate and couldn't reproduce the issue. It turned out that a nuget reference to Microsoft.Net.Compilers
was causing the issue.
Standard spec in progress at https://github.com/dotnet/csharpstandard/pull/263