go type 源码

2022-07-15 浏览 (755)

golang type 代码

文件路径:/src/internal/reflectlite/type.go

/ Copyright 2009 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 reflectlite implements lightweight version of reflect, not using
/ any package except for "runtime" and "unsafe".
package reflectlite

import (
	"internal/unsafeheader"
	"unsafe"
)

/ Type is the representation of a Go type.
/
/ Not all methods apply to all kinds of types. Restrictions,
/ if any, are noted in the documentation for each method.
/ Use the Kind method to find out the kind of type before
/ calling kind-specific methods. Calling a method
/ inappropriate to the kind of type causes a run-time panic.
/
/ Type values are comparable, such as with the == operator,
/ so they can be used as map keys.
/ Two Type values are equal if they represent identical types.
type Type interface {
	/ Methods applicable to all 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 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 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
	/ a value of the given type; it is analogous to unsafe.Sizeof.
	Size() uintptr

	/ Kind returns the specific kind of this type.
	Kind() Kind

	/ Implements reports whether the type implements the interface type u.
	Implements(u Type) bool

	/ AssignableTo reports whether a value of the type is assignable to type u.
	AssignableTo(u Type) bool

	/ Comparable reports whether values of this type are comparable.
	Comparable() bool

	/ String returns a string representation of the type.
	/ The string representation may use shortened package names
	/ (e.g., base64 instead of "encoding/base64") and is not
	/ guaranteed to be unique among types. To test for type identity,
	/ compare the Types directly.
	String() string

	/ Elem returns a type's element type.
	/ It panics if the type's Kind is not Ptr.
	Elem() Type

	common() *rtype
	uncommon() *uncommonType
}

/*
 * 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.
 */

/ A Kind represents the specific kind of type that a Type represents.
/ The zero Kind is not a valid kind.
type Kind uint

const (
	Invalid Kind = iota
	Bool
	Int
	Int8
	Int16
	Int32
	Int64
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Uintptr
	Float32
	Float64
	Complex64
	Complex128
	Array
	Chan
	Func
	Interface
	Map
	Pointer
	Slice
	String
	Struct
	UnsafePointer
)

const Ptr = Pointer

/ tflag is used by an rtype to signal what extra type information is
/ available in the memory directly following the rtype value.
/
/ tflag values must be kept in sync with copies in:
/
/	cmd/compile/internal/reflectdata/reflect.go
/	cmd/link/internal/ld/decodesym.go
/	runtime/type.go
type tflag uint8

const (
	/ tflagUncommon means that there is a pointer, *uncommonType,
	/ just beyond the outer type structure.
	/
	/ For example, if t.Kind() == Struct and t.tflag&tflagUncommon != 0,
	/ then t has uncommonType data and it can be accessed as:
	/
	/	type tUncommon struct {
	/		structType
	/		u uncommonType
	/	}
	/	u := &(*tUncommon)(unsafe.Pointer(t)).u
	tflagUncommon tflag = 1 << 0

	/ tflagExtraStar means the name in the str field has an
	/ extraneous '*' prefix. This is because for most types T in
	/ a program, the type *T also exists and reusing the str data
	/ saves binary size.
	tflagExtraStar tflag = 1 << 1

	/ tflagNamed means the type has a name.
	tflagNamed tflag = 1 << 2

	/ tflagRegularMemory means that equal and hash functions can treat
	/ this type as a single region of t.size bytes.
	tflagRegularMemory tflag = 1 << 3
)

/ rtype is the common implementation of most values.
/ It is embedded in other struct types.
/
/ rtype must be kept in sync with ../runtime/type.go:/^type._type.
type rtype struct {
	size       uintptr
	ptrdata    uintptr / number of bytes in the type that can contain pointers
	hash       uint32  / hash of type; avoids computation in hash tables
	tflag      tflag   / extra type information flags
	align      uint8   / alignment of variable with this type
	fieldAlign uint8   / alignment of struct field with this type
	kind       uint8   / enumeration for C
	/ function for comparing objects of this type
	/ (ptr to object A, ptr to object B) -> ==?
	equal     func(unsafe.Pointer, unsafe.Pointer) bool
	gcdata    *byte   / garbage collection data
	str       nameOff / string form
	ptrToThis typeOff / type for pointer to this type, may be zero
}

