diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index 3ffb7dcefa7..21301ab1499 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -412,7 +412,7 @@ func dimportpath(p *types.Pkg) { } s := base.Ctxt.Lookup("type..importpath." + p.Prefix + ".") - ot := dnameData(s, 0, p.Path, "", nil, false) + ot := dnameData(s, 0, p.Path, "", nil, false, false) objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA) s.Set(obj.AttrContentAddressable, true) p.Pathsym = s @@ -461,12 +461,12 @@ func dnameField(lsym *obj.LSym, ot int, spkg *types.Pkg, ft *types.Field) int { if !types.IsExported(ft.Sym.Name) && ft.Sym.Pkg != spkg { base.Fatalf("package mismatch for %v", ft.Sym) } - nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name)) + nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name), ft.Embedded != 0) return objw.SymPtr(lsym, ot, nsym, 0) } // dnameData writes the contents of a reflect.name into s at offset ot. -func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported bool) int { +func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported, embedded bool) int { if len(name) >= 1<<29 { base.Fatalf("name too long: %d %s...", len(name), name[:1024]) } @@ -491,6 +491,9 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b if pkg != nil { bits |= 1 << 2 } + if embedded { + bits |= 1 << 3 + } b := make([]byte, l) b[0] = bits copy(b[1:], nameLen[:nameLenLen]) @@ -513,7 +516,7 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b var dnameCount int // dname creates a reflect.name for a struct field or method. -func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym { +func dname(name, tag string, pkg *types.Pkg, exported, embedded bool) *obj.LSym { // Write out data as "type.." to signal two things to the // linker, first that when dynamically linking, the symbol // should be moved to a relro section, and second that the @@ -538,11 +541,14 @@ func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym { sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount) dnameCount++ } + if embedded { + sname += ".embedded" + } s := base.Ctxt.Lookup(sname) if len(s.P) > 0 { return s } - ot := dnameData(s, 0, name, tag, pkg, exported) + ot := dnameData(s, 0, name, tag, pkg, exported, embedded) objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA) s.Set(obj.AttrContentAddressable, true) return s @@ -610,7 +616,7 @@ func dextratypeData(lsym *obj.LSym, ot int, t *types.Type) int { if !exported && a.name.Pkg != typePkg(t) { pkg = a.name.Pkg } - nsym := dname(a.name.Name, "", pkg, exported) + nsym := dname(a.name.Name, "", pkg, exported, false) ot = objw.SymPtrOff(lsym, ot, nsym) ot = dmethodptrOff(lsym, ot, writeType(a.mtype)) @@ -775,7 +781,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int { } ot = objw.SymPtr(lsym, ot, gcsym, 0) // gcdata - nsym := dname(p, "", nil, exported) + nsym := dname(p, "", nil, exported, false) ot = objw.SymPtrOff(lsym, ot, nsym) // str // ptrToThis if sptr == nil { @@ -1074,7 +1080,7 @@ func writeType(t *types.Type) *obj.LSym { if !exported && a.name.Pkg != tpkg { pkg = a.name.Pkg } - nsym := dname(a.name.Name, "", pkg, exported) + nsym := dname(a.name.Name, "", pkg, exported, false) ot = objw.SymPtrOff(lsym, ot, nsym) ot = objw.SymPtrOff(lsym, ot, writeType(a.type_)) @@ -1180,14 +1186,7 @@ func writeType(t *types.Type) *obj.LSym { // ../../../../runtime/type.go:/structField ot = dnameField(lsym, ot, spkg, f) ot = objw.SymPtr(lsym, ot, writeType(f.Type), 0) - offsetAnon := uint64(f.Offset) << 1 - if offsetAnon>>1 != uint64(f.Offset) { - base.Fatalf("%v: bad field offset for %s", t, f.Sym.Name) - } - if f.Embedded != 0 { - offsetAnon |= 1 - } - ot = objw.Uintptr(lsym, ot, offsetAnon) + ot = objw.Uintptr(lsym, ot, uint64(f.Offset)) } } @@ -1356,7 +1355,7 @@ func WriteTabs() { // name nameOff // typ typeOff // pointer to symbol // } - nsym := dname(p.Sym().Name, "", nil, true) + nsym := dname(p.Sym().Name, "", nil, true, false) t := p.Type() if p.Class != ir.PFUNC { t = types.NewPtr(t) diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index a6ae202859e..b0f4b875633 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -132,6 +132,15 @@ func decodetypeName(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs return string(data[1+nameLenLen : 1+nameLenLen+int(nameLen)]) } +func decodetypeNameEmbedded(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) bool { + r := decodeRelocSym(ldr, symIdx, relocs, int32(off)) + if r == 0 { + return false + } + data := ldr.Data(r) + return data[0]&(1<<3) != 0 +} + func decodetypeFuncInType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym { uadd := commonsize(arch) + 4 if arch.PtrSize == 8 { @@ -204,12 +213,18 @@ func decodetypeStructFieldType(ldr *loader.Loader, arch *sys.Arch, symIdx loader return decodeRelocSym(ldr, symIdx, &relocs, int32(off+arch.PtrSize)) } -func decodetypeStructFieldOffsAnon(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 { +func decodetypeStructFieldOffset(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 { off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) data := ldr.Data(symIdx) return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize)) } +func decodetypeStructFieldEmbedded(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) bool { + off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i) + relocs := ldr.Relocs(symIdx) + return decodetypeNameEmbedded(ldr, symIdx, &relocs, off) +} + // decodetypeStr returns the contents of an rtype's str field (a nameOff). func decodetypeStr(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) string { relocs := ldr.Relocs(symIdx) diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 6ed9697aec4..c42511ea3f0 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -682,9 +682,9 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { } fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f) d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) - offsetAnon := decodetypeStructFieldOffsAnon(d.ldr, d.arch, gotype, i) - newmemberoffsetattr(fld, int32(offsetAnon>>1)) - if offsetAnon&1 != 0 { // is embedded field + offset := decodetypeStructFieldOffset(d.ldr, d.arch, gotype, i) + newmemberoffsetattr(fld, int32(offset)) + if decodetypeStructFieldEmbedded(d.ldr, d.arch, gotype, i) { newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0) } } diff --git a/src/internal/reflectlite/export_test.go b/src/internal/reflectlite/export_test.go index 3e5c258fb17..e9a928bdc6a 100644 --- a/src/internal/reflectlite/export_test.go +++ b/src/internal/reflectlite/export_test.go @@ -36,7 +36,7 @@ func Field(v Value, i int) Value { // In the former case, we want v.ptr + offset. // In the latter case, we must have field.offset = 0, // so v.ptr + field.offset is still the correct address. - ptr := add(v.ptr, field.offset(), "same as non-reflect &v.field") + ptr := add(v.ptr, field.offset, "same as non-reflect &v.field") return Value{typ, ptr, fl} } diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go index bc6fc947738..21e3c1278d0 100644 --- a/src/internal/reflectlite/type.go +++ b/src/internal/reflectlite/type.go @@ -269,17 +269,13 @@ type sliceType struct { // Struct field type structField struct { - name name // name is always non-empty - typ *rtype // type of field - offsetEmbed uintptr // byte offset of field<<1 | isEmbedded -} - -func (f *structField) offset() uintptr { - return f.offsetEmbed >> 1 + 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.offsetEmbed&1 != 0 + return f.name.embedded() } // structType represents a struct type. @@ -328,6 +324,10 @@ 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) { @@ -947,7 +947,10 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if cmpTags && tf.name.tag() != vf.name.tag() { return false } - if tf.offsetEmbed != vf.offsetEmbed { + if tf.offset != vf.offset { + return false + } + if tf.embedded() != vf.embedded() { return false } } diff --git a/src/reflect/abi.go b/src/reflect/abi.go index 9957d237689..32cb3141887 100644 --- a/src/reflect/abi.go +++ b/src/reflect/abi.go @@ -237,7 +237,7 @@ func (a *abiSeq) regAssign(t *rtype, offset uintptr) bool { st := (*structType)(unsafe.Pointer(t)) for i := range st.fields { f := &st.fields[i] - if !a.regAssign(f.typ, offset+f.offset()) { + if !a.regAssign(f.typ, offset+f.offset) { return false } } diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index a5a3c1c2710..f7d2cc362d5 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -140,7 +140,7 @@ func IsExported(t Type) bool { } func ResolveReflectName(s string) { - resolveReflectName(newName(s, "", false)) + resolveReflectName(newName(s, "", false, false)) } type Buffer struct { diff --git a/src/reflect/type.go b/src/reflect/type.go index 97040b51887..7b8cf0ee621 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -433,17 +433,13 @@ type sliceType struct { // Struct field type structField struct { - name name // name is always non-empty - typ *rtype // type of field - offsetEmbed uintptr // byte offset of field<<1 | isEmbedded -} - -func (f *structField) offset() uintptr { - return f.offsetEmbed >> 1 + 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.offsetEmbed&1 != 0 + return f.name.embedded() } // structType represents a struct type. @@ -460,6 +456,7 @@ type structType struct { // 1<<0 the name is exported // 1<<1 tag data follows the name // 1<<2 pkgPath nameOff follows the name and tag +// 1<<3 the name is of an embedded (a.k.a. anonymous) field // // Following that, there is a varint-encoded length of the name, // followed by the name itself. @@ -496,6 +493,10 @@ 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) { @@ -565,7 +566,7 @@ func (n name) pkgPath() string { return pkgPathName.name() } -func newName(n, tag string, exported bool) name { +func newName(n, tag string, exported, embedded bool) name { if len(n) >= 1<<29 { panic("reflect.nameFrom: name too long: " + n[:1024] + "...") } @@ -586,6 +587,9 @@ func newName(n, tag string, exported bool) name { l += tagLenLen + len(tag) bits |= 1 << 1 } + if embedded { + bits |= 1 << 3 + } b := make([]byte, l) b[0] = bits @@ -1256,7 +1260,7 @@ func (t *structType) Field(i int) (f StructField) { if tag := p.name.tag(); tag != "" { f.Tag = StructTag(tag) } - f.Offset = p.offset() + f.Offset = p.offset // NOTE(rsc): This is the only allocation in the interface // presented by a reflect.Type. It would be nice to avoid, @@ -1472,7 +1476,7 @@ func (t *rtype) ptrTo() *rtype { prototype := *(**ptrType)(unsafe.Pointer(&iptr)) pp := *prototype - pp.str = resolveReflectName(newName(s, "", false)) + pp.str = resolveReflectName(newName(s, "", false, false)) pp.ptrToThis = 0 // For the type structures linked into the binary, the @@ -1739,7 +1743,10 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if cmpTags && tf.name.tag() != vf.name.tag() { return false } - if tf.offsetEmbed != vf.offsetEmbed { + if tf.offset != vf.offset { + return false + } + if tf.embedded() != vf.embedded() { return false } } @@ -1891,7 +1898,7 @@ func ChanOf(dir ChanDir, t Type) Type { ch := *prototype ch.tflag = tflagRegularMemory ch.dir = uintptr(dir) - ch.str = resolveReflectName(newName(s, "", false)) + ch.str = resolveReflectName(newName(s, "", false, false)) ch.hash = fnv1(typ.hash, 'c', byte(dir)) ch.elem = typ @@ -1934,7 +1941,7 @@ func MapOf(key, elem Type) Type { // in ../cmd/compile/internal/reflectdata/reflect.go:writeType. var imap any = (map[unsafe.Pointer]unsafe.Pointer)(nil) mt := **(**mapType)(unsafe.Pointer(&imap)) - mt.str = resolveReflectName(newName(s, "", false)) + mt.str = resolveReflectName(newName(s, "", false, false)) mt.tflag = 0 mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash)) mt.key = ktyp @@ -2113,7 +2120,7 @@ func FuncOf(in, out []Type, variadic bool) Type { } // Populate the remaining fields of ft and store in cache. - ft.str = resolveReflectName(newName(str, "", false)) + ft.str = resolveReflectName(newName(str, "", false, false)) ft.ptrToThis = 0 return addToCache(&ft.rtype) } @@ -2290,7 +2297,7 @@ func bucketOf(ktyp, etyp *rtype) *rtype { gcdata: gcdata, } s := "bucket(" + ktyp.String() + "," + etyp.String() + ")" - b.str = resolveReflectName(newName(s, "", false)) + b.str = resolveReflectName(newName(s, "", false, false)) return b } @@ -2369,7 +2376,7 @@ func SliceOf(t Type) Type { prototype := *(**sliceType)(unsafe.Pointer(&islice)) slice := *prototype slice.tflag = 0 - slice.str = resolveReflectName(newName(s, "", false)) + slice.str = resolveReflectName(newName(s, "", false, false)) slice.hash = fnv1(typ.hash, '[') slice.elem = typ slice.ptrToThis = 0 @@ -2632,7 +2639,7 @@ func StructOf(fields []StructField) Type { typalign = ft.align } size = offset + ft.size - f.offsetEmbed |= offset << 1 + f.offset = offset if ft.size == 0 { lastzero = size @@ -2698,7 +2705,7 @@ func StructOf(fields []StructField) Type { *typ = *prototype typ.fields = fs if pkgpath != "" { - typ.pkgPath = newName(pkgpath, "", false) + typ.pkgPath = newName(pkgpath, "", false, false) } // Look in cache. @@ -2742,7 +2749,7 @@ func StructOf(fields []StructField) Type { } } - typ.str = resolveReflectName(newName(str, "", false)) + typ.str = resolveReflectName(newName(str, "", false, false)) typ.tflag = 0 // TODO: set tflagRegularMemory typ.hash = hash typ.size = size @@ -2774,14 +2781,14 @@ func StructOf(fields []StructField) Type { continue } // Pad to start of this field with zeros. - if ft.offset() > off { - n := (ft.offset() - off) / goarch.PtrSize + if ft.offset > off { + n := (ft.offset - off) / goarch.PtrSize prog = append(prog, 0x01, 0x00) // emit a 0 bit if n > 1 { prog = append(prog, 0x81) // repeat previous bit prog = appendVarint(prog, n-1) // n-1 times } - off = ft.offset() + off = ft.offset } prog = appendGCProg(prog, ft.typ) @@ -2803,8 +2810,8 @@ func StructOf(fields []StructField) Type { if comparable { typ.equal = func(p, q unsafe.Pointer) bool { for _, ft := range typ.fields { - pi := add(p, ft.offset(), "&x.field safe") - qi := add(q, ft.offset(), "&x.field safe") + pi := add(p, ft.offset, "&x.field safe") + qi := add(q, ft.offset, "&x.field safe") if !ft.typ.equal(pi, qi) { return false } @@ -2841,16 +2848,11 @@ func runtimeStructField(field StructField) (structField, string) { } } - offsetEmbed := uintptr(0) - if field.Anonymous { - offsetEmbed |= 1 - } - resolveReflectType(field.Type.common()) // install in runtime f := structField{ - name: newName(field.Name, string(field.Tag), field.IsExported()), - typ: field.Type.common(), - offsetEmbed: offsetEmbed, + name: newName(field.Name, string(field.Tag), field.IsExported(), field.Anonymous), + typ: field.Type.common(), + offset: 0, } return f, field.PkgPath } @@ -2874,7 +2876,7 @@ func typeptrdata(t *rtype) uintptr { return 0 } f := st.fields[field] - return f.offset() + f.typ.ptrdata + return f.offset + f.typ.ptrdata default: panic("reflect.typeptrdata: unexpected type, " + t.String()) @@ -2917,7 +2919,7 @@ func ArrayOf(length int, elem Type) Type { prototype := *(**arrayType)(unsafe.Pointer(&iarray)) array := *prototype array.tflag = typ.tflag & tflagRegularMemory - array.str = resolveReflectName(newName(s, "", false)) + array.str = resolveReflectName(newName(s, "", false, false)) array.hash = fnv1(typ.hash, '[') for n := uint32(length); n > 0; n >>= 8 { array.hash = fnv1(array.hash, byte(n)) @@ -3097,7 +3099,7 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Poo } else { s = "funcargs(" + t.String() + ")" } - x.str = resolveReflectName(newName(s, "", false)) + x.str = resolveReflectName(newName(s, "", false, false)) // cache result for future callers framePool = &sync.Pool{New: func() any { @@ -3165,7 +3167,7 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) { tt := (*structType)(unsafe.Pointer(t)) for i := range tt.fields { f := &tt.fields[i] - addTypeBits(bv, offset+f.offset(), f.typ) + addTypeBits(bv, offset+f.offset, f.typ) } } } diff --git a/src/reflect/value.go b/src/reflect/value.go index 5abdca28207..74554a3ac80 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1287,7 +1287,7 @@ func (v Value) Field(i int) Value { // In the former case, we want v.ptr + offset. // In the latter case, we must have field.offset = 0, // so v.ptr + field.offset is still the correct address. - ptr := add(v.ptr, field.offset(), "same as non-reflect &v.field") + ptr := add(v.ptr, field.offset, "same as non-reflect &v.field") return Value{typ, ptr, fl} } diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 5d7d1c77f4d..2a413eeef35 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -182,7 +182,7 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr { if f.name.isBlank() { continue } - h = typehash(f.typ, add(p, f.offset()), h) + h = typehash(f.typ, add(p, f.offset), h) } return h default: diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index 977d049378e..892654ed5b1 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -536,7 +536,7 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) { if f.typ.ptrdata == 0 { continue } - cgoCheckArg(f.typ, add(p, f.offset()), true, top, msg) + cgoCheckArg(f.typ, add(p, f.offset), true, top, msg) } case kindPtr, kindUnsafePointer: if indir { diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go index a841a31a27e..e42d71ad65e 100644 --- a/src/runtime/syscall_windows.go +++ b/src/runtime/syscall_windows.go @@ -174,7 +174,7 @@ func (p *abiDesc) tryRegAssignArg(t *_type, offset uintptr) bool { st := (*structtype)(unsafe.Pointer(t)) for i := range st.fields { f := &st.fields[i] - if !p.tryRegAssignArg(f.typ, offset+f.offset()) { + if !p.tryRegAssignArg(f.typ, offset+f.offset) { return false } } diff --git a/src/runtime/type.go b/src/runtime/type.go index b650d6d7959..e8e7819ecfe 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -414,13 +414,9 @@ type ptrtype struct { } type structfield struct { - name name - typ *_type - offsetAnon uintptr -} - -func (f *structfield) offset() uintptr { - return f.offsetAnon >> 1 + name name + typ *_type + offset uintptr } type structtype struct { @@ -443,6 +439,10 @@ func (n name) isExported() bool { return (*n.bytes)&(1<<0) != 0 } +func (n name) isEmbedded() bool { + return (*n.bytes)&(1<<3) != 0 +} + func (n name) readvarint(off int) (int, int) { v := 0 for i := 0; ; i++ { @@ -703,7 +703,10 @@ func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool { if tf.name.tag() != vf.name.tag() { return false } - if tf.offsetAnon != vf.offsetAnon { + if tf.offset != vf.offset { + return false + } + if tf.name.isEmbedded() != vf.name.isEmbedded() { return false } }