March 11, 2021
On Wednesday, 10 March 2021 at 22:51:58 UTC, tsbockman wrote:
> Am I the only one who thinks that it would be better to have syntax that accurately reflects the semantics, instead of just documenting "this syntax is a lie"?

No, I think there is a problem with using opAssign here, because "this" will refers to something that is possibly uninitialized, and the old value may not be consumed fully.

Due to problem #1, this kinda need to be a constructor rather than an opAssign.
Due to problem #2, this kinda need not to take another struct as argument.

Which leave me with the one conclusion: the postblit needs to be recycled into a move constructor.
March 11, 2021
On Wednesday, 10 March 2021 at 23:00:35 UTC, Max Haughton wrote:
> On Wednesday, 10 March 2021 at 22:51:58 UTC, tsbockman wrote:
>> On Friday, 5 March 2021 at 23:03:57 UTC, tsbockman wrote:
>>> On Friday, 5 March 2021 at 12:19:54 UTC, Mike Parker wrote:
>>>> This is the discussion thread for the first round of Community Review of DIP 1040, "Copying, Moving, and Forwarding":
>>>
>>> From the DIP:
>>>> A Move Constructor for struct S is declared as:
>>>>     this(S s) { ... }
>>>> A Move Assignment Operator for struct S is declared as:
>>>>     void opAssign(S s) { ... }
>>
>> Am I the only one who thinks that it would be better to have syntax that accurately reflects the semantics, instead of just documenting "this syntax is a lie"?
>
> I'm still fairly open minded about this, however aesthetically at least I really like the idea of move semantics being fairly invisible - e.g. it's not passing a struct by move but rather being up to the struct. When you take into account the object lifetime in a move it's not pass by value of reference.

I agree that move semantics should be fairly invisible outside the custom move operators themselves, but I don't understand how that is an argument either for or against using the `ref` keyword like all other by-reference value type parameters must.

Either way the custom move operator (if one is needed) must be defined explicitly, and either way calls to it must be implicitly inserted by the compiler at special points that can only be correctly and completely predicted by understanding the rules in the DIP, and not from standard overload resolution rules.

> Definitely needs to be carefully considered.

Thank you for taking the time to do so. Please consider that the more that surprising special cases like this are added to the language, the harder it gets for non-experts to read and comprehend D code, for newcomers to learn the language, and especially for meta-programmers to write clean and correct code.

There is a consequence to adding exception to otherwise simple rules like, "Value type parameters that are passed by reference are labeled `ref` or `out`." In generic code (like most of Phobos) exceptions lead to bugs, bugs lead to frustration, frustration leads to `static if` branches, and too many `static if` branches leads to templates getting down-graded to string mixins (probably still with bugs).
March 11, 2021
On Thursday, 11 March 2021 at 00:31:01 UTC, deadalnix wrote:
> On Wednesday, 10 March 2021 at 22:51:58 UTC, tsbockman wrote:
>> Am I the only one who thinks that it would be better to have syntax that accurately reflects the semantics, instead of just documenting "this syntax is a lie"?
>
> No, I think there is a problem with using opAssign here, because "this" will refers to something that is possibly uninitialized, and the old value may not be consumed fully.
>
> Due to problem #1, this kinda need to be a constructor rather than an opAssign.

Yeah, studying the DIP I can't figure out what problem the move `opAssign` is supposed to solve that the constructor doesn't:
    https://forum.dlang.org/post/kzgybicwqwlfyiiefucc@forum.dlang.org

> Due to problem #2, this kinda need not to take another struct as argument.

I think forbidding the move constructor from explicitly accessing the source at all is overly restrictive for a systems programming language. Full access should still be available, even if it can't be compiler-verified @safe.

> Which leave me with the one conclusion: the postblit needs to be recycled into a move constructor.

Since the `this(this)` syntax of the postblit is unique, that would be a valid solution to my concern about misleading syntax.
March 10, 2021
On 3/10/2021 5:27 PM, tsbockman wrote:
> On Thursday, 11 March 2021 at 00:31:01 UTC, deadalnix wrote:
>> On Wednesday, 10 March 2021 at 22:51:58 UTC, tsbockman wrote:
>>> Am I the only one who thinks that it would be better to have syntax that accurately reflects the semantics, instead of just documenting "this syntax is a lie"?
>>
>> No, I think there is a problem with using opAssign here, because "this" will refers to something that is possibly uninitialized, and the old value may not be consumed fully.
>>
>> Due to problem #1, this kinda need to be a constructor rather than an opAssign.

opAssign is only for assigning to initialized objects. Constructors are for uninitialized objects.


> Yeah, studying the DIP I can't figure out what problem the move `opAssign` is supposed to solve that the constructor doesn't:
> https://forum.dlang.org/post/kzgybicwqwlfyiiefucc@forum.dlang.org

The thing about "destroy after move" is to deal with the case of both the source and the destination referring to the same object. The concern is that destroying the destination's original contents first will destroy them for the source before it gets moved in.

It's the same problem "swap" has. It's also necessary semantics for a reference counted object.
March 11, 2021
On Thursday, 11 March 2021 at 03:33:31 UTC, Walter Bright wrote:
> On 3/10/2021 5:27 PM, tsbockman wrote:
>> Yeah, studying the DIP I can't figure out what problem the move `opAssign` is supposed to solve that the constructor doesn't:
>> https://forum.dlang.org/post/kzgybicwqwlfyiiefucc@forum.dlang.org
>
> The thing about "destroy after move" is to deal with the case of both the source and the destination referring to the same object. The concern is that destroying the destination's original contents first will destroy them for the source before it gets moved in.

Wouldn't it make more sense to just skip the move operation entirely when the source and destination are the same? Or are there circumstances in which that cannot be determined, even at runtime?

