Thread overview | |||||||||
---|---|---|---|---|---|---|---|---|---|
|
February 12, 2015 Attributes lost in TypeInfo, please advise | ||||
---|---|---|---|---|
| ||||
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. |
February 12, 2015 Re: Attributes lost in TypeInfo, please advise | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jakob Ovrum | On Thursday, 12 February 2015 at 04:08:23 UTC, Jakob Ovrum wrote: > Is it possible to call the destructor or postblit constructor directly yes, they are available as obj.__dtor() and obj.__postblit(); But... > and will they correctly destruct/copy recursively No. 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. |
February 12, 2015 Re: Attributes lost in TypeInfo, please advise | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | 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: >> Is it possible to call the destructor or postblit constructor directly > > yes, they are available as obj.__dtor() and obj.__postblit(); But... > >> and will they correctly destruct/copy recursively > > No. Thanks. > 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. |
February 12, 2015 Re: Attributes lost in TypeInfo, please advise | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jakob Ovrum | 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 |
February 12, 2015 Re: Attributes lost in TypeInfo, please advise | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | 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.
>
> -Steve
Yes please. Its also going to genereate more optimal code. Calling the destructor through the TypeInfo leads to two unnecessary indirections.
Kind Regards
Benjamin Thaut
|
February 13, 2015 Re: Attributes lost in TypeInfo, please advise | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | 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. > > -Steve 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. |
February 13, 2015 Re: Attributes lost in TypeInfo, please advise | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jakob Ovrum | On 2/12/15 11:01 PM, Jakob Ovrum wrote:
> 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.
>>
>
> 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.
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.
-Steve
|
Copyright © 1999-2021 by the D Language Foundation