Change-Id: I86e48219ddfdf0d8607a69d7915443e8e21194d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/768940 Auto-Submit: Sean Liao <sean@liao.dev> Reviewed-by: Sean Liao <sean@liao.dev> Reviewed-by: Carlos Amedee <carlos@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
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 separate 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 separate 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()
|
|
}
|