Commit e1ee3b5d authored by Russ Cox's avatar Russ Cox

reflect: add Type.Implements, Type.AssignableTo, Value.CallSlice; make Set match Go

This CL makes reflect require that values be assignable to the target type
in exactly the same places where that is the rule in Go.  It also adds
the Implements and AssignableTo methods so that callers can check
the types themselves so as to avoid a panic.

Before this CL, reflect required strict type identity.

This CL expands Call to accept and correctly marshal arbitrary
argument lists for variadic functions; it introduces CallSlice for use
in the case where the slice for the variadic argument is already known.

Fixes #327.
Fixes #1212.

R=r, dsymonds
CC=golang-dev
https://golang.org/cl/4439058
parent ec735c4e
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package reflect_test package reflect_test
import ( import (
"bytes"
"container/vector" "container/vector"
"fmt" "fmt"
"io" "io"
...@@ -1449,3 +1450,20 @@ func TestSlice(t *testing.T) { ...@@ -1449,3 +1450,20 @@ func TestSlice(t *testing.T) {
t.Errorf("xa.Slice(2, 5) = %v", v) t.Errorf("xa.Slice(2, 5) = %v", v)
} }
} }
func TestVariadic(t *testing.T) {
var b bytes.Buffer
V := NewValue
b.Reset()
V(fmt.Fprintf).Call([]Value{V(&b), V("%s, %d world"), V("hello"), V(42)})
if b.String() != "hello, 42 world" {
t.Errorf("after Fprintf Call: %q != %q", b.String(), "hello 42 world")
}
b.Reset()
V(fmt.Fprintf).CallSlice([]Value{V(&b), V("%s, %d world"), V([]interface{}{"hello", 42})})
if b.String() != "hello, 42 world" {
t.Errorf("after Fprintf CallSlice: %q != %q", b.String(), "hello 42 world")
}
}
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package reflect_test
import (
"bytes"
"go/ast"
"io"
. "reflect"
"testing"
"unsafe"
)
type MyBuffer bytes.Buffer
func TestImplicitMapConversion(t *testing.T) {
// Test implicit conversions in MapIndex and SetMapIndex.
{
// direct
m := make(map[int]int)
mv := NewValue(m)
mv.SetMapIndex(NewValue(1), NewValue(2))
x, ok := m[1]
if x != 2 {
t.Errorf("#1 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
}
if n := mv.MapIndex(NewValue(1)).Interface().(int); n != 2 {
t.Errorf("#1 MapIndex(1) = %d", n)
}
}
{
// convert interface key
m := make(map[interface{}]int)
mv := NewValue(m)
mv.SetMapIndex(NewValue(1), NewValue(2))
x, ok := m[1]
if x != 2 {
t.Errorf("#2 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
}
if n := mv.MapIndex(NewValue(1)).Interface().(int); n != 2 {
t.Errorf("#2 MapIndex(1) = %d", n)
}
}
{
// convert interface value
m := make(map[int]interface{})
mv := NewValue(m)
mv.SetMapIndex(NewValue(1), NewValue(2))
x, ok := m[1]
if x != 2 {
t.Errorf("#3 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
}
if n := mv.MapIndex(NewValue(1)).Interface().(int); n != 2 {
t.Errorf("#3 MapIndex(1) = %d", n)
}
}
{
// convert both interface key and interface value
m := make(map[interface{}]interface{})
mv := NewValue(m)
mv.SetMapIndex(NewValue(1), NewValue(2))
x, ok := m[1]
if x != 2 {
t.Errorf("#4 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
}
if n := mv.MapIndex(NewValue(1)).Interface().(int); n != 2 {
t.Errorf("#4 MapIndex(1) = %d", n)
}
}
{
// convert both, with non-empty interfaces
m := make(map[io.Reader]io.Writer)
mv := NewValue(m)
b1 := new(bytes.Buffer)
b2 := new(bytes.Buffer)
mv.SetMapIndex(NewValue(b1), NewValue(b2))
x, ok := m[b1]
if x != b2 {
t.Errorf("#5 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
}
if p := mv.MapIndex(NewValue(b1)).Elem().Pointer(); p != uintptr(unsafe.Pointer(b2)) {
t.Errorf("#5 MapIndex(b1) = %p want %p", p, b2)
}
}
{
// convert channel direction
m := make(map[<-chan int]chan int)
mv := NewValue(m)
c1 := make(chan int)
c2 := make(chan int)
mv.SetMapIndex(NewValue(c1), NewValue(c2))
x, ok := m[c1]
if x != c2 {
t.Errorf("#6 after SetMapIndex(c1, c2): %p (!= %p), %t (map=%v)", x, c2, ok, m)
}
if p := mv.MapIndex(NewValue(c1)).Pointer(); p != NewValue(c2).Pointer() {
t.Errorf("#6 MapIndex(c1) = %p want %p", p, c2)
}
}
{
// convert identical underlying types
// TODO(rsc): Should be able to define MyBuffer here.
// 6l prints very strange messages about .this.Bytes etc
// when we do that though, so MyBuffer is defined
// at top level.
m := make(map[*MyBuffer]*bytes.Buffer)
mv := NewValue(m)
b1 := new(MyBuffer)
b2 := new(bytes.Buffer)
mv.SetMapIndex(NewValue(b1), NewValue(b2))
x, ok := m[b1]
if x != b2 {
t.Errorf("#7 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
}
if p := mv.MapIndex(NewValue(b1)).Pointer(); p != uintptr(unsafe.Pointer(b2)) {
t.Errorf("#7 MapIndex(b1) = %p want %p", p, b2)
}
}
}
func TestImplicitSetConversion(t *testing.T) {
// Assume TestImplicitMapConversion covered the basics.
// Just make sure conversions are being applied at all.
var r io.Reader
b := new(bytes.Buffer)
rv := NewValue(&r).Elem()
rv.Set(NewValue(b))
if r != b {
t.Errorf("after Set: r=%T(%v)", r, r)
}
}
func TestImplicitSendConversion(t *testing.T) {
c := make(chan io.Reader, 10)
b := new(bytes.Buffer)
NewValue(c).Send(NewValue(b))
if bb := <-c; bb != b {
t.Errorf("Received %p != %p", bb, b)
}
}
func TestImplicitCallConversion(t *testing.T) {
// Arguments must be assignable to parameter types.
fv := NewValue(io.WriteString)
b := new(bytes.Buffer)
fv.Call([]Value{NewValue(b), NewValue("hello world")})
if b.String() != "hello world" {
t.Errorf("After call: string=%q want %q", b.String(), "hello world")
}
}
func TestImplicitAppendConversion(t *testing.T) {
// Arguments must be assignable to the slice's element type.
s := []io.Reader{}
sv := NewValue(&s).Elem()
b := new(bytes.Buffer)
sv.Set(Append(sv, NewValue(b)))
if len(s) != 1 || s[0] != b {
t.Errorf("after append: s=%v want [%p]", s, b)
}
}
var implementsTests = []struct {
x interface{}
t interface{}
b bool
}{
{new(*bytes.Buffer), new(io.Reader), true},
{new(bytes.Buffer), new(io.Reader), false},
{new(*bytes.Buffer), new(io.ReaderAt), false},
{new(*ast.Ident), new(ast.Expr), true},
}
func TestImplements(t *testing.T) {
for _, tt := range implementsTests {
xv := Typeof(tt.x).Elem()
xt := Typeof(tt.t).Elem()
if b := xv.Implements(xt); b != tt.b {
t.Errorf("(%s).Implements(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
}
}
}
var assignableTests = []struct {
x interface{}
t interface{}
b bool
}{
{new(chan int), new(<-chan int), true},
{new(<-chan int), new(chan int), false},
{new(*int), new(IntPtr), true},
{new(IntPtr), new(*int), true},
{new(IntPtr), new(IntPtr1), false},
// test runs implementsTests too
}
type IntPtr *int
type IntPtr1 *int
func TestAssignableTo(t *testing.T) {
for _, tt := range append(assignableTests, implementsTests...) {
xv := Typeof(tt.x).Elem()
xt := Typeof(tt.t).Elem()
if b := xv.AssignableTo(xt); b != tt.b {
t.Errorf("(%s).AssignableTo(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
}
}
}
...@@ -73,6 +73,12 @@ type Type interface { ...@@ -73,6 +73,12 @@ type Type interface {
// Kind returns the specific kind of this type. // Kind returns the specific kind of this type.
Kind() Kind Kind() Kind
// Implements returns true if the type implements the interface type u.
Implements(u Type) bool
// AssignableTo returns true if a value of the type is assignable to type u.
AssignableTo(u Type) bool
// Methods applicable only to some types, depending on Kind. // Methods applicable only to some types, depending on Kind.
// The methods allowed for each kind are: // The methods allowed for each kind are:
// //
...@@ -888,3 +894,168 @@ func PtrTo(t Type) Type { ...@@ -888,3 +894,168 @@ func PtrTo(t Type) Type {
ptrMap.Unlock() ptrMap.Unlock()
return p.commonType.toType() return p.commonType.toType()
} }
func (t *commonType) Implements(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.Implements")
}
if u.Kind() != Interface {
panic("reflect: non-interface type passed to Type.Implements")
}
return implements(u.(*commonType), t)
}
func (t *commonType) AssignableTo(u Type) bool {
if u == nil {
panic("reflect: nil type passed to Type.AssignableTo")
}
uu := u.(*commonType)
return directlyAssignable(uu, t) || implements(uu, t)
}
// implements returns true if the type V implements the interface type T.
func implements(T, V *commonType) bool {
if T.Kind() != Interface {
return false
}
t := (*interfaceType)(unsafe.Pointer(T))
if len(t.methods) == 0 {
return true
}
// The same algorithm applies in both cases, but the
// method tables for an interface type and a concrete type
// are different, so the code is duplicated.
// In both cases the algorithm is a linear scan over the two
// lists - T's methods and V's methods - simultaneously.
// Since method tables are stored in a unique sorted order
// (alphabetical, with no duplicate method names), the scan
// through V's methods must hit a match for each of T's
// methods along the way, or else V does not implement T.
// This lets us run the scan in overall linear time instead of
// the quadratic time a naive search would require.
// See also ../runtime/iface.c.
if V.Kind() == Interface {
v := (*interfaceType)(unsafe.Pointer(V))
i := 0
for j := 0; j < len(v.methods); j++ {
tm := &t.methods[i]
vm := &v.methods[j]
// TODO(rsc): && vm.pkgPath == tm.pkgPath should be here
// but it breaks the *ast.Ident vs ast.Expr test.
if vm.name == tm.name && vm.typ == tm.typ {
if i++; i >= len(t.methods) {
return true
}
}
}
return false
}
v := V.uncommon()
if v == nil {
return false
}
i := 0
for j := 0; j < len(v.methods); j++ {
tm := &t.methods[i]
vm := &v.methods[j]
// TODO(rsc): && vm.pkgPath == tm.pkgPath should be here
// but it breaks the *ast.Ident vs ast.Expr test.
if vm.name == tm.name && vm.mtyp == tm.typ {
if i++; i >= len(t.methods) {
return true
}
}
}
return false
}
// directlyAssignable returns true if a value x of type V can be directly
// assigned (using memmove) to a value of type T.
// http://golang.org/doc/go_spec.html#Assignability
// Ignoring the interface rules (implemented elsewhere)
// and the ideal constant rules (no ideal constants at run time).
func directlyAssignable(T, V *commonType) bool {
// x's type V is identical to T?
if T == V {
return true
}
// Otherwise at least one of T and V must be unnamed
// and they must have the same kind.
if T.Name() != "" && V.Name() != "" || T.Kind() != V.Kind() {
return false
}
// x's type T and V have identical underlying types.
// Since at least one is unnamed, only the composite types
// need to be considered.
switch T.Kind() {
case Array:
return T.Elem() == V.Elem() && T.Len() == V.Len()
case Chan:
// Special case:
// x is a bidirectional channel value, T is a channel type,
// and x's type V and T have identical element types.
if V.ChanDir() == BothDir && T.Elem() == V.Elem() {
return true
}
// Otherwise continue test for identical underlying type.
return V.ChanDir() == T.ChanDir() && T.Elem() == V.Elem()
case Func:
t := (*funcType)(unsafe.Pointer(T))
v := (*funcType)(unsafe.Pointer(V))
if t.dotdotdot != v.dotdotdot || len(t.in) != len(v.in) || len(t.out) != len(v.out) {
return false
}
for i, typ := range t.in {
if typ != v.in[i] {
return false
}
}
for i, typ := range t.out {
if typ != v.out[i] {
return false
}
}
return true
case Interface:
t := (*interfaceType)(unsafe.Pointer(T))
v := (*interfaceType)(unsafe.Pointer(V))
if len(t.methods) == 0 && len(v.methods) == 0 {
return true
}
// Might have the same methods but still
// need a run time conversion.
return false
case Map:
return T.Key() == V.Key() && T.Elem() == V.Elem()
case Ptr, Slice:
return T.Elem() == V.Elem()
case Struct:
t := (*structType)(unsafe.Pointer(T))
v := (*structType)(unsafe.Pointer(V))
if len(t.fields) != len(v.fields) {
return false
}
for i := range t.fields {
tf := &t.fields[i]
vf := &v.fields[i]
if tf.name != vf.name || tf.pkgPath != vf.pkgPath ||
tf.typ != vf.typ || tf.tag != vf.tag || tf.offset != vf.offset {
return false
}
}
return true
}
return false
}
This diff is collapsed.
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