Live data from Hacker News

Catch-23: The New C Standard Sets the World on Fire

queue.acm.org

241–250 of 275 posts

Re: Catch-23: The New C Standard Sets the World on Fire

#241

Earlier quoted context omitted.

>This would prevent integer operation reordering as an optimization, leading to slower code. The sane way to address that is to add explicit opt-in annotations like 'restrict'. #push_optimize(assume_no_integer_overflow) int x = a + b; // more performance orientated code #pop_optimize // back to sane C #push_optimize(assume_no_alias(a, b), assume_stride(a, 16), assume_stride(b, 16)) void compute(float *a, float *b, in…

That’s a lot uglier and clunkier than just using the ckd_add, ckd_mul etc. safe checked arithmetic. Plus if an overflow occurs you still get an incorrect result which you probably don’t want. Or maybe I’m wrong? Do people actually want overflows to occur and incorrect results? If they’re willing to tolerate incorrect results, why would they also want optimizations disabled?

The thing is it's ugly in the rare case that absolute performance is worth fighting for. And not ugly in the majority case where it isn't in the top three important things.

Re: Catch-23: The New C Standard Sets the World on Fire

#242

Earlier quoted context omitted.

My complaint here is that it took C more than 30 years between defining signed integer overflow as UB and providing programmers with standard library facilities to check if a signed integer operation would result in overflow. I much prefer Rust's approach to arithmetic, where overflow with plain arithmetic operators is defined as a bug, and panics on debug-enabled builds, plus special operations in the standard libra…

My complaint here is that it took C more than 30 years ... I much prefer Rust's approach That's an odd complaint. Rust didn't spring forth fully formed from the ether, it stands on the shoulders of C (and other giants of PL history). 30 years ago you couldn't use Rust at all because it didn't exist. The reason the committee doesn't just radically change C in all these nice ways to catch up to Rust is because it would…

Interestingly, the first Ada standard in 1983 defined signed integer overflow to raise a CONSTRAINT_ERROR exception.

But apparently it lacked unsigned integers with modular arithmetic?

http://archive.adaic.com/standards/83lrm/html/lrm-11-01.html... http://archive.adaic.com/standards/83lrm/html/lrm-03-05.html

The 2012 version is a bit more readable, and has unsigned integers:

For a signed integer type, the exception Constraint_Error is raised by the execution of an operation that cannot deliver the correct result because it is outside the base range of the type. For any integer type, Constraint_Error is raised by the operators "/", "rem", and "mod" if the right operand is zero.

For a modular type, if the result of the execution of a predefined operator (see 4.5) is outside the base range of the type, the result is reduced modulo the modulus of the type to a value that is within the base range of the type.

http://www.ada-auth.org/standards/rm12_w_tc1/html/RM-3-5-4.h...

Re: Catch-23: The New C Standard Sets the World on Fire

#243

Earlier quoted context omitted.

That’s a lot uglier and clunkier than just using the ckd_add, ckd_mul etc. safe checked arithmetic. Plus if an overflow occurs you still get an incorrect result which you probably don’t want. Or maybe I’m wrong? Do people actually want overflows to occur and incorrect results? If they’re willing to tolerate incorrect results, why would they also want optimizations disabled?

The thing is it's ugly in the rare case that absolute performance is worth fighting for. And not ugly in the majority case where it isn't in the top three important things.

No, GP's proposal is ugly in the majority case. If you're going to make signed overflow defined behaviour then every time you write:

    int c = a + b;
You have to assume it will overflow and give an incorrect result. So now you need to check everything, everywhere, and you don't get any optimizations unless you explicitly ask for them with those ugly #push_optimize annotations. I completely fail to see how this is an advantage.

The way C works right now, the assumption is that you want optimization by default and safety is opt-in. The GP's proposal takes away the optimization by default. It then makes incorrect results the default, but it does not make safety the default. To make safety the default you would have to force people to write conditionals all over the place to check for the overflows with ckd_add, ckd_mul etc. Merely writing:

    int c = a + b;
Does not give you any assurances that your answer will be correct.

Re: Catch-23: The New C Standard Sets the World on Fire

#244

Earlier quoted context omitted.

I agree rust isn’t perfect, but I think you underestimate the value of “safe” code. I often write programs that have unsafe code. However, the unsafe code is never more than 100 lines, which means I have a very small amount of code to reason about — Rust users expect (of course, you as a programmer has to enforce) that it should be possible to cause UB from safe code, so my “safe interface” to my unsafe code ensures…

In one sense, C is the unsafe code block for myriad other languages, like Python. Python users don’t want to deal with undefined behaviour either. They want to write their high level code in NumPy or PyTorch and just have everything work very fast. Little do they know: they rely on C for those libraries and for things like ATLAS and LAPACK, which implement the underlying numerical linear algebra code. Well, it turns…

Isn't LAPACK written in Fortran?

