www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Extra .tupleof field in structs with disabled postblit blocks

reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
Why (on earth) does

     struct S
     {
          disable this(this);
         int* _ptr;
     }
     pragma(msg, typeof(S.tupleof));

prints

(int*, void*)

when

     struct S
     {
         int* _ptr;
     }
     pragma(msg, typeof(S.tupleof));

prints

(int*)

?!!!

This prevents the trait `mustAddGCRangeOfStructOrUnion` [1] from 
detecting when a container with manual memory management doesn't 
have to be scanned by the GC as in, for instance,

     enum NoGc;
     struct S
     {
          disable this(this); // disable S postlib
          NoGc int* _ptr;
     }
     static assert(!mustAddGCRangeOfStructOrUnion!S); // is false 
when postblit of `S` is disabled

[1] 
https://github.com/nordlow/phobos-next/blob/master/src/gc_traits.d#L81
May 09 2018
next sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 9 May 2018 at 14:07:37 UTC, Per Nordlöw wrote:
 This prevents the trait `mustAddGCRangeOfStructOrUnion` [1] 
 from detecting when a container with manual memory management 
 doesn't have to be scanned by the GC as in, for instance,

     enum NoGc;
     struct S
     {
          disable this(this); // disable S postlib
          NoGc int* _ptr;
     }
     static assert(!mustAddGCRangeOfStructOrUnion!S); // is 
 false when postblit of `S` is disabled

 [1] 
 https://github.com/nordlow/phobos-next/blob/master/src/gc_traits.d#L81
Can we statically check if the postblit has been disabled via disable this(this); ? If so, we can temporarily modify the trait to exclude the last `void*` member of the `S.tuple`. Given that it's always added as the last member.
May 09 2018
next sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 9 May 2018 at 14:20:41 UTC, Per Nordlöw wrote:
 If so, we can temporarily modify the trait to exclude the last 
 `void*` member of the `S.tuple`. Given that it's always added 
 as the last member.
Note that `std.traits.isCopyable!S` cannot be used, because it will return true when `S` has uncopyable members regardless of whether S.tupleof have any extra void* element or not (because of S's disabled postblit).
May 09 2018
prev sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 9 May 2018 at 14:20:41 UTC, Per Nordlöw wrote:
 If so, we can temporarily modify the trait to exclude the last 
 `void*` member of the `S.tuple`. Given that it's always added 
 as the last member.
Also note that pragma(msg, __traits(isDisabled, S.this(this))); fails to compile as Error: identifier expected following `.`, not `this`
May 09 2018
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 9 May 2018 at 14:34:02 UTC, Per Nordlöw wrote:
 On Wednesday, 9 May 2018 at 14:20:41 UTC, Per Nordlöw wrote:
 If so, we can temporarily modify the trait to exclude the last 
 `void*` member of the `S.tuple`. Given that it's always added 
 as the last member.
Also note that pragma(msg, __traits(isDisabled, S.this(this))); fails to compile as Error: identifier expected following `.`, not `this`
Ahh, but both pragma(msg, __traits(isDisabled, S.__postblit)); pragma(msg, __traits(isDisabled, S.__xpostblit)); prints true for a struct with ` disable this(this);` Which one should I pick to check if last element of `S.tupleof` should be discarded?
May 09 2018
parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 9 May 2018 at 14:36:38 UTC, Per Nordlöw wrote:
 On Wednesday, 9 May 2018 at 14:34:02 UTC, Per Nordlöw wrote:
 On Wednesday, 9 May 2018 at 14:20:41 UTC, Per Nordlöw wrote:
 If so, we can temporarily modify the trait to exclude the 
 last `void*` member of the `S.tuple`. Given that it's always 
 added as the last member.
Also note that pragma(msg, __traits(isDisabled, S.this(this))); fails to compile as Error: identifier expected following `.`, not `this`
Ahh, but both pragma(msg, __traits(isDisabled, S.__postblit)); pragma(msg, __traits(isDisabled, S.__xpostblit)); prints true for a struct with ` disable this(this);` Which one should I pick to check if last element of `S.tupleof` should be discarded?
Managed to put together the hack private template mustAddGCRangeOfStructOrUnion(T) if (is(T == struct) || is(T == union)) { import std.traits : hasUDA; import std.meta : anySatisfy; static if (__traits(hasMember, T, "__postblit")) { static if (__traits(isDisabled, T.__postblit)) { enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof[0 .. $ - 1]); } else { enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof); } } else { enum mustAddGCRangeOfStructOrUnion = anySatisfy!(mustAddGCRangeOfMember, T.tupleof); } } defined here https://github.com/nordlow/phobos-next/blob/master/src/gc_traits.d#L81 Destroy.
May 09 2018
prev sibling parent reply Meta <jared771 gmail.com> writes:
On Wednesday, 9 May 2018 at 14:07:37 UTC, Per Nordlöw wrote:
 Why (on earth) does

     struct S
     {
          disable this(this);
         int* _ptr;
     }
     pragma(msg, typeof(S.tupleof));

 prints

 (int*, void*)

 when

     struct S
     {
         int* _ptr;
     }
     pragma(msg, typeof(S.tupleof));

 prints

 (int*)

 ?!!!
