• int n = {{0}};

    From Keith Thompson@21:1/5 to All on Thu Oct 26 19:57:19 2023
    Citations are from N1570 (C11 draft). I don't think there are any
    relevant differences in other editions or drafts of the standard.

    Quick summary: Why is
    int n = {{0}};
    undefined behavior rather than a constraint violation?

    6.7.9 (Initialization) includes a list of constraints, starting with "No initializer shall attempt to provide a value for an object not contained
    within the entity being initialized."

    6.7.9p11 says:

    The initializer for a scalar shall be a single expression,
    optionally enclosed in braces. The initial value of the object is
    that of the expression (after conversion); the same type constraints
    and conversions as for simple assignment apply, taking the type of
    the scalar to be the unqualified version of its declared type.

    (I presume that "optionally enclosed in braces" is not intended to permit
    more than one level of braces.)

    Both
    int n = 0;
    and
    int n = {0};
    are clearly valid, with identical semantics. But this:
    int n = {{0}};
    violates that requirement. Since the requirement is under Semantics,
    not Constraints, code that violates it has undefined behavior. Starting
    with C99, this is mentioned in Annex J.

    My question: Why is this undefined behavior rather than a constraint
    violation?

    I suggest that moving that paragraph from Semantics to Constraints would
    be an improvement. Extra braces on a scalar initializer are easy to
    detect, and requiring a diagnostic should not be a significant burden,
    and would promote consistency. (Alternatively, the standard could have explicitly allowed arbitrarily nested braces.)

    With the current requirements, a compiler could quietly generate code to
    set n to 42, though I doubt that anyone would do that.

    (I note that gcc issues a non-fatal warning with "-std=c17
    -pedantic-errors", while clang treats it as a fatal error.)

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Will write code for food.
    void Void(void) { Void(); } /* The recursive call of the void */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to Keith Thompson on Sat Oct 28 09:41:51 2023
    On 10/26/23 7:57 PM, Keith Thompson wrote:
    Citations are from N1570 (C11 draft). I don't think there are any
    relevant differences in other editions or drafts of the standard.

    Quick summary: Why is
    int n = {{0}};
    undefined behavior rather than a constraint violation?

    6.7.9 (Initialization) includes a list of constraints, starting with "No initializer shall attempt to provide a value for an object not contained within the entity being initialized."

    6.7.9p11 says:

    The initializer for a scalar shall be a single expression,
    optionally enclosed in braces. The initial value of the object is
    that of the expression (after conversion); the same type constraints
    and conversions as for simple assignment apply, taking the type of
    the scalar to be the unqualified version of its declared type.

    (I presume that "optionally enclosed in braces" is not intended to permit more than one level of braces.)

    Both
    int n = 0;
    and
    int n = {0};
    are clearly valid, with identical semantics. But this:
    int n = {{0}};
    violates that requirement. Since the requirement is under Semantics,
    not Constraints, code that violates it has undefined behavior. Starting
    with C99, this is mentioned in Annex J.

    My question: Why is this undefined behavior rather than a constraint violation?

    I suggest that moving that paragraph from Semantics to Constraints would
    be an improvement. Extra braces on a scalar initializer are easy to
    detect, and requiring a diagnostic should not be a significant burden,
    and would promote consistency. (Alternatively, the standard could have explicitly allowed arbitrarily nested braces.)

    With the current requirements, a compiler could quietly generate code to
    set n to 42, though I doubt that anyone would do that.

    (I note that gcc issues a non-fatal warning with "-std=c17
    -pedantic-errors", while clang treats it as a fatal error.)


    I wonder a bit if this is bowing to conflicting existing
    implementations. So were giving a fatal error for the extra braces,
    while some specifically permitted them, and have a measurable code base
    that uses that permision.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun Dec 3 11:23:43 2023
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Citations are from N1570 (C11 draft). I don't think there are any
    relevant differences in other editions or drafts of the standard.

    Quick summary: Why is
    int n = {{0}};
    undefined behavior rather than a constraint violation?

    Because there is no practical benefit to changing what the
    standard says now.

    6.7.9 (Initialization) includes a list of constraints, starting
    with "No initializer shall attempt to provide a value for an
    object not contained within the entity being initialized."

    6.7.9p11 says:

    The initializer for a scalar shall be a single expression,
    optionally enclosed in braces. The initial value of the
    object is that of the expression (after conversion); the same
    type constraints and conversions as for simple assignment
    apply, taking the type of the scalar to be the unqualified
    version of its declared type.

    (I presume that "optionally enclosed in braces" is not intended to
    permit more than one level of braces.)

    Both
    int n = 0;
    and
    int n = {0};
    are clearly valid, with identical semantics. But this:
    int n = {{0}};
    violates that requirement. Since the requirement is under
    Semantics, not Constraints, code that violates it has undefined
    behavior. Starting with C99, this is mentioned in Annex J.

    My question: Why is this undefined behavior rather than a
    constraint violation?

    There's no practical benefit to changing it. The current rule
    has been in place since the original ANSI C standard, more than
    30 years ago. Whatever the motivation may have been at that
    time, there is no incentive to change the rules now; compilers
    already give diagnostics in such cases, even though they don't
    have to. Changing the rules would mean throwing away a degree
    of freedom with no significant benefit.

    I suggest that moving that paragraph from Semantics to Constraints
    would be an improvement. Extra braces on a scalar initializer are
    easy to detect,

    All work is easy if it's someone else who is doing it. Certainly
    it would be easier just to leave things as is.

    and requiring a diagnostic should not be a significant burden,

    This statement assumes other people have the same reaction that
    you do. Not everyone does.

    and would promote consistency. [...]

    There is already consistency. There is no evidence to suggest
    that changing the standard would make a difference at all to what
    code people write. Most people are driven by what warnings or
    errors they get, and compilers already give these messages.

    With the current requirements, a compiler could quietly generate
    code to set n to 42, though I doubt that anyone would do that.

    If such forms were constraint violations, compilers could give a
    warning (which they already do), and then generate code to set n
    to 42. In practical terms there is no difference from the
    current situation.

    (I note that gcc issues a non-fatal warning with "-std=c17
    -pedantic-errors", while clang treats it as a fatal error.)

    If anyone absolutely wants a diagnostic, they can simply compile
    with gcc or clang.

    Note that clang has a compiler option to turn off the diagnostic
    for such cases. Presumably that option is there because some
    people want to use it. Making double braces a constraint violation
    would interfere with what they want to do. What is suggested as an
    improvement looks like all downside and no upside.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Thu Jan 18 17:52:27 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Citations are from N1570 (C11 draft). I don't think there are any
    relevant differences in other editions or drafts of the standard.

    Quick summary: Why is
    int n = {{0}};
    undefined behavior rather than a constraint violation?

    Because there is no practical benefit to changing what the
    standard says now.

    Do you have an opinion about whether it should have been specified as a constraint violation rather than undefined behavior in the first place?

    I don't have any pertinent information on which to base such an
    opinion. It's possible there was existing code that prompted
    what decision was made at the time. It's also possible that what
    was done is just the result of an oversight. I'm inclined to think
    that there was some reasoning that led to what was actually done
    (ie, that there is undefined behavior but no constraint violation),
    but that's just a general impression, not based on any specific
    facts. If there was such reasoning, then without more information
    I would tend to favor supporting it.

    I guess I could add that there are other constructions in C that
    are undefined behavior, and are identifiable at compile time, but
    which are not constraint violations, and I think that's the right
    decision for at least some of them.

    There's no practical benefit to changing it. The current rule
    has been in place since the original ANSI C standard, more than
    30 years ago. Whatever the motivation may have been at that
    time, there is no incentive to change the rules now; compilers
    already give diagnostics in such cases, even though they don't
    have to. Changing the rules would mean throwing away a degree
    of freedom with no significant benefit.

    It would remove a degree of freedom for implementers, one that
    few if any currently take advantage of.

    As I mentioned in my earlier post, the clang compiler does take
    advantage of this particular degree of freedom, with an option to
    turn off diagnostics corresponding to such cases.

    Meaningless degrees of freedom for implementers do not help
    users.

    I infer from the existence of the clang option that some people
    think the degree of freedom here is both meaningful and useful.

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