October 16
On 10/14/24 18:14, RazvanN wrote:
> On Monday, 14 October 2024 at 16:03:15 UTC, RazvanN wrote:
> 
>> overload set (__movector). That way, if we have __copyctor, __movector and __ctor the compiler can reason on which one should be called depending on the circumstances and thus infinite loops can be avoided.
>>
> 
> That doesn't fix the templated copy/move constructor issue, which the
> new syntax will probably fix.

I think it's too hacky of a fix in general, what you discussed with Manu regarding adding base cases seems more promising.
October 16
On 16/10/24 2:43, Timon Gehr wrote:
> You are right assuming move constructors work as shown in DIP1040. However, I think this design is not really workable, and I think Manu is arguing from a position of assuming that the argument needs to remain valid and will be destroyed by default.

Please excuse the question if it sounds too obvious (no irony here, I'm absolutely no expert in language design), but if the argument remains valid, and can be destroyed or not, doesn't it become a copy constructor?
October 16

On Sunday, 6 October 2024 at 04:14:16 UTC, H. S. Teoh wrote:

>

What about .opMove? Since .opXxx have been unofficially reserved for operator overloading, and one could argue that a move ctor is a kind of operator overloading (overloading the assignment operator). I really dislike symbols that are part of the ctor name, like C++'s operator@() overloads. It's just needlessly complex syntax.

Yes, and if so deprecate the existing this(...) syntax is favor or

  • opCreate() or opNew()
  • opCopy()
  • opPostDestruct(), opPostDelete(), etc...

for conformance. A couple of characters longer but linguistically clearer.

October 16

On Wednesday, 16 October 2024 at 00:43:39 UTC, Timon Gehr wrote:

>

A benefit of =this(ref S) syntax or similar is that it supports the design where the argument is not destructed without additional language features to elide the destructor call, and without a special case where destructor elision may be unexpected given the syntax.

I didn't quite understand, what did you mean? If possible, in short sentences...:)

SDB@79

October 16

On Wednesday, 16 October 2024 at 07:52:23 UTC, Salih Dincer wrote:

>

On Wednesday, 16 October 2024 at 00:43:39 UTC, Timon Gehr wrote:

>

A benefit of =this(ref S) syntax or similar is that it supports the design where the argument is not destructed without additional language features to elide the destructor call, and without a special case where destructor elision may be unexpected given the syntax.

Destructor Elision! Yes, there is such a thing! Is it officially available in D?

SDB@79

October 16
On Tuesday, 15 October 2024 at 16:54:50 UTC, Manu wrote:

> 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.
>

I think that Timon did an excellent job at explaining why we are focusing on constructors. Also, as he noted, those are implementation details - modifying
the code to fix the current infinite recursion depends largely on how we treat
overload resolution with respect to copy/move constructors. I tried to express
the same idea, but Timon did it better. So, from my perspective, which is an
implementation perspective, it's a matter if we are willing to special case the
move/copy constructor base case in the overload resolution mechanism or not.

> "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?

I was talking about implementation trade-offs, not user facing trade-offs.
I.E. we are modifying the overload resolution algorithm by adding a special (or base) case. It could be that this is desirable in this case, I do not know, but
I suspect that this is the reason why Walter is on the "let's add new syntax for this" wagon (but I might be wrong).

As for the `this(typeof(this))` being a move constructor - I completely agree with everything you've said this far. I am not counter-arguing anything that you said, I'm just presenting what the situation with respect to the compiler implementation is.

RazvanN
October 16
On Tuesday, 15 October 2024 at 19:29:11 UTC, Arafel wrote:
> On 15/10/24 20:29, Max Samukha wrote:
>> On Tuesday, 15 October 2024 at 17:52:43 UTC, Arafel wrote:
>
...
> ```d
> struct S {
> 	this (int i) { }
> 	this (S s) { }
> }
>
> void main() {
> 	S s1, s2;
> 	s1 = S(1);
> 	s2 = S(s1);
> 	// Is s1 valid here?
> }
> ```

unlike Walter/Timon/Razvan/Manu/<add others> I'm also no language design guru but, in the above case:

- s1 is an lvalue, so shouldn't a copy ctor be implicitly generated by the compiler (like in C++)..?
- ... and be preferred in this case?

Something like:
```d
	S s1, s2, s3;
	s1 = S(1);       // this (int i)
	s2 = S(s1);      // this (ref S s)  -> implicit
        s3 = S(S(2));    // this (S s)
        // ...and s1 is valid here
```

October 16
On 16.10.24 12:39, ShadoLight wrote:
> unlike Walter/Timon/Razvan/Manu/<add others> I'm also no language design guru but, in the above case:
> 
> - s1 is an lvalue, so shouldn't a copy ctor be implicitly generated by the compiler (like in C++)..?
> - ... and be preferred in this case?
> 
> Something like:
> ```d
>      S s1, s2, s3;
>      s1 = S(1);       // this (int i)
>      s2 = S(s1);      // this (ref S s)  -> implicit
>          s3 = S(S(2));    // this (S s)
>          // ...and s1 is valid here
> ```

That would make sense, but this would in turn mean that the move constructor can never be invoked explicitly.

This might well be a design goal, but, in any case, I think that the explicit usage of constructors with a move constructor signature should be specified and clarified as part of the (eventual) DIP.

Also, it wouldn't change the fact that perhaps a struct might need at the same time a move constructor, a copy constructor, and a templated constructor that could overlap any or both of these.

I'd be all for considering all templated constructors as "normal", even if their signature in a given instantiation would match that of a copy or move constructor.
October 16
On 10/16/24 08:44, Arafel wrote:
> On 16/10/24 2:43, Timon Gehr wrote:
>> You are right assuming move constructors work as shown in DIP1040. However, I think this design is not really workable, and I think Manu is arguing from a position of assuming that the argument needs to remain valid and will be destroyed by default.
> 
> Please excuse the question

No worries!

> if it sounds too obvious (no irony here, I'm absolutely no expert in language design), but if the argument remains valid, and can be destroyed or not, doesn't it become a copy constructor?

A copy constructor is expected to result in two objects that represent the same value.

A move constructor is expected to result in the original object in a new memory location. With Walter's current preferred design, a new dummy object is created in the old memory location. I.e., it has to be valid, but it does not have to contain any data. For example, if you move an array with 3 elements from location A to location B, then location B will contain the original array while location A will contain an empty array.

With copy construction, both locations would contain two equal arrays with 3 elements, both equal to the original array.

The question of whether a value is _valid_ is distinct from the question whether a value is useful. For example, in D `null` is a valid value for a class reference.

This enables moving objects out of memory locations that may still be reachable later, such as a field of a class object.

If the dummy object is required to be a value that does not need, yet allows (no-op) destruction, the compiler has a bit of leeway in how it calls destructors. It is also the least surprising behavior for the case where we move a class field, as a class finalizer may never even run.

A key difference between `this(S s)` and `=this(ref S)` is that the latter elides the destructor by default, while for the former, the least surprising semantics would be that it calls the destructor of the source at the end of the scope by default. There would then need to be an additional feature to elide the destructor call with manual syntax.

October 16
On Wednesday, 16 October 2024 at 12:00:03 UTC, Arafel wrote:
> On 16.10.24 12:39, ShadoLight wrote:
>> unlike Walter/Timon/Razvan/Manu/<add others> I'm also no language design guru but, in the above case:
>> 
>> - s1 is an lvalue, so shouldn't a copy ctor be implicitly generated by the compiler (like in C++)..?
>> - ... and be preferred in this case?
>> 
>> Something like:
>> ```d
>>      S s1, s2, s3;
>>      s1 = S(1);       // this (int i)
>>      s2 = S(s1);      // this (ref S s)  -> implicit
>>          s3 = S(S(2));    // this (S s)
>>          // ...and s1 is valid here
>> ```
>
> That would make sense, but this would in turn mean that the move constructor can never be invoked explicitly.

What do you mean?
   s3 = S(S(2));
...is invoking the move constructor explicitly.

>
> This might well be a design goal, but, in any case, I think that the explicit usage of constructors with a move constructor signature should be specified and clarified as part of the (eventual) DIP.

Agreed.

>
> Also, it wouldn't change the fact that perhaps a struct might need at the same time a move constructor, a copy constructor, and a templated constructor that could overlap any or both of these.

Agreed. But I wonder if some form of precedence may help? AFAICS it would be safe(r) if a copy ctor is always preferred to a move ctor - also in the case if a templated constructor can resolve to either.

For example, in Razvan's infinite recursion example, I wonder if it can work if, as Razvan explained, it tries the copy constructor 1st and sees that it's an exact match, then simply stop any further matching and do copy construction. I mean, are any other matches (besides a move constructor) even possible if the copy constructor was a _perfect match_ in the 1st place? Templated constructors may complicate this but, again, just prefer copy construction if any ambiguity.

>
> I'd be all for considering all templated constructors as "normal", even if their signature in a given instantiation would match that of a copy or move constructor.

Agreed.