Also, doesn't this mean that every move of an object with a destructor requires a copy? Does the copy constructor get called in such cases? If so, what benefit do moves have over copies? If not, how do you know you're not breaking assumptions that the destructor depends upon for correct functioning by making this the one circumstance in which a value may be copied without calling its copy constructor? Which function sees the "real" address of the object: the move operator, or the destructor?
March 11, 2021
On 10/3/21 23:51, tsbockman wrote:
> On Friday, 5 March 2021 at 23:03:57 UTC, tsbockman wrote:
>> On Friday, 5 March 2021 at 12:19:54 UTC, Mike Parker wrote:
>>> This is the discussion thread for the first round of Community Review of DIP 1040, "Copying, Moving, and Forwarding":
>>
>> From the DIP:
>>> A Move Constructor for struct S is declared as:
>>>     this(S s) { ... }
>>> A Move Assignment Operator for struct S is declared as:
>>>     void opAssign(S s) { ... }
>>
>> Is the parameter to these methods really pass-by-value?
>> ...
>> If the parameter is, in fact, intended to be pass-by-reference, then I must strenuously object to the chosen syntax.
> 
> Over in the feedback thread, Atila Neves also concluded that the syntax is misleading here:
> 
> On Wednesday, 10 March 2021 at 21:27:25 UTC, Atila Neves wrote:
>> I eventually understood what this meant, but this confused me when I read it the first time. I'd reword it to mention that the syntax looks like a by-value parameter but ends up being passed by reference. It also confused me that the 2nd function had `ref` in there.
> 
> Am I the only one who thinks that it would be better to have syntax that accurately reflects the semantics, instead of just documenting "this syntax is a lie"?

There is another issue with the proposed semantics, unless I'm missing something.

How can I implement both an "identity assignment operator" [1] and a "move assignment operator"? The proposed syntax is co-opting an existing pattern for a different use case.

Let's say I have a struct that includes an associative array, where I implement deep copy for the assignment operator.

Will in this case my deep copy be reused automatically for movement? That's obviously not what I want, for that I'd just want to copy the reference to the existing AA.

If anything, this should be added to the breaking changes and deprecations, or at least mentioned as something to check for.

I could have used a `ref` parameter, but this wouldn't work with rvalues, and in any case the syntax is currently allowed, unlike constructors (bug 20424 [2]) which are mentioned in the DIP... although even there I tend to agree with comment #5, but I digress.

[1]: https://dlang.org/spec/operatoroverloading.html#assignment
[2]: https://issues.dlang.org/show_bug.cgi?id=20424
March 11, 2021
On Thursday, 11 March 2021 at 00:56:19 UTC, tsbockman wrote:
> On Wednesday, 10 March 2021 at 23:00:35 UTC, Max Haughton wrote:
>> On Wednesday, 10 March 2021 at 22:51:58 UTC, tsbockman wrote:
>>> On Friday, 5 March 2021 at 23:03:57 UTC, tsbockman wrote:
>>>> On Friday, 5 March 2021 at 12:19:54 UTC, Mike Parker wrote:
>>>>> This is the discussion thread for the first round of Community Review of DIP 1040, "Copying, Moving, and Forwarding":
>>>>
>>>> 
>> Definitely needs to be carefully considered.
>
> Thank you for taking the time to do so. Please consider that the more that surprising special cases like this are added to the language, the harder it gets for non-experts to read and comprehend D code, for newcomers to learn the language, and especially for meta-programmers to write clean and correct code.

This is important
March 11, 2021
On 3/10/2021 8:17 PM, tsbockman wrote:
> On Thursday, 11 March 2021 at 03:33:31 UTC, Walter Bright wrote:
>> On 3/10/2021 5:27 PM, tsbockman wrote:
>>> Yeah, studying the DIP I can't figure out what problem the move `opAssign` is supposed to solve that the constructor doesn't:
>>> https://forum.dlang.org/post/kzgybicwqwlfyiiefucc@forum.dlang.org
>>
>> The thing about "destroy after move" is to deal with the case of both the source and the destination referring to the same object. The concern is that destroying the destination's original contents first will destroy them for the source before it gets moved in.
> 
> Wouldn't it make more sense to just skip the move operation entirely when the source and destination are the same? Or are there circumstances in which that cannot be determined, even at runtime?

The idea is that the move assignment operation takes care of this, and makes it "as if" the move was done, then the destruction.
March 11, 2021
On 3/10/2021 11:44 PM, Arafel wrote:
> There is another issue with the proposed semantics, unless I'm missing something.
> 
> How can I implement both an "identity assignment operator" [1] and a "move assignment operator"? The proposed syntax is co-opting an existing pattern for a different use case.
> 
> Let's say I have a struct that includes an associative array, where I implement deep copy for the assignment operator.
> 
> Will in this case my deep copy be reused automatically for movement? That's obviously not what I want, for that I'd just want to copy the reference to the existing AA.
> 
> If anything, this should be added to the breaking changes and deprecations, or at least mentioned as something to check for.
> 
> I could have used a `ref` parameter, but this wouldn't work with rvalues, and in any case the syntax is currently allowed, unlike constructors (bug 20424 [2]) which are mentioned in the DIP... although even there I tend to agree with comment #5, but I digress.
> 
> [1]: https://dlang.org/spec/operatoroverloading.html#assignment
> [2]: https://issues.dlang.org/show_bug.cgi?id=20424

Constructing from an rvalue essentially is move construction.
March 11, 2021
On 3/11/2021 12:42 AM, Walter Bright wrote:
> Constructing from an rvalue essentially is move construction.

I forgot to mention that the new semantics only apply to EMO objects, which require both a move constructor and a move assignment operator. The move constructor is new syntax. Therefore, it shouldn't break existing code.