Is this infinite recursion UB?
Asked Answered
P

2

27

In C++11, as an infinite loop with no side-effects, the following program is UB:

int main() {
   while (true) {}
}

Is the following also UB?

void foo() {
   foo();
}

int main() {
   foo();
}

Citations from the standard for both programs would be ideal.

Pentecost answered 5/5, 2011 at 23:19 Comment(6)
Wait, infinite loops are UB? Wtf?Meraree
@Tomalak: Yeah, beyond words... +1Meraree
@Tomalak: Wait, so what do you do if, for example, you want to loop forever until somebody interrupts you?Meraree
@Mehrdad: Then that's not forever.Pentecost
@Tomalak: But they can might interrupt you.Meraree
I don't think that the order of I/O calls relative to volatile accesses need to be kept. So I think an impl is allowed to execute the write to volatileA before entering the loop, if shouldExit() does not depend on volatileA, in the following: while(shouldExit()) ; volatileA = false; (assuming shouldExit() peeks an I/O device for user input, i.e "loop forever until somebody interrupts you").Bibeau
B
24

It's UB because it's not worded in terms of loops, but in terms of (1.10p24):

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

  • terminate,
  • make a call to a library I/O function,
  • access or modify a volatile object, or
  • perform a synchronization operation or an atomic operation.

This applies to both, as opposed to the more older formulation in one of the C++0x drafts. (See this question for discussions).

Note that disregarding of that, the behavior can easily be undefined if the recursion exceeds the implementation limit of the number of nested recursive function calls. That has always been the case.

Bibeau answered 5/5, 2011 at 23:21 Comment(9)
Does this mean that my clarification "with no side effects" is an inaccurate one?Pentecost
Oh, and is the implication sound that the application's "base thread" is one of the "any thread[s]" that the clause refers to? Or could it simply be referring to spawned child threads?Pentecost
@Tomalak ah I think i finally know the precise meaning of the above text. It means, at any point, it may assume that the implementation does one of those things (if the program terminates, the condition is full-filled, for example). So volatile int a = 0; while(1); is UB; because the program never again does one of the 4 bullet-point things.Bibeau
@litb: Right; makes sense. Seems a bit ambiguous, though.Pentecost
@Tomalak I would think the thread that has main executed is a thread too. The spec says "The execution of the entire program consists of an execution of all of its threads.". So for int main() { }, the main function executes, so by the above paragraph, since something executes, the main function executes in a thread, I think!Bibeau
@Tomalak I can't think of any example where an infinite loop without side effects would satisfy one of the above 4 bullets. But I've not looked up where the spec says or where it follows from that performing a synchronization operation or an atomic operation is a side effect.Bibeau
This does not imply UB. Note ISO C++ has no rule equivalent to Clause 4p2 in ISO C.Sprayberry
@Sprayberry i don't understand. Do you say that the program terminates? Do you also say that the program has a finite number of nested recursive function calls?Bibeau
It depends on the implementation. There is no guarantee that the program will or will not terminate. This still does not mean it incurs UB which voids any predictable guarantee (not only about termination).Sprayberry
P
1

I don't think the standard says the behavior is undefined, it just says a loop that has no side effects may be assumed to eventually terminate.

So:

int main() {
   while (true) {}
}

May terminate or loop forever.

void foo() {
   foo();
}

int main() {
   foo();
}

May also terminate, loop forever, or possibly run out of stack space (if the compiler does not implement tail recursion).

I don't think either have any right to do anything other than listed, so I don't think the behavior is completely "undefined".

Patronize answered 6/5, 2011 at 2:51 Comment(3)
Surely if an assumption is made and the program does not satisfy the assumption, then the program's behaviour is undefined by definition?Pentecost
This means that the implementation may assume that a program will not have code that will not eventually terminate. This means that no valid programs shall have code that does not eventually terminate (or satisfy the other requirements in 1.10/24). This isn't the same as "you can do this, as long as you (the user) assume that your loop might just stop on its own one day".Pentecost
It means something more like, "eating up CPU cycles is not a side effect". Code that has no side effects other than CPU usage, and does not produce a value that is used later, need not run at all. The implementation is free to decide whether to run that code or not. It does not, however, get to order pizza.Bluebill

© 2022 - 2024 — McMap. All rights reserved.