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