We care about the wrapper-ness of logical frames, not physical frames.
Fixes #73916
Fixes #73917
Fixex #73920
Change-Id: Ia17c8390e71e6c0e13e23dcbb7bc7273ef25da90
Reviewed-on: https://go-review.googlesource.com/c/go/+/685375
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
nonWrapperFrames := 0
loop:
for ; u.valid(); u.next() {
- switch u.frame.fn.funcID {
- case abi.FuncIDWrapper:
- continue
- case abi.FuncID_gopanic:
- if u.frame.fp == uintptr(p.gopanicFP) && nonWrapperFrames > 0 {
- canRecover = true
- }
- break loop
- default:
- nonWrapperFrames++
- if nonWrapperFrames > 1 {
+ for iu, f := newInlineUnwinder(u.frame.fn, u.symPC()); f.valid(); f = iu.next(f) {
+ sf := iu.srcFunc(f)
+ switch sf.funcID {
+ case abi.FuncIDWrapper:
+ continue
+ case abi.FuncID_gopanic:
+ if u.frame.fp == uintptr(p.gopanicFP) && nonWrapperFrames > 0 {
+ canRecover = true
+ }
break loop
+ default:
+ nonWrapperFrames++
+ if nonWrapperFrames > 1 {
+ break loop
+ }
}
}
}
--- /dev/null
+// run
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func callRecover() {
+ if recover() != nil {
+ println("recovered")
+ }
+}
+
+func F(int) { callRecover() }
+
+func main() {
+ mustPanic(func() {
+ defer F(1)
+ panic("XXX")
+ })
+}
+
+func mustPanic(f func()) {
+ defer func() {
+ r := recover()
+ if r == nil {
+ panic("didn't panic")
+ }
+ }()
+ f()
+}
--- /dev/null
+// run
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func callRecover() {
+ func() {
+ if recover() != nil {
+ println("recovered")
+ }
+ }()
+}
+
+func F() int { callRecover(); return 0 }
+
+func main() {
+ mustPanic(func() {
+ defer F()
+ panic("XXX")
+ })
+}
+
+func mustPanic(f func()) {
+ defer func() {
+ r := recover()
+ if r == nil {
+ panic("didn't panic")
+ }
+ }()
+ f()
+}
--- /dev/null
+// run
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func callRecover() {
+ if recover() != nil {
+ println("recovered")
+ }
+}
+
+type T int
+
+func (*T) M() { callRecover() }
+
+type S struct{ *T } // has a wrapper S.M wrapping (*T.M)
+
+var p = S{new(T)}
+
+var fn = S.M // using a function pointer to force using the wrapper
+
+func main() {
+ mustPanic(func() {
+ defer fn(p)
+ panic("XXX")
+ })
+}
+
+func mustPanic(f func()) {
+ defer func() {
+ r := recover()
+ if r == nil {
+ panic("didn't panic")
+ }
+ }()
+ f()
+}
--- /dev/null
+// run
+
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func callRecover() {
+ if recover() != nil {
+ println("recovered")
+ }
+}
+
+type T int
+
+func (*T) M() { callRecover() }
+
+type S struct{ *T } // has a wrapper (*S).M wrapping (*T.M)
+
+var p = &S{new(T)}
+
+var fn = (*S).M // using a function pointer to force using the wrapper
+
+func main() {
+ mustPanic(func() {
+ defer fn(p)
+ panic("XXX")
+ })
+}
+
+func mustPanic(f func()) {
+ defer func() {
+ r := recover()
+ if r == nil {
+ panic("didn't panic")
+ }
+ }()
+ f()
+}