On Thu, 10 Oct 2024 at 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.

The compiler would go into infinite recursion converting a ref to an rvalue and
back to a ref again. There were maybe 10 or more functions in the recursion
stack, looping through the heavy semantic routines.

Another problem was failure to compile libmir:
```
source/mir/rc/ptr.d(395,15): Error: `mir.rc.ptr.castTo` called with argument
types `(mir_rcptr!(C))` matches both:
   source/mir/rc/ptr.d(275,13):     `mir.rc.ptr.castTo!(I,
C).castTo(mir_rcptr!(C) context)`
   and:
   source/mir/rc/ptr.d(291,25):     `mir.rc.ptr.castTo!(I,
C).castTo(immutable(mir_rcptr!(C)) context)`

```
If libmir breaks, that means other code will break, too. I eventually decided it
was not a good idea to modify existing semantics (rvalue constructors exist and
are used already), as who knows how much breakage that would ensue. Adding
distinct syntax for the new semantics seemed like a much more practical approach.

I.e. `this(S)` already is accepted by the compiler and is used.


> The story you present is incomplete, let me enhance:
>
> struct S { ... }
> struct Other { ... }
>
> this(ref S) // copy constructor
> this(this)  // postblit
> this(S)     // move constructor
> ~this()     // destructor

It's not that simple. You can have:

```
alias T = S;
this(T);     // can't tell if move constructor by syntax
```

> this(Other) // move from other (Other is an rvalue here)
> this(ref Other) // copy from other (Other is obviously a ref)

Problems show up when `Other` is implicitly convertible to `S`.

If the overload resolution rules don't prefer an exact match over a conversion, then something is very wrong with the overload selection rules. What situations cause those waters to become murky?

But, I think you actually missed my point here; I provided a ref and non-ref overload for Other... how do I move-construct from some OTHER type?
Move constructors can't just be for S s = otherS; that's obviously an important case, but it excludes an enormous range of the "move semantics" landscape.

The fact you're talking about a separate constructor type to identify the move case leads me to suspect that the rvalue semantic only applies to the argument to that particular function and nothing else?
That's not really "move semantics", that's just a move constructor... and I think I've completely misunderstood your DIP if this is correct?

> Likewise, I don't understand why opMove would be necessary to distinguish from
> opAssign?

Is `a = b` a move or a copy?

is `b` an rvalue or an lvalue? (or had some special sauce tag it with a 'last-use' flag or anything like that)

> If you introduce opMove, then you'll end up with opIndexMove, opOpMove, etc.
> Is that something you want? Assuming you want to avoid this; then I also imagine
> solving for that issue would equally solve for the main constructor case?

This is why I don't like implicit conversions for a struct - you wind up with
impossible tangles of meaning. Oh, and rvalue to ref conversions, too.

I don't feel like I have these problems in C++, which is far less constrictive. I have no way to know or assess whether any of the arrangements of rules you considered or tried actually arrange into a nice tight lattice or not... or if they were generally cohesive and well-formed. We're expected to accept that you tried every possible reasonable arrangement and conclusively determined that it's not workable. I have no idea how we can assess that for reality or not... :/