/ Method on non-interface type
type method struct {
	name nameOff / name of method
	mtyp typeOff / method type (without receiver)
	ifn  textOff / fn used in interface call (one-word receiver)
	tfn  textOff / fn used for normal method call
}

/ 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 a non-defined type with no methods.
type uncommonType struct {
	pkgPath nameOff / import path; empty for built-in types like int, string
	mcount  uint16  / number of methods
	xcount  uint16  / number of exported methods
	moff    uint32  / offset from this uncommontype to [mcount]method
	_       uint32  / unused
}

/ chanDir represents a channel type's direction.
type chanDir int

const (
	recvDir chanDir             = 1 << iota / <-chan
	sendDir                                 / chan<-
	bothDir = recvDir | sendDir             / chan
)

/ arrayType represents a fixed array type.
type arrayType struct {
	rtype
	elem  *rtype / array element type
	slice *rtype / slice type
	len   uintptr
}

/ chanType represents a channel type.
type chanType struct {
	rtype
	elem *rtype  / channel element type
	dir  uintptr / channel direction (chanDir)
}

/ funcType represents a function type.
/
/ A *rtype for each in and out parameter is stored in an array that
/ directly follows the funcType (and possibly its uncommonType). So
/ a function type with one method, one input, and one output is:
/
/	struct {
/		funcType
/		uncommonType
/		[2]*rtype    / [0] is in, [1] is out
/	}
type funcType struct {
	rtype
	inCount  uint16
	outCount uint16 / top bit is set if last input parameter is ...
}

/ imethod represents a method on an interface type
type imethod struct {
	name nameOff / name of method
	typ  typeOff / .(*FuncType) underneath
}

/ interfaceType represents an interface type.
type interfaceType struct {
	rtype
	pkgPath name      / import path
	methods []imethod / sorted by hash
}

/ mapType represents a map type.
type mapType struct {
	rtype
	key    *rtype / map key type
	elem   *rtype / map element (value) type
	bucket *rtype / internal bucket structure
	/ function for hashing keys (ptr to key, seed) -> hash
	hasher     func(unsafe.Pointer, uintptr) uintptr
	keysize    uint8  / size of key slot
	valuesize  uint8  / size of value slot
	bucketsize uint16 / size of bucket
	flags      uint32
}

/ ptrType represents a pointer type.
type ptrType struct {
	rtype
	elem *rtype / pointer element (pointed at) type
}

/ sliceType represents a slice type.
type sliceType struct {
	rtype
	elem *rtype / slice element type
}

/ Struct field
type structField struct {
	name   name    / name is always non-empty
	typ    *rtype  / type of field
	offset uintptr / byte offset of field
}

func (f *structField) embedded() bool {
	return f.name.embedded()
}

/ structType represents a struct type.
type structType struct {
	rtype
	pkgPath name
	fields  []structField / sorted by offset
}

/ name is an encoded type name with optional extra data.
/
/ The first byte is a bit field containing:
/
/	1<<0 the name is exported
/	1<<1 tag data follows the name
/	1<<2 pkgPath nameOff follows the name and tag
/
/ The next two bytes are the data length:
/
/	l := uint16(data[1])<<8 | uint16(data[2])
/
/ Bytes [3:3+l] are the string data.
/
/ If tag data follows then bytes 3+l and 3+l+1 are the tag length,
/ with the data following.
/
/ If the import path follows, then 4 bytes at the end of
/ the data form a nameOff. The import path is only set for concrete
/ methods that are defined in a different package than their type.
/
/ If a name starts with "*", then the exported bit represents
/ whether the pointed to type is exported.
type name struct {
	bytes *byte
}

func (n name) data(off int, whySafe string) *byte {
	return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off), whySafe))
}

func (n name) isExported() bool {
	return (*n.bytes)&(1<<0) != 0
}

