summaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/devirtualize/devirtualize.go
diff options
context:
space:
mode:
authorCuong Manh Le <cuong.manhle.vn@gmail.com>2022-09-06 12:54:54 +0700
committerGopher Robot <gobot@golang.org>2022-09-06 22:14:56 +0000
commitc82304b7129d7e675ff010d54f58d039235e4802 (patch)
tree67adef2ab68750287b0090ccb232bce6e597c570 /src/cmd/compile/internal/devirtualize/devirtualize.go
parent812fd2fe70112209557c4f6392ab8eaee6f68dda (diff)
downloadgo-git-c82304b7129d7e675ff010d54f58d039235e4802.tar.gz
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>
Diffstat (limited to 'src/cmd/compile/internal/devirtualize/devirtualize.go')
-rw-r--r--src/cmd/compile/internal/devirtualize/devirtualize.go20
1 files changed, 18 insertions, 2 deletions
diff --git a/src/cmd/compile/internal/devirtualize/devirtualize.go b/src/cmd/compile/internal/devirtualize/devirtualize.go
index b620470b0e..7350a6f171 100644
--- a/src/cmd/compile/internal/devirtualize/devirtualize.go
+++ b/src/cmd/compile/internal/devirtualize/devirtualize.go
@@ -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)
+ }
}
})
}