[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