func (n name) hasTag() bool {
	return (*n.bytes)&(1<<1) != 0
}

func (n name) embedded() bool {
	return (*n.bytes)&(1<<3) != 0
}

/ readVarint parses a varint as encoded by encoding/binary.
/ It returns the number of encoded bytes and the encoded value.
func (n name) readVarint(off int) (int, int) {
	v := 0
	for i := 0; ; i++ {
		x := *n.data(off+i, "read varint")
		v += int(x&0x7f) << (7 * i)
		if x&0x80 == 0 {
			return i + 1, v
		}
	}
}

func (n name) name() (s string) {
	if n.bytes == nil {
		return
	}
	i, l := n.readVarint(1)
	hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
	hdr.Data = unsafe.Pointer(n.data(1+i, "non-empty string"))
	hdr.Len = l
	return
}

func (n name) tag() (s string) {
	if !n.hasTag() {
		return ""
	}
	i, l := n.readVarint(1)
	i2, l2 := n.readVarint(1 + i + l)
	hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
	hdr.Data = unsafe.Pointer(n.data(1+i+l+i2, "non-empty string"))
	hdr.Len = l2
	return
}

func (n name) pkgPath() string {
	if n.bytes == nil || *n.data(0, "name flag field")&(1<<2) == 0 {
		return ""
	}
	i, l := n.readVarint(1)
	off := 1 + i + l
	if n.hasTag() {
		i2, l2 := n.readVarint(off)
		off += i2 + l2
	}
	var nameOff int32
	/ Note that this field may not be aligned in memory,
	/ so we cannot use a direct int32 assignment here.
	copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off, "name offset field")))[:])
	pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n.bytes), nameOff))}
	return pkgPathName.name()
}

/*
 * The compiler knows the exact layout of all the data structures above.
 * The compiler does not know about the data structures and methods below.
 */

const (
	kindDirectIface = 1 << 5
	kindGCProg      = 1 << 6 / Type.gc points to GC program
	kindMask        = (1 << 5) - 1
)

/ String returns the name of k.
func (k Kind) String() string {
	if int(k) < len(kindNames) {
		return kindNames[k]
	}
	return kindNames[0]
}

var kindNames = []string{
	Invalid:       "invalid",
	Bool:          "bool",
	Int:           "int",
	Int8:          "int8",
	Int16:         "int16",
	Int32:         "int32",
	Int64:         "int64",
	Uint:          "uint",
	Uint8:         "uint8",
	Uint16:        "uint16",
	Uint32:        "uint32",
	Uint64:        "uint64",
	Uintptr:       "uintptr",
	Float32:       "float32",
	Float64:       "float64",
	Complex64:     "complex64",
	Complex128:    "complex128",
	Array:         "array",
	Chan:          "chan",
	Func:          "func",
	Interface:     "interface",
	Map:           "map",
	Ptr:           "ptr",
	Slice:         "slice",
	String:        "string",
	Struct:        "struct",
	UnsafePointer: "unsafe.Pointer",
}

func (t *uncommonType) methods() []method {
	if t.mcount == 0 {
		return nil
	}
	return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), "t.mcount > 0"))[:t.mcount:t.mcount]
}

func (t *uncommonType) exportedMethods() []method {
	if t.xcount == 0 {
		return nil
	}
	return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), "t.xcount > 0"))[:t.xcount:t.xcount]
}

/ resolveNameOff resolves a name offset from a base pointer.
/ The (*rtype).nameOff method is a convenience wrapper for this function.
/ Implemented in the runtime package.
func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer

/ resolveTypeOff resolves an *rtype offset from a base type.
/ The (*rtype).typeOff method is a convenience wrapper for this function.
/ Implemented in the runtime package.
func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer

type nameOff int32 / offset to a name
type typeOff int32 / offset to an *rtype
type textOff int32 / offset from top of text section

func (t *rtype) nameOff(off nameOff) name {
	return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))}
}

func (t *rtype) typeOff(off typeOff) *rtype {
	return (*rtype)(resolveTypeOff(unsafe.Pointer(t), int32(off)))
}

