1
0
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:
Alan Donovan 2014-04-09 18:00:57 -04:00
parent b12fe1707c
commit 7ef831a4e6
3 changed files with 39 additions and 3 deletions

View File

@ -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)
} }
} }

View File

@ -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},

View File

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