go/test/devirtualization_nil_panics.go
Mateusz Poliwczak de9da0de30 cmd/compile/internal/devirtualize: improve concrete type analysis
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: e8fa0b12f0
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>
2025-10-08 14:09:22 -07:00

100 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()
}