ISO/ IEC JTC1/SC22/WG14 N782

SC22/WG14 N782


Cleanup of aggregate initialization
Clive D.W. Feather
clive@demon.net
1997-10-20


Abstract
========

The compound literals proposal - N716 - innocently introduces an
inconsistency in aggregate initialization. No-one has given a rationale
for it, and this paper proposes that it be removed.

Following discussion at Menlo Park, other areas needing change to bring
them into line have been noted. These are the compound literals section
itself and the IEC 559 floating-point arithmetic annex.


Discussion
==========

Consider the following code:

    struct s { int a; int b; };
    int x = 1, y = 2;
    struct s sx;

    int main (void)
    {
        sx.a = 3; sx.b = 4;
        {
            int z = x;                          // Valid
            struct s s1 = sx;                   // Valid
            struct s s2 = (struct s) { x, y };  // Valid
            struct s s3 = { x, y };             // Forbidden
            /* ... */
        }
    }

There is no semantic difference between the declarations of s2 and s3,
yet the latter is forbidden by 6.5.7 paragraph 4:

    All the expressions in an initializer for an object that has
    static storage duration or in an initializer list for an object
    that has aggregate or union type shall be constant expressions.

It has been suggested that the original constraint was to allow for
implementations where the dynamic initializer would have been a burden.
Since such implementations now have to cope with compound literals and
the declaration of s2, the restriction has outlived its usefulness.


Proposal
========
[References are to draft 11 pre 3.]

Change 6.3.2.6 paragraph 7 from:

    Except that the initializers need not be constant expressions (when
    the unnamed object has automatic storage duration), all the semantic
    rules and constraints for initializer lists in 6.5.7 are applicable
    to compound literals.[71] The order in which any side effects occur
    among the initialization list expressions is unspecified.[72]

to:

 |  All the semantic rules and constraints for initializer lists in 6.5.8
 |  are applicable to compound literals.[71]

Delete footnote 72.

Change 6.5.8 paragraph 4 from:

    All the expressions in an initializer for an object that has
    static storage duration or in an initializer list for an object
    that has aggregate or union type shall be constant expressions.

to:

    All the expressions in an initializer for an object that has
 |  static storage duration shall be constant expressions.

Add a new paragraph to the end of subclause 6.5.8:

 |  The order in which any side effects occur among the initialization
 |  list expressions is unspecified.[*]

 |  [*] In particular, the evaluation order need not be the same as the
 |  order of subobject initialization.

Change F.7.4 paragraph 1 from:

    An arithmetic constant expression of floating type, other than one in
    an initializer for an object that has static storage duration or in an
    initializer list for an object that has aggregate or union type, is
    evaluated (as if) during execution. As execution-time evaluation, it
    is affected by any operative modes and raises exceptions as required
    by IEC 559 (provided the state for the FENV_ACCESS pragma is on).[283]

to:

    An arithmetic constant expression of floating type, other than one in
 |  an initializer for an object that has static storage duration, is
    evaluated (as if) during execution. As execution-time evaluation, it
    is affected by any operative modes and raises exceptions as required
    by IEC 559 (provided the state for the FENV_ACCESS pragma is on).[283]

In the example, change:

        float w[] = { 0.0/0.0 }; /* does not raise an exception */

to:

        float w[] = { 0.0/0.0 }; /* raises an exception */

and change the text paragraph from:

    For the aggregate and static initializations, the division is done at
    translation time, raising no (execution-time) exceptions. On the
    other hand, for the two automatic scalar initializations the invalid
    division occurs at execution time.

to:

 |  For the static initialization, the division is done at
    translation time, raising no (execution-time) exceptions. On the
 |  other hand, for the three automatic initializations the invalid
    division occurs at execution time.

Change F.7.5 paragraph 1 from:

    All computation for automatic scalar initialization is done (as if)
    at execution time. As execution-time evaluation, it is affected by
    any operative modes and raises exceptions as required by IEC 559
    (provided the state for the FENV_ACCESS pragma is on). All
    computation for initialization of objects that have static storage
    duration or that have aggregate or union type is done (as if) at
    translation time.

to:

 |  All computation for automatic initialization is done (as if)
    at execution time. As execution-time evaluation, it is affected by
    any operative modes and raises exceptions as required by IEC 559
    (provided the state for the FENV_ACCESS pragma is on). All
    computation for initialization of objects that have static storage
 |  duration is done (as if) at translation time.

In the example, change:

        float u[] = { 1.1e75 }; /* does not raise exceptions */

to:

        float u[] = { 1.1e75 }; /* raises exceptions */

and change the text paragraph from:

    The aggregate and static initializations of u and v raise no
    (execution-time) exceptions because their computation is done at
    translation time. The automatic initialization of w requires an
    execution-time conversion to float of the wider value 1.1e75, which
    raises exceptions. The automatic initializations of x and y entail
    execution-time conversion; however, in some expression evaluation
    methods, the conversions is not to a narrower format, in which case
    no exception is raised.[284] The automatic initialization of z entails
    execution-time conversion, but not to a narrower format, so no
    exception is raised. Note that the conversions of the floating
    constants 1.1e75 and 1.1e75f to their internal representations occur
    at translation time in all cases.

to:

 |  The static initialization of v raises no
 |  (execution-time) exception because its computation is done at
 |  translation time. The automatic initializations of u and w require an
    execution-time conversion to float of the wider value 1.1e75, which
    raises exceptions. The automatic initializations of x and y entail
    execution-time conversion; however, in some expression evaluation
    methods, the conversions is not to a narrower format, in which case
    no exception is raised.[284] The automatic initialization of z entails
    execution-time conversion, but not to a narrower format, so no
    exception is raised. Note that the conversions of the floating
    constants 1.1e75 and 1.1e75f to their internal representations occur
    at translation time in all cases.