7

Infinite loops without side effect are undefined behaviour. See here for the example from cppreference. Much simpler example:

int foo() {
    while(true) {}
    return 42;
}

Now consider the almost equivalent

int bar() {
    if (true) return bar();
    return 42;
}

Does this invoke undefined behaviour as well?

Or phrased differently: What class of error is infinite recursion according to the language?

PS: note that I am aware of the implications at runtime: The loop in principle could run forever, while the recursion will eventually result in a stackoverflow. Though I am mainly interested in what the compiler does to them. Maybe a quite academic question...

  • 1
    "The loop in principle could run forever, while the recursion will eventually result in a stackoverflow." – Actually, since it is UB it could do anything. And it doesn't have to recurse in the first place on actual hardware because of the as-if rule. In particular, if optimizations are enabled, the compiler is likely to replace your recursive example with effectively a loop (tail call optimization) even if it has side effects. – Arne Vogel Apr 18 at 7:33
  • @ArneVogel Yes the code is contrived but in practice most C++ code has no objects with non trivial dtors and cannot be tail optimized most of the time – curiousguy Apr 18 at 8:27
  • @ArneVogel read "in principle" as "what would happen if it wasnt ub" – formerlyknownas_463035818 Apr 18 at 11:16
  • @user463035818: I'd say if it wasn't UB I'd say an implementation should be expected to refrain from performing any action which wouldn't occur if the loop were omitted, or any action which would have a non-hoistable data dependency on anything in the loop, but may perform any actions which would occur if the loop were omitted, unless or until it encounters a non-hoistable data dependency. – supercat Apr 23 at 21:09
10

No there is no difference. [basic.progress]p1:

The implementation may assume that any thread will eventually do one of the following:

  • terminate,

  • make a call to a library I/O function,

  • perform an access through a volatile glvalue, or

  • perform a synchronization operation or an atomic operation.

It doesn't matter how you have your infinite loop; if it doesn't do any of the points above, you get UB. Including the following:

int bar(int cond) {
    if (cond == 42) bar(cond);
    return 42;
}
bar(some_user_input);

The compiler is allowed to assume that some_user_input will never be 42.

  • 1
    @Jean-BaptisteYunès It's in the "may assume" part. If your program doesn't do that you're violating the compiler's assumption which is the same thing as UB. It does but that's just an example of what could happen. – Rakete1111 Apr 17 at 19:43
  • 1
    i am a bit confused. Naively put: Compiler knows that my stacksize is limited, infinite recursion will result in stack overflow, thread will terminate, all fine. Where am I wrong? – formerlyknownas_463035818 Apr 17 at 19:46
  • 3
    @user463035818 In the C++ abstract machine there is no stack size. That's one of those implementation defined limits, but it doesn't count as a program terminating if that makes sense. – Rakete1111 Apr 17 at 19:50
  • 1
    @Rakete1111 you want to tell me stackoverflow isnt real ? :P Seriously, what is a stackoverflow in terms of the c++ language? It simply does not exist and therefore is undefined by definition? – formerlyknownas_463035818 Apr 17 at 20:22
  • 1
    @user463035818 Well yeah, it is not something the language is concerned about. It's not necessarily undefined though, it's just that that concept doesn't exist in the abstract machine. – Rakete1111 Apr 17 at 20:23
0

In most real-world situations, an invitation to assume something will happen implies that one is not obligated to take any special measures to allow for the possibility that it doesn't, but is not a license to behave in arbitrary fashion if one discovers that the event in question won't occur.

Under fashionable interpretations of the C Standard, however, there is no reason to expect that implementations will refrain from totally nonsensical fashion if any assumption the Standard would invite them to make proves incorrect.

I don't think the authors of the Standard intended that C compilers would use complicated inference engines which cascade assumptions to the point that a compiler given e.g.

if (x > 1000) foo(x);
while ( x <= 1000)
  somethingWithNoSideEffectsThatCantAffectX();

would conclude that foo should be executed unconditionally regardless of the value of x. More likely, they intended that a compiler given something like:

volatile int yy;
void test(int x, int *restrict arr)
{
  int y;
  do
    x=arr[x];
  while(x & 1);
  y=yy;
  if (y)
    printf("%d\n",x);
}

would be allowed to use the fact that the final value of x may or may not be needed to rearrange the code to be more efficient in the cases where it isn't (by skipping its calculation altogether). Essentially, the fact that a piece of code takes time to execute--even if infinite--should not be considered a side-effect in and of itself, and if a compiler can prove that a piece of code cannot execute any side-effects that would be visible before it reaches a certain place, it need not wait for that piece of code to finish before executing code at that place.

Unfortunately, the authors of the Standard rely upon compiler writers to recognize what kinds of actions may be usefully taken on the basis of certain assumptions, while compiler writers assume that any possible inference they can draw should be presumed useful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.