• Re: switch/extension for see below strongly needed

    From David Brown@3:633/10 to All on Thu May 21 14:55:58 2026
    On 21/05/2026 13:56, Bart wrote:
    On 21/05/2026 07:45, David Brown wrote:
    On 20/05/2026 22:14, Bart wrote:
    On 20/05/2026 18:51, David Brown wrote:
    On 20/05/2026 17:41, Bart wrote:

    First, a niche use is not pointless.

    A niche use that takes advantage of some accidental quirk in a
    language? One that wouldn't exist if the quirk wasn't there; that
    sort of niche use?

    Over 50 years of use, every misfeature of C has been exploited by /
    somebody/. One reason why the language couldn't properly evolve.


    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    (I did do a survey of my codebases to find examples of label names
    shadowing local identifiers, but found no instances. I didn't post the results (afaicr) since the code sample was too small, but it would be a massive job to do a bigger test.)

    So in conclusion, we have not seen a case where people have had the same identifier as a label and a variable, typedef or function name. But we
    can't rule out the possibility. It is certainly feasible that it
    happens sometimes by accident or coincidence.

    (The separation of the struct/union/enum tag name space from variable namespace /is/ a feature that people use - it is not uncommon to see
    "struct thing thing;" in code, whether or not either of us thinks it is
    clear coding. And separate name spaces for the members of each struct
    or union is trivially useful.)

    We have no idea of a user benefit from /not/ having separate name spaces
    here. (gcc's extension to let you take the value of a label with the && operator is orthogonal to the separation of the name spaces.)

    As far as we can see, it does not make a difference to users one way or
    the other. But I can easily see how it might be convenient for
    implementers.

    So are you justified in claiming it is pointless? No, you are not -
    because we haven't ruled out any possibility of user benefits, there is
    a definite implementer benefit, and having the opposite choice would be
    less likely to be of any benefit to anyone. But I think we can
    reasonably say it is a very minor matter that makes little practical difference to anyone.


    ÿ I don't - I merely can't rule out niche cases.ÿ I could imagine some
    situations - perhaps from machine or human translation from other
    languages, or old implementations with very short identifier lengths.

    Yeah, a C implementation that only has 'a' to 'z' variables, but also
    has a separate set of 'a' to 'z' labels!

    You seem obsessed with calling every aspect of C a "misfeature".

    Yes, it is astounding how much there is. Some can be excused due to its
    age, but also lots could have been fixed long ago.

    Some of it is an actual nuisance, but quite a bit is also aesthetically displeasing.

    You didn't like my example of being able to write '(F)(x)' (or 'int
    (a);') but not 'goto (L)', but the anomaly is there.


    Your example was pointless. The fact that function names in C act as
    pointers and can be used in expressions, and that expressions evaluating
    to function pointers can be used to call functions, does not mean people typically go around writing "(printf)("hello");". Disallowing
    parentheses around the function name, on the other hand, would require additional rules in the grammar of C - so it would not make sense to
    disallow it. Similarly, the extra parentheses in "int (a);" are allowed because parentheses can be useful in complex declarations (perhaps
    mixing pointers, arrays, and function types) - disallowing them in some circumstances would complicate the grammar of the language.

    But labels are inherently simpler - you can only "goto" directly to a
    defined label. Parentheses could be of no benefit, so /allowing/ them
    would complicate the grammar.

    It is not an anomaly when very different things have different rules.

    There is also not being able to write 'L:}'; this one bites.

    I don't see why that "bites", unless it is some weird smiley. I would
    expect most people to have a the close brace on a separate line from the label, but it is legitimate for people to want to jump to the end of a
    block.



    But I can have a good idea of the implications both by there being a
    separate namespace, or not. You snipped my example where it caused a
    limitation in the syntax.


    Yes, it was irrelevant.

    And it's irrelevant because? It was a minor consequence due to
    restrictions on where labels can appear, in turn due to not sharing the
    same namespace as ordinary identifiers.


    It was irrelevant because it is not relevant to standard C, but mostly
    because it is not relevant to the dead horse we've been flogging. gcc
    has a syntax to take the address of a label - it uses "&&label". The
    choice of syntax is not in any way related to the name spaces. Or do
    you think that labels should automatically be treated as constants of
    type "void *" ? Getting the "value" of a label is a highly unusual
    situation - but it's useful in things like byte-code interpreters. It /should/ involve a special syntax that is clearly recognisable.

    Irrelevant because it's part of a language extension? Those tend to
    become standard.

    No, most language extensions do not become standard. This one has been
    in gcc for maybe three decades or more, and is not part of the standard.



    You can have an opinion from ignorance, as many people have told you.
    You can't expect that opinion to be respected.

    That's just rubbish, and an attempt to stifle criticism.

    No, it is perfectly true.


    Somebody asks why do I have to do X and not Y? Y makes more sense, it is easier etc.

    That's okay as far as it goes, but is missing context. Y makes more
    sense /to you/. Y makes more sense /for your specific needs/. Y makes
    more sense /in your personal language/. Your opinions and preferences
    are not fact, and your likes and dislikes do not generalise to all
    programmers and all languages.


    The response here will be because the Standard says so. End of discussion.


    In issues of facts about the C language, yes. You are free to like or
    dislike something the standard says, but not to pretend it doesn't say
    what it does.

    Somebody was able to voice that opinion using their own experience and common sense. It may be valid; maybe other languages do do Y instead of X.

    However all anyone here wants to suggest that that person is ignorant because they haven't read the standard. Well, they might read the
    standard then find that Y is still better!


    As many people have told you countless times, pretty much everybody has dislikes about some aspects of C. We all have things that we think
    would have been better if they had been different. That does not in any
    way change what C /is/, or how the language is defined.

    As has been said, "There are two kinds of languages - the ones people
    complain about, and the ones nobody uses".


    You don't 'own' C. There is no copyright on it. Anyone can use it as
    casually or as intensely as they like. Anyone can choose to create as
    professional or as casual a version as they like. Anyone choose to
    pontificate on things they like or dislike.

    The C23 draft open on my desktop at the moment says "? ISO 2024 - All
    rights reserved" on every page.ÿ It /is/ copyrighted, and it is a
    defined and standardised language.

    The standards document itself might be copyrighted, not the language.


    The point of a language standard, and a language that has a standard, is
    that the standard defines the language. No one is likely to sue you for copyright infringement if you use the term "C" to refer to a different language, but it's not helpful to any discussion.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Thu May 21 14:56:23 2026
    On 2026-05-21 13:46, Kenny McCormack wrote:
    In article <10uloem$e6bl$1@kst.eternal-september.org>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    [...]
    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a
    handful of people on the planet do.

    Please take this to comp.lang.misc. There's already some discussion
    of Algol 68 there.

    The Lord and Master has spoken. All must obey.

    Yes, even JP.

    Curious; why "even"?

    I'm (in that respect) not different to others who post "dirty",
    non-OT-free topics.

    Here it started from "C" and someone generalized some statement,
    asking for non-C examples.

    I think it's fair to rebut wrong claims. It's also fair to point
    to any digression that got too far. So I'm open to suggestions
    concerning termination/relocation of pure OT sub-threads. Some
    folks are more picky than others, though. And the judgement and
    final decision has anyway to be done by the individual poster(s).

    Personally I skip (don't even read) followups if a topic is too
    far off, or if everything had already been said, or if there's
    some pathological poster who's meaningless answer (or squirming,
    or red herrings, or rhetorical moves) can anyway be anticipated.

    Cheers! :-)

    Janis


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Thu May 21 15:08:57 2026
    On 2026-05-21 13:56, Bart wrote:
    [...]

    Some of it is an actual nuisance, but quite a bit is also aesthetically displeasing.

    (I'm not interested in what you find a nuisance, pleasing, aesthetic;
    you have a very specific and "sharply limited" and strong opinion it
    seems, so no good base for discussions which ask for a somewhat more
    open mind.)

    [...]
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    int main (void)
    {
    {
    int a = 42;
    goto end;
    return a;
    end:
    }
    return 0;
    }

    (although I wouldn't have been astonished if there'd be a requirement
    to allow labels at statements only, thus at least requiring something
    like an empty statement as in

    ...
    end: ;
    }

    But all fine in my book. (Especially since I'm anyway rarely - actually
    not at all - using 'goto' labels.)

    Janis

    [...]


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Thu May 21 15:16:23 2026
    On 21/05/2026 14:18, Dan Cross wrote:
    In article <10umdsa$hnml$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 20/05/2026 20:17, Dan Cross wrote:
    [snip]
    I particularly agree with the use of a static inline to avoid
    some `goto`s. When the `goto fail` bug was announced, as an
    exercise, I rewrote Apple's code to demonstrate how one might
    eliminate the problematic pattern entirely:


    (The mixture of tabs and spaces in your post resulted in a bit messed up
    indentation in the quotation in my reply. I've tried to fix it up to
    match the original indentation, but be warned it might look different
    when you view it or re-quote it.)

    Apologies for that; I suspect that was my editor trying to be
    "helpful": I used spaces in the example, but usually use tabs;
    I probably should have just stuck with the latter for
    consistency.


    No problem - I just wanted to point it out in case my own posts looked
    wrong.

    <snip for brevity>


    This writeup of the original bug is pretty good: https://dwheeler.com/essays/apple-goto-fail.html


    I believe I read that at the time of the event. It's good to work
    through such critical errors to see how to avoid them in the future.

    He also recommends several of the techniques you do.

    In this particular case, the bug _should_ have been caught.
    Some combination of rigorous testing, static analysis, and
    manual review ought to have prevented it; that the bug made it
    into production software anyway is a software engineering
    failure.

    I think one can level some reasonable criticism at the language,
    however. The `goto error;` idiom is used in C because there are
    few alternatives for cleanup handling on failure. Modulo what
    we discussed before, that code can _often_ be restructured to
    avoid it, but sometimes it can't, and frequently it just isn't.


    I think "isn't" is more common than "can't". But I also don't think the
    "goto error" idiom is necessarily bad in itself - it's just that it is
    often used badly. A typical indication of poor usage is when a function
    is getting very long, and there are multiple "error" labels.

    However, the problem with this code was not the "goto error" idiom, or
    the "goto" itself - the problem was the mismatch between indentation and
    the statement under control of the "if". It would have been equally bad
    if it had been "return" rather than "goto", or if gcc cleanup attributes
    had been used to handle cleanup, or if some kind of "defer" mechanism
    had been used (as supported by some programming languages, and proposed
    for a future C version).

    These various cleanup mechanisms can definitely be better than "goto
    error", but they would not have prevented this error. (A programming
    language that requires braces for statements controlled by "if", on the
    other hand, /would/ have prevented the error.)

    In contrast, newer languages give you more expressive power in
    this regard: Go has the `defer` keyword to register a closure
    that runs when the enclosing function returns:

    ```go
    f, err := os.Open(...)
    if err != nil {
    return err
    }
    defer f.Close()
    ...
    ```

    The list of deferred closures will be run whenever the function
    returns, no matter what path it takes. You can't accidentally
    omit the close.

    Similarly, the RAII idiom prevalent in C++ and Rust uses object
    destructors that are automatically run when something goes out
    of scope to do the cleanup. C++ pairs that with
    exceptions (arguably worse than goto) while Rust represents
    errors with sum types and a little bit of syntactic sugar with
    the `?` operator. In either case, the descriptors run and do
    the cleanup, regardless of whether the return was an error path
    or a success path.

    These are all nice ways of handling cleanup.


    Other languages feature "linear types", instances of which must
    be used exactly once: failure to cleanup on an error path is a
    compile time error. This means that you can't forget to
    deallocate memory, close a file, or unlock a mutex, for example,
    but I don't know that it directly addresses the issue where you
    just skip over the actual thing the program is supposed to do.
    Still, with more expressive type systems, it is likely one can
    much more easily structure the program so that the type of logic
    that lead to this failure is unnecessary.


    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    I did some work, eons ago, in a language called XC that was specifically
    for XMOS microcontrollers. The language and tools had a feature that
    made data races impossible by not allowing competing access to shared variables from different threads. Since threads were part of the
    hardware, and the tools analysed the code flow through threads, this
    could all be enforced at build time - data had to be passed in messages,
    not shared memory. But for some things that involved large buffers,
    that was hopelessly inefficient - and these devices were regularly used
    with USB, audio, and similar things that needed large and predictable
    buffers. So code - even library and example code from the manufacturer
    - was full of inline assembly to work around the "smart-arse" language
    and tools.

    I understand that someone has written a paper proposing adding
    `defer` to C; that would obviate many of these problems.

    - Dan C.


    Yes. Jens Gustedt - one of the few members of the C standards committee
    who is vocal and public about pushing new ideas into C. (Of course not everyone will agree with the ideas he comes with.)

    <https://gustedt.wordpress.com/2026/02/15/defer-available-in-gcc-and-clang/>




    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Thu May 21 14:31:19 2026
    On 21/05/2026 14:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    [...]

    Some of it is an actual nuisance, but quite a bit is also
    aesthetically displeasing.

    (I'm not interested in what you find a nuisance, pleasing, aesthetic;
    you have a very specific and "sharply limited" and strong opinion it
    seems, so no good base for discussions which ask for a somewhat more
    open mind.)

    [...]
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    It didn't use to be allowed (becase a label was defined as being a
    prefix to another statement, so you needed at least an empty statement
    like ";").

    It seems to be relaxed in C23. If you do:

    gcc -std=cxx -pedantic

    then it will give warnings for xx = 90, 99, 11 and 17. Arbitrary other C compilers may complain too.

    So this is a rare example of something I'd thought was an annoying
    quirk, being fixed.

    Of course, if I'd brought it up years ago, people would say the same
    things criticising me, my knowledge, my 'ignorance' and suggest it was a non-issue as you can work around it, exactly like you do below.

    (If generating code, then you don't always know in advance if a label
    will be followed by a } or not; the result is that all labels are
    written as "L:;".)

    int main (void)
    {
    ÿÿÿ {
    ÿÿÿÿÿÿÿ int a = 42;
    ÿÿÿÿÿÿÿ goto end;
    ÿÿÿÿÿÿÿ return a;
    ÿÿÿ end:
    ÿÿÿ }
    ÿÿÿ return 0;
    }

    (although I wouldn't have been astonished if there'd be a requirement
    to allow labels at statements only,

    So you didn't know this? How ignorant of you!

    However there is another aspect to this: in the original spec for
    labels, defining it as a prefix to a statemen meant that its definition
    was recursive. That means that a program like this:

    L1:
    L2:
    ....
    Ln:

    would risk overflowing the compiler stack, compared with a non-recursive version. A minor risk as you'd need tens of thousands of such labels. It
    would only show up in stress tests.

    thus at least requiring something
    like an empty statement as in

    ÿÿÿ ...
    ÿÿÿ end: ;
    ÿÿÿ }

    But all fine in my book. (Especially since I'm anyway rarely - actually
    not at all - using 'goto' labels.)

    No, 'end:;' is not ugly at all!



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Thu May 21 15:04:53 2026
    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    Similarly, the RAII idiom prevalent in C++ and Rust uses object
    destructors that are automatically run when something goes out
    of scope to do the cleanup. C++ pairs that with
    exceptions (arguably worse than goto) while Rust represents
    errors with sum types and a little bit of syntactic sugar with
    the `?` operator. In either case, the descriptors run and do
    the cleanup, regardless of whether the return was an error path
    or a success path.

    We used this in 1990 when implementing a fork() function in
    an unix-compatible distributed operating system (although it
    wasn't known formally as RAII in those days).

    int
    tProc::Fork(KnCap* PActUI, int Caller, tLwp *CrLwp, int IsVFork, boolean_t ForkAll)
    {
    int Error = 0;
    int nlwp; // total number of active lwps in the child
    int total_lwp; // total number of lwps in the child (active + zombies)

    ASSERT((Caller == FORK_SYSCALL) || (Caller == FORK_BOOTPROC));

    KnCap *ChildCap = Actor.GetCap();

    // Hand crafted processes do not have a ParentP.
    tProc *ParentP = CrLwp->MyProc();

    // Initialise the global Ops event that is used for global Ops
    // synchronisation

    EVENT_INIT(&GlobalEvt);

    //
    // Only Single threaded processes can vfork().
    // Currently, we don't support vfork.
    if (IsVFork && (!(ParentP->LwpList.SingleThreaded()))) {
    return EINVAL;
    }

    if (ForkAll) {
    nlwp = ParentP->LwpList.GetCountLwp();
    total_lwp = ParentP->LwpList.GetTotalLwp();

    } else {
    nlwp = 1;
    total_lwp = 1;
    }

    //
    // Check to see if we will be exceeding the quota.
    // XXXKYS: Check the interface for picking up quota tokens.
    // Would prefer an interface that accepted the number of
    // tokens needed - proc + lwps.
    // NOTE: The number of LWP tokens picked up will be equal to the
    // total number of LWPs in the child (including zombie LWPs).

    if (UidQuota.Fork(ProcCred->GetUid(), ProcCred->GetEuid(), total_lwp)) {
    return EAGAIN;
    }

    tForkFailure ff(this, CrLwp, ChildCap, nlwp, total_lwp);
    .
    .
    .
    }

    === The tForkfailure class encapsulated the resources allocated
    to the thread. The fork function could return at any time
    after this and the destructor for tForkFailure would clean up
    any allocated resources.

    //
    // Destructor: undo what fork has done upon failure.
    //
    tForkFailure::~tForkFailure()
    {
    if (ret == 0) {
    return;
    }

    if (PActUI != NULL) {
    procp->Mem.Exit(ChildCap);
    }

    //
    // XXXKYS: the DeleteAll() needs to take
    // into account that some of the LWPs
    // in the list may be zombies.
    //
    procp->LwpList.DeleteAll();

    if (rgn == B_TRUE) {
    procp->Rgn.ShmExit(CrLwp, procp);
    }

    if (semundo == B_TRUE) {
    procp->Undo.SemUnFork();
    }

    if (file == B_TRUE) {
    procp->File.ForkUndo();
    }

    procp->ProcRlim->rlFree(nlwp + 1);
    procp->ProcCred->CrFree(nlwp + 1);

    procp->UidQuota.FreeProc(B_TRUE, total_lwp, 1);
    }

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Thu May 21 15:12:16 2026
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    On 2026-05-20 12:55, Bart wrote:
    On 20/05/2026 05:24, Janis Papanagnou wrote:
    [...]

    2. Are you sure that above code is valid (error-free) Algol 68 code?

    I'm not sure about that. - My old textbook says that you can use these >>>> identifiers *after* you've set them (not before). And skimming through >>>> the standard document, the Revised Report, I could not find anything.
    If there is; can you point me to the relevant chapter, please?

    It could also just not have been defined in the standard. (The above
    code or Genie's behavior would then not tell anything relevant about
    the language. - It would just expose yet another example of code that
    an experienced programmer would just not write that way.)

    You seem to have tested that with the Genie interpreter? - This would
    not say anything about what the Algol 68 standard says, mind.

    You seem determined to prove Bart wrong, aren't you?

    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a handful
    of people on the planet do.

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf


    For that you took an arbitrary Algol 68
    interpreter

    So give me links to some others, or to playgrounds.

    You are not capable of searching the internet yourself?


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Thu May 21 16:47:49 2026
    On 21/05/2026 16:12, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    On 2026-05-20 12:55, Bart wrote:
    On 20/05/2026 05:24, Janis Papanagnou wrote:
    [...]

    2. Are you sure that above code is valid (error-free) Algol 68 code? >>>>>
    I'm not sure about that. - My old textbook says that you can use these >>>>> identifiers *after* you've set them (not before). And skimming through >>>>> the standard document, the Revised Report, I could not find anything. >>>>> If there is; can you point me to the relevant chapter, please?

    It could also just not have been defined in the standard. (The above >>>>> code or Genie's behavior would then not tell anything relevant about >>>>> the language. - It would just expose yet another example of code that >>>>> an experienced programmer would just not write that way.)

    You seem to have tested that with the Genie interpreter? - This would >>>>> not say anything about what the Algol 68 standard says, mind.

    You seem determined to prove Bart wrong, aren't you?

    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a handful
    of people on the planet do.

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    That's not what I wouldn't have a clue about.

    BTW there are better, newer versions than that badly scanned and poorly typeset document:

    https://www.algol68-lang.org/docs/algol68-revised-report.pdf

    Have a glance through that then you will understand my remark.

    For that you took an arbitrary Algol 68
    interpreter

    So give me links to some others, or to playgrounds.

    You are not capable of searching the internet yourself?

    Yes of course. But there is nothing out there other than A68Genie, or
    some experimental version using gcc 16.

    This is why I asked: because I suspect there is little else.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Thu May 21 19:27:51 2026
    On Thu, 21 May 2026 15:12:16 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    Bart <bc@freeuk.com> writes:
    On 21/05/2026 01:30, Janis Papanagnou wrote:
    On 2026-05-20 12:55, Bart wrote:
    On 20/05/2026 05:24, Janis Papanagnou wrote:
    [...]

    2. Are you sure that above code is valid (error-free) Algol 68
    code?

    I'm not sure about that. - My old textbook says that you can use
    these identifiers *after* you've set them (not before). And
    skimming through the standard document, the Revised Report, I
    could not find anything. If there is; can you point me to the
    relevant chapter, please?

    It could also just not have been defined in the standard. (The
    above code or Genie's behavior would then not tell anything
    relevant about the language. - It would just expose yet another
    example of code that an experienced programmer would just not
    write that way.)

    You seem to have tested that with the Genie interpreter? - This
    would not say anything about what the Algol 68 standard says,
    mind.

    You seem determined to prove Bart wrong, aren't you?

    You made a claim that looked strange, and I had been asking you
    whether you can point to the standard

    To the Algol68 standard? I wouldn't have a clue; probably only a
    handful of people on the planet do.

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf



    I did not open it, but my guess is that finding a clue from A68 Standard
    is a Very Hard Job.

    For that you took an arbitrary Algol 68
    interpreter

    So give me links to some others, or to playgrounds.

    You are not capable of searching the internet yourself?




    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Thu May 21 19:37:21 2026
    On Thu, 21 May 2026 14:31:19 +0100
    Bart <bc@freeuk.com> wrote:

    On 21/05/2026 14:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    [...]

    Some of it is an actual nuisance, but quite a bit is also
    aesthetically displeasing.

    (I'm not interested in what you find a nuisance, pleasing,
    aesthetic; you have a very specific and "sharply limited" and
    strong opinion it seems, so no good base for discussions which ask
    for a somewhat more open mind.)

    [...]
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    It didn't use to be allowed (becase a label was defined as being a
    prefix to another statement, so you needed at least an empty
    statement like ";").

    It seems to be relaxed in C23. If you do:

    gcc -std=cxx -pedantic

    then it will give warnings for xx = 90, 99, 11 and 17. Arbitrary
    other C compilers may complain too.

    So this is a rare example of something I'd thought was an annoying
    quirk, being fixed.

    Of course, if I'd brought it up years ago, people would say the same
    things criticising me, my knowledge, my 'ignorance' and suggest it
    was a non-issue as you can work around it, exactly like you do below.


    When I brought it not many years ago, but before C23 was finalized,
    most people that reacted agreed with me that being pedantic in this
    case is not a good idea on part of compiler unless it is asked
    specifically to be pedantic.

    (If generating code, then you don't always know in advance if a label
    will be followed by a } or not; the result is that all labels are
    written as "L:;".)

    int main (void)
    {
    ??? {
    ??????? int a = 42;
    ??????? goto end;
    ??????? return a;
    ??? end:
    ??? }
    ??? return 0;
    }

    (although I wouldn't have been astonished if there'd be a
    requirement to allow labels at statements only,

    So you didn't know this? How ignorant of you!

    However there is another aspect to this: in the original spec for
    labels, defining it as a prefix to a statemen meant that its
    definition was recursive. That means that a program like this:

    L1:
    L2:
    ....
    Ln:

    would risk overflowing the compiler stack, compared with a
    non-recursive version. A minor risk as you'd need tens of thousands
    of such labels. It would only show up in stress tests.

    thus at least requiring something
    like an empty statement as in

    ??? ...
    ??? end: ;
    ??? }

    But all fine in my book. (Especially since I'm anyway rarely -
    actually not at all - using 'goto' labels.)

    No, 'end:;' is not ugly at all!





    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Thu May 21 18:26:46 2026
    On 21/05/2026 13:55, David Brown wrote:
    On 21/05/2026 13:56, Bart wrote:

    [Separate 'namespace' for label names]


    (The separation of the struct/union/enum tag name space from variable namespace /is/ a feature that people use


    Urgh, another discussion. Let's not go there. Suffice that it is unique
    to C.

    We have no idea of a user benefit from /not/ having separate name spaces here.

    So let's have separate namespaces for everything! We just have to keep
    it quite so that people can discover that amazing fact by accident.

    So are you justified in claiming it is pointless?ÿ No, you are not -
    because we haven't ruled out any possibility of user benefits, there is
    a definite implementer benefit, and having the opposite choice would be
    less likely to be of any benefit to anyone.ÿ But I think we can
    reasonably say it is a very minor matter that makes little practical difference to anyone.

    You keep saying this but then still disagree it is pointless.

    If that extra namespace disappeared overnight, would anyone notice?

    If making a new language, would you choose to have the same label name
    and non-label name co-exist in the same scope?

    If not familiar with C, the concept would be bizarre (it is bizarre anyway).

    But labels are inherently simpler - you can only "goto" directly to a defined label.ÿ Parentheses could be of no benefit, so /allowing/ them
    would complicate the grammar.

    Having a label be just another term could be a benefit. For example:

    goto cond ? L1 : L2;

    That means that whatever follows 'goto' is just an expression and those
    may have parentheses.

    However, that example gives a better clue as to why label names are
    special in C: that "L1:" looks like a label definition. I doubt such definitions are allowed in the middle of an expression, but it looks problematical.


    It is not an anomaly when very different things have different rules.

    OK, call it a lack of orthogonality.

    There is also not being able to write 'L:}'; this one bites.

    I don't see why that "bites", unless it is some weird smiley.ÿ I would expect most people to have a the close brace on a separate line from the label, but it is legitimate for people to want to jump to the end of a block.

    An intervening newline doesn't help. But this particular restriction has
    been eased in C23.

    No, most language extensions do not become standard.ÿ This one has been
    in gcc for maybe three decades or more, and is not part of the standard.

    It (label pointers) has been used to help make CPython faster for a long
    time. But only on Linux. On Windows, CPython has to build with MSVC (as
    of a decade ago but maybe still the case), and that doesn't have the
    feature.

    So Python on Windows may be slower because that useful extension was not standardised.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Thu May 21 18:38:51 2026
    On 21/05/2026 17:37, Michael S wrote:
    On Thu, 21 May 2026 14:31:19 +0100
    Bart <bc@freeuk.com> wrote:

    On 21/05/2026 14:08, Janis Papanagnou wrote:

    It didn't use to be allowed (becase a label was defined as being a
    prefix to another statement, so you needed at least an empty
    statement like ";").

    It seems to be relaxed in C23. If you do:

    gcc -std=cxx -pedantic

    then it will give warnings for xx = 90, 99, 11 and 17. Arbitrary
    other C compilers may complain too.

    So this is a rare example of something I'd thought was an annoying
    quirk, being fixed.

    Of course, if I'd brought it up years ago, people would say the same
    things criticising me, my knowledge, my 'ignorance' and suggest it
    was a non-issue as you can work around it, exactly like you do below.


    When I brought it not many years ago, but before C23 was finalized,
    most people that reacted agreed with me that being pedantic in this
    case is not a good idea on part of compiler unless it is asked
    specifically to be pedantic.

    This is what I get from various compilers and versions, using default
    options:

    gcc 10.x: error: label at end of compound statement

    gcc 14.x/16.x: nothing (-pedantic will give a warning)

    DMC: error

    lccwin32: error

    Tiny C: warning

    clang 18.x: nothing

    So it used to be taken seriously for decades, but why would it do that
    in the first place?

    And why would anyone want it reported? This is a clear misfeature that provides no benefit.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Thu May 21 19:46:17 2026
    On 2026-05-21 17:12, Scott Lurndal wrote:
    [...]

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    The Genie system (that I strongly presume Bart is also using)
    comes with a 700-pages manual/tutorial/reference that contains
    the full Revised Report.

    https://jmvdveer.home.xs4all.nl/learning-algol-68-genie.pdf

    Yes, it would have been easy to *find* it, but I'd presume if
    one is using the Genie he would already *have* the document.

    But for some folks it's easier to write a dozen posts full of
    complaints instead of just taking the time to sit back and slow
    down, think, and inspect the documentation instead of emitting
    wild guesses and declare them as facts.

    Janis

    [...]


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From James Kuyper@3:633/10 to All on Thu May 21 13:48:12 2026
    On 21/05/2026 13:56, Bart wrote:
    On 21/05/2026 07:45, David Brown wrote:
    You can have an opinion from ignorance, as many people have told you.
    You can't expect that opinion to be respected.

    That's just rubbish, and an attempt to stifle criticism.
    No, only to stifle uninformed criticism.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From James Kuyper@3:633/10 to All on Thu May 21 13:54:04 2026
    On 2026-05-21 09:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    ...
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    The only two places where labels are permitted are in labeled statements (6.8.2p1) and goto statements. Since ';' qualifies as a null statement,
    Bart is required to make the extremely painful modification of instead
    writing 'L:;}'.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Thu May 21 20:03:31 2026
    On 2026-05-21 18:27, Michael S wrote:
    On Thu, 21 May 2026 15:12:16 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    I did not open it, but my guess is that finding a clue from A68 Standard
    is a Very Hard Job.

    Well, my experience with (most) standards is that they are typically
    not written for the casual user or ordinary programmer. The Algol 68
    standard, the Revised Report, specifically seems to be regularly
    considered as being "not the easiest" to read document. (Myself I've
    never read a standards document to learn a programming language, but
    I've studied other international standards, documents of hundreds and
    thousands of pages, so I'm at least not repelled by such documents
    beforehand, and if I'm interested and want clarity about something I
    look up these documents. - I did that also to look for Bart's claim,
    but I could not find anything that would support Bart's statement.)

    Though for the given sub-thread we have to state that to clarify the
    correct syntax and semantics of a language we will have to look into
    the respective defining reference documents; the standards. - A wild
    guess, speculation, wish, or opinion, cannot substitute a standard.

    Janis


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Thu May 21 20:09:41 2026
    On 2026-05-21 19:54, James Kuyper wrote:
    On 2026-05-21 09:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    ...
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...

    The only two places where labels are permitted are in labeled statements (6.8.2p1) and goto statements. Since ';' qualifies as a null statement,
    Bart is required to make the extremely painful modification of instead writing 'L:;}'.

    Aha, so the GNU C-compiler I'm using was just generous to not require
    a semicolon at the block-terminating brace.

    Thanks for the information (and the standard reference)!

    Janis


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Thu May 21 19:42:30 2026
    On 21/05/2026 19:03, Janis Papanagnou wrote:
    On 2026-05-21 18:27, Michael S wrote:
    On Thu, 21 May 2026 15:12:16 GMT
    scott@slp53.sl.home (Scott Lurndal) wrote:

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    I did not open it, but my guess is that finding a clue from A68 Standard
    is a Very Hard Job.

    Well, my experience with (most) standards is that they are typically
    not written for the casual user or ordinary programmer. The Algol 68 standard, the Revised Report, specifically seems to be regularly
    considered as being "not the easiest" to read document. (Myself I've
    never read a standards document to learn a programming language, but
    I've studied other international standards, documents of hundreds and thousands of pages, so I'm at least not repelled by such documents beforehand, and if I'm interested and want clarity about something I
    look up these documents. - I did that also to look for Bart's claim,
    but I could not find anything that would support Bart's statement.)

    Though for the given sub-thread we have to state that to clarify the
    correct syntax and semantics of a language we will have to look into
    the respective defining reference documents; the standards. - A wild
    guess, speculation, wish, or opinion, cannot substitute a standard.

    Have you yet found a working version that implements what it said?



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Thu May 21 20:08:19 2026
    On 21/05/2026 18:46, Janis Papanagnou wrote:
    On 2026-05-21 17:12, Scott Lurndal wrote:
    [...]

    A google search for "algol 68 standard document" would give you
    a clue.

    https://algol68-lang.org/docs/algol68-draft-report.pdf

    The Genie system (that I strongly presume Bart is also using)
    comes with a 700-pages manual/tutorial/reference that contains
    the full Revised Report.

    https://jmvdveer.home.xs4all.nl/learning-algol-68-genie.pdf

    Yes, it would have been easy to *find* it, but I'd presume if
    one is using the Genie he would already *have* the document.

    But for some folks it's easier to write a dozen posts full of
    complaints

    I haven't made any complaint about Algol68 here.

    I merely wrote a program and observed its behaviour on what might be the
    only current implementation in the world.

    What reason would I have had to doubt that it was performing correctly?

    instead of just taking the time to sit back and slow
    down, think, and inspect the documentation instead of emitting
    wild guesses and declare them as facts.

    You mean that completely impenetrable gobbledygook? Perhaps you'd like
    to enlighten us to what exactly it says.

    That is, what exactly is in scope within the dotted lines here:

    INT a:=1; BEGIN ... INT a:=2; END

    In C it would be that first 'a'.

    In both my languages it would be that second 'a'.

    In Python, it would also be that second 'a' (which is another
    'real-world' language that does this; I didn't need Algol68 after all!)

    In Algol68, assuming a working reference implementation, it would be
    .... ? Simple question.

    in the same way I politely pointed out the issue with labels at the ends
    of compound statements, that you seemed to be unaware of, you could have politely corrected my assumption.

    (I guess now you're going to study the Python reference manual, and
    badger the CPython maintainers to admit they may have made a mistake,
    just to prove me wrong again.)


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Thu May 21 21:23:27 2026
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    On 21/05/2026 13:56, Bart wrote:

    [Separate 'namespace' for label names]


    (The separation of the struct/union/enum tag name space from variable
    namespace /is/ a feature that people use


    Urgh, another discussion. Let's not go there. Suffice that it is unique
    to C.

    Really? All other languages keep everything in the one name space, do
    they? Are you /sure/ about that? You have checked how Lisp, Scala,
    Ocaml, and other languages work?


    We have no idea of a user benefit from /not/ having separate name
    spaces here.

    So let's have separate namespaces for everything! We just have to keep
    it quite so that people can discover that amazing fact by accident.

    So are you justified in claiming it is pointless?ÿ No, you are not -
    because we haven't ruled out any possibility of user benefits, there
    is a definite implementer benefit, and having the opposite choice
    would be less likely to be of any benefit to anyone.ÿ But I think we
    can reasonably say it is a very minor matter that makes little
    practical difference to anyone.

    You keep saying this but then still disagree it is pointless.

    If that extra namespace disappeared overnight, would anyone notice?

    "Pointless" means there is /no/ point. That's different from saying it
    is a minor matter and alternative decisions would likely not make a big difference.


    If making a new language, would you choose to have the same label name
    and non-label name co-exist in the same scope?


    The question I'd be asking myself is why would I choose to put label
    names in the same scope as anything else? (And that's assuming a new
    language even has a concept of labels.) There's no reason to do so.
    Much more interesting are thoughts about whether types, functions and variables should share a name space or be separate.

    If not familiar with C, the concept would be bizarre (it is bizarre
    anyway).

    At what point in your C programming life have you felt "I wish labels
    were in the same name space as variables. It would make my programming
    /so/ much easier and clearer" ?


    But labels are inherently simpler - you can only "goto" directly to a
    defined label.ÿ Parentheses could be of no benefit, so /allowing/ them
    would complicate the grammar.

    Having a label be just another term could be a benefit. For example:

    ÿ goto cond ? L1 : L2;

    C does not have any kind of goto statement with an expression. This is
    good and appropriate. "goto" is an unstructured concept, and should be
    used sparingly in well-written code.


    That means that whatever follows 'goto' is just an expression and those
    may have parentheses.

    However, that example gives a better clue as to why label names are
    special in C: that "L1:" looks like a label definition. I doubt such definitions are allowed in the middle of an expression, but it looks problematical.


    Yes, in the same way that "x * y" looks like pointer dereference. In
    other words, not at all.


    It is not an anomaly when very different things have different rules.

    OK, call it a lack of orthogonality.


    "A foolish consistency is the hobgoblin of little minds."

    There is also not being able to write 'L:}'; this one bites.

    I don't see why that "bites", unless it is some weird smiley.ÿ I would
    expect most people to have a the close brace on a separate line from
    the label, but it is legitimate for people to want to jump to the end
    of a block.

    An intervening newline doesn't help. But this particular restriction has been eased in C23.

    Oh, I see what you mean - you are complaining that (prior to C23) you
    could /not/ have a label just before the closing brace. I still don't
    see how that "bites" - you had to write "L: ;". I'd be surprised if
    anyone found it a hardship, never mind thinking it "bites".


    No, most language extensions do not become standard.ÿ This one has
    been in gcc for maybe three decades or more, and is not part of the
    standard.

    It (label pointers) has been used to help make CPython faster for a long time. But only on Linux. On Windows, CPython has to build with MSVC (as
    of a decade ago but maybe still the case), and that doesn't have the feature.

    So Python on Windows may be slower because that useful extension was not standardised.


    It is not a lack of standardisation - it is a lack of support for the
    feature in MSVC. There is nothing to stop MSVC having the same
    extensions, and MSVC is renowned for failing to support lots of C
    standard features (basically, anything in C99 onwards that is not also
    in C++ - though I believe they have got marginally better in recent years).


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Thu May 21 20:59:59 2026
    On 21/05/2026 20:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    On 21/05/2026 13:56, Bart wrote:

    [Separate 'namespace' for label names]


    (The separation of the struct/union/enum tag name space from variable
    namespace /is/ a feature that people use


    Urgh, another discussion. Let's not go there. Suffice that it is
    unique to C.

    Really?ÿ All other languages keep everything in the one name space, do
    they?ÿ Are you /sure/ about that?ÿ You have checked how Lisp, Scala,
    Ocaml, and other languages work?

    OK, it's something I haven't come across before in other languages.

    Some may have naming conventions, others may only have types appearing
    in certain contexts.

    But I don't recall seeing anything like C's scheme which has both struct
    tag names in their own namespace, AND user-defined type names in the
    general namespace:

    typedef struct Point {int x, y;} Point;
    struct Point a;
    Point b;

    or:

    struct Vector {Point p, q;};
    struct Vector Vector;

    It's messy; Point is both a struct tag and type name in the same scope.
    Vector is both a struct tag and variable name in the same scope.

    You can't have the hat-trick however because the same type name and
    variable name can't appear in the same scope; it needs an extra namespace!

    At best they can be in the same block:

    struct Vector Vector; {....; typedef struct Vector Vector; Vector:;}

    Within those {} exist Vector (variable); Vector (struct tag); and Vector
    (type name); plus Vector (label) for good measure.

    You're saying Lisp has all this too?

    Lisp does has a reputation for being everything (interpreted/compiled; static/dynamic; functional/imperative etc), but this seems a stretch.


    --------------------------------

    record Point = (int x, y)
    record Vector = (Point p, q)

    Point a, b
    Vector `Vector # best I could do

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Thu May 21 14:23:27 2026
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 07:45, David Brown wrote:
    [...]
    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact. Nobody here other than you particularly cares.

    If that fact had caused you any inconvenience, you might have a
    glimmering of point.

    If you really care about the reason, you could do your own research.
    You could *ask* rather than whining about the fact most of us aren't
    bothered by it. You could look at documents for earlier versions
    of C, and for C's predecessor languages.

    David knows that labels have their own name space. He apparently
    doesn't know why, and likely has decided it doesn't matter much.
    And yet you continue to argue.

    Here's an entirely speculative possible rationale. If I define
    a variable "foo" within a function, and then add a label "foo:"
    later in the same function, the label name does not interfere with
    the variable name. If labels were not in their own name space,
    adding the label later in the function would interfere with the
    declaration earlier in the function. Labels are the only named
    entities whose scope begins before their definition. That *might*
    have been in Ritchie's mind when he specified how labels work.

    [...]

    You didn't like my example of being able to write '(F)(x)' (or 'int
    (a);') but not 'goto (L)', but the anomaly is there.

    Different entities have different rules. You invent contrived
    examples meant to demonstrate that C Is Bad And Inconsistent rather
    than doing anything at all to help anyone understand what the rules
    actually are or how to work with them. You expend tremendous effort
    trying to prove that C has quirks, something that everyone here is
    perfectly well aware of.

    Putting parentheses around a label name wouldn't even make any sense. Disallowing `goto (L)` is not a quirk. Allowing it would be.

    There is also not being able to write 'L:}'; this one bites.

    It's always been trivially easy to write `L:;}` rather than `L:}`.
    And C23 allows a bare label just before a `}`, so the problem is
    now fixed.

    I predict that you will be outraged that this minor problem wasn't
    solved sooner rather than happy that it was actually solved.

    [...]

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

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Thu May 21 14:31:31 2026
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-21 19:54, James Kuyper wrote:
    On 2026-05-21 09:08, Janis Papanagnou wrote:
    On 2026-05-21 13:56, Bart wrote:
    ...
    There is also not being able to write 'L:}'; this one bites.

    But, being curious, what is that supposed to mean? - A label at the
    end of a block? - Seems to work for me (with my GNU C-compiler)...
    The only two places where labels are permitted are in labeled
    statements
    (6.8.2p1) and goto statements. Since ';' qualifies as a null statement,
    Bart is required to make the extremely painful modification of instead
    writing 'L:;}'.

    Aha, so the GNU C-compiler I'm using was just generous to not require
    a semicolon at the block-terminating brace.

    Thanks for the information (and the standard reference)!

    gcc by default compiles GNU C, not ISO C. It also fails to emit many language-required diagnostics. GNU C is ISO C with gcc extensions.
    Prior to C23, allowing "L:}" is a documented gcc extension.
    It's rejected if you ask gcc to conform to ISO C17 or earlier.

    Observing what gcc allows by default tells you very litlte about
    the C language.

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

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Thu May 21 21:35:10 2026
    In article <10uno7u$10dvs$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >[snip]

    You're saying Lisp has all this too?

    You're moving the goalposts, again. You posted some abstruse C
    code and are asking if that code, specifically, exists in all of
    its grotesque glory in Lisp. But the question was really
    whether C was unique in having different namespaces for
    different kinds of objects (types, functions, variables, and so
    on).

    Lisp does has a reputation for being everything (interpreted/compiled; >static/dynamic; functional/imperative etc), but this seems a stretch.

    It depends on the specific Lisp you are talking about (there are
    many). Note that it is typical to refer to the plural of Lisp
    as "Lisps" (as opposed to dialects or similar).

    In https://dreamsongs.com/Separation.html, Gabriel describes the
    dual notions of a "value namespace" and a "function namespace",
    and categories lists into two broad categories, based on whether
    a particular Lisp uses a single namespace value and function
    (Scheme, Clojure) or these are distinct (Common Lisp). He calls
    thse Lisp_1 and Lisp_2, respectively (note the numbers are meant
    to be subscripted).

    As a Lisp_2, in Common Lisp a symbol has an associated plist
    with distinct value and function slots; which is given when a
    symbol is evaluated depends on context: if the symbol is in
    the function position of a form, you get the entry from the
    function namespace, otherwise, from the value namespace. Some
    people don't like this. Some do.

    So, in Common Lisp, a symbol can refer to both a function and a
    value simultaneously; which you get depends on context. See
    also section 12 of Gabriel's paper, linked above.

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Thu May 21 23:47:02 2026
    On 21/05/2026 22:23, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 07:45, David Brown wrote:
    [...]
    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact. Nobody here other than you particularly cares.

    This is how labels entered the discussion:

    "The simplest scoping rule in C is for labels: they have function-wide
    scope, regardless of block nesting label.

    That would almost be as simple as how my own scopes work, except C just
    has to still be quirky:

    double c; c: goto c;

    (Labels of course have their own namespace, for some obscure reason. I'm
    sure 99% of C programmers don't know that.) "

    I introduced label names as an example of a much simpler scoping scheme
    than other kinds of names.

    Here's an entirely speculative possible rationale. If I define
    a variable "foo" within a function, and then add a label "foo:"
    later in the same function, the label name does not interfere with
    the variable name. If labels were not in their own name space,
    adding the label later in the function would interfere with the
    declaration earlier in the function. Labels are the only named
    entities whose scope begins before their definition. That *might*
    have been in Ritchie's mind when he specified how labels work.

    You can also define a new type, or macro, or enum, or declare a function called "foo" whose name could interfere with variable "foo" if within
    the same block.

    Did the earliest C have block scopes? Then scoping rules between labels
    and non-labels would have clashed. Perhaps that was a reason.


    Putting parentheses around a label name wouldn't even make any sense. Disallowing `goto (L)` is not a quirk. Allowing it would be.

    Allowing labels as expressions has advantages; I gave some examples.
    Being able to use parentheses is a consequence, but not useful in
    itself. But the special namespace puts paid to that.


    There is also not being able to write 'L:}'; this one bites.

    It's always been trivially easy to write `L:;}` rather than `L:}`.
    And C23 allows a bare label just before a `}`, so the problem is
    now fixed.

    I predict that you will be outraged that this minor problem wasn't
    solved sooner rather than happy that it was actually solved.

    Yes; why did it exist in the first place?

    (1) I complain about something

    (2) Everyone says it is a non-issue and I'm complaining about
    triviliaties

    (3) Despite that, it mysteriously gets fixed anyway

    Is it possible that I might have had a valid point in the first place?


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From James Kuyper@3:633/10 to All on Thu May 21 19:41:52 2026
    On 2026-05-21 17:31, Keith Thompson wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-21 19:54, James Kuyper wrote:
    ...
    The only two places where labels are permitted are in labeled
    statements
    (6.8.2p1) and goto statements. Since ';' qualifies as a null statement,
    Bart is required to make the extremely painful modification of instead
    writing 'L:;}'.

    Aha, so the GNU C-compiler I'm using was just generous to not require
    a semicolon at the block-terminating brace.

    Thanks for the information (and the standard reference)!

    gcc by default compiles GNU C, not ISO C. It also fails to emit many language-required diagnostics. GNU C is ISO C with gcc extensions.
    Prior to C23, allowing "L:}" is a documented gcc extension.
    It's rejected if you ask gcc to conform to ISO C17 or earlier.

    Observing what gcc allows by default tells you very litlte about
    the C language.

    He's not relying on gcc, he's relying on my citation of a draft of the standard. I missed the fact that 6.8.3p1 has been changed to allow a
    label with no following statement as a block item in a compound statement.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From James Kuyper@3:633/10 to All on Thu May 21 19:49:40 2026
    On 2026-05-21 17:23, Keith Thompson wrote:
    ...
    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact.

    I did. I pointed out that the standard sets aside a separate name space
    for each kind of identifier which is usable only in syntactic contexts
    where an ordinary identifier would not be allowed.

    The Rationale says "The position adopted in the Standard is to permit as
    many separate name spaces as can be distinguished by context, ...".

    Apparently the Committee likes separate name spaces; you'll have to ask
    them why.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Thu May 21 17:24:02 2026
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 22:23, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    On 21/05/2026 07:45, David Brown wrote:
    [...]
    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact. Nobody here other than you particularly cares.

    This is how labels entered the discussion:

    "The simplest scoping rule in C is for labels: they have function-wide
    scope, regardless of block nesting label.

    That would almost be as simple as how my own scopes work, except C
    just has to still be quirky:

    double c; c: goto c;

    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "

    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    I introduced label names as an example of a much simpler scoping
    scheme than other kinds of names.

    And that point has been abundantly made.

    [...]

    Did the earliest C have block scopes? Then scoping rules between
    labels and non-labels would have clashed. Perhaps that was a reason.

    Maybe. If you care, do the research. If you don't, why are you still
    talking about it?

    Putting parentheses around a label name wouldn't even make any sense.
    Disallowing `goto (L)` is not a quirk. Allowing it would be.

    Allowing labels as expressions has advantages; I gave some
    examples. Being able to use parentheses is a consequence, but not
    useful in itself. But the special namespace puts paid to that.

    In C, label names are not expressions. It's that simple. C does
    not allow parentheses around a label name, and there is no reason
    at all why it should do so. (If it did, you'd probably complain
    that it's inconsistent.)

    There is also not being able to write 'L:}'; this one bites.

    It's always been trivially easy to write `L:;}` rather than `L:}`.
    And C23 allows a bare label just before a `}`, so the problem is
    now fixed.

    I predict that you will be outraged that this minor problem wasn't
    solved sooner rather than happy that it was actually solved.

    Yes; why did it exist in the first place?

    It wasn't seen as necessary. The workaround, adding a semicolon, is
    trivial, and has been mentioned at least as far back as K&R1, 1978.

    (1) I complain about something

    (2) Everyone says it is a non-issue and I'm complaining about
    triviliaties

    (3) Despite that, it mysteriously gets fixed anyway

    Is it possible that I might have had a valid point in the first place?

    Sure. FYI, here's the proposal that, as far as I can tell, led to C23
    allowing a label immediately before a "}". It was written by Martin
    Uecker in 2020.

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    The author described the existing definition as "an unnecessarily and
    annoying limitation" and proposed a fix, which was accepted.

    Two things can be true simultaneously:

    1. The existing limitation was no more than a minor annoyance with
    an easy workaround. Few C programmers thought that it "bites".

    2. The committee decided it was worth fixing. Somebody (notably not
    you) took the time to write a proposal and submit it to the
    commmittee, which accepted it. The proposal was designed not
    to break any existing code.

    Note that this change did not affect the scoping rules for labels.

    There is a proposal to change the scoping rules for labels in C2y.
    If you're curious, visit

    <https://www.open-std.org/jtc1/sc22/wg14/www/wg14_document_log>

    and search for recent papers with "labels" in their titles. This is
    related to named break and continue statements, a new feature in C2y.
    It does not affect scoping for labels that are used only as targets
    for goto statements.

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

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Thu May 21 17:27:23 2026
    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 2026-05-21 17:23, Keith Thompson wrote:
    ...
    This entire discussion is pointless. It is a fact that C labels
    are in their own name space. Nobody here has provided a rationale
    for that fact.

    I did. I pointed out that the standard sets aside a separate name space
    for each kind of identifier which is usable only in syntactic contexts
    where an ordinary identifier would not be allowed.

    The Rationale says "The position adopted in the Standard is to permit as
    many separate name spaces as can be distinguished by context, ...".

    I stand corrected. (This wording appears in both the C89 and C99
    Rationale documents.)

    Apparently the Committee likes separate name spaces; you'll have to ask
    them why.

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

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 01:51:20 2026
    On 22/05/2026 01:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:

    Allowing labels as expressions has advantages; I gave some
    examples. Being able to use parentheses is a consequence, but not
    useful in itself. But the special namespace puts paid to that.

    In C, label names are not expressions. It's that simple. C does
    not allow parentheses around a label name, and there is no reason
    at all why it should do so. (If it did, you'd probably complain
    that it's inconsistent.)

    You don't understand my point, so I will leave it.

    It wasn't seen as necessary. The workaround, adding a semicolon, is
    trivial, and has been mentioned at least as far back as K&R1, 1978.

    (1) I complain about something

    (2) Everyone says it is a non-issue and I'm complaining about
    triviliaties

    (3) Despite that, it mysteriously gets fixed anyway

    Is it possible that I might have had a valid point in the first place?

    Sure. FYI, here's the proposal that, as far as I can tell, led to C23 allowing a label immediately before a "}". It was written by Martin
    Uecker in 2020.

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers encountered the quirk, found it annoying, and moved on.

    Some may have complained (and possibly told to shut up and go away).
    It's only until 2020 that somebody does something about it.


    The author described the existing definition as "an unnecessarily and annoying limitation" and proposed a fix, which was accepted.

    Two things can be true simultaneously:

    1. The existing limitation was no more than a minor annoyance with
    an easy workaround. Few C programmers thought that it "bites".

    2. The committee decided it was worth fixing. Somebody (notably not
    you)

    Nor you or anyone else here either. Remember I'm a casual user of C.

    And notably:

    * The issue has NEVER existed in my own languages

    * The fix for C already exists in my C compiler (that is, allowing
    labels in front of } or declarations). A hangover from when I tried to
    fix a few other things.

    So I've already done more than most in dealing with it within language
    design, within an implementation, and talking about it here.




    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Fri May 22 05:23:17 2026
    On 2026-05-22 01:41, James Kuyper wrote:
    On 2026-05-21 17:31, Keith Thompson wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-21 19:54, James Kuyper wrote:
    ...
    The only two places where labels are permitted are in labeled
    statements
    (6.8.2p1) and goto statements. Since ';' qualifies as a null statement, >>>> Bart is required to make the extremely painful modification of instead >>>> writing 'L:;}'.

    Aha, so the GNU C-compiler I'm using was just generous to not require
    a semicolon at the block-terminating brace.

    Thanks for the information (and the standard reference)!

    gcc by default compiles GNU C, not ISO C. It also fails to emit many
    language-required diagnostics. GNU C is ISO C with gcc extensions.
    Prior to C23, allowing "L:}" is a documented gcc extension.
    It's rejected if you ask gcc to conform to ISO C17 or earlier.

    Observing what gcc allows by default tells you very litlte about
    the C language.

    He's not relying on gcc, he's relying on my citation of a draft of the standard.

    Exactly. - Actually I didn't read Keith's comment as presuming I'd
    rely on gcc's behavior; but only because I factually don't rely on
    gcc. But his wording could indeed be misread and impute something
    like that. So thanks for pointing that out; myself I wouldn't have
    noticed that connotation.

    My intention should have been clear to Keith by my original saying
    "Seems to work for me (with my GNU C-compiler)" with an *explicit*
    hint on specific behavior of a specific tool mentioned. But he may
    have missed that. - And to his favor we can still also interpret
    his post's "tells you" having been meant just as a general hint to
    all readers, to be understood as "tells one".

    I missed the fact that 6.8.3p1 has been changed to allow a
    label with no following statement as a block item in a compound statement.

    Thanks.

    Janis


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Fri May 22 05:58:51 2026
    On 2026-05-22 02:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]

    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "

    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).

    So if you want to talk about a namespace there's nothing wrong
    with using the term namespace (without any punctuation or any
    arbitrary spacing).

    I think (instead of a space) it's sensible to apply typographical
    conventions to signify "features". I've, for example, generally
    used single quotes to identify programming language entities or
    keywords, as in 'namespace' (a C++ feature or keyword).

    ("A namespace in C++ is denoted by the keyword 'namespace'.")

    Denoting such things with (for example) single quotes makes the
    whole text also better readable, because the typical programming
    languages' entities are (also) English words and interfere with
    the surrounding texts.

    Janis

    [...]


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Thu May 21 21:21:49 2026
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-22 02:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "
    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).

    It's not in dictionary.com or the online Merriam Webster (not
    surprisingly, the OED has it), and I don't think it's a common term
    outside programming.

    So if you want to talk about a namespace there's nothing wrong
    with using the term namespace (without any punctuation or any
    arbitrary spacing).

    The problem is that "namespace" is a C++ keyword with a specific
    meaning that doesn't apply to C. And the C standard consistently
    uses the phrase "name space", and defines it as a technical term.

    Of course C and C++ are two different languages, but they share a lot
    in common. C++ has both C-style name spaces and its own namespaces.

    Referring to C's name spaces as "namespaces" might not cause much
    confusion, but it's likely to induce a reaction from the more
    pedantic among us.

    [snip]

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

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Fri May 22 06:52:28 2026
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    Labels are no first-class object in "C", and there's no necessity
    to be (IMO).


    C does not have any kind of goto statement with an expression.ÿ This is
    good and appropriate.ÿ "goto" is an unstructured concept, and should be
    used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer
    languages, though. I'd suppose it's primarily a remnant of history.)

    [...]
    It is not an anomaly when very different things have different rules.

    OK, call it a lack of orthogonality.

    "A foolish consistency is the hobgoblin of little minds."

    Oh, orthogonality is a valuable property of a programming language!

    "C" is mostly lacking that property, but "C" is what it is. There's
    certainly no point in Bart's continuous complaints-tirades.

    Janis

    [...]


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Fri May 22 07:17:30 2026
    On 2026-05-22 06:21, Keith Thompson wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-22 02:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]
    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "
    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).

    It's not in dictionary.com or the online Merriam Webster (not
    surprisingly, the OED has it), and I don't think it's a common term
    outside programming.

    So if you want to talk about a namespace there's nothing wrong
    with using the term namespace (without any punctuation or any
    arbitrary spacing).

    The problem is that "namespace" is a C++ keyword with a specific
    meaning that doesn't apply to C. And the C standard consistently
    uses the phrase "name space", and defines it as a technical term.

    Of course C and C++ are two different languages, but they share a lot
    in common. C++ has both C-style name spaces and its own namespaces.

    Referring to C's name spaces as "namespaces" might not cause much
    confusion, but it's likely to induce a reaction from the more
    pedantic among us.

    I wouldn't import the conventions of a specific language's "bible"
    to colloquial communication, even if that communication is mostly
    about that language. If we're talking about concepts ("namespaces")
    it's fine and appropriate to simply name them as such. (YMMV.)

    A subtle space is just an IMO inappropriate means; if compared to
    the typographical suggestion I made. Specifically in the light that
    we often see word-compounds written in many alternative forms (say,
    "name space", "name-space", or "namespace").

    I don't much care about the pedants - I know them by name :-) - if
    they're missing the more important global picture; the clearness of formulations (as I previously expanded on [in the part you trimmed]).

    In the light of your suggestion (a space) I wanted to suggest a
    (IMO better) typographical alternative (quoted technical entities).

    Janis


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Fri May 22 09:31:36 2026
    On 21/05/2026 21:59, Bart wrote:
    On 21/05/2026 20:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    On 21/05/2026 13:56, Bart wrote:

    [Separate 'namespace' for label names]


    (The separation of the struct/union/enum tag name space from
    variable namespace /is/ a feature that people use


    Urgh, another discussion. Let's not go there. Suffice that it is
    unique to C.

    Really?ÿ All other languages keep everything in the one name space, do
    they?ÿ Are you /sure/ about that?ÿ You have checked how Lisp, Scala,
    Ocaml, and other languages work?

    OK, it's something I haven't come across before in other languages.

    Fair enough. I am happy to go along with that. We both know a bit
    about a number of languages, but it is a big stretch to make claims
    about /all/ languages (even all mainstream languages).

    Scala and ML languages, AFAIUI, have separate name spaces for types and variables, and it is not uncommon to have "int : int" style declarations
    to make a variable of type "int". Lisp, according to my brief googling
    (so take this with a cup-load of salt) has separate name spaces for
    functions and variables. Languages with sigils like Perl, PHP and BASIC
    have multiple name spaces for different kinds of variables, where the
    sigil makes the name space explicit.


    Some may have naming conventions, others may only have types appearing
    in certain contexts.

    But I don't recall seeing anything like C's scheme which has both struct
    tag names in their own namespace, AND user-defined type names in the
    general namespace:

    ÿÿ typedef struct Point {int x, y;} Point;
    ÿÿ struct Point a;
    ÿÿ Point b;

    or:

    ÿÿ struct Vector {Point p, q;};
    ÿÿ struct Vector Vector;

    It's messy; Point is both a struct tag and type name in the same scope. Vector is both a struct tag and variable name in the same scope.

    It's not "messy" - it is simple and clear. The rules are in no sense difficult.

    People have different preferences here - some people to use "struct
    Point" to emphasise that the type is a structure and not a scalar type,
    some people like to use "Point" and avoid the distinction (this is,
    naturally, common for C++ programmers). Some people like to skip the
    struct tag name entirely and write "typedef struct { int x; int y; }
    Point;". (You can't do that for recursive types.) And some people like
    to use different tag names and typedef names.

    It gives a flexibility and choice of style.

    Of course, if a programmer randomly mixes the different styles, it can
    be messy and confusing. But no matter what rules a language has, no
    language can eliminate programmers' ability to write messy and confusing
    code.

    You think of every feature in C as a way for people to write unclear
    code - in reality, almost everything you complain about is a way for
    people to write clear and readable code.

    I think perhaps your problem here is that for your own language, all the
    code samples you can find were written by /you/. Thus all code written
    in your language is clear and easily understood by everyone reading the language - because it is all the one person. With real-world languages,
    there is a huge variety of readers and writers, and it is normal that
    they have wide differences in what they consider messy or neat, readable
    or incomprehensible.


    You can't have the hat-trick however because the same type name and
    variable name can't appear in the same scope; it needs an extra namespace!

    At best they can be in the same block:

    ÿÿ struct Vector Vector; {....; typedef struct Vector Vector; Vector:;}

    Within those {} exist Vector (variable); Vector (struct tag); and Vector (type name); plus Vector (label) for good measure.

    You're saying Lisp has all this too?

    I didn't say any details about Lisp - merely that it does not have
    (AFAIUI) everything in one name space.


    Lisp does has a reputation for being everything (interpreted/compiled; static/dynamic; functional/imperative etc), but this seems a stretch.


    --------------------------------

    record Pointÿ = (int x, y)
    record Vector = (Point p, q)

    Point a, b
    Vector `Vectorÿÿÿÿÿÿÿÿÿÿ # best I could do


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Fri May 22 09:34:28 2026
    On 22/05/2026 06:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    Labels are no first-class object in "C", and there's no necessity
    to be (IMO).


    C does not have any kind of goto statement with an expression.ÿ This
    is good and appropriate.ÿ "goto" is an unstructured concept, and
    should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer languages, though. I'd suppose it's primarily a remnant of history.)

    [...]
    It is not an anomaly when very different things have different rules.

    OK, call it a lack of orthogonality.

    "A foolish consistency is the hobgoblin of little minds."

    Oh, orthogonality is a valuable property of a programming language!

    "C" is mostly lacking that property, but "C" is what it is. There's
    certainly no point in Bart's continuous complaints-tirades.


    Emphasis is on the "foolish". Consistency and orthogonality are good,
    taking them too far is counter-productive.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Fri May 22 10:49:02 2026
    On 22/05/2026 05:58, Janis Papanagnou wrote:
    On 2026-05-22 02:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:
    [...]

    (Labels of course have their own namespace, for some obscure
    reason. I'm sure 99% of C programmers don't know that.) "

    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).

    So if you want to talk about a namespace there's nothing wrong
    with using the term namespace (without any punctuation or any
    arbitrary spacing).

    I think (instead of a space) it's sensible to apply typographical
    conventions to signify "features". I've, for example, generally
    used single quotes to identify programming language entities or
    keywords, as in 'namespace' (a C++ feature or keyword).

    ("A namespace in C++ is denoted by the keyword 'namespace'.")

    Denoting such things with (for example) single quotes makes the
    whole text also better readable, because the typical programming
    languages' entities are (also) English words and interfere with
    the surrounding texts.

    Janis


    There are two reasons to use the term "name space" rather than
    "namespace". On is that C++, and many other languages, support
    "namespaces" as a type of structuring of identifier scope. This is a different concept from the "name spaces" we are discussing here.

    The other reason to include the space is that the C standards use the
    term "name space", so if anyone wants to look things up in the standards
    and search for mentions of the concept, they will need to include the space.


    It is unfortunate that there are no good, convenient ways to distinguish between "identifier name spaces" in C (and other languages) and "scope namespaces" in C++ (and other languages, but not C). Such collisions
    occur all the time. We might colloquially say that functions and
    variables are different types of objects - but "type" and "object" have specific meanings in C that do not match such loose usage. (And the
    meaning of "object" in C differs substantially from the same term in
    many other languages.) So we can try other words, like "kind" (but
    that's a keyword in Fortran), or "concept" (now a keyword in C++), and
    so on.

    The best we can do is try to be as accurate as we can about terms in the
    C standards, since we are in c.l.c., and try to make the context clear.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 11:43:06 2026
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?

    Actually you can do pretty much this with gnu C:

    goto *(c ? &&L1 : &&L2);

    but it still doesn't allow a general expression. It seems that what
    comes after 'goto' must be either a regular label, or '*' to dereference
    a label pointer.

    In my syntax, it can do this:

    goto (c | L1, L2, L3 | Lx)

    It's sweeter /because/ label names live in the normal name space. I'm
    sure A68 can do this too, but I couldn't get it to work.

    The point is, other people do think is has a benefit! At least in gnu C
    and A68G even if you dismiss my own use of it.



    Labels are no first-class object in "C", and there's no necessity
    to be (IMO).


    C does not have any kind of goto statement with an expression.ÿ This
    is good and appropriate.ÿ "goto" is an unstructured concept, and
    should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer languages, though. I'd suppose it's primarily a remnant of history.)

    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch',
    because it uses multiple dispatch points (which help CPU branch
    predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 11:41:11 2026
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?

    Actually you can do pretty much this with gnu C:

    goto *(c ? &&L1 : &&L2);

    but it still doesn't allow a general expression. It seems that what
    comes after 'goto' must be either a regular label, or '*' to dereference
    a label pointer.

    In my syntax, it can do this:

    goto (c | L1, L2, L3 | Lx)

    It's sweeter /because/ label names live in the normal name space. I'm
    sure A68 can do this too, but I couldn't get it to work.

    The point is, other people do think is has a benefit! At least in gnu C
    and A68G even if you dismiss my own use of it.



    Labels are no first-class object in "C", and there's no necessity
    to be (IMO).


    C does not have any kind of goto statement with an expression.ÿ This
    is good and appropriate.ÿ "goto" is an unstructured concept, and
    should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer languages, though. I'd suppose it's primarily a remnant of history.)

    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch',
    because it uses multiple dispatch points (which help CPU branch
    predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 11:45:34 2026
    [Possible duplicate post]

    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

    goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?
    You're asking why '?:' exists when you can use 'if-else'?

    Actually you can do pretty much this with gnu C:

    goto *(c ? &&L1 : &&L2);

    but it still doesn't allow a general expression. It seems that what
    comes after 'goto' must be either a regular label, or '*' to dereference
    a label pointer.

    In my syntax, it can do this:

    goto (c | L1, L2, L3 | Lx)

    It's sweeter /because/ label names live in the normal name space. I'm
    sure A68 can do this too, but I couldn't get it to work.

    The point is, other people do think is has a benefit! At least in gnu C
    and A68G even if you dismiss my own use of it.



    Labels are no first-class object in "C", and there's no necessity
    to be (IMO).


    C does not have any kind of goto statement with an expression. This
    is good and appropriate. "goto" is an unstructured concept, and
    should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer languages, though. I'd suppose it's primarily a remnant of history.)
    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch',
    because it uses multiple dispatch points (which help CPU branch
    predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)




    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Fri May 22 13:31:05 2026
    On 22/05/2026 12:41, Bart wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?


    I don't believe that is at all what he was asking. As I see it, he was
    asking what benefit you have from writing :

    goto cond ? L1 : L2;

    compared to :

    if (cond) {
    goto L1;
    } else {
    goto L2;
    }

    Given that - except for niche applications - goto's are rarely used in C
    and then almost always with a single labele ("goto error", or escaping
    from nested loops), it seems absurd to me to complicate gotos and labels
    by supporting expressions. C is mostly a structured programming
    language - unstructured constructs like "goto" should be minimal, not encouraged.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Fri May 22 12:13:19 2026
    In article <10upbvq$1dhq4$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example:

    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?

    Is that meant to be obtuse? No, the question is why anyone
    would want to write code that uses `goto` in that fashion.

    The ternary operator, `?:`, is expression-oriented. Label names
    are just identifiers; identifiers may appear in expressions
    (specifically, "primary expressions"), but only if they name an
    object, or a function.

    A label name can only appear as part of a labeled statement;
    thus, it names neither an object nor a function, so it cannot be
    used as part of a primary expression, and so therefore, one
    cannot use label names in the form mentioned above.

    You seem to be saying, "look, you can't do this!" But the
    question is, _why would you want to do that in the first place_?

    You further seem to be, once again, using this as some sort of
    subjective argument for the inferiority of C. Ok, we get it:
    you don't like C. But, yet again, for the $n$th time, not being
    able to write "weird" code like this is not a good argument in
    support of your opinion.

    Actually you can do pretty much this with gnu C:

    goto *(c ? &&L1 : &&L2);

    but it still doesn't allow a general expression. It seems that what
    comes after 'goto' must be either a regular label, or '*' to dereference
    a label pointer.

    In my syntax, it can do this:

    goto (c | L1, L2, L3 | Lx)

    It's sweeter /because/ label names live in the normal name space. I'm
    sure A68 can do this too, but I couldn't get it to work.

    The point is, other people do think is has a benefit! At least in gnu C
    and A68G even if you dismiss my own use of it.

    My subjective opinion is that that is not sweet: it's hideous.

    Obviously someone thinks, or thought, that this had some kind of
    benefit, otherwise they wouldn't have implemented it. It seems
    extraordinarily rarely used.

    Sometimes people have an idea of what they think is going to be
    a neat feature, only to find out with some experience that it
    didn't pan out the way they thought it might. This happens all
    the time; language designers are not perfect. I strongly
    suspect that this feature falls into that category.

    C does not have any kind of goto statement with an expression.ÿ This
    is good and appropriate.ÿ "goto" is an unstructured concept, and
    should be used sparingly in well-written code.

    Well, for someone used to 'goto' the existence of a calculated 'goto'
    might be considered worthwhile. (Many older languages do support
    such a feature. But I have no overview about its prevalence in newer
    languages, though. I'd suppose it's primarily a remnant of history.)

    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch', >because it uses multiple dispatch points (which help CPU branch >predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised >/standard/ C that uses plain switch.)

    Absent a benchmark, I find that assertion specious. Speculation
    about performance on modern machines is just that; speculation.
    But machines these days are so incredibly complicated, and so
    dynamic during execution, that we simply cannot reason about
    them from first principles anymore: you either produce data for
    a particular target machine, or you're just guessing.

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 13:42:34 2026
    On 22/05/2026 12:31, David Brown wrote:
    On 22/05/2026 12:41, Bart wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example: >>>>>
    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?


    I don't believe that is at all what he was asking.ÿ As I see it, he was asking what benefit you have from writing :

    ÿÿÿÿgoto cond ? L1 : L2;

    compared to :

    ÿÿÿÿif (cond) {
    ÿÿÿÿÿÿÿ goto L1;
    ÿÿÿÿ} else {
    ÿÿÿÿÿÿÿ goto L2;
    ÿÿÿÿ}

    This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter, then
    the same applies to the goto.


    Given that - except for niche applications - goto's are rarely used in C

    You seem to be supporting some kinds of /very/ small niches like those
    enabled by label name spaces.

    But I bet 'goto' in C is a lot more common!


    and then almost always with a single labele ("goto error", or escaping
    from nested loops), it seems absurd to me to complicate gotos and labels
    by supporting expressions.

    And yet, in the gnu extension, exactly this is enabled, by allowing first-class labels (sort of) and allowing them within expressions.


    ÿ C is mostly a structured programming
    language - unstructured constructs like "goto" should be minimal, not encouraged.

    But when you do need to conditionally jump to L1 or L2, then ?: lets you
    do that with only one 'goto', not two. (I can do it with zero: (cond |
    L1 | L2); the 'goto' can be implied.)

    We all know you don't use it yourself, but I can list half a dozen
    use-cases for 'goto'. Here are two:

    (1) For generated C code when transpiling from a higher level language. Without 'goto' this would be near-impossible. (Not impossible, but hard
    enough that it is easier to use another language.)

    (2) For porting code to C that already uses 'goto'.





    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Fri May 22 15:11:42 2026
    On 22/05/2026 14:42, Bart wrote:
    On 22/05/2026 12:31, David Brown wrote:
    On 22/05/2026 12:41, Bart wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example: >>>>>>
    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?


    I don't believe that is at all what he was asking.ÿ As I see it, he
    was asking what benefit you have from writing :

    ÿÿÿÿÿgoto cond ? L1 : L2;

    compared to :

    ÿÿÿÿÿif (cond) {
    ÿÿÿÿÿÿÿÿ goto L1;
    ÿÿÿÿÿ} else {
    ÿÿÿÿÿÿÿÿ goto L2;
    ÿÿÿÿÿ}

    This is the same difference between:

    ÿÿÿ a = cond ? x : y;

    and:

    ÿÿÿ if (cond) {
    ÿÿÿÿÿÿÿ a = x;
    ÿÿÿ } else {
    ÿÿÿÿÿÿÿ a = y;
    ÿÿÿ }


    No, it is not.

    In expressions, the tertiary operator can be useful for giving neater
    and more compact code. It is also important in situations where you
    want to be working with a single expression - such as for variable initialisation. The second form can be clearer precisely because it is
    more verbose - it gives space to put comments, or perhaps breakpoints
    when debugging, or when you want to get more accurate indications of
    changes when doing line-by-line comparisons of code versions. The
    second form is also better if the sub-expressions "x" and "y" are more complex, or it is important to be visually and immediately clear which expressions are being evaluated, or if you have more than two branches
    or the possibility of never assigning to "a".

    So while you can sometimes use either form, often there are very
    distinct advantages in picking a particular form for such conditional assignments.

    With goto, there is never a particular benefit. Who cares if you save
    three lines of source code on a construct that occurs once in a million
    lines? I'd rather have it stand out, not be compact and terse.

    If ?: is considered a convenient, clearer short-cut for the latter, then
    the same applies to the goto.


    Given that - except for niche applications - goto's are rarely used in C

    You seem to be supporting some kinds of /very/ small niches like those enabled by label name spaces.

    I do not "seem" to be supporting anything of the kind. As usual, you
    are completely wrong about what I think.


    But I bet 'goto' in C is a lot more common!

    I bet it is. But I bet the desire to write "goto cond ? L1 : L2;" is
    very, very small.



    and then almost always with a single labele ("goto error", or escaping
    from nested loops), it seems absurd to me to complicate gotos and
    labels by supporting expressions.

    And yet, in the gnu extension, exactly this is enabled, by allowing first-class labels (sort of) and allowing them within expressions.


    All that demonstrates is that someone working on gcc development, long,
    long ago (in a galaxy far, far away) thought it might be useful. The
    early days of gcc development were full of wild ideas about extensions,
    often added with little thought or planning, and it is generally very difficult to remove them later (though it has been done in some cases).
    The fact that gcc has a particular extension does not imply that it is
    widely used (though I know this one /is/ used in a few programs), nor
    that it is popular or well liked (even amongst those that use it), or
    that it is suitable for adding to the C language.


    ÿ C is mostly a structured programming language - unstructured
    constructs like "goto" should be minimal, not encouraged.

    But when you do need to conditionally jump to L1 or L2, then ?: lets you
    do that with only one 'goto', not two. (I can do it with zero: (cond |
    L1 | L2); the 'goto' can be implied.)

    Let me try again, slowly.

    Almost no C code has any use of "goto" or labels. I'd guess that
    rounded up to the nearest 0.1%, 0.1% of functions in C use "goto" or labels.

    Of the C code that uses "goto", almost none of it needs anything more
    complex than "goto error" or "goto break_from_all_loops". When (if) the upcoming "defer" and "break label" features are added to the language,
    those use-cases will disappear. (Of course old code will not change,
    and it will take time for people to use new C2y features once they are supported.)

    So we are at perhaps 0.1% of 0.1% of functions that have benefits of
    anything more complex than a simple "goto label".

    And when "goto label" is not enough, it is common to want significantly
    more complicated setups - such as using the gcc extensions - for things
    like virtual machine byte-code interpreters. There, something akin to
    "goto cond ? L1 : L2;" would be useless.

    Let the three people in the world who want to write "goto cond ? L1 :
    L2;" use the "if" construction - it is not worth changing the language
    and tools to save them a few centimetres of screen space.



    We all know you don't use it yourself, but I can list half a dozen use- cases for 'goto'. Here are two:

    (1) For generated C code when transpiling from a higher level language. Without 'goto' this would be near-impossible. (Not impossible, but hard enough that it is easier to use another language.)

    (2) For porting code to C that already uses 'goto'.






    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 14:16:11 2026
    On 22/05/2026 13:13, Dan Cross wrote:
    In article <10upbvq$1dhq4$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example: >>>>>
    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?

    Is that meant to be obtuse? No, the question is why anyone
    would want to write code that uses `goto` in that fashion.

    The ternary operator, `?:`, is expression-oriented. Label names
    are just identifiers; identifiers may appear in expressions
    (specifically, "primary expressions"), but only if they name an
    object, or a function.

    And as I said, gnu C allows just that.

    You're anyone asking the wrong question. It's usually a mistake to try
    and double-guess the needs of a programmer, since it's possible they may
    have a genuine use-case you haven't thought of. Or they have a
    particular coding style they want to use.


    A label name can only appear as part of a labeled statement;
    thus, it names neither an object nor a function, so it cannot be
    used as part of a primary expression, and so therefore, one
    cannot use label names in the form mentioned above.

    You seem to be saying, "look, you can't do this!" But the
    question is, _why would you want to do that in the first place_?

    So what on earth was the point of gnu C's &&L label pointers?

    Other than enabling faster interpreters such as with CPython (which
    needs all the help it can get). Writing FSMs and so on...

    You seem to downplaying the benefits only because standard C doesn't
    allow it.

    My subjective opinion is that that is not sweet: it's hideous.

    OK. Of course we'd have to see the alternative.


    Obviously someone thinks, or thought, that this had some kind of
    benefit, otherwise they wouldn't have implemented it. It seems extraordinarily rarely used.

    Sometimes people have an idea of what they think is going to be
    a neat feature, only to find out with some experience that it
    didn't pan out the way they thought it might. This happens all
    the time;

    It happens with me too! You should see some of my ideas that were too
    naff. There were also better ones but that just didn't get enough usage
    to warrant the support needed.

    But I believe 'goto' is fundamental at this level of language, and it is
    handy to allow first-class handling of labels even if little used: it's something that if it isn't there when needed, it is harder to get around.

    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch',
    because it uses multiple dispatch points (which help CPU branch
    predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)

    Absent a benchmark, I find that assertion specious. Speculation
    about performance on modern machines is just that; speculation.
    But machines these days are so incredibly complicated, and so
    dynamic during execution, that we simply cannot reason about
    them from first principles anymore: you either produce data for
    a particular target machine, or you're just guessing.

    OK, here is a test program which is a toy Pascal interpreter, running recursive Fibonacci to calculate fib(36).

    It was in C, then ported to my language. Someone also upgraded the C
    version to use computed goto when available:

    gcc -O2 using switch: 1.33 seconds (C version)
    gcc -O2 using computed goto: 0.78 seconds

    mm using 'doswitch': 1.03 seconds (my languages)
    mm using 'doswitchu': 0.74 seconds (with special feature)

    Timings are on Windows running on x64.

    In my language, 'doswitch' is just a looping version of switch.
    'doswitchu' is a version where the compiler uses multiple dispatch
    points, one per branch, rather than looping back to the start.

    Switching between the two means changing 'doswitch' to 'doswitchu'.

    In C switching would require a rewrite, unless you write it in a
    contrived manner. But then it still needs that bunch of macros shown
    below, and it needs that table of label pointers to be manually maintained.


    ----------------------------------------------------
    #if !defined(__GNUC__) || defined(FORCE_SWITCH)
    #define START start: switch (*pc++)
    #define CASE case
    #define NEXT goto start
    #define DEFAULT default
    puts("switch");
    #else

    /*************************************************************************
    Dependency: the ording of this table depends on the Code_t enum.

    *************************************************************************/
    static void *label[] = {
    &&add, &&sub, &&neg, &&mul, &&divd, &&remd, &&div2, &&rem2,
    &&eqli, &&neqi,
    &&lssi, &&leqi, &&gtri, &&geqi, &&dupl, &&swap, &&andb, &&orb, &&load, &&stor,
    &&halt, &&wri, &&wrc, &&wrl, &&rdi, &&rdc, &&rdl, &&eofi,
    &&eol, &&ldc, &&ldla,
    &&ldl, &&ldg, &&stl, &&stg, &&move, &&copy, &&addc, &&subc,
    &&mulc, &&jump,
    &&jumpz, &&call, &&adjs, &&sets, &&ret, &&DEFAULT
    };

    assert(label[ret] == &&ret);
    assert(label[ret+1] == &&DEFAULT);

    #define START goto *label[*pc++];
    #define CASE
    #define NEXT goto *label[*pc++]
    #define DEFAULT DEFAULT
    puts("goto");
    #endif

    START {
    CASE add : s[sp+1] += s[sp]; sp++; pc++; NEXT;
    CASE sub : s[sp+1] -= s[sp]; sp++; pc++; NEXT;
    CASE neg : s[sp]= -s[sp]; pc++; NEXT;
    CASE mul : s[sp+1] *= s[sp]; sp++; pc++; NEXT;
    ....








    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Fri May 22 14:04:07 2026
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:
    Bart <bc@freeuk.com> writes:

    Allowing labels as expressions has advantages; I gave some
    examples. Being able to use parentheses is a consequence, but not
    useful in itself. But the special namespace puts paid to that.

    In C, label names are not expressions. It's that simple. C does
    not allow parentheses around a label name, and there is no reason
    at all why it should do so. (If it did, you'd probably complain
    that it's inconsistent.)

    You don't understand my point, so I will leave it.

    You don't seem to have ever made a "point", you just complain
    about things you either don't like or don't understand.


    Sure. FYI, here's the proposal that, as far as I can tell, led to C23
    allowing a label immediately before a "}". It was written by Martin
    Uecker in 2020.

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers >encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Fri May 22 14:44:55 2026
    In article <10un0j7$obiv$2@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 21/05/2026 14:18, Dan Cross wrote:
    In article <10umdsa$hnml$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    I think one can level some reasonable criticism at the language,
    however. The `goto error;` idiom is used in C because there are
    few alternatives for cleanup handling on failure. Modulo what
    we discussed before, that code can _often_ be restructured to
    avoid it, but sometimes it can't, and frequently it just isn't.

    I think "isn't" is more common than "can't".

    Yes, that's the distinction I tried to draw.

    But I also don't think the
    "goto error" idiom is necessarily bad in itself - it's just that it is
    often used badly. A typical indication of poor usage is when a function
    is getting very long, and there are multiple "error" labels.

    However, the problem with this code was not the "goto error" idiom, or
    the "goto" itself - the problem was the mismatch between indentation and
    the statement under control of the "if". It would have been equally bad
    if it had been "return" rather than "goto", or if gcc cleanup attributes
    had been used to handle cleanup, or if some kind of "defer" mechanism
    had been used (as supported by some programming languages, and proposed
    for a future C version).

    These various cleanup mechanisms can definitely be better than "goto
    error", but they would not have prevented this error. (A programming >language that requires braces for statements controlled by "if", on the >other hand, /would/ have prevented the error.)

    Yes, the immediate cause of the error was the repeated `goto`;
    whether the misleading indentation was observed by the
    programmer or not I couldn't say, however. I have no way of
    knowing, but my sense at the time was that this was an example
    of copy-paste gone wrong. Wheeler suggested it might be due to
    removal as well; that's plausable.

    I do agree that a language where the grammar forced braces (or
    the equivalent) around conditionals would have prevented this,
    or at least made it much more obvious. Notably, both Rust and
    Go do that. Both also have automated formatters that are used
    as a matter of course, which would have made the error easier to
    spot; compare to the myriad different individual styles of C and
    C++, and paucity of effective formatters.

    Another method, one that I don't particularly care for, would
    have been to nest the conditionals on success; this gets ugly
    because code tends to move towards the right, and deeply nested
    control structures can be terribly confusing.

    However, Ken Thompson used to commit this grave sin, which sort
    of ameliorates the problem:

    if ((err = thing()) == SUCCESS)
    if ((err = other()) == SUCCESS)
    if ((err = third()) == SUCCESS)
    dosomething();
    return err;

    One could, of course, string those conditional together into a
    single `if` using `&&`, but Ken thought this was cleaner.

    [snip]
    Other languages feature "linear types", instances of which must
    be used exactly once: failure to cleanup on an error path is a
    compile time error. This means that you can't forget to
    deallocate memory, close a file, or unlock a mutex, for example,
    but I don't know that it directly addresses the issue where you
    just skip over the actual thing the program is supposed to do.
    Still, with more expressive type systems, it is likely one can
    much more easily structure the program so that the type of logic
    that lead to this failure is unnecessary.

    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    Interesting. I've found it to be somewhat the opposite; using a
    richer type system has lead to code that is easier to understand
    and reason about, and less buggy: type-oriented programming can
    make entire categories of errors *unrepresentable*, so it's not
    just _harder_ to make certain kinds of mistakes, but
    _impossible_. The existance of an object of some type can be
    thought of as an existence proof that the invariants the type
    represents hold.

    And Alexis King wrote the great, "Parse, Don't Validate" essay
    some years ago where she talks about "Type-Driven Development": https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

    More recently, Yaron Minsky gave a talk and discussed this in an
    OCaml context (https://www.youtube.com/watch?v=rUYP4C29yCw; the
    most relevant bits start at about 6:35. The talk is about AI
    and constraining agents, but the discussion around types is more
    general).

    I wrote the production OS loader for Oxide compute sleds using
    this technique in the virtual memory system (and other places):
    the loader uses multiple page sizes, and the rule is that, when
    mapping a region of memory, it uses the largest page size that
    it can, given size and alignment constraints. But I use the
    type system to make it impossible to, say, map a 2MiB "large"
    physical page frame to a non-2MiB aligned virtual boundary.

    I did some work, eons ago, in a language called XC that was specifically
    for XMOS microcontrollers. The language and tools had a feature that
    made data races impossible by not allowing competing access to shared >variables from different threads. Since threads were part of the
    hardware, and the tools analysed the code flow through threads, this
    could all be enforced at build time - data had to be passed in messages,
    not shared memory. But for some things that involved large buffers,
    that was hopelessly inefficient - and these devices were regularly used
    with USB, audio, and similar things that needed large and predictable >buffers. So code - even library and example code from the manufacturer
    - was full of inline assembly to work around the "smart-arse" language
    and tools.

    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    I'm going to mention Rust again; apologies. When confined to
    the safe subset, it _also_ has data race freedom. The rules
    that give this property are:

    1. Every object has exactly one owner, and assignments of
    non-trivial types change ownership (they are logically a
    "move");
    2. References to an object may be "borrowed" from the owner,
    and mutable references (that is, references that may be used
    to write to the object) are distinct from immutable
    references (that is, references that may only be used to read
    from an object);
    3. Mutable and immutable references are temporally mutually
    exclusive: a mutable reference may be borrowed from an object
    iff that is the only live reference to that object at the
    time: that is, it is not permitted to borrow a mut ref to an
    object if either another mut ref or any immutable refs to it
    are live; any number of immutable references may be taken to
    an object concurrently.

    If all of these rules are obeyed (and in safe rust, they're
    verified at compile time by the borrow checker) then you cannot
    have data races.

    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    That last bit is important: since the `Mutex` owns whatever it
    protects, it controls access to that thing: there's no going
    behind the `Mutex`'s back and accessing something outside of
    the lock. Instead, the `lock` method returns a `MutexGuard`
    object, which one might think of as a handle to the data,
    allowing the user to manipulate it, but also having a drop
    method (in Rust, that's basically a destructor) that
    automatically unlocks the mutex run the guard is destroyed.

    Anyway, the point is, both the rigor _and_ the expressiveness of
    the type system let you do things like this, whereas you just
    cannot in C.

    I understand that someone has written a paper proposing adding
    `defer` to C; that would obviate many of these problems.

    Yes. Jens Gustedt - one of the few members of the C standards committee
    who is vocal and public about pushing new ideas into C. (Of course not >everyone will agree with the ideas he comes with.)

    <https://gustedt.wordpress.com/2026/02/15/defer-available-in-gcc-and-clang/>

    Ah, I didn't realize it was Jens. Very cool.

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Fri May 22 14:57:09 2026
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    then the same applies to the goto.

    You have made it clear that you think that _should_ be true.
    However, it is _not_ true. A brief perusal of the standard
    would show _how_ it is not true. You may ask _why_ it is not
    true, but I don't know that anyone here can answer that
    definitively. Your argument that C is somehow bad because of
    _this_ does not follow.

    [snip]
    ÿ C is mostly a structured programming
    language - unstructured constructs like "goto" should be minimal, not
    encouraged.

    But when you do need to conditionally jump to L1 or L2, then ?: lets you
    do that with only one 'goto', not two.

    Actually it doesn't let you do that, because it's not allowed by
    the language. ;-}

    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    We all know you don't use it yourself, but I can list half a dozen
    use-cases for 'goto'. Here are two:

    (1) For generated C code when transpiling from a higher level language. >Without 'goto' this would be near-impossible. (Not impossible, but hard >enough that it is easier to use another language.)

    (2) For porting code to C that already uses 'goto'.

    Strawman. No one said `goto` in C should be disallowed.

    The above quote said it should be _discouraged_ and its use
    _minimal_. That doesn't mean it's not useful in some cases.

    However, that's not support for standardizing the use of labels
    you are advocating for so passionately.

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Fri May 22 17:12:55 2026
    On 22/05/2026 16:44, Dan Cross wrote:
    In article <10un0j7$obiv$2@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 21/05/2026 14:18, Dan Cross wrote:
    In article <10umdsa$hnml$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:

    [snip]
    Other languages feature "linear types", instances of which must
    be used exactly once: failure to cleanup on an error path is a
    compile time error. This means that you can't forget to
    deallocate memory, close a file, or unlock a mutex, for example,
    but I don't know that it directly addresses the issue where you
    just skip over the actual thing the program is supposed to do.
    Still, with more expressive type systems, it is likely one can
    much more easily structure the program so that the type of logic
    that lead to this failure is unnecessary.

    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    Interesting. I've found it to be somewhat the opposite; using a
    richer type system has lead to code that is easier to understand
    and reason about, and less buggy: type-oriented programming can
    make entire categories of errors *unrepresentable*, so it's not
    just _harder_ to make certain kinds of mistakes, but
    _impossible_. The existance of an object of some type can be
    thought of as an existence proof that the invariants the type
    represents hold.

    In general, I agree - I prefer strongly typed languages, and it's always
    best if an error is identified by the code being uncompilable rather
    than waiting for run-time testing (or testing by evil hackers). But if
    people want to do something with the language that is hard to achieve efficiently within the norms of the language, and there are escape
    hatches ("unsafe" code, inline assembly, calling external C functions,
    etc.) then people will use them. People do sometimes write C code that knowingly depends on undefined behaviour, because it makes their results
    more efficient and "it worked when I tested it".

    Type safety - like any kind of safety - can sometimes get in the way of "getting things done". A balance always needs to be found to ensure
    that people can do what they need to do without breaking rules or using "escapes" more than absolutely necessary. If people find that much of
    their Rust code is "unsafe", then most of the point of using Rust is
    lost. (Of course this also means that different languages are suited to different tasks.)


    And Alexis King wrote the great, "Parse, Don't Validate" essay
    some years ago where she talks about "Type-Driven Development": https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

    More recently, Yaron Minsky gave a talk and discussed this in an
    OCaml context (https://www.youtube.com/watch?v=rUYP4C29yCw; the
    most relevant bits start at about 6:35. The talk is about AI
    and constraining agents, but the discussion around types is more
    general).

    I wrote the production OS loader for Oxide compute sleds using
    this technique in the virtual memory system (and other places):
    the loader uses multiple page sizes, and the rule is that, when
    mapping a region of memory, it uses the largest page size that
    it can, given size and alignment constraints. But I use the
    type system to make it impossible to, say, map a 2MiB "large"
    physical page frame to a non-2MiB aligned virtual boundary.

    I did some work, eons ago, in a language called XC that was specifically
    for XMOS microcontrollers. The language and tools had a feature that
    made data races impossible by not allowing competing access to shared
    variables from different threads. Since threads were part of the
    hardware, and the tools analysed the code flow through threads, this
    could all be enforced at build time - data had to be passed in messages,
    not shared memory. But for some things that involved large buffers,
    that was hopelessly inefficient - and these devices were regularly used
    with USB, audio, and similar things that needed large and predictable
    buffers. So code - even library and example code from the manufacturer
    - was full of inline assembly to work around the "smart-arse" language
    and tools.

    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an indication that the restrictions inherent in the language and tools made
    them somewhat unsuitable for a lot of use-cases that fitted the hardware
    well. (Modern XMOS tools have changed significantly since those days,
    perhaps partly because of such issues.)


    I'm going to mention Rust again; apologies. When confined to
    the safe subset, it _also_ has data race freedom. The rules
    that give this property are:

    1. Every object has exactly one owner, and assignments of
    non-trivial types change ownership (they are logically a
    "move");
    2. References to an object may be "borrowed" from the owner,
    and mutable references (that is, references that may be used
    to write to the object) are distinct from immutable
    references (that is, references that may only be used to read
    from an object);
    3. Mutable and immutable references are temporally mutually
    exclusive: a mutable reference may be borrowed from an object
    iff that is the only live reference to that object at the
    time: that is, it is not permitted to borrow a mut ref to an
    object if either another mut ref or any immutable refs to it
    are live; any number of immutable references may be taken to
    an object concurrently.

    If all of these rules are obeyed (and in safe rust, they're
    verified at compile time by the borrow checker) then you cannot
    have data races.

    That's all good, by the sound of it. But if people find this gets in
    the way of efficiency - perhaps because they know that it is safe to
    hold two separate mutable references to an object for reasons the borrow checker can't see - the temptation to use "unsafe" or other workarounds
    comes quickly.

    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that
    as much code as possible is correct, or at least immune to particular
    classes of bugs, checked by the compiler and tools.


    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.


    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    That last bit is important: since the `Mutex` owns whatever it
    protects, it controls access to that thing: there's no going
    behind the `Mutex`'s back and accessing something outside of
    the lock. Instead, the `lock` method returns a `MutexGuard`
    object, which one might think of as a handle to the data,
    allowing the user to manipulate it, but also having a drop
    method (in Rust, that's basically a destructor) that
    automatically unlocks the mutex run the guard is destroyed.

    Anyway, the point is, both the rigor _and_ the expressiveness of
    the type system let you do things like this, whereas you just
    cannot in C.


    Sure.

    I understand that someone has written a paper proposing adding
    `defer` to C; that would obviate many of these problems.

    Yes. Jens Gustedt - one of the few members of the C standards committee
    who is vocal and public about pushing new ideas into C. (Of course not
    everyone will agree with the ideas he comes with.)

    <https://gustedt.wordpress.com/2026/02/15/defer-available-in-gcc-and-clang/>

    Ah, I didn't realize it was Jens. Very cool.

    - Dan C.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 16:16:18 2026
    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers
    encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'




    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Fri May 22 17:47:39 2026
    On 22/05/2026 17:16, Bart wrote:
    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers
    encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    If a programmer wants a case label or default label at the end of a
    switch, doing nothing, then (prior to C23, and excluding compiler
    extensions) they have to put in an empty statement - "default : ;". (Or
    they could use "break;" to make code feel more symmetrical.) I can
    imagine that happening, and I can imagine that is the most likely
    situation where you would want a label right before an end brace. This
    seems to be the main motivation for the change in C23.

    It is much harder to imagine that this "bites" anyone, or even annoys
    people in any significant way.


    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'

    I think you might need to switch out your crystal ball - your
    predictions about what people will say or do have rarely come close to reality. Or, perhaps, you should stop saying such stupid things.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Fri May 22 16:16:55 2026
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c", is_feature1()?'+':'-',
    is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 17:35:06 2026
    On 22/05/2026 16:47, David Brown wrote:
    On 22/05/2026 17:16, Bart wrote:
    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers >>>> encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    If a programmer wants a case label or default label at the end of a
    switch, doing nothing, then (prior to C23, and excluding compiler extensions) they have to put in an empty statement - "default : ;".ÿ (Or they could use "break;" to make code feel more symmetrical.)ÿ I can
    imagine that happening, and I can imagine that is the most likely
    situation where you would want a label right before an end brace.ÿ This seems to be the main motivation for the change in C23.

    It is much harder to imagine that this "bites" anyone, or even annoys
    people in any significant way.

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.

    There was no reason whatsoever to ban:

    L:}
    L:int x;

    other than the grammar happening to define labels in a certain way. That
    there are workarounds is not the point.


    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'

    I think you might need to switch out your crystal ball - your
    predictions about what people will say or do have rarely come close to reality.ÿ Or, perhaps, you should stop saying such stupid things.

    Scott Lurndal said something stupid and I responded with sarcasm. Yet
    you attack me and not him. Biased at all?

    This is what I responded to in his post:

    * An assertion that the 'vast majority' of C programmers have never used 'goto'.

    * The assumption that the labels issue was only about goto labels

    Is that assertion true? I don't know, but it sounds unlikely more than
    likely. If I look at some codebases:

    gotos used?

    Lua Yes (300 in 30Kloc)
    SQLite Yes (Over 600 in 220Kloc of comment-heavy code)
    Tiny C Yes (40 in 30Kloc
    LibJPEG Yes (About 80 in 60Kloc)
    CPython Yes (over 1000 just in Objects folder, about 100Kloc)
    A68G Yes (a handful, but they exist, in 70Kloc)
    GMP Yes (50 in mpn/generic, 36Kloc)

    I struggled to find any sizeable project without it.

    So if you had to put money on it would you say Scott was right? What
    does 'vast majority' mean anyway?

    (Of course, Scott said programmers rather than applications. Maybe 1000
    people work on CPython, but there's only one who writes all the gotos!)

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 17:53:06 2026
    On 22/05/2026 15:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wonder if there is any feature of C that is not used sparingly by the regulars here? So far:

    * Hardly anyone uses 'goto' or labels

    * Few use ?:

    * Nobody takes advantage of labels' private namespace (but at the same
    time, it is definitely not pointless!)

    * Very few ever refactor source files such as by splitting or combining

    * Nobody here has ever used label pointers. (An extension, yes, but then
    I guess nobody has ever used an extension either?)

    * David Brown at least rarely uses forward function declarations

    I have a sneaking suspicion that people are deliberately playing such
    things down (or up, for label namespaces); I wonder why?


    then the same applies to the goto.

    You have made it clear that you think that _should_ be true.
    However, it is _not_ true. A brief perusal of the standard
    would show _how_ it is not true.

    You mean whether C allows 'goto expr'? Yes I know it is not allowed. I responded to someone saying it wouldn't be useful if it was.

    (Part of a broader point about what is missed with labels having a
    private namespace and only appearing in certain contexts.)


    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    Blame Algol68, that's where implied goto comes from.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Fri May 22 17:32:08 2026
    In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 13:13, Dan Cross wrote:
    In article <10upbvq$1dhq4$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 05:52, Janis Papanagnou wrote:
    On 2026-05-21 21:23, David Brown wrote:
    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:
    [...]
    Having a label be just another term could be a benefit. For example: >>>>>>
    ÿÿ goto cond ? L1 : L2;

    What benefit is it to have an alternative construct for existing
    clearer 'if' and 'switch' features? - What's the concrete gain? -
    To create harder to follow spaghetti-code?

    You're asking why '?:' exists when you can use 'if-else'?

    Is that meant to be obtuse? No, the question is why anyone
    would want to write code that uses `goto` in that fashion.

    The ternary operator, `?:`, is expression-oriented. Label names
    are just identifiers; identifiers may appear in expressions
    (specifically, "primary expressions"), but only if they name an
    object, or a function.

    And as I said, gnu C allows just that.

    Actually no, it doesn't; syntactically it uses a very different
    form (one that appears to conflict with rvalue references in
    C++, FWIW).
    https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

    But by itself, "GNU C does it!" is meaningless. Ok, GNU C
    allows one to write expression treat labels as values. So what?
    Is it widely used? It doesn't appear to be.

    You're anyone asking the wrong question. It's usually a mistake to try
    and double-guess the needs of a programmer, since it's possible they may >have a genuine use-case you haven't thought of. Or they have a
    particular coding style they want to use.

    Congratulations: that's the central tension faced by language
    designers. Let me give a counter example: in the Plan 9 C
    dialect, there's an extension where, if a type referring to a
    struct is named in an enclosing struct, then the elements of
    that struct are available in the enclosing struct.

    For example:

    typedef struct Lock Lock;
    struct Lock {
    int lock;
    Thread *owner;
    // Whatever else goes into a lock...
    };

    int lock(Lock *lock);
    void unlock(Lock *lock);

    typedef struct Ref Ref;
    struct Ref {
    Lock;
    int nref;
    };

    void incref(Ref *ref);
    int decref(Ref *ref);

    Using this, one can pass a pointer to a `Ref` to a function that
    takes a lock. For example, image if `decref` were defined to
    return the previous value of the ref count, it might be written
    as such:

    int
    decref(Ref *ref)
    {
    int nref;

    if (ref == nil)
    panic("decref got nil ref");

    lock(ref);
    nref = ref->nref;
    --ref->nref;
    unlock(ref);

    if (nref <= 0)
    panic("decref got corrupt ref);

    return nref;
    }

    Note that Plan 9 C spells `NULL` as `nil`, and note how we just
    passed the `Ref*` to `lock()` and `unlock()`. Kinda cool, huh?

    Well no. It actually hides a really subtle problem. Consider
    this struct:

    // A Chan (short for "channel") is sort of like a queue;
    // lots of Plan 9's internals were inspired for Hoare's
    // CSP.
    typedef struct Chan Chan;
    struct Chan {
    Ref;
    Lock;

    // ...The things that go into a channel.
    };

    Here, there's a problem; which "Lock" do we get? Actually, we
    don't get to choose. The compiler combines them for us.

    Why is this a problem? Because the two locks are doing
    semantically different things: the `Lock` in the `Ref` is
    protecting updates to the reference counter, which in turn
    concerns the lifetime of the `Chan`. The `Lock` embedded
    directly into the `Chan`, on the other hand, is meant to
    serialize access to the `Chan`'s non-`Ref` data members.

    By combining them, we are now conflating the the two, but
    moreover creating contention where it was very likely not meant
    to be.

    And in fact, it was found that this _was_ a problem. Consider
    a program that is forking repeatedly, but also using a channel;
    the `Ref` on the channel is incremented every time the process
    `rfork`'s (Plan 9 uses `rfork`; `fork` is just a library
    function that calls `rfork` with some default flags), but that
    de facto locks the `Chan`, which contends with a proess that's
    actually trying to _use_ the `Chan`.

    In retrospect, it was determined that that language feature was
    a mistake.

    A label name can only appear as part of a labeled statement;
    thus, it names neither an object nor a function, so it cannot be
    used as part of a primary expression, and so therefore, one
    cannot use label names in the form mentioned above.

    You seem to be saying, "look, you can't do this!" But the
    question is, _why would you want to do that in the first place_?

    So what on earth was the point of gnu C's &&L label pointers?

    Who knows? Go find when it was added and ask the person who
    wrote it? Probably Stallman. Good luck getting an answer.

    Other than enabling faster interpreters such as with CPython (which
    needs all the help it can get). Writing FSMs and so on...

    You seem to downplaying the benefits only because standard C doesn't
    allow it.

    Not at all. It may well be useful for some applications. Does
    that justify adding it to the language, though?

    My subjective opinion is that that is not sweet: it's hideous.

    OK. Of course we'd have to see the alternative.

    The alternative is an `if` statement with a `goto`.

    Obviously someone thinks, or thought, that this had some kind of
    benefit, otherwise they wouldn't have implemented it. It seems
    extraordinarily rarely used.

    Sometimes people have an idea of what they think is going to be
    a neat feature, only to find out with some experience that it
    didn't pan out the way they thought it might. This happens all
    the time;

    It happens with me too! You should see some of my ideas that were too
    naff. There were also better ones but that just didn't get enough usage
    to warrant the support needed.

    But I believe 'goto' is fundamental at this level of language, and it is >handy to allow first-class handling of labels even if little used: it's >something that if it isn't there when needed, it is harder to get around.

    I don't know. Rust doesn't have `goto`, and it does ok at the
    same level as C: I've used it in Unix-class, multi-processor,
    multi-tasking, internally-threaded kernels; my colleagues use it
    on embedded microcontrollers; people I know use it to write
    jitted bytecode interpreters; all that kind of thing that people
    often incorrectly assume cannot be written in any language other
    than C.

    You don't have to like Rust, of course, but other languages
    exist that have similar capabilities and don't have `goto`.

    In gnu C computed goto tends to be used for faster dispatching (in
    bytecode interpreters, emulators etc) compared with regular 'switch',
    because it uses multiple dispatch points (which help CPU branch
    predication). 'switch' uses only one.

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)

    Absent a benchmark, I find that assertion specious. Speculation
    about performance on modern machines is just that; speculation.
    But machines these days are so incredibly complicated, and so
    dynamic during execution, that we simply cannot reason about
    them from first principles anymore: you either produce data for
    a particular target machine, or you're just guessing.

    OK, here is a test program which is a toy Pascal interpreter, running >recursive Fibonacci to calculate fib(36).

    It was in C, then ported to my language. Someone also upgraded the C
    version to use computed goto when available:

    gcc -O2 using switch: 1.33 seconds (C version)
    gcc -O2 using computed goto: 0.78 seconds

    mm using 'doswitch': 1.03 seconds (my languages)
    mm using 'doswitchu': 0.74 seconds (with special feature)

    Timings are on Windows running on x64.

    In my language, 'doswitch' is just a looping version of switch.
    'doswitchu' is a version where the compiler uses multiple dispatch
    points, one per branch, rather than looping back to the start.

    Where to start.

    First, why not post the code? It's hard know what is _actually_
    going on here because one cannot see how the implementations
    might differ otherwise, aside from the tiny snippet you
    included.

    Second, how was it compiled? What optimization settings? What
    compiler? Did you test different compilers to see if they did
    things differently? You've made a broad general assertion about
    the code generated in response to a `switch` statement; my own
    experience is that that varies widely based on compiler, target
    architecture, and optimization settings.

    Finally, your assertion was that the version using your multiple
    dispatch is faster due to branch predicition: you've shown that
    this is faster, but you haven't shown _why_, let alone that it
    is due to branch prediction. Since you're running this on
    x86_64, did you try to look at the machine's perf counters to
    see what's actually happening?

    Switching between the two means changing 'doswitch' to 'doswitchu'.

    In C switching would require a rewrite, unless you write it in a
    contrived manner. But then it still needs that bunch of macros shown
    below, and it needs that table of label pointers to be manually maintained.

    [snip]

    I'm not sure what you mean here. You've given examples of
    timings using computed gotos and `switch` in C; it's unclear
    what would "require a rewrite" since you appear to already have
    both versions.

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Fri May 22 17:43:34 2026
    In article <Xj%PR.313884$yHZ7.10738@fx10.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>>This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c", is_feature1()?'+':'-',
    is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);

    Those are the kinds of scenarios where it can be useful; what
    percentage of code overall uses that versus `if`, though?

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Fri May 22 20:27:12 2026
    On 22/05/2026 18:32, Dan Cross wrote:
    In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:

    [computed goto]

    (My language has a dedicated feature for this, so using explicit
    computed goto is not needed. That allows it to outperform optimised
    /standard/ C that uses plain switch.)

    Absent a benchmark, I find that assertion specious. Speculation
    about performance on modern machines is just that; speculation.
    But machines these days are so incredibly complicated, and so
    dynamic during execution, that we simply cannot reason about
    them from first principles anymore: you either produce data for
    a particular target machine, or you're just guessing.

    OK, here is a test program which is a toy Pascal interpreter, running
    recursive Fibonacci to calculate fib(36).

    It was in C, then ported to my language. Someone also upgraded the C
    version to use computed goto when available:

    gcc -O2 using switch: 1.33 seconds (C version)
    gcc -O2 using computed goto: 0.78 seconds

    mm using 'doswitch': 1.03 seconds (my languages)
    mm using 'doswitchu': 0.74 seconds (with special feature)

    Timings are on Windows running on x64.

    In my language, 'doswitch' is just a looping version of switch.
    'doswitchu' is a version where the compiler uses multiple dispatch
    points, one per branch, rather than looping back to the start.

    Where to start.

    First, why not post the code? It's hard know what is _actually_
    going on here because one cannot see how the implementations
    might differ otherwise, aside from the tiny snippet you
    included.


    See: https://github.com/sal55/langs/blob/master/tinypas.c

    About 1.5Kloc. This will use computed goto with gnu C (ie __GNUC__ is defined). Uncomment 'FORCE_SWITCH' macro definition to always use 'switch'.

    When it runs it will anyway say 'goto' or 'switch'.

    For test input, use the program below, for example when it is called 'fib.pas':

    > time ./tinypas fib.pas

    Use any optimisation setting you like; I used -O2 and -O3 since we're
    trying to get the best performance.

    On Windows I got timings of 0.78/1.33 seconds goto/switch for x64.

    I also just tried RPi4 with ARM64, and it was 2.6/5.6 seconds
    goto/switch. So it made it twice as fast.

    ------------------------------------------
    program fibonacci(output);

    var i:integer;

    function fib(n:integer): integer;
    begin
    if (n < 3) then
    fib := 1
    else
    fib := fib(n-1) + fib(n-2);
    end;


    begin
    for i := 36 to 36 do
    writeln(i, ' ', fib(i));
    writeln('...');
    end.
    ------------------------------------------


    I use the same methods for own scripting language interpreter, a project
    of 40Kloc in my language. The same task as above takes 0.5s on Windows
    (recall that gcc took 0.78 seconds), even though this language uses
    dynamic typing not static like the Pascal.

    I can port that project to Linux and/or ARM64 via C, and maintain the
    same speeds, because I can translate my language's fast dispatch methods
    into gnu C label pointers.

    Second, how was it compiled? What optimization settings? What
    compiler? Did you test different compilers to see if they did
    things differently? You've made a broad general assertion about
    the code generated in response to a `switch` statement; my own
    experience is that that varies widely based on compiler, target
    architecture, and optimization settings.

    It will depend on the task and spread of workload. If the program being
    run is spending a lot of time in libraries, then bytecode dispatch is
    less of the overall overhead.

    Finally, your assertion was that the version using your multiple
    dispatch is faster due to branch predicition: you've shown that
    this is faster, but you haven't shown _why_, let alone that it
    is due to branch prediction. Since you're running this on
    x86_64, did you try to look at the machine's perf counters to
    see what's actually happening?

    It's a well-known technique. It was used on CPython (as compiled for
    Linux, since on Windows it doesn't use gcc), and may still be. Athough
    there are some experients to move towards complex threaded code via TCO instead.

    In any case, it seems to work, so who cares why?

    Switching between the two means changing 'doswitch' to 'doswitchu'.

    In C switching would require a rewrite, unless you write it in a
    contrived manner. But then it still needs that bunch of macros shown
    below, and it needs that table of label pointers to be manually maintained. >>
    [snip]

    I'm not sure what you mean here. You've given examples of
    timings using computed gotos and `switch` in C; it's unclear
    what would "require a rewrite" since you appear to already have
    both versions.

    You know what normal switch looks like:

    while (!stopped) {
    switch (opcode) {
    case ADD:
    ...

    Computed goto would first need a jumptable:

    void* jumptable[] = {&&ADDLAB, ....};

    Then dispatch at every point:

    goto *jumptable[opcode];
    ADDLAB:
    ....
    goto *jumptable[opcode]

    It is quite different. In Tinypas, it wraps it up in macros so that the
    same body of the 'switch' can be used for both choices.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Fri May 22 20:57:26 2026
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 16:47, David Brown wrote:
    On 22/05/2026 17:16, Bart wrote:
    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers >>>>> encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    If a programmer wants a case label or default label at the end of a
    switch, doing nothing, then (prior to C23, and excluding compiler
    extensions) they have to put in an empty statement - "default : ;".ÿ (Or
    they could use "break;" to make code feel more symmetrical.)ÿ I can
    imagine that happening, and I can imagine that is the most likely
    situation where you would want a label right before an end brace.ÿ This
    seems to be the main motivation for the change in C23.

    It is much harder to imagine that this "bites" anyone, or even annoys
    people in any significant way.

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.

    There was no reason whatsoever to ban:

    L:}
    L:int x;

    other than the grammar happening to define labels in a certain way. That >there are workarounds is not the point.


    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'

    I think you might need to switch out your crystal ball - your
    predictions about what people will say or do have rarely come close to
    reality.ÿ Or, perhaps, you should stop saying such stupid things.

    Scott Lurndal said something stupid and I responded with sarcasm. Yet
    you attack me and not him. Biased at all?

    This is what I responded to in his post:

    * An assertion that the 'vast majority' of C programmers have never used >'goto'.

    Think about it. You cite 7 projects that have goto's in them.

    Google estimates there are 11 to 13 million C programmers.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Fri May 22 21:03:06 2026
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <Xj%PR.313884$yHZ7.10738@fx10.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>>>This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c", is_feature1()?'+':'-',
    is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);

    Those are the kinds of scenarios where it can be useful; what
    percentage of code overall uses that versus `if`, though?

    - Dan C.


    Other than the cases above, 'if' dominates by orders of magnitude.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Fri May 22 22:02:59 2026
    In article <ew3QR.651258$ZRIe.237915@fx22.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <Xj%PR.313884$yHZ7.10738@fx10.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>>>>This is the same difference between:

    a = cond ? x : y;

    and:

    if (cond) {
    a = x;
    } else {
    a = y;
    }

    If ?: is considered a convenient, clearer short-cut for the latter,

    First, it's not, for reasons that have been explained to you but
    that you are ignoring because you don't like them.

    But beyond that, I don't think that's what it is "considered" to
    be, at all. `?:` can be used in an expression; `if` cannot.

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c", is_feature1()?'+':'-',
    is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);

    Those are the kinds of scenarios where it can be useful; what
    percentage of code overall uses that versus `if`, though?

    Other than the cases above, 'if' dominates by orders of magnitude.

    That was all I meant by "sparingly"; sorry if that wasn't clear.

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From James Kuyper@3:633/10 to All on Fri May 22 18:37:49 2026
    On 2026-05-21 23:58, Janis Papanagnou wrote:
    On 2026-05-22 02:24, Keith Thompson wrote:
    ...
    I suggest using the term "name space" rather than "namespace",
    which happens to be the name of a C++ feature.

    The term "namespace" is, as I observe, a regular English word
    (it's in my dictionary, at least).
    ...
    ("A namespace in C++ is denoted by the keyword 'namespace'.")

    The term "name spaces" is officially defined by the C standard (6.2.3p1).

    "Thus, there are separate _name spaces_ for various categories of
    identifiers, as follows:"

    Note that "name spaces" is in italics, an ISO convention indicating that
    the sentence containing the term is the official definition of that
    term, with a meaning more specific than that given in your dictionary.
    Every use of "name space" in the C standard is consistent with it being
    the singular term corresponding to the plural term "name spaces".

    The C++ standard also uses the term "name space", but does not define it
    - but seems to use it in a fashion consistent with C's definition,
    except that C++ has a smaller variety of categories. It also uses the
    term namespace without definition. It does formally define the terms namespace-definition and namespace-body, and the keyword namespace.
    Every appearance of "namespace" in the C++ standard has a meaning
    inconsistent with the one defined by the C standard and used by the C++ standard for the term "name spaces".

    Therefore, I think it's crucial not to confuse the two meanings, which
    requires being careful about the difference between "name space" and "namespace".

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Sat May 23 00:24:45 2026
    On 22/05/2026 21:57, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:

    This is what I responded to in his post:

    * An assertion that the 'vast majority' of C programmers have never used
    'goto'.

    Think about it. You cite 7 projects that have goto's in them.

    How many millions do you want me to look at?

    Google estimates there are 11 to 13 million C programmers.

    I can't isolate the work of individual programmers. I can only look at C codebases.

    So let's say that a 'vast majority' of programmers equates to 90% of
    codebases being free of 'goto'.

    That is, only 1 in 10 projects have one or more 'goto'.

    Then the probably of the first 7 projects I look at all having gotos is near-zero (I worked it out as 1 in 10 million).

    If I look at more projects, the results are more varied, but it looks
    like more than half of them use goto. Certainly not as few as 1 in 10.
    So I don't think it's even a majority that avoid 'goto', and it's
    unlikely to be a 'vast' majority.

    There's more to it than that: an individual programmer will have worked
    on multiple projects. Some may have used goto and some not, if examined.
    But you claimed that nearly all have /never/ used it. That is not
    practical to test for.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Sat May 23 05:49:18 2026
    On 2026-05-22 10:49, David Brown wrote:
    [snip for brevity]

    All that you (and James downthread) wrote about namespaces I
    already knew (and acknowledge) from your earlier posts.

    My conclusions and suggestion are, for the already explained
    reasons, still the same. - So our opinions on that obviously
    differ. Never mind.

    Janis


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Fri May 22 21:38:16 2026
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    gcc by default compiles GNU C, not ISO C. It also fails to emit
    many language-required diagnostics. GNU C is ISO C with gcc
    extensions. Prior to C23, allowing "L:}" is a documented gcc
    extension.

    I seem to remember that sometime in the past gcc behaved in this
    way. And I see that current documentation on the gcc website says
    something to that effect. Trying it just now, however, I couldn't
    get gcc to accept "L:}" under any circumstances, no matter which
    combination of options was used. The unavailability doesn't bother
    me; I just thought the observation was interesting and worth
    reporting.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Sat May 23 07:33:53 2026
    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    (I haven't inspected their application-types or done any ad hoc
    classification; these are just [huge] raw numbers. If interested
    in details the sample grep-output is currently available through http://volatile.gridbug.de/ternaries.txt .)

    [...]

    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    I don't know what Bart refers to, but the sample is obviously a
    valid construct in Algol 68 - I think Bart has borrowed quite
    some Algol 68 concepts - where you have conditionals like either

    IF cond THEN val_1 ELSE val_2 FI

    or its terse alternative form

    ( cond | val_1 | val_2 )

    where, due to its orthogonality, it's obviously possible to allow
    all sorts of first class objects to be provided as values.

    There's also a (non-boolean) numeric case variant that may be used
    also with labels, as in its abbreviated form of

    ( value | val_1 , val_2 , val_3 , val_4 | val_else );

    I have to admit that I haven't checked the standard whether labels
    are actually first class objects in Algol 68 - I rarely use jumps -
    but typically you can put all types of first class items' values
    (including procedures and labels) as "values", where ever "values"
    are expected.

    The orthogonal concept I consider to be worthwhile, its long and
    short variants may satisfy personal preferences and various code
    contexts, and you can have the same conditionals also as "lvalues"
    not only as ("rvalue") expressions.

    I don't understand what makes you "Yikes." when hearing about such
    language options. - I mean beyond 'goto' being generally considered
    an inferior concept.

    If it's the "'goto' can be implied" you should know that in Algol 68
    it's equivalent to say either of
    GOTO stop
    GO TO stop
    stop
    These are semantically equivalent forms; you don't need that indecent
    'goto' keyword. IF n = 0 THEN stop FI or ( n = 0 | stop ) are
    both fairly clear forms (without an explicit GOTO).

    Janis

    [...]


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Sat May 23 07:51:49 2026
    On 2026-05-22 18:53, Bart wrote:
    On 22/05/2026 15:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bartÿ <bc@freeuk.com> wrote:
    [...]

    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    Blame Algol68, that's where implied goto comes from.

    No; since you said "I can do it ..." it was obviously _your decision_
    to borrow it for "your language(s)". So only *you* are to be accounted
    for *your* language implementation. - Stand by it, coward!

    You seem to like proudly pointing out how your languages work, and if
    there's criticism you blame the original source where the ideas stem
    from. - Disgusting moves; yikes!

    I think Algol 68's redundant/optional 'goto' keyword is good. (I made
    a statement and gave examples in a recent post.)

    Janis


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Sat May 23 07:56:37 2026
    On 2026-05-22 14:13, Dan Cross wrote:
    In article <10upbvq$1dhq4$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    [...]

    In my syntax, it can do this:

    goto (c | L1, L2, L3 | Lx)

    It's sweeter /because/ label names live in the normal name space. I'm
    sure A68 can do this too, but I couldn't get it to work.

    You are right that you can do that in Algol 68. - In Algol 68 it's just

    (c | L1, L2, L3 | Lx)


    Janis

    [...]


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Fri May 22 23:17:06 2026
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    gcc by default compiles GNU C, not ISO C. It also fails to emit
    many language-required diagnostics. GNU C is ISO C with gcc
    extensions. Prior to C23, allowing "L:}" is a documented gcc
    extension.

    I seem to remember that sometime in the past gcc behaved in this
    way. And I see that current documentation on the gcc website says
    something to that effect. Trying it just now, however, I couldn't
    get gcc to accept "L:}" under any circumstances, no matter which
    combination of options was used. The unavailability doesn't bother
    me; I just thought the observation was interesting and worth
    reporting.

    Apparently this extension was introduced in gcc 11, released in 2021.
    It also allows labels on declarations.

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

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sat May 23 00:04:51 2026
    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:

    gcc by default compiles GNU C, not ISO C. It also fails to emit
    many language-required diagnostics. GNU C is ISO C with gcc
    extensions. Prior to C23, allowing "L:}" is a documented gcc
    extension.

    I seem to remember that sometime in the past gcc behaved in this
    way. And I see that current documentation on the gcc website says
    something to that effect. Trying it just now, however, I couldn't
    get gcc to accept "L:}" under any circumstances, no matter which
    combination of options was used. The unavailability doesn't bother
    me; I just thought the observation was interesting and worth
    reporting.

    Apparently this extension was introduced in gcc 11, released in 2021.
    It also allows labels on declarations.

    That's good to know, but it doesn't help my (probably incorrect)
    memory telling me I observed it working sometime in the distant
    past. In any case thank you for the reportage.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Sat May 23 09:35:58 2026
    On 2026-05-23 08:17, Keith Thompson wrote:
    [...]
    Apparently this extension was introduced in gcc 11, released in 2021.

    It also allows labels on declarations.

    Also in plain (i.e. uninitialized) declarations?

    Is there some application case where that's useful?
    (Or just making the formulation of a rule simpler?)

    Janis


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Sat May 23 11:46:13 2026
    On 22/05/2026 18:35, Bart wrote:
    On 22/05/2026 16:47, David Brown wrote:
    On 22/05/2026 17:16, Bart wrote:
    On 22/05/2026 15:04, Scott Lurndal wrote:
    Bart <bc@freeuk.com> writes:
    On 22/05/2026 01:24, Keith Thompson wrote:

    <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2496.pdf>

    This is very interesting. So perhaps tens of millions of C programmers >>>>> encountered the quirk, found it annoying, and moved on.

    The vast majority of C programmers have likely never used goto.

    Source? Oh, 'likely', so it is just a random guess.

    In any case the issue (labels at the end of a compound statement)
    applied also to case labels and to 'default:'.

    If a programmer wants a case label or default label at the end of a
    switch, doing nothing, then (prior to C23, and excluding compiler
    extensions) they have to put in an empty statement - "default : ;".
    (Or they could use "break;" to make code feel more symmetrical.)ÿ I
    can imagine that happening, and I can imagine that is the most likely
    situation where you would want a label right before an end brace.
    This seems to be the main motivation for the change in C23.

    It is much harder to imagine that this "bites" anyone, or even annoys
    people in any significant way.

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with case
    or default labels). One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee, took the
    time to figure out what wording would need to be changed in the standard
    to allow "L:}". This was not a change worth putting much effort into,
    but no one had to do much work for it.

    There was no reason whatsoever to ban:

    ÿÿ L:}
    ÿÿ L:int x;

    other than the grammar happening to define labels in a certain way. That there are workarounds is not the point.

    For someone who is so proud of having "designed" several languages, you
    are remarkably ignorant about how languages are designed. Most of the
    time, people decide what they want to /allow/, not what they want to
    ban. And most of the time, the aim is to keep things as simple as
    possible (but no simpler) to be able to do what you want to do. For C,
    up until C23, one form of "statement" is "labelled statement" that looks
    like "label : statement". That's simple and easy to write in the
    standards, simple and easy to use in practice, and simple and easy to
    support in implementations. While I am not privy to the thoughts of
    either Ritchie or early C implementers and influencers, I cannot imagine
    "L:}" was /banned/ - it was simply not supported in the grammar of the language, probably because that would have been an unnecessary
    complication in the descriptions.




    Now you're going to say the vast majority of C programmers have never
    used 'switch' either! 'Probably...'

    I think you might need to switch out your crystal ball - your
    predictions about what people will say or do have rarely come close to
    reality.ÿ Or, perhaps, you should stop saying such stupid things.

    Scott Lurndal said something stupid and I responded with sarcasm. Yet
    you attack me and not him. Biased at all?

    Scott can answer that if he wants to.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Sat May 23 10:58:34 2026
    On 23/05/2026 06:51, Janis Papanagnou wrote:
    On 2026-05-22 18:53, Bart wrote:
    On 22/05/2026 15:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bartÿ <bc@freeuk.com> wrote:
    [...]

    (I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)

    Yikes.

    Blame Algol68, that's where implied goto comes from.

    No; since you said "I can do it ..." it was obviously _your decision_
    to borrow it for "your language(s)". So only *you* are to be accounted
    for *your* language implementation. - Stand by it, coward!

    You seem to like proudly pointing out how your languages work, and if
    there's criticism you blame the original source where the ideas stem
    from. - Disgusting moves; yikes!

    Yes of course, take the credit when there is any (hardly ever) and pass
    the blame otherwise.

    But here I was trying to highlight bias: some here react strongly to a
    feature if they think I've had anything to do with it; there would be a
    more muted response if it's from some other more highly regarded or more established language.

    But, people have also been scathing of features in C, and even long-established C extensions. Maybe it is just the case that if I think
    well of them, there is an automatic reaction to take the opposite attitude.




    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Sat May 23 11:31:18 2026
    On 23/05/2026 10:46, David Brown wrote:
    On 22/05/2026 18:35, Bart wrote:

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with case
    or default labels).ÿ One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee,

    Oh, right. So there would have been no point in my doing it. But Keith Thompson had a go a me for not doing that anyway:

    "Somebody (notably not you) took the time to write a proposal and submit
    it to the commmittee, which accepted it."


    There was no reason whatsoever to ban:

    ÿÿÿ L:}
    ÿÿÿ L:int x;

    other than the grammar happening to define labels in a certain way.
    That there are workarounds is not the point.

    For someone who is so proud of having "designed" several languages, you
    are remarkably ignorant about how languages are designed.ÿ Most of the
    time, people decide what they want to /allow/, not what they want to
    ban.ÿ And most of the time, the aim is to keep things as simple as
    possible (but no simpler) to be able to do what you want to do.ÿ For C,
    up until C23, one form of "statement" is "labelled statement" that looks like "label : statement".ÿ That's simple and easy to write in the
    standards, simple and easy to use in practice, and simple and easy to support in implementations.ÿ While I am not privy to the thoughts of
    either Ritchie or early C implementers and influencers, I cannot imagine "L:}" was /banned/ - it was simply not supported in the grammar of the language, probably because that would have been an unnecessary
    complication in the descriptions.

    So, you're picking up on the word "ban". How would you have worded it?

    For someone who is so proud of having "designed" several languages,

    All of which allow labels at the end of a block or before declarations,
    when the latter could be mixed within executable code.

    Actually, I probably allow labels in too many places, including in the
    middle of expressions, like here (expressed in A68G syntax):

    INT a, b:=2, c:=3, d:=4;

    a := IF b=c THEN fred: c ELSE d FI;
    GOTO fred;

    Declaring the label is not an error with A68G, but trying to jump into
    the expression doesn't work; presumably the scope of 'fred' is limited
    to the block so it just can't see it.

    Jumping out of the expression via GOTO is allowed however.

    My languages allow both. Jumping into the middle of a block is sometimes
    done and can be safe. Into the middle of an expression is less so, so
    should ideally be detected and blocked, probably in a later compiler pass.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Sat May 23 03:59:52 2026
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2026-05-23 08:17, Keith Thompson wrote:
    [...]
    Apparently this extension was introduced in gcc 11, released in 2021.

    It also allows labels on declarations.

    Also in plain (i.e. uninitialized) declarations?

    A declaration without an initializer is a declaration, so yes.
    The thing after a label doesn't have to denote executable code.

    Is there some application case where that's useful?
    (Or just making the formulation of a rule simpler?)

    Both C99 and C23 arguably simplified the syntax of a
    compound-statement and made it more flexible.

    In C90, a compound-statement is a "{" followed by zero or more
    declarations, followed by zero or more statements, followed by a "}".

    In C99 through C23, a compound-statement is a "{", zero or more
    block-items, and a "}".

    In C99 through C17, a block-item is a declaration or a
    statement, allowing declarations and statements to be mixed.
    (A labeled-statement is a kind of statement.)

    In C23, a block-item is a declaration, an unlabeled-statement, or a
    label, so now you have three kinds of items that can be freely mixed.

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

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Sat May 23 13:51:04 2026
    On 23/05/2026 12:31, Bart wrote:
    On 23/05/2026 10:46, David Brown wrote:
    On 22/05/2026 18:35, Bart wrote:

    And yet, the link at the top was about somebody proposing to fix this
    very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with case
    or default labels).ÿ One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee,

    Oh, right. So there would have been no point in my doing it. But Keith Thompson had a go a me for not doing that anyway:

    Do you think Keith, me, and perhaps the rest of the c.l.c. regulars all
    get together in secret meetings to discuss how we are going to attack
    you? Or do you think we are all the same person, with different
    pseudonyms? If not, why do you keep asking people to justify the
    opinions or statements made by others? Or, more often, why do you ask
    people to justify words and sentiments that you put in other peoples'
    mouths? Don't you think it would be more productive to read what people write, think about what they wrote, try to understand it, and perhaps
    ask them if you can't figure out what they are saying?


    "Somebody (notably not you) took the time to write a proposal and submit
    it to the commmittee, which accepted it."


    There was no reason whatsoever to ban:

    ÿÿÿ L:}
    ÿÿÿ L:int x;

    other than the grammar happening to define labels in a certain way.
    That there are workarounds is not the point.

    For someone who is so proud of having "designed" several languages,
    you are remarkably ignorant about how languages are designed.ÿ Most of
    the time, people decide what they want to /allow/, not what they want
    to ban.ÿ And most of the time, the aim is to keep things as simple as
    possible (but no simpler) to be able to do what you want to do.ÿ For
    C, up until C23, one form of "statement" is "labelled statement" that
    looks like "label : statement".ÿ That's simple and easy to write in
    the standards, simple and easy to use in practice, and simple and easy
    to support in implementations.ÿ While I am not privy to the thoughts
    of either Ritchie or early C implementers and influencers, I cannot
    imagine "L:}" was /banned/ - it was simply not supported in the
    grammar of the language, probably because that would have been an
    unnecessary complication in the descriptions.

    So, you're picking up on the word "ban". How would you have worded it?

    If you "ban" something, you actively and explicitly choose not to allow it.


    For someone who is so proud of having "designed" several languages,

    All of which ...
    ... are irrelevant.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Sat May 23 13:07:59 2026
    On 23/05/2026 12:51, David Brown wrote:
    On 23/05/2026 12:31, Bart wrote:
    On 23/05/2026 10:46, David Brown wrote:
    On 22/05/2026 18:35, Bart wrote:

    And yet, the link at the top was about somebody proposing to fix
    this very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with
    case or default labels).ÿ One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee,

    Oh, right. So there would have been no point in my doing it. But Keith
    Thompson had a go a me for not doing that anyway:


    Do you think Keith, me, ....

    Irrelevant. That was clearly a snide comment aimed at me.
    KT:
    "Somebody (notably not you) took the time to write a proposal and
    submit it to the commmittee, which accepted it."


    So, you're picking up on the word "ban". How would you have worded it?

    If you "ban" something, you actively and explicitly choose not to allow it.

    So you don't like the word. Maybe it wasn't intentional at the start,
    and maybe neither was doing nothing about it for decades
    (unintentionally of course), but the end result was the same.



    For someone who is so proud of having "designed" several languages,

    All of which ...
    ... are irrelevant.

    My choices of label placement are irrelevant to the choices made in C in
    a paragraph about language design? OK.

    But you managed to squeeze in another snarky comment anyway.

    ALWAYS with the personal attacks in this group.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From fir@3:633/10 to All on Sat May 23 14:35:07 2026
    Bart pisze:
    On 23/05/2026 12:51, David Brown wrote:
    On 23/05/2026 12:31, Bart wrote:
    On 23/05/2026 10:46, David Brown wrote:
    On 22/05/2026 18:35, Bart wrote:

    And yet, the link at the top was about somebody proposing to fix
    this very thing. And it was accepted.


    We all know that "L:;}" is used sometimes in code (typically with
    case or default labels).ÿ One day, someone who happened to use that
    construction regularly, and who was already a highly respected C
    developer and already worked with the C standards committee,

    Oh, right. So there would have been no point in my doing it. But
    Keith Thompson had a go a me for not doing that anyway:


    Do you think Keith, me, ....

    Irrelevant. That was clearly a snide comment aimed at me.
    KT:
    "Somebody (notably not you) took the time to write a proposal and
    submit it to the commmittee, which accepted it."


    So, you're picking up on the word "ban". How would you have worded it?

    If you "ban" something, you actively and explicitly choose not to
    allow it.

    So you don't like the word. Maybe it wasn't intentional at the start,
    and maybe neither was doing nothing about it for decades
    (unintentionally of course), but the end result was the same.



    For someone who is so proud of having "designed" several languages,

    All of which ...
    ... are irrelevant.

    My choices of label placement are irrelevant to the choices made in C in
    a paragraph about language design? OK.

    But you managed to squeeze in another snarky comment anyway.

    ALWAYS with the personal attacks in this group.

    im not reading into that threads (it would need a very big focus not to
    get lost in that large branches of that trees) but i may say i find you
    and mclean as the most sense users of comp.lang.c - not that i hate this
    so called 'regulars' (im not - if the regulars would be not present the
    group would be empty and it would bo no place to post at all ;c
    esides thay talke on topic (thoug i would say only on part of it as they
    say most interesting part of topics too)

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From fir@3:633/10 to All on Sat May 23 14:38:22 2026
    fir pisze:
    esides thay talke on topic (thoug i would say only on part of it as they
    say most interesting part of topics too)

    errata:
    "besides they talk on topic (though, i would say, only on part of it, as
    they "skip" (left not risen) most interesting part of topics, too)

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Sat May 23 13:55:13 2026
    On 23/05/2026 11:31, Bart wrote:
    On 23/05/2026 10:46, David Brown wrote:

    For someone who is so proud of having "designed" several languages,

    All of which allow labels at the end of a block or before declarations,
    when the latter could be mixed within executable code.

    Actually, I probably allow labels in too many places, including in the middle of expressions, like here (expressed in A68G syntax):

    ÿ INT a, b:=2, c:=3, d:=4;

    ÿ a := IF b=c THEN fred: c ELSE d FI;
    ÿ GOTO fred;

    Declaring the label is not an error with A68G, but trying to jump into
    the expression doesn't work; presumably the scope of 'fred' is limited
    to the block so it just can't see it.

    Jumping out of the expression via GOTO is allowed however.

    My languages allow both. Jumping into the middle of a block is sometimes done and can be safe. Into the middle of an expression is less so, so
    should ideally be detected and blocked, probably in a later compiler pass.

    I tried gnu C, which has statement expressions. That also lets you have
    labels inside expressions.

    Jumping into an expression from outside is not allowed (for a different
    reason than Algol68), but jumping out of it is.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Sat May 23 15:30:51 2026
    In article <10uqamg$1nrsr$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 18:32, Dan Cross wrote:
    In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:

    [snip]

    Well, Bart, you'll be pleased to know that I was able to
    replicate your results.

    I compiled your `tinypas.c` on a slightly older machine (16 core
    AMD Ryzen 9 5950X; 64 GiB of RAM; otherwise unloaded) running
    Arch Linux. I built with `clang -Os` and ran your recursive
    fibonacci program. The version using computed gotos was
    consistently about 60% faster. Absolute runtimes were about .3
    seconds of user time for the computed goto version, and about
    .47 to .5 seconds for the switch-based version.

    Furthermore, you appear to be correct about branch overhead, as
    well. Using the `perf stat` tool, from the `computed goto`
    version (slightly reformatted for posting):

    343.56 msec task-clock:u
    1,175,236 branch-misses:u # 0.3% branch_miss_rate
    372,329,571 branches:u # 1083.7 M/sec branch_frequency
    1,458,602,759 cpu-cycles:u
    3,562,568,240 instructions:u # 2.4 instructions insn_per_cycle
    190,845,228 stalled-cycles-frontend:u

    0.344303755 seconds time elapsed
    0.340037000 seconds user
    0.001000000 seconds sys

    Whereas for the `switch`-based version:

    486.68 msec task-clock:u
    6,387,425 branch-misses:u # 0.6 % branch_miss_rate
    1,149,861,784 branches:u # 2362.7 M/sec branch_frequency
    2,071,031,603 cpu-cycles:u
    5,271,732,241 instructions:u # 2.5 instructions insn_per_cycle
    152,508,399 stalled-cycles-frontend:u

    0.487171235 seconds time elapsed
    0.481526000 seconds user
    0.001000000 seconds sys

    Second, how was it compiled? What optimization settings? What
    compiler? Did you test different compilers to see if they did
    things differently? You've made a broad general assertion about
    the code generated in response to a `switch` statement; my own
    experience is that that varies widely based on compiler, target
    architecture, and optimization settings.

    It will depend on the task and spread of workload. If the program being
    run is spending a lot of time in libraries, then bytecode dispatch is
    less of the overall overhead.

    That wasn't what I meant. The point was that the generated code
    from the compiler will vary widely.

    Here is a post from a person who noted a much _smaller_
    performance difference between a switch-based loop and computed
    gotos in the cpython interpreter, after seeing a regression due
    to clang-19: https://blog.nelhage.com/post/cpython-tail-call/

    This asserts that modern compilers are capable of generating
    code for a `switch` that is essentially the same as that
    manually generated with computed gotos. I don't know why this
    doesn't seem to be the case with your code.

    Finally, your assertion was that the version using your multiple
    dispatch is faster due to branch predicition: you've shown that
    this is faster, but you haven't shown _why_, let alone that it
    is due to branch prediction. Since you're running this on
    x86_64, did you try to look at the machine's perf counters to
    see what's actually happening?

    It's a well-known technique. It was used on CPython (as compiled for
    Linux, since on Windows it doesn't use gcc), and may still be. Athough
    there are some experients to move towards complex threaded code via TCO >instead.

    In any case, it seems to work, so who cares why?

    This is actually the main point I was trying to make. You
    asserted a specific behavior, but without data to back it up. I
    found the claim dubious, in part because of the compiler
    optimizations mentioned above. In this case, you were correct;
    but understanding why is important.

    Switching between the two means changing 'doswitch' to 'doswitchu'.

    In C switching would require a rewrite, unless you write it in a
    contrived manner. But then it still needs that bunch of macros shown
    below, and it needs that table of label pointers to be manually maintained. >>>
    [snip]

    I'm not sure what you mean here. You've given examples of
    timings using computed gotos and `switch` in C; it's unclear
    what would "require a rewrite" since you appear to already have
    both versions.

    You know what normal switch looks like:

    while (!stopped) {
    switch (opcode) {
    case ADD:
    ...

    Computed goto would first need a jumptable:

    void* jumptable[] = {&&ADDLAB, ....};

    Then dispatch at every point:

    goto *jumptable[opcode];
    ADDLAB:
    ....
    goto *jumptable[opcode]

    It is quite different. In Tinypas, it wraps it up in macros so that the
    same body of the 'switch' can be used for both choices.

    I still don't understand what you mean by "rewrite": it appeared
    you were referring to work that had yet to be done, but the work
    was done.

    Anyway, it doesn't much matter. Your analysis was correct here.

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Sat May 23 17:59:57 2026
    On 23/05/2026 16:30, Dan Cross wrote:
    In article <10uqamg$1nrsr$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    On 22/05/2026 18:32, Dan Cross wrote:
    In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:

    [snip]

    Well, Bart, you'll be pleased to know that I was able to
    replicate your results.

    OK, that's good. (Secretly thinking, phew!)

    Here is a post from a person who noted a much _smaller_
    performance difference between a switch-based loop and computed
    gotos in the cpython interpreter, after seeing a regression due
    to clang-19: https://blog.nelhage.com/post/cpython-tail-call/

    I think that was explained due to LLVM combining the multiple dispatch
    points into one, defeating the purpose of the technique.


    This asserts that modern compilers are capable of generating
    code for a `switch` that is essentially the same as that
    manually generated with computed gotos. I don't know why this
    doesn't seem to be the case with your code.

    It says that it needs Clang 18, or 19 with appropriate flags. So will
    Clang 18 just do it anyway? That sounds undesirable; individual dispatch points generates more code. (You may want to look at your -Os option).

    I wasn't aware C compilers could do this (of course it would have to be
    a switch within loop). And for a long time they didn't, and some still
    don't, that's the reason for the computed goto method.

    (The actual change in the compiler is simple, and that is what I do in
    my language. But I control it per-switch rather than across the program
    for the reasons above. I start with a 'doswitch' keyword so it already
    knows it loops. The keywords used to control it are:

    doswitch # normal looping switch with single dispatch
    doswitchu # multiple dispatch
    doswitchx # multiple dispatch without indexing

    The 'u' in 'doswitchu' was originally because only range-checking was disabled, but that made little difference. I may change the name.

    The 'doswitchx' goes a little further than gnu C's computed gotos. There
    the dispatch code looks like this:

    goto *jumptable[pc.opcode]

    There's a table lookup. By doing a fixup pass, the labels can be
    directly written into the bytecode program. Then the dispatch code can
    look like this:

    goto *pc.labaddr

    It's a few % faster.)

    Regarding computed goto (either explict, or via compiler option) and TCO methods, I wouldn't expect the latter to be by itself much faster.

    AIUI, the benefits are that with TCO each bytecode handler has its own function, which is easier to optimise than one giant function with a big switch statement or computed goto labels.


    I still don't understand what you mean by "rewrite": it appeared
    you were referring to work that had yet to be done, but the work
    was done.

    Yes, somebody did that.

    In general, you start by writing a switch statement. If later you decide
    to use computed goto, it is massively different. Or you can make it
    worth with both via macro tricks as used by tinypas.c.

    Now apparently there's a compiler option, if using Clang, to enable
    multiple dispatch points.

    Note that with tinypas.c in 'switch' mode, it uses:

    start:
    switch (...)
    CASE ADD: ... NEXT

    'NEXT' is 'goto start', so it is not obvious that this is a looping
    switch. (You might want to use 'break' and wrap 'while(1){} around the
    switch in case this is the problem. I don't have a fully working Clang.)



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From makendo@3:633/10 to All on Sun May 24 01:14:35 2026
    Back in those days, languages that needed multipass compilers (e.g.
    Algol 68) were considered complicated and expensive to implement.
    That?s why C went for a single-pass language design, like Pascal. And
    like Pascal, it has forward declarations to mitigate this somewhat.

    You need some kind of use-before-define facility in any realistic
    language, if you want to allow recursion, and in particular mutual
    recursion.

    It?s amusing to think that C++, that behemoth that, in terms of sheer complexity, leaves old-style monsters like Algol 68 or PL/I in the
    dust, is still essentially a single-pass language design.

    Nobody really cares about whatever shit multipass is now -- you've got gigabytes of main memory to work with, and you can always store a
    yet-to-be defined label in a hash table if you are reinventing your
    own assembler, as a simple example. Multipass is for when you have
    only kilobits of core memory, where the only way to realistically
    compile your code is to dump intermediate results (in the assembler
    case, memory address of labels) on a magnetic tape.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Sun May 24 02:48:25 2026
    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote:
    [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines. I'd say that's pretty
    sparingly used.

    (I haven't inspected their application-types or done any ad hoc >classification; these are just [huge] raw numbers. If interested
    in details the sample grep-output is currently available through >http://volatile.gridbug.de/ternaries.txt .)

    I'm not terribly. I never said it wasn't used; just used
    sparingly. That's not a precise unit of measure, unfortunately;
    perhaps a better way to put it would have been that, compared to
    `if` or even `if (foo) bar = 1; else bar = 2;` the ternary
    operator is used much less frequently.

    [snip]
    If it's the "'goto' can be implied" you should know that in Algol 68
    it's equivalent to say either of
    GOTO stop
    GO TO stop
    stop
    These are semantically equivalent forms; you don't need that indecent
    'goto' keyword. IF n = 0 THEN stop FI or ( n = 0 | stop ) are
    both fairly clear forms (without an explicit GOTO).

    Careful or you're going to make me say "yikes" again. I think
    these are awful design decisions. Wirth had strong opinions
    about Algol 68; I can see why.

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sat May 23 14:21:14 2026
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Bart <bc@freeuk.com> writes:

    On 21/05/2026 07:45, David Brown wrote:

    [...]

    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. [...]

    I was hoping you would stop there. Your point would have
    been made much more effectively.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sat May 23 14:19:00 2026
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Bart <bc@freeuk.com> writes:

    On 21/05/2026 07:45, David Brown wrote:

    [...]

    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. [...]

    I was hoping you would stop there. Your point would have
    been made much more effectively.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sat May 23 14:03:46 2026
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Bart <bc@freeuk.com> writes:

    On 21/05/2026 07:45, David Brown wrote:

    [...]

    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. [...]

    I was hoping you would stop there. Your point would have
    been made much more effectively.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sat May 23 20:32:39 2026
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

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

    Bart <bc@freeuk.com> writes:

    On 21/05/2026 07:45, David Brown wrote:

    [...]

    So do you have an example of where code has been written to take
    advantage of the separate name spaces?

    No. This is why I claimed it was pointless.

    This entire discussion is pointless. [...]

    I was hoping you would stop there. Your point would have
    been made much more effectively.

    Sorry for the duplicates. News server hiccups.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Sat May 23 20:46:31 2026
    Bart <bc@freeuk.com> writes:
    On 23/05/2026 12:51, David Brown wrote:
    [...]
    Do you think Keith, me, ....

    Irrelevant. That was clearly a snide comment aimed at me.
    KT:
    "Somebody (notably not you) took the time to write a proposal and
    submit it to the commmittee, which accepted it."

    Yes, it was a snide comment aimed at you. Not part of a conspiracy
    or any biased attempt to reject ideas just because they're yours,
    just a single snide comment.

    [...]

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

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sat May 23 21:12:35 2026
    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:

    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; [...]

    Yes.

    Breaking out of nested loops without a lot of unnecessary
    ceremony is sort of an obvious example; [...]

    Another scenario where using goto can be attractive is when
    so-called "loop and a half" processing is needed. Something
    like this

    goto ENTRY;
    do {
    ....
    ENTRY:
    ....
    } while( ..condition.. );

    is in many cases more attractive than goto-less alternatives.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sat May 23 21:31:22 2026
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:

    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; [...]

    Yes.

    Breaking out of nested loops without a lot of unnecessary
    ceremony is sort of an obvious example; [...]

    Another scenario where using goto can be attractive is when
    so-called "loop and a half" processing is needed. Something
    like this

    goto ENTRY;
    do {
    ....
    ENTRY:
    ....
    } while( ..condition.. );

    is in many cases more attractive than goto-less alternatives.

    I should add that, even though there are these and some other
    well-known counterexamples, IME occasions where goto is needed
    are exceedinly rare; under 0.02%, say. My heuristic is, when
    considering possibly using a goto, write two or three versions
    without using goto; then, only if a goto version is better than
    all the others should goto be used.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Sun May 24 10:13:39 2026
    On 2026-05-24 04:48, Dan Cross wrote:
    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>> [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines. I'd say that's pretty
    sparingly used.

    (I haven't inspected their application-types or done any ad hoc
    classification; these are just [huge] raw numbers. If interested
    in details the sample grep-output is currently available through
    http://volatile.gridbug.de/ternaries.txt .)

    I'm not terribly. I never said it wasn't used; just used
    sparingly. That's not a precise unit of measure, unfortunately;

    (As you I also wasn't "precise" with my wording, deliberately;
    it's hard to be precise with such things without doing thorough
    statistics on huge code bases and analysis of application cases,
    common code patterns, and socialized personal preferences.)

    But for that size of code I think it's extremely commonly used
    for these sorts of _expressions_. (Mind that you are not allowed
    to use 'if' in expressions.) It's not that you have to seek for
    these with a magnifying glass; you open a file and immediately
    see such code patterns (in the appropriate places, of course).

    perhaps a better way to put it would have been that, compared to
    `if` or even `if (foo) bar = 1; else bar = 2;` the ternary
    operator is used much less frequently.

    The "C" language is, as other languages, not only about 'if' and
    ternary conditionals; given that your "1.5%" number is comparably
    huge.

    That sample source had ~7 times as many 'if' as those ternaries.
    But that also isn't surprising, as ternaries, in "C", can only be
    used in expressions (as opposed to, say, conditionals in Algol 68
    for example).

    The point is, if you're replacing all those ternaries by explicit
    if-constructs you'd blow up the source code, and often make it,
    because of the syntactic bloat - especially in "C" syntax - less
    readable by that.[*] For many/most of those cases in the sample
    code the corresponding 'if' would make the code much worse, IMO.


    [snip]
    If it's the "'goto' can be implied" you should know that in Algol 68
    it's equivalent to say either of
    GOTO stop
    GO TO stop
    stop
    These are semantically equivalent forms; you don't need that indecent
    'goto' keyword. IF n = 0 THEN stop FI or ( n = 0 | stop ) are
    both fairly clear forms (without an explicit GOTO).

    Careful or you're going to make me say "yikes" again.

    (You may say what you like. - But I wonder why, given that an
    explicit 'goto' keyword is just redundant and doesn't serve any
    purpose; unless one is thinking only in pre-1968 mindsets...

    I think
    these are awful design decisions. Wirth had strong opinions
    about Algol 68; I can see why.

    ...and an unspecific reference is also not helpful in any way.)

    You are unspecifically generalizing/extrapolation/inferring here.
    (Not sure about the most appropriate word - most likely all are
    fitting concerning your previous statement; hope you understand
    what I'm meaning here and also by what I'm expanding on below.)

    Back these days there were quite many, and well known CS experts
    (besides Wirth also Hoare and Knuth), who had other ideas about
    how a new Algol language should look like.[**] But their reasoning
    where differing;[***] Wirth didn't want a large powerful language,
    but a simpler small one, usable for education/teaching.[****] Also
    the problem of implementing a powerful large (in some aspects even
    complex) language was an issue, considering the difficulty that
    implementers might have. As far as I recall, mainly Algol's size
    (and perceived complexity, specifically the 2-level W-grammar) was
    primary source of disagreement.

    It's hard to believe that profane details like the optionality of
    a 'goto' statement would have been a crucial reason of dissent.

    The existence of the 'goto' in Algol 68 to me looks just like a
    concession to all those that were used to that keyword from prior
    existing low-level languages, for programmers who want imperative
    sounding code, and who maybe need, when programming, a feeling of
    wading through the mud of a BASIC-like type of code organization.
    I can explain the liking of an explicit 'goto' (or disliking its
    optionality despite its redundancy) only by specific socialization.

    But if you like a mandatory 'goto' I'm fine with that. No argument
    on opinions and personal preferences.

    Janis


    - Dan C.


    [*] Some time ago I gave examples where it makes, beyond preferences,
    sense to even mix (in Algol 68) its if-conditionals and the shortcut
    'if' variant, ( | | ), (that resembles C's ternary) in expressions.

    [**] Dissent is an effect of strong characters with strong opinions.

    [***] "But their objections were diverse." [Lindsay]

    [****] But he obviously completely misjudged Algol 68's design fitting
    for educational purposes; universities did use Algol 68 for teaching
    purpose. Algol's coherent design was actually a strong argument to use
    it for educational purposes.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Sun May 24 11:00:13 2026
    On 2026-05-24 06:12, Tim Rentsch wrote:
    [...]

    Another scenario where using goto can be attractive is when
    so-called "loop and a half" processing is needed. Something
    like this

    goto ENTRY;
    do {
    ....
    ENTRY:
    ....
    } while( ..condition.. );

    is in many cases more attractive than goto-less alternatives.

    Yes, a (low-level) code-pattern that works well in "C".

    May there be issues if you have declarations in the loop?

    Other languages forbid that to not encounter inherent
    problems when trying to enter inner scopes. - Just noting.


    I think in "C" I'd prefer instead of

    goto entry; do { A; entry: B; } while (condition);

    for several reasons rather

    while (1) { B; if (!condition) break; A; }

    despite of the additional outer true-condition.

    Janis


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sun May 24 04:15:00 2026
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <10ujm3r$3pnbb$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:

    [snip] I have almost never had need of "goto" or labels (excluding
    switch case labels, of course), and don't expect ever to do so in the
    future.

    While I generally try to avoid it, there are times (in C in
    particular) when it really is the right tool; [...]

    Yes.

    Breaking out of nested loops without a lot of unnecessary
    ceremony is sort of an obvious example; [...]

    Another scenario where using goto can be attractive is when
    so-called "loop and a half" processing is needed. Something
    like this

    goto ENTRY;
    do {
    ....
    ENTRY:
    ....
    } while( ..condition.. );

    is in many cases more attractive than goto-less alternatives.

    Another log on the goto fire...

    The code below is a transcription of Algorithm A in section 6.2.3
    of Knuth TAOCP, for insertion into an AVL tree. It uses the same
    variable names as in the original, except not capitalized.

    I think it's fair to call this algorithm a candidate poster child
    for showing abuse of goto. Ick.

    It makes a good challenge problem: provide a more comprehensible
    re-write, especially with respect to reducing use of goto.


    #include <assert.h>
    #include <stdlib.h>

    typedef struct tree_node_s *Tree;

    typedef signed int Key;
    typedef signed int Balance;

    struct tree_node_s {
    Tree left, right;
    Key key;
    Balance b;
    };


    void
    avl_insert( Tree head, Key k ){
    Tree t, s, p, q, r;
    Balance a;

    A1:
    t = head, s = p = head->right;
    /* fall through */

    A2:
    if( k < p->key ) goto A3;
    if( k > p->key ) goto A4;
    return;

    A3:
    q = p->left;
    if( !q ){
    left = q = malloc( sizeof *q );
    goto A5;
    }
    if( q->b != 0 ) t = p, s = q;
    p = q;
    goto A2;

    A4:
    q = p->right;
    if( !q ){
    right = q = malloc( sizeof *q );
    goto A5;
    }
    if( q->b != 0 ) t = p, s = q;
    p = q;
    goto A2;

    A5:
    q->left = q->right = 0, q->key = k, q->b = 0;
    /* fall through */

    A6:
    if( k < s->key ) r = p = s->left;
    else r = p = s->right;
    while( p != q ){
    if( k < p->key ) p->b = -1, p = p->left;
    else p->b = +1, p = p->right;
    }
    /* fall through */

    A7:
    if( k < s->key ) a = -1;
    else a = +1;
    if( s->b == 0 ){
    b = a;
    return;
    }
    if( s->b == -a ){
    b = 0;
    return;
    }
    if( s->b == a ){
    if( r->b == a ) goto A8;
    if( r->b == -a ) goto A9;
    assert( 0 );
    }
    assert( 0 );

    A8:
    p = r;
    if( a == 1 ) s->right = r->left, r->left = s;
    else s->left = r->right, r->right = s;
    s->b = r->b = 0;
    goto A10;

    A9:
    if( a == 1 ){
    p = r->right;
    right = p->left, p->left = r;
    left = p->right, p->right = s;
    } else {
    p = r->left;
    left = p->right, p->right = r;
    right = p->left, p->left = s;
    }
    if( p->b == a ) s->b = -a, r->b = 0;
    if( p->b == 0 ) s->b = 0, r->b = 0;
    if( p->b == -a ) s->b = 0, r->b = -a;
    p->b = 0;
    /* fall through */

    A10:
    if( s == t->right ) t->right = p;
    else t->left = p;

    }

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Sun May 24 12:30:19 2026
    On 24/05/2026 03:48, Dan Cross wrote:
    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bart <bc@freeuk.com> wrote: >>> [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines. I'd say that's pretty
    sparingly used.

    I think if you take just about any feature, you will find it is only
    used in a small percentage of lines.

    But don't just take my opinion, I put it to to the test.

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header, which
    are combined into a 235Kloc source file).

    This was preprocessed down to a 84Kloc file, which would increase the percentages and work againt my argument. Even so, these were the figures
    I got:

    goto 0.75 % (no. of instances as percentage of line count)
    for 1.00
    while 0.36
    funcs 2.31 (no. of function definitions)
    + 2.38
    - minus 1.66
    if 8.73
    do 0.07
    ?: 0.56
    switch 0.79
    case 1.32
    break/sw 0.95
    break 0.35
    const 2.52
    union 0.06
    += 0.38
    ~ 0.32
    != 1.63
    [] 6.03 (includes declarations)
    ; 62.88

    Since that one has an unusual composition, here is another, sources for
    the Tiny C compiler, preprocessed to 28Kloc:

    goto 1.08 %
    for 0.82
    while 0.46
    funcs 1.88
    + 2.27
    - minus 1.00
    if 8.98
    do 0.05
    ?: 2.80
    switch 0.17
    case 2.38
    break/sw 1.18
    break 0.35
    const 2.30
    union 0.19
    += 0.41
    ~ 1.67
    != 1.97
    [] 3.25
    ; 64.55

    There is a reasonable correlation.

    Anyway. it seems half these common C features fall below the threshold
    of what you'd call sparingly used.

    (For me the most interesting one is ";". In my languages, the figure is
    around 0.1%. There must be a reason so many languages get rid of it or
    make it optional.)

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Sun May 24 13:39:52 2026
    On 24/05/2026 12:30, Bart wrote:
    On 24/05/2026 03:48, Dan Cross wrote:
    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnouÿ <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:
    In article <10upivp$1fkbh$1@dont-email.me>, Bartÿ <bc@freeuk.com>
    wrote:
    [...]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the kind of code you posted above.

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    ÿÿÿÿÿ 130ÿ C-files
    ÿÿ 250249ÿ total LOC
    shows
    ÿÿÿÿ 3864ÿ lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines.ÿ I'd say that's pretty
    sparingly used.

    I think if you take just about any feature, you will find it is only
    used in a small percentage of lines.

    But don't just take my opinion, I put it to to the test.

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header, which
    are combined into a 235Kloc source file).

    This was preprocessed down to a 84Kloc file, which would increase the percentages and work againt my argument. Even so, these were the figures
    I got:

    ÿgotoÿÿÿÿ 0.75 % (no. of instances as percentage of line count)
    ÿforÿÿÿÿÿ 1.00
    ÿwhileÿÿÿ 0.36
    ÿfuncsÿÿÿ 2.31ÿÿ (no. of function definitions)
    ÿ+ÿÿÿÿÿÿÿ 2.38
    ÿ- minusÿ 1.66

    I meant negation as in '-a'. I forgot 'minus' can mean both.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Sun May 24 19:08:55 2026
    In article <10uprpn$1hfpm$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 22/05/2026 16:44, Dan Cross wrote:
    In article <10un0j7$obiv$2@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    Interesting. I've found it to be somewhat the opposite; using a
    richer type system has lead to code that is easier to understand
    and reason about, and less buggy: type-oriented programming can
    make entire categories of errors *unrepresentable*, so it's not
    just _harder_ to make certain kinds of mistakes, but
    _impossible_. The existance of an object of some type can be
    thought of as an existence proof that the invariants the type
    represents hold.

    In general, I agree - I prefer strongly typed languages, and it's always >best if an error is identified by the code being uncompilable rather
    than waiting for run-time testing (or testing by evil hackers). But if >people want to do something with the language that is hard to achieve >efficiently within the norms of the language, and there are escape
    hatches ("unsafe" code, inline assembly, calling external C functions,
    etc.) then people will use them.

    Sure; that seems pretty clear, given extensive available
    evidence.

    People do sometimes write C code that
    knowingly depends on undefined behaviour, because it makes their results >more efficient and "it worked when I tested it".

    Yes, or they force some knob on their compiler into the required
    position to give them guaranteed results. Linux does this, for
    example; last time I worked in it, Google's massive (2BLOC) code
    base similarly.

    Type safety - like any kind of safety - can sometimes get in the way of >"getting things done". A balance always needs to be found to ensure
    that people can do what they need to do without breaking rules or using >"escapes" more than absolutely necessary. If people find that much of
    their Rust code is "unsafe", then most of the point of using Rust is
    lost. (Of course this also means that different languages are suited to >different tasks.)

    It _can_, but I wouldn't take it as a given that it _does_, and
    what I'm trying to say is that contrary to often getting in the
    way, it can be used effectively to make programs better and
    safer, with no runtime downside. Indeed, a well-typed program
    can be faster than the alternative, since a) the compiler can
    prove that some properties hold, and b) it can be more
    aggressively optimized at a higher level. An example here are
    non-nullable references; there's no need to check whether they
    are NULL or not before indirecting through them, since by
    definition they cannot be NULL.

    And Alexis King wrote the great, "Parse, Don't Validate" essay
    some years ago where she talks about "Type-Driven Development":
    https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

    More recently, Yaron Minsky gave a talk and discussed this in an
    OCaml context (https://www.youtube.com/watch?v=rUYP4C29yCw; the
    most relevant bits start at about 6:35. The talk is about AI
    and constraining agents, but the discussion around types is more
    general).

    I wrote the production OS loader for Oxide compute sleds using
    this technique in the virtual memory system (and other places):
    the loader uses multiple page sizes, and the rule is that, when
    mapping a region of memory, it uses the largest page size that
    it can, given size and alignment constraints. But I use the
    type system to make it impossible to, say, map a 2MiB "large"
    physical page frame to a non-2MiB aligned virtual boundary.

    I did some work, eons ago, in a language called XC that was specifically >>> for XMOS microcontrollers. The language and tools had a feature that
    made data races impossible by not allowing competing access to shared
    variables from different threads. Since threads were part of the
    hardware, and the tools analysed the code flow through threads, this
    could all be enforced at build time - data had to be passed in messages, >>> not shared memory. But for some things that involved large buffers,
    that was hopelessly inefficient - and these devices were regularly used
    with USB, audio, and similar things that needed large and predictable
    buffers. So code - even library and example code from the manufacturer
    - was full of inline assembly to work around the "smart-arse" language
    and tools.

    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an >indication that the restrictions inherent in the language and tools made >them somewhat unsuitable for a lot of use-cases that fitted the hardware >well. (Modern XMOS tools have changed significantly since those days, >perhaps partly because of such issues.)

    Sure, but that's a failure of that system to provide good
    abstractions: the system was not rich enough to provide the
    functionality you needed or wanted.

    I think the central tension here is that the idea of a strong
    type system that is also semantically rich is being conflated
    with one that's highly restrictive. What I'm suggesting is that
    the opposite is true.

    I'm going to mention Rust again; apologies. When confined to
    the safe subset, it _also_ has data race freedom. The rules
    that give this property are:

    1. Every object has exactly one owner, and assignments of
    non-trivial types change ownership (they are logically a
    "move");
    2. References to an object may be "borrowed" from the owner,
    and mutable references (that is, references that may be used
    to write to the object) are distinct from immutable
    references (that is, references that may only be used to read
    from an object);
    3. Mutable and immutable references are temporally mutually
    exclusive: a mutable reference may be borrowed from an object
    iff that is the only live reference to that object at the
    time: that is, it is not permitted to borrow a mut ref to an
    object if either another mut ref or any immutable refs to it
    are live; any number of immutable references may be taken to
    an object concurrently.

    If all of these rules are obeyed (and in safe rust, they're
    verified at compile time by the borrow checker) then you cannot
    have data races.

    That's all good, by the sound of it. But if people find this gets in
    the way of efficiency - perhaps because they know that it is safe to
    hold two separate mutable references to an object for reasons the borrow >checker can't see - the temptation to use "unsafe" or other workarounds >comes quickly.

    Not really. The rule is that borrowing a mutable reference to
    some object is mutually exclusive with borrowing any other kind
    of reference to that same object at the same time. `unsafe`
    doesn't change that rules, or let the programmer off the hook
    for violating it: `unsafe` code isn't allowed to mix mut ref
    with other references any more than _safe_ code. All `unsafe`
    means is that the burden of upholding the rules of the language
    falls to the programmer, without compiler assistence, because
    the programmer knows something that the compiler does not (and
    probably cannot) for some reason.

    However, if it really _is_ the case that multiple simultaneous
    writes are ok, the ability to create new types and assign them
    semantics and behavior menas that a programmer can build a _new_
    abstraction that lets them do something similar. Atomics are
    an interesting case in point: one can store to them using a
    non-mutable reference: while this seems counter-intuitive at
    first, it sort of makes sense for the same reason that a `Mutex`
    is accessed via an immutable reference: one cannot observe an
    atomic in an intermediate state due to an update, and `load` and
    `store` are explicit operations (in practice, the compiler
    lowers those to single load/store operations and whatever
    barriers are appropriate for the target architecture).

    A hardware device modeling a framebuffer might be another case;
    those are output-only devices, and there's no reason that two
    threads cannot write to different parts of the buffer at the
    same time, but given a suitable interface, the interface doesn't
    need to expose unsafety. And if you've got a rich type system,
    you have the rules to build that interface.

    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that
    as much code as possible is correct, or at least immune to particular >classes of bugs, checked by the compiler and tools.

    I think you're saying something different than what I'm saying;
    my point is that the safe interface shouldn't be seen as a
    burden and, if well-executed, it shouldn't add a runtime tax in
    terms of reduced performance. You seem to be taking it as a
    given that the rules of the language will make both of those
    things true, however.

    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    Yes. And moreover, one can hide that unsafety behind a safe
    interface.

    - Dan C.


    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From tTh@3:633/10 to All on Sun May 24 22:09:21 2026
    On 5/24/26 13:30, Bart wrote:

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header, which
    are combined into a 235Kloc source file).

    I don't understand your obsession with ?combining? different
    sources into a single file. I'm no C expert, but I've been
    coding for 40 years, so let's take an example (simplified here)
    from my real-life experience as a programmer:

    /* here the various classic includes */
    static unsigned count;
    void count_increment()
    {
    count++;
    }
    void count_display()
    {
    printf("count is %ld\n", count);
    }
    /* just a little compil unit :) */

    If you "combine" that _independent_ code in an huge
    single file, and there was more than one compilation
    unit who use a static variable named count, two things
    can happen :
    a) your friend the compiler is barfing.
    b) nasal daemons are not accurate.

    choise you toxicity. I'm very curious...


    --
    ** **
    * tTh des Bourtoulots *
    * http://maison.tth.netlib.re/ *
    ** **

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bart@3:633/10 to All on Sun May 24 21:48:33 2026
    On 24/05/2026 21:09, tTh wrote:
    On 5/24/26 13:30, Bart wrote:

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header,
    which are combined into a 235Kloc source file).

    ÿÿ I don't understand your obsession with ?combining? different
    ÿÿ sources into a single file.

    It's not me doing it. sqlite3.c is an amalgamation of over 100 C files
    created by the SQLite developers. It's done to simplify embedding within
    your own applications. That file itself was already 220Kloc.

    'shell.c' is a program which can be used to create a standalone SQLite
    app, and it was convenient to combine it with sqlite2.c for test purposes.

    Other amalgamations I use include Lua 5.4 as one source file, again to
    simply embedding; somebody else did that too, so complain to them: https://github.com/edubart/minilua

    There are actually lots of one-file C and C++ libraries about; some
    might only be one file anyway, some might involve combining. For example stb_image.h.


    I'm no C expert, but I've been
    ÿÿ coding for 40 years, so let's take an example (simplified here)
    ÿÿ from my real-life experience as a programmer:

    /* here the various classic includes */
    static unsigned count;
    void count_increment()
    {
    count++;
    }
    void count_display()
    {
    printf("count is %ld\n", count);
    }
    /* just a little compil unit :) */

    ÿÿ If you "combine" that _independent_ code in an huge
    ÿÿ single file, and there was more than one compilation
    ÿÿ unit who use a static variable named count, two things
    ÿÿ can happen :
    ÿÿÿÿÿ a) your friend the compiler is barfing.
    ÿÿÿÿÿ b) nasal daemons are not accurate.

    Yeah, 'combining' doesn't mean just blindly concatenating a bunch of C
    source files. Various approaches are used; I don't know because I've
    never done it.

    My own single-file C programs are machine-generated by a transpiler,
    from non-C sources in another language, and it will take care of all
    those details.

    So if modules A and B both have a local called 'count', that might be
    renamed to A_count and B_count.



    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Sun May 24 22:04:52 2026
    Bart <bc@freeuk.com> writes:
    On 24/05/2026 21:09, tTh wrote:
    On 5/24/26 13:30, Bart wrote:

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header,
    which are combined into a 235Kloc source file).

    ÿÿ I don't understand your obsession with ?combining? different
    ÿÿ sources into a single file.

    It's not me doing it. sqlite3.c is an amalgamation of over 100 C files >created by the SQLite developers. It's done to simplify embedding within >your own applications. That file itself was already 220Kloc.

    It's useful to note that the developers of SQLite work with
    those 100 C files, not with the single amalgamtion.

    sqlite3.c amalgamation is a distribution vehicle for
    people who want the library embedded in their code (rare, as
    most will simply link with their favorite distributions
    sqlite package). Primarily useful for those who need a
    lightweight database in e.g. a firmware image, or small
    kernel. They even offer one using autotools, which Bart
    detests.

    The full source tree archive can be downloaded for those
    building packages in distributions, or wishing to modify
    the code in some fashion.

    --- PyGate Linux v1.5.14
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to Unknown on Mon May 25 10:34:48 2026
    On 24/05/2026 22:09, tTh wrote:
    On 5/24/26 13:30, Bart wrote:

    I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header,
    which are combined into a 235Kloc source file).

    ÿÿ I don't understand your obsession with ?combining? different
    ÿÿ sources into a single file. I'm no C expert, but I've been
    ÿÿ coding for 40 years, so let's take an example (simplified here)
    ÿÿ from my real-life experience as a programmer:

    /* here the various classic includes */
    static unsigned count;
    void count_increment()
    {
    count++;
    }
    void count_display()
    {
    printf("count is %ld\n", count);
    }
    /* just a little compil unit :) */

    ÿÿ If you "combine" that _independent_ code in an huge
    ÿÿ single file, and there was more than one compilation
    ÿÿ unit who use a static variable named count, two things
    ÿÿ can happen :
    ÿÿÿÿÿ a) your friend the compiler is barfing.
    ÿÿÿÿÿ b) nasal daemons are not accurate.

    ÿÿ choise you toxicity. I'm very curious...



    Combining files does not mean simply jumbling them all in one big file. (Though it is possible to write code in a way for that to be possible,
    and maybe the sqlite folks have done that.)

    /Very/ roughly speaking, you start by taking all the #include files
    mentioned in your other files. If these are well-written, with guards
    and with their own #include files so that ordering and duplication does
    not matter when used in "normal" C code, it should not be too difficult
    to paste together from these in a single group. Then you take the C
    files, and for every file-scope static declaration, you pre-pend or
    append the filename or other unique value to the name. Thus "static
    unsigned count;" becomes "static unsigned compil_unit_count;". Type definitions (typedef, struct, enum, etc.) will need similar treatment if
    there are multiple conflicting definitions, and de-duplication if there
    are multiple identical definitions.

    If the code is more complicated, with macros being re-defined and such
    like then you might be better off pre-processing all the files before
    trying to join them.

    If code is written with a view to joining to a single distribution file,
    this should all be straightforward and easily automated. If it is
    general code, it's a lot of work. But there are no doubt tools
    available to help.


    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to Unknown on Mon May 25 12:16:03 2026
    On 24/05/2026 21:08, Dan Cross wrote:
    In article <10uprpn$1hfpm$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 22/05/2026 16:44, Dan Cross wrote:
    In article <10un0j7$obiv$2@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    There is definitely potential for a language's type system to make it
    harder to make some kinds of mistakes. But there is a risk in making
    the language too restrictive - people end up writing horrible code to
    work around restrictions, or use "unsafe" code too much.

    Interesting. I've found it to be somewhat the opposite; using a
    richer type system has lead to code that is easier to understand
    and reason about, and less buggy: type-oriented programming can
    make entire categories of errors *unrepresentable*, so it's not
    just _harder_ to make certain kinds of mistakes, but
    _impossible_. The existance of an object of some type can be
    thought of as an existence proof that the invariants the type
    represents hold.

    In general, I agree - I prefer strongly typed languages, and it's always
    best if an error is identified by the code being uncompilable rather
    than waiting for run-time testing (or testing by evil hackers). But if
    people want to do something with the language that is hard to achieve
    efficiently within the norms of the language, and there are escape
    hatches ("unsafe" code, inline assembly, calling external C functions,
    etc.) then people will use them.

    Sure; that seems pretty clear, given extensive available
    evidence.

    People do sometimes write C code that
    knowingly depends on undefined behaviour, because it makes their results
    more efficient and "it worked when I tested it".

    Yes, or they force some knob on their compiler into the required
    position to give them guaranteed results. Linux does this, for
    example; last time I worked in it, Google's massive (2BLOC) code
    base similarly.


    What you then get is code that is UB in C, but not UB in C-with-no-strict-aliasing, or whatever "augmented" language you pick.
    (I have a strict policy of putting any such knobs in "GCC optimize"
    pragmas, so that the code correctness does not depend on the flags
    picked in a makefile. Of course such code remains equally limited in
    its portability.)

    Type safety - like any kind of safety - can sometimes get in the way of
    "getting things done". A balance always needs to be found to ensure
    that people can do what they need to do without breaking rules or using
    "escapes" more than absolutely necessary. If people find that much of
    their Rust code is "unsafe", then most of the point of using Rust is
    lost. (Of course this also means that different languages are suited to
    different tasks.)

    It _can_, but I wouldn't take it as a given that it _does_, and
    what I'm trying to say is that contrary to often getting in the
    way, it can be used effectively to make programs better and
    safer, with no runtime downside. Indeed, a well-typed program
    can be faster than the alternative, since a) the compiler can
    prove that some properties hold, and b) it can be more
    aggressively optimized at a higher level. An example here are
    non-nullable references; there's no need to check whether they
    are NULL or not before indirecting through them, since by
    definition they cannot be NULL.


    I fully agree.



    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an
    indication that the restrictions inherent in the language and tools made
    them somewhat unsuitable for a lot of use-cases that fitted the hardware
    well. (Modern XMOS tools have changed significantly since those days,
    perhaps partly because of such issues.)

    Sure, but that's a failure of that system to provide good
    abstractions: the system was not rich enough to provide the
    functionality you needed or wanted.

    I think the central tension here is that the idea of a strong
    type system that is also semantically rich is being conflated
    with one that's highly restrictive. What I'm suggesting is that
    the opposite is true.

    To be clear - the restrictions here were not part of the type system.
    The XC language did have some differences in typing from C, if I
    remember the details correctly - rather than C pointers it had
    references which could not be null. And that's fine.

    The restrictions were enforced by whole-program analysis. I think (and
    you are the Rust expert here, not me) that the Rust borrow checker is
    similar. The rules of the language specify aspects of what can and
    cannot be done with access to data, and these are enforced at a higher
    level than the main compilation. They are language rules, but not part
    of the type system.


    I'm going to mention Rust again; apologies. When confined to
    the safe subset, it _also_ has data race freedom. The rules
    that give this property are:

    1. Every object has exactly one owner, and assignments of
    non-trivial types change ownership (they are logically a
    "move");
    2. References to an object may be "borrowed" from the owner,
    and mutable references (that is, references that may be used
    to write to the object) are distinct from immutable
    references (that is, references that may only be used to read
    from an object);
    3. Mutable and immutable references are temporally mutually
    exclusive: a mutable reference may be borrowed from an object
    iff that is the only live reference to that object at the
    time: that is, it is not permitted to borrow a mut ref to an
    object if either another mut ref or any immutable refs to it
    are live; any number of immutable references may be taken to
    an object concurrently.

    If all of these rules are obeyed (and in safe rust, they're
    verified at compile time by the borrow checker) then you cannot
    have data races.

    That's all good, by the sound of it. But if people find this gets in
    the way of efficiency - perhaps because they know that it is safe to
    hold two separate mutable references to an object for reasons the borrow
    checker can't see - the temptation to use "unsafe" or other workarounds
    comes quickly.

    Not really. The rule is that borrowing a mutable reference to
    some object is mutually exclusive with borrowing any other kind
    of reference to that same object at the same time. `unsafe`
    doesn't change that rules, or let the programmer off the hook
    for violating it: `unsafe` code isn't allowed to mix mut ref
    with other references any more than _safe_ code. All `unsafe`
    means is that the burden of upholding the rules of the language
    falls to the programmer, without compiler assistence, because
    the programmer knows something that the compiler does not (and
    probably cannot) for some reason.

    That is roughly what I was trying to express, although there is perhaps
    a grey area between "I am following the rules even though the tools
    can't see it" and "I am breaking the rules but I know it is safe to do
    so here without compromising the reason for those rules".

    In C (trying desperately to bring us back to c.l.c. :-) ), you might use
    casts to do something that looks wrong to the compiler, but which you
    know is safe and correct.


    However, if it really _is_ the case that multiple simultaneous
    writes are ok, the ability to create new types and assign them
    semantics and behavior menas that a programmer can build a _new_
    abstraction that lets them do something similar. Atomics are
    an interesting case in point: one can store to them using a
    non-mutable reference: while this seems counter-intuitive at
    first, it sort of makes sense for the same reason that a `Mutex`
    is accessed via an immutable reference: one cannot observe an
    atomic in an intermediate state due to an update, and `load` and
    `store` are explicit operations (in practice, the compiler
    lowers those to single load/store operations and whatever
    barriers are appropriate for the target architecture).

    OK. Though that is probably a level of detail that would be clearer to
    me if I read the Rust documentation first!


    A hardware device modeling a framebuffer might be another case;
    those are output-only devices, and there's no reason that two
    threads cannot write to different parts of the buffer at the
    same time, but given a suitable interface, the interface doesn't
    need to expose unsafety. And if you've got a rich type system,
    you have the rules to build that interface.

    That is the kind of thing that was difficult in XC. Perhaps it would be
    fair to say that its type system was not powerful enough to work
    conveniently with its data race and thread safety checking systems.


    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that
    as much code as possible is correct, or at least immune to particular
    classes of bugs, checked by the compiler and tools.

    I think you're saying something different than what I'm saying;
    my point is that the safe interface shouldn't be seen as a
    burden and, if well-executed, it shouldn't add a runtime tax in
    terms of reduced performance. You seem to be taking it as a
    given that the rules of the language will make both of those
    things true, however.

    I am saying that a safe interface /can/ sometimes be a burden - not that
    it has to be.

    An interface defines what can be done, and also what cannot be done.
    It's power as an interface comes from both of these - letting you do
    useful things, and preventing you from doing harmful things. Sometimes, however, there may be things you want to do that you know are useful and
    not harmful, but that the interface disallows. If the language or
    interface is well designed, and a good fit for the tasks people want to
    do with the language, then such situations will be minimal. But I think
    it is quite easy to fall into a trap when designing an interface where
    you exclude too many useful cases. If that happens, then programmers
    have to use riskier or less efficient workarounds, or find alternative interfaces (or languages).

    As a concrete example, I recently wanted to use a std::variant<> in my
    C++ code. A std::variant<> is, approximately, a struct like :

    struct {
    enum { ... } type_tag;
    union { ... } contents;
    }

    In C++, this is all type-safe - you don't have direct access to these
    fields, but instead they are kept consistent automatically so that
    objects of different types can be stored in the union and have their constructors and destructors called correctly. But in my case, I wanted
    to access the fields independently - I wanted to fill the "contents"
    with data retrieved over a network, and set the "type_tag" according to
    other data in the network packet. I knew this was safe (since no
    constructors or destructors were needed). But there was no way to
    handle this within the interface. My options included a risky,
    non-portable and difficult workaround (digging through the <variant>
    header and directly "hacking" the variant object using unsigned char* pointers), serious run-time inefficiencies (with extra copies of the
    data), or finding a different interface. I opted for the last one,
    making my own simple variation of std::variant that gave me the access I needed.

    This, of course, does not necessarily mean the interface of
    std::variant<> is bad. If my needs were highly unusual, then a standard library does not need to support them. But it is an example where the restrictions imposed by a safe, powerful interface limited how I could
    work with what is essentially the same kind of object.



    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    Yes. And moreover, one can hide that unsafety behind a safe
    interface.


    I do like that the unsafety is marked explicitly and clearly.


    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Mon May 25 07:39:28 2026
    [I am writing a single response on one topic that was being
    discussed in two posts in parallel sub-threads. Some
    extraneous material removed. Responding to:]

    scott@slp53.sl.home (Scott Lurndal) writes:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <Xj%PR.313884$yHZ7.10738@fx10.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    [example of conditional operator: a = cond ? x : y; ]

    In fact, I find the ternary operator to be used rather
    sparingly, even in the [example shown above].

    I wouldn't necessarily categorize it as "sparingly"; Searching
    through the simulator source base, I find it used rather
    frequently in certain scenarios:

    diag = snprintf(cp, remaining, "Feature1:%c Feature2:%c",
    is_feature1()?'+':'-', is_feature2()?'+':'-');

    deliver_interrupt(IntVec_TIMER, is_secure()?FLAGS_SECURE:0u);

    Those are the kinds of scenarios where it can be useful; what
    percentage of code overall uses that versus `if`, though?

    Other than the cases above, 'if' dominates by orders of magnitude.

    [and to:]

    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    In article <10ure81$1s756$1@dont-email.me>,
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2026-05-22 16:57, Dan Cross wrote:

    In fact, I find the ternary operator to be used rather
    sparingly, even in the [same example as before].

    I haven't got that impression.

    Some piece of software (it's just a random example, to be clear)
    that has
    130 C-files
    250249 total LOC
    shows
    3864 lines with ?: appearing
    I think this is a lot!

    That's about 1.5% of source lines. I'd say that's pretty
    sparingly used.

    It wouldn't surprise me to find that 'if' dominates '?:' by two
    orders of magnitude in some code bases, or even many code bases.
    Probably someone has produced some statistics on such things,
    although off the top of my head I'm not aware of any (but I should
    add that I haven't looked either).

    On the other hand, I am confident that the numbers vary a lot
    depending on programming style. I have become accustomed to writing
    code in a mostly functional style, even in conventional imperative
    languages like C. Out of curiosity I tabulated some numbers from a
    project I am working on now, just for constructs related to transfer
    of control. As is my custom I calculate ratios not as a percentage
    of total source lines but as a percentage of lines inside function
    boundaries. (Looking at different code bases I see that the ratio
    of function body lines to total source lines can easily vary by a
    factor of two or more between code bases; hence I think percentage
    of function body lines gives a better measure.) Here are the
    results:

    goto/continue 0.3%
    for 0.7%
    switch 0.7%
    break 0.8% (all of these are from switch cases)
    while 2.3% (0.7% do-while / 1.6% plain while)
    if 8.1% (2.0% multi-line / 6.1% single line)
    ?: 14.3%
    return 21.4%

    Of course I'm not claiming that these results are in any way
    representative. In fact at some level that is the point - actual
    ratios and relative percentages can vary widely, depending on
    programming style. Beyond that I'm not offering any conclusions;
    I just thought people might be interested in some additional data
    points.

    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Tue May 26 14:09:16 2026
    In article <10v17h4$18mp3$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 24/05/2026 21:08, Dan Cross wrote:
    In article <10uprpn$1hfpm$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    People do sometimes write C code that
    knowingly depends on undefined behaviour, because it makes their results >>> more efficient and "it worked when I tested it".

    Yes, or they force some knob on their compiler into the required
    position to give them guaranteed results. Linux does this, for
    example; last time I worked in it, Google's massive (2BLOC) code
    base similarly.

    What you then get is code that is UB in C, but not UB in >C-with-no-strict-aliasing, or whatever "augmented" language you pick.

    Yes. An observation pointed out to me at my last gig, and that
    I now point out occasionally myself, is that Linux (for example)
    is not so much written in C but rather in Linux C, which is the
    dialect of the language defined by the compilers they use and
    the specific behaviors they force using flags, pragmas, etc, for
    those compilers. At any rate, it's certainly not strictly
    conforming ISO C: is _any_ large program strictly conforming at
    this point? I suspect many projects strive for such, but few
    actually attain it.

    (I have a strict policy of putting any such knobs in "GCC optimize"
    pragmas, so that the code correctness does not depend on the flags
    picked in a makefile. Of course such code remains equally limited in
    its portability.)

    Good idea, though I like the idea of the compiler failing with a
    usage error if an option is no longer available (or someone is
    trying to build using an old compiler or what have you).

    [snip]
    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an
    indication that the restrictions inherent in the language and tools made >>> them somewhat unsuitable for a lot of use-cases that fitted the hardware >>> well. (Modern XMOS tools have changed significantly since those days,
    perhaps partly because of such issues.)

    Sure, but that's a failure of that system to provide good
    abstractions: the system was not rich enough to provide the
    functionality you needed or wanted.

    I think the central tension here is that the idea of a strong
    type system that is also semantically rich is being conflated
    with one that's highly restrictive. What I'm suggesting is that
    the opposite is true.

    To be clear - the restrictions here were not part of the type system.
    The XC language did have some differences in typing from C, if I
    remember the details correctly - rather than C pointers it had
    references which could not be null. And that's fine.

    The restrictions were enforced by whole-program analysis. I think (and
    you are the Rust expert here, not me) that the Rust borrow checker is >similar. The rules of the language specify aspects of what can and
    cannot be done with access to data, and these are enforced at a higher
    level than the main compilation. They are language rules, but not part
    of the type system.

    Sorry, let me be clear here: the _language_ is not expressive
    enough to provide the kinds of abstractions that would be useful
    for the types of applications the hardware seems well-suited
    for.

    But the original context was types; my point was that a richer
    langauge can provide the building blocks so that one can build a
    safe abstraction around those kinds of behaviors, even if one
    uses `unsafe` in the implementation of those abstractions.

    This was meant in response to your earlier statement, that e.g.
    performance might cause one to want to reach for `unsafe` to
    work _around_ the language; my point is that the language gives
    you tools to work _with_ it. That is, the initial desire to
    reach for `unsafe` is _reduced_ by the ability to effectively
    hide it.

    Implicit in this is a distinction between building what one
    might call infrastructure within one's program, and using that
    infrastructure: the former might involve some controlled (and
    hopefully limited) use of `unsafe`, while the latter ideally
    does not. I find this is the opposite of what many people
    assume when coming from other language backgrounds, where the
    desire is to push `unsafe` to the point of use; often this is
    because in other languages, building up those abstractions
    requires a lot of machinery with high runtime costs.

    [snip]
    Not really. The rule is that borrowing a mutable reference to
    some object is mutually exclusive with borrowing any other kind
    of reference to that same object at the same time. `unsafe`
    doesn't change that rules, or let the programmer off the hook
    for violating it: `unsafe` code isn't allowed to mix mut ref
    with other references any more than _safe_ code. All `unsafe`
    means is that the burden of upholding the rules of the language
    falls to the programmer, without compiler assistence, because
    the programmer knows something that the compiler does not (and
    probably cannot) for some reason.

    That is roughly what I was trying to express, although there is perhaps
    a grey area between "I am following the rules even though the tools
    can't see it" and "I am breaking the rules but I know it is safe to do
    so here without compromising the reason for those rules".

    That's what I'm saying; a program really _doesn't_ get to break
    the rules and retain any guarantee of behavior, unsafe or not.
    In that sense, writing `unsafe` Rust code is _harder_ than
    writing C or another unsafe language. There is no grey area:
    either the programmer makes sure the program follows the rules,
    or all bets are off.

    In C (trying desperately to bring us back to c.l.c. :-) ), you might use >casts to do something that looks wrong to the compiler, but which you
    know is safe and correct.

    That's qualitatively different, though. If, say, I manually
    validate that a pointer points to valid memory, and is properly
    aligned, and so forth, then I can cast that pointer to some
    type. You can absolutely do that in Rust, too. But if,
    instead, you say, "I'm going to intentionally invoke UB here..."
    then both languages will bite you.

    However, if it really _is_ the case that multiple simultaneous
    writes are ok, the ability to create new types and assign them
    semantics and behavior menas that a programmer can build a _new_
    abstraction that lets them do something similar. Atomics are
    an interesting case in point: one can store to them using a
    non-mutable reference: while this seems counter-intuitive at
    first, it sort of makes sense for the same reason that a `Mutex`
    is accessed via an immutable reference: one cannot observe an
    atomic in an intermediate state due to an update, and `load` and
    `store` are explicit operations (in practice, the compiler
    lowers those to single load/store operations and whatever
    barriers are appropriate for the target architecture).

    OK. Though that is probably a level of detail that would be clearer to
    me if I read the Rust documentation first!

    A hardware device modeling a framebuffer might be another case;
    those are output-only devices, and there's no reason that two
    threads cannot write to different parts of the buffer at the
    same time, but given a suitable interface, the interface doesn't
    need to expose unsafety. And if you've got a rich type system,
    you have the rules to build that interface.

    That is the kind of thing that was difficult in XC. Perhaps it would be >fair to say that its type system was not powerful enough to work >conveniently with its data race and thread safety checking systems.

    Yes. It also doesn't give you any means to build an abstraction
    that lets you do the thing you want to do.

    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that
    as much code as possible is correct, or at least immune to particular
    classes of bugs, checked by the compiler and tools.

    I think you're saying something different than what I'm saying;
    my point is that the safe interface shouldn't be seen as a
    burden and, if well-executed, it shouldn't add a runtime tax in
    terms of reduced performance. You seem to be taking it as a
    given that the rules of the language will make both of those
    things true, however.

    I am saying that a safe interface /can/ sometimes be a burden - not that
    it has to be.

    Well yes, of course; I thought that was a given in the context
    of this discussion.

    An interface defines what can be done, and also what cannot be done.
    It's power as an interface comes from both of these - letting you do
    useful things, and preventing you from doing harmful things. Sometimes, >however, there may be things you want to do that you know are useful and
    not harmful, but that the interface disallows. If the language or
    interface is well designed, and a good fit for the tasks people want to
    do with the language, then such situations will be minimal. But I think
    it is quite easy to fall into a trap when designing an interface where
    you exclude too many useful cases. If that happens, then programmers
    have to use riskier or less efficient workarounds, or find alternative >interfaces (or languages).

    Yes. And what I'm saying is that when working with a language
    that allows you to create useful, robust, types, you can often
    bridge the two worlds and build a new, useful abstraction. C is
    not a language where one can easily do that; you've got structs,
    unions, and functions...and that's about it. But you cannot
    restrict the behavior of a struct in the way that King described
    in the "Parse, Don't Validate" piece I linked earlier.

    An instance of a struct is Not a proof that some property holds,
    but it _may_ be in Rust or Haskell or OCaml, SML, etc. I
    suspect one cannot easily do that in C++, because it's not
    managed and doesn't make ownership a first-class property,
    though one can usefully write a number of classes and so forth
    that give much the same effect, if not actual guarantees.
    Interestingly (maybe?), I find Ada lacking in this area.

    As a concrete example, I recently wanted to use a std::variant<> in my
    C++ code. A std::variant<> is, approximately, a struct like :

    struct {
    enum { ... } type_tag;
    union { ... } contents;
    }

    In C++, this is all type-safe - you don't have direct access to these >fields, but instead they are kept consistent automatically so that
    objects of different types can be stored in the union and have their >constructors and destructors called correctly. But in my case, I wanted
    to access the fields independently - I wanted to fill the "contents"
    with data retrieved over a network, and set the "type_tag" according to >other data in the network packet. I knew this was safe (since no >constructors or destructors were needed). But there was no way to
    handle this within the interface. My options included a risky,
    non-portable and difficult workaround (digging through the <variant>
    header and directly "hacking" the variant object using unsigned char* >pointers), serious run-time inefficiencies (with extra copies of the
    data), or finding a different interface. I opted for the last one,
    making my own simple variation of std::variant that gave me the access I >needed.

    This, of course, does not necessarily mean the interface of
    std::variant<> is bad. If my needs were highly unusual, then a standard >library does not need to support them. But it is an example where the >restrictions imposed by a safe, powerful interface limited how I could
    work with what is essentially the same kind of object.

    Well, sure, that's the danger of general libraries: you're
    getting something that is a decent combination of functionality
    and expressiveness contrasted with safety for many needs, but
    (almost by definition) not for all needs. As you point out,
    that is useful for many cases, though.

    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    Yes. And moreover, one can hide that unsafety behind a safe
    interface.

    I do like that the unsafety is marked explicitly and clearly.

    It goes beyond that, though: _if_ the programmer is successful
    in providing a safe abstraction around the unsafety, then it is
    impossible to compromise the memory safety of the program by
    using that abstraction. Naturally, "if" is doing a lot of work
    here: as I said, the burden for writing `unsafe` code is higher
    in Rust than it is in C, and it requires a lot of care. But the
    resulting guarantees are much stronger.

    - Dan C.


    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Tue May 26 18:16:23 2026
    On 26/05/2026 16:09, Dan Cross wrote:

    (The posts here are getting long, and I think significantly off-topic
    for c.l.c. I am reading your posts with interest and appreciation, but
    I am afraid it is getting too time-consuming to do justice to them in
    replies. I've given a somewhat terse reply here, though an even terser version would be "I agree with most of it, you've given me lots of
    things to think about, and I really need to learn Rust :-)". I will
    re-read the post later and may reply more then.)

    In article <10v17h4$18mp3$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    On 24/05/2026 21:08, Dan Cross wrote:
    In article <10uprpn$1hfpm$1@dont-email.me>,
    David Brown <david.brown@hesbynett.no> wrote:
    [snip]
    People do sometimes write C code that
    knowingly depends on undefined behaviour, because it makes their results >>>> more efficient and "it worked when I tested it".

    Yes, or they force some knob on their compiler into the required
    position to give them guaranteed results. Linux does this, for
    example; last time I worked in it, Google's massive (2BLOC) code
    base similarly.

    What you then get is code that is UB in C, but not UB in
    C-with-no-strict-aliasing, or whatever "augmented" language you pick.

    Yes. An observation pointed out to me at my last gig, and that
    I now point out occasionally myself, is that Linux (for example)
    is not so much written in C but rather in Linux C, which is the
    dialect of the language defined by the compilers they use and
    the specific behaviors they force using flags, pragmas, etc, for
    those compilers. At any rate, it's certainly not strictly
    conforming ISO C: is _any_ large program strictly conforming at
    this point? I suspect many projects strive for such, but few
    actually attain it.


    I think a lot of code is fully portable C, but very few (if any) real
    programs are. Even if they don't contain compiler-specific
    requirements, they will generally depend on particular
    implementation-specific behaviours or non-standard libraries and headers.

    (I have a strict policy of putting any such knobs in "GCC optimize"
    pragmas, so that the code correctness does not depend on the flags
    picked in a makefile. Of course such code remains equally limited in
    its portability.)

    Good idea, though I like the idea of the compiler failing with a
    usage error if an option is no longer available (or someone is
    trying to build using an old compiler or what have you).


    You should get an error (in gcc) for a "#pragma GCC optimize" that is
    not supported. On the other hand, if you misspell it as "#pragma GCC optimise", it will be ignored unless you have "-Wunknown-pragmas" or
    "-Wall" enabled. And other compilers could silently ignore the GCC
    optimize pragmas.



    [snip]
    Hmm. This reads less like an indictment of the idea of stronger
    typing, but rather a failure to provide adequate abstractions in
    the type system.

    It is not an indication of problems with stronger typing, but it is an >>>> indication that the restrictions inherent in the language and tools made >>>> them somewhat unsuitable for a lot of use-cases that fitted the hardware >>>> well. (Modern XMOS tools have changed significantly since those days, >>>> perhaps partly because of such issues.)

    Sure, but that's a failure of that system to provide good
    abstractions: the system was not rich enough to provide the
    functionality you needed or wanted.

    I think the central tension here is that the idea of a strong
    type system that is also semantically rich is being conflated
    with one that's highly restrictive. What I'm suggesting is that
    the opposite is true.

    To be clear - the restrictions here were not part of the type system.
    The XC language did have some differences in typing from C, if I
    remember the details correctly - rather than C pointers it had
    references which could not be null. And that's fine.

    The restrictions were enforced by whole-program analysis. I think (and
    you are the Rust expert here, not me) that the Rust borrow checker is
    similar. The rules of the language specify aspects of what can and
    cannot be done with access to data, and these are enforced at a higher
    level than the main compilation. They are language rules, but not part
    of the type system.

    Sorry, let me be clear here: the _language_ is not expressive
    enough to provide the kinds of abstractions that would be useful
    for the types of applications the hardware seems well-suited
    for.

    Agreed.


    But the original context was types; my point was that a richer
    langauge can provide the building blocks so that one can build a
    safe abstraction around those kinds of behaviors, even if one
    uses `unsafe` in the implementation of those abstractions.

    This was meant in response to your earlier statement, that e.g.
    performance might cause one to want to reach for `unsafe` to
    work _around_ the language; my point is that the language gives
    you tools to work _with_ it. That is, the initial desire to
    reach for `unsafe` is _reduced_ by the ability to effectively
    hide it.

    Fair enough.


    Implicit in this is a distinction between building what one
    might call infrastructure within one's program, and using that infrastructure: the former might involve some controlled (and
    hopefully limited) use of `unsafe`, while the latter ideally
    does not. I find this is the opposite of what many people
    assume when coming from other language backgrounds, where the
    desire is to push `unsafe` to the point of use; often this is
    because in other languages, building up those abstractions
    requires a lot of machinery with high runtime costs.


    I believe we can agree that the ideal should be that "unsafe", or
    equivalent, should not be needed in most code. My original point was
    just that trying too hard to make a language "safe" without enough care
    for how it will be used can push people towards "unsafe" alternatives.
    But if suitable care is taken, then the result is less unsafe code and
    an overall better language for the task.

    [snip]
    Not really. The rule is that borrowing a mutable reference to
    some object is mutually exclusive with borrowing any other kind
    of reference to that same object at the same time. `unsafe`
    doesn't change that rules, or let the programmer off the hook
    for violating it: `unsafe` code isn't allowed to mix mut ref
    with other references any more than _safe_ code. All `unsafe`
    means is that the burden of upholding the rules of the language
    falls to the programmer, without compiler assistence, because
    the programmer knows something that the compiler does not (and
    probably cannot) for some reason.

    That is roughly what I was trying to express, although there is perhaps
    a grey area between "I am following the rules even though the tools
    can't see it" and "I am breaking the rules but I know it is safe to do
    so here without compromising the reason for those rules".

    That's what I'm saying; a program really _doesn't_ get to break
    the rules and retain any guarantee of behavior, unsafe or not.
    In that sense, writing `unsafe` Rust code is _harder_ than
    writing C or another unsafe language. There is no grey area:
    either the programmer makes sure the program follows the rules,
    or all bets are off.

    In C (trying desperately to bring us back to c.l.c. :-) ), you might use
    casts to do something that looks wrong to the compiler, but which you
    know is safe and correct.

    That's qualitatively different, though. If, say, I manually
    validate that a pointer points to valid memory, and is properly
    aligned, and so forth, then I can cast that pointer to some
    type. You can absolutely do that in Rust, too. But if,
    instead, you say, "I'm going to intentionally invoke UB here..."
    then both languages will bite you.

    However, if it really _is_ the case that multiple simultaneous
    writes are ok, the ability to create new types and assign them
    semantics and behavior menas that a programmer can build a _new_
    abstraction that lets them do something similar. Atomics are
    an interesting case in point: one can store to them using a
    non-mutable reference: while this seems counter-intuitive at
    first, it sort of makes sense for the same reason that a `Mutex`
    is accessed via an immutable reference: one cannot observe an
    atomic in an intermediate state due to an update, and `load` and
    `store` are explicit operations (in practice, the compiler
    lowers those to single load/store operations and whatever
    barriers are appropriate for the target architecture).

    OK. Though that is probably a level of detail that would be clearer to
    me if I read the Rust documentation first!

    A hardware device modeling a framebuffer might be another case;
    those are output-only devices, and there's no reason that two
    threads cannot write to different parts of the buffer at the
    same time, but given a suitable interface, the interface doesn't
    need to expose unsafety. And if you've got a rich type system,
    you have the rules to build that interface.

    That is the kind of thing that was difficult in XC. Perhaps it would be
    fair to say that its type system was not powerful enough to work
    conveniently with its data race and thread safety checking systems.

    Yes. It also doesn't give you any means to build an abstraction
    that lets you do the thing you want to do.


    Indeed. (And again, let me note that this was my experience with XC a
    long time ago - the tools have changed since then.)

    That does not mean that a language should not try to have such rules,
    and enforce them at compile time - far from it. The aim should be that >>>> as much code as possible is correct, or at least immune to particular
    classes of bugs, checked by the compiler and tools.

    I think you're saying something different than what I'm saying;
    my point is that the safe interface shouldn't be seen as a
    burden and, if well-executed, it shouldn't add a runtime tax in
    terms of reduced performance. You seem to be taking it as a
    given that the rules of the language will make both of those
    things true, however.

    I am saying that a safe interface /can/ sometimes be a burden - not that
    it has to be.

    Well yes, of course; I thought that was a given in the context
    of this discussion.

    An interface defines what can be done, and also what cannot be done.
    It's power as an interface comes from both of these - letting you do
    useful things, and preventing you from doing harmful things. Sometimes,
    however, there may be things you want to do that you know are useful and
    not harmful, but that the interface disallows. If the language or
    interface is well designed, and a good fit for the tasks people want to
    do with the language, then such situations will be minimal. But I think
    it is quite easy to fall into a trap when designing an interface where
    you exclude too many useful cases. If that happens, then programmers
    have to use riskier or less efficient workarounds, or find alternative
    interfaces (or languages).

    Yes. And what I'm saying is that when working with a language
    that allows you to create useful, robust, types, you can often
    bridge the two worlds and build a new, useful abstraction. C is
    not a language where one can easily do that; you've got structs,
    unions, and functions...and that's about it. But you cannot
    restrict the behavior of a struct in the way that King described
    in the "Parse, Don't Validate" piece I linked earlier.


    Indeed. C is not the language of choice for that sort of thing.

    An instance of a struct is Not a proof that some property holds,
    but it _may_ be in Rust or Haskell or OCaml, SML, etc. I
    suspect one cannot easily do that in C++, because it's not
    managed and doesn't make ownership a first-class property,
    though one can usefully write a number of classes and so forth
    that give much the same effect, if not actual guarantees.

    I would say you can do this sort of thing in C++ too, but there are
    perhaps more ways to "cheat" or break promises and guarantees than in in
    some other languages.

    Interestingly (maybe?), I find Ada lacking in this area.

    My experience with Ada is not enough to judge your opinion here, but I
    agree this is interesting (and a little surprising).


    As a concrete example, I recently wanted to use a std::variant<> in my
    C++ code. A std::variant<> is, approximately, a struct like :

    struct {
    enum { ... } type_tag;
    union { ... } contents;
    }

    In C++, this is all type-safe - you don't have direct access to these
    fields, but instead they are kept consistent automatically so that
    objects of different types can be stored in the union and have their
    constructors and destructors called correctly. But in my case, I wanted
    to access the fields independently - I wanted to fill the "contents"
    with data retrieved over a network, and set the "type_tag" according to
    other data in the network packet. I knew this was safe (since no
    constructors or destructors were needed). But there was no way to
    handle this within the interface. My options included a risky,
    non-portable and difficult workaround (digging through the <variant>
    header and directly "hacking" the variant object using unsigned char*
    pointers), serious run-time inefficiencies (with extra copies of the
    data), or finding a different interface. I opted for the last one,
    making my own simple variation of std::variant that gave me the access I
    needed.

    This, of course, does not necessarily mean the interface of
    std::variant<> is bad. If my needs were highly unusual, then a standard
    library does not need to support them. But it is an example where the
    restrictions imposed by a safe, powerful interface limited how I could
    work with what is essentially the same kind of object.

    Well, sure, that's the danger of general libraries: you're
    getting something that is a decent combination of functionality
    and expressiveness contrasted with safety for many needs, but
    (almost by definition) not for all needs. As you point out,
    that is useful for many cases, though.

    At first glance it appears that it must suffer from the same
    drawbacks as `XC`, which you mentioned above. Except that the
    language does provide controlled ways to share data
    concurrently.

    Using the _unsafe_ subset, there is one place where it is
    permitted to have multiple, mutable references to an object: the
    `UnsafeCell`. This gives Rust interior mutability, which means
    that you can build safe abstractions for data sharing, like
    `Mutex` types that own the data they protect.

    If "unsafe" gives enough, but is rarely needed in most code, then it
    sounds like a good balance.

    Yes. And moreover, one can hide that unsafety behind a safe
    interface.

    I do like that the unsafety is marked explicitly and clearly.

    It goes beyond that, though: _if_ the programmer is successful
    in providing a safe abstraction around the unsafety, then it is
    impossible to compromise the memory safety of the program by
    using that abstraction. Naturally, "if" is doing a lot of work
    here: as I said, the burden for writing `unsafe` code is higher
    in Rust than it is in C, and it requires a lot of care. But the
    resulting guarantees are much stronger.

    - Dan C.



    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed May 27 01:05:22 2026
    On Sun, 24 May 2026 01:14:35 +0800, makendo wrote:

    Multipass is for when you have only kilobits of core memory, where
    the only way to realistically compile your code is to dump
    intermediate results (in the assembler case, memory address of
    labels) on a magnetic tape.

    That?s a different meaning of ?pass?, not what I meant at all.

    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Sun May 31 17:00:39 2026
    Bart <bc@freeuk.com> writes:

    Every time I do a survey of C codebases, I tend to get about the
    same set of results:

    * On average functions have only around three local variables

    * 80% of all functions have 4 locals or fewer

    Here are some statistics from a codebase for a C library:

    Average number of locals per function: just over 2

    Fraction of functions with 4 locals or fewer: just over 85%

    What conclusions should we draw from these statistics?

    I don't find such stats a compelling reason for block scopes,

    As most people know, a problem with averages is that they rarely
    tell the whole story. Despite the similarity of the statistics
    shown above with the ones you gave, code in the library codebase
    has locals declared in inner blocks, and locals declared in the
    outermost block but after some number of executable statements,
    and also locals declared in other nested scopes. In no case were
    any of these choices made from indifference: the code shows a
    clear preference for declaring locals in the outermost block, and
    at the top of that block before any executable code. In every
    instance that doesn't follow that rule there was a particular
    motivation for adopting the choice that was made. The idea that
    such averages make a good argument for never accepting locals
    other than at the top of the outermost block just doesn't hold
    water.

    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Thu Jun 4 07:19:27 2026
    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    [...] An observation pointed out to me at my last gig, and that
    I now point out occasionally myself, is that Linux (for example)
    is not so much written in C but rather in Linux C, which is the
    dialect of the language defined by the compilers they use and
    the specific behaviors they force using flags, pragmas, etc, for
    those compilers.

    Any program accepted by a conforming implementation is a C program.
    That remains true even if the program relies on compiler options
    such as -fwrap or -fno-strict-aliasing, or takes advantage of
    specific behaviors chosen by the compiler in situations that the C
    standard deems undefined behavior, or uses non-standard #pragmas
    that the implementation has chosen to define. As long as the
    implementation stays inside the bounds of being conforming, any
    program it accepts is written in C. Writing in a subset of the full
    language doesn't change that: a C program that has no 'goto'
    statements in it is still a C program. The same reasoning applies
    to programs that make use of -fwrapv, etc.

    At any rate, it's certainly not strictly
    conforming ISO C: is _any_ large program strictly conforming at
    this point? I suspect many projects strive for such, but few
    actually attain it.

    I expect most people don't realize how high a bar it is to write a C
    program that is strictly conforming. Except for toy examples I
    don't think I've ever seen one. I wouldn't advocate choosing such a
    criterion. Even more than that, if a member of any team I was part
    of did suggest doing so I would strongly argue against it. Even if
    it could be done, which I think is unlikely, the amount of effort
    needed to do so would be prohibitive.

    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Dan Cross@3:633/10 to All on Thu Jun 4 17:18:42 2026
    In article <86se72bd74.fsf@linuxsc.com>,
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    cross@spitfire.i.gajendra.net (Dan Cross) writes:
    [...] An observation pointed out to me at my last gig, and that
    I now point out occasionally myself, is that Linux (for example)
    is not so much written in C but rather in Linux C, which is the
    dialect of the language defined by the compilers they use and
    the specific behaviors they force using flags, pragmas, etc, for
    those compilers.

    Any program accepted by a conforming implementation is a C program.
    That remains true even if the program relies on compiler options
    such as -fwrap or -fno-strict-aliasing, or takes advantage of
    specific behaviors chosen by the compiler in situations that the C
    standard deems undefined behavior, or uses non-standard #pragmas
    that the implementation has chosen to define. As long as the
    implementation stays inside the bounds of being conforming, any
    program it accepts is written in C. Writing in a subset of the full
    language doesn't change that: a C program that has no 'goto'
    statements in it is still a C program. The same reasoning applies
    to programs that make use of -fwrapv, etc.

    This is quibbling, but for the record, Linux is written in an
    _extended_ subset of ISO C, and makes use of a number of
    non-conforming extensions (e.g., GCC-style function attributes
    and so on). While it's obviously "C" in some generic sense,
    describing it as a dialect called "Linux C" is entirely
    appropriate.

    But don't take my word for it: I urge you to have a look at the
    code and draw your own conclusion.

    At any rate, it's certainly not strictly
    conforming ISO C: is _any_ large program strictly conforming at
    this point? I suspect many projects strive for such, but few
    actually attain it.

    I expect most people don't realize how high a bar it is to write a C
    program that is strictly conforming. Except for toy examples I
    don't think I've ever seen one. I wouldn't advocate choosing such a >criterion. Even more than that, if a member of any team I was part
    of did suggest doing so I would strongly argue against it. Even if
    it could be done, which I think is unlikely, the amount of effort
    needed to do so would be prohibitive.

    Agreed.

    - Dan C.


    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Fri Jun 5 08:45:17 2026
    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    In article <86se72bd74.fsf@linuxsc.com>,
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    cross@spitfire.i.gajendra.net (Dan Cross) writes:

    [...] An observation pointed out to me at my last gig, and that
    I now point out occasionally myself, is that Linux (for example)
    is not so much written in C but rather in Linux C, which is the
    dialect of the language defined by the compilers they use and
    the specific behaviors they force using flags, pragmas, etc, for
    those compilers.

    Any program accepted by a conforming implementation is a C program.
    That remains true even if the program relies on compiler options
    such as -fwrap or -fno-strict-aliasing, or takes advantage of
    specific behaviors chosen by the compiler in situations that the C
    standard deems undefined behavior, or uses non-standard #pragmas
    that the implementation has chosen to define. As long as the
    implementation stays inside the bounds of being conforming, any
    program it accepts is written in C. Writing in a subset of the full
    language doesn't change that: a C program that has no 'goto'
    statements in it is still a C program. The same reasoning applies
    to programs that make use of -fwrapv, etc.

    This is quibbling, but for the record, Linux is written in an
    _extended_ subset of ISO C, and makes use of a number of
    non-conforming extensions (e.g., GCC-style function attributes
    and so on).

    The C standard allows extensions. As long as the extensions
    don't change the meaning of any strictly conforming program,
    the extended language still counts as C.

    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From dave_thompson_2@3:633/10 to All on Sat Jun 6 17:55:51 2026
    On Tue, 19 May 2026 15:40:13 +0200, David Brown
    <david.brown@hesbynett.no> wrote:

    Can you give me an example of a real-world programming language in which
    the scope of a local variable begins at the start of the enclosing
    block, rather than at its declaration / definition?

    PL/I aka PL/1. It was/is infamous for this.

    Plus it's the only language I know with a preprocessor almost as ugly
    and confusing as C's. But fortunately less needed.

    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From dave_thompson_2@3:633/10 to All on Sat Jun 6 19:00:59 2026
    On Thu, 21 May 2026 21:23:27 +0200, David Brown
    <david.brown@hesbynett.no> wrote:

    On 21/05/2026 19:26, Bart wrote:
    On 21/05/2026 13:55, David Brown wrote:

    (The separation of the struct/union/enum tag name space from variable
    namespace /is/ a feature that people use


    Urgh, another discussion. Let's not go there. Suffice that it is unique
    to C.

    Fortran -- since it standardized struct under the keyword TYPE in 1990
    -- has typenames separate/nonconflicting. So are program-unit names
    (including now-outdated COMMON blocks, except for the special handling
    of function results) and module names. And, $deity preserve us,
    NAMELIST groups -- which act almost like labels and (in Fortran) those
    aren't names at all but small integers.

    It doesn't have union or enum, though EQUIVALENCE COMMON and certain 'associations' can produce the effect of union.

    --- PyGate Linux v1.5.15
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)