Related to issue #7319 I wrote this program:
---------8<-------------
import std.typetuple;
import std.stdio;
struct StuffDefault(T) { T[1024 * 1024] m_data; }
struct StuffVoid(T) { T[1024 * 1024] m_data = void; }
struct StuffZero(T) { T[1024 * 1024] m_data = 0; }
void main(string[] args)
{
alias Ts = TypeTuple!(
StuffDefault!void,
StuffVoid!void,
StuffDefault!ubyte,
StuffVoid!ubyte,
StuffZero!ubyte,
StuffVoid!char,
StuffZero!char,
);
// .inits visible at runtime
foreach (T; Ts) {
writefln("Does %s have a superfluous init? %s",
T.stringof, typeid(T).init.ptr is null ? "no" : "YES");
}
}
----------->8------------
Prints:
Does StuffDefault!void have a superfluous init? no
Does StuffVoid!void have a superfluous init? YES
Does StuffDefault!ubyte have a superfluous init? no
Does StuffVoid!ubyte have a superfluous init? YES
Does StuffZero!ubyte have a superfluous init? YES
Does StuffVoid!char have a superfluous init? YES
Does StuffZero!char have a superfluous init? YES
Ironically initializing things as void /creates/ a .init in the TypeInfo in the first place. It is inconsistent and I don't know if there was a plan behind when .init.ptr is null, but it shouldn't be available on void-initialized variables.
What's the use case of TypeInfo.init ? To circumvent the type system in preparing a memory location for a struct or class, such as emplacing them in an malloc'd block/array. For that to work we want to know:
Is the initializer void? -> Do nothing.
Is the initializer all zeroes? -> memset(p, 0, sz);
Otherwise -> memcpy(p, init.ptr, init.length);
If supporting all three is infeasible, the 2nd case should go. I.e.: TypeInfo.init.ptr is only unset for void initializers.
In addition I think I saw one of the TypeInfo_<xyz>.init return a ubyte[] instead of a void[]. So void[] = typeid(T).init would not work. If someone takes on this bug, they might want to look into this as well and make it consistent.
init.ptr is null means that the data should be initialized to zero.
As far as I know, there is no mechanism to say you shouldn't initialize the data.
That being said, I can agree that all of the cases above should have null pointers for init (soon to be called initializer). No reason to store those zeros in the data segment.
Comment #3 by Marco.Leise — 2016-01-18T04:24:04Z
(In reply to Steven Schveighoffer from comment #2)
> As far as I know, there is no mechanism to say you shouldn't initialize the
> data.
That is correct and my thinking is that this was due to the .init/.initializer property being underspecified, since we do have it in the language:
struct Output
{
double[1024] data = void;
}
Instead of:
null => initializer is all zeroes
array of correct length => initializer contains non-zero bytes
we could also define three states:
ptr is null, length is correct => initializer is all zeroes
ptr is null, length == 0 => initializer is void
else => initializer contains non-zero bytes
This would allow us to avoid a memset where the data structure is explicitly declared with all void initializers.
Where the type is statically known, 'x = T.init' can still be more effective, because the compiler can freely chose how to initialize a data structure, based on whether x is already initialized or not, T.init contains a mix of void and non-void fields or if one or more of the fields are overwritten shortly after. typeid(T).initializer is the more blunt tool. T.init is a compiler intrinsic, typeid(T).initializer is data.
Comment #4 by robert.schadek — 2024-12-13T18:32:32Z