Re: Copy Constructor DIP
On Wed, 11 Jul 2018 at 00:40, RazvanN via Digitalmars-d wrote: > > On Tuesday, 10 July 2018 at 20:58:09 UTC, Manu wrote: > > On Tue, 10 Jul 2018 at 03:50, RazvanN via Digitalmars-d > > wrote: > >> > >> Hi everyone! > >> > >> I managed to put together a first draft of the DIP for adding > >> the copy constructor to the language [1]. If anyone is > >> interested, please take a look. Suggestions and comments about > >> technical aspects and wording are all welcome. > >> > >> Thanks, > >> RazvanN > >> > >> [1] https://github.com/dlang/DIPs/pull/129 > > > > I feel there's some things missing. > > > > 1. Explain the need and reasoning behind `@implicit`... that's > > weird > > It is a simple way of defining a copy constructor without the > need of > adding new keywords and with minimal additions to the parser. What's wrong with: struct S { this(ref S copyFrom); } That looks like a perfectly good copy constructor declaration ;) I'm just saying, the DIP needs to explain this. > > and I don't like it at face value. > > 2. It looks like copy constructors are used to perform > > assignments > > (and not constructions)... but, there is also opAssign. What > > gives? > > Eg: > > S b = a; // <- copy construction? looks like an > > assignment. > > And not: > > S b = S(a); // <- actually looks like a construction, but > > this > > syntax seems to not be intended (and rightly so, it's pretty > > terrible) > > 3. In C++, copy constructors and copy assignment operators come > > in > > pairs (which is totally lame!), but we don't see that same > > pattern > > extend here, and it's not clear at all why. > > 4. Given the special rules where assignments are lifted to > > constructions, I want to know when that occurs (maybe that is > > already > > spec-ed wrt postblit?) > > > > - Manu > > Copy construction is used solely when the object was not > initialized and it > cannot be used otherwise. Consider this example: > > struct A > { > immutable int a = 7; > @implicit this(ref A another) > { > this.a = A.a; // first assignment over .init state > - ok > } > > void opAssign(A rhs) > { > this.a = rhs.a; > } > } > > void main() > { > A a = A(2); > A b = a; // initialization -> calls copy > constructor > b = a; // cannot call copy constructor because > it will > // modify immutable; call opAssign. > } > > The first assignment of b calls the copy constructor and the > immutable field is > initialized; later assignments to b.a will result in "modify > immutable" error. > The second assignment of b cannot call the copy constructor > because it would then modify an initialized immutable field. > However, with opAssign the compiler knows that A.a cannot be > modified because it is immutable. Right. This is all obvious and intuitive. What I'm hearing is that under this proposal, copy constructors and assignment operators DO come in pairs (just like in C++), but that's not mentioned here in this DIP. Since this proposal will introduce that recommended pattern from C++, it may be worth mentioning.
Re: Copy Constructor DIP
On Wednesday, 11 July 2018 at 07:40:32 UTC, RazvanN wrote: But there's a super explicit `@implicit` thing written right there... so should we expect that an *explicit* call to the copy constructor is not allowed? Or maybe it is allowed and `@implicit` is a lie? The @implicit is there to point out that you cannot call that method explicitly; it gets called for you implicitly when you construct an object as a copy of another object. Can be explicit constructor overloaded with implicit constructor when both have same signature?
Re: Copy Constructor DIP
On Wednesday, 11 July 2018 at 07:40:32 UTC, RazvanN wrote: But there's a super explicit `@implicit` thing written right there... so should we expect that an *explicit* call to the copy constructor is not allowed? Or maybe it is allowed and `@implicit` is a lie? The @implicit is there to point out that you cannot call that method explicitly; it gets called for you implicitly when you construct an object as a copy of another object. How is this different from other types of constructors or destructors? I also very much dislike the syntax - it makes no sense to me at all. I commented on the PR itself asking why it differs so much from C++ - specifically, what's bad about the C++ way of doing things there that we want to avoid? Atila
Re: Copy Constructor DIP
On Tuesday, 10 July 2018 at 10:47:04 UTC, RazvanN wrote: [1] https://github.com/dlang/DIPs/pull/129 Thanks for making the DIP. I can't get this code to compile (my struct has an `int i` field): static foreach (i, ref field; src.tupleof) this.tupleof[i] = field; Error: constant value src.i cannot be ref https://run.dlang.io/is/qeugC8 Removing `static` works. Otherwise I tried changing `ref` to `alias`: Error: variable src cannot be read at compile time But this shorter code seems to work fine: this.tupleof = src.tupleof;
Re: Copy Constructor DIP
But there's a super explicit `@implicit` thing written right there... so should we expect that an *explicit* call to the copy constructor is not allowed? Or maybe it is allowed and `@implicit` is a lie? The @implicit is there to point out that you cannot call that method explicitly; it gets called for you implicitly when you construct an object as a copy of another object.
Re: Copy Constructor DIP
On Tuesday, 10 July 2018 at 20:58:09 UTC, Manu wrote: On Tue, 10 Jul 2018 at 03:50, RazvanN via Digitalmars-d wrote: Hi everyone! I managed to put together a first draft of the DIP for adding the copy constructor to the language [1]. If anyone is interested, please take a look. Suggestions and comments about technical aspects and wording are all welcome. Thanks, RazvanN [1] https://github.com/dlang/DIPs/pull/129 I feel there's some things missing. 1. Explain the need and reasoning behind `@implicit`... that's weird It is a simple way of defining a copy constructor without the need of adding new keywords and with minimal additions to the parser. and I don't like it at face value. 2. It looks like copy constructors are used to perform assignments (and not constructions)... but, there is also opAssign. What gives? Eg: S b = a; // <- copy construction? looks like an assignment. And not: S b = S(a); // <- actually looks like a construction, but this syntax seems to not be intended (and rightly so, it's pretty terrible) 3. In C++, copy constructors and copy assignment operators come in pairs (which is totally lame!), but we don't see that same pattern extend here, and it's not clear at all why. 4. Given the special rules where assignments are lifted to constructions, I want to know when that occurs (maybe that is already spec-ed wrt postblit?) - Manu Copy construction is used solely when the object was not initialized and it cannot be used otherwise. Consider this example: struct A { immutable int a = 7; @implicit this(ref A another) { this.a = A.a; // first assignment over .init state - ok } void opAssign(A rhs) { this.a = rhs.a; } } void main() { A a = A(2); A b = a; // initialization -> calls copy constructor b = a; // cannot call copy constructor because it will // modify immutable; call opAssign. } The first assignment of b calls the copy constructor and the immutable field is initialized; later assignments to b.a will result in "modify immutable" error. The second assignment of b cannot call the copy constructor because it would then modify an initialized immutable field. However, with opAssign the compiler knows that A.a cannot be modified because it is immutable.
Re: Copy Constructor DIP
On Tue, 10 Jul 2018 at 15:23, Jonathan M Davis via Digitalmars-d wrote: > > On Tuesday, 10 July 2018 14:58:09 MDT Manu via Digitalmars-d wrote: > > 2. It looks like copy constructors are used to perform assignments > > (and not constructions)... but, there is also opAssign. What gives? > > Eg: > > S b = a; // <- copy construction? looks like an assignment. > > And not: > > S b = S(a); // <- actually looks like a construction, but this > > syntax seems to not be intended (and rightly so, it's pretty terrible) > > S b = a; > > has never been assignment in either C++ or D. It's initialization / > construction, which means that it calls a constructor - be that a postblit > constructor or a copy constructor. Assignment only occurs when you're giving > an existing object a new value. I know this, but it's not syntactically obvious, it just depends on the fact that you already know that fact... I feel a DIP about copy construction needs to have some text explaining that, and where the edges are. Is an initialisation assignment can use a copy constructor, why can't a normal assignment implicitly use a copy constructor? (implicit destruct then copy-construct) > And why would > > S b = S(a); > > not be intended? Sure, it's kind of pointless if a is an S, but if you have > a copy constructor, it makes perfect sense that S(a) would work and would be > pretty bizarre if it didn't, since it's explicitly calling the copy > constructor. But there's a super explicit `@implicit` thing written right there... so should we expect that an *explicit* call to the copy constructor is not allowed? Or maybe it is allowed and `@implicit` is a lie? > It even works right now if you give S a constructor that takes > an S. It just isn't actually treated as a proper copy constructor at the > moment, since that's currently the postblit constructor's job. Current language doesn't have `@implicit` written anywhere...
Re: Copy Constructor DIP
On Tuesday, 10 July 2018 14:58:09 MDT Manu via Digitalmars-d wrote: > 2. It looks like copy constructors are used to perform assignments > (and not constructions)... but, there is also opAssign. What gives? > Eg: > S b = a; // <- copy construction? looks like an assignment. > And not: > S b = S(a); // <- actually looks like a construction, but this > syntax seems to not be intended (and rightly so, it's pretty terrible) S b = a; has never been assignment in either C++ or D. It's initialization / construction, which means that it calls a constructor - be that a postblit constructor or a copy constructor. Assignment only occurs when you're giving an existing object a new value. And why would S b = S(a); not be intended? Sure, it's kind of pointless if a is an S, but if you have a copy constructor, it makes perfect sense that S(a) would work and would be pretty bizarre if it didn't, since it's explicitly calling the copy constructor. It even works right now if you give S a constructor that takes an S. It just isn't actually treated as a proper copy constructor at the moment, since that's currently the postblit constructor's job. - Jonathan M Davis
Re: Copy Constructor DIP
On Tue, 10 Jul 2018 at 03:50, RazvanN via Digitalmars-d wrote: > > Hi everyone! > > I managed to put together a first draft of the DIP for adding the > copy constructor to the language [1]. If anyone is interested, > please take a look. Suggestions and comments about technical > aspects and wording are all welcome. > > Thanks, > RazvanN > > [1] https://github.com/dlang/DIPs/pull/129 I feel there's some things missing. 1. Explain the need and reasoning behind `@implicit`... that's weird and I don't like it at face value. 2. It looks like copy constructors are used to perform assignments (and not constructions)... but, there is also opAssign. What gives? Eg: S b = a; // <- copy construction? looks like an assignment. And not: S b = S(a); // <- actually looks like a construction, but this syntax seems to not be intended (and rightly so, it's pretty terrible) 3. In C++, copy constructors and copy assignment operators come in pairs (which is totally lame!), but we don't see that same pattern extend here, and it's not clear at all why. 4. Given the special rules where assignments are lifted to constructions, I want to know when that occurs (maybe that is already spec-ed wrt postblit?) - Manu
Re: Copy Constructor DIP
On Tuesday, 10 July 2018 at 13:38:33 UTC, Jonathan M Davis wrote: He thought that it was critical that the invariant be valid when opAssign was called - and there are cases where that's arguably true - but since it doesn't work once you try to do fancier stuff like emplace, I'm of the opinion that invariants are unfortunately a waste of time - even without getting into the issue of init values. - Jonathan M Davis moveEmplace bypasses opAssign, since it memcopies directly. However, it resets its source value to T.init... so if T.init isn't valid, you simply crash whenever the source value goes out of scope. Using types with an invalid T.init feels like playing musical chairs with a crash. You can shuffle things around, and make some parts work, but *something* is always left crashing at the end.
Re: Copy Constructor DIP
On Tuesday, 10 July 2018 at 14:28:09 UTC, FeepingCreature wrote: On Tuesday, 10 July 2018 at 13:38:33 UTC, Jonathan M Davis wrote: [...] Using types with an invalid T.init feels like playing musical chairs with a crash. You can shuffle things around, and make some parts work, but *something* is always left crashing at the end. This is where I feel being able to redefine T.init would be useful, but as Andrei said, that is beyond the scope of this DIP
Re: Copy Constructor DIP
On 07/10/2018 06:52 AM, Guillaume Piolat wrote: On Tuesday, 10 July 2018 at 10:47:04 UTC, RazvanN wrote: Hi everyone! I managed to put together a first draft of the DIP for adding the copy constructor to the language [1]. If anyone is interested, please take a look. Suggestions and comments about technical aspects and wording are all welcome. Thanks, RazvanN [1] https://github.com/dlang/DIPs/pull/129 Does it allow to remove the "T.init must always be valid for structs" rule? That's not part of this proposal's charter.
Re: Copy Constructor DIP
On Tuesday, 10 July 2018 06:34:34 MDT Guillaume Piolat via Digitalmars-d wrote: > On Tuesday, 10 July 2018 at 11:58:53 UTC, Jonathan M Davis wrote: > >> Does it allow to remove the "T.init must always be valid for > >> structs" rule? > > > > Why would it? init is the state of the object before any > > constructor runs, and quite a few things rely on it. The fact > > that we've allowed default initialization to be disabled > > already causes plenty of problems as it is. It's occasionally > > useful, but it definitely complicates things. D was designed > > with the idea that every type has an init value. > > > > What problem are you trying to solve here? > > > > - Jonathan M Davis > > None, I was just reacting to > https://medium.com/@feepingcreature/d-structs-dont-work-for-domain-data-c0 > 9332349f43 > > Proper D structs almost require having a T.init that is valid, > and in turn many public members may get to check for internal > validity before doing things. This is in stark contrast to C++ > where "proper" constructor is guaranteed so you don't check such > validity. > > What is a "correct" T.init for a mutex RAII struct? It is a null > handle value. It's obviously an invalid value for any purpose, > but the D wrapper has to be a valid struct. It's not necessarily expected that all of the functions on T.init are going to work. It's the stuff like copying it, assigning it, destroying it, etc. that have to work (and aside from destruction, you can even @disable them if you need to). Not even toString actually needs to work. It's just that it's so common for folks to print values that it gets annoying when it doesn't work. And yes, if T.init does not match the invariant, then you can start running into problems, though it isn't necessarily fatal. Basically, in that case, you're forced to disable default initialization and avoid actually doing much with the init value. Honestly though, the reason that I think that invariants are terrible has nothing to do with T.init specifically but with opAssign. The invariant gets checked before opAssign, and in many cases, this makes sense, but in the case where you're doing something like void initialization or using emplace on uninitialized memory, it blows up in your face if the garbage that happened to be in the struct didn't match the invariant. The same would happen if T.init does not pass the invariant, but T.init isn't required to hit the problem. It's the fact that you can't bypass the invariant when giving the object a new value that's the main problem. If both assignment and copying worked without checking the invariant, then invariants would be a _lot_ more useful. Unfortunately, when I argued about this quite a bit with regards to opAssign several years ago, Walter didn't agree at all. He thought that it was critical that the invariant be valid when opAssign was called - and there are cases where that's arguably true - but since it doesn't work once you try to do fancier stuff like emplace, I'm of the opinion that invariants are unfortunately a waste of time - even without getting into the issue of init values. - Jonathan M Davis
Re: Copy Constructor DIP
On Tuesday, 10 July 2018 at 11:58:53 UTC, Jonathan M Davis wrote: Does it allow to remove the "T.init must always be valid for structs" rule? Why would it? init is the state of the object before any constructor runs, and quite a few things rely on it. The fact that we've allowed default initialization to be disabled already causes plenty of problems as it is. It's occasionally useful, but it definitely complicates things. D was designed with the idea that every type has an init value. What problem are you trying to solve here? - Jonathan M Davis None, I was just reacting to https://medium.com/@feepingcreature/d-structs-dont-work-for-domain-data-c09332349f43 Proper D structs almost require having a T.init that is valid, and in turn many public members may get to check for internal validity before doing things. This is in stark contrast to C++ where "proper" constructor is guaranteed so you don't check such validity. What is a "correct" T.init for a mutex RAII struct? It is a null handle value. It's obviously an invalid value for any purpose, but the D wrapper has to be a valid struct.
Re: Copy Constructor DIP
I'm not keen on the added attribute. Something along the lines of: this(this; ref Foo foo) {} might look a little better no? And now no super special attribute to worry about parsing.
Re: Copy Constructor DIP
On Tuesday, 10 July 2018 04:52:18 MDT Guillaume Piolat via Digitalmars-d wrote: > On Tuesday, 10 July 2018 at 10:47:04 UTC, RazvanN wrote: > > Hi everyone! > > > > I managed to put together a first draft of the DIP for adding > > the copy constructor to the language [1]. If anyone is > > interested, please take a look. Suggestions and comments about > > technical aspects and wording are all welcome. > > > > Thanks, > > RazvanN > > > > [1] https://github.com/dlang/DIPs/pull/129 > > Does it allow to remove the "T.init must always be valid for > structs" rule? Why would it? init is the state of the object before any constructor runs, and quite a few things rely on it. The fact that we've allowed default initialization to be disabled already causes plenty of problems as it is. It's occasionally useful, but it definitely complicates things. D was designed with the idea that every type has an init value. What problem are you trying to solve here? - Jonathan M Davis
Re: Copy Constructor DIP
On Tuesday, 10 July 2018 at 10:47:04 UTC, RazvanN wrote: Hi everyone! I managed to put together a first draft of the DIP for adding the copy constructor to the language [1]. If anyone is interested, please take a look. Suggestions and comments about technical aspects and wording are all welcome. Thanks, RazvanN [1] https://github.com/dlang/DIPs/pull/129 Does it allow to remove the "T.init must always be valid for structs" rule?
Copy Constructor DIP
Hi everyone! I managed to put together a first draft of the DIP for adding the copy constructor to the language [1]. If anyone is interested, please take a look. Suggestions and comments about technical aspects and wording are all welcome. Thanks, RazvanN [1] https://github.com/dlang/DIPs/pull/129