Manu
Posted in reply to RazvanN
| On Tue, 15 Oct 2024, 23:01 RazvanN via Digitalmars-d, < digitalmars-d@puremagic.com> wrote:
> On Tuesday, 15 October 2024 at 09:33:59 UTC, Manu wrote:
> > On Tue, 15 Oct 2024 at 01:56, RazvanN via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
> >
> >> On Friday, 11 October 2024 at 16:12:39 UTC, Manu wrote:
> >> > On Thu, 10 Oct 2024, 17:10 Walter Bright via Digitalmars-d, < digitalmars-d@puremagic.com> wrote:
> >> >
> >> >> On 10/8/2024 10:42 PM, Manu wrote:
> >> >> > Can you show us some cases?
> >> >>
> >> >> I'd get infinite recursion with overload resolution, because
> >> >> the compiler
> >> >> will
> >> >> try and match the argument to `S` and `ref S`, made even
> >> >> more
> >> >> complicated
> >> >> with
> >> >> rvalue references enabled.
> >> >>
> >> >
> >> > I don't understand; was the argument an rvalue or an lvalue?
> >> > It is not at all ambiguous or difficult to select the proper
> >> > overload
> >> > here... one should have been an exact match, the other would
> >> > have required
> >> > a copy or conversion; making it an obviously less preferable
> >> > match.
> >> >
> >>
> >> ```d
> >> struct S
> >> {
> >> this(ref typeof(this));
> >> this(typeof(this));
> >> }
> >>
> >> void fun(S);
> >>
> >> void main()
> >> {
> >> S a;
> >> fun(a);
> >> }
> >>
> >> ```
> >>
> >> When the fun(a) is called, the compiler will have to check both
> >> constructors to see which one is a better match. It first tries
> >> the copy constructor and sees that it's an exact match, then it
> >> proceeds to the next overload - the move constructor. Now it
> >> wants
> >> to see if the move constructor is callable in this situation.
> >> The move constructor receives its argument by value so the
> >> compiler
> >> will think that it needs to call the copy constructor (if it
> >> exists).
> >
> >
> > Isn't this the exact moment that the recursion ends? If the copy ctor was an exact match (we must have been supplied an lvalue), and (therefore) while considering the move constructor it was determined that a copy is necessary, then it is not an exact match... copy ctor wins. Case closed.
> >
>
> The way overload resolution works is that you try to call match
> each function in the overload set and always save (1) the best
> matching level
> up this far, (2) the number of matches and (3)a pointer to the
> best matching function (and potentially a second pointer to a
> second function, provided that
> you have 2 functions that have the same matching level). Once
> overload
> resolution is done you inspect these results and either pick a
> single
> function or error depending on what you get. This works without
> any
> special casings (there are minor special casings for unique
> constructors,
> but that's fairly non-invasive).
>
> If we were to accept `this(typeof(this))` as a move constructor,
> we would need
> to special case the overload resolution mechanism. I'm not saying
> it's not possible to implement, rather that we need to add this
> special case to a
> battle tested algorithm.
>
Can you explain this further?
void f(ref T) it not capable with T() at all, so void f(T) is the only
possible match for the rvalue case. Today's algorithm works for that case.
The lvalue case could conceivably match to T or ref T (but ref T is obviously the superior selection, not requiring a superfluous copy)... but even now, the rvalue constructor already exists... what logic allows these to coexist in today's rules?
What is the special case you're describing?
Can you show me exactly what the algorithm does now that's incompatible
with selecting the version with proper ref-ness, and what change would be
necessary?
In contrast, we could leave the overload resolution code
> untouched and simply
> give the move constructor a different identifier (what I mean is,
> you type `this(S)`, but internally the compiler gives the
> __movector name to the function). When the compiler inserts calls
> to the move constructor it will then need to do overload
> resolution using the name __movector. This seems to me like a
> more desirable alternative: you get the this(S) syntax (provided
> that Walter accepts the possibility of code breakage or code
> fix-up as you call it) and the overload
> resolution code remains intact. Additionally, you get smaller
> overload resolution
> penalties given that the overload sets get smaller.
>
Okay, but I've asked people to stop talking about move constructors... it seems to have poisoned everyone's brains with irrelevant focus. We're talking about function arguments in general, we don't need more bizarre edge cases, especially not so deep in the foundations.
Run your thought experiment with:
void f(T);
void f(ref T)
The constructor isn't special... don't special-case the constructor.
> Now, since the copy constructor is in the same overload
> >> set as the move constructor, both need to be checked to see their matching levels. => infinite recursion.
> >>
> >> That is how overload resolution works for any kind of function
> >> (constructor, destructor, normal function). The way to fix this
> >> is to either move the copy constructor and the move
> >> constructor into different overload sets or to special case
> >> them
> >> in the
> >> function resolution algorithm.
> >>
> >
> > Yeah sorry, I just don't see it.
> > When the pair are both defined; one is an exact match, and the
> > other is
> > not. Given an rvalue, the move ctor is an exact match, given an
> > lvalue, the
> > copy ctor is an exact match. There is no case where this is
> > ambiguous?
>
> Before having copy ctors it was allowed to have this(ref S) and
> this(S)
> and it worked. So I don't see any opportunity for ambiguity.
> However, when you
> add some implicit constructor calls that's when things get a bit
> messy, however,
> that's not an unsolvable problem. It just depends on what sort of
> trade-offs you
> are willing to make from an implementation stand point.
"Trade offs"? Do you have anything in mind?
I would be surprised if there are any 'trade-offs'; any change here will
probably be fixing language holes or broken edge cases... what actually
stands to break? What good-stuff™ could we possibly be 'trading' away?
|