Thread overview
[Issue 24324] A default-initialized variable is not identical to its init value when it contains a default-initialized member variable that is a dynamic array
Jan 08, 2024
RazvanN
Jan 08, 2024
Bastiaan Veelo
Jan 08, 2024
Jonathan M Davis
Jan 08, 2024
Jonathan M Davis
January 08, 2024
https://issues.dlang.org/show_bug.cgi?id=24324

RazvanN <razvan.nitu1305@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |razvan.nitu1305@gmail.com

--- Comment #1 from RazvanN <razvan.nitu1305@gmail.com> ---
My first impression on this was that the compiler probably stores the struct initializer in the data/rodata segment and when an instance is created, it allocates heap memory with the GC and copies over the dynamic array elements. However, looking at the compiler code, things get weirder. It seems that in this case, the compiler lowers `S.init` to a Struct Literal Expression "S([1, 2, 3])" (i.e. it does not store a literal representation in the data/rodata segment), whereas when it encounters `S s1;` is uses a global symbol to initialize s1. That is why s1 and s2 have the same bit representation but `S.init` does not.

A consequence of this is that `assert(S.init is S.init)` fails in this case. Which is kind of absurd. However, I would argue that when dynamic arrays are involved the backend is free to do whatever it sees fit with regards to the addresses of dynamic arrays, so using `is` in this case (as opposed to opEquals) is brittle.

--
January 08, 2024
https://issues.dlang.org/show_bug.cgi?id=24324

Bastiaan Veelo <Bastiaan@Veelo.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |Bastiaan@Veelo.net

--- Comment #2 from Bastiaan Veelo <Bastiaan@Veelo.net> ---
I argue that it should be made an error to define an initializer of a non-shared non-static field that allocates. Thread: https://forum.dlang.org/post/ogvubzgprghefclgluce@forum.dlang.org

If you *want* the array to be shared across all instances, the field should be declared `shared static`.

In https://forum.dlang.org/post/t7vm2o$p4q$1@digitalmars.com, Steven notes that the array is shared among different threads, even, as discovered in https://issues.dlang.org/show_bug.cgi?id=2947.

--
January 08, 2024
https://issues.dlang.org/show_bug.cgi?id=24324

--- Comment #3 from Jonathan M Davis <issues.dlang@jmdavisProg.com> ---
(In reply to RazvanN from comment #1)
> A consequence of this is that `assert(S.init is S.init)` fails in this case. Which is kind of absurd. However, I would argue that when dynamic arrays are involved the backend is free to do whatever it sees fit with regards to the addresses of dynamic arrays, so using `is` in this case (as opposed to opEquals) is brittle.

My expectation is that

SomeStruct s;
assert(s is SomeStruct.init);

will pass for every type, and IMHO, if it doesn't, we should rethink what we're doing.

--
January 08, 2024
https://issues.dlang.org/show_bug.cgi?id=24324

--- Comment #4 from Jonathan M Davis <issues.dlang@jmdavisProg.com> ---
(In reply to Bastiaan Veelo from comment #2)
> I argue that it should be made an error to define an initializer of a non-shared non-static field that allocates. Thread: https://forum.dlang.org/post/ogvubzgprghefclgluce@forum.dlang.org
> 
> If you *want* the array to be shared across all instances, the field should be declared `shared static`.
> 
> In https://forum.dlang.org/post/t7vm2o$p4q$1@digitalmars.com, Steven notes that the array is shared among different threads, even, as discovered in https://issues.dlang.org/show_bug.cgi?id=2947.

It may very well be the best call to make cases like this illegal. However, I think that requirement is going to have to take into account whether mutable indirections are involved rather than just whether any allocations take place. Otherwise, cases like SysTime's Rebindable!(immutable(TimeZone)) (which is supposed to be default-initialized to a specific time zone so that the code doesn't have to check for null, but it isn't due to another bug) become illegal. The fact that the TimeZone itself is immutable avoids the issue, whereas it would be an issue if TimeZone weren't immutable.

--
January 09, 2024
https://issues.dlang.org/show_bug.cgi?id=24324

Steven Schveighoffer <schveiguy@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |schveiguy@gmail.com

--- Comment #5 from Steven Schveighoffer <schveiguy@gmail.com> ---
Hm... what appears to be happening here is `S.init` is treated like an enum.

If you have an enum of an array, then it is like you typed in the literal directly.

Mark main as `@nogc` and it fails (Because S.init allocates!)

Even `S s1 = S.init` allocates a new array on the heap.

This is quite unexpected, and I can't believe we haven't seen this before.

I tested via run.dlang.io, and this has happened at least as far back as 2.060.

--