aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/reflect
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2018-09-24 21:46:21 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-09-24 21:46:21 +0000
commitdd931d9b48647e898dc80927c532ae93cc09e192 (patch)
tree71be2295cd79b8a182f6130611658db8628772d5 /libgo/go/reflect
parent779d8a5ad09b01428726ea5a0e6c87bd9ac3c0e4 (diff)
downloadgcc-dd931d9b48647e898dc80927c532ae93cc09e192.zip
gcc-dd931d9b48647e898dc80927c532ae93cc09e192.tar.gz
gcc-dd931d9b48647e898dc80927c532ae93cc09e192.tar.bz2
libgo: update to Go 1.11
Reviewed-on: https://go-review.googlesource.com/136435 gotools/: * Makefile.am (mostlyclean-local): Run chmod on check-go-dir to make sure it is writable. (check-go-tools): Likewise. (check-vet): Copy internal/objabi to check-vet-dir. * Makefile.in: Rebuild. From-SVN: r264546
Diffstat (limited to 'libgo/go/reflect')
-rw-r--r--libgo/go/reflect/all_test.go98
-rw-r--r--libgo/go/reflect/deepequal.go2
-rw-r--r--libgo/go/reflect/set_test.go7
-rw-r--r--libgo/go/reflect/type.go183
-rw-r--r--libgo/go/reflect/value.go30
5 files changed, 203 insertions, 117 deletions
diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go
index 96b57ef..eb893e9 100644
--- a/libgo/go/reflect/all_test.go
+++ b/libgo/go/reflect/all_test.go
@@ -2536,9 +2536,9 @@ func TestFieldPkgPath(t *testing.T) {
}{})
type pkgpathTest struct {
- index []int
- pkgPath string
- anonymous bool
+ index []int
+ pkgPath string
+ embedded bool
}
checkPkgPath := func(name string, s []pkgpathTest) {
@@ -2547,7 +2547,7 @@ func TestFieldPkgPath(t *testing.T) {
if got, want := f.PkgPath, test.pkgPath; got != want {
t.Errorf("%s: Field(%d).PkgPath = %q, want %q", name, test.index, got, want)
}
- if got, want := f.Anonymous, test.anonymous; got != want {
+ if got, want := f.Anonymous, test.embedded; got != want {
t.Errorf("%s: Field(%d).Anonymous = %v, want %v", name, test.index, got, want)
}
}
@@ -4833,7 +4833,20 @@ func (i StructI) Get() int { return int(i) }
type StructIPtr int
-func (i *StructIPtr) Get() int { return int(*i) }
+func (i *StructIPtr) Get() int { return int(*i) }
+func (i *StructIPtr) Set(v int) { *(*int)(i) = v }
+
+type SettableStruct struct {
+ SettableField int
+}
+
+func (p *SettableStruct) Set(v int) { p.SettableField = v }
+
+type SettablePointer struct {
+ SettableField *int
+}
+
+func (p *SettablePointer) Set(v int) { *p.SettableField = v }
/*
gccgo does not yet support StructOf with methods.
@@ -4843,6 +4856,9 @@ func TestStructOfWithInterface(t *testing.T) {
type Iface interface {
Get() int
}
+ type IfaceSet interface {
+ Set(int)
+ }
tests := []struct {
name string
typ Type
@@ -4904,7 +4920,7 @@ func TestStructOfWithInterface(t *testing.T) {
})
// We currently do not correctly implement methods
- // for anonymous fields other than the first.
+ // for embedded fields other than the first.
// Therefore, for now, we expect those methods
// to not exist. See issues 15924 and 20824.
// When those issues are fixed, this test of panic
@@ -4950,6 +4966,76 @@ func TestStructOfWithInterface(t *testing.T) {
}
}
}
+
+ // Test an embedded nil pointer with pointer methods.
+ fields := []StructField{{
+ Name: "StructIPtr",
+ Anonymous: true,
+ Type: PtrTo(TypeOf(StructIPtr(want))),
+ }}
+ rt := StructOf(fields)
+ rv := New(rt).Elem()
+ // This should panic since the pointer is nil.
+ shouldPanic(func() {
+ rv.Interface().(IfaceSet).Set(want)
+ })
+
+ // Test an embedded nil pointer to a struct with pointer methods.
+
+ fields = []StructField{{
+ Name: "SettableStruct",
+ Anonymous: true,
+ Type: PtrTo(TypeOf(SettableStruct{})),
+ }}
+ rt = StructOf(fields)
+ rv = New(rt).Elem()
+ // This should panic since the pointer is nil.
+ shouldPanic(func() {
+ rv.Interface().(IfaceSet).Set(want)
+ })
+
+ // The behavior is different if there is a second field,
+ // since now an interface value holds a pointer to the struct
+ // rather than just holding a copy of the struct.
+ fields = []StructField{
+ {
+ Name: "SettableStruct",
+ Anonymous: true,
+ Type: PtrTo(TypeOf(SettableStruct{})),
+ },
+ {
+ Name: "EmptyStruct",
+ Anonymous: true,
+ Type: StructOf(nil),
+ },
+ }
+ // With the current implementation this is expected to panic.
+ // Ideally it should work and we should be able to see a panic
+ // if we call the Set method.
+ shouldPanic(func() {
+ StructOf(fields)
+ })
+
+ // Embed a field that can be stored directly in an interface,
+ // with a second field.
+ fields = []StructField{
+ {
+ Name: "SettablePointer",
+ Anonymous: true,
+ Type: TypeOf(SettablePointer{}),
+ },
+ {
+ Name: "EmptyStruct",
+ Anonymous: true,
+ Type: StructOf(nil),
+ },
+ }
+ // With the current implementation this is expected to panic.
+ // Ideally it should work and we should be able to call the
+ // Set and Get methods.
+ shouldPanic(func() {
+ StructOf(fields)
+ })
}
*/
diff --git a/libgo/go/reflect/deepequal.go b/libgo/go/reflect/deepequal.go
index 2fdd6a3..5b6694d 100644
--- a/libgo/go/reflect/deepequal.go
+++ b/libgo/go/reflect/deepequal.go
@@ -116,7 +116,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
for _, k := range v1.MapKeys() {
val1 := v1.MapIndex(k)
val2 := v2.MapIndex(k)
- if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
+ if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited, depth+1) {
return false
}
}
diff --git a/libgo/go/reflect/set_test.go b/libgo/go/reflect/set_test.go
index 7c39623..a633e6e 100644
--- a/libgo/go/reflect/set_test.go
+++ b/libgo/go/reflect/set_test.go
@@ -14,8 +14,6 @@ import (
"unsafe"
)
-type MyBuffer bytes.Buffer
-
func TestImplicitMapConversion(t *testing.T) {
// Test implicit conversions in MapIndex and SetMapIndex.
{
@@ -102,10 +100,7 @@ func TestImplicitMapConversion(t *testing.T) {
}
{
// 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.
+ type MyBuffer bytes.Buffer
m := make(map[*MyBuffer]*bytes.Buffer)
mv := ValueOf(m)
b1 := new(MyBuffer)
diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go
index bbbef91..da7796f 100644
--- a/libgo/go/reflect/type.go
+++ b/libgo/go/reflect/type.go
@@ -68,14 +68,15 @@ type Type interface {
// NumMethod returns the number of exported methods in the type's method set.
NumMethod() int
- // Name returns the type's name within its package.
- // It returns an empty string for unnamed types.
+ // Name returns the type's name within its package for a defined type.
+ // For other (non-defined) types it returns the empty string.
Name() string
- // PkgPath returns a named type's package path, that is, the import path
+ // PkgPath returns a defined type's package path, that is, the import path
// that uniquely identifies the package, such as "encoding/base64".
- // If the type was predeclared (string, error) or unnamed (*T, struct{}, []int),
- // the package path will be the empty string.
+ // If the type was predeclared (string, error) or not defined (*T, struct{},
+ // []int, or A where A is an alias for a non-defined type), the package path
+ // will be the empty string.
PkgPath() string
// Size returns the number of bytes needed to store
@@ -166,13 +167,13 @@ type Type interface {
// the field was found.
//
// FieldByNameFunc considers the fields in the struct itself
- // and then the fields in any anonymous structs, in breadth first order,
+ // and then the fields in any embedded structs, in breadth first order,
// stopping at the shallowest nesting depth containing one or more
// fields satisfying the match function. If multiple fields at that depth
// satisfy the match function, they cancel each other
// and FieldByNameFunc returns no match.
// This behavior mirrors Go's handling of name lookup in
- // structs containing anonymous fields.
+ // structs containing embedded fields.
FieldByNameFunc(match func(string) bool) (StructField, bool)
// In returns the type of a function type's i'th input parameter.
@@ -258,9 +259,7 @@ const (
)
// rtype is the common implementation of most values.
-// It is embedded in other, public struct types, but always
-// with a unique tag like `reflect:"array"` or `reflect:"ptr"`
-// so that code cannot convert from, say, *arrayType to *ptrType.
+// It is embedded in other struct types.
//
// rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
@@ -290,10 +289,10 @@ type method struct {
tfn unsafe.Pointer // fn used for normal method call
}
-// uncommonType is present only for types with names or methods
-// (if T is a named type, the uncommonTypes for T and *T have methods).
+// uncommonType is present only for defined types or types with methods
+// (if T is a defined type, the uncommonTypes for T and *T have methods).
// Using a pointer to this struct reduces the overall size required
-// to describe an unnamed type with no methods.
+// to describe a non-defined type with no methods.
type uncommonType struct {
name *string // name of type
pkgPath *string // import path; nil for built-in types like int, string
@@ -311,7 +310,7 @@ const (
// arrayType represents a fixed array type.
type arrayType struct {
- rtype `reflect:"array"`
+ rtype
elem *rtype // array element type
slice *rtype // slice type
len uintptr
@@ -319,14 +318,14 @@ type arrayType struct {
// chanType represents a channel type.
type chanType struct {
- rtype `reflect:"chan"`
- elem *rtype // channel element type
- dir uintptr // channel direction (ChanDir)
+ rtype
+ elem *rtype // channel element type
+ dir uintptr // channel direction (ChanDir)
}
// funcType represents a function type.
type funcType struct {
- rtype `reflect:"func"`
+ rtype
dotdotdot bool // last input parameter is ...
in []*rtype // input parameter types
out []*rtype // output parameter types
@@ -341,13 +340,13 @@ type imethod struct {
// interfaceType represents an interface type.
type interfaceType struct {
- rtype `reflect:"interface"`
+ rtype
methods []imethod // sorted by hash
}
// mapType represents a map type.
type mapType struct {
- rtype `reflect:"map"`
+ rtype
key *rtype // map key type
elem *rtype // map element (value) type
bucket *rtype // internal bucket structure
@@ -362,36 +361,36 @@ type mapType struct {
// ptrType represents a pointer type.
type ptrType struct {
- rtype `reflect:"ptr"`
- elem *rtype // pointer element (pointed at) type
+ rtype
+ elem *rtype // pointer element (pointed at) type
}
// sliceType represents a slice type.
type sliceType struct {
- rtype `reflect:"slice"`
- elem *rtype // slice element type
+ rtype
+ elem *rtype // slice element type
}
// Struct field
type structField struct {
- name *string // name is always non-empty
- pkgPath *string // nil for exported Names; otherwise import path
- typ *rtype // type of field
- tag *string // nil if no tag
- offsetAnon uintptr // byte offset of field<<1 | isAnonymous
+ name *string // name is always non-empty
+ pkgPath *string // nil for exported Names; otherwise import path
+ typ *rtype // type of field
+ tag *string // nil if no tag
+ offsetEmbed uintptr // byte offset of field<<1 | isAnonymous
}
func (f *structField) offset() uintptr {
- return f.offsetAnon >> 1
+ return f.offsetEmbed >> 1
}
-func (f *structField) anon() bool {
- return f.offsetAnon&1 != 0
+func (f *structField) embedded() bool {
+ return f.offsetEmbed&1 != 0
}
// structType represents a struct type.
type structType struct {
- rtype `reflect:"struct"`
+ rtype
fields []structField // sorted by offset
}
@@ -478,6 +477,39 @@ func (t *uncommonType) Name() string {
return *t.name
}
+var methodCache sync.Map // map[*uncommonType][]method
+
+func (t *uncommonType) exportedMethods() []method {
+ methodsi, found := methodCache.Load(t)
+ if found {
+ return methodsi.([]method)
+ }
+
+ allm := t.methods
+ allExported := true
+ for _, m := range allm {
+ if m.pkgPath != nil {
+ allExported = false
+ break
+ }
+ }
+ var methods []method
+ if allExported {
+ methods = allm
+ } else {
+ methods = make([]method, 0, len(allm))
+ for _, m := range allm {
+ if m.pkgPath == nil {
+ methods = append(methods, m)
+ }
+ }
+ methods = methods[:len(methods):len(methods)]
+ }
+
+ methodsi, _ = methodCache.LoadOrStore(t, methods)
+ return methodsi.([]method)
+}
+
func (t *rtype) rawString() string { return *t.string }
func (t *rtype) String() string {
@@ -520,41 +552,12 @@ func (t *rtype) pointers() bool { return t.kind&kindNoPointers == 0 }
func (t *rtype) common() *rtype { return t }
-var methodCache sync.Map // map[*rtype][]method
-
func (t *rtype) exportedMethods() []method {
- methodsi, found := methodCache.Load(t)
- if found {
- return methodsi.([]method)
- }
-
ut := t.uncommon()
if ut == nil {
return nil
}
- allm := ut.methods
- allExported := true
- for _, m := range allm {
- if m.pkgPath != nil {
- allExported = false
- break
- }
- }
- var methods []method
- if allExported {
- methods = allm
- } else {
- methods = make([]method, 0, len(allm))
- for _, m := range allm {
- if m.pkgPath == nil {
- methods = append(methods, m)
- }
- }
- methods = methods[:len(methods):len(methods)]
- }
-
- methodsi, _ = methodCache.LoadOrStore(t, methods)
- return methodsi.([]method)
+ return ut.exportedMethods()
}
func (t *rtype) NumMethod() int {
@@ -562,9 +565,6 @@ func (t *rtype) NumMethod() int {
tt := (*interfaceType)(unsafe.Pointer(t))
return tt.NumMethod()
}
- if t.uncommonType == nil {
- return 0 // avoid methodCache synchronization
- }
return len(t.exportedMethods())
}
@@ -907,7 +907,7 @@ func (t *structType) Field(i int) (f StructField) {
p := &t.fields[i]
f.Type = toType(p.typ)
f.Name = *p.name
- f.Anonymous = p.anon()
+ f.Anonymous = p.embedded()
if p.pkgPath != nil {
f.PkgPath = *p.pkgPath
}
@@ -1001,11 +1001,11 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel
visited[t] = true
for i := range t.fields {
f := &t.fields[i]
- // Find name and (for anonymous field) type for field f.
+ // Find name and (for embedded field) type for field f.
fname := *f.name
var ntyp *rtype
- if f.anon() {
- // Anonymous field of type T or *T.
+ if f.embedded() {
+ // Embedded field of type T or *T.
ntyp = f.typ
if ntyp.Kind() == Ptr {
ntyp = ntyp.Elem().common()
@@ -1062,20 +1062,20 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel
// FieldByName returns the struct field with the given name
// and a boolean to indicate if the field was found.
func (t *structType) FieldByName(name string) (f StructField, present bool) {
- // Quick check for top-level name, or struct without anonymous fields.
- hasAnon := false
+ // Quick check for top-level name, or struct without embedded fields.
+ hasEmbeds := false
if name != "" {
for i := range t.fields {
tf := &t.fields[i]
if *tf.name == name {
return t.Field(i), true
}
- if tf.anon() {
- hasAnon = true
+ if tf.embedded() {
+ hasEmbeds = true
}
}
}
- if !hasAnon {
+ if !hasEmbeds {
return
}
return t.FieldByNameFunc(func(s string) bool { return s == name })
@@ -1274,7 +1274,7 @@ func directlyAssignable(T, V *rtype) bool {
return true
}
- // Otherwise at least one of T and V must be unnamed
+ // Otherwise at least one of T and V must not be defined
// and they must have the same kind.
if T.Name() != "" && V.Name() != "" || T.Kind() != V.Kind() {
return false
@@ -1383,7 +1383,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
if cmpTags && tf.tag != vf.tag && (tf.tag == nil || vf.tag == nil || *tf.tag != *vf.tag) {
return false
}
- if tf.offsetAnon != vf.offsetAnon {
+ if tf.offsetEmbed != vf.offsetEmbed {
return false
}
}
@@ -1715,7 +1715,7 @@ func needKeyUpdate(t *rtype) bool {
}
}
-// Make sure these routines stay in sync with ../../runtime/hashmap.go!
+// Make sure these routines stay in sync with ../../runtime/map.go!
// These types exist only for GC, so we only fill out GC relevant info.
// Currently, that's just size and the GC program. We also fill in string
// for possible debugging use.
@@ -1726,7 +1726,7 @@ const (
)
func bucketOf(ktyp, etyp *rtype) *rtype {
- // See comment on hmap.overflow in ../runtime/hashmap.go.
+ // See comment on hmap.overflow in ../runtime/map.go.
var kind uint8
if ktyp.kind&kindNoPointers != 0 && etyp.kind&kindNoPointers != 0 &&
ktyp.size <= maxKeySize && etyp.size <= maxValSize {
@@ -1907,8 +1907,9 @@ func isValidFieldName(fieldName string) bool {
// The Offset and Index fields are ignored and computed as they would be
// by the compiler.
//
-// StructOf currently does not generate wrapper methods for embedded fields.
-// This limitation may be lifted in a future version.
+// StructOf currently does not generate wrapper methods for embedded
+// fields and panics if passed unexported StructFields.
+// These limitations may be lifted in a future version.
func StructOf(fields []StructField) Type {
var (
hash = uint32(12)
@@ -1949,7 +1950,7 @@ func StructOf(fields []StructField) Type {
// Update string and hash
name := *f.name
hash = (hash << 1) + ft.hash
- if !f.anon() {
+ if !f.embedded() {
repr = append(repr, (" " + name)...)
} else {
// Embedded field
@@ -1958,7 +1959,7 @@ func StructOf(fields []StructField) Type {
// Embedded ** and *interface{} are illegal
elem := ft.Elem()
if k := elem.Kind(); k == Ptr || k == Interface {
- panic("reflect.StructOf: illegal anonymous field type " + ft.String())
+ panic("reflect.StructOf: illegal embedded field type " + ft.String())
}
name = elem.String()
} else {
@@ -2012,7 +2013,7 @@ func StructOf(fields []StructField) Type {
typalign = int8(ft.fieldAlign)
}
size = offset + ft.size
- f.offsetAnon |= offset << 1
+ f.offsetEmbed |= offset << 1
if ft.size == 0 {
lastzero = size
@@ -2197,9 +2198,9 @@ func runtimeStructField(field StructField) structField {
panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but missing PkgPath")
}
- offsetAnon := uintptr(0)
+ offsetEmbed := uintptr(0)
if field.Anonymous {
- offsetAnon |= 1
+ offsetEmbed |= 1
}
s := field.Name
@@ -2212,11 +2213,11 @@ func runtimeStructField(field StructField) structField {
}
return structField{
- name: name,
- pkgPath: nil,
- typ: field.Type.common(),
- tag: tag,
- offsetAnon: offsetAnon,
+ name: name,
+ pkgPath: nil,
+ typ: field.Type.common(),
+ tag: tag,
+ offsetEmbed: offsetEmbed,
}
}
diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go
index 3682b39..5ddd30d 100644
--- a/libgo/go/reflect/value.go
+++ b/libgo/go/reflect/value.go
@@ -632,7 +632,7 @@ func (v Value) Field(i int) Value {
fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
// Using an unexported field forces flagRO.
if field.pkgPath != nil {
- if field.anon() {
+ if field.embedded() {
fl |= flagEmbedRO
} else {
fl |= flagStickyRO
@@ -1078,7 +1078,7 @@ func overflowFloat32(x float64) bool {
}
// OverflowInt reports whether the int64 x cannot be represented by v's type.
-// It panics if v's Kind is not Int, Int8, int16, Int32, or Int64.
+// It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64.
func (v Value) OverflowInt(x int64) bool {
k := v.kind()
switch k {
@@ -1942,7 +1942,7 @@ func MakeSlice(typ Type, len, cap int) Value {
}
s := sliceHeader{unsafe_NewArray(typ.Elem().(*rtype), cap), len, cap}
- return Value{typ.common(), unsafe.Pointer(&s), flagIndir | flag(Slice)}
+ return Value{typ.(*rtype), unsafe.Pointer(&s), flagIndir | flag(Slice)}
}
// MakeChan creates a new channel with the specified type and buffer size.
@@ -1956,8 +1956,9 @@ func MakeChan(typ Type, buffer int) Value {
if typ.ChanDir() != BothDir {
panic("reflect.MakeChan: unidirectional channel type")
}
- ch := makechan(typ.(*rtype), buffer)
- return Value{typ.common(), unsafe.Pointer(&ch), flag(Chan) | flagIndir}
+ t := typ.(*rtype)
+ ch := makechan(t, buffer)
+ return Value{t, unsafe.Pointer(&ch), flag(Chan) | flagIndir}
}
// MakeMap creates a new map with the specified type.
@@ -1971,8 +1972,9 @@ func MakeMapWithSize(typ Type, n int) Value {
if typ.Kind() != Map {
panic("reflect.MakeMapWithSize of non-map type")
}
- m := makemap(typ.(*rtype), n)
- return Value{typ.common(), unsafe.Pointer(&m), flag(Map) | flagIndir}
+ t := typ.(*rtype)
+ m := makemap(t, n)
+ return Value{t, unsafe.Pointer(&m), flag(Map) | flagIndir}
}
// Indirect returns the value that v points to.
@@ -2010,10 +2012,10 @@ func Zero(typ Type) Value {
if typ == nil {
panic("reflect: Zero(nil)")
}
- t := typ.common()
+ t := typ.(*rtype)
fl := flag(t.Kind())
if ifaceIndir(t) {
- return Value{t, unsafe_New(typ.(*rtype)), fl | flagIndir}
+ return Value{t, unsafe_New(t), fl | flagIndir}
}
return Value{t, nil, fl}
}
@@ -2024,16 +2026,18 @@ func New(typ Type) Value {
if typ == nil {
panic("reflect: New(nil)")
}
- ptr := unsafe_New(typ.(*rtype))
+ t := typ.(*rtype)
+ ptr := unsafe_New(t)
fl := flag(Ptr)
- return Value{typ.common().ptrTo(), ptr, fl}
+ return Value{t.ptrTo(), ptr, fl}
}
// NewAt returns a Value representing a pointer to a value of the
// specified type, using p as that pointer.
func NewAt(typ Type, p unsafe.Pointer) Value {
fl := flag(Ptr)
- return Value{typ.common().ptrTo(), p, fl}
+ t := typ.(*rtype)
+ return Value{t.ptrTo(), p, fl}
}
// assignTo returns a value v that can be assigned directly to typ.
@@ -2155,7 +2159,7 @@ func convertOp(dst, src *rtype) func(Value, Type) Value {
return cvtDirect
}
- // dst and src are unnamed pointer types with same underlying base type.
+ // dst and src are non-defined pointer types with same underlying base type.
if dst.Kind() == Ptr && dst.Name() == "" &&
src.Kind() == Ptr && src.Name() == "" &&
haveIdenticalUnderlyingType(dst.Elem().common(), src.Elem().common(), false) {