From 7ef831a4e63ba47f1eff18536af12427b5d3cac3 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Wed, 9 Apr 2014 18:00:57 -0400 Subject: [PATCH] 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 --- cmd/ssadump/main.go | 3 +-- go/ssa/builder.go | 9 ++++++++- go/ssa/interp/testdata/boundmeth.go | 30 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/cmd/ssadump/main.go b/cmd/ssadump/main.go index caa794cb..484ceee5 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 4a03f632..c3b7db98 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 60f20dcc..255cc607 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() }