digitalmars.D - Attributes lost in TypeInfo, please advise
- Jakob Ovrum (31/31) Feb 11 2015 Consider the following:
- Adam D. Ruppe (24/27) Feb 11 2015 yes, they are available as obj.__dtor() and obj.__postblit();
- Jakob Ovrum (4/15) Feb 11 2015 Thanks.
- Steven Schveighoffer (12/43) Feb 12 2015 The reason typeid.destroy is used is because it does what you think
- Benjamin Thaut (7/11) Feb 12 2015 Yes please. Its also going to genereate more optimal code.
- Jakob Ovrum (41/45) Feb 12 2015 I think these do the right thing with only marginal overhead:
- Steven Schveighoffer (10/52) Feb 13 2015 Thanks, but I wonder, aren't we simply duplicating what the compiler
Consider the following: --- struct S { ~this() safe {} } void foo() safe { S s; // OK } void bar() safe { S s; destroy(s); // test.d(15): Error: safe function 'test.bar' cannot call system function 'object.destroy!(S).destroy' } --- `destroy` is used in algorithms using low-level operations like `emplace[Ref]`, and while `destroy` itself is a template and thus enjoys attribute inference, it calls the non-generic typeid(T).destroy function, which is unconditionally system. This unsafety then propagates all the way up to high-level code that is otherwise inferred to be safe. The `postblit` TypeInfo method, used from `emplace[Ref]`, has the same problem. Is it possible to call the destructor or postblit constructor directly, and will they correctly destruct/copy recursively like TypeInfo.destroy/postblit do? If so, we should change `destroy` and `emplaceImpl` to use direct calls ASAP.
Feb 11 2015
On Thursday, 12 February 2015 at 04:08:23 UTC, Jakob Ovrum wrote:Is it possible to call the destructor or postblit constructor directlyyes, they are available as obj.__dtor() and obj.__postblit(); But...and will they correctly destruct/copy recursivelyNo. extern(C) trusted void printf(const char*); struct Child { safe ~this() { printf("child dtor\n"); } } struct Parent { Child c; safe ~this() { printf("parent dtor\n"); } } void main() safe { Parent p; p.__dtor(); } parent dtor // this is the one from the p.__dtor // note it did NOT run child dtor parent dtor // the natural destruction from going out of scope child dtor // ...which also calls the child So you'd have to loop through all members in a custom destroy function and call them yourself. Then attribute inference should work.
Feb 11 2015
On Thursday, 12 February 2015 at 04:18:06 UTC, Adam D. Ruppe wrote:On Thursday, 12 February 2015 at 04:08:23 UTC, Jakob Ovrum wrote:Thanks.Is it possible to call the destructor or postblit constructor directlyyes, they are available as obj.__dtor() and obj.__postblit(); But...and will they correctly destruct/copy recursivelyNo.So you'd have to loop through all members in a custom destroy function and call them yourself. Then attribute inference should work.I feared as much. I'll cook something up and send a PR for review.
Feb 11 2015
On 2/11/15 11:08 PM, Jakob Ovrum wrote:Consider the following: --- struct S { ~this() safe {} } void foo() safe { S s; // OK } void bar() safe { S s; destroy(s); // test.d(15): Error: safe function 'test.bar' cannot call system function 'object.destroy!(S).destroy' } --- `destroy` is used in algorithms using low-level operations like `emplace[Ref]`, and while `destroy` itself is a template and thus enjoys attribute inference, it calls the non-generic typeid(T).destroy function, which is unconditionally system. This unsafety then propagates all the way up to high-level code that is otherwise inferred to be safe. The `postblit` TypeInfo method, used from `emplace[Ref]`, has the same problem. Is it possible to call the destructor or postblit constructor directly, and will they correctly destruct/copy recursively like TypeInfo.destroy/postblit do? If so, we should change `destroy` and `emplaceImpl` to use direct calls ASAP.The reason typeid.destroy is used is because it does what you think calling the destructor directly should do. As Adam pointed out, __dtor does not do it right. Note, there is a hidden function generated by the compiler, that TypeInfo.destroy maps to. There is no way to call it directly. There is a bug report that shows why we do it that way (recall that destroy was once named clear): https://issues.dlang.org/show_bug.cgi?id=5667 I think given the necessity of the above (which was not discussed or noticed in that bug report), we should add a way to call the true destructor properly in the compiler. -Steve
Feb 12 2015
On Thursday, 12 February 2015 at 12:59:39 UTC, Steven Schveighoffer wrote:I think given the necessity of the above (which was not discussed or noticed in that bug report), we should add a way to call the true destructor properly in the compiler. -SteveYes please. Its also going to genereate more optimal code. Calling the destructor through the TypeInfo leads to two unnecessary indirections. Kind Regards Benjamin Thaut
Feb 12 2015
On Thursday, 12 February 2015 at 12:59:39 UTC, Steven Schveighoffer wrote:I think given the necessity of the above (which was not discussed or noticed in that bug report), we should add a way to call the true destructor properly in the compiler. -SteveI think these do the right thing with only marginal overhead: --- void destructRecurse(S)(ref S s) if (is(S == struct)) { static if (__traits(hasMember, S, "__dtor")) s.__dtor(); foreach_reverse (ref field; s.tupleof) { alias Field = typeof(field); static if (is(Field == struct) && hasElaborateDestructor!Field) destructRecurse(field); } } void postblitRecurse(S)(ref S s) if (is(S == struct)) { foreach (ref field; s.tupleof) { alias Field = typeof(field); static if (is(Field == struct) && hasElaborateCopyConstructor!Field) postblitRecurse(field); } static if (__traits(hasMember, S, "__postblit")) s.__postblit(); } --- I notice now it is missing proper handling of fixed-length arrays: I'll add that. Anything else missing? Efficiency-wise they should at least be a lot better than the status quo - two indirect calls. For absolutely optimal performance it relies on the inliner, but if it is demonstrated to be a problem compared to the compiler-generated solution, it could always generate optimal code with some hands-on string mixins :) I am aware that std.traits is not available in object_.d - the hasElaborate* templates are fairly simple and easy to reimplement.
Feb 12 2015
On 2/12/15 11:01 PM, Jakob Ovrum wrote:On Thursday, 12 February 2015 at 12:59:39 UTC, Steven Schveighoffer wrote:Thanks, but I wonder, aren't we simply duplicating what the compiler does? It seems kind of wasteful to have 2 identical functions. Plus, if any behavior changes, I'd rather have one place to change it. Last thing we need is subtle differences between builtin destruction and explicit destruction. I really think either the compiler should be redirected to call this function (ideal) or we should be able to call the compiler function directly. -SteveI think given the necessity of the above (which was not discussed or noticed in that bug report), we should add a way to call the true destructor properly in the compiler.I think these do the right thing with only marginal overhead: --- void destructRecurse(S)(ref S s) if (is(S == struct)) { static if (__traits(hasMember, S, "__dtor")) s.__dtor(); foreach_reverse (ref field; s.tupleof) { alias Field = typeof(field); static if (is(Field == struct) && hasElaborateDestructor!Field) destructRecurse(field); } } void postblitRecurse(S)(ref S s) if (is(S == struct)) { foreach (ref field; s.tupleof) { alias Field = typeof(field); static if (is(Field == struct) && hasElaborateCopyConstructor!Field) postblitRecurse(field); } static if (__traits(hasMember, S, "__postblit")) s.__postblit(); } --- I notice now it is missing proper handling of fixed-length arrays: I'll add that. Anything else missing? Efficiency-wise they should at least be a lot better than the status quo - two indirect calls. For absolutely optimal performance it relies on the inliner, but if it is demonstrated to be a problem compared to the compiler-generated solution, it could always generate optimal code with some hands-on string mixins :) I am aware that std.traits is not available in object_.d - the hasElaborate* templates are fairly simple and easy to reimplement.
Feb 13 2015