Sujet : Re: else ladders practice
De : bc (at) *nospam* freeuk.com (Bart)
Groupes : comp.lang.cDate : 07. Nov 2024, 13:23:04
Autres entêtes
Organisation : A noiseless patient Spider
Message-ID : <vgibf8$2l858$1@dont-email.me>
References : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
User-Agent : Mozilla Thunderbird
On 06/11/2024 14:50, David Brown wrote:
On 05/11/2024 23:48, Bart wrote:
On 05/11/2024 13:29, David Brown wrote:
int small_int_sqrt(int x) {
if (x == 0) return 0;
if (x < 4) return 1;
if (x < 9) return 2;
if (x < 16) return 3;
unreachable();
}
"unreachable()" is a C23 standardisation of a feature found in most high-end compilers. For gcc and clang, there is __builtin_unreachable(), and MSVC has its version.
So it's a kludge. Cool, I can create one of those too:
func smallsqrt(int x)int =
if
elsif x=0 then 0
elsif x<4 then 1
elsif x<9 then 2
elsif x<16 then 3
dummyelse int.min
fi
end
'dummyelse' is a special version of 'else' that tells the compiler that control will (should) never arrive there. ATM it does nothing but inform the reader of that and to remind the author. But later stages of the compiler can choose not to generate code for it, or to generate error-reporting code.
(A couple of things about this: the first 'if' condition and branch can be omitted; it starts at elsif. This removes the special-casing for the first of an if-elsif-chain, so to allow easier maintenance, and better alignment.
Second is that, unlike your C, the whole if-fi construct is a single expression term that yields the function return value. Hence the need for all branches to be present and balanced regarding their common type.
This could have been handled internally (compiler adds 'dummyelse <empty value for type>'), but I think it's better that it is explicit (user might forget to add that branch).
That int.main is something I sometimes use for in-band signalling. Here that is the value -9223372036854775808 so it's quite a wide band! Actually it is out-of-band it the user expects only result with an i32 range.
BTW your example lets through negative values; I haven't fixed that.)
Getting that right will satisfy both the language (if it cared more about such matters than C apparently does), and the casual reader curious about how the function contract is met (that is, supplying that promised return int type if or when it returns).
C gets it right here. There is no need for a return type when there is no return
There is no return for only half the function! A function with a return type is a function that CAN return. If it can't ever return, then make it a procedure.
Take this function where N can never be zero; is this the right way to write it in C:
int F(int N) {
if (N==0) unreachable();
return abc/N; // abc is a global with value 100
}
If doesn't look right. If I compile it with gcc (using __builtin_unreachable), and call F(0), then it crashes. So it doesn't do much does it?!
indeed, trying to force some sort of type or "default" value would be counterproductive. It would be confusing to the reader, > add
untestable and unexecutable source code,
But it IS confusing, since it quite clearly IS reachable. There's a difference between covering all possible values of N, so that is genuinely is unreachable, and having code that COULD be reachable.
Let's now look at another alternative - have the function check for validity, and return some kind of error signal if the input is invalid. There are two ways to do this - we can have a value of the main return type acting as an error signal, or we can have an additional return value.
...
All in all, we have a significant costs in various aspects, with no real benefit, all in the name of a mistaken belief that we are avoiding undefined behaviour.
This is all a large and complex subject. But it's not really the point of the discussion.
I'm not talking about what happens when running a program, but what happens at compilation, and satisfying the needs of the language.
C here is less strict in being happy to have parts of a function body as no-go areas where various requirements can be ignored, like a function with a designed return type T, being allowed to return without satisfying that need.
Here, you demostrated bolted-on hacks that are not part of the language, like the snappy __builtin_unreachable (the () are apparently optional). I can't see however that it does much.
It is a fact C as a language allows this:
T F() {} // T is not void
(I've had to qualify T - point number 9 in procedures vs. function.)
All that C says is that control flow running into that closing }, without encountering a 'return x', is UB.
IMV, sloppy. My language simply doesn't allow it.