March 26
https://issues.dlang.org/show_bug.cgi?id=24454

          Issue ID: 24454
           Summary: Disallow initialization of non-static reference type
                    data members by non-immutable values or rvalues
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: dmd
          Assignee: nobody@puremagic.com
          Reporter: qs.il.paperinik@gmail.com

When a class or struct has a member of reference type (common cases: a class, a slice, an AA, a pointer, but also a struct type with indirections), require that the initializer be an immutable lvalue.

Otherwise, it should be an error.

Example code:
```d
class C { } // not important what’s in there

struct S
{
    static C static_c = new C();
    static const C static_const_c = static_c;
    static immutable immut_c = new immutable C();

    C c1 = new C(); // error: rvalue
    immutable C c2 = new immutable C(); // error: rvalue
    const C c3 = static_c; // error: (possibly) mutable
    immutable C c4 = immut_c; // ok: lvalue && immutable
}
```

Rationale:

Bad case `c1` is bad because default-initialized `S` instances share the same
`c1` value. This is defined behavior, but highly unexpected and
counter-intuitive for programmers coming from other languages such as Java and
C#, where the `new` expression is essentially placed to the constructor.
This instance is mutable, meaning when declaring a `S` variable and mutating
the `c1` member, this change affects all (future) `S` values.

Bad case `c2`: Has the same issue as `c1` except the mutation part. Still, two
default-constructed `S` values have the very same `c1` object, which is
counter-intuitive.
If `C` objects are constructed by a `pure` constructor, this case is largely
not that bad. However, if `C` objects are individually constructed, such that
even with equal parameters, there are meaningful differences between instances,
this can lead to a lot of confusion.

Bad case `c3`: Here it is clear that the instance is the same one, given the programmer understands reference types: The same static instance is used. However, this instance is mutable, so all `S` instances initially share the same state mutable. Making this an error avoids a foot-gun. As the example shows, the fact that the initializer is `const` does not help. A `const` object may (and in this example clearly does) have a mutable object underlying it.

Good case `c4`: While as with `c3`, one needs to understand reference type semantics, the problem is minor. While all `S` instances share the same `c4` object, which is also quite clearly the case, this object is additionally immutable. Even in cases where a programmer mistakes this initialization for a deep copy and incorrectly assumes `S` objects don’t share `c4`s, this conflation is largely unproblematic.

---

The corrective action suggested by the compiler would be to move the initialization to constructors. There, any of the above cases can be achieved, but in particular, the case for `c1` changes meaning largely to how programmers (from other languages such as Java and C#) expect. In this case, if the actual meaning was intended, the programmer must be more explicit about it and e.g. define a private static variable.

It may be noteworthy that D disallows otherwise defined syntax for the mere purpose of not confusing programmers with experience in other languages. It is designed like that initially (e.g. operator precedence of bitwise and comparison) and introduced breaking changes, e.g. https://issues.dlang.org/show_bug.cgi?id=16001

--