]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: MakeFunc: allow assignment conversions on values returned from the wrapped...
authorKeith Randall <khr@google.com>
Tue, 30 Apr 2019 23:10:43 +0000 (16:10 -0700)
committerKeith Randall <khr@golang.org>
Thu, 2 May 2019 22:17:31 +0000 (22:17 +0000)
Instead of requiring exact type match, allow assignment conversions
(those conversions allowed in the language spec without a cast) on the
returned values.

Particularly useful when the type being returned is an interface type,
but the Value actually returned is a concrete value implementing that
type (as it is tricky to return a Value which has interface type).

RELNOTE=y

Fixes #28761

Change-Id: I69eef07ca51690b2086dfa1eb549db5e4724c657
Reviewed-on: https://go-review.googlesource.com/c/go/+/174531
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/reflect/all_test.go
src/reflect/value.go

index 964d8c6e951e09bfd64ff349e2413cffce672683..dbee822cea7033942e4f364bf71bc32f16b21703 100644 (file)
@@ -1934,6 +1934,91 @@ func TestMakeFuncVariadic(t *testing.T) {
        }
 }
 
+// Dummy type that implements io.WriteCloser
+type WC struct {
+}
+
+func (w *WC) Write(p []byte) (n int, err error) {
+       return 0, nil
+}
+func (w *WC) Close() error {
+       return nil
+}
+
+func TestMakeFuncValidReturnAssignments(t *testing.T) {
+       // reflect.Values returned from the wrapped function should be assignment-converted
+       // to the types returned by the result of MakeFunc.
+
+       // Concrete types should be promotable to interfaces they implement.
+       var f func() error
+       f = MakeFunc(TypeOf(f), func([]Value) []Value {
+               return []Value{ValueOf(io.EOF)}
+       }).Interface().(func() error)
+       f()
+
+       // Super-interfaces should be promotable to simpler interfaces.
+       var g func() io.Writer
+       g = MakeFunc(TypeOf(g), func([]Value) []Value {
+               var w io.WriteCloser = &WC{}
+               return []Value{ValueOf(&w).Elem()}
+       }).Interface().(func() io.Writer)
+       g()
+
+       // Channels should be promotable to directional channels.
+       var h func() <-chan int
+       h = MakeFunc(TypeOf(h), func([]Value) []Value {
+               return []Value{ValueOf(make(chan int))}
+       }).Interface().(func() <-chan int)
+       h()
+
+       // Unnamed types should be promotable to named types.
+       type T struct{ a, b, c int }
+       var i func() T
+       i = MakeFunc(TypeOf(i), func([]Value) []Value {
+               return []Value{ValueOf(struct{ a, b, c int }{a: 1, b: 2, c: 3})}
+       }).Interface().(func() T)
+       i()
+}
+
+func TestMakeFuncInvalidReturnAssignments(t *testing.T) {
+       // Type doesn't implement the required interface.
+       shouldPanic(func() {
+               var f func() error
+               f = MakeFunc(TypeOf(f), func([]Value) []Value {
+                       return []Value{ValueOf(int(7))}
+               }).Interface().(func() error)
+               f()
+       })
+       // Assigning to an interface with additional methods.
+       shouldPanic(func() {
+               var f func() io.ReadWriteCloser
+               f = MakeFunc(TypeOf(f), func([]Value) []Value {
+                       var w io.WriteCloser = &WC{}
+                       return []Value{ValueOf(&w).Elem()}
+               }).Interface().(func() io.ReadWriteCloser)
+               f()
+       })
+       // Directional channels can't be assigned to bidirectional ones.
+       shouldPanic(func() {
+               var f func() chan int
+               f = MakeFunc(TypeOf(f), func([]Value) []Value {
+                       var c <-chan int = make(chan int)
+                       return []Value{ValueOf(c)}
+               }).Interface().(func() chan int)
+               f()
+       })
+       // Two named types which are otherwise identical.
+       shouldPanic(func() {
+               type T struct{ a, b, c int }
+               type U struct{ a, b, c int }
+               var f func() T
+               f = MakeFunc(TypeOf(f), func([]Value) []Value {
+                       return []Value{ValueOf(U{a: 1, b: 2, c: 3})}
+               }).Interface().(func() T)
+               f()
+       })
+}
+
 type Point struct {
        x, y int
 }
index 79a7c6a2047e03c71e2681f61337a04890ec9d03..f0db43400951cf6f0d1981adb9ac06e3d731790d 100644 (file)
@@ -560,11 +560,6 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool) {
                }
                for i, typ := range ftyp.out() {
                        v := out[i]
-                       if v.typ != typ {
-                               panic("reflect: function created by MakeFunc using " + funcName(f) +
-                                       " returned wrong type: have " +
-                                       out[i].typ.String() + " for " + typ.String())
-                       }
                        if v.flag&flagRO != 0 {
                                panic("reflect: function created by MakeFunc using " + funcName(f) +
                                        " returned value obtained from unexported field")
@@ -574,6 +569,12 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool) {
                                continue
                        }
                        addr := add(ptr, off, "typ.size > 0")
+
+                       // Convert v to type typ if v is assignable to a variable
+                       // of type t in the language spec.
+                       // See issue 28761.
+                       v = v.assignTo("reflect.MakeFunc", typ, addr)
+
                        // We are writing to stack. No write barrier.
                        if v.flag&flagIndir != 0 {
                                memmove(addr, v.ptr, typ.size)