mirror of
https://github.com/golang/go
synced 2024-11-18 15:04:44 -07:00
go.tools/go/ssa: perform nil check when taking value of interface method.
+ test. Fixes golang/go#7269 LGTM=gri R=gri CC=golang-codereviews https://golang.org/cl/84650046
This commit is contained in:
parent
b12fe1707c
commit
7ef831a4e6
@ -130,8 +130,7 @@ func doMain() error {
|
|||||||
case 'R':
|
case 'R':
|
||||||
interpMode |= interp.DisableRecover
|
interpMode |= interp.DisableRecover
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(os.Stderr, "ssadump: unknown -interp option: '%c'.", c)
|
return fmt.Errorf("unknown -interp option: '%c'", c)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,9 +631,16 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
|
|||||||
// e.f where e is an expression and f is a method.
|
// e.f where e is an expression and f is a method.
|
||||||
// The result is a bound method closure.
|
// The result is a bound method closure.
|
||||||
obj := sel.Obj().(*types.Func)
|
obj := sel.Obj().(*types.Func)
|
||||||
wantAddr := isPointer(recvType(obj))
|
rt := recvType(obj)
|
||||||
|
wantAddr := isPointer(rt)
|
||||||
escaping := true
|
escaping := true
|
||||||
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
|
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
|
||||||
|
if _, ok := rt.Underlying().(*types.Interface); ok {
|
||||||
|
// If v has interface type I,
|
||||||
|
// we must emit a check that v is non-nil.
|
||||||
|
// We use: typeassert v.(I).
|
||||||
|
emitTypeAssert(fn, v, rt, token.NoPos)
|
||||||
|
}
|
||||||
c := &MakeClosure{
|
c := &MakeClosure{
|
||||||
Fn: boundMethodWrapper(fn.Prog, obj),
|
Fn: boundMethodWrapper(fn.Prog, obj),
|
||||||
Bindings: []Value{v},
|
Bindings: []Value{v},
|
||||||
|
30
go/ssa/interp/testdata/boundmeth.go
vendored
30
go/ssa/interp/testdata/boundmeth.go
vendored
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
func assert(b bool) {
|
func assert(b bool) {
|
||||||
if !b {
|
if !b {
|
||||||
panic("oops")
|
panic("oops")
|
||||||
@ -100,6 +102,32 @@ func regress1(x error) func() string {
|
|||||||
return x.Error
|
return x.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for b/7269:
|
||||||
|
// taking the value of an interface method performs a nil check.
|
||||||
|
func nilInterfaceMethodValue() {
|
||||||
|
err := fmt.Errorf("ok")
|
||||||
|
f := err.Error
|
||||||
|
if got := f(); got != "ok" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nil
|
||||||
|
if got := f(); got != "ok" {
|
||||||
|
panic(got)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
r := fmt.Sprint(recover())
|
||||||
|
// runtime panic string varies across toolchains
|
||||||
|
if r != "runtime error: interface conversion: interface is nil, not error" &&
|
||||||
|
r != "runtime error: invalid memory address or nil pointer dereference" {
|
||||||
|
panic("want runtime panic from nil interface method value, got " + r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
f = err.Error // runtime panic: err is nil
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
valueReceiver()
|
valueReceiver()
|
||||||
pointerReceiver()
|
pointerReceiver()
|
||||||
@ -111,4 +139,6 @@ func main() {
|
|||||||
if e := regress1(errString("hi"))(); e != "hi" {
|
if e := regress1(errString("hi"))(); e != "hi" {
|
||||||
panic(e)
|
panic(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nilInterfaceMethodValue()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user