www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - We need to define the semantics of block initialization of arrays

reply "Don" <turnyourkidsintocash nospam.com> writes:
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]
}
---
Jun 03 2013
next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Monday, 3 June 2013 at 09:06:25 UTC, Don wrote:
 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.
+1 I see no point in just initialising the first member. If you want that, just default init then set the first member.
Jun 03 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/3/13, Don <turnyourkidsintocash nospam.com> wrote:
 A lot of code relies on this behaviour, but the spec doesn't
 mention it!!!
I didn't know about it until Walter mentioned the syntax to me. I've found it quite useful since then. E.g.: char[100] buffer = 0; Without this buffer is normally initialized with 0xFF, and this could break C functions when you pass a pointer to such an array.
 Personally I'd like to just use block-init everywhere.
Me too. You get my vote.
Jun 03 2013
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2013-06-03, 11:06, Don wrote:

 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.
Votes++; -- Simen
Jun 03 2013
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 06/03/2013 11:06 AM, Don wrote:
 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.
Agreed, kill first-element init.
Jun 03 2013
parent "David Nadlinger" <code klickverbot.at> writes:
On Monday, 3 June 2013 at 19:41:35 UTC, Timon Gehr wrote:
 On 06/03/2013 11:06 AM, Don wrote:
 Personally I'd like to just use block-init everywhere. […]
Agreed, kill first-element init.
Kill it with a vengeance! Honestly, I can't see how that could have ever been intended as a feature, and while fixing an issue in LDC a while back, I already removed that one instance with a first-element struct initializer from the test suite: https://github.com/ldc-developers/dmd-testsuite/blob/ldc/runnable/test42.d#L3226 David
Jun 03 2013
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, June 03, 2013 11:06:22 Don wrote:
 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.
I honestly didn't know about either, but first element init seems very wrong to me, whereas block init makes sense. - Jonathan M Davis
Jun 03 2013
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 3 June 2013 at 09:06:25 UTC, Don wrote:
 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.
KILL IT WITH FIRE !
Jun 03 2013
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 03 Jun 2013 05:06:22 -0400, Don <turnyourkidsintocash nospam.com>  
wrote:

 DMD has always accepted this initializer syntax [with unexplained black  
 magic behavior that is inconsistent from one usage to the next]
BURRRRRN IT!!!! Seriously, I had no idea this was going on. It would surprise the hell out of me if I had discovered it! -Steve
Jun 03 2013
prev sibling next sibling parent reply Kenji Hara <k.hara.pg gmail.com> writes:
2013/6/3 Don <turnyourkidsintocash nospam.com>

 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.
First-element-init is definitely a bug. I can argue that nobody wants the strange behavior. 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
Currently block-initialization for multi-dimensional static array is just only allowed for variable declaration in statement scope. I'm planning to fix bug 3849 and 7019, but changing this behavior might affect them. As my hope, I'd like to keep this as-is so I've not finished thinking about it well. S s = { 8 }; // OK, struct static initializer. first-element-init

This is definitely a bug. Instead, block-init should occur.


    S r = S( 8 ); // OK, struct literal, block-init.
    T t;          // Default initialized, block-init
OK.
    assert( s.x[2] == 8); // Fails; it was first-element-initialized
Also, definitely a bug.
    assert( r.x[2] == 8); // Passes; all elements are 8. Block-init.
    assert( t.x[2] == 8); // Passes; all elements are 8. Block-init.
OK.
    U u = { 9 };  // Does not compile
    // Error: cannot implicitly convert expression (9) of type int to
 int[3LU][3LU]
For reasons I've already mentioned in `int [3][4] w = 7;`, I'd like to keep this current behavior.
 }
 ---
Kenji Hara
Jun 03 2013
parent reply "Don" <turnyourkidsintocash nospam.com> writes:
On Tuesday, 4 June 2013 at 02:33:54 UTC, Kenji Hara wrote:
 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.
First-element-init is definitely a bug. I can argue that nobody wants the strange behavior.
Good, it seems that everyone agrees. It's therefore a bug in todt.c, StructInitializer::todt()
 void main()
 {
    int [3][4] w = 7;
    assert( w[2][2] == 7); // Passes, it was block-initialized
Currently block-initialization for multi-dimensional static array is just only allowed for variable declaration in statement scope. I'm planning to fix bug 3849 and 7019, but changing this behavior might affect them. As my hope, I'd like to keep this as-is so I've not finished thinking about it well.
Yeah, there's difficulties with things like: int [3][4] = [7, 7, 7]; which could be a block initialization -- is this allowed or not? Though I think we have already dealt with such issues for block assignment.
    S s =  { 8 }; // OK, struct static initializer. 
 first-element-init

 This is definitely a bug. Instead, block-init should occur.
Good.
    S r = S( 8 ); // OK, struct literal, block-init.
    T t;          // Default initialized, block-init
OK.
    assert( s.x[2] == 8); // Fails; it was 
 first-element-initialized
Also, definitely a bug.
    assert( r.x[2] == 8); // Passes; all elements are 8. 
 Block-init.
    assert( t.x[2] == 8); // Passes; all elements are 8. 
 Block-init.
OK.
    U u = { 9 };  // Does not compile
    // Error: cannot implicitly convert expression (9) of type 
 int to
 int[3LU][3LU]
For reasons I've already mentioned in `int [3][4] w = 7;`, I'd like to keep this current behavior.
There is still one problem, bug 10198. This currently compiles, and does something stupid: --- struct U { int [3][3] y; } U u = U(4); --- What do you think should happen here?
Jun 04 2013
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Don:

 Yeah, there's difficulties with things like:

 int [3][4] = [7, 7, 7];

 which could be a block initialization -- is this allowed or not?
I hope it keeps being disallowed, to avoid programmer's mistakes. (Maybe this is acceptable: int [3][4] = [7, 7, 7][]; ) Bye, bearophile
Jun 04 2013
prev sibling parent Kenji Hara <k.hara.pg gmail.com> writes:
2013/6/4 Don <turnyourkidsintocash nospam.com>

 There is still one problem, bug 10198. This currently compiles, and does
 something stupid:
 ---

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

 U u = U(4);
 ---
 What do you think should happen here?
Oh! I did not know it is currently accepted. I think accepting multi-dimensional block initializing on StructLiteralExp arguments is very bug-prone behavior. Different from variable declaration in statement scope, there is no target type we can look for. So inferring the cost of static array construction is difficult. struct U { int[3][3][3][3] w; } U u = U(1); // looks trivial, but actually costly operation. int[3][3][3][3] w = 1; // initializing cost is very obvious At most it would be better that it is restricted up to one-dimensional block initializing, same as StructInitializer. Kenji Hara
Jun 04 2013
prev sibling next sibling parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Monday, 3 June 2013 at 09:06:25 UTC, Don wrote:
 DMD has always accepted this initializer syntax for static 
 arrays:

 float [50] x = 1.0;
I would expect block initialization and have never considered first element initialization.
Jun 03 2013
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Sometimes I'd like to write code like this:


struct Foo {
     int x;
}
void main() {
     Foo[100] foos;
     foos[].x = 10;
}


Bye,
bearophile
Jun 04 2013