Description
Go version
tip, and Go 1.22 - 1.24
Output of go env
in your module/workspace:
darwin/arm64
What did you do?
Run https://go.dev/play/p/9kYvE9NH8da
package main
func callRecover() {
if recover() != nil {
println("recovered")
}
}
func F(int) { callRecover() }
func main() {
defer F(1)
panic("XXX")
}
What did you see happen?
The panic is recovered. The program prints "recovered" and exits normally.
What did you expect to see?
The panic is not recovered, crashing the program.
The spec says "The return value of recover
is nil
when ... or recover
was not called directly by a deferred function." In this case, recover
is not directly called by the deferred function F
. There is an intermediate function. So recover
should return nil, not recovering the panic.
The program panics as expected if inlining is disabled.
$ go run -gcflags=-l x2.go
panic: XXX
goroutine 1 [running]:
main.main()
/tmp/x2.go:13 +0x48
exit status 2
At least, the program's behavior should not depend on inlining.
Go 1.21 got it right. Go 1.22 and later fail. Bisection points to CL https://go.dev/cl/520260 . Before that CL, defer wrappers are created after inlining, so the wrapper always makes an actual call to the wrapped function. That CL makes the generation of defer wrappers earlier, before inlining. So the wrapped function could be inlined into the wrapper. But we still keep the WRAPPER attribute on the wrapper function, which makes the compiler insert a special prologue that adjusts the panic frame if it is panicking. This causes the panic to be treated one frame too deep.
I think if we inline the wrapped function into the wrapper, the wrapper should not do the panic frame adjustment. We still need to tell the runtime to omit the wrapper frame from traceback. So we can't simply unset the WRAPPER attribute, which has two effects: panic frame adjustment, and omitting the frame from traceback. We may need a separate attribute for just omitting the frame from traceback without the panic adjustment. Or the inlining table could say that if the wrapper inlines the wrapped function, during the inlined part, it doesn't have a virtual caller (which would be the wrapper).
Found while investigating #73747. There is at least another related issue dated back even earlier, which I'm still investigating.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status