This change improves the concrete type analysis in the devirtualizer, it not longer relies on ir.Reassigned, it now statically tries to determine the concrete type of an interface, even when assigned multiple times, following type assertions and iface conversions. Alternative to CL 649195 Updates #69521 Fixes #64824 Change-Id: Ib1656e19f3619ab2e1e6b2c78346cc320490b2af GitHub-Last-Rev: e8fa0b12f0a7b1d7ae00e5edb54ce04d1f702c09 GitHub-Pull-Request: golang/go#71935 Reviewed-on: https://go-review.googlesource.com/c/go/+/652036 Reviewed-by: Michael Pratt <mpratt@google.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Keith Randall <khr@golang.org>
101 lines
1.7 KiB
Go
101 lines
1.7 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
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
type A interface{ A() }
|
|
|
|
type Impl struct{}
|
|
|
|
func (*Impl) A() {}
|
|
|
|
type Impl2 struct{}
|
|
|
|
func (*Impl2) A() {}
|
|
|
|
func main() {
|
|
shouldNilPanic(28, func() {
|
|
var v A
|
|
v.A()
|
|
v = &Impl{}
|
|
})
|
|
shouldNilPanic(36, func() {
|
|
var v A
|
|
defer func() {
|
|
v = &Impl{}
|
|
}()
|
|
v.A()
|
|
})
|
|
shouldNilPanic(43, func() {
|
|
var v A
|
|
f := func() {
|
|
v = &Impl{}
|
|
}
|
|
v.A()
|
|
f()
|
|
})
|
|
|
|
// Make sure that both devirtualized and non devirtualized
|
|
// variants have the panic at the same line.
|
|
shouldNilPanic(55, func() {
|
|
var v A
|
|
defer func() {
|
|
v = &Impl{}
|
|
}()
|
|
v. // A() is on a sepearate line
|
|
A()
|
|
})
|
|
shouldNilPanic(64, func() {
|
|
var v A
|
|
defer func() {
|
|
v = &Impl{}
|
|
v = &Impl2{} // assign different type, such that the call below does not get devirtualized
|
|
}()
|
|
v. // A() is on a sepearate line
|
|
A()
|
|
})
|
|
}
|
|
|
|
var cnt = 0
|
|
|
|
func shouldNilPanic(wantLine int, f func()) {
|
|
cnt++
|
|
defer func() {
|
|
p := recover()
|
|
if p == nil {
|
|
panic("no nil deref panic")
|
|
}
|
|
if strings.Contains(fmt.Sprintf("%s", p), "invalid memory address or nil pointer dereference") {
|
|
callers := make([]uintptr, 128)
|
|
n := runtime.Callers(0, callers)
|
|
callers = callers[:n]
|
|
|
|
frames := runtime.CallersFrames(callers)
|
|
line := -1
|
|
for f, next := frames.Next(); next; f, next = frames.Next() {
|
|
if f.Func.Name() == fmt.Sprintf("main.main.func%v", cnt) {
|
|
line = f.Line
|
|
break
|
|
}
|
|
}
|
|
|
|
if line != wantLine {
|
|
panic(fmt.Sprintf("invalid line number in panic = %v; want = %v", line, wantLine))
|
|
}
|
|
|
|
return
|
|
}
|
|
panic(p)
|
|
}()
|
|
f()
|
|
}
|