github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/runtime/type.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Runtime type representation.
     6  
     7  package runtime
     8  
     9  import (
    10  	"internal/abi"
    11  	"unsafe"
    12  )
    13  
    14  // tflag is documented in reflect/type.go.
    15  //
    16  // tflag values must be kept in sync with copies in:
    17  //	cmd/compile/internal/reflectdata/reflect.go
    18  //	cmd/link/internal/ld/decodesym.go
    19  //	reflect/type.go
    20  //      internal/reflectlite/type.go
    21  type tflag uint8
    22  
    23  const (
    24  	tflagUncommon      tflag = 1 << 0
    25  	tflagExtraStar     tflag = 1 << 1
    26  	tflagNamed         tflag = 1 << 2
    27  	tflagRegularMemory tflag = 1 << 3 // equal and hash can treat values of this type as a single region of t.size bytes
    28  )
    29  
    30  // Needs to be in sync with ../cmd/link/internal/ld/decodesym.go:/^func.commonsize,
    31  // ../cmd/compile/internal/reflectdata/reflect.go:/^func.dcommontype and
    32  // ../reflect/type.go:/^type.rtype.
    33  // ../internal/reflectlite/type.go:/^type.rtype.
    34  type _type struct {
    35  	size       uintptr
    36  	ptrdata    uintptr // size of memory prefix holding all pointers
    37  	hash       uint32
    38  	tflag      tflag
    39  	align      uint8
    40  	fieldAlign uint8
    41  	kind       uint8
    42  	// function for comparing objects of this type
    43  	// (ptr to object A, ptr to object B) -> ==?
    44  	equal func(unsafe.Pointer, unsafe.Pointer) bool
    45  	// gcdata stores the GC type data for the garbage collector.
    46  	// If the KindGCProg bit is set in kind, gcdata is a GC program.
    47  	// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
    48  	gcdata    *byte
    49  	str       nameOff
    50  	ptrToThis typeOff
    51  }
    52  
    53  func (t *_type) string() string {
    54  	s := t.nameOff(t.str).name()
    55  	if t.tflag&tflagExtraStar != 0 {
    56  		return s[1:]
    57  	}
    58  	return s
    59  }
    60  
    61  func (t *_type) uncommon() *uncommontype {
    62  	if t.tflag&tflagUncommon == 0 {
    63  		return nil
    64  	}
    65  	switch t.kind & kindMask {
    66  	case kindStruct:
    67  		type u struct {
    68  			structtype
    69  			u uncommontype
    70  		}
    71  		return &(*u)(unsafe.Pointer(t)).u
    72  	case kindPtr:
    73  		type u struct {
    74  			ptrtype
    75  			u uncommontype
    76  		}
    77  		return &(*u)(unsafe.Pointer(t)).u
    78  	case kindFunc:
    79  		type u struct {
    80  			functype
    81  			u uncommontype
    82  		}
    83  		return &(*u)(unsafe.Pointer(t)).u
    84  	case kindSlice:
    85  		type u struct {
    86  			slicetype
    87  			u uncommontype
    88  		}
    89  		return &(*u)(unsafe.Pointer(t)).u
    90  	case kindArray:
    91  		type u struct {
    92  			arraytype
    93  			u uncommontype
    94  		}
    95  		return &(*u)(unsafe.Pointer(t)).u
    96  	case kindChan:
    97  		type u struct {
    98  			chantype
    99  			u uncommontype
   100  		}
   101  		return &(*u)(unsafe.Pointer(t)).u
   102  	case kindMap:
   103  		type u struct {
   104  			maptype
   105  			u uncommontype
   106  		}
   107  		return &(*u)(unsafe.Pointer(t)).u
   108  	case kindInterface:
   109  		type u struct {
   110  			interfacetype
   111  			u uncommontype
   112  		}
   113  		return &(*u)(unsafe.Pointer(t)).u
   114  	default:
   115  		type u struct {
   116  			_type
   117  			u uncommontype
   118  		}
   119  		return &(*u)(unsafe.Pointer(t)).u
   120  	}
   121  }
   122  
   123  func (t *_type) name() string {
   124  	if t.tflag&tflagNamed == 0 {
   125  		return ""
   126  	}
   127  	s := t.string()
   128  	i := len(s) - 1
   129  	for i >= 0 && s[i] != '.' {
   130  		i--
   131  	}
   132  	return s[i+1:]
   133  }
   134  
   135  // pkgpath returns the path of the package where t was defined, if
   136  // available. This is not the same as the reflect package's PkgPath
   137  // method, in that it returns the package path for struct and interface
   138  // types, not just named types.
   139  func (t *_type) pkgpath() string {
   140  	if u := t.uncommon(); u != nil {
   141  		return t.nameOff(u.pkgpath).name()
   142  	}
   143  	switch t.kind & kindMask {
   144  	case kindStruct:
   145  		st := (*structtype)(unsafe.Pointer(t))
   146  		return st.pkgPath.name()
   147  	case kindInterface:
   148  		it := (*interfacetype)(unsafe.Pointer(t))
   149  		return it.pkgpath.name()
   150  	}
   151  	return ""
   152  }
   153  
   154  // reflectOffs holds type offsets defined at run time by the reflect package.
   155  //
   156  // When a type is defined at run time, its *rtype data lives on the heap.
   157  // There are a wide range of possible addresses the heap may use, that
   158  // may not be representable as a 32-bit offset. Moreover the GC may
   159  // one day start moving heap memory, in which case there is no stable
   160  // offset that can be defined.
   161  //
   162  // To provide stable offsets, we add pin *rtype objects in a global map
   163  // and treat the offset as an identifier. We use negative offsets that
   164  // do not overlap with any compile-time module offsets.
   165  //
   166  // Entries are created by reflect.addReflectOff.
   167  var reflectOffs struct {
   168  	lock mutex
   169  	next int32
   170  	m    map[int32]unsafe.Pointer
   171  	minv map[unsafe.Pointer]int32
   172  }
   173  
   174  func reflectOffsLock() {
   175  	lock(&reflectOffs.lock)
   176  	if raceenabled {
   177  		raceacquire(unsafe.Pointer(&reflectOffs.lock))
   178  	}
   179  }
   180  
   181  func reflectOffsUnlock() {
   182  	if raceenabled {
   183  		racerelease(unsafe.Pointer(&reflectOffs.lock))
   184  	}
   185  	unlock(&reflectOffs.lock)
   186  }
   187  
   188  func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {
   189  	if off == 0 {
   190  		return name{}
   191  	}
   192  	base := uintptr(ptrInModule)
   193  	for md := &firstmoduledata; md != nil; md = md.next {
   194  		if base >= md.types && base < md.etypes {
   195  			res := md.types + uintptr(off)
   196  			if res > md.etypes {
   197  				println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
   198  				throw("runtime: name offset out of range")
   199  			}
   200  			return name{(*byte)(unsafe.Pointer(res))}
   201  		}
   202  	}
   203  
   204  	// No module found. see if it is a run time name.
   205  	reflectOffsLock()
   206  	res, found := reflectOffs.m[int32(off)]
   207  	reflectOffsUnlock()
   208  	if !found {
   209  		println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
   210  		for next := &firstmoduledata; next != nil; next = next.next {
   211  			println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
   212  		}
   213  		throw("runtime: name offset base pointer out of range")
   214  	}
   215  	return name{(*byte)(res)}
   216  }
   217  
   218  func (t *_type) nameOff(off nameOff) name {
   219  	return resolveNameOff(unsafe.Pointer(t), off)
   220  }
   221  
   222  func resolveTypeOff(ptrInModule unsafe.Pointer, off typeOff) *_type {
   223  	if off == 0 || off == -1 {
   224  		// -1 is the sentinel value for unreachable code.
   225  		// See cmd/link/internal/ld/data.go:relocsym.
   226  		return nil
   227  	}
   228  	base := uintptr(ptrInModule)
   229  	var md *moduledata
   230  	for next := &firstmoduledata; next != nil; next = next.next {
   231  		if base >= next.types && base < next.etypes {
   232  			md = next
   233  			break
   234  		}
   235  	}
   236  	if md == nil {
   237  		reflectOffsLock()
   238  		res := reflectOffs.m[int32(off)]
   239  		reflectOffsUnlock()
   240  		if res == nil {
   241  			println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:")
   242  			for next := &firstmoduledata; next != nil; next = next.next {
   243  				println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
   244  			}
   245  			throw("runtime: type offset base pointer out of range")
   246  		}
   247  		return (*_type)(res)
   248  	}
   249  	if t := md.typemap[off]; t != nil {
   250  		return t
   251  	}
   252  	res := md.types + uintptr(off)
   253  	if res > md.etypes {
   254  		println("runtime: typeOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
   255  		throw("runtime: type offset out of range")
   256  	}
   257  	return (*_type)(unsafe.Pointer(res))
   258  }
   259  
   260  func (t *_type) typeOff(off typeOff) *_type {
   261  	return resolveTypeOff(unsafe.Pointer(t), off)
   262  }
   263  
   264  func (t *_type) textOff(off textOff) unsafe.Pointer {
   265  	if off == -1 {
   266  		// -1 is the sentinel value for unreachable code.
   267  		// See cmd/link/internal/ld/data.go:relocsym.
   268  		return unsafe.Pointer(abi.FuncPCABIInternal(unreachableMethod))
   269  	}
   270  	base := uintptr(unsafe.Pointer(t))
   271  	var md *moduledata
   272  	for next := &firstmoduledata; next != nil; next = next.next {
   273  		if base >= next.types && base < next.etypes {
   274  			md = next
   275  			break
   276  		}
   277  	}
   278  	if md == nil {
   279  		reflectOffsLock()
   280  		res := reflectOffs.m[int32(off)]
   281  		reflectOffsUnlock()
   282  		if res == nil {
   283  			println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:")
   284  			for next := &firstmoduledata; next != nil; next = next.next {
   285  				println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
   286  			}
   287  			throw("runtime: text offset base pointer out of range")
   288  		}
   289  		return res
   290  	}
   291  	res := md.textAddr(uint32(off))
   292  	return unsafe.Pointer(res)
   293  }
   294  
   295  func (t *functype) in() []*_type {
   296  	// See funcType in reflect/type.go for details on data layout.
   297  	uadd := uintptr(unsafe.Sizeof(functype{}))
   298  	if t.typ.tflag&tflagUncommon != 0 {
   299  		uadd += unsafe.Sizeof(uncommontype{})
   300  	}
   301  	return (*[1 << 20]*_type)(add(unsafe.Pointer(t), uadd))[:t.inCount]
   302  }
   303  
   304  func (t *functype) out() []*_type {
   305  	// See funcType in reflect/type.go for details on data layout.
   306  	uadd := uintptr(unsafe.Sizeof(functype{}))
   307  	if t.typ.tflag&tflagUncommon != 0 {
   308  		uadd += unsafe.Sizeof(uncommontype{})
   309  	}
   310  	outCount := t.outCount & (1<<15 - 1)
   311  	return (*[1 << 20]*_type)(add(unsafe.Pointer(t), uadd))[t.inCount : t.inCount+outCount]
   312  }
   313  
   314  func (t *functype) dotdotdot() bool {
   315  	return t.outCount&(1<<15) != 0
   316  }
   317  
   318  type nameOff int32
   319  type typeOff int32
   320  type textOff int32
   321  
   322  type method struct {
   323  	name nameOff
   324  	mtyp typeOff
   325  	ifn  textOff
   326  	tfn  textOff
   327  }
   328  
   329  type uncommontype struct {
   330  	pkgpath nameOff
   331  	mcount  uint16 // number of methods
   332  	xcount  uint16 // number of exported methods
   333  	moff    uint32 // offset from this uncommontype to [mcount]method
   334  	_       uint32 // unused
   335  }
   336  
   337  type imethod struct {
   338  	name nameOff
   339  	ityp typeOff
   340  }
   341  
   342  type interfacetype struct {
   343  	typ     _type
   344  	pkgpath name
   345  	mhdr    []imethod
   346  }
   347  
   348  type maptype struct {
   349  	typ    _type
   350  	key    *_type
   351  	elem   *_type
   352  	bucket *_type // internal type representing a hash bucket
   353  	// function for hashing keys (ptr to key, seed) -> hash
   354  	hasher     func(unsafe.Pointer, uintptr) uintptr
   355  	keysize    uint8  // size of key slot
   356  	elemsize   uint8  // size of elem slot
   357  	bucketsize uint16 // size of bucket
   358  	flags      uint32
   359  }
   360  
   361  // Note: flag values must match those used in the TMAP case
   362  // in ../cmd/compile/internal/reflectdata/reflect.go:writeType.
   363  func (mt *maptype) indirectkey() bool { // store ptr to key instead of key itself
   364  	return mt.flags&1 != 0
   365  }
   366  func (mt *maptype) indirectelem() bool { // store ptr to elem instead of elem itself
   367  	return mt.flags&2 != 0
   368  }
   369  func (mt *maptype) reflexivekey() bool { // true if k==k for all keys
   370  	return mt.flags&4 != 0
   371  }
   372  func (mt *maptype) needkeyupdate() bool { // true if we need to update key on an overwrite
   373  	return mt.flags&8 != 0
   374  }
   375  func (mt *maptype) hashMightPanic() bool { // true if hash function might panic
   376  	return mt.flags&16 != 0
   377  }
   378  
   379  type arraytype struct {
   380  	typ   _type
   381  	elem  *_type
   382  	slice *_type
   383  	len   uintptr
   384  }
   385  
   386  type chantype struct {
   387  	typ  _type
   388  	elem *_type
   389  	dir  uintptr
   390  }
   391  
   392  type slicetype struct {
   393  	typ  _type
   394  	elem *_type
   395  }
   396  
   397  type functype struct {
   398  	typ      _type
   399  	inCount  uint16
   400  	outCount uint16
   401  }
   402  
   403  type ptrtype struct {
   404  	typ  _type
   405  	elem *_type
   406  }
   407  
   408  type structfield struct {
   409  	name       name
   410  	typ        *_type
   411  	offsetAnon uintptr
   412  }
   413  
   414  func (f *structfield) offset() uintptr {
   415  	return f.offsetAnon >> 1
   416  }
   417  
   418  type structtype struct {
   419  	typ     _type
   420  	pkgPath name
   421  	fields  []structfield
   422  }
   423  
   424  // name is an encoded type name with optional extra data.
   425  // See reflect/type.go for details.
   426  type name struct {
   427  	bytes *byte
   428  }
   429  
   430  func (n name) data(off int) *byte {
   431  	return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off)))
   432  }
   433  
   434  func (n name) isExported() bool {
   435  	return (*n.bytes)&(1<<0) != 0
   436  }
   437  
   438  func (n name) readvarint(off int) (int, int) {
   439  	v := 0
   440  	for i := 0; ; i++ {
   441  		x := *n.data(off + i)
   442  		v += int(x&0x7f) << (7 * i)
   443  		if x&0x80 == 0 {
   444  			return i + 1, v
   445  		}
   446  	}
   447  }
   448  
   449  func (n name) name() (s string) {
   450  	if n.bytes == nil {
   451  		return ""
   452  	}
   453  	i, l := n.readvarint(1)
   454  	if l == 0 {
   455  		return ""
   456  	}
   457  	hdr := (*stringStruct)(unsafe.Pointer(&s))
   458  	hdr.str = unsafe.Pointer(n.data(1 + i))
   459  	hdr.len = l
   460  	return
   461  }
   462  
   463  func (n name) tag() (s string) {
   464  	if *n.data(0)&(1<<1) == 0 {
   465  		return ""
   466  	}
   467  	i, l := n.readvarint(1)
   468  	i2, l2 := n.readvarint(1 + i + l)
   469  	hdr := (*stringStruct)(unsafe.Pointer(&s))
   470  	hdr.str = unsafe.Pointer(n.data(1 + i + l + i2))
   471  	hdr.len = l2
   472  	return
   473  }
   474  
   475  func (n name) pkgPath() string {
   476  	if n.bytes == nil || *n.data(0)&(1<<2) == 0 {
   477  		return ""
   478  	}
   479  	i, l := n.readvarint(1)
   480  	off := 1 + i + l
   481  	if *n.data(0)&(1<<1) != 0 {
   482  		i2, l2 := n.readvarint(off)
   483  		off += i2 + l2
   484  	}
   485  	var nameOff nameOff
   486  	copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:])
   487  	pkgPathName := resolveNameOff(unsafe.Pointer(n.bytes), nameOff)
   488  	return pkgPathName.name()
   489  }
   490  
   491  func (n name) isBlank() bool {
   492  	if n.bytes == nil {
   493  		return false
   494  	}
   495  	_, l := n.readvarint(1)
   496  	return l == 1 && *n.data(2) == '_'
   497  }
   498  
   499  // typelinksinit scans the types from extra modules and builds the
   500  // moduledata typemap used to de-duplicate type pointers.
   501  func typelinksinit() {
   502  	if firstmoduledata.next == nil {
   503  		return
   504  	}
   505  	typehash := make(map[uint32][]*_type, len(firstmoduledata.typelinks))
   506  
   507  	modules := activeModules()
   508  	prev := modules[0]
   509  	for _, md := range modules[1:] {
   510  		// Collect types from the previous module into typehash.
   511  	collect:
   512  		for _, tl := range prev.typelinks {
   513  			var t *_type
   514  			if prev.typemap == nil {
   515  				t = (*_type)(unsafe.Pointer(prev.types + uintptr(tl)))
   516  			} else {
   517  				t = prev.typemap[typeOff(tl)]
   518  			}
   519  			// Add to typehash if not seen before.
   520  			tlist := typehash[t.hash]
   521  			for _, tcur := range tlist {
   522  				if tcur == t {
   523  					continue collect
   524  				}
   525  			}
   526  			typehash[t.hash] = append(tlist, t)
   527  		}
   528  
   529  		if md.typemap == nil {
   530  			// If any of this module's typelinks match a type from a
   531  			// prior module, prefer that prior type by adding the offset
   532  			// to this module's typemap.
   533  			tm := make(map[typeOff]*_type, len(md.typelinks))
   534  			pinnedTypemaps = append(pinnedTypemaps, tm)
   535  			md.typemap = tm
   536  			for _, tl := range md.typelinks {
   537  				t := (*_type)(unsafe.Pointer(md.types + uintptr(tl)))
   538  				for _, candidate := range typehash[t.hash] {
   539  					seen := map[_typePair]struct{}{}
   540  					if typesEqual(t, candidate, seen) {
   541  						t = candidate
   542  						break
   543  					}
   544  				}
   545  				md.typemap[typeOff(tl)] = t
   546  			}
   547  		}
   548  
   549  		prev = md
   550  	}
   551  }
   552  
   553  type _typePair struct {
   554  	t1 *_type
   555  	t2 *_type
   556  }
   557  
   558  // typesEqual reports whether two types are equal.
   559  //
   560  // Everywhere in the runtime and reflect packages, it is assumed that
   561  // there is exactly one *_type per Go type, so that pointer equality
   562  // can be used to test if types are equal. There is one place that
   563  // breaks this assumption: buildmode=shared. In this case a type can
   564  // appear as two different pieces of memory. This is hidden from the
   565  // runtime and reflect package by the per-module typemap built in
   566  // typelinksinit. It uses typesEqual to map types from later modules
   567  // back into earlier ones.
   568  //
   569  // Only typelinksinit needs this function.
   570  func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool {
   571  	tp := _typePair{t, v}
   572  	if _, ok := seen[tp]; ok {
   573  		return true
   574  	}
   575  
   576  	// mark these types as seen, and thus equivalent which prevents an infinite loop if
   577  	// the two types are identical, but recursively defined and loaded from
   578  	// different modules
   579  	seen[tp] = struct{}{}
   580  
   581  	if t == v {
   582  		return true
   583  	}
   584  	kind := t.kind & kindMask
   585  	if kind != v.kind&kindMask {
   586  		return false
   587  	}
   588  	if t.string() != v.string() {
   589  		return false
   590  	}
   591  	ut := t.uncommon()
   592  	uv := v.uncommon()
   593  	if ut != nil || uv != nil {
   594  		if ut == nil || uv == nil {
   595  			return false
   596  		}
   597  		pkgpatht := t.nameOff(ut.pkgpath).name()
   598  		pkgpathv := v.nameOff(uv.pkgpath).name()
   599  		if pkgpatht != pkgpathv {
   600  			return false
   601  		}
   602  	}
   603  	if kindBool <= kind && kind <= kindComplex128 {
   604  		return true
   605  	}
   606  	switch kind {
   607  	case kindString, kindUnsafePointer:
   608  		return true
   609  	case kindArray:
   610  		at := (*arraytype)(unsafe.Pointer(t))
   611  		av := (*arraytype)(unsafe.Pointer(v))
   612  		return typesEqual(at.elem, av.elem, seen) && at.len == av.len
   613  	case kindChan:
   614  		ct := (*chantype)(unsafe.Pointer(t))
   615  		cv := (*chantype)(unsafe.Pointer(v))
   616  		return ct.dir == cv.dir && typesEqual(ct.elem, cv.elem, seen)
   617  	case kindFunc:
   618  		ft := (*functype)(unsafe.Pointer(t))
   619  		fv := (*functype)(unsafe.Pointer(v))
   620  		if ft.outCount != fv.outCount || ft.inCount != fv.inCount {
   621  			return false
   622  		}
   623  		tin, vin := ft.in(), fv.in()
   624  		for i := 0; i < len(tin); i++ {
   625  			if !typesEqual(tin[i], vin[i], seen) {
   626  				return false
   627  			}
   628  		}
   629  		tout, vout := ft.out(), fv.out()
   630  		for i := 0; i < len(tout); i++ {
   631  			if !typesEqual(tout[i], vout[i], seen) {
   632  				return false
   633  			}
   634  		}
   635  		return true
   636  	case kindInterface:
   637  		it := (*interfacetype)(unsafe.Pointer(t))
   638  		iv := (*interfacetype)(unsafe.Pointer(v))
   639  		if it.pkgpath.name() != iv.pkgpath.name() {
   640  			return false
   641  		}
   642  		if len(it.mhdr) != len(iv.mhdr) {
   643  			return false
   644  		}
   645  		for i := range it.mhdr {
   646  			tm := &it.mhdr[i]
   647  			vm := &iv.mhdr[i]
   648  			// Note the mhdr array can be relocated from
   649  			// another module. See #17724.
   650  			tname := resolveNameOff(unsafe.Pointer(tm), tm.name)
   651  			vname := resolveNameOff(unsafe.Pointer(vm), vm.name)
   652  			if tname.name() != vname.name() {
   653  				return false
   654  			}
   655  			if tname.pkgPath() != vname.pkgPath() {
   656  				return false
   657  			}
   658  			tityp := resolveTypeOff(unsafe.Pointer(tm), tm.ityp)
   659  			vityp := resolveTypeOff(unsafe.Pointer(vm), vm.ityp)
   660  			if !typesEqual(tityp, vityp, seen) {
   661  				return false
   662  			}
   663  		}
   664  		return true
   665  	case kindMap:
   666  		mt := (*maptype)(unsafe.Pointer(t))
   667  		mv := (*maptype)(unsafe.Pointer(v))
   668  		return typesEqual(mt.key, mv.key, seen) && typesEqual(mt.elem, mv.elem, seen)
   669  	case kindPtr:
   670  		pt := (*ptrtype)(unsafe.Pointer(t))
   671  		pv := (*ptrtype)(unsafe.Pointer(v))
   672  		return typesEqual(pt.elem, pv.elem, seen)
   673  	case kindSlice:
   674  		st := (*slicetype)(unsafe.Pointer(t))
   675  		sv := (*slicetype)(unsafe.Pointer(v))
   676  		return typesEqual(st.elem, sv.elem, seen)
   677  	case kindStruct:
   678  		st := (*structtype)(unsafe.Pointer(t))
   679  		sv := (*structtype)(unsafe.Pointer(v))
   680  		if len(st.fields) != len(sv.fields) {
   681  			return false
   682  		}
   683  		if st.pkgPath.name() != sv.pkgPath.name() {
   684  			return false
   685  		}
   686  		for i := range st.fields {
   687  			tf := &st.fields[i]
   688  			vf := &sv.fields[i]
   689  			if tf.name.name() != vf.name.name() {
   690  				return false
   691  			}
   692  			if !typesEqual(tf.typ, vf.typ, seen) {
   693  				return false
   694  			}
   695  			if tf.name.tag() != vf.name.tag() {
   696  				return false
   697  			}
   698  			if tf.offsetAnon != vf.offsetAnon {
   699  				return false
   700  			}
   701  		}
   702  		return true
   703  	default:
   704  		println("runtime: impossible type kind", kind)
   705  		throw("runtime: impossible type kind")
   706  		return false
   707  	}
   708  }