• Re: 80386 C compiler

    From Kaz Kylheku@21:1/5 to James Kuyper on Sat Nov 30 01:30:15 2024
    On 2024-11-27, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 11/27/24 16:52, Kaz Kylheku wrote:
    On 2024-11-27, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    On 11/27/24 14:42, Kaz Kylheku wrote:
    ...
    The specification has an inconsistency, because it gives the order
    in which initializations occur, yet not the order of evaluation of
    the expressions that produce their values.

    That's not an inconsistency, it's a deliberate choice to give
    implementations freedom to use whichever order is most convenient.

    Implementations are not given freedom about initialization order;
    in { A, B } the initialization implied by A happens before B.

    Granting a freedom here while taking it away there is inconsistent.

    Expression B may rely on the initialization A having completed, but
    not on the effects of A having been settled.

    I'm sorry - I thought you meant that they were logically inconsistent.
    What you're actually saying is more like stylistically inconsistent.

    In C90, the order in which the initializers were evaluated didn't
    matter, because they were required to be static initializers. It was
    only in C99 that they were allowed to be arbitrary expressions.

    However, in the same version of the standard, designated initializers
    were added. Designated initializers are allowed to update elements in a different order from their order in memory, and to initialize the same element multiple times, with only the final initialization actually occurring. This can be convenient for setting up a rule and then adding exceptions to that rule.

    But it simply ends up being left to right.

    Given { A, B, C }, the members are initialized in order of increasing
    offset address, corresponding to left-to-right order in the syntax.

    Given { [2] = A, [1] = B, [0] = C }, they are initialized in the
    left-to-right order in the syntax: [2] first, then [1] then [0].

    So we have order. And yet we don't have order; the expressions are not
    actually sequenced.

    If there weren't a rule mandating the order in
    which initializers were applied, when two or more initializers affect
    the same object, it wouldn't be possible to be certain which one
    overrode the others.

    It would make sense for that simply to be a constraint violation;
    two initializations for the same object are being requested.

    There is no sequencing in the initialization: { i++, i++ } would
    be undefined behavior. Yet, you can request multiple initializations
    of the same subobject and have it safely resolved to the rightmost?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Kaz Kylheku on Fri Nov 29 21:00:20 2024
    Kaz Kylheku <643-408-1753@kylheku.com> writes:

    On 2024-11-27, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    If there weren't a rule mandating the order in which initializers
    were applied, when two or more initializers affect the same
    object, it wouldn't be possible to be certain which one overrode
    the others.

    That's wrong. The priority rule for initializing the same subobject
    depends not on order of evaluation but on syntactic order. There
    doesn't have to be a rule for evaluation order to make the order
    of subobject overriding be well defined.

    It would make sense for that simply to be a constraint violation;
    two initializations for the same object are being requested.

    It isn't that simple. There are situations where overriding the
    initialization of a particular subobject makes sense, and is
    useful. Example:

    typedef struct { int x, y; } Bas;
    typedef struct { Bas b[2]; } Foo;

    Foo
    sample_foo( Bas b ){
    Foo foo = { b, b, .b[1].y = -1 };
    return foo;
    }

    The subobject .b[1].y is overridden, but we can't take the previous initialization of .b[1] without changing the semantics.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Kaz Kylheku on Sat Nov 30 09:00:32 2024
    On 11/29/24 20:30, Kaz Kylheku wrote:
    On 2024-11-27, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    ...
    In C90, the order in which the initializers were evaluated didn't
    matter, because they were required to be static initializers. It was
    only in C99 that they were allowed to be arbitrary expressions.

    However, in the same version of the standard, designated initializers
    were added. Designated initializers are allowed to update elements in a
    different order from their order in memory, and to initialize the same
    element multiple times, with only the final initialization actually
    occurring. This can be convenient for setting up a rule and then adding
    exceptions to that rule.

    But it simply ends up being left to right.

    Why is that it a "but"? If you want to give users control over the order
    of initialization, what is simpler or more natural that using the
    textual order.

    Given { A, B, C }, the members are initialized in order of increasing
    offset address, corresponding to left-to-right order in the syntax.

    Given { [2] = A, [1] = B, [0] = C }, they are initialized in the left-to-right order in the syntax: [2] first, then [1] then [0].

    So we have order. And yet we don't have order; the expressions are not actually sequenced.

    You can always make something sound contradictory or confusing by
    leaving out the details that resolve the contradiction or remove the
    confusion.
    Yes, the initializations of the members of an aggregate object are
    ordered. And also yes, the evaluations of the initializer expressions
    for those objects are unordered, the same as is generally the case -
    there's only a few features of C that impose order on expressions - the semicolon at the ends of declarations or statements are the most common.
    As a general way, whenever you need to order the evaluation of
    expressions that would otherwise be unordered, the way to do so is
    simply put them in a different declarations or statements, which often
    requires creating temperaries to hold the results of intermediate
    evaluations.

    If there weren't a rule mandating the order in
    which initializers were applied, when two or more initializers affect
    the same object, it wouldn't be possible to be certain which one
    overrode the others.

    It would make sense for that simply to be a constraint violation;
    two initializations for the same object are being requested.

    The committee felt otherwise. The standard quite explicitly says: "...
    each initializer provided for a particular subobject overriding any
    previously listed initializer for the same subobject ..." (6.7.10p20). I
    agree - I can see obscure situations where that rule makes the feature
    more convenient. The standard also provides a relevant example where the intended behavior depends upon this feature:

    "EXAMPLE 13 Space can be "allocated" from both ends of an array by using
    a single designator:
    int a[MAX] = {
    1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0
    };

    In the above, if MAX is greater than ten, there will be some zero-valued elements in the middle; if it is less than ten, some of the values
    provided by the first five initializers will be overridden by the
    second five."

    If you wanted the behavior to depend upon the value of MAX in precisely
    the fashion provided by this feature, and this feature were not
    available, the code would have to be a lot more complicated.

    There is no sequencing in the initialization: { i++, i++ } would
    be undefined behavior. Yet, you can request multiple initializations
    of the same subobject and have it safely resolved to the rightmost?

    Correct. If you need initializer expressions to be ordered, you'll have
    to put them in different statements or declarations.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rosario19@21:1/5 to Kaz Kylheku on Sat Nov 30 16:41:03 2024
    On Tue, 26 Nov 2024 17:59:08 -0000 (UTC), Kaz Kylheku wrote:

    On 2024-11-25, Rosario19 <Ros@invalid.invalid> wrote:
    On Mon, 25 Nov 2024 18:23:58 -0000 (UTC), Kaz Kylheku wrote:

    void fn(int a)
    {
    int x[3] = { foo(), bar(), a }; /* not in C90 */

    is in the above foo() called before bar()?

    No, you cannot rely on that. Maybe it's fixed in a more recent standard,
    but C99 (which I happen to have open in a PDF reader tab) stated that
    "The order in which any side effects occur among the initialization list >expressions is unspecified.". This implies that there is no sequence
    point between any two initializing expressions, which means we don't
    know whose expression's function call takes place first.

    In any case, a C90 compiler with the above support as an extension to
    C90 can specify rigid sequencing behavior.

    void fn(int a)
    {
    int x[3];
    x[0]=foo(); x[1]=bar(); x[2]=a;

    this would be ok with every C compiler

    One problem is, if you're doing this because your compiler is C90, you
    also have to do something about all declarations which follow the int
    x[3], since they cannot occur after a statement. You can add another
    level of block nesting for them, or whatever.

    int fn(int a)
    { int x[3];
    int b=9;
    x[0]=foo(); x[1]=bar(); x[2]=a;
    return x[0]==0||a==b;
    }

    i don't see onother level of block nesting

    Initialization is preferable to leaving an object uninitialized and >assigning. There is a scope where the name is visible, but the object
    is not initialized, inviting code to be inserted there which tries
    to use it.

    If I needed foo to be called before bar, I would still rather do
    the following than assignment:

    int f = foo();
    int b = bar();
    int x[3] = { f, b, a };

    ok

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