Re: Catch-23: The New C Standard Sets the World on Fire

#245

Earlier quoted context omitted.

In one sense, C is the unsafe code block for myriad other languages, like Python. Python users don’t want to deal with undefined behaviour either. They want to write their high level code in NumPy or PyTorch and just have everything work very fast. Little do they know: they rely on C for those libraries and for things like ATLAS and LAPACK, which implement the underlying numerical linear algebra code. Well, it turns…

Isn't LAPACK written in Fortran?

You're right, and ATLAS is as well, but Fortran has undefined behaviour [1] for all the same reasons that C does.

[1] https://stackoverflow.com/a/57558908

Re: Catch-23: The New C Standard Sets the World on Fire

#246
post #239

Earlier quoted context omitted.

No? Conformance allows unspecified and implementation defined. Strict conformance is the absence of that (i.e. same output in every conforming environment). Neither includes UB, as UB is "outside the standard" in some sense and doesn't have defined semantics.

It's a common misconception that a conforming program may not engender undefined behavior. In fact this very article touches on how realloc has introduced new (and backwards incompatible) undefined behavior precisely to accommodate the POSIX standard (so that POSIX compliant implementations of C can redefine the otherwise undefined behavior however they please).

Can you cite that? It runs against a plain reading of the standards (both C and C++) and would be insane for the standard to allow "correct" programs to include those with undefined behavior. There was even an unadopted proposal (n853 [1]) attempting to clarify this.

While I was making sure I wasn't missing something obvious, I took a look through the rest of the WG14 proposals to see if I was somehow off in my understanding regarding translators being allowed to barf over UB anywhere in the program. There was a proposal clarifying the situation to the possible-execution understanding from upthread submitted by Victor Yodaiken (n2278 [2]), but unfortunately it was also never adopted.

[1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n853.htm

[2] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2278.pdf

Re: Catch-23: The New C Standard Sets the World on Fire

#247
post #90

I actually like unreachable() a lot. What it does is that it invokes undefined behavior, that's all. It does nothing trickier than any other kind of UB. In fact, I could implement unreachable() like this: void unreachable() { (char *)0 = 1; }. Standardizing it however gives interesting options for compilers and tool writers. The best use I can find is to bound the values of the argument of a function. For example, if…

Using `unreachable()` instead of `assert()` for your preconditions without profiling first is just pre-loading the gun to shoot yourself in the foot in the future. When those preconditions are inevitably violated at some point, you will get random UB corruption rather than simply aborting as is the case for assert.

Re: Catch-23: The New C Standard Sets the World on Fire

#248

Earlier quoted context omitted.

Fine. HN is, after all, a place where you can be pedantic. But those of us who are actually writing programs mostly care about "in practical terms", and in practical terms, this doesn't happen, so we don't care . We've got enough trouble worrying about what does happen; we don't have time and energy to worry about what doesn't and won't happen.

That's like saying: "I don't care what the standard says!" Sure, this is perfectly fine. Only that you're not writing any C/C++ than, but something in the "gcc 12 language with some switches", or maybe the "LLVM 15 language with some switches", or something like that.

Well, if Visual Studio (or whatever Microsoft calls their compiler these days), and all known versions of gcc, and all known versions of LLVM all do something sane, then I'm not sure I care all that much about the theoretical possibility that some compiler someday might do something insane.

Re: Catch-23: The New C Standard Sets the World on Fire

#249
post #90

I actually like unreachable() a lot. What it does is that it invokes undefined behavior, that's all. It does nothing trickier than any other kind of UB. In fact, I could implement unreachable() like this: void unreachable() { (char *)0 = 1; }. Standardizing it however gives interesting options for compilers and tool writers. The best use I can find is to bound the values of the argument of a function. For example, if…

> What it does is that it invokes undefined behavior, that's all. [...] it can also be used in debug builds to trigger a crash

How can it be used to trigger a crash (a specific behavior) if the behavior it invokes is undefined? Are you saying it would be defined differently for debug builds so that it doesn't invoke undefined behavior?

Re: Catch-23: The New C Standard Sets the World on Fire

#250
post #175

> C23 furthermore gives the compiler license to use an unreachable annotation on one code path to justify removing, without notice or warning, an entirely different code path that is not marked unreachable: see the discussion of puts() in Example 1 on page 316 of N3054.9 I don't agree with that description at all. Here's the code: 1 if (argc The only code path that's "entirely different" is lines 1,4,5 and in that ca…

This just shows that "unreachable" is almost impossible to use safely. The only safe use of unreachable is if it is immediately after an instruction that makes the program stop running. It is not for "this cannot happen", because things that "cannot happen" happen all the time. If you use "unreachable", you're just asking for trouble and it seems the compiler authors are happy to oblige.

One example of it being useful is unchecked std::variant access in c++ - there isn’t any api to access it like a union (if you already know the type) but you can mark the wrong type path unreachable to the same effect.
Post reply on HN