Why does infinitely-recursing code not give stack overflow in Playground?

753 views
Skip to first unread message

ben...@gmail.com

unread,
Apr 5, 2022, 9:18:07 PM4/5/22
to golang-nuts
Normally the Go Playground gives errors when a runtime panic or other error occurs, including "timeout running program" for programs that run too long. I'm wondering why the Playground doesn't show a stack overflow error (or any error) for this infinitely-recursing program?


It seems like a quirk (bug?) in the way this particular error is handled. This confused my colleague (who's new to Go) because he (correctly) expected it to show some kind of overflow/recursion error.

For comparison, here's what the Go runtime says locally for this code:

$ go run t.go
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc020160398 stack=[0xc020160000, 0xc040160000]
fatal error: stack overflow
... full stack trace elided ...

Should I report a golang/go issue?

-Ben

Ian Lance Taylor

unread,
Apr 5, 2022, 9:47:03 PM4/5/22
to ben...@gmail.com, golang-nuts
The playground isn't intended to be an exact replica of running a
program on a real machine. If the program uses too many resources it
will simply be stopped.

Ian

ben...@gmail.com

unread,
Apr 5, 2022, 9:50:16 PM4/5/22
to golang-nuts
The playground isn't intended to be an exact replica of running a
program on a real machine. If the program uses too many resources it
will simply be stopped.

Both fair enough. But surely the runner can distinguish when the program ran successfully to completion versus when it was stopped, and print an error in the latter case? Even if it was just something generic like "out of memory" or even "program stopped". Currently it prints nothing, which looks like a success.

-Ben
 

Kurtis Rader

unread,
Apr 5, 2022, 9:57:40 PM4/5/22
to ben...@gmail.com, golang-nuts
On Tue, Apr 5, 2022 at 6:18 PM ben...@gmail.com <ben...@gmail.com> wrote:
In addition to what Ian said  I'll point out that this is a borderline HCF (halt-catch-fire) situation. In other words, I'm pretty sure infinite recursion is not guaranteed to result in the specific fatal error you observed on whatever platform you ran `go run t.go`. Your system might just as well failed with a kernel panic. Yes, that would be unlikely on any OS supported by Go but, I'm pretty sure, is allowed to occur. Feel free to point me to the portion of the Go specification which contradicts what I just wrote. :-)

--
Kurtis Rader
Caretaker of the exceptional canines Junior and Hank

Ian Lance Taylor

unread,
Apr 5, 2022, 11:13:58 PM4/5/22
to ben...@gmail.com, golang-nuts
Sure, report an issue against the playground. I honestly have no idea
how difficult this would be to fix.

Ian

ben...@gmail.com

unread,
Apr 5, 2022, 11:32:11 PM4/5/22
to golang-nuts
Sure, report an issue against the playground. I honestly have no idea
how difficult this would be to fix.


The source for the Playground is private, correct?

-Ben

ben...@gmail.com

unread,
Apr 5, 2022, 11:33:55 PM4/5/22
to golang-nuts
Oh wait, looks like it's public: https://github.com/golang/playground

Jesper Louis Andersen

unread,
Apr 11, 2022, 11:47:09 AM4/11/22
to ben...@gmail.com, golang-nuts
On Wed, Apr 6, 2022 at 3:18 AM ben...@gmail.com <ben...@gmail.com> wrote:
Normally the Go Playground gives errors when a runtime panic or other error occurs, including "timeout running program" for programs that run too long. I'm wondering why the Playground doesn't show a stack overflow error (or any error) for this infinitely-recursing program?


Depending on implementation, infinite recursion is not guaranteed to blow the stack for the program given. The function call is in tail position, so a tail-call optimization (TCO) pass would be able to rewrite the program into an infinite loop by reusing the existing stack frame for each invocation of f[0].

I don't think the spec explicitly rejects such optimizations, but you can't rely on TCO either, because it isn't mandated, like it is in e.g., the specification of the programming language Scheme.

[0] One of the flip sides of such rewriting are that the stack might not exactly represent the control flow of the program, so in a debugging situation you have to apply more imagination to figure out what the program did.

ben...@gmail.com

unread,
Apr 11, 2022, 6:04:21 PM4/11/22
to golang-nuts
Depending on implementation, infinite recursion is not guaranteed to blow the stack for the program given. The function call is in tail position, so a tail-call optimization (TCO) pass would be able to rewrite the program into an infinite loop by reusing the existing stack frame for each invocation of f[0].

Understood. But I think there are two reasons this code in the playground should still show an error:

1) The existing Go compilers don't do tail-call optimization, so keeping the discussion real and concrete, they will overflow the stack.
2) Even if they did implement tail-call optimization, this would be an infinite loop, so it should show a timeout error, like an infinite for loop does: https://go.dev/play/p/GH67vNtpZyp

-Ben
 

Sam Hughes

unread,
Apr 11, 2022, 8:01:54 PM4/11/22
to golang-nuts
I've hit this problems a few times, and I immediately thumbs-upped that issue report.

To correct @Ben, I suggest the purest reasoning for an error being displayed is "The process completed, and did not succeed". In your case, @Ben, yeah, it was killed while waiting on something, but the normal case is when I kick off an A/B test with too many iterations, or that is particularly heavy. It's easy enough to do an A/B test locally, but if you're changing someone's snippet and resharing, or else producing the test to send to a colleague. It happens seldom enough that I go through the same little confusion-frustration-shorten sequence whenever this happens.

If the process got killed, whatever it's running on, it's worth notifying the remote (us) that it had to be killed, even if it doesn't include a stack-trace. 

Reply all
Reply to author
Forward
0 new messages