func (t *rtype) uncommon() *uncommonType {
	if t.tflag&tflagUncommon == 0 {
		return nil
	}
	switch t.Kind() {
	case Struct:
		return &(*structTypeUncommon)(unsafe.Pointer(t)).u
	case Ptr:
		type u struct {
			ptrType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Func:
		type u struct {
			funcType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Slice:
		type u struct {
			sliceType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Array:
		type u struct {
			arrayType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Chan:
		type u struct {
			chanType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Map:
		type u struct {
			mapType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	case Interface:
		type u struct {
			interfaceType
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	default:
		type u struct {
			rtype
			u uncommonType
		}
		return &(*u)(unsafe.Pointer(t)).u
	}
}

func (t *rtype) String() string {
	s := t.nameOff(t.str).name()
	if t.tflag&tflagExtraStar != 0 {
		return s[1:]
	}
	return s
}

func (t *rtype) Size() uintptr { return t.size }

func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) }

func (t *rtype) pointers() bool { return t.ptrdata != 0 }

func (t *rtype) common() *rtype { return t }

func (t *rtype) exportedMethods() []method {
	ut := t.uncommon()
	if ut == nil {
		return nil
	}
	return ut.exportedMethods()
}

func (t *rtype) NumMethod() int {
	if t.Kind() == Interface {
		tt := (*interfaceType)(unsafe.Pointer(t))
		return tt.NumMethod()
	}
	return len(t.exportedMethods())
}

func (t *rtype) PkgPath() string {
	if t.tflag&tflagNamed == 0 {
		return ""
	}
	ut := t.uncommon()
	if ut == nil {
		return ""
	}
	return t.nameOff(ut.pkgPath).name()
}

func (t *rtype) hasName() bool {
	return t.tflag&tflagNamed != 0
}

func (t *rtype) Name() string {
	if !t.hasName() {
		return ""
	}
	s := t.String()
	i := len(s) - 1
	sqBrackets := 0
	for i >= 0 && (s[i] != '.' || sqBrackets != 0) {
		switch s[i] {
		case ']':
			sqBrackets++
		case '[':
			sqBrackets--
		}
		i--
	}
	return s[i+1:]
}

func (t *rtype) chanDir() chanDir {
	if t.Kind() != Chan {
		panic("reflect: chanDir of non-chan type")
	}
	tt := (*chanType)(unsafe.Pointer(t))
	return chanDir(tt.dir)
}

func (t *rtype) Elem() Type {
	switch t.Kind() {
	case Array:
		tt := (*arrayType)(unsafe.Pointer(t))
		return toType(tt.elem)
	case Chan:
		tt := (*chanType)(unsafe.Pointer(t))
		return toType(tt.elem)
	case Map:
		tt := (*mapType)(unsafe.Pointer(t))
		return toType(tt.elem)
	case Ptr:
		tt := (*ptrType)(unsafe.Pointer(t))
		return toType(tt.elem)
	case Slice:
		tt := (*sliceType)(unsafe.Pointer(t))
		return toType(tt.elem)
	}
	panic("reflect: Elem of invalid type")
}

func (t *rtype) In(i int) Type {
	if t.Kind() != Func {
		panic("reflect: In of non-func type")
	}
	tt := (*funcType)(unsafe.Pointer(t))
	return toType(tt.in()[i])
}

func (t *rtype) Key() Type {
	if t.Kind() != Map {
		panic("reflect: Key of non-map type")
	}
	tt := (*mapType)(unsafe.Pointer(t))
	return toType(tt.key)
}

func (t *rtype) Len() int {
	if t.Kind() != Array {
		panic("reflect: Len of non-array type")
	}
	tt := (*arrayType)(unsafe.Pointer(t))
	return int(tt.len)
}

func (t *rtype) NumField() int {
	if t.Kind() != Struct {
		panic("reflect: NumField of non-struct type")
	}
	tt := (*structType)(unsafe.Pointer(t))
	return len(tt.fields)
}

func (t *rtype) NumIn() int {
	if t.Kind() != Func {
		panic("reflect: NumIn of non-func type")
	}
	tt := (*funcType)(unsafe.Pointer(t))
	return int(tt.inCount)
}

func (t *rtype) NumOut() int {
	if t.Kind() != Func {
		panic("reflect: NumOut of non-func type")
	}
	tt := (*funcType)(unsafe.Pointer(t))
	return len(tt.out())
}

func (t *rtype) Out(i int) Type {
	if t.Kind() != Func {
		panic("reflect: Out of non-func type")
	}
	tt := (*funcType)(unsafe.Pointer(t))
	return toType(tt.out()[i])
}

func (t *funcType) in() []*rtype {
	uadd := unsafe.Sizeof(*t)
	if t.tflag&tflagUncommon != 0 {
		uadd += unsafe.Sizeof(uncommonType{})
	}
	if t.inCount == 0 {
		return nil
	}
	return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.inCount:t.inCount]
}

func (t *funcType) out() []*rtype {
	uadd := unsafe.Sizeof(*t)
	if t.tflag&tflagUncommon != 0 {
		uadd += unsafe.Sizeof(uncommonType{})
	}
	outCount := t.outCount & (1<<15 - 1)
	if outCount == 0 {
		return nil
	}
	return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd, "outCount > 0"))[t.inCount : t.inCount+outCount : t.inCount+outCount]
}

/ add returns p+x.
/
/ The whySafe string is ignored, so that the function still inlines
/ as efficiently as p+x, but all call sites should use the string to
/ record why the addition is safe, which is to say why the addition
/ does not cause x to advance to the very end of p's allocation
/ and therefore point incorrectly at the next block in memory.
func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
	return unsafe.Pointer(uintptr(p) + x)
}

/ NumMethod returns the number of interface methods in the type's method set.
func (t *interfaceType) NumMethod() int { return len(t.methods) }

/ TypeOf returns the reflection Type that represents the dynamic type of i.
/ If i is a nil interface value, TypeOf returns nil.
func TypeOf(i any) Type {
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	return toType(eface.typ)
}

func (t *rtype) 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.(*rtype), t)
}

