• Initialization of anonymous structures and unions

    From Stephen Heumann@21:1/5 to All on Mon Oct 18 20:43:58 2021
    I'm trying to figure out how initialization of anonymous structures and
    unions is supposed to work, particularly when using brace-enclosed
    initializer lists without designators. Quotes below are from N2596
    (the latest available C23 draft), but except as noted the wording is
    the same back to C11.

    6.7.2.1 p15 defines anonymous structures and unions as follows:
    "An unnamed member whose type specifier is a structure specifier with
    no tag is called an anonymous structure; an unnamed member whose type
    specifier is a union specifier with no tag is called an anonymous
    union. The members of an anonymous structure or union are considered to
    be members of the containing structure or union, keeping their
    structure or union layout. This applies recursively if the containing
    structure or union is also anonymous."

    The phrase "keeping their structure or union layout" was added in the
    C23 drafts based on DR 499, but I think it reflects what the intent
    always was.

    6.7.9 (Initialization) p9 says:
    "Except where explicitly stated otherwise, for the purposes of this
    subclause unnamed members of objects of structure and union type do not participate in initialization. Unnamed members of structure objects
    have indeterminate value even after initialization."

    The only other explicit reference to "unnamed members" in 6.7.9 is in
    p13. It relates to initialization of a structure or union using "a
    single expression that has compatible structure or union type" (rather
    than a brace-enclosed initializer list). There is no explicit
    discussion of initializing anonymous structures or unions.

    Based on this, it seems that an anonymous structure or union should not participate as such in the initialization of the containing structure
    or union using a brace-enclosed initializer list. But presumably the individual members of the anonymous structure or union do participate,
    since they "are considered to be members of the containing structure or
    union".

    It seems to follow from this that there may not be a brace-enclosed
    initializer list corresponding to the anonymous structure or union.

    I think it also follows that when using a brace-enclosed initializer
    list with no designations, the members of an anonymous union within a
    structure should follow the rules for structure initialization as given
    in 6.7.9 p17 (all [named] structure members are initialized in
    declaration order). Similarly, the members of an anonymous structure
    within a union would follow the rules for union initialization (only
    the first named member of the union is initialized).

    Thus, the below code would behave as indicated:

    struct S {
    union {
    int a;
    long b;
    };
    int c;
    };

    union U {
    struct {
    int x;
    long y;
    };
    int z;
    };

    struct S s1 = {1,2,3}; // valid, like "= {.a=1, .b=2, .c=3}"
    struct S s2 = {1,2}; // valid, like "= {.a=1, .b=2, .c=0}"
    struct S s3 = {{{1}},2}; // invalid (too many levels of braces)

    union U u1 = {1}; // valid, like "= {.x=1}"
    union U u2 = {1,2}; // invalid (too many initializers for u2)
    union U u3 = {{1,2}}; // invalid (too many initializers for u3.x)

    But at least GCC and Clang behave differently than this (when run with
    -std=c17 -pedantic-errors, which should make them standard-conforming).
    They give an "excess elements in struct initializer" error for the
    declaration of s1 and accept the other declarations (with a "braces
    around scalar initializer" warning for the declaration of s3). They
    also treat the declaration of s2 differently, setting s2.a to 1 and
    s2.c to 2.

    In general, GCC and Clang seem to be acting like an anonymous structure
    or union participates in initialization as a structure or union, even
    though it is unnamed. This would be a reasonable thing to do in the
    abstract, but I don't see how it agrees with the wording in the
    standard. Am I missing something, or do GCC and Clang get this wrong?

    --
    Stephen Heumann

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Stephen Heumann on Tue Oct 19 07:39:24 2021
    Stephen Heumann <stephen.heumann@gmail.com> writes:

    I'm trying to figure out how initialization of anonymous
    structures and unions is supposed to work, particularly when
    using brace-enclosed initializer lists without designators.
    [..elaboration..]

    In general, GCC and Clang seem to be acting like an anonymous
    structure or union participates in initialization as a structure
    or union, even though it is unnamed. This would be a reasonable
    thing to do in the abstract, but I don't see how it agrees with
    the wording in the standard. Am I missing something, or do GCC
    and Clang get this wrong?

    Short answer: I think what clang and gcc are doing is right.

    Slightly longer answer: I can see how the wording used in the C
    standard could be read in the way that clang and gcc behave. I
    agree that other readings are plausible, and that at the very
    least clarification is needed, and probably re-writing. What
    clang and gcc do doesn't surprise me; on the contrary it's what
    I would expect if your comments hadn't drawn my attention to the
    question. If your goal is to understand the rules I think there
    isn't any more to say. If your goal is to understand how the
    rules follow from the wording, I think the question needs to be
    put to the WG14 committee, perhaps in the form of a DR or change
    request or whatever it is those are called these days.

    Hope this helps, and good luck.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)