October 06

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

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.

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.

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

  1. Do you think people will read =this as move this = is move?
  2. =S, that's worry emoji, look it up
  3. for a feature that appeal to C++ programmers, if they ever decide to try D, they'll get confused with ->
@move this(S)
October 06
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.
October 06
Good thoughts! One reason I like the `=this` syntax is its similarity to the existing `~this` syntax. Having `this(=S)` is inconsistent, and would lead to awkward questions like "why can't I use `=` on other parameters?"
October 06
On 10/6/2024 2:58 AM, Richard (Rikki) Andrew Cattermole wrote:
> Does that not have desirable potential additions, once move constructors have been resolved?
> For now, it can error if seen elsewhere.

I know you've proposed @move for parameters already, but this seems inconsistent with that.
October 06
> So, whatever solution we come up with for helping the compiler identify move constructors, I hope we can apply it to copy constructors too.

Sure, but I think that would be a separate proposal.
October 06
People took to `~this` like a duck to water, so I don't think it'll be a big deal for `=this` to be a move constructor. After all, it's better than the C++ `S(S&&)` by a mile!
October 06

On Sunday, 6 October 2024 at 17:06:10 UTC, Walter Bright wrote:

>

People took to ~this like a duck to water, so I don't think it'll be a big deal for =this to be a move constructor. After all, it's better than the C++ S(S&&) by a mile!

If we adopt =this as the move-constructor syntax, what will we use if we decide to add a dedicated copy-constructor syntax in the future? The = symbol is used for both moves and copies, after all.

October 06

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

>

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

I'm tempted to propose Rust-style syntax using the UTF delivery truck emoji: 🚚this(S). Since that seems problematic, what's wrong with a clear and simple approach like

this(move S)

Any of the three you've proposed would add considerable overhead to someone learning the language. I'm not a fan of even more syntax.

October 06

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

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

Option 3 is really bad, but I like option 2. In this syntax, the = operator is at the beginning of the parameter. This could mean that the parameter is inherited. In other words, we are transferring the source of the S object to the this object.

struct MyStruct {
    int[] data;

    this(int size) {
        data = new int[size];
        writeln("Constructor called, data: ", data);
    }

    this(=MyStruct other) {
        data = other.data;
        other.data = null;
        writeln("Move constructor called");
    }
}

SDB@79

October 06
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(...)