func (t *rtype) AssignableTo(u Type) bool {
	if u == nil {
		panic("reflect: nil type passed to Type.AssignableTo")
	}
	uu := u.(*rtype)
	return directlyAssignable(uu, t) || implements(uu, t)
}

func (t *rtype) Comparable() bool {
	return t.equal != nil
}

/ implements reports whether the type V implements the interface type T.
func implements(T, V *rtype) 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.go.
	if V.Kind() == Interface {
		v := (*interfaceType)(unsafe.Pointer(V))
		i := 0
		for j := 0; j < len(v.methods); j++ {
			tm := &t.methods[i]
			tmName := t.nameOff(tm.name)
			vm := &v.methods[j]
			vmName := V.nameOff(vm.name)
			if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) {
				if !tmName.isExported() {
					tmPkgPath := tmName.pkgPath()
					if tmPkgPath == "" {
						tmPkgPath = t.pkgPath.name()
					}
					vmPkgPath := vmName.pkgPath()
					if vmPkgPath == "" {
						vmPkgPath = v.pkgPath.name()
					}
					if tmPkgPath != vmPkgPath {
						continue
					}
				}
				if i++; i >= len(t.methods) {
					return true
				}
			}
		}
		return false
	}

	v := V.uncommon()
	if v == nil {
		return false
	}
	i := 0
	vmethods := v.methods()
	for j := 0; j < int(v.mcount); j++ {
		tm := &t.methods[i]
		tmName := t.nameOff(tm.name)
		vm := vmethods[j]
		vmName := V.nameOff(vm.name)
		if vmName.name() == tmName.name() && V.typeOff(vm.mtyp) == t.typeOff(tm.typ) {
			if !tmName.isExported() {
				tmPkgPath := tmName.pkgPath()
				if tmPkgPath == "" {
					tmPkgPath = t.pkgPath.name()
				}
				vmPkgPath := vmName.pkgPath()
				if vmPkgPath == "" {
					vmPkgPath = V.nameOff(v.pkgPath).name()
				}
				if tmPkgPath != vmPkgPath {
					continue
				}
			}
			if i++; i >= len(t.methods) {
				return true
			}
		}
	}
	return false
}

