Black Lives Matter. Support the Equal Justice Initiative.

Source file src/runtime/type.go

Documentation: runtime

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

View as plain text