www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 22562] New: Spec for default initialization is

https://issues.dlang.org/show_bug.cgi?id=22562

          Issue ID: 22562
           Summary: Spec for default initialization is self-contradicting
                    wrt. nested structs
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: dlang.org
          Assignee: nobody puremagic.com
          Reporter: stanislav.blinov gmail.com

The current spec uses .init and "default initializer" interchangeably:

https://dlang.org/spec/property.html#init
https://dlang.org/spec/type.html#basic-data-types (in table header - "Default
initializer (.init)").

In the first link, it also explicitly states that .init for a nested struct
contains null context pointer.

However, these following sections...

https://dlang.org/spec/struct.html#default_struct_init
https://dlang.org/spec/struct.html#default_union_init

...state that:
"Struct/union fields are by default initialized to whatever the Initializer for
the field is, and if none is supplied, to the default initializer for the
field's type."

Then it follows that, if field initializer isn't supplied, that field is
initialized to its .init (i.e. the default initializer). Which is not what
happens when nested structs are involved.

Consider this code:

void main()
{
    int a;
    struct Nested { void foo() { ++a; } }
    static struct Holder { Nested n; }
    union Wrapper { Nested n; }
    Nested n;
    Holder h;
    Wrapper w;

    assert(n.tupleof[$-1]);
    assert(h.n.tupleof[$-1]);
    assert(w.n.tupleof[$-1]);
}

All asserts pass. Which means that neither n nor h.n nor w.n are initialized
with default initializer. If https://dlang.org/spec/property.html#init is
correct, .init and "default initializer" are one and same, and .init of nested
structs contains null context pointer, then:

If a context pointer of n (which is a field, albeit hidden) was to be
default-initialized per field initialization spec
(https://dlang.org/spec/struct.html#default_struct_init), it would've been set
to null.

If h.n was to be default-initialized per field initialization spec
(https://dlang.org/spec/struct.html#default_struct_init), its context pointer
would've been set to null.

If w.n was to be default-initialized per union initialization spec
(https://dlang.org/spec/struct.html#default_union_init), its context pointer
would've been set to null.

I.e. all three asserts should've failed. Yet they pass, which means either
initializations of those three variables violate the spec, or the spec
contradicts itself.

To remove this contradiction, the spec should explicitly define whether .init
is "default initializer", and explicitly address default initialization of
nested structs (including when they are fields in other aggregates).

There may be a worthwhile language enhancement whereby all nested structs
should have implicitly disabled `this()`. Such behavior is already partially
enforced by the compiler, when nested struct appears as a field of a struct
defined outside of nested struct's parent:

struct Container(T)
{
    T value;

    // following line yields Error: field `value` must be initialized in
constructor, because it is nested struct
    this(Args)(auto ref Args)
    {
        /* ... */
    }
}

void main()
{
    int a;
    struct Nested { void foo() { ++a; } }
    Container!Nested cont = 3;
}

--
Dec 03 2021