digitalmars.dip.ideas - Fixing tail mutable TLS/field initializers
- Nick Treleaven (46/46) Jun 07 ```d
- Nick Treleaven (12/24) Jun 10 Rather:
- Steven Schveighoffer (10/17) Jun 10 Yes!
- Dukc (11/20) Jun 12 Unless we want to disallow array literals for mutable static variables,
```d int[] x = [1, 2, 3]; ``` The above is really like: ```d __gshared gsx = [1, 2, 3]; int[] x = gsx; ``` Except that lowering is not valid D, because gsx 'cannot be read at compile time'. So if one thread mutates `x[0]`, even though `x` is thread-local, existing and new threads will get their `x[0]` contents mutated too (assuming `x` still points to the initializer data). Note that any tail mutable initializer has the same problem, not just array literals. It gets worse with an aggregate type T field initializer - a mutable instance can mutate the initializer contents of an `immutable T` instance field: https://issues.dlang.org/show_bug.cgi?id=10376 The immutable violation problem must be fixed. There are 2 solutions for that proposed: * Timon (in 2013): Having two versions of the initializer, one for mutable field instances in the writable data segment and one for immutable field instances in the non-writable data segment. Possibly this solution could cause subtle issues, like not allowing a temporary unique mutable instance to be cast to immutable (e.g. pure factory functions). * Walter: Disallowing constructing an `immutable(T)` if it has fields pointing to mutable initializers. Instead, use `cast(immutable)` on a new instance, which is not allowed in safe code. This could mean such library types not unit tested with immutable won't work with immutable in user code, that otherwise could do (bug-prone, but possible to use correctly). Neither of these attempt to solve the problem of accidental mutation of a field when the programmer does not expect the initializer to be shared across instances. That is something that gets discovered repeatedly (e.g. [yesterday](https://forum.dlang.org/post/ldfagjdbjybadqjpjce forum.dlang.org)). And has been filed as a bug many times (see duplicates and duplicates of duplicates!): https://issues.dlang.org/show_bug.cgi?id=2947 Given that we will have editions, ideally we would disallow any tail mutable initializer for both fields and thread-local variables. They are bug-prone - use a constructor instead if a unique initializer is intended. One reason why we might not want to disallow them is if it makes porting existing code to the next edition too awkward. So we would need to investigate that.
Jun 07
On Friday, 7 June 2024 at 11:05:03 UTC, Nick Treleaven wrote:```d int[] x = [1, 2, 3]; ``` The above is really like: ```d __gshared gsx = [1, 2, 3]; int[] x = gsx; ```Rather: ```d __gshared int[3] gsx = [1, 2, 3]; ``` So `x` is a TLS slice of `__gshared` data. And given that `__gshared` mutable variables are not allowed to be accessed from safe code (including basic data types), tail mutable initializers should also be disallowed in safe code, to prevent data races.So if one thread mutates `x[0]`, even though `x` is thread-local, existing and new threads will get their `x[0]` contents mutated too (assuming `x` still points to the initializer data).Mutable `shared` globals & fields can still be allowed to use tail mutable initializers.
Jun 10
On Friday, 7 June 2024 at 11:05:03 UTC, Nick Treleaven wrote:Given that we will have editions, ideally we would disallow any tail mutable initializer for both fields and thread-local variables. They are bug-prone - use a constructor instead if a unique initializer is intended.Yes! As far as TLS is concerned, I think tail-shared is fine to allow as well. For fields, yeah, we should just eliminate initializers that contain pointers to mutable data. Nobody expects what happens, even seasoned D programmers.One reason why we might not want to disallow them is if it makes porting existing code to the next edition too awkward. So we would need to investigate that.Making it harder to create bugs is a plus, even if it breaks previously buggy code. -Steve
Jun 10
Nick Treleaven kirjoitti 7.6.2024 klo 14.05:```d int[] x = [1, 2, 3]; ``` The above is really like: ```d __gshared gsx = [1, 2, 3]; int[] x = gsx; ``` [snip]Unless we want to disallow array literals for mutable static variables, there's really only one solution: the array has to be duplicated at module initialisation time, any time a thread is initialised: ```d __gshared gsx = [1, 2, 3]; int[] x; static this() { x = gsx.dup; } ```
Jun 12