6

Is the result of the following indirect recursion defined by the standard or is it undefined behavior?

auto abc() -> int ;

auto xyz() -> int  {
    static int instance = 3 + abc();
    return instance;
}

auto abc() -> int {
    static int instance = 2 + xyz();
    return instance;
}

int main() {
    int tmp = xyz();//or abc();
}

In VS2012 tmp is 5 but I'm not sure if that's guaranteed by the standard.

MFH
  • 1,584
  • 2
  • 16
  • 36
  • 1
    Result in g++ 4.8.1: http://ideone.com/ZPtVCG – xorguy Oct 25 '13 at 21:01
  • @xorguy: thanks, that what I expected – MFH Oct 25 '13 at 21:04
  • 4
    `auto abc() -> int ;` Is this [Almost Always Auto](http://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/)? o.O Why not just `int abc();`? – dyp Oct 25 '13 at 21:05
  • This is `segfault`, even if you make inner function calls after initialization. – lapk Oct 25 '13 at 21:08
  • I threw this onto a 16-bit MCU with clang/LLVM, and it went into an infinite loop. – Sam Cristall Oct 25 '13 at 21:08
  • @DyP: it's just a matter of taste after all, I started using trailing return types on a regular basis and have a code base where every non-void function uses that convention for consistency's sake… – MFH Oct 25 '13 at 21:10
  • @MFH Every function uses that convention? Good lord ... and I thought people overused `var` in C# ... – Zac Howland Oct 25 '13 at 21:24
  • @ZacHowland: yes, it helps if all you need to know is if a function returns anything. It also helps visually as you all functions - no matter what return type - are visually aligned in the code… This ain't got anything to do with `var`, but yes `AAA`… – MFH Oct 25 '13 at 21:30
  • 3
    @MFH: Because figuring out the return value from `int abc()` is very difficult? And visual alignment in both cases is handled by spacing. This is a misuse of AAA (as you have to specify the return type anyway). – Zac Howland Oct 25 '13 at 21:37
  • @ZacHowland: no it's not but my code base is not littered with small functions… why should I introduced manual alignment for return types of unknown length when I can simply use trailing return types? And in C++11 you have to specify the return type in all cases so your argument would make trailing return types for free functions as a whole void… – MFH Oct 25 '13 at 21:44
  • @MFH Your codebase is *still* "littered" with small functions; you are simply using an uncommon syntax. And you *still* would have to visually align the return types (which is done better when the return type is first, since it would *always* be the first item) as function names of varying length would move your trailing return value all over the place. In essence, there is no good reason to declare these functions as `auto abc() -> int` instead of `int abc()`. You gain nothing and reduce readability. – Zac Howland Oct 26 '13 at 06:27
  • @ZacHowland: don't make assumptions on my code base... – MFH Oct 26 '13 at 20:05
  • @MFH I'm not. I'm going purely on what you described. – Zac Howland Oct 26 '13 at 20:06
  • @ZacHowland: »Your codebase is still "littered" with small functions« That's what I'd call an assumption… – MFH Oct 26 '13 at 22:05
  • @MFH You stated that having `auto abc() -> int` prevents you from having your codebase "littered" with small functions (as opposed to having `int abc()`). I retorted that if you have `auto abc() -> int` all over the place, your codebase is still "littered" with the small functions you claim you do not have. You just changed the syntax. That is not an assumption, but a rebuttal from your flawed assertion. – Zac Howland Oct 26 '13 at 22:13
  • @ZacHowland: `abc` and `xyz` are not real world functions, they are simple examples to support the question… – MFH Oct 26 '13 at 22:20
  • @MFH That was not the point at all ... – Zac Howland Oct 26 '13 at 22:21

2 Answers2

10

It's undefined behaviour.

[statement.decl]/4

If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined. [Example:

int foo(int i) {
    static int s = foo(2*i); // recursive call - undefined
    return i+1;
}

end example ]

dyp
  • 35,820
  • 10
  • 96
  • 156
  • 6
    +1 I don't know how VS2012 handled this and returns `5`. They're problematic for defined behavior and solid for undefined things :-D – masoud Oct 25 '13 at 21:12
  • @MM.: I'd expect it does the following: on second entry of `xyz` it ignores jumps to the second line (aka pretend the initialization is complete) and returns 0 (zero initialization)… – MFH Oct 25 '13 at 21:15
  • 1
    Yes, in both functions, the second entry cause ignoring `static` line and return `instance` as `0` (why it's zero?) and this stops infinite recursion. The problem is, VS2012 has assumed an uninitialized static variable is _always_ 0. – masoud Oct 25 '13 at 21:22
  • @MM. I would guess that is only true for a debug build. If he did it in a release build, the results from VS would probably be some random value in memory. – Zac Howland Oct 25 '13 at 21:26
  • @MM.: aren't namespace scoped statics zero initialized? so maybe VC uses the same logic here - UB means that all bets are off after all… – MFH Oct 25 '13 at 21:26
  • @ZacHowland: nope, it's also 5 in Release mode… Unitialized stack memory would actually have the value `CCCCCCCC` in Debug mode… – MFH Oct 25 '13 at 21:28
  • @MFH True, "The zero-initialization of all block-scope variables with static storage duration or thread storage duration is performed before any other initialization takes place.". I guess MSVC marks `xyz`'s `instance` as initialized when it first encounters it, then when it encounters it the second time, it just uses the stored value (which is `0`). – dyp Oct 25 '13 at 21:33
2

This should not result in anything useful from any compiler. It is infinite recursion and will likely result in a segmentation fault or a stack overruns heap error (depending on the system), even if you fix the undefined behavior of attempting to recursively set the value of a static variable:

auto abc() -> int;

auto xyz() -> int  
{
    return abc();
}

auto abc() -> int 
{
    return xyz();
}

int main() 
{
    int tmp = xyz(); // defined behavior, but infinite recursion
}
Zac Howland
  • 15,149
  • 1
  • 23
  • 37