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.)
ÿ 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.
There is also not being able to write 'L:}'; this one bites.
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.
Irrelevant because it's part of a language extension? Those tend to
become 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.
Somebody asks why do I have to do X and not Y? Y makes more sense, it is easier etc.
The response here will be because the Standard says so. End of discussion.
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!
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.
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.
[...]
Some of it is an actual nuisance, but quite a bit is also aesthetically displeasing.
[...]
There is also not being able to write 'L:}'; this one bites.
[...]
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.
This writeup of the original bug is pretty good: https://dwheeler.com/essays/apple-goto-fail.html
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.
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.
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.
I understand that someone has written a paper proposing adding
`defer` to C; that would obviate many of these problems.
- Dan C.
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.)
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.
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.
For that you took an arbitrary Algol 68
interpreter
So give me links to some others, or to playgrounds.
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?
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?
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!
On 21/05/2026 13:56, Bart wrote:
(The separation of the struct/union/enum tag name space from variable namespace /is/ a feature that people use
We have no idea of a user benefit from /not/ having separate name spaces here.
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.
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.
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.
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.
[...]
A google search for "algol 68 standard document" would give you
a clue.
https://algol68-lang.org/docs/algol68-draft-report.pdf
[...]
On 21/05/2026 07:45, David Brown wrote:No, only to stifle uninformed criticism.
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.
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)...
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.
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:;}'.
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.
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.
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.
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?
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.
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.
There is also not being able to write 'L:}'; this one bites.
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:...
The only two places where labels are permitted are in labeledThere 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)...
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)!
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.
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.
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.
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.
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.
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.
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.
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?
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.
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.)
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)
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.
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.
[...]
On 2026-05-22 02:24, Keith Thompson wrote:
Bart <bc@freeuk.com> writes:
[...]I suggest using the term "name space" rather than "namespace",
(Labels of course have their own namespace, for some obscure
reason. I'm sure 99% of C programmers don't know that.) "
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).
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;
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.
[...]
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."
[...]
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 2026-05-22 02:24, Keith Thompson wrote:
Bart <bc@freeuk.com> writes:
[...]I suggest using the term "name space" rather than "namespace",
(Labels of course have their own namespace, for some obscure
reason. I'm sure 99% of C programmers don't know that.) "
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.
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
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.
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
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.)
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.)
On 2026-05-21 21:23, David Brown wrote:You're asking why '?:' exists when you can use 'if-else'?
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 necessityIn gnu C computed goto tends to be used for faster dispatching (in
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.)
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'?
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.
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.)
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.
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'.
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_?
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;
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.
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.
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.
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".
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.)
[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.
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.
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/>
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.
[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.
(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'.
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.
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.
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.
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...'
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.
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.
I think you might need to switch out your crystal ball - your
Now you're going to say the vast majority of C programmers have never
used 'switch' either! 'Probably...'
predictions about what people will say or do have rarely come close to reality.ÿ Or, perhaps, you should stop saying such stupid things.
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.
(I can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)
Yikes.
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.
[snip]
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);
In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:
(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.
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.
I think you might need to switch out your crystal ball - your
Now you're going to say the vast majority of C programmers have never
used 'switch' either! 'Probably...'
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'.
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.
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.
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'.")
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.
Google estimates there are 11 to 13 million C programmers.
[snip for brevity]
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.
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 can do it with zero: (cond | L1 | L2); the 'goto' can be implied.)
Yikes.
[...]
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.
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.
[...]
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.
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.
[...]
Apparently this extension was introduced in gcc 11, released in 2021.
It also allows labels on declarations.
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.
I think you might need to switch out your crystal ball - your
Now you're going to say the vast majority of C programmers have never
used 'switch' either! 'Probably...'
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?
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!
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,
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.
For someone who is so proud of having "designed" several languages,
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?)
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?
... are irrelevant.For someone who is so proud of having "designed" several languages,
All of which ...
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, ....
"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.
... are irrelevant.
For someone who is so proud of having "designed" several languages,
All of which ...
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.
... are irrelevant.
For someone who is so proud of having "designed" several languages,
All of which ...
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.
esides thay talke on topic (thoug i would say only on part of it as they
say most interesting part of topics too)
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.
On 22/05/2026 18:32, Dan Cross wrote:
In article <10upkur$1fkbh$2@dont-email.me>, Bart <bc@freeuk.com> wrote:
[snip]
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.
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.
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.
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.
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.
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 .)
[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).
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. [...]
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. [...]
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. [...]
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.
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."
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; [...]
Breaking out of nested loops without a lot of unnecessary
ceremony is sort of an obvious example; [...]
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.
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.
[...]
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.
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.
left = q = malloc( sizeof *q );goto A5;
right = q = malloc( sizeof *q );goto A5;
b = a;return;
b = 0;return;
right = p->left, p->left = r;} else {
left = p->right, p->right = s;
left = p->right, p->right = r;}
right = p->left, p->left = s;
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.
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
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.
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.
I took sqlite3.c sources from 2018 (sqlite3.c + shell.c + header, which
are combined into a 235Kloc source file).
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.
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.
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...
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.
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.
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.
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.
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.
(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.)
[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.
[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".
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.
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.
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.
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
I don't find such stats a compelling reason for block scopes,
[...] 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.
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.
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).
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?
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.
| Sysop: | Tetrazocine |
|---|---|
| Location: | Melbourne, VIC, Australia |
| Users: | 14 |
| Nodes: | 8 (0 / 8) |
| Uptime: | 198:18:24 |
| Calls: | 218 |
| Files: | 21,503 |
| Messages: | 82,303 |