www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Member function increases size of a struct defined in a function - is

reply Ferdinand Majerech <dummy gmail.com> writes:
Ran into this while playing around with std.bitmanip.bitfields 
for packet protocol fields.

```
void fun()
{
     struct InnerFun
     {
         ubyte a;
         void fun() {}
     }
     struct InnerNoFun
     {
         ubyte a;
     }
     pragma(msg, "InnerFun ",   InnerFun.sizeof);  // prints 16
     pragma(msg, "InnerNoFun ", InnerNoFun.sizeof); // prints 1
}
```

Size of InnerFun is 16 while size of InnerNoFun is 1 - this took 
a while to debug as I could not figure out why my protocol 
headers are unexpectedly large - `align(1)` did not help.

This does not happen if the structs are defined outside of a 
function.

Is this the result of some D feature?
I can imagine this could happen if InnerFun had a silently added 
context pointer, but I don't know of such a feature in D.


Reported here:
https://issues.dlang.org/show_bug.cgi?id=20564
remove if this is expected behavior.
Feb 07 2020
next sibling parent Ferdinand Majerech <dummy gmail.com> writes:
On Friday, 7 February 2020 at 12:36:23 UTC, Ferdinand Majerech 
wrote:
 Ran into this while playing around with std.bitmanip.bitfields 
 for packet protocol fields.

 [...]