I wasn't able to reproduce it on dmd-nightly: https://run.dlang.io/is/9wT8tH What version of the compiler are you using?
May 09 2018
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 9 May 2018 at 17:52:48 UTC, Meta wrote:
 I wasn't able to reproduce it on dmd-nightly: 
 https://run.dlang.io/is/9wT8tH

 What version of the compiler are you using?
Ahh, the struct needs to be in a unittest block for it to happen: struct R { disable this(this); int* _ptr; } unittest { struct S { disable this(this); int* _ptr; } struct T { int* _ptr; } pragma(msg, "R: ", typeof(R.tupleof)); pragma(msg, "S: ", typeof(S.tupleof)); pragma(msg, "T: ", typeof(T.tupleof)); } prints R: (int*) S: (int*, void*) T: (int*) Why is that?
May 09 2018
parent reply Meta <jared771 gmail.com> writes:
On Wednesday, 9 May 2018 at 18:04:40 UTC, Per Nordlöw wrote:
 On Wednesday, 9 May 2018 at 17:52:48 UTC, Meta wrote:
 I wasn't able to reproduce it on dmd-nightly: 
 https://run.dlang.io/is/9wT8tH

 What version of the compiler are you using?
Ahh, the struct needs to be in a unittest block for it to happen: struct R { disable this(this); int* _ptr; } unittest { struct S { disable this(this); int* _ptr; } struct T { int* _ptr; } pragma(msg, "R: ", typeof(R.tupleof)); pragma(msg, "S: ", typeof(S.tupleof)); pragma(msg, "T: ", typeof(T.tupleof)); } prints R: (int*) S: (int*, void*) T: (int*) Why is that?
It's a context pointer to the enclosing function/object/struct. Mark the struct as static to get rid of it.
May 09 2018
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 9 May 2018 at 21:09:12 UTC, Meta wrote:
 It's a context pointer to the enclosing function/object/struct. 
 Mark the struct as static to get rid of it.
Ok, but why an extra void* for `S.tupleof` and not for `T.tupleof` which is also scoped inside a unittest?
May 10 2018
parent reply Uknown <sireeshkodali1 gmail.com> writes:
On Thursday, 10 May 2018 at 11:06:06 UTC, Per Nordlöw wrote:
 On Wednesday, 9 May 2018 at 21:09:12 UTC, Meta wrote:
 It's a context pointer to the enclosing 
 function/object/struct. Mark the struct as static to get rid 
 of it.
Ok, but why an extra void* for `S.tupleof` and not for `T.tupleof` which is also scoped inside a unittest?
I'm guessing T is a POD, so it doesn't need a context pointer, whereas S is not counted as a POD since a member function was disabled.
May 10 2018
parent reply Meta <jared771 gmail.com> writes:
On Thursday, 10 May 2018 at 12:55:36 UTC, Uknown wrote:
 On Thursday, 10 May 2018 at 11:06:06 UTC, Per Nordlöw wrote:
 On Wednesday, 9 May 2018 at 21:09:12 UTC, Meta wrote:
 It's a context pointer to the enclosing 
 function/object/struct. Mark the struct as static to get rid 
 of it.
Ok, but why an extra void* for `S.tupleof` and not for `T.tupleof` which is also scoped inside a unittest?
I'm guessing T is a POD, so it doesn't need a context pointer, whereas S is not counted as a POD since a member function was disabled.
Yes, exactly. From my tests, if you add _any_ member method to a struct, it becomes non-POD. When you think about it, this makes perfect sense, as there's no possible way to access anything through a context pointer if there is no executable code within the struct's scope. As far an disabled postblit: Plain Old Data A struct or union is Plain Old Data (POD) if it meets the following criteria: it is not nested it has no postblits, destructors, or assignment operators it has no ref fields or fields that are themselves non-PO https://docarchives.dlang.io/v2.079.0/spec/struct.html#POD Now if you do this: struct R { disable this(this); int* _ptr; } unittest { struct S { disable this(this); int* _ptr; } struct T { int* _ptr; } pragma(msg, "R: ", typeof(R.tupleof)); pragma(msg, __traits(allMembers, R)); pragma(msg, "S: ", typeof(S.tupleof)); pragma(msg, __traits(allMembers, S)); pragma(msg, "T: ", typeof(T.tupleof)); pragma(msg, __traits(allMembers, T)); } It prints: R: (int*) tuple("__postblit", "_ptr", "__xpostblit", "opAssign") S: (int*, void*) tuple("__postblit", "_ptr", "this", "__xpostblit", "opAssign") T: (int*) tuple("_ptr") So it looks like disabling a struct's postblit actually counts as having a __postblit and __xpostblit function (don't ask me why), in addition to a construction and opAssign... no idea why, and maybe this is a bug, but I bet there's a good reason for it. Anyway, as per my first point, this means it'll need a context pointer unless you mark the struct as static.
May 10 2018
parent Johan Engelen <j j.nl> writes:
On Thursday, 10 May 2018 at 19:14:39 UTC, Meta wrote:
 So it looks like disabling a struct's postblit actually counts 
 as having a __postblit and __xpostblit function (don't ask me 
 why), in addition to a construction and opAssign... no idea 
 why, and maybe this is a bug, but I bet there's a good reason 
 for it.
https://issues.dlang.org/show_bug.cgi?id=18628 -Johan
May 11 2018