csharplang icon indicating copy to clipboard operation
csharplang copied to clipboard

Champion "Improved overload candidates" (15.7)

Open gafter opened this issue 8 years ago • 18 comments

  • [ ] 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:

  1. 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 no this is defined, such as static members, as well as places where this cannot be used, such as field initializers and constructor-initializers.
  2. When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
  3. 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.

gafter avatar Feb 14 '17 23:02 gafter

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 avatar May 06 '17 03:05 gafter

@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 avatar Oct 25 '17 20:10 jcouv

@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"?

gafter avatar Oct 26 '17 03:10 gafter

This likely deserves a whole other discussion, but: overload resolution based on generic constraints would be so wonderful.

scalablecory avatar Oct 26 '17 04:10 scalablecory

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

gafter avatar Feb 08 '18 06:02 gafter

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

alrz avatar May 25 '18 11:05 alrz

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) { }

Kryptos-FR avatar Jun 29 '18 06:06 Kryptos-FR

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.

HaloFour avatar Jun 29 '18 11:06 HaloFour

@Kryptos-FR No, there are no such plans.

gafter avatar Jun 29 '18 16:06 gafter

@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 avatar Jul 02 '18 00:07 Kryptos-FR

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

HaloFour avatar Jul 02 '18 00:07 HaloFour

@richard-fine It has been shipped in C# 7.3, as you can see here, which comes with Visual Studio 15.7.

Logerfo avatar Oct 02 '18 12:10 Logerfo

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

gafter avatar Oct 03 '18 21:10 gafter

Any news about that?

tcfialho avatar Feb 27 '21 04:02 tcfialho

This has been implemented since C# 7.3.

333fred avatar Feb 27 '21 05:02 333fred

@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 avatar May 14 '21 18:05 jcouv

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

las-nsc avatar May 14 '21 19:05 las-nsc

Standard spec in progress at https://github.com/dotnet/csharpstandard/pull/263

gafter avatar May 13 '22 23:05 gafter