...
Run Format

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

View as plain text