mirror of
https://github.com/golang/go
synced 2025-04-03 23:25:20 +00:00
The existing code for recover from deferrangefunc was broken in several ways. 1. the code following a deferrangefunc call did not check the return value for an out-of-band value indicating "return now" (i.e., recover was called) 2. the returned value was delivered using a bespoke ABI that happened to match on register-ABI platforms, but not on older stack-based ABI. 3. the returned value was the wrong width (1 word versus 2) and type/value(integer 1, not a pointer to anything) for deferrangefunc's any-typed return value (in practice, the OOB value check could catch this, but still, it's sketchy). This -- using the deferreturn lookup method already in place for open-coded defers -- turned out to be a much-less-ugly way of obtaining the desired transfer of control for recover(). TODO: we also could do this for regular defer, and delete some code. Fixes #71675 Change-Id: If7d7ea789ad4320821aab3b443759a7d71647ff0 Reviewed-on: https://go-review.googlesource.com/c/go/+/650476 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
100 lines
1.4 KiB
Go
100 lines
1.4 KiB
Go
// 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
|
|
|
|
//go:noinline
|
|
func i() {
|
|
for range yieldInts {
|
|
defer func() {
|
|
println("I")
|
|
recover()
|
|
}()
|
|
}
|
|
// This panic causes dead code elimination of the return block.
|
|
// The compiler should nonetheless emit a deferreturn.
|
|
panic("i panic")
|
|
}
|
|
|
|
//go:noinline
|
|
func h() {
|
|
defer func() {
|
|
println("H first")
|
|
}()
|
|
for range yieldInts {
|
|
defer func() {
|
|
println("H second")
|
|
}()
|
|
}
|
|
defer func() {
|
|
println("H third")
|
|
}()
|
|
for range yieldIntsPanic {
|
|
defer func() {
|
|
println("h recover:called")
|
|
recover()
|
|
}()
|
|
}
|
|
}
|
|
|
|
//go:noinline
|
|
func yieldInts(yield func(int) bool) {
|
|
if !yield(0) {
|
|
return
|
|
}
|
|
}
|
|
|
|
//go:noinline
|
|
func g() {
|
|
defer func() {
|
|
println("G first")
|
|
}()
|
|
for range yieldIntsPanic {
|
|
defer func() {
|
|
println("g recover:called")
|
|
recover()
|
|
}()
|
|
}
|
|
}
|
|
|
|
//go:noinline
|
|
func yieldIntsPanic(yield func(int) bool) {
|
|
if !yield(0) {
|
|
return
|
|
}
|
|
panic("yield stop")
|
|
}
|
|
|
|
//go:noinline
|
|
func next(i int) int {
|
|
if i == 0 {
|
|
panic("next stop")
|
|
}
|
|
return i + 1
|
|
}
|
|
|
|
//go:noinline
|
|
func f() {
|
|
defer func() {
|
|
println("F first")
|
|
}()
|
|
for i := 0; i < 1; i = next(i) {
|
|
defer func() {
|
|
println("f recover:called")
|
|
recover()
|
|
}()
|
|
}
|
|
}
|
|
func main() {
|
|
f()
|
|
println("f returned")
|
|
g()
|
|
println("g returned")
|
|
h()
|
|
println("h returned")
|
|
i()
|
|
println("i returned")
|
|
|
|
}
|