Why would adding a method add an ambiguous call, if it wouldn’t be involved in the ambiguity

Is this a bug?

Yes.

Congratulations, you have found a bug in overload resolution. The bug reproduces in C# 4 and 5; it does not reproduce in the “Roslyn” version of the semantic analyzer. I’ve informed the C# 5 test team, and hopefully we can get this investigated and resolved before the final release. (As always, no promises.)

A correct analysis follows. The candidates are:

0: C(params string[]) in its normal form
1: C(params string[]) in its expanded form
2: C<string>(string) 
3: C(string, object) 

Candidate zero is obviously inapplicable because string is not convertible to string[]. That leaves three.

Of the three, we must determine a unique best method. We do so by making pairwise comparisons of the three remaining candidates. There are three such pairs. All of them have identical parameter lists once we strip off the omitted optional parameters, which means that we have to go to the advanced tiebreaking round described in section 7.5.3.2 of the specification.

Which is better, 1 or 2? The relevant tiebreaker is that a generic method is always worse than a non-generic method. 2 is worse than 1. So 2 cannot be the winner.

Which is better, 1 or 3? The relevant tiebreaker is: a method applicable only in its expanded form is always worse than a method applicable in its normal form. Therefore 1 is worse than 3. So 1 cannot be the winner.

Which is better, 2 or 3? The relevant tiebreaker is that a generic method is always worse than a non-generic method. 2 is worse than 3. So 2 cannot be the winner.

To be chosen from a set of multiple applicable candidates a candidate must be (1) unbeaten, (2) beat at least one other candidate, and (3) be the unique candidate that has the first two properties. Candidate three is beaten by no other candidate, and beats at least one other candidate; it is the only candidate with this property. Therefore candidate three is the unique best candidate. It should win.

Not only is the C# 4 compiler getting it wrong, as you correctly note it is reporting a bizarre error message. That the compiler is getting the overload resolution analysis wrong is a little bit surprising. That it is getting the error message wrong is completely unsurprising; the “ambiguous method” error heuristic basically picks any two methods from the candidate set if a best method cannot be determined. It is not very good at finding the “real” ambiguity, if in fact there is one.

One might reasonably ask why that is. It is quite tricky to find two methods that are “unambigously ambiguous” because the “betterness” relation is intransitive. It is possible to come up with situations where candidate 1 is better than 2, 2 is better than 3, and 3 is better than 1. In such situations we cannot do better than picking two of them as “the ambiguous ones”.

I would like to improve this heuristic for Roslyn but it is a low priority.

(Exercise to the reader: “Devise a linear-time algorithm to identify the unique best member of a set of n elements where the betterness relation is intransitive” was one of the questions I was asked the day I interviewed for this team. It’s not a very hard algorithm; give it a shot.)

One of the reasons why we pushed back on adding optional arguments to C# for so long was the number of complex ambiguous situations it introduces into the overload resolution algorithm; apparently we did not get it right.

If you would like to enter a Connect issue to track it, feel free. If you just want it brought to our attention, consider it done. I’ll follow up with testing next year.

Thanks for bringing this to my attention. Apologies for the error.

Leave a Comment