Never mind, got a reply on bugzilla (https://issues.dlang.org/show_bug.cgi?id=20564), this is a known issue.
Feb 07 2020
prev sibling parent reply Max Samukha <maxsamukha gmail.com> writes:
On Friday, 7 February 2020 at 12:36:23 UTC, Ferdinand Majerech 
wrote:

 Is this the result of some D feature?
 I can imagine this could happen if InnerFun had a silently 
 added context pointer, but I don't know of such a feature in D.
A pointer to the environment is a added to InnerFun for some reason (probably, a bug). You can work around by marking InnerFun 'static': void fun() { static struct InnerFun { ubyte a; void fun() {} } ... }
Feb 07 2020
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Feb 07, 2020 at 12:50:11PM +0000, Max Samukha via Digitalmars-d wrote:
 On Friday, 7 February 2020 at 12:36:23 UTC, Ferdinand Majerech wrote:
 
 Is this the result of some D feature?
 I can imagine this could happen if InnerFun had a silently added
 context pointer, but I don't know of such a feature in D.
 
A pointer to the environment is a added to InnerFun for some reason (probably, a bug).
It's not a bug; the struct is declared inside function scope, and so member functions can access function local variables. Therefore a context pointer is necessary.
 You can work around by marking InnerFun 'static':
[...] Yes, this disables access to function local variables from inside the struct, so it eliminates the context pointer. T -- To provoke is to call someone stupid; to argue is to call each other stupid.
Feb 07 2020
parent reply Max Samukha <maxsamukha gmail.com> writes:
On Friday, 7 February 2020 at 18:58:54 UTC, H. S. Teoh wrote:
 On Fri, Feb 07, 2020 at 12:50:11PM +0000, Max Samukha via 
 Digitalmars-d wrote:
 On Friday, 7 February 2020 at 12:36:23 UTC, Ferdinand Majerech 
 wrote:
 
 Is this the result of some D feature?
 I can imagine this could happen if InnerFun had a silently 
 added
 context pointer, but I don't know of such a feature in D.
 
A pointer to the environment is a added to InnerFun for some reason (probably, a bug).
It's not a bug; the struct is declared inside function scope, and so member functions can access function local variables. Therefore a context pointer is necessary.
It is not necessary in this case, because the member function doesn't reference any variables in the outer function.
 You can work around by marking InnerFun 'static':
[...] Yes, this disables access to function local variables from inside the struct, so it eliminates the context pointer.
Yes, but the compiler could infer that the context pointer is not necessary. Maybe it can't, who knows. The bug report has been open for 7 years without any response from language maintainers.
 T
Feb 07 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/7/20 4:31 PM, Max Samukha wrote:
 On Friday, 7 February 2020 at 18:58:54 UTC, H. S. Teoh wrote:
 Yes, this disables access to function local variables from inside the 
 struct, so it eliminates the context pointer.
Yes, but the compiler could infer that the context pointer is not necessary. Maybe it can't, who knows. The bug report has been open for 7 years without any response from language maintainers.
There are some cases in D which *could* be done with enough analysis, but aren't because it would complicate the compiler for little benefit (or make the language dependent on how clever the compiler is). This is one of those cases. Just add static to the struct declaration. -Steve
Feb 07 2020
parent reply Max Samukha <maxsamukha gmail.com> writes:
On Friday, 7 February 2020 at 21:38:52 UTC, Steven Schveighoffer 
wrote:

 There are some cases in D which *could* be done with enough 
 analysis, but aren't because it would complicate the compiler 
 for little benefit (or make the language dependent on how 
 clever the compiler is).

 This is one of those cases. Just add static to the struct 
 declaration.

 -Steve
What analysis? Doesn't compiler already know the set of variables in the closure environment?
Feb 07 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/7/20 5:27 PM, Max Samukha wrote:
 On Friday, 7 February 2020 at 21:38:52 UTC, Steven Schveighoffer wrote:
 
 There are some cases in D which *could* be done with enough analysis, 
 but aren't because it would complicate the compiler for little benefit 
 (or make the language dependent on how clever the compiler is).

 This is one of those cases. Just add static to the struct declaration.
What analysis? Doesn't compiler already know the set of variables in the closure environment?
Yeah true. It would have to, while compiling the struct functions, add references to the closure, which means it would know if any exists by the time it's done compiling the struct functions. However, it might need to know that before compiling. It could really be based on how the compilation is implemented, making it difficult to figure this out. The "add a closure reference if there are any functions" seems like a really easy thing to just do and not worry about it. And honestly, just putting static in front of the struct is also easy. -Steve
Feb 07 2020
parent reply Max Samukha <maxsamukha gmail.com> writes:
On Friday, 7 February 2020 at 22:41:25 UTC, Steven Schveighoffer 
wrote:

 However, it might need to know that before compiling.

 It could really be based on how the compilation is implemented, 
 making it difficult to figure this out.

 The "add a closure reference if there are any functions" seems 
 like a really easy thing to just do and not worry about it. And 
 honestly, just putting static in front of the struct is also 
 easy.

 -Steve
Sorry for the late reply. I agree it is not a big issue. On the other hand, to draw an (probably far-fetched) analogy with C++ structs, I would be surprised if the compiler added a vtable pointer to the struct in the absence of virtual functions.
Feb 11 2020
parent reply Dennis <dkorpel gmail.com> writes:
On Tuesday, 11 February 2020 at 11:32:47 UTC, Max Samukha wrote:
 Sorry for the late reply. I agree it is not a big issue. On the 
 other hand, to draw an (probably far-fetched) analogy with C++ 
 structs, I would be surprised if the compiler added a vtable 
 pointer to the struct in the absence of virtual functions.
The thing is, because of D's static introspection and order-independent declarations things can get very complicated very quick. Here is an example that can be compiled two ways: ``` import std; void main() { string str = "from closure"; struct S { int b; string method() { static if (S.sizeof > int.sizeof) { return str; // current behavior } else { return "constant"; // also a valid option } } } writeln(S.init.method); } ``` Of course you can account for paradoxical cases like this, and there are cases where the compiler already does: ``` struct S { int a = 3; static if (S.sizeof == int.sizeof) { int b = 3; } static if (S.sizeof == int.sizeof) { int c = 3; } } ```
 variable onlineapp.S.c cannot be further field because it will 
 change the determined S size
But the takeaway is: even if you make a decision a bit more intelligently, it can increase compiler complexity in unexpected ways.
Feb 11 2020
parent Max Samukha <maxsamukha gmail.com> writes:
On Tuesday, 11 February 2020 at 13:04:01 UTC, Dennis wrote:

 But the takeaway is: even if you make a decision a bit more 
 intelligently, it can increase compiler complexity in 
 unexpected ways.
Yes, I am aware of the problem: struct S { int a; // has the same problem as sizeof but compiles static if (__traits(allMembers, S).length == 1) { int b; } } Don't know what would I do about it.
Feb 11 2020