aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/reflect
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2021-07-30 14:28:58 -0700
committerIan Lance Taylor <iant@golang.org>2021-08-12 20:23:07 -0700
commitc5b21c3f4c17b0649155035d2f9aa97b2da8a813 (patch)
treec6d3a68b503ba5b16182acbb958e3e5dbc95a43b /libgo/go/reflect
parent72be20e20299ec57b4bc9ba03d5b7d6bf10e97cc (diff)
downloadgcc-c5b21c3f4c17b0649155035d2f9aa97b2da8a813.zip
gcc-c5b21c3f4c17b0649155035d2f9aa97b2da8a813.tar.gz
gcc-c5b21c3f4c17b0649155035d2f9aa97b2da8a813.tar.bz2
libgo: update to Go1.17rc2
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/341629
Diffstat (limited to 'libgo/go/reflect')
-rw-r--r--libgo/go/reflect/all_test.go410
-rw-r--r--libgo/go/reflect/internal/example1/example.go6
-rw-r--r--libgo/go/reflect/internal/example2/example.go6
-rw-r--r--libgo/go/reflect/type.go74
-rw-r--r--libgo/go/reflect/value.go66
-rw-r--r--libgo/go/reflect/visiblefields.go105
-rw-r--r--libgo/go/reflect/visiblefields_test.go330
7 files changed, 816 insertions, 181 deletions
diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go
index c73ef47..7fa815f 100644
--- a/libgo/go/reflect/all_test.go
+++ b/libgo/go/reflect/all_test.go
@@ -15,6 +15,8 @@ import (
"math/rand"
"os"
. "reflect"
+ "reflect/internal/example1"
+ "reflect/internal/example2"
"runtime"
"sort"
"strconv"
@@ -1945,6 +1947,22 @@ func BenchmarkCall(b *testing.B) {
})
}
+type myint int64
+
+func (i *myint) inc() {
+ *i = *i + 1
+}
+
+func BenchmarkCallMethod(b *testing.B) {
+ b.ReportAllocs()
+ z := new(myint)
+
+ v := ValueOf(z.inc)
+ for i := 0; i < b.N; i++ {
+ v.Call(nil)
+ }
+}
+
func BenchmarkCallArgCopy(b *testing.B) {
byteArray := func(n int) Value {
return Zero(ArrayOf(n, TypeOf(byte(0))))
@@ -2903,6 +2921,7 @@ func TestFieldPkgPath(t *testing.T) {
index []int
pkgPath string
embedded bool
+ exported bool
}
checkPkgPath := func(name string, s []pkgpathTest) {
@@ -2914,27 +2933,63 @@ func TestFieldPkgPath(t *testing.T) {
if got, want := f.Anonymous, test.embedded; got != want {
t.Errorf("%s: Field(%d).Anonymous = %v, want %v", name, test.index, got, want)
}
+ if got, want := f.IsExported(), test.exported; got != want {
+ t.Errorf("%s: Field(%d).IsExported = %v, want %v", name, test.index, got, want)
+ }
}
}
checkPkgPath("testStruct", []pkgpathTest{
- {[]int{0}, "", false}, // Exported
- {[]int{1}, "reflect_test", false}, // unexported
- {[]int{2}, "", true}, // OtherPkgFields
- {[]int{2, 0}, "", false}, // OtherExported
- {[]int{2, 1}, "reflect", false}, // otherUnexported
- {[]int{3}, "reflect_test", true}, // int
- {[]int{4}, "reflect_test", true}, // *x
+ {[]int{0}, "", false, true}, // Exported
+ {[]int{1}, "reflect_test", false, false}, // unexported
+ {[]int{2}, "", true, true}, // OtherPkgFields
+ {[]int{2, 0}, "", false, true}, // OtherExported
+ {[]int{2, 1}, "reflect", false, false}, // otherUnexported
+ {[]int{3}, "reflect_test", true, false}, // int
+ {[]int{4}, "reflect_test", true, false}, // *x
})
type localOtherPkgFields OtherPkgFields
typ = TypeOf(localOtherPkgFields{})
checkPkgPath("localOtherPkgFields", []pkgpathTest{
- {[]int{0}, "", false}, // OtherExported
- {[]int{1}, "reflect", false}, // otherUnexported
+ {[]int{0}, "", false, true}, // OtherExported
+ {[]int{1}, "reflect", false, false}, // otherUnexported
})
}
+func TestMethodPkgPath(t *testing.T) {
+ type I interface {
+ x()
+ X()
+ }
+ typ := TypeOf((*interface {
+ I
+ y()
+ Y()
+ })(nil)).Elem()
+
+ tests := []struct {
+ name string
+ pkgPath string
+ exported bool
+ }{
+ {"X", "", true},
+ {"Y", "", true},
+ {"x", "reflect_test", false},
+ {"y", "reflect_test", false},
+ }
+
+ for _, test := range tests {
+ m, _ := typ.MethodByName(test.name)
+ if got, want := m.PkgPath, test.pkgPath; got != want {
+ t.Errorf("MethodByName(%q).PkgPath = %q, want %q", test.name, got, want)
+ }
+ if got, want := m.IsExported(), test.exported; got != want {
+ t.Errorf("MethodByName(%q).IsExported = %v, want %v", test.name, got, want)
+ }
+ }
+}
+
func TestVariadicType(t *testing.T) {
// Test example from Type documentation.
var f func(x int, y ...float64)
@@ -3765,8 +3820,22 @@ type Empty struct{}
type MyStruct struct {
x int `some:"tag"`
}
+type MyStruct1 struct {
+ x struct {
+ int `some:"bar"`
+ }
+}
+type MyStruct2 struct {
+ x struct {
+ int `some:"foo"`
+ }
+}
type MyString string
type MyBytes []byte
+type MyBytesArrayPtr0 *[0]byte
+type MyBytesArrayPtr *[4]byte
+type MyBytesArray0 [0]byte
+type MyBytesArray [4]byte
type MyRunes []int32
type MyFunc func()
type MyByte byte
@@ -4073,6 +4142,30 @@ var convertTests = []struct {
{V(MyString("runes♝")), V(MyRunes("runes♝"))},
{V(MyRunes("runes♕")), V(MyString("runes♕"))},
+ // slice to array pointer
+ {V([]byte(nil)), V((*[0]byte)(nil))},
+ {V([]byte{}), V(new([0]byte))},
+ {V([]byte{7}), V(&[1]byte{7})},
+ {V(MyBytes([]byte(nil))), V((*[0]byte)(nil))},
+ {V(MyBytes([]byte{})), V(new([0]byte))},
+ {V(MyBytes([]byte{9})), V(&[1]byte{9})},
+ {V([]byte(nil)), V(MyBytesArrayPtr0(nil))},
+ {V([]byte{}), V(MyBytesArrayPtr0(new([0]byte)))},
+ {V([]byte{1, 2, 3, 4}), V(MyBytesArrayPtr(&[4]byte{1, 2, 3, 4}))},
+ {V(MyBytes([]byte{})), V(MyBytesArrayPtr0(new([0]byte)))},
+ {V(MyBytes([]byte{5, 6, 7, 8})), V(MyBytesArrayPtr(&[4]byte{5, 6, 7, 8}))},
+
+ {V([]byte(nil)), V((*MyBytesArray0)(nil))},
+ {V([]byte{}), V((*MyBytesArray0)(new([0]byte)))},
+ {V([]byte{1, 2, 3, 4}), V(&MyBytesArray{1, 2, 3, 4})},
+ {V(MyBytes([]byte(nil))), V((*MyBytesArray0)(nil))},
+ {V(MyBytes([]byte{})), V((*MyBytesArray0)(new([0]byte)))},
+ {V(MyBytes([]byte{5, 6, 7, 8})), V(&MyBytesArray{5, 6, 7, 8})},
+ {V(new([0]byte)), V(new(MyBytesArray0))},
+ {V(new(MyBytesArray0)), V(new([0]byte))},
+ {V(MyBytesArrayPtr0(nil)), V((*[0]byte)(nil))},
+ {V((*[0]byte)(nil)), V(MyBytesArrayPtr0(nil))},
+
// named types and equal underlying types
{V(new(int)), V(new(integer))},
{V(new(integer)), V(new(int))},
@@ -4115,6 +4208,9 @@ var convertTests = []struct {
x int `some:"bar"`
}{}), V(MyStruct{})},
+ {V(MyStruct1{}), V(MyStruct2{})},
+ {V(MyStruct2{}), V(MyStruct1{})},
+
// can convert *byte and *MyByte
{V((*byte)(nil)), V((*MyByte)(nil))},
{V((*MyByte)(nil)), V((*byte)(nil))},
@@ -4218,6 +4314,9 @@ func TestConvert(t *testing.T) {
// vout1 represents the in value converted to the in type.
v1 := tt.in
+ if !v1.CanConvert(t1) {
+ t.Errorf("ValueOf(%T(%[1]v)).CanConvert(%s) = false, want true", tt.in.Interface(), t1)
+ }
vout1 := v1.Convert(t1)
out1 := vout1.Interface()
if vout1.Type() != tt.in.Type() || !DeepEqual(out1, tt.in.Interface()) {
@@ -4225,11 +4324,17 @@ func TestConvert(t *testing.T) {
}
// vout2 represents the in value converted to the out type.
+ if !v1.CanConvert(t2) {
+ t.Errorf("ValueOf(%T(%[1]v)).CanConvert(%s) = false, want true", tt.in.Interface(), t2)
+ }
vout2 := v1.Convert(t2)
out2 := vout2.Interface()
if vout2.Type() != tt.out.Type() || !DeepEqual(out2, tt.out.Interface()) {
t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t2, out2, tt.out.Interface())
}
+ if got, want := vout2.Kind(), vout2.Type().Kind(); got != want {
+ t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) has internal kind %v want %v", tt.in.Interface(), t1, got, want)
+ }
// vout3 represents a new value of the out type, set to vout2. This makes
// sure the converted value vout2 is really usable as a regular value.
@@ -4274,6 +4379,22 @@ func TestConvert(t *testing.T) {
}
}
+func TestConvertPanic(t *testing.T) {
+ s := make([]byte, 4)
+ p := new([8]byte)
+ v := ValueOf(s)
+ pt := TypeOf(p)
+ if !v.Type().ConvertibleTo(pt) {
+ t.Errorf("[]byte should be convertible to *[8]byte")
+ }
+ if v.CanConvert(pt) {
+ t.Errorf("slice with length 4 should not be convertible to *[8]byte")
+ }
+ shouldPanic("reflect: cannot convert slice with length 4 to pointer to array with length 8", func() {
+ _ = v.Convert(pt)
+ })
+}
+
var gFloat32 float32
func TestConvertNaNs(t *testing.T) {
@@ -4612,6 +4733,14 @@ func TestArrayOfDirectIface(t *testing.T) {
}
}
+// Ensure passing in negative lengths panics.
+// See https://golang.org/issue/43603
+func TestArrayOfPanicOnNegativeLength(t *testing.T) {
+ shouldPanic("reflect: negative length passed to ArrayOf", func() {
+ ArrayOf(-1, TypeOf(byte(0)))
+ })
+}
+
func TestSliceOf(t *testing.T) {
// check construction and use of type not in binary
type T int
@@ -6357,148 +6486,142 @@ func clobber() {
runtime.GC()
}
-type funcLayoutTest struct {
- rcvr, t Type
- size, argsize, retOffset uintptr
- stack []byte // pointer bitmap: 1 is pointer, 0 is scalar
- gc []byte
-}
-
-var funcLayoutTests []funcLayoutTest
-
-func init() {
- var argAlign uintptr = PtrSize
- roundup := func(x uintptr, a uintptr) uintptr {
- return (x + a - 1) / a * a
- }
-
- funcLayoutTests = append(funcLayoutTests,
- funcLayoutTest{
- nil,
- ValueOf(func(a, b string) string { return "" }).Type(),
- 6 * PtrSize,
- 4 * PtrSize,
- 4 * PtrSize,
- []byte{1, 0, 1, 0, 1},
- []byte{1, 0, 1, 0, 1},
- })
+/*
+func TestFuncLayout(t *testing.T) {
+ align := func(x uintptr) uintptr {
+ return (x + PtrSize - 1) &^ (PtrSize - 1)
+ }
var r []byte
if PtrSize == 4 {
r = []byte{0, 0, 0, 1}
} else {
r = []byte{0, 0, 1}
}
- funcLayoutTests = append(funcLayoutTests,
- funcLayoutTest{
- nil,
- ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(),
- roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign),
- roundup(3*4, PtrSize) + PtrSize + 2,
- roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign),
- r,
- r,
- })
-
- funcLayoutTests = append(funcLayoutTests,
- funcLayoutTest{
- nil,
- ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(),
- 4 * PtrSize,
- 4 * PtrSize,
- 4 * PtrSize,
- []byte{1, 0, 1, 1},
- []byte{1, 0, 1, 1},
- })
type S struct {
a, b uintptr
c, d *byte
}
- funcLayoutTests = append(funcLayoutTests,
- funcLayoutTest{
- nil,
- ValueOf(func(a S) {}).Type(),
- 4 * PtrSize,
- 4 * PtrSize,
- 4 * PtrSize,
- []byte{0, 0, 1, 1},
- []byte{0, 0, 1, 1},
- })
-
- funcLayoutTests = append(funcLayoutTests,
- funcLayoutTest{
- ValueOf((*byte)(nil)).Type(),
- ValueOf(func(a uintptr, b *int) {}).Type(),
- roundup(3*PtrSize, argAlign),
- 3 * PtrSize,
- roundup(3*PtrSize, argAlign),
- []byte{1, 0, 1},
- []byte{1, 0, 1},
- })
- funcLayoutTests = append(funcLayoutTests,
- funcLayoutTest{
- nil,
- ValueOf(func(a uintptr) {}).Type(),
- roundup(PtrSize, argAlign),
- PtrSize,
- roundup(PtrSize, argAlign),
- []byte{},
- []byte{},
- })
-
- funcLayoutTests = append(funcLayoutTests,
- funcLayoutTest{
- nil,
- ValueOf(func() uintptr { return 0 }).Type(),
- PtrSize,
- 0,
- 0,
- []byte{},
- []byte{},
- })
-
- funcLayoutTests = append(funcLayoutTests,
- funcLayoutTest{
- ValueOf(uintptr(0)).Type(),
- ValueOf(func(a uintptr) {}).Type(),
- 2 * PtrSize,
- 2 * PtrSize,
- 2 * PtrSize,
- []byte{1},
- []byte{1},
+ type test struct {
+ rcvr, typ Type
+ size, argsize, retOffset uintptr
+ stack, gc, inRegs, outRegs []byte // pointer bitmap: 1 is pointer, 0 is scalar
+ intRegs, floatRegs int
+ floatRegSize uintptr
+ }
+ tests := []test{
+ {
+ typ: ValueOf(func(a, b string) string { return "" }).Type(),
+ size: 6 * PtrSize,
+ argsize: 4 * PtrSize,
+ retOffset: 4 * PtrSize,
+ stack: []byte{1, 0, 1, 0, 1},
+ gc: []byte{1, 0, 1, 0, 1},
+ },
+ {
+ typ: ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(),
+ size: align(align(3*4) + PtrSize + 2),
+ argsize: align(3*4) + PtrSize + 2,
+ retOffset: align(align(3*4) + PtrSize + 2),
+ stack: r,
+ gc: r,
+ },
+ {
+ typ: ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(),
+ size: 4 * PtrSize,
+ argsize: 4 * PtrSize,
+ retOffset: 4 * PtrSize,
+ stack: []byte{1, 0, 1, 1},
+ gc: []byte{1, 0, 1, 1},
+ },
+ {
+ typ: ValueOf(func(a S) {}).Type(),
+ size: 4 * PtrSize,
+ argsize: 4 * PtrSize,
+ retOffset: 4 * PtrSize,
+ stack: []byte{0, 0, 1, 1},
+ gc: []byte{0, 0, 1, 1},
+ },
+ {
+ rcvr: ValueOf((*byte)(nil)).Type(),
+ typ: ValueOf(func(a uintptr, b *int) {}).Type(),
+ size: 3 * PtrSize,
+ argsize: 3 * PtrSize,
+ retOffset: 3 * PtrSize,
+ stack: []byte{1, 0, 1},
+ gc: []byte{1, 0, 1},
+ },
+ {
+ typ: ValueOf(func(a uintptr) {}).Type(),
+ size: PtrSize,
+ argsize: PtrSize,
+ retOffset: PtrSize,
+ stack: []byte{},
+ gc: []byte{},
+ },
+ {
+ typ: ValueOf(func() uintptr { return 0 }).Type(),
+ size: PtrSize,
+ argsize: 0,
+ retOffset: 0,
+ stack: []byte{},
+ gc: []byte{},
+ },
+ {
+ rcvr: ValueOf(uintptr(0)).Type(),
+ typ: ValueOf(func(a uintptr) {}).Type(),
+ size: 2 * PtrSize,
+ argsize: 2 * PtrSize,
+ retOffset: 2 * PtrSize,
+ stack: []byte{1},
+ gc: []byte{1},
// Note: this one is tricky, as the receiver is not a pointer. But we
// pass the receiver by reference to the autogenerated pointer-receiver
// version of the function.
- })
-}
-
-func TestFuncLayout(t *testing.T) {
- t.Skip("gccgo does not use funcLayout")
- for _, lt := range funcLayoutTests {
- typ, argsize, retOffset, stack, gc, ptrs := FuncLayout(lt.t, lt.rcvr)
- if typ.Size() != lt.size {
- t.Errorf("funcLayout(%v, %v).size=%d, want %d", lt.t, lt.rcvr, typ.Size(), lt.size)
- }
- if argsize != lt.argsize {
- t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.t, lt.rcvr, argsize, lt.argsize)
- }
- if retOffset != lt.retOffset {
- t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.t, lt.rcvr, retOffset, lt.retOffset)
- }
- if !bytes.Equal(stack, lt.stack) {
- t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.t, lt.rcvr, stack, lt.stack)
- }
- if !bytes.Equal(gc, lt.gc) {
- t.Errorf("funcLayout(%v, %v).gc=%v, want %v", lt.t, lt.rcvr, gc, lt.gc)
- }
- if ptrs && len(stack) == 0 || !ptrs && len(stack) > 0 {
- t.Errorf("funcLayout(%v, %v) pointers flag=%v, want %v", lt.t, lt.rcvr, ptrs, !ptrs)
+ },
+ // TODO(mknyszek): Add tests for non-zero register count.
+ }
+ for _, lt := range tests {
+ name := lt.typ.String()
+ if lt.rcvr != nil {
+ name = lt.rcvr.String() + "." + name
}
+ t.Run(name, func(t *testing.T) {
+ defer SetArgRegs(SetArgRegs(lt.intRegs, lt.floatRegs, lt.floatRegSize))
+
+ typ, argsize, retOffset, stack, gc, inRegs, outRegs, ptrs := FuncLayout(lt.typ, lt.rcvr)
+ if typ.Size() != lt.size {
+ t.Errorf("funcLayout(%v, %v).size=%d, want %d", lt.typ, lt.rcvr, typ.Size(), lt.size)
+ }
+ if argsize != lt.argsize {
+ t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.typ, lt.rcvr, argsize, lt.argsize)
+ }
+ if retOffset != lt.retOffset {
+ t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.typ, lt.rcvr, retOffset, lt.retOffset)
+ }
+ if !bytes.Equal(stack, lt.stack) {
+ t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.typ, lt.rcvr, stack, lt.stack)
+ }
+ if !bytes.Equal(gc, lt.gc) {
+ t.Errorf("funcLayout(%v, %v).gc=%v, want %v", lt.typ, lt.rcvr, gc, lt.gc)
+ }
+ if !bytes.Equal(inRegs, lt.inRegs) {
+ t.Errorf("funcLayout(%v, %v).inRegs=%v, want %v", lt.typ, lt.rcvr, inRegs, lt.inRegs)
+ }
+ if !bytes.Equal(outRegs, lt.outRegs) {
+ t.Errorf("funcLayout(%v, %v).outRegs=%v, want %v", lt.typ, lt.rcvr, outRegs, lt.outRegs)
+ }
+ if ptrs && len(stack) == 0 || !ptrs && len(stack) > 0 {
+ t.Errorf("funcLayout(%v, %v) pointers flag=%v, want %v", lt.typ, lt.rcvr, ptrs, !ptrs)
+ }
+ })
}
}
+*/
+
func verifyGCBits(t *testing.T, typ Type, bits []byte) {
heapBits := GCBits(New(typ).Interface())
if !bytes.Equal(heapBits, bits) {
@@ -6861,21 +6984,6 @@ func TestExported(t *testing.T) {
}
*/
-type embed struct {
- EmbedWithUnexpMeth
-}
-
-/*
-func TestNameBytesAreAligned(t *testing.T) {
- typ := TypeOf(embed{})
- b := FirstMethodNameBytes(typ)
- v := uintptr(unsafe.Pointer(b))
- if v%unsafe.Alignof((*byte)(nil)) != 0 {
- t.Errorf("reflect.name.bytes pointer is not aligned: %x", v)
- }
-}
-*/
-
func TestTypeStrings(t *testing.T) {
type stringTest struct {
typ Type
@@ -7215,3 +7323,13 @@ func iterateToString(it *MapIter) string {
sort.Strings(got)
return "[" + strings.Join(got, ", ") + "]"
}
+
+func TestConvertibleTo(t *testing.T) {
+ t1 := ValueOf(example1.MyStruct{}).Type()
+ t2 := ValueOf(example2.MyStruct{}).Type()
+
+ // Shouldn't raise stack overflow
+ if t1.ConvertibleTo(t2) {
+ t.Fatalf("(%s).ConvertibleTo(%s) = true, want false", t1, t2)
+ }
+}
diff --git a/libgo/go/reflect/internal/example1/example.go b/libgo/go/reflect/internal/example1/example.go
new file mode 100644
index 0000000..0f829a8
--- /dev/null
+++ b/libgo/go/reflect/internal/example1/example.go
@@ -0,0 +1,6 @@
+package example1
+
+type MyStruct struct {
+ MyStructs []MyStruct
+ MyStruct *MyStruct
+}
diff --git a/libgo/go/reflect/internal/example2/example.go b/libgo/go/reflect/internal/example2/example.go
new file mode 100644
index 0000000..df64ba1
--- /dev/null
+++ b/libgo/go/reflect/internal/example2/example.go
@@ -0,0 +1,6 @@
+package example2
+
+type MyStruct struct {
+ MyStructs []MyStruct
+ MyStruct *MyStruct
+}
diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go
index c05ec68..0c394a2 100644
--- a/libgo/go/reflect/type.go
+++ b/libgo/go/reflect/type.go
@@ -108,9 +108,15 @@ type Type interface {
AssignableTo(u Type) bool
// ConvertibleTo reports whether a value of the type is convertible to type u.
+ // Even if ConvertibleTo returns true, the conversion may still panic.
+ // For example, a slice of type []T is convertible to *[N]T,
+ // but the conversion will panic if its length is less than N.
ConvertibleTo(u Type) bool
// Comparable reports whether values of this type are comparable.
+ // Even if Comparable returns true, the comparison may still panic.
+ // For example, values of interface type are comparable,
+ // but the comparison will panic if their dynamic type is not comparable.
Comparable() bool
// Methods applicable only to some types, depending on Kind.
@@ -224,7 +230,7 @@ type Type interface {
// See https://golang.org/issue/4876 for more details.
/*
- * These data structures are known to the compiler (../../cmd/internal/gc/reflect.go).
+ * These data structures are known to the compiler (../../cmd/internal/reflectdata/reflect.go).
* A few are known to ../runtime/type.go to convey to debuggers.
* They are also known to ../runtime/type.go.
*/
@@ -419,12 +425,13 @@ type structType struct {
// Method represents a single method.
type Method struct {
// Name is the method name.
+ Name string
+
// PkgPath is the package path that qualifies a lower case (unexported)
// method name. It is empty for upper case (exported) method names.
// The combination of PkgPath and Name uniquely identifies a method
// in a method set.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
- Name string
PkgPath string
Type Type // method type
@@ -432,6 +439,11 @@ type Method struct {
Index int // index for Type.Method
}
+// IsExported reports whether the method is exported.
+func (m Method) IsExported() bool {
+ return m.PkgPath == ""
+}
+
const (
kindDirectIface = 1 << 5
kindGCProg = 1 << 6 // Type.gc points to GC program
@@ -828,6 +840,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) {
type StructField struct {
// Name is the field name.
Name string
+
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
@@ -840,6 +853,11 @@ type StructField struct {
Anonymous bool // is an embedded field
}
+// IsExported reports whether the field is exported.
+func (f StructField) IsExported() bool {
+ return f.PkgPath == ""
+}
+
// A StructTag is the tag string in a struct field.
//
// By convention, tag strings are a concatenation of
@@ -1300,7 +1318,7 @@ func haveIdenticalType(T, V Type, cmpTags bool) bool {
return T == V
}
- if T.Name() != V.Name() || T.Kind() != V.Kind() {
+ if T.Name() != V.Name() || T.Kind() != V.Kind() || T.PkgPath() != V.PkgPath() {
return false
}
@@ -1529,7 +1547,7 @@ func MapOf(key, elem Type) Type {
// Make a map type.
// Note: flag values must match those used in the TMAP case
- // in ../cmd/compile/internal/gc/reflect.go:dtypesym.
+ // in ../cmd/compile/internal/reflectdata/reflect.go:writeType.
var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil)
mt := **(**mapType)(unsafe.Pointer(&imap))
mt.string = &s
@@ -2257,8 +2275,7 @@ func runtimeStructField(field StructField) (structField, string) {
panic("reflect.StructOf: field \"" + field.Name + "\" is anonymous but has PkgPath set")
}
- exported := field.PkgPath == ""
- if exported {
+ if field.IsExported() {
// Best-effort check for misuse.
// Since this field will be treated as exported, not much harm done if Unicode lowercase slips through.
c := field.Name[0]
@@ -2298,7 +2315,7 @@ func runtimeStructField(field StructField) (structField, string) {
// typeptrdata returns the length in bytes of the prefix of t
// containing pointer data. Anything after this offset is scalar data.
-// keep in sync with ../cmd/compile/internal/gc/reflect.go
+// keep in sync with ../cmd/compile/internal/reflectdata/reflect.go
func typeptrdata(t *rtype) uintptr {
switch t.Kind() {
case Struct:
@@ -2322,25 +2339,29 @@ func typeptrdata(t *rtype) uintptr {
}
}
-// See cmd/compile/internal/gc/reflect.go for derivation of constant.
+// See cmd/compile/internal/reflectdata/reflect.go for derivation of constant.
const maxPtrmaskBytes = 2048
-// ArrayOf returns the array type with the given count and element type.
+// ArrayOf returns the array type with the given length and element type.
// For example, if t represents int, ArrayOf(5, t) represents [5]int.
//
// If the resulting type would be larger than the available address space,
// ArrayOf panics.
-func ArrayOf(count int, elem Type) Type {
+func ArrayOf(length int, elem Type) Type {
+ if length < 0 {
+ panic("reflect: negative length passed to ArrayOf")
+ }
+
typ := elem.(*rtype)
// Look in cache.
- ckey := cacheKey{Array, typ, nil, uintptr(count)}
+ ckey := cacheKey{Array, typ, nil, uintptr(length)}
if array, ok := lookupCache.Load(ckey); ok {
return array.(Type)
}
// Look in known types.
- s := "[" + strconv.Itoa(count) + "]" + *typ.string
+ s := "[" + strconv.Itoa(length) + "]" + *typ.string
if tt := lookupType(s); tt != nil {
array := (*arrayType)(unsafe.Pointer(toType(tt).(*rtype)))
if array.elem == typ {
@@ -2358,28 +2379,27 @@ func ArrayOf(count int, elem Type) Type {
// gccgo uses a different hash.
// array.hash = fnv1(typ.hash, '[')
- // for n := uint32(count); n > 0; n >>= 8 {
+ // for n := uint32(length); n > 0; n >>= 8 {
// array.hash = fnv1(array.hash, byte(n))
// }
// array.hash = fnv1(array.hash, ']')
array.hash = typ.hash + 1 + 13
-
array.elem = typ
array.ptrToThis = nil
if typ.size > 0 {
max := ^uintptr(0) / typ.size
- if uintptr(count) > max {
+ if uintptr(length) > max {
panic("reflect.ArrayOf: array size would exceed virtual address space")
}
}
- array.size = typ.size * uintptr(count)
- if count > 0 && typ.ptrdata != 0 {
- array.ptrdata = typ.size*uintptr(count-1) + typ.ptrdata
+ array.size = typ.size * uintptr(length)
+ if length > 0 && typ.ptrdata != 0 {
+ array.ptrdata = typ.size*uintptr(length-1) + typ.ptrdata
}
array.align = typ.align
array.fieldAlign = typ.fieldAlign
array.uncommonType = nil
- array.len = uintptr(count)
+ array.len = uintptr(length)
array.slice = SliceOf(elem).(*rtype)
switch {
@@ -2388,7 +2408,7 @@ func ArrayOf(count int, elem Type) Type {
array.gcdata = nil
array.ptrdata = 0
- case count == 1:
+ case length == 1:
// In memory, 1-element array looks just like the element.
array.kind |= typ.kind & kindGCProg
array.gcdata = typ.gcdata
@@ -2397,7 +2417,7 @@ func ArrayOf(count int, elem Type) Type {
case typ.kind&kindGCProg == 0 && array.size <= maxPtrmaskBytes*8*ptrSize:
// Element is small with pointer mask; array is still small.
// Create direct pointer mask by turning each 1 bit in elem
- // into count 1 bits in larger mask.
+ // into length 1 bits in larger mask.
mask := make([]byte, (array.ptrdata/ptrSize+7)/8)
emitGCMask(mask, 0, typ, array.len)
array.gcdata = &mask[0]
@@ -2418,14 +2438,14 @@ func ArrayOf(count int, elem Type) Type {
prog = appendVarint(prog, elemWords-elemPtrs-1)
}
}
- // Repeat count-1 times.
+ // Repeat length-1 times.
if elemWords < 0x80 {
prog = append(prog, byte(elemWords|0x80))
} else {
prog = append(prog, 0x80)
prog = appendVarint(prog, elemWords)
}
- prog = appendVarint(prog, uintptr(count)-1)
+ prog = appendVarint(prog, uintptr(length)-1)
prog = append(prog, 0)
*(*uint32)(unsafe.Pointer(&prog[0])) = uint32(len(prog) - 4)
array.kind |= kindGCProg
@@ -2439,9 +2459,9 @@ func ArrayOf(count int, elem Type) Type {
array.equal = nil
if eequal := etyp.equal; eequal != nil {
array.equal = func(p, q unsafe.Pointer) bool {
- for i := 0; i < count; i++ {
- pi := arrayAt(p, i, esize, "i < count")
- qi := arrayAt(q, i, esize, "i < count")
+ for i := 0; i < length; i++ {
+ pi := arrayAt(p, i, esize, "i < length")
+ qi := arrayAt(q, i, esize, "i < length")
if !eequal(pi, qi) {
return false
}
@@ -2451,7 +2471,7 @@ func ArrayOf(count int, elem Type) Type {
}
switch {
- case count == 1 && !ifaceIndir(typ):
+ case length == 1 && !ifaceIndir(typ):
// array of 1 direct iface type can be direct
array.kind |= kindDirectIface
default:
diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go
index 0b4f094..300ef1a 100644
--- a/libgo/go/reflect/value.go
+++ b/libgo/go/reflect/value.go
@@ -5,6 +5,7 @@
package reflect
import (
+ "internal/itoa"
"internal/unsafeheader"
"math"
"runtime"
@@ -350,6 +351,8 @@ func (v Value) CallSlice(in []Value) []Value {
var callGC bool // for testing; see TestCallMethodJump
+const debugReflectCall = false
+
func (v Value) call(op string, in []Value) []Value {
// Get function pointer, type.
t := (*funcType)(unsafe.Pointer(v.typ))
@@ -372,8 +375,9 @@ func (v Value) call(op string, in []Value) []Value {
isSlice := op == "CallSlice"
n := t.NumIn()
+ isVariadic := t.IsVariadic()
if isSlice {
- if !t.IsVariadic() {
+ if !isVariadic {
panic("reflect: CallSlice of non-variadic function")
}
if len(in) < n {
@@ -383,13 +387,13 @@ func (v Value) call(op string, in []Value) []Value {
panic("reflect: CallSlice with too many input arguments")
}
} else {
- if t.IsVariadic() {
+ if isVariadic {
n--
}
if len(in) < n {
panic("reflect: Call with too few input arguments")
}
- if !t.IsVariadic() && len(in) > n {
+ if !isVariadic && len(in) > n {
panic("reflect: Call with too many input arguments")
}
}
@@ -403,7 +407,7 @@ func (v Value) call(op string, in []Value) []Value {
panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String())
}
}
- if !isSlice && t.IsVariadic() {
+ if !isSlice && isVariadic {
// prepare slice for remaining values
m := len(in) - n
slice := MakeSlice(t.In(n), m, m)
@@ -857,10 +861,16 @@ func valueInterface(v Value, safe bool) interface{} {
return packEface(v)
}
-// InterfaceData returns the interface v's value as a uintptr pair.
+// InterfaceData returns a pair of unspecified uintptr values.
// It panics if v's Kind is not Interface.
+//
+// In earlier versions of Go, this function returned the interface's
+// value as a uintptr pair. As of Go 1.4, the implementation of
+// interface values precludes any defined use of InterfaceData.
+//
+// Deprecated: The memory representation of interface values is not
+// compatible with InterfaceData.
func (v Value) InterfaceData() [2]uintptr {
- // TODO: deprecate this
v.mustBe(Interface)
// We treat this as a read operation, so we allow
// it even for unexported data, because the caller
@@ -2200,9 +2210,14 @@ func New(typ Type) Value {
panic("reflect: New(nil)")
}
t := typ.(*rtype)
+ pt := t.ptrTo()
+ if ifaceIndir(pt) {
+ // This is a pointer to a go:notinheap type.
+ panic("reflect: New of type that may not be allocated in heap (possibly undefined cgo C type)")
+ }
ptr := unsafe_New(t)
fl := flag(Ptr)
- return Value{t.ptrTo(), ptr, fl}
+ return Value{pt, ptr, fl}
}
// NewAt returns a Value representing a pointer to a value of the
@@ -2255,7 +2270,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value
// Convert returns the value v converted to type t.
// If the usual Go conversion rules do not allow conversion
-// of the value v to type t, Convert panics.
+// of the value v to type t, or if converting v to type t panics, Convert panics.
func (v Value) Convert(t Type) Value {
if v.flag&flagMethod != 0 {
v = makeMethodValue("Convert", v)
@@ -2267,6 +2282,26 @@ func (v Value) Convert(t Type) Value {
return op(v, t)
}
+// CanConvert reports whether the value v can be converted to type t.
+// If v.CanConvert(t) returns true then v.Convert(t) will not panic.
+func (v Value) CanConvert(t Type) bool {
+ vt := v.Type()
+ if !vt.ConvertibleTo(t) {
+ return false
+ }
+ // Currently the only conversion that is OK in terms of type
+ // but that can panic depending on the value is converting
+ // from slice to pointer-to-array.
+ if vt.Kind() == Slice && t.Kind() == Ptr && t.Elem().Kind() == Array {
+ n := t.Elem().Len()
+ h := (*unsafeheader.Slice)(v.ptr)
+ if n > h.Len {
+ return false
+ }
+ }
+ return true
+}
+
// convertOp returns the function to convert a value of type src
// to a value of type dst. If the conversion is illegal, convertOp returns nil.
func convertOp(dst, src *rtype) func(Value, Type) Value {
@@ -2326,6 +2361,11 @@ func convertOp(dst, src *rtype) func(Value, Type) Value {
return cvtRunesString
}
}
+ // "x is a slice, T is a pointer-to-array type,
+ // and the slice and array types have identical element types."
+ if dst.Kind() == Ptr && dst.Elem().Kind() == Array && src.Elem() == dst.Elem().Elem() {
+ return cvtSliceArrayPtr
+ }
case Chan:
if dst.Kind() == Chan && specialChannelAssignability(dst, src) {
@@ -2519,6 +2559,16 @@ func cvtStringRunes(v Value, t Type) Value {
return makeRunes(v.flag.ro(), []rune(v.String()), t)
}
+// convertOp: []T -> *[N]T
+func cvtSliceArrayPtr(v Value, t Type) Value {
+ n := t.Elem().Len()
+ h := (*unsafeheader.Slice)(v.ptr)
+ if n > h.Len {
+ panic("reflect: cannot convert slice with length " + itoa.Itoa(h.Len) + " to pointer to array with length " + itoa.Itoa(n))
+ }
+ return Value{t.common(), h.Data, v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Ptr)}
+}
+
// convertOp: direct copy
func cvtDirect(v Value, typ Type) Value {
f := v.flag
diff --git a/libgo/go/reflect/visiblefields.go b/libgo/go/reflect/visiblefields.go
new file mode 100644
index 0000000..1a2b535
--- /dev/null
+++ b/libgo/go/reflect/visiblefields.go
@@ -0,0 +1,105 @@
+// Copyright 2021 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
+
+// VisibleFields returns all the visible fields in t, which must be a
+// struct type. A field is defined as visible if it's accessible
+// directly with a FieldByName call. The returned fields include fields
+// inside anonymous struct members and unexported fields. They follow
+// the same order found in the struct, with anonymous fields followed
+// immediately by their promoted fields.
+//
+// For each element e of the returned slice, the corresponding field
+// can be retrieved from a value v of type t by calling v.FieldByIndex(e.Index).
+func VisibleFields(t Type) []StructField {
+ if t == nil {
+ panic("reflect: VisibleFields(nil)")
+ }
+ if t.Kind() != Struct {
+ panic("reflect.VisibleFields of non-struct type")
+ }
+ w := &visibleFieldsWalker{
+ byName: make(map[string]int),
+ visiting: make(map[Type]bool),
+ fields: make([]StructField, 0, t.NumField()),
+ index: make([]int, 0, 2),
+ }
+ w.walk(t)
+ // Remove all the fields that have been hidden.
+ // Use an in-place removal that avoids copying in
+ // the common case that there are no hidden fields.
+ j := 0
+ for i := range w.fields {
+ f := &w.fields[i]
+ if f.Name == "" {
+ continue
+ }
+ if i != j {
+ // A field has been removed. We need to shuffle
+ // all the subsequent elements up.
+ w.fields[j] = *f
+ }
+ j++
+ }
+ return w.fields[:j]
+}
+
+type visibleFieldsWalker struct {
+ byName map[string]int
+ visiting map[Type]bool
+ fields []StructField
+ index []int
+}
+
+// walk walks all the fields in the struct type t, visiting
+// fields in index preorder and appending them to w.fields
+// (this maintains the required ordering).
+// Fields that have been overridden have their
+// Name field cleared.
+func (w *visibleFieldsWalker) walk(t Type) {
+ if w.visiting[t] {
+ return
+ }
+ w.visiting[t] = true
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ w.index = append(w.index, i)
+ add := true
+ if oldIndex, ok := w.byName[f.Name]; ok {
+ old := &w.fields[oldIndex]
+ if len(w.index) == len(old.Index) {
+ // Fields with the same name at the same depth
+ // cancel one another out. Set the field name
+ // to empty to signify that has happened, and
+ // there's no need to add this field.
+ old.Name = ""
+ add = false
+ } else if len(w.index) < len(old.Index) {
+ // The old field loses because it's deeper than the new one.
+ old.Name = ""
+ } else {
+ // The old field wins because it's shallower than the new one.
+ add = false
+ }
+ }
+ if add {
+ // Copy the index so that it's not overwritten
+ // by the other appends.
+ f.Index = append([]int(nil), w.index...)
+ w.byName[f.Name] = len(w.fields)
+ w.fields = append(w.fields, f)
+ }
+ if f.Anonymous {
+ if f.Type.Kind() == Ptr {
+ f.Type = f.Type.Elem()
+ }
+ if f.Type.Kind() == Struct {
+ w.walk(f.Type)
+ }
+ }
+ w.index = w.index[:len(w.index)-1]
+ }
+ delete(w.visiting, t)
+}
diff --git a/libgo/go/reflect/visiblefields_test.go b/libgo/go/reflect/visiblefields_test.go
new file mode 100644
index 0000000..915bbee
--- /dev/null
+++ b/libgo/go/reflect/visiblefields_test.go
@@ -0,0 +1,330 @@
+// Copyright 2021 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 (
+ . "reflect"
+ "testing"
+)
+
+type structField struct {
+ name string
+ index []int
+}
+
+var fieldsTests = []struct {
+ testName string
+ val interface{}
+ expect []structField
+}{{
+ testName: "SimpleStruct",
+ val: struct {
+ A int
+ B string
+ C bool
+ }{},
+ expect: []structField{{
+ name: "A",
+ index: []int{0},
+ }, {
+ name: "B",
+ index: []int{1},
+ }, {
+ name: "C",
+ index: []int{2},
+ }},
+}, {
+ testName: "NonEmbeddedStructMember",
+ val: struct {
+ A struct {
+ X int
+ }
+ }{},
+ expect: []structField{{
+ name: "A",
+ index: []int{0},
+ }},
+}, {
+ testName: "EmbeddedExportedStruct",
+ val: struct {
+ SFG
+ }{},
+ expect: []structField{{
+ name: "SFG",
+ index: []int{0},
+ }, {
+ name: "F",
+ index: []int{0, 0},
+ }, {
+ name: "G",
+ index: []int{0, 1},
+ }},
+}, {
+ testName: "EmbeddedUnexportedStruct",
+ val: struct {
+ sFG
+ }{},
+ expect: []structField{{
+ name: "sFG",
+ index: []int{0},
+ }, {
+ name: "F",
+ index: []int{0, 0},
+ }, {
+ name: "G",
+ index: []int{0, 1},
+ }},
+}, {
+ testName: "TwoEmbeddedStructsWithCancellingMembers",
+ val: struct {
+ SFG
+ SF
+ }{},
+ expect: []structField{{
+ name: "SFG",
+ index: []int{0},
+ }, {
+ name: "G",
+ index: []int{0, 1},
+ }, {
+ name: "SF",
+ index: []int{1},
+ }},
+}, {
+ testName: "EmbeddedStructsWithSameFieldsAtDifferentDepths",
+ val: struct {
+ SFGH3
+ SG1
+ SFG2
+ SF2
+ L int
+ }{},
+ expect: []structField{{
+ name: "SFGH3",
+ index: []int{0},
+ }, {
+ name: "SFGH2",
+ index: []int{0, 0},
+ }, {
+ name: "SFGH1",
+ index: []int{0, 0, 0},
+ }, {
+ name: "SFGH",
+ index: []int{0, 0, 0, 0},
+ }, {
+ name: "H",
+ index: []int{0, 0, 0, 0, 2},
+ }, {
+ name: "SG1",
+ index: []int{1},
+ }, {
+ name: "SG",
+ index: []int{1, 0},
+ }, {
+ name: "G",
+ index: []int{1, 0, 0},
+ }, {
+ name: "SFG2",
+ index: []int{2},
+ }, {
+ name: "SFG1",
+ index: []int{2, 0},
+ }, {
+ name: "SFG",
+ index: []int{2, 0, 0},
+ }, {
+ name: "SF2",
+ index: []int{3},
+ }, {
+ name: "SF1",
+ index: []int{3, 0},
+ }, {
+ name: "SF",
+ index: []int{3, 0, 0},
+ }, {
+ name: "L",
+ index: []int{4},
+ }},
+}, {
+ testName: "EmbeddedPointerStruct",
+ val: struct {
+ *SF
+ }{},
+ expect: []structField{{
+ name: "SF",
+ index: []int{0},
+ }, {
+ name: "F",
+ index: []int{0, 0},
+ }},
+}, {
+ testName: "EmbeddedNotAPointer",
+ val: struct {
+ M
+ }{},
+ expect: []structField{{
+ name: "M",
+ index: []int{0},
+ }},
+}, {
+ testName: "RecursiveEmbedding",
+ val: Rec1{},
+ expect: []structField{{
+ name: "Rec2",
+ index: []int{0},
+ }, {
+ name: "F",
+ index: []int{0, 0},
+ }, {
+ name: "Rec1",
+ index: []int{0, 1},
+ }},
+}, {
+ testName: "RecursiveEmbedding2",
+ val: Rec2{},
+ expect: []structField{{
+ name: "F",
+ index: []int{0},
+ }, {
+ name: "Rec1",
+ index: []int{1},
+ }, {
+ name: "Rec2",
+ index: []int{1, 0},
+ }},
+}, {
+ testName: "RecursiveEmbedding3",
+ val: RS3{},
+ expect: []structField{{
+ name: "RS2",
+ index: []int{0},
+ }, {
+ name: "RS1",
+ index: []int{1},
+ }, {
+ name: "i",
+ index: []int{1, 0},
+ }},
+}}
+
+type SFG struct {
+ F int
+ G int
+}
+
+type SFG1 struct {
+ SFG
+}
+
+type SFG2 struct {
+ SFG1
+}
+
+type SFGH struct {
+ F int
+ G int
+ H int
+}
+
+type SFGH1 struct {
+ SFGH
+}
+
+type SFGH2 struct {
+ SFGH1
+}
+
+type SFGH3 struct {
+ SFGH2
+}
+
+type SF struct {
+ F int
+}
+
+type SF1 struct {
+ SF
+}
+
+type SF2 struct {
+ SF1
+}
+
+type SG struct {
+ G int
+}
+
+type SG1 struct {
+ SG
+}
+
+type sFG struct {
+ F int
+ G int
+}
+
+type RS1 struct {
+ i int
+}
+
+type RS2 struct {
+ RS1
+}
+
+type RS3 struct {
+ RS2
+ RS1
+}
+
+type M map[string]interface{}
+
+type Rec1 struct {
+ *Rec2
+}
+
+type Rec2 struct {
+ F string
+ *Rec1
+}
+
+func TestFields(t *testing.T) {
+ for _, test := range fieldsTests {
+ test := test
+ t.Run(test.testName, func(t *testing.T) {
+ typ := TypeOf(test.val)
+ fields := VisibleFields(typ)
+ if got, want := len(fields), len(test.expect); got != want {
+ t.Fatalf("unexpected field count; got %d want %d", got, want)
+ }
+
+ for j, field := range fields {
+ expect := test.expect[j]
+ t.Logf("field %d: %s", j, expect.name)
+ gotField := typ.FieldByIndex(field.Index)
+ // Unfortunately, FieldByIndex does not return
+ // a field with the same index that we passed in,
+ // so we set it to the expected value so that
+ // it can be compared later with the result of FieldByName.
+ gotField.Index = field.Index
+ expectField := typ.FieldByIndex(expect.index)
+ // ditto.
+ expectField.Index = expect.index
+ if !DeepEqual(gotField, expectField) {
+ t.Fatalf("unexpected field result\ngot %#v\nwant %#v", gotField, expectField)
+ }
+
+ // Sanity check that we can actually access the field by the
+ // expected name.
+ gotField1, ok := typ.FieldByName(expect.name)
+ if !ok {
+ t.Fatalf("field %q not accessible by name", expect.name)
+ }
+ if !DeepEqual(gotField1, expectField) {
+ t.Fatalf("unexpected FieldByName result; got %#v want %#v", gotField1, expectField)
+ }
+ }
+ })
+ }
+}