Commit fe837316 authored by Keith Randall's avatar Keith Randall Committed by Keith Randall

reflect: MakeFunc: allow assignment conversions on values returned from the wrapped function

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: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent bf35b7c8
......@@ -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
}
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment