Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[over.best.ics] Disambiguating ambiguous conversion sequences (again) #3978

Open
sdkrystian opened this issue May 3, 2020 · 5 comments
Open
Labels
cwg Issue must be reviewed by CWG. decision-required A decision of the editorial group (or the Project Editor) is required.

Comments

@sdkrystian
Copy link
Contributor

sdkrystian commented May 3, 2020

In #2288 we attempted to clarify ambiguous conversion sequences, but I think there are a couple problems left unaddressed.

Consider the following:

#include <initializer_list>

struct A { A(int); };
struct B { B(int); };
struct C
{
  C(A);
  C(B);
};

struct D
{
  D(C); // #1
  D(std::initializer_list<A>); // #2
};

D d({0}); // calls #2

[over.best.ics] p10 says:

For the purpose of ranking implicit conversion sequences as described in [over.ics.rank], the ambiguous conversion sequence is treated as a user-defined conversion sequence that is indistinguishable from any other user-defined conversion sequence.

In the above example, the ICS from {0} to C is the ambiguous conversion sequence (ACS) and the ICS from {0} to std::initializer_list<A> is a user-defined conversion sequence (UCS). Now with respect to the quoted paragraph: is the ACS considered to be indistinguishable from any other UCS regardless of [over.ics.rank], or will the rules in [over.ics.rank] p3 apply to disambiguate in this situation, making the conversion to std::initializer_list<A> a better ICS? I think the intent is pretty clear that the latter is true, but the wording does not make this clear.

For this issue, I think the following change would be sufficient:
Change [over.best.ics] p10 sentence 2:

For the purpose of ranking implicit conversion sequences as described in [over.ics.rank], the ambiguous conversion sequence is treated as considered to be a user-defined conversion sequence that is indistinguishable from any of the same form as every other user-defined conversion sequence.

As for the other issue, consider the first sentence of [over.best.ics] p10:

If there are multiple well-formed implicit conversion sequences converting the argument to the parameter type [...]

First off, this wording seems to be at odds with the note in [over.best.ics] p2, particularly with the "well-formed" bit which I take to say that the application of that sequence of conversions must be be well-formed, most notably with respect to access control and whether a function is deleted. In addition to this, it's unclear what it even means for there to be multiple implicit conversion sequences. For example:

struct A
{
  A(int); // #1
  A(long); // #2
};

void f(A);

f(0); // not ill-formed

Arguably, there are "multiple well-formed implicit conversion sequences" here, both of which are user-defined conversion sequences. Here the intent is obvious as to what should occur.

There are two ways to approach this: either a substantial overhaul as to how we chose the ICS for a particular function parameter, or a smaller scale patch. For the latter, I suggest the following:

Change [over.best.ics] p10 sentence 1:

If there are multiple well-formed at least two implicit conversion sequences S1 and S2 converting the argument to the parameter type such that S1 is indistinguishable from S2, the implicit conversion sequence associated with the parameter is defined to be the unique conversion sequence designated the ambiguous conversion sequence.

As for the more substantial change, it would entail forming a set of candidate conversion sequences from each argument to its respective parameter in each applicable subclause, and then determining if the resultant conversion from the argument to the parameter is either formed, not formed, or ambiguous.

@jensmaurer
Copy link
Member

For your first example, there is implementation divergence: https://godbolt.org/z/wYskLe
Clang believes the call is ambiguous; gcc, MSVC and icc believe the call is fine.

All compilers agree that the second example is well-formed. I fail to find a rule that would prefer int -> A over int -> long -> A, though. [over.ics.rank] p3.3 only applies if the same constructor of A would be called for both sequences.

@sdkrystian
Copy link
Contributor Author

@jensmaurer Ah, I forgot to mention that this was motivated by the implementation divergence (clang fails to disambiguate when it should).

I fail to find a rule that would prefer int -> A over int -> long -> A

[over.ics.rank] p3.3 doesn't apply here since the initialization copy initialization of A from 0 isn't ambiguous (overload resolution is performed for that initialization independently and it succeeds http://eel.is/c++draft/over.best.ics#6)

@jensmaurer
Copy link
Member

jensmaurer commented Jun 6, 2020

Agreed for the second example.

For the first example, "of the same form" in over.ics.rank p3 first sentence refers to standard/user-defined/ellipsis (the "basic form" mentioned in p2). We have already established that the ambiguous conversion sequence is a user-defined conversion sequence ([over.best.ics] p10), so this particular condition is already satisfied.
However, I can't find a rule in over.ics.rank p3 that would rank an ambiguous conversion sequence vs. a "good" user-defined conversion sequence. Also, your example looks suspiciously similar to the one in over.best.ics p10 (first case), where we expressly say overload resolution is ambiguous.

@sdkrystian
Copy link
Contributor Author

sdkrystian commented Jun 6, 2020

I can't find a rule in over.ics.rank p3 that would rank an ambiguous conversion sequence vs. a "good" user-defined conversion sequence. Also, your example looks suspiciously similar to the one in over.best.ics p10 (first case), where we expressly say overload resolution is ambiguous.

In my example, I think we want over.ics.rank p3.1.1 to apply here, making the initializer_list constructor a better candidate. p3 is supposed to serve as a tiebreaker after all, so it would make the most sense (and it seems that msvc & gcc agree).

@jensmaurer
Copy link
Member

jensmaurer commented Jun 6, 2020

Given the example in over.best.ics p10 (first case) and the existing text, I'm not seeing any editorial leeway here.

@jensmaurer jensmaurer added cwg Issue must be reviewed by CWG. decision-required A decision of the editorial group (or the Project Editor) is required. labels Jun 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cwg Issue must be reviewed by CWG. decision-required A decision of the editorial group (or the Project Editor) is required.
Projects
None yet
Development

No branches or pull requests

2 participants