[dmd-internals] Questions: how consttructors and postblits work with qualifiers

Andrei Alexandrescu andrei at erdani.com
Mon Oct 22 08:48:04 PDT 2012


This is a reply to an older email by Kenji (I keep all of its text, in 
which I insert my replies).

On 5/9/12 12:10 PM, kenji hara wrote:
> I have some questions for the spec of struct object construction and copying.
> I hope that Walter and Andrei gives the answers.
>
> Question 1:
>      This is about syntax question.
>
>      If struct S has no constructor, struct literal syntax fills the
> fields with arguments.
>      ->  S(a0, a1, ...) is similar to:
>         { S s; s.field0=a0, s.field1=a1, ..., return s; }()
>      ->  S() is valid and it is same as S.init.

That is correct.

>      If struct S has some constructors, it is always used for struct
> literal syntax.
>      ->  S(...) always calls one of constructors. It there is no match,
> raises compile error.
>      ->  S() is always invalid, because constructor should have one or
> more parameters.
>
>      This is my understanding, is it right?

That would be closer to the C++ rule. Right now (2.061) "auto s = S();" 
or "S s;" for a struct S with some constructors defined works and 
initializes the struct with S.init. Only if S.init is @disable the story 
is different.

I think that's a fine rule. It's important to be able to avail ourselves 
of a default constructor as often as possible.

> Question 2:
>      If S has a constructor which it gets one argument and its type is
> not a kind of S:
>
>          struct S {
>              this(int n) { ... }
>          }
>          S s1 = 1;    // runs this(int n) implicitly.
>          S s2 = S(1); // same as above
>
>      This is filed as issue 7019 and 4875 in bugzilla.
>      Is this a bug or feature? I think it is useful feature for such
> types like BigInt.
>      And should it work in module scope?

I sent a detailed reply to 
http://d.puremagic.com/issues/show_bug.cgi?id=7019 - in brief we should 
fix construction at module scope, and change nothing else.

> Question 3:
>      If S has a constructor which it gets one argument and its type is
> a kind of S:
>
>          struct S {
>              this(S src) { ... }
>              this(const S src) { ... }
>              this(immutable S src) { ... }
>          }
>
>      It works as *copy constructor* and called on initialization with copying.
>
>          S sm;
>          immutable S si = immutable(S)(sm); // calls this(immutable S
> src) explicitly
>          immutable S si = sm; // calls this(immutable S src) instead of
> blit copying
>
>      This is a feature explained in TDPL, but I think it is not good.
>      a. If S has a postblit, it seems to me that they conflicts each other.
>      b. If S has no mutable indirection and no postblit, we can switch
> object qualifier
>         with blit copying, like built-types. (e.g. int n; immutable int m = n;)
>         In the case, I think copy constructors won't work. Is it right?
>
>      It seems to me that copy constructor concept conflicts with postblit one.

I agree there's a big problem here. The worst of it is that postblit 
ctors make it VERY difficult to typecheck copy construction of qualified 
objects (const and immutable). It's much easier to check that some 
fields get initialized properly one by one, than to make sure the 
postblit "adjustments" leave the object in the correct state.

We didn't predict this when we designed postblit. It's probably D's 
largest design mistake.

Going forward I think we should design good copy constructors and 
migrate away from postblits, while still allowing them (they're very 
good when they're good - simple adjustments for non-qualified structs).

> Question 4:
>      Qualified postblits should work?
>
>          struct S {
>              this(this) {}
>              this(this) immutable {}
>          }
>          S sm;
>          const S sc = sm;     // runs this(this)? or runs only blit copy? [X]
>          immutable S si = sm; // runs this(this)immutable?
>
>      But, with current dmd, multiple postblits conflicts each other.
>      Even if should do, I can't imagine how they works in some cases, like [X].

There are two qualifiers involved in a postblit: the source and the 
target. We could allow such by using e.g.

this(const this) immutable {}

which would be called when constructing an immutable object from a const 
one.

But as I mentioned above semantically checking postblits is likely to be 
a nightmare. So I think we should go with exploring C++-style copy 
constructors that accept qualified arguments. In theory there are nine, 
excluding "shared":

this(S src) { ... }
this(const S src) { ... }
this(immutable S src) { ... }
this(S src) const { ... }
this(const S src) const { ... }
this(immutable S src) const { ... }
this(S src) immutable { ... }
this(const S src) immutable { ... }
this(immutable S src) immutable { ... }

In practice, of course, many would not need an implementation and rely 
on the default one (memcpy).

The right next step here is to start a DIP with a detailed explanation 
of how these constructors will be defined and typechecked - the whole 
raw to cooked business etc. Kenji, would you like to start that?


Thanks,

Andrei


More information about the dmd-internals mailing list