diff --git a/cmd/ssadump/main.go b/cmd/ssadump/main.go index caa794cb45..484ceee5d5 100644 --- a/cmd/ssadump/main.go +++ b/cmd/ssadump/main.go @@ -130,8 +130,7 @@ func doMain() error { case 'R': interpMode |= interp.DisableRecover default: - fmt.Fprintf(os.Stderr, "ssadump: unknown -interp option: '%c'.", c) - os.Exit(1) + return fmt.Errorf("unknown -interp option: '%c'", c) } } diff --git a/go/ssa/builder.go b/go/ssa/builder.go index 4a03f632df..c3b7db9852 100644 --- a/go/ssa/builder.go +++ b/go/ssa/builder.go @@ -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. // The result is a bound method closure. obj := sel.Obj().(*types.Func) - wantAddr := isPointer(recvType(obj)) + rt := recvType(obj) + wantAddr := isPointer(rt) escaping := true 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{ Fn: boundMethodWrapper(fn.Prog, obj), Bindings: []Value{v}, diff --git a/go/ssa/interp/testdata/boundmeth.go b/go/ssa/interp/testdata/boundmeth.go index 60f20dcc8b..255cc60703 100644 --- a/go/ssa/interp/testdata/boundmeth.go +++ b/go/ssa/interp/testdata/boundmeth.go @@ -2,6 +2,8 @@ package main +import "fmt" + func assert(b bool) { if !b { panic("oops") @@ -100,6 +102,32 @@ func regress1(x error) func() string { 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() { valueReceiver() pointerReceiver() @@ -111,4 +139,6 @@ func main() { if e := regress1(errString("hi"))(); e != "hi" { panic(e) } + + nilInterfaceMethodValue() }