re PR go/61877 (reflect: cannot use []string as type string in Call)

PR go/61877
refect: fix direct call of variadic method value

As reported in bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61877
gcc mainline has regressed in this.  This CL adds the tests proposed
for the main Go repository:

        https://codereview.appspot.com/151280043/
        https://codereview.appspot.com/152060043/

restores the code from the amd64/386 path that makes this work and
was lost when the Go 1.3 stdlib was merged and changes the FFI path
to call into the same helper code as the amd64/386 path.

I've only tested this on amd64 but I did test a version that was
patched to unconditionally take the FFI path.

From-SVN: r215859
This commit is contained in:
Ian Lance Taylor 2014-10-03 15:51:38 +00:00
parent acbe08d242
commit 22806403ec
2 changed files with 97 additions and 39 deletions

View File

@ -1543,7 +1543,17 @@ func TestMakeFuncVariadic(t *testing.T) {
fv := MakeFunc(TypeOf(fn), func(in []Value) []Value { return in[1:2] }) fv := MakeFunc(TypeOf(fn), func(in []Value) []Value { return in[1:2] })
ValueOf(&fn).Elem().Set(fv) ValueOf(&fn).Elem().Set(fv)
r := fv.Call([]Value{ValueOf(1), ValueOf(2), ValueOf(3)})[0].Interface().([]int) r := fn(1, 2, 3)
if r[0] != 2 || r[1] != 3 {
t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
}
r = fn(1, []int{2, 3}...)
if r[0] != 2 || r[1] != 3 {
t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
}
r = fv.Call([]Value{ValueOf(1), ValueOf(2), ValueOf(3)})[0].Interface().([]int)
if r[0] != 2 || r[1] != 3 { if r[0] != 2 || r[1] != 3 {
t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1]) t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
} }
@ -1552,6 +1562,17 @@ func TestMakeFuncVariadic(t *testing.T) {
if r[0] != 2 || r[1] != 3 { if r[0] != 2 || r[1] != 3 {
t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1]) t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
} }
f := fv.Interface().(func(int, ...int) []int)
r = f(1, 2, 3)
if r[0] != 2 || r[1] != 3 {
t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
}
r = f(1, []int{2, 3}...)
if r[0] != 2 || r[1] != 3 {
t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
}
} }
type Point struct { type Point struct {
@ -1569,6 +1590,24 @@ func (p Point) Dist(scale int) int {
return p.x*p.x*scale + p.y*p.y*scale return p.x*p.x*scale + p.y*p.y*scale
} }
// This will be index 2.
func (p Point) GCMethod(k int) int {
runtime.GC()
return k + p.x
}
// This will be index 3.
func (p Point) TotalDist(points ...Point) int {
tot := 0
for _, q := range points {
dx := q.x - p.x
dy := q.y - p.y
tot += dx*dx + dy*dy // Should call Sqrt, but it's just a test.
}
return tot
}
func TestMethod(t *testing.T) { func TestMethod(t *testing.T) {
// Non-curried method of type. // Non-curried method of type.
p := Point{3, 4} p := Point{3, 4}
@ -1751,6 +1790,37 @@ func TestMethodValue(t *testing.T) {
} }
} }
func TestVariadicMethodValue(t *testing.T) {
p := Point{3, 4}
points := []Point{{20, 21}, {22, 23}, {24, 25}}
want := int64(p.TotalDist(points[0], points[1], points[2]))
// Curried method of value.
tfunc := TypeOf((func(...Point) int)(nil))
v := ValueOf(p).Method(3)
if tt := v.Type(); tt != tfunc {
t.Errorf("Variadic Method Type is %s; want %s", tt, tfunc)
}
i := ValueOf(v.Interface()).Call([]Value{ValueOf(points[0]), ValueOf(points[1]), ValueOf(points[2])})[0].Int()
if i != want {
t.Errorf("Variadic Method returned %d; want %d", i, want)
}
i = ValueOf(v.Interface()).CallSlice([]Value{ValueOf(points)})[0].Int()
if i != want {
t.Errorf("Variadic Method CallSlice returned %d; want %d", i, want)
}
f := v.Interface().(func(...Point) int)
i = int64(f(points[0], points[1], points[2]))
if i != want {
t.Errorf("Variadic Method Interface returned %d; want %d", i, want)
}
i = int64(f(points...))
if i != want {
t.Errorf("Variadic Method Interface Slice returned %d; want %d", i, want)
}
}
// Reflect version of $GOROOT/test/method5.go // Reflect version of $GOROOT/test/method5.go
// Concrete types implementing M method. // Concrete types implementing M method.
@ -3718,11 +3788,6 @@ func TestReflectFuncTraceback(t *testing.T) {
f.Call([]Value{}) f.Call([]Value{})
} }
func (p Point) GCMethod(k int) int {
runtime.GC()
return k + p.x
}
func TestReflectMethodTraceback(t *testing.T) { func TestReflectMethodTraceback(t *testing.T) {
p := Point{3, 4} p := Point{3, 4}
m := ValueOf(p).MethodByName("GCMethod") m := ValueOf(p).MethodByName("GCMethod")

View File

@ -117,29 +117,21 @@ func makeMethodValue(op string, v Value) Value {
ftyp := (*funcType)(unsafe.Pointer(t)) ftyp := (*funcType)(unsafe.Pointer(t))
method := int(v.flag) >> flagMethodShift method := int(v.flag) >> flagMethodShift
var code uintptr fv := &makeFuncImpl{
var ffi *ffiData typ: ftyp,
method: method,
rcvr: rcvr,
}
switch runtime.GOARCH { switch runtime.GOARCH {
case "amd64", "386": case "amd64", "386":
// Indirect Go func value (dummy) to obtain actual // Indirect Go func value (dummy) to obtain actual
// code address. (A Go func value is a pointer to a C // code address. (A Go func value is a pointer to a C
// function pointer. http://golang.org/s/go11func.) // function pointer. http://golang.org/s/go11func.)
dummy := makeFuncStub dummy := makeFuncStub
code = **(**uintptr)(unsafe.Pointer(&dummy)) fv.code = **(**uintptr)(unsafe.Pointer(&dummy))
default: default:
code, ffi = makeFuncFFI(ftyp, fv.code, fv.ffi = makeFuncFFI(ftyp, fv.call)
func(in []Value) []Value {
m := rcvr.Method(method)
return m.Call(in)
})
}
fv := &makeFuncImpl{
code: code,
typ: ftyp,
method: method,
rcvr: rcvr,
ffi: ffi,
} }
return Value{ft, unsafe.Pointer(&fv), v.flag&flagRO | flag(Func)<<flagKindShift | flagIndir} return Value{ft, unsafe.Pointer(&fv), v.flag&flagRO | flag(Func)<<flagKindShift | flagIndir}
@ -160,28 +152,21 @@ func makeValueMethod(v Value) Value {
t := typ.common() t := typ.common()
ftyp := (*funcType)(unsafe.Pointer(t)) ftyp := (*funcType)(unsafe.Pointer(t))
var code uintptr impl := &makeFuncImpl{
var ffi *ffiData typ: ftyp,
method: -2,
rcvr: v,
}
switch runtime.GOARCH { switch runtime.GOARCH {
case "amd64", "386": case "amd64", "386":
// Indirect Go func value (dummy) to obtain actual // Indirect Go func value (dummy) to obtain actual
// code address. (A Go func value is a pointer to a C // code address. (A Go func value is a pointer to a C
// function pointer. http://golang.org/s/go11func.) // function pointer. http://golang.org/s/go11func.)
dummy := makeFuncStub dummy := makeFuncStub
code = **(**uintptr)(unsafe.Pointer(&dummy)) impl.code = **(**uintptr)(unsafe.Pointer(&dummy))
default: default:
code, ffi = makeFuncFFI(ftyp, impl.code, impl.ffi = makeFuncFFI(ftyp, impl.call)
func(in []Value) []Value {
return v.Call(in)
})
}
impl := &makeFuncImpl{
code: code,
typ: ftyp,
method: -2,
rcvr: v,
ffi: ffi,
} }
return Value{t, unsafe.Pointer(&impl), flag(Func<<flagKindShift) | flagIndir} return Value{t, unsafe.Pointer(&impl), flag(Func<<flagKindShift) | flagIndir}
@ -192,9 +177,17 @@ func (c *makeFuncImpl) call(in []Value) []Value {
if c.method == -1 { if c.method == -1 {
return c.fn(in) return c.fn(in)
} else if c.method == -2 { } else if c.method == -2 {
return c.rcvr.Call(in) if c.typ.IsVariadic() {
return c.rcvr.CallSlice(in)
} else {
return c.rcvr.Call(in)
}
} else { } else {
m := c.rcvr.Method(c.method) m := c.rcvr.Method(c.method)
return m.Call(in) if c.typ.IsVariadic() {
return m.CallSlice(in)
} else {
return m.Call(in)
}
} }
} }