On Sun, 13 Oct 2024, 08:01 Timon Gehr via Digitalmars-d, <digitalmars-d@puremagic.com> wrote:
On 10/12/24 10:06, Manu wrote:
>
>     4. rvalue constructors exist and are used, and are NOT move constructors
>
>
> They are rvalue constructors; it doesn't matter what they do, their
> selection criteria remains identical.

Just as long as the semantics of the actual declaration does not change.
DIP1040 proposed to change the semantics of the move constructor
declaration itself.

Anyway, I agree that a clean design may be that a constructor that
accepts an lvalue of the same type should be a copy constructor and a
constructor that accepts an rvalue of the same type should be a move
constructor (templated or not). Without any special semantics of the
declaration itself.

Then standard overload resolution (with an after-the-fact check whether
there is `ref` on the first parameter) can be used to check whether
something is copyable or moveable.


I guess one change in behavior is that code that previously worked with
implicit compiler-generated moves or explicit moves will now invoke
something that had been written as an rvalue constructor, which also
precluded there being any user-defined copy constructor in the struct or
its fields (disabled or otherwise).

Yes, that's the case in question. We need to see some case studies; I have a suspicion that much code that makes sense and is not actually a bug is actually already some kind of move constructor. 
The calling rules choosing a compiler-generated move or an rvalue constructor are murky (because they are both perfect match; there much be a hard coded tie-breaker, and it must be circumstantial), and I bet any code that uses that is brittle and subject to very special care when handling.


I am not sure for what purpose rvalue constructors are even being
written today, but I think the only way to invoke them is as an explicit
`S(s)`.

Maybe, and that might be the definition of the hard-coded tie-breaker rule I described a moment ago... It's non-uniform in any event, a complete surprise that there would be 2 separate cases.

If someone does something funky in such a constructor, it may
not actually do the right thing if it suddenly starts being called for
moves.

Maybe. Let's find out!
I wouldn't worry about it... It's already a bad API pattern that depends on weird and brittle rules.

What the hell could it reasonably mean to initialise an S from another S and not be some kind of move when written in your code exactly the way you expect a move to appear?
I think if we find examples in the wild either a) it's already a move in waiting, or b) it's actually broken and the author didn't realise. 

My bet is that instances of this code were written by inexperienced D programmers who probably had a poor understanding around the semantics of what they wrote. 

But let's find out! We need case studies relating to this super weird and frankly nonsense pattern. 
In any event, if this is the only breaking change we encounter, then we're in amazingly good shape!
I will submit the PR to correct every project that suffers from this myself...