Live data from Hacker News

Weekend projects: getting silly with C

lcamtuf.substack.com

61–70 of 118 posts

Re: Weekend projects: getting silly with C

#62
post #17

This features the construct switch(k) { if (0) case 0: x = 1; if (0) case 1: x = 2; if (0) default: x = 3; } which is a switch where you don't have to write break at the end of every clause. #define brkcase if (0) case That might be worth using. Compilers won't love the control flow but they'll probably delete it effectively.

Surely the following would work just as well? #define brkcase break;case kinda defeats the purpose of the macro even.

I think the behavior is slightly different since this one breaks the above case, and the other one only omits its case from fallthrough

Incidentally, what happens if you use your brkcase as the first case?

I don't find either particularly exciting - a macro that would append break to the current case feels better

Re: Weekend projects: getting silly with C

#63
post #47

Earlier quoted context omitted.

> Also broken compilers [1]. The issue you linked to is not a counter example because, as the poster said, g may terminate the program in which case that snippet does not have undefined behaviour even if b is zero. The fact that they bothered to mention that g may terminate the program seems like an acknowledgement that it would be valid to do that time travelling if it didn't. > Note that blog post is correct about…

The poster is me. You are right that this is not an example for time-travel. There aren't really good examples for true time travel because compilers generally do not do this. But my point is that with compilers behaving like this, people might confuse this for time-traveling UB. I have certainly met some who did and the blog posts seems to have similar examples (but I haven't looked closely now). Note that I am a me…

I didn't notice that section when I last read through C23, but I'm very glad to see it. Reining in UB is one of the hardest problems I've had to deal with, and being able to say operations are defined up to the point of UB makes my job so much easier.

The lack of clarity in earlier standards made it impossible to deal with code incrementally, since all the unknown execution paths could potentially breach back in time and smash your semantics.

Re: Weekend projects: getting silly with C

#64
post #62
post #17

Earlier quoted context omitted.

Surely the following would work just as well? #define brkcase break;case kinda defeats the purpose of the macro even.

I think the behavior is slightly different since this one breaks the above case, and the other one only omits its case from fallthrough Incidentally, what happens if you use your brkcase as the first case? I don't find either particularly exciting - a macro that would append break to the current case feels better

Both version of the macro makes this fall through from 0:

  switch (a) {
    brkcase 0: foo();
    case 1: bar();
  }
so in a sense the `if (0) case` trick also affects the previous case, not the current one. But that one also falls apart when there are multiple statements under the brkcase.

Re: Weekend projects: getting silly with C

#65
post #48
post #43

Earlier quoted context omitted.

I'm inclined to trust Raymond Chen and John Regehr on these matters, so if you assert that they're incorrect here then a source to back up your assertion would help your argument.

I am a member of WG14. You should check the C standard. I do not see how "time-travel" is a possible reading of the definition of UB in C. We added another footnote to C23 to counter this idea: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf "Any other behavior during execution of a program is only affected as a direct consequence of the concrete behavior that occurs when encountering the erroneous or non…

So maybe we have different definitions of "time travel". But I recall that

- if a compiler finds that condition A would lead to UB, it can assume that A is never true - that fact can "backpropagate" to, for example, eliminate comparisons long before the UB.

Here is an older discussion: https://softwareengineering.stackexchange.com/q/291548

Is that / will that no longer be true for C23? Or does "time-travel" mean something else in this context?

Re: Weekend projects: getting silly with C

#66
post #42
post #38

This reminds me of some silly C code I once wrote for fun, which counts down from 10 to 1: #include // compile & run: gcc -Wall countdown.c -o countdown && ./countdown int n = 10; int main(int argc, char *argv[]) { printf("%d\n", n) && --n && main(n, NULL); } Python version: import sys # run: python3 countdown.py 10 def main(n:int): sys.stdout.write(f"{n}\n") and n-1 and main(n-1) main(int(sys.argv[1])) Shell version…

I don't think I've ever thought of explicitly calling main(). Made me chuckle.

I think it is UB

Edit: actually looks like it is UB in C++ but not C

Re: Weekend projects: getting silly with C

#68
post #48

