]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: fix interface to interface conversion in Call
authorRuss Cox <rsc@golang.org>
Wed, 29 Nov 2017 19:44:43 +0000 (14:44 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 1 Dec 2017 15:31:31 +0000 (15:31 +0000)
Call is meant to mirror the language semantics, which allow:

var r io.ReadWriter
f := func(io.Reader){}
f(r)

even though the conversion from io.ReadWriter to io.Reader is
being applied to a nil interface. This is different from an explicit
conversion:

_ = r.(io.Reader)
f(r.(io.Reader))

Both of those lines panic, but the implicit conversion does not.

By using E2I, which is the implementation of the explicit conversion,
the reflect.Call equivalent of f(r) was inadvertently panicking.
Avoid the panic.

Fixes #22143.

Change-Id: I6b2f5b808e0cd3b89ae8bc75881e307bf1c25558
Reviewed-on: https://go-review.googlesource.com/80736
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/reflect/all_test.go
src/reflect/value.go

index 0a1a38dd2ed12ef53e413c0d32be81e370d1938c..e51d19efdd6fa686711e9fef4361f3a522f5d33a 100644 (file)
@@ -1631,6 +1631,15 @@ func TestFunc(t *testing.T) {
        }
 }
 
+func TestCallConvert(t *testing.T) {
+       v := ValueOf(new(io.ReadWriter)).Elem()
+       f := ValueOf(func(r io.Reader) io.Reader { return r })
+       out := f.Call([]Value{v})
+       if len(out) != 1 || out[0].Type() != TypeOf(new(io.Reader)).Elem() || !out[0].IsNil() {
+               t.Errorf("expected [nil], got %v", out)
+       }
+}
+
 type emptyStruct struct{}
 
 type nonEmptyStruct struct {
index 0184e6820eccfc4a990cd7ba73df62e9de1e5f08..d3575cae6b7b4c83288d6d9a20c4d6c1dc85bbe8 100644 (file)
@@ -2197,6 +2197,12 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value
                if target == nil {
                        target = unsafe_New(dst)
                }
+               if v.Kind() == Interface && v.IsNil() {
+                       // A nil ReadWriter passed to nil Reader is OK,
+                       // but using ifaceE2I below will panic.
+                       // Avoid the panic by returning a nil dst (e.g., Reader) explicitly.
+                       return Value{dst, nil, flag(Interface)}
+               }
                x := valueInterface(v, false)
                if dst.NumMethod() == 0 {
                        *(*interface{})(target) = x