/ directlyAssignable reports whether a value x of type V can be directly
/ assigned (using memmove) to a value of type T.
/ https://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 *rtype) bool {
	/ x's type V is identical to T?
	if T == V {
		return true
	}

	/ Otherwise at least one of T and V must not be defined
	/ and they must have the same kind.
	if T.hasName() && V.hasName() || T.Kind() != V.Kind() {
		return false
	}

	/ x's type T and V must  have identical underlying types.
	return haveIdenticalUnderlyingType(T, V, true)
}

func haveIdenticalType(T, V Type, cmpTags bool) bool {
	if cmpTags {
		return T == V
	}

	if T.Name() != V.Name() || T.Kind() != V.Kind() {
		return false
	}

	return haveIdenticalUnderlyingType(T.common(), V.common(), false)
}

func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
	if T == V {
		return true
	}

	kind := T.Kind()
	if kind != V.Kind() {
		return false
	}

	/ Non-composite types of equal kind have same underlying type
	/ (the predefined instance of the type).
	if Bool <= kind && kind <= Complex128 || kind == String || kind == UnsafePointer {
		return true
	}

	/ Composite types.
	switch kind {
	case Array:
		return T.Len() == V.Len() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

	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 && haveIdenticalType(T.Elem(), V.Elem(), cmpTags) {
			return true
		}

		/ Otherwise continue test for identical underlying type.
		return V.chanDir() == T.chanDir() && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

	case Func:
		t := (*funcType)(unsafe.Pointer(T))
		v := (*funcType)(unsafe.Pointer(V))
		if t.outCount != v.outCount || t.inCount != v.inCount {
			return false
		}
		for i := 0; i < t.NumIn(); i++ {
			if !haveIdenticalType(t.In(i), v.In(i), cmpTags) {
				return false
			}
		}
		for i := 0; i < t.NumOut(); i++ {
			if !haveIdenticalType(t.Out(i), v.Out(i), cmpTags) {
				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 haveIdenticalType(T.Key(), V.Key(), cmpTags) && haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

	case Ptr, Slice:
		return haveIdenticalType(T.Elem(), V.Elem(), cmpTags)

	case Struct:
		t := (*structType)(unsafe.Pointer(T))
		v := (*structType)(unsafe.Pointer(V))
		if len(t.fields) != len(v.fields) {
			return false
		}
		if t.pkgPath.name() != v.pkgPath.name() {
			return false
		}
		for i := range t.fields {
			tf := &t.fields[i]
			vf := &v.fields[i]
			if tf.name.name() != vf.name.name() {
				return false
			}
			if !haveIdenticalType(tf.typ, vf.typ, cmpTags) {
				return false
			}
			if cmpTags && tf.name.tag() != vf.name.tag() {
				return false
			}
			if tf.offset != vf.offset {
				return false
			}
			if tf.embedded() != vf.embedded() {
				return false
			}
		}
		return true
	}

	return false
}

type structTypeUncommon struct {
	structType
	u uncommonType
}

/ toType converts from a *rtype to a Type that can be returned
/ to the client of package reflect. In gc, the only concern is that
/ a nil *rtype must be replaced by a nil Type, but in gccgo this
/ function takes care of ensuring that multiple *rtype for the same
/ type are coalesced into a single Type.
func toType(t *rtype) Type {
	if t == nil {
		return nil
	}
	return t
}

/ ifaceIndir reports whether t is stored indirectly in an interface value.
func ifaceIndir(t *rtype) bool {
	return t.kind&kindDirectIface == 0
}

相关信息

go 源码目录

相关文章

go all_test 源码

go export_test 源码

go reflect_mirror_test 源码

go set_test 源码

go swapper 源码

go tostring_test 源码

go value 源码

^