October 08
On Monday, 7 October 2024 at 18:56:49 UTC, Jonathan M Davis wrote:
> The current state of things strongly implies that copy constructors were implemented and tested only in fairly simple situations. They're just too much of a mess in practice.

The copy constructor proposal, DIP 1018, was fast-tracked through the DIP process "in the interest of releasing the implementation as soon as possible." [1] The result speaks for itself, I think.

[1] https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1018.md#formal-assessment


October 08

On Sunday, 6 October 2024 at 16:43:34 UTC, Paul Backus wrote:

>

On Sunday, 6 October 2024 at 04:04:28 UTC, Walter Bright wrote:

>

As the above illustrates, a move constructor cannot be distinguished from a regular constructor by syntax alone. It needs semantic analysis.

It's worth pointing out that this is not just a problem for move constructors; it's also a problem for copy constructors. Specifically, it's what prevents copy constructors from being templates.

As a result, if you're writing generic code and want to handle arbitrary combinations of type qualifiers on the original object and the copy, you cannot do the simple, obvious thing and use a template, like this:

this(this This, Other)(ref Other other)
if (is(immutable Other == immutable typeof(this))
{
    // copy logic
}

Instead, you must write individual copy constructor overloads for every combination of qualifiers:

// Function bodies omitted for brevity
// mutable original
this(ref typeof(this));
this(ref typeof(this)) const;
this(ref typeof(this)) immutable;
this(ref typeof(this)) inout;

// const original
this(ref const typeof(this));
this(ref const typeof(this)) const;
this(ref const typeof(this)) immutable;
this(ref const typeof(this)) inout;

// immutable original
this(ref immutable typeof(this));
this(ref immutable typeof(this)) const;
this(ref immutable typeof(this)) immutable;
this(ref immutable typeof(this)) inout;

// inout original
this(ref inout typeof(this));
this(ref inout typeof(this)) const;
this(ref inout typeof(this)) immutable;
this(ref inout typeof(this)) inout;

// etc.

So, whatever solution we come up with for helping the compiler identify move constructors, I hope we can apply it to copy constructors too.

I have similar problem in implementation of generic container:

		static if(isCopyConstructable!(typeof(this), typeof(this)))
			this(ref scope typeof(this) rhs)@safe{this(rhs, Forward.init);}
		else
			@disable this(ref scope typeof(this) rhs)@safe;

		static if(isCopyConstructable!(typeof(this), const typeof(this)))
			this(ref scope typeof(this) rhs)const @safe{this(rhs, Forward.init);}
		else
			@disable this(ref scope typeof(this) rhs)const @safe;

		static if(isCopyConstructable!(typeof(this), immutable typeof(this)))
			this(ref scope typeof(this) rhs)immutable @safe{this(rhs, Forward.init);}
		else
			@disable this(ref scope typeof(this) rhs)immutable @safe;

		static if(isCopyConstructable!(typeof(this), shared typeof(this)))
			this(ref scope typeof(this) rhs)shared @safe{this(rhs, Forward.init);}
		else
			@disable this(ref scope typeof(this) rhs)shared @safe;

		static if(isCopyConstructable!(typeof(this), const shared typeof(this)))
			this(ref scope typeof(this) rhs)const shared @safe{this(rhs, Forward.init);}
		else
			@disable this(ref scope typeof(this) rhs)const shared @safe;

Having template copy/move ctor will be nice.

October 08

On Sunday, 6 October 2024 at 19:00:09 UTC, Anonymous wrote:

>

On Sunday, 6 October 2024 at 04:04:28 UTC, Walter Bright wrote:

>
struct S { ... }

this(ref S) // copy constructor
this(this)  // postblit
this(S)     // move constructor
~this()     // destructor
...

...
A fix that would simplify the language and the compiler would be to have a unique syntax for a move constructor,
...

Something like one of:

1. =this(S)
2. this(=S)
3. <-this(S)

?

this move(...)
this copy(...)
this(...)
~this(...)

this is the cleaniest and most understandable syntax, but please make them attributes

copy / move are very common keywords, if they become reserved, i'll riot

i have no desire to use RAII, literally 0, don't also remove words i can use in my code

i already complained about destroy being in object.d and top top it all, it being a freaking template...

it took me DAYS wondering why my destroy function was never called despite the program compiling

October 08

On Tuesday, 8 October 2024 at 07:30:08 UTC, ryuukk_ wrote:

>

On Sunday, 6 October 2024 at 19:00:09 UTC, Anonymous wrote:

>

this move(...)
this copy(...)
this(...)
~this(...)

>

...
copy / move are very common keywords, if they become reserved, i'll riot
...

Copy and move should not be reserved, they specify 'this'. I'm assuming it's possible to add anything between this and (), without reserving anything new or be in conflict with anything existing.
'''
struct S {
  ~this(...)
  this(...) // existing variants
  this.copy (...) // copy constructor
  this.move (...) // move constructor
  this.blit (...) // postblit constructor
  this.future (...) // unknown now
  copy (...)   // normal method
  move (...)   // normal method
  blit (...)   // normal method
  future (...) // normal method
}
...
S s;
s(...); // existing
s.this.copy(...);
s.this.move(...);
s.this.blit(...);
s.copy(...);
s.move(...);
s.blit(...);
s.future(...);
'''
October 09

On Tuesday, 8 October 2024 at 17:57:55 UTC, Anonymous wrote:

Why not:

struct S {
  ~this(...)
  this(...) // existing variants
  _copy (...) // copy constructor
  _move (...) // move constructor
  _blit (...) // postblit constructor
  _future (...) // unknown now
  copy (...)   // normal method
}
October 09

On Wednesday, 9 October 2024 at 00:46:08 UTC, zjh wrote:

>

On Tuesday, 8 October 2024 at 17:57:55 UTC, Anonymous wrote:

Why not:

struct S {
  ~this(...)
  this(...) // existing variants
  _copy (...) // copy constructor
  _move (...) // move constructor
  _blit (...) // postblit constructor
  _future (...) // unknown now
  copy (...)   // normal method
}

D currently has a rule that constructors are always named this. It would be nice if we could avoid breaking that rule, since it would make the language more consistent and easier to learn.

October 09

On Wednesday, 9 October 2024 at 01:49:40 UTC, Paul Backus wrote:

>

D currently has a rule that constructors are always named this.

There is no need to limit to this at all.

October 09
On Sunday, 6 October 2024 at 04:04:28 UTC, Walter Bright wrote:
> ```
> struct S { ... }
>
> [...]

Just a question, are move semantics out of the question?
October 09
On Sun, 6 Oct 2024 at 14:06, Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> ```
> struct S { ... }
>
> this(ref S) // copy constructor
> this(this)  // postblit
> this(S)     // move constructor
> ~this()     // destructor
>
> alias T = S;
> this(T);    // also move constructor
>
> alias Q = int;
> this(Q);    // regular constructor
> ```
> As the above illustrates, a move constructor cannot be distinguished from
> a
> regular constructor by syntax alone. It needs semantic analysis.
>

Yes, I would expect this.

While this seems simple enough, it isn't I have discovered to my chagrin.
> The
> overload rules in the language (including things like rvalue references
> where
> sometimes an rvalue becomes an lvalue) that the move constructors get
> confused
> with the copy constructors, leading to recursive semantic loops and other
> problems.
>
> I've struggled with this for days now.
>

Can you show us some cases?

A fix that would simplify the language and the compiler would be to have a
> unique syntax for a move constructor, instead of the ambiguous one in the
> proposal. That way, searching for a copy constructor will only yield copy
> constructors, and searching for a move constructor will only yield move
> constructors. There will be a sharp distinction between them, even in the
> source
> code. (I have seen code so dense with templates it is hard to figure out
> what
> kind of constructor it is.)
>
> Something like one of:
> ```
> 1. =this(S)
> 2. this(=S)
> 3. <-this(S)
> ```
> ?
>
> It may take a bit of getting used to. I kinda prefer (1) as it is sorta
> like
> `~this()`.
>

It's not right to distinguish move constructors, by-value argument is a
potential move opportunity.
Why aren't the regular parameter matching semantics sufficient? Can you
please give us a set of examples where the regular parameter matching
semantics are failing or infinite-looping?


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

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


Likewise, I don't understand why opMove would be necessary to distinguish
from opAssign?
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?


October 09
Ohhh man, that was a grueling slog getting through the sea of people casually adding some opinion on the flavour of a new keyword, while completely skipping over the premise of the proposition...

Sorry Walter, I think it's a very bad idea, and unlike everyone else here apparently, I want to see clear evidence that this path is the only reasonable solution before I even consider weighing on the colour of this bikeshed...

Please show some fail cases so we can actually consider the problem?

Why can't a constructor be selected by normal overload resolution rules? And why should a copy or a move constructor be special or distinct from a "regular constructor" as you call it? Can you show some cases where the distinction is necessary?


On Wed, 9 Oct 2024 at 15:42, Manu <turkeyman@gmail.com> wrote:

> On Sun, 6 Oct 2024 at 14:06, Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
>
>> ```
>> struct S { ... }
>>
>> this(ref S) // copy constructor
>> this(this)  // postblit
>> this(S)     // move constructor
>> ~this()     // destructor
>>
>> alias T = S;
>> this(T);    // also move constructor
>>
>> alias Q = int;
>> this(Q);    // regular constructor
>> ```
>> As the above illustrates, a move constructor cannot be distinguished from
>> a
>> regular constructor by syntax alone. It needs semantic analysis.
>>
>
> Yes, I would expect this.
>
> While this seems simple enough, it isn't I have discovered to my chagrin.
>> The
>> overload rules in the language (including things like rvalue references
>> where
>> sometimes an rvalue becomes an lvalue) that the move constructors get
>> confused
>> with the copy constructors, leading to recursive semantic loops and other
>> problems.
>>
>> I've struggled with this for days now.
>>
>
> Can you show us some cases?
>
> A fix that would simplify the language and the compiler would be to have a
>> unique syntax for a move constructor, instead of the ambiguous one in the
>> proposal. That way, searching for a copy constructor will only yield copy
>> constructors, and searching for a move constructor will only yield move
>> constructors. There will be a sharp distinction between them, even in the
>> source
>> code. (I have seen code so dense with templates it is hard to figure out
>> what
>> kind of constructor it is.)
>>
>> Something like one of:
>> ```
>> 1. =this(S)
>> 2. this(=S)
>> 3. <-this(S)
>> ```
>> ?
>>
>> It may take a bit of getting used to. I kinda prefer (1) as it is sorta
>> like
>> `~this()`.
>>
>
> It's not right to distinguish move constructors, by-value argument is a
> potential move opportunity.
> Why aren't the regular parameter matching semantics sufficient? Can you
> please give us a set of examples where the regular parameter matching
> semantics are failing or infinite-looping?
>
>
> 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
>
> this(Other) // move from other (Other is an rvalue here)
> this(ref Other) // copy from other (Other is obviously a ref)
>
>
> Likewise, I don't understand why opMove would be necessary to distinguish
> from opAssign?
> 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?
>