Earlier quoted context omitted.

I am a member of WG14. You should check the C standard. I do not see how "time-travel" is a possible reading of the definition of UB in C. We added another footnote to C23 to counter this idea: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf "Any other behavior during execution of a program is only affected as a direct consequence of the concrete behavior that occurs when encountering the erroneous or non…

So maybe we have different definitions of "time travel". But I recall that - if a compiler finds that condition A would lead to UB, it can assume that A is never true - that fact can "backpropagate" to, for example, eliminate comparisons long before the UB. Here is an older discussion: https://softwareengineering.stackexchange.com/q/291548 Is that / will that no longer be true for C23? Or does "time-travel" mean some…

E.g. this godbolt: https://godbolt.org/z/eMYWzv8P8

There is unconditional use of a pointer b, which is UB if b is null. However, there is an earlier branch that checks if b is null. If we expected the UB to "backpropagate", the compiler would eliminate that branch, but both gcc and clang at O3 keep the branch.

However, both gcc and clang have rearranged the side effects of that branch to become visible at the end of the function. I.e. if b is null, it's as if that initial branch never ran. You could observe the difference if you trapped SIGSEGV. So even though the compiler didn't attempt to "time-travel" the UB, in combination with other allowed optimizations (reordering memory accesses), it ended up with the same effect.

Re: Weekend projects: getting silly with C

#69
post #47

Earlier quoted context omitted.

The poster is me. You are right that this is not an example for time-travel. There aren't really good examples for true time travel because compilers generally do not do this. But my point is that with compilers behaving like this, people might confuse this for time-traveling UB. I have certainly met some who did and the blog posts seems to have similar examples (but I haven't looked closely now). Note that I am a me…

I didn't notice that section when I last read through C23, but I'm very glad to see it. Reining in UB is one of the hardest problems I've had to deal with, and being able to say operations are defined up to the point of UB makes my job so much easier. The lack of clarity in earlier standards made it impossible to deal with code incrementally, since all the unknown execution paths could potentially breach back in time…

Thank you. This was my motivation. It is only a small step... much more work to do.

Re: Weekend projects: getting silly with C

#70
post #48

Earlier quoted context omitted.

I am a member of WG14. You should check the C standard. I do not see how "time-travel" is a possible reading of the definition of UB in C. We added another footnote to C23 to counter this idea: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf "Any other behavior during execution of a program is only affected as a direct consequence of the concrete behavior that occurs when encountering the erroneous or non…

So maybe we have different definitions of "time travel". But I recall that - if a compiler finds that condition A would lead to UB, it can assume that A is never true - that fact can "backpropagate" to, for example, eliminate comparisons long before the UB. Here is an older discussion: https://softwareengineering.stackexchange.com/q/291548 Is that / will that no longer be true for C23? Or does "time-travel" mean some…

There may be different definitions, but also a lot of incorrect information. Nothing changes with C23 except that we added a note that clarifies that UB can not time-travel. The semantic model in C only requires that observable effects are preserved. Everything else can be changed by the optimizer as long as it does not change those observable effects (known as the "as if" principle). This is generally the basis of most optimizations. Thus, I call time-travel only when it would affect previous observable effects, and this what is allowed for UB in C++ but not in C. Earlier non-observable effects can be changed in any case and is nothing speicifc to UB. So if you call time-travel also certain optimization that do not affect earlier observable behavior, then this was and is still allowed. But the often repeated statement that a compiler can assume that "A is never true" does not follow (or only in very limited sense) from the definition of UB in ISO C (and never did), so one has to be more careful here. In particular it is not possible to remove I/O before UB. The following code has to print 0 when called with zero and a compiler which would remove the I/O would not be conforming.

int foo(int x)

{

  printf("%d\n", x);

  fflush(stdout);

  return 1 / x;
}

In the following example

int foo(int x)

{

  if (x) bar(x);

  return 1 / x;
}

the compiler could indeed remove the "if" but not because it were allowed to assume that x can never be zero, but because 1 / 0 can have arbitrary behavior, so could also call "bar()" and then it is called for zero and non-zero x and the if condition could be removed (not that compilers would do this)

Post reply on HN