mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
cmd/compile: do not devirtualize defer/go calls
For defer/go calls, the function/method value are evaluated immediately. So after devirtualizing, it may trigger a panic when implicitly deref a nil pointer receiver, causing the program behaves unexpectedly. It's safer to not devirtualizing defer/go calls at all. Fixes #52072 Change-Id: I562c2860e3e577b36387dc0a12ae5077bc0766bf Reviewed-on: https://go-review.googlesource.com/c/go/+/428495 Reviewed-by: Michael Knyszek <mknyszek@google.com> Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
parent
812fd2fe70
commit
c82304b712
@ -17,9 +17,25 @@ import (
|
||||
// Func devirtualizes calls within fn where possible.
|
||||
func Func(fn *ir.Func) {
|
||||
ir.CurFunc = fn
|
||||
|
||||
// For promoted methods (including value-receiver methods promoted to pointer-receivers),
|
||||
// the interface method wrapper may contain expressions that can panic (e.g., ODEREF, ODOTPTR, ODOTINTER).
|
||||
// Devirtualization involves inlining these expressions (and possible panics) to the call site.
|
||||
// This normally isn't a problem, but for go/defer statements it can move the panic from when/where
|
||||
// the call executes to the go/defer statement itself, which is a visible change in semantics (e.g., #52072).
|
||||
// To prevent this, we skip devirtualizing calls within go/defer statements altogether.
|
||||
goDeferCall := make(map[*ir.CallExpr]bool)
|
||||
ir.VisitList(fn.Body, func(n ir.Node) {
|
||||
if call, ok := n.(*ir.CallExpr); ok {
|
||||
Call(call)
|
||||
switch n := n.(type) {
|
||||
case *ir.GoDeferStmt:
|
||||
if call, ok := n.Call.(*ir.CallExpr); ok {
|
||||
goDeferCall[call] = true
|
||||
}
|
||||
return
|
||||
case *ir.CallExpr:
|
||||
if !goDeferCall[n] {
|
||||
Call(n)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
32
test/fixedbugs/issue52072.go
Normal file
32
test/fixedbugs/issue52072.go
Normal file
@ -0,0 +1,32 @@
|
||||
// run
|
||||
|
||||
// Copyright 2022 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
|
||||
|
||||
type I interface{ M() }
|
||||
|
||||
type T struct {
|
||||
x int
|
||||
}
|
||||
|
||||
func (T) M() {}
|
||||
|
||||
var pt *T
|
||||
|
||||
func f() (r int) {
|
||||
defer func() { recover() }()
|
||||
|
||||
var i I = pt
|
||||
defer i.M()
|
||||
r = 1
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
if got := f(); got != 1 {
|
||||
panic(got)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user