From eafa29bdce13d535d0b121f9b7be5783092d1496 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 29 Nov 2017 14:44:43 -0500 Subject: [PATCH] reflect: fix interface to interface conversion in Call 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 TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/reflect/all_test.go | 9 +++++++++ src/reflect/value.go | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 0a1a38dd2ed..e51d19efdd6 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -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 { diff --git a/src/reflect/value.go b/src/reflect/value.go index 0184e6820ec..d3575cae6b7 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -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