We need to define the semantics of block initialization of arrays

Don turnyourkidsintocash at nospam.com
Mon Jun 3 02:06:22 PDT 2013


DMD has always accepted this initializer syntax for static arrays:

float [50] x = 1.0;

If this declaration happens inside a function, or in global 
scope, the compiler sets all members of x to 1.0.  That is, it's 
the same as:

float [50] x = void;
x[] = 1.0;

In my DMD pull requests, I've called this 'block initialization', 
since there was no standard name for it.

A lot of code relies on this behaviour, but the spec doesn't 
mention it!!!

The problem is not simply that this is unspecified. A long time 
ago, if this same declaration was a member of a struct 
declaration, the behaviour was completely different. It used to 
set x[0] to 1.0, and leave the others at float.init. I'll call 
this "first-element-initialization", and it still applies in many 
cases, for example when you use a struct static initializer. Ie, 
it's the same as:

float [50] x;
x[0] = 1.0;

Note however that this part of the compiler has historically been 
very bug-prone, and the behaviour has changed several times.


I didn't know about first-element-initialization when I 
originally did the CTFE code, so when CTFE is involved, it always 
does block initialization instead.
Internally, the compiler has two functions, defaultInit() and 
defaultInitLiteral(). The first does first-element-init, the 
second does block-init.
There are several other situations which do block initialization 
(not just CTFE). There are a greater number of situations where 
first-init can happen, but the most frequently encountered 
situations use block-init. There are even some foul cases, like 
bug 10198, where due to a bug in CTFE, you currently get a 
bizarre mix of both first-init and block-init!


So, we have a curious mix of the two behaviours. Which way is 
correct?

Personally I'd like to just use block-init everywhere. I 
personally find first-element-init rather unexpected, but maybe 
that's just me. I don't know when it would be useful. But 
regardless, we need to get this sorted out.
It's a blocker for my CTFE work.


Here's an example of some of the oddities:
----
struct S {
    int [3] x;
}

struct T {
     int [3] x = 8;
}

struct U {
    int [3][3] y;
}

void main()
{
    int [3][4] w = 7;
    assert( w[2][2] == 7); // Passes, it was block-initialized

    S s =  { 8 }; // OK, struct static initializer. 
first-element-init
    S r = S( 8 ); // OK, struct literal, block-init.
    T t;          // Default initialized, block-init
    assert( s.x[2] == 8); // Fails; it was 
first-element-initialized
    assert( r.x[2] == 8); // Passes; all elements are 8. 
Block-init.
    assert( t.x[2] == 8); // Passes; all elements are 8. 
Block-init.

    U u = { 9 };  // Does not compile
    // Error: cannot implicitly convert expression (9) of type int 
to int[3LU][3LU]
}
---


More information about the Digitalmars-d mailing list