Live data from Hacker News

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

queue.acm.org

221–230 of 275 posts

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

#221
post #63

Earlier quoted context omitted.

This would mean you’d have to insert a check every time you add two signed integers together, because signed overflow is UB. You’d also have to wrap every memory access with bounds checks, because OOB memory access is UB. There are also tons and tons of loop optimizations compilers do for side-effect free loops which would have to be removed completely. This is because infinite loops without side effects are UB. So i…

Another option would be to define behaviors for integer overflow and out of bounds memory access. Presumably they happen fairly often and it might be a good idea to nail down what should happen in those cases.

UB is a better option though. When your signed integer overflows it's a bug nevertheless. Why force the compiler to generate code for a pointless case instead of letting it optimize the intended one?

If you value never having bugs over performance then just insert a check or run your program with a sanitizer that does that for you. It's a solved problem for a case where performance doesn't matter. The thing is that it does.

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

#222
post #213
post #33

Earlier quoted context omitted.

You should consider a program with undefined behaviour to be the equivalent of a mathematical proof that contains an unstated contradiction. Ex falso quodlibet : from a falsehood anything follows. Also called the principle of explosion. Undefined behaviour renders your entire program meaningless. It must be avoided at all costs. Using undefined behaviour on purpose is like sticking a fork in an electrical socket.

It's funny that your original post was an objection to how undefined behavior gives license to screw developers over, but here you are talking about how undefined behavior is like sticking a fork in an electrical socket.

But just to be clear @chongli is logical

Think of UB as a probabilistic error. I.e. it is always stupid to rely on it

1. Write code without errors -- sensible 2. Allow compilers to assume the absence of errors -- occasionally sensible, since it speeds up your program

In defence of UB, for the most part they are things that should break your program anyway: stack overflow is never correct. So your choice is mostly to fail badly quickly, or to fail slowly well

Thanks to google making the UB sanitizers you are free to make that choice even in C

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

#223
post #178

Earlier quoted context omitted.

Yeah, I realize that now. That's even worse. So you'll have to write something like int aa,twoa,twoab,bb,aaplustwoab,aaplustwoabplusbb; if (ckd_mul(a,a,&aa)) { return error; } if (ckd_mul(2,a,&twoa)) { return error; } // … if (ckd_add(aaplustwoab,bb,aaplustwoabplusbb)) { return error; } return aaplustwoabplusbb; So ergonomic! > If you're just going to throw out the bool and ignore the overflows, why bother with check…

Why not just write: bool aplusb_sqr(int* c, int a, int b) { return c && ckd_add(c, a, b) && ckd_mul(c, *c, *c); }

Obviously you could do that in this case, I just wanted to come up with a complicated formula.

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

#224

Earlier quoted context omitted.

Why would you want that? Is it to aid building for multiple targets? For debug builds?

> Why would you want that? To aid with optimisation, it basically lets you ask the compiler to remove branches, and provide constraints to the same. An implementation might trap in debug code, but given no context would be provided you'd likely avoid this and would instead use your own wrapper macro to output a message of some sort in that case.

But why put in unreachable? Doesn't make any sense to me.

If a branch is truly not supposed to ever happen, why have a branch at all? Just remove that code from the source entirely- that helps the optimizer even more, because the most optimal code is of course no code at all.

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

#225
post #213

Earlier quoted context omitted.

It's funny that your original post was an objection to how undefined behavior gives license to screw developers over, but here you are talking about how undefined behavior is like sticking a fork in an electrical socket.

But just to be clear @chongli is logical Think of UB as a probabilistic error. I.e. it is always stupid to rely on it 1. Write code without errors -- sensible 2. Allow compilers to assume the absence of errors -- occasionally sensible, since it speeds up your program In defence of UB, for the most part they are things that should break your program anyway: stack overflow is never correct. So your choice is mostly to…

I'd argue that it's stupid to think that it's stupid to rely on UB.

Almost any non-trivial software explicitly relies on undefined behavior, including safety critical libraries such as cryptographic libraries, the Linux operating system has rampant undefined behavior that it makes a conscious decision to use. POSIX makes use of undefined behavior for shared libraries (it treats functions loaded from shared libraries as void*, which is undefined behavior).

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

#226

Earlier quoted context omitted.

That's not an argument to keep live grenades laying around, it's an argument to remove them from the spec. Like signed int being UB. Define it to have 2 complement semantics. Problem solved. I'm sure the nutters trying to extend C++ with templates will howl but this is C not C++. And seriously C++ is dead man walking at this point.

C23 does make two’s complement standard. It also adds checked arithmetic so you can safely avoid signed overflow. It does not make signed overflow defined behaviour. This would prevent integer operation reordering as an optimization, leading to slower code.

>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, int index)
  {
   // here the compiler can assume a and b do not alias
   // and it can assume it can always load 16 bytes at a time
   // the programmer has made sure it's aligned and padded to so with any index
   // there's always 16 bytes to load
   // so go on, use any vectorized simd instruction you want
  }
  #pop_optimize
  // back to sane C

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

#227

Earlier quoted context omitted.

I went to look up undefined behaviour in Rust and I got this scary warning: Warning: The following list is not exhaustive. There is no formal model of Rust's semantics for what is and is not allowed in unsafe code, so there may be more behavior considered unsafe. The following list is just what we know for sure is undefined behavior. Please read the Rustonomicon before writing unsafe code. After the warning was a lis…

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 out that ATLAS relies pretty heavily on optimizing C compilers to generate optimal code on many different platforms. At the bottom of all this are the many loop optimizations included in compilers which, thanks to undefined behaviour in the C spec, are able to assume that code is always on the happy path.

It also turns out that Rust includes bindings to ATLAS and LAPACK. I would imagine at some point people might want to write a new linear algebra package in pure Rust. I think it’ll be quite difficult to match the performance of those two in safe Rust, but we’ll see.

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

#228
post #63

Earlier quoted context omitted.

This would mean you’d have to insert a check every time you add two signed integers together, because signed overflow is UB. You’d also have to wrap every memory access with bounds checks, because OOB memory access is UB. There are also tons and tons of loop optimizations compilers do for side-effect free loops which would have to be removed completely. This is because infinite loops without side effects are UB. So i…

Another option would be to define behaviors for integer overflow and out of bounds memory access. Presumably they happen fairly often and it might be a good idea to nail down what should happen in those cases.

That would be great if it was possible, but how do you specify & implement sensible behavior for this:

    void foo(int *a, int b) { a[b] = 1}
At runtime there is no information about whether that write is in bounds and no way to prevent this from corrupting arbitrary data unless you compile for something like CHERI.

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

#229

Earlier quoted context omitted.

C23 does make two’s complement standard. It also adds checked arithmetic so you can safely avoid signed overflow. It does not make signed overflow defined behaviour. This would prevent integer operation reordering as an optimization, leading to slower code.

>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?

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

#230

Earlier quoted context omitted.

That's not an argument to keep live grenades laying around, it's an argument to remove them from the spec. Like signed int being UB. Define it to have 2 complement semantics. Problem solved. I'm sure the nutters trying to extend C++ with templates will howl but this is C not C++. And seriously C++ is dead man walking at this point.

C23 does make two’s complement standard. It also adds checked arithmetic so you can safely avoid signed overflow. It does not make signed overflow defined behaviour. This would prevent integer operation reordering as an optimization, leading to slower code.

C++ 20 did that too.
Post reply on HN