// read version specific flags - extend as necessary
switch p.version {
- // case 4:
+ // case 5:
// ...
// fallthrough
- case 3, 2, 1:
+ case 4, 3, 2, 1:
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
p.trackAllTypes = p.bool()
p.posInfoFormat = p.bool()
typecheckok = tcok
resumecheckwidth()
- testdclstack() // debugging only
+ if debug_dclstack != 0 {
+ testdclstack()
+ }
}
func formatErrorf(format string, args ...interface{}) {
val := p.value(typ)
importconst(sym, idealType(typ), nodlit(val))
+ case aliasTag:
+ p.pos()
+ sym := p.qualifiedName()
+ typ := p.typ()
+ importalias(sym, typ)
+
case typeTag:
p.typ()
}
}
- case aliasTag:
- p.pos()
- alias := importpkg.Lookup(p.string())
- orig := p.qualifiedName()
-
- // Although the protocol allows the alias to precede the original,
- // this never happens in files produced by gc.
- alias.Flags |= SymAlias
- alias.Def = orig.Def
- importsym(alias, orig.Def.Op)
-
default:
formatErrorf("unexpected object (tag = %d)", tag)
}
result := p.paramList()
nointerface := p.bool()
- base := recv[0].Type
- star := false
- if base.IsPtr() {
- base = base.Elem()
- star = true
- }
-
- n := methodname0(sym, star, base.Sym)
+ n := newfuncname(methodname(sym, recv[0].Type))
n.Type = functypefield(recv[0], params, result)
checkwidth(n.Type)
addmethod(sym, n.Type, false, nointerface)
func (p *importer) field() *Field {
p.pos()
- sym := p.fieldName()
+ sym, alias := p.fieldName()
typ := p.typ()
note := p.string()
f := newField()
if sym.Name == "" {
- // anonymous field - typ must be T or *T and T must be a type name
+ // anonymous field: typ must be T or *T and T must be a type name
s := typ.Sym
if s == nil && typ.IsPtr() {
s = typ.Elem().Sym // deref
}
sym = sym.Pkg.Lookup(s.Name)
f.Embedded = 1
+ } else if alias {
+ // anonymous field: we have an explicit name because it's a type alias
+ f.Embedded = 1
}
f.Sym = sym
func (p *importer) method() *Field {
p.pos()
- sym := p.fieldName()
+ sym := p.methodName()
params := p.paramList()
result := p.paramList()
return f
}
- func (p *importer) fieldName() *Sym {
+ func (p *importer) fieldName() (*Sym, bool) {
name := p.string()
if p.version == 0 && name == "_" {
- // version 0 didn't export a package for _ fields
+ // version 0 didn't export a package for _ field names
// but used the builtin package instead
- return builtinpkg.Lookup(name)
+ return builtinpkg.Lookup(name), false
}
pkg := localpkg
- if name != "" && !exportname(name) {
- if name == "?" {
- name = ""
+ alias := false
+ switch name {
+ case "":
+ // 1) field name matches base type name and is exported: nothing to do
+ case "?":
+ // 2) field name matches base type name and is not exported: need package
+ name = ""
+ pkg = p.pkg()
+ case "@":
+ // 3) field name doesn't match base type name (alias name): need name and possibly package
+ name = p.string()
+ alias = true
+ fallthrough
+ default:
+ if !exportname(name) {
+ pkg = p.pkg()
}
+ }
+ return pkg.Lookup(name), alias
+ }
+
+ func (p *importer) methodName() *Sym {
+ name := p.string()
+ if p.version == 0 && name == "_" {
+ // version 0 didn't export a package for _ method names
+ // but used the builtin package instead
+ return builtinpkg.Lookup(name)
+ }
+ pkg := localpkg
+ if !exportname(name) {
pkg = p.pkg()
}
return pkg.Lookup(name)
)
var (
- Debug_append int
- Debug_closure int
- Debug_panic int
- Debug_slice int
- Debug_wb int
+ Debug_append int
+ Debug_closure int
+ debug_dclstack int
+ Debug_panic int
+ Debug_slice int
+ Debug_wb int
)
// Debug arguments.
{"append", &Debug_append}, // print information about append compilation
{"closure", &Debug_closure}, // print information about closure compilation
{"disablenil", &disable_checknil}, // disable nil checks
+ {"dclstack", &debug_dclstack}, // run internal dclstack checks
{"gcprog", &Debug_gcprog}, // print dump of GC programs
{"nil", &Debug_checknil}, // print information about nil checks
{"panic", &Debug_panic}, // do not hide any compiler panic
timings.Stop()
timings.AddEvent(int64(lexlineno-lexlineno0), "lines")
- testdclstack()
mkpackage(localpkg.Name) // final import not used checks
finishUniverse()
// Phase 1: const, type, and names and types of funcs.
// This will gather all the information about types
// and methods but doesn't depend on any of it.
+ // We also defer type alias declarations until phase 2
+ // to avoid cycles like #18640.
defercheckwidth()
// Don't use range--typecheck can add closures to xtop.
timings.Start("fe", "typecheck", "top1")
for i := 0; i < len(xtop); i++ {
- if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 {
- xtop[i] = typecheck(xtop[i], Etop)
+ n := xtop[i]
+ if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) {
+ xtop[i] = typecheck(n, Etop)
}
}
// Don't use range--typecheck can add closures to xtop.
timings.Start("fe", "typecheck", "top2")
for i := 0; i < len(xtop); i++ {
- if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 {
- xtop[i] = typecheck(xtop[i], Etop)
+ n := xtop[i]
+ if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {
+ xtop[i] = typecheck(n, Etop)
}
}
resumecheckwidth()
timings.Start("fe", "typecheck", "func")
var fcount int64
for i := 0; i < len(xtop); i++ {
- if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE {
- Curfn = xtop[i]
+ n := xtop[i]
+ if op := n.Op; op == ODCLFUNC || op == OCLOSURE {
+ Curfn = n
decldepth = 1
saveerrors()
typecheckslice(Curfn.Nbody.Slice(), Etop)
timings.Start("be", "compilefuncs")
fcount = 0
for i := 0; i < len(xtop); i++ {
- if xtop[i].Op == ODCLFUNC {
- funccompile(xtop[i])
+ n := xtop[i]
+ if n.Op == ODCLFUNC {
+ funccompile(n)
fcount++
}
}
continue
}
- if s.Def.Sym != s && s.Flags&SymAlias == 0 {
+ if s.isAlias() {
// throw away top-level name left over
// from previous import . "x"
if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
}
if nsyntaxerrors == 0 {
+ // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
testdclstack()
}
}
func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
names := p.declNames(decl.NameList)
-
- var typ *Node
- if decl.Type != nil {
- typ = p.typeExpr(decl.Type)
- }
+ typ := p.typeExprOrNil(decl.Type)
var exprs []*Node
if decl.Values != nil {
func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
names := p.declNames(decl.NameList)
-
- var typ *Node
- if decl.Type != nil {
- typ = p.typeExpr(decl.Type)
- }
+ typ := p.typeExprOrNil(decl.Type)
var exprs []*Node
if decl.Values != nil {
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
name := typedcl0(p.name(decl.Name))
- name.Name.Param.Pragma = Pragma(decl.Pragma)
- var typ *Node
- if decl.Type != nil {
- typ = p.typeExpr(decl.Type)
- }
+ // decl.Type may be nil but in that case we got a syntax error during parsing
+ typ := p.typeExprOrNil(decl.Type)
- return typedcl1(name, typ, true)
+ return typedcl1(name, typ, Pragma(decl.Pragma), decl.Alias)
}
func (p *noder) declNames(names []*syntax.Name) []*Node {
yyerror("func main must have no arguments and no return values")
}
}
-
- f.Func.Nname = newfuncname(name)
} else {
- // Receiver MethodName Signature
-
- f.Func.Shortname = newfuncname(name)
- f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right)
+ f.Func.Shortname = name
+ name = nblank.Sym // filled in by typecheckfunc
}
+ f.Func.Nname = newfuncname(name)
f.Func.Nname.Name.Defn = f
f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype
- declare(f.Func.Nname, PFUNC)
+ if fun.Recv == nil {
+ declare(f.Func.Nname, PFUNC)
+ }
+
funchdr(f)
return f
}
return p.expr(typ)
}
+ func (p *noder) typeExprOrNil(typ syntax.Expr) *Node {
+ if typ != nil {
+ return p.expr(typ)
+ }
+ return nil
+ }
+
func (p *noder) chanDir(dir syntax.ChanDir) ChanDir {
switch dir {
case 0:
lookup(f[1]).Linkname = f[2]
case strings.HasPrefix(text, "go:cgo_"):
+ lineno = p.baseline + int32(line) - 1 // pragcgo may call yyerror
pragcgobuf += pragcgo(text)
fallthrough // because of //go:cgo_unsafe_args
default:
if i := strings.Index(text, " "); i >= 0 {
verb = verb[:i]
}
+ lineno = p.baseline + int32(line) - 1 // pragmaValue may call yyerror
return syntax.Pragma(pragmaValue(verb))
}
case *ast.FuncLit:
p.expr(x.Type)
- p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body)
+ p.funcBody(p.distanceFrom(x.Type.Pos()), blank, x.Body)
case *ast.ParenExpr:
if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
if x.Type != nil {
p.expr1(x.Type, token.HighestPrec, depth)
}
+ p.level++
p.print(x.Lbrace, token.LBRACE)
p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
// do not insert extra line break following a /*-style comment
mode |= noExtraBlank
}
p.print(mode, x.Rbrace, token.RBRACE, mode)
+ p.level--
case *ast.Ellipsis:
p.print(token.ELLIPSIS)
} else {
p.print(vtab)
}
+ if s.Assign.IsValid() {
+ p.print(token.ASSIGN, blank)
+ }
p.expr(s.Type)
p.setComment(s.Comment)
return bodySize
}
-// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following
-// a header (e.g., a for-loop control clause or function signature) of given headerSize.
+// funcBody prints a function body following a function header of given headerSize.
// If the header's and block's size are "small enough" and the block is "simple enough",
// the block is printed on the current line, without line breaks, spaced from the header
// by sep. Otherwise the block's opening "{" is printed on the current line, followed by
// lines for the block's statements and its closing "}".
//
-func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
+func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
if b == nil {
return
}
+ // save/restore composite literal nesting level
+ defer func(level int) {
+ p.level = level
+ }(p.level)
+ p.level = 0
+
const maxSize = 100
if headerSize+p.bodySize(b, maxSize) <= maxSize {
p.print(sep, b.Lbrace, token.LBRACE)
}
p.expr(d.Name)
p.signature(d.Type.Params, d.Type.Results)
- p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body)
+ p.funcBody(p.distanceFrom(d.Pos()), vtab, d.Body)
}
func (p *printer) decl(decl ast.Decl) {
"unsafe"
)
+var sink interface{}
+
func TestBool(t *testing.T) {
v := ValueOf(true)
if v.Bool() != true {
func TestArrayOf(t *testing.T) {
// check construction and use of type not in binary
- for _, table := range []struct {
+ tests := []struct {
n int
value func(i int) interface{}
comparable bool
comparable: true,
want: "[{0 0} {1 1} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} {9 9}]",
},
- } {
+ }
+
+ for _, table := range tests {
at := ArrayOf(table.n, TypeOf(table.value(0)))
v := New(at).Elem()
vok := New(at).Elem()
f()
}
- for i, test := range []struct {
+ tests := []struct {
field StructField
mustPanic bool
exported bool
}{
{
- field: StructField{Name: "", Type: TypeOf(S1{})},
- mustPanic: false,
- exported: true,
+ field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{})},
+ exported: true,
},
{
- field: StructField{Name: "", Type: TypeOf((*S1)(nil))},
- mustPanic: false,
- exported: true,
+ field: StructField{Name: "S1", Anonymous: true, Type: TypeOf((*S1)(nil))},
+ exported: true,
},
{
- field: StructField{Name: "", Type: TypeOf(s2{})},
- mustPanic: false,
- exported: false,
+ field: StructField{Name: "s2", Anonymous: true, Type: TypeOf(s2{})},
+ mustPanic: true,
},
{
- field: StructField{Name: "", Type: TypeOf((*s2)(nil))},
- mustPanic: false,
- exported: false,
+ field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil))},
+ mustPanic: true,
},
{
- field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
+ field: StructField{Name: "Name", Type: nil, PkgPath: ""},
mustPanic: true,
- exported: true,
},
{
- field: StructField{Name: "", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
+ field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: ""},
+ mustPanic: true,
+ },
+ {
+ field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{}), PkgPath: "other/pkg"},
+ mustPanic: true,
+ },
+ {
+ field: StructField{Name: "S1", Anonymous: true, Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
+ mustPanic: true,
+ },
+ {
+ field: StructField{Name: "s2", Anonymous: true, Type: TypeOf(s2{}), PkgPath: "other/pkg"},
+ mustPanic: true,
+ },
+ {
+ field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
mustPanic: true,
- exported: true,
},
{
- field: StructField{Name: "", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
+ field: StructField{Name: "s2", Type: TypeOf(int(0)), PkgPath: "other/pkg"},
mustPanic: true,
- exported: false,
},
{
- field: StructField{Name: "", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
+ field: StructField{Name: "s2", Type: TypeOf(int(0)), PkgPath: "other/pkg"},
mustPanic: true,
- exported: false,
},
{
field: StructField{Name: "S", Type: TypeOf(S1{})},
exported: true,
},
{
- field: StructField{Name: "S", Type: TypeOf((*S1)(nil))},
- mustPanic: false,
- exported: true,
+ field: StructField{Name: "S", Type: TypeOf((*S1)(nil))},
+ exported: true,
},
{
- field: StructField{Name: "S", Type: TypeOf(s2{})},
- mustPanic: false,
- exported: true,
+ field: StructField{Name: "S", Type: TypeOf(s2{})},
+ exported: true,
},
{
- field: StructField{Name: "S", Type: TypeOf((*s2)(nil))},
- mustPanic: false,
- exported: true,
+ field: StructField{Name: "S", Type: TypeOf((*s2)(nil))},
+ exported: true,
},
{
field: StructField{Name: "s", Type: TypeOf(S1{})},
mustPanic: true,
- exported: false,
},
{
field: StructField{Name: "s", Type: TypeOf((*S1)(nil))},
mustPanic: true,
- exported: false,
},
{
field: StructField{Name: "s", Type: TypeOf(s2{})},
mustPanic: true,
- exported: false,
},
{
field: StructField{Name: "s", Type: TypeOf((*s2)(nil))},
mustPanic: true,
- exported: false,
},
{
field: StructField{Name: "s", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
mustPanic: true, // TODO(sbinet): creating a name with a package path
- exported: false,
},
{
field: StructField{Name: "s", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
mustPanic: true, // TODO(sbinet): creating a name with a package path
- exported: false,
},
{
field: StructField{Name: "s", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
mustPanic: true, // TODO(sbinet): creating a name with a package path
- exported: false,
},
{
field: StructField{Name: "s", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
mustPanic: true, // TODO(sbinet): creating a name with a package path
- exported: false,
},
{
field: StructField{Name: "", Type: TypeOf(ΦType{})},
- mustPanic: false,
- exported: true,
+ mustPanic: true,
},
{
field: StructField{Name: "", Type: TypeOf(φType{})},
- mustPanic: false,
- exported: false,
+ mustPanic: true,
},
{
- field: StructField{Name: "Φ", Type: TypeOf(0)},
- mustPanic: false,
- exported: true,
+ field: StructField{Name: "Φ", Type: TypeOf(0)},
+ exported: true,
},
{
- field: StructField{Name: "φ", Type: TypeOf(0)},
- mustPanic: false,
- exported: false,
+ field: StructField{Name: "φ", Type: TypeOf(0)},
+ exported: false,
},
- } {
+ }
+
+ for i, test := range tests {
testPanic(i, test.mustPanic, func() {
typ := StructOf([]StructField{test.field})
if typ == nil {
field := typ.Field(0)
n := field.Name
if n == "" {
- n = field.Type.Name()
+ panic("field.Name must not be empty")
}
exported := isExported(n)
if exported != test.exported {
{Name: "S1", Type: st1},
})
- for _, table := range []struct {
+ tests := []struct {
rt Type
idx []int
}{
),
idx: []int{2},
},
- } {
+ }
+
+ for _, table := range tests {
v1 := New(table.rt).Elem()
v2 := New(table.rt).Elem()
type Iface interface {
Get() int
}
- for i, table := range []struct {
+ tests := []struct {
+ name string
typ Type
val Value
impl bool
}{
{
+ name: "StructI",
typ: TypeOf(StructI(want)),
val: ValueOf(StructI(want)),
impl: true,
},
{
- typ: PtrTo(TypeOf(StructI(want))),
+ name: "StructI",
+ typ: PtrTo(TypeOf(StructI(want))),
val: ValueOf(func() interface{} {
v := StructI(want)
return &v
impl: true,
},
{
- typ: PtrTo(TypeOf(StructIPtr(want))),
+ name: "StructIPtr",
+ typ: PtrTo(TypeOf(StructIPtr(want))),
val: ValueOf(func() interface{} {
v := StructIPtr(want)
return &v
impl: true,
},
{
+ name: "StructIPtr",
typ: TypeOf(StructIPtr(want)),
val: ValueOf(StructIPtr(want)),
impl: false,
// val: ValueOf(StructI(want)),
// impl: true,
// },
- } {
+ }
+
+ for i, table := range tests {
rt := StructOf(
[]StructField{
{
- Name: "",
- PkgPath: "",
- Type: table.typ,
+ Name: table.name,
+ Anonymous: true,
+ PkgPath: "",
+ Type: table.typ,
},
},
)
f2("four", "five5", "six666", "seven77", "eight888")
}
+// Issue 18635 (function version).
+func TestKeepFuncLive(t *testing.T) {
+ // Test that we keep makeFuncImpl live as long as it is
+ // referenced on the stack.
+ typ := TypeOf(func(i int) {})
+ var f, g func(in []Value) []Value
+ f = func(in []Value) []Value {
+ clobber()
+ i := int(in[0].Int())
+ if i > 0 {
+ // We can't use Value.Call here because
+ // runtime.call* will keep the makeFuncImpl
+ // alive. However, by converting it to an
+ // interface value and calling that,
+ // reflect.callReflect is the only thing that
+ // can keep the makeFuncImpl live.
+ //
+ // Alternate between f and g so that if we do
+ // reuse the memory prematurely it's more
+ // likely to get obviously corrupted.
+ MakeFunc(typ, g).Interface().(func(i int))(i - 1)
+ }
+ return nil
+ }
+ g = func(in []Value) []Value {
+ clobber()
+ i := int(in[0].Int())
+ MakeFunc(typ, f).Interface().(func(i int))(i)
+ return nil
+ }
+ MakeFunc(typ, f).Call([]Value{ValueOf(10)})
+}
+
+// Issue 18635 (method version).
+type KeepMethodLive struct{}
+
+func (k KeepMethodLive) Method1(i int) {
+ clobber()
+ if i > 0 {
+ ValueOf(k).MethodByName("Method2").Interface().(func(i int))(i - 1)
+ }
+}
+
+func (k KeepMethodLive) Method2(i int) {
+ clobber()
+ ValueOf(k).MethodByName("Method1").Interface().(func(i int))(i)
+}
+
+func TestKeepMethodLive(t *testing.T) {
+ // Test that we keep methodValue live as long as it is
+ // referenced on the stack.
+ KeepMethodLive{}.Method1(10)
+}
+
+// clobber tries to clobber unreachable memory.
+func clobber() {
+ runtime.GC()
+ for i := 1; i < 32; i++ {
+ for j := 0; j < 10; j++ {
+ obj := make([]*byte, i)
+ sink = obj
+ }
+ }
+ runtime.GC()
+}
+
type funcLayoutTest struct {
rcvr, t Type
size, argsize, retOffset uintptr
want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}},
},
}
+
for i, tt := range tests {
inStr := fmt.Sprint(tt.in)
Swapper(tt.in)(tt.i, tt.j)
lv.Set(rv)
})
}
+
+
+ type Tint int
+
+ type Tint2 = Tint
+
+ type Talias1 struct {
+ byte
+ uint8
+ int
+ int32
+ rune
+ }
+
+ type Talias2 struct {
+ Tint
+ Tint2
+ }
+
+ func TestAliasNames(t *testing.T) {
+ t1 := Talias1{byte: 1, uint8: 2, int: 3, int32: 4, rune: 5}
+ out := fmt.Sprintf("%#v", t1)
+ want := "reflect_test.Talias1{byte:0x1, uint8:0x2, int:3, int32:4, rune:5}"
+ if out != want {
+ t.Errorf("Talias1 print:\nhave: %s\nwant: %s", out, want)
+ }
+
+ t2 := Talias2{Tint: 1, Tint2: 2}
+ out = fmt.Sprintf("%#v", t2)
+ want = "reflect_test.Talias2{Tint:1, Tint2:2}"
+ if out != want {
+ t.Errorf("Talias2 print:\nhave: %s\nwant: %s", out, want)
+ }
+ }
+
off += typ.size
}
}
+
+ // runtime.getArgInfo expects to be able to find ctxt on the
+ // stack when it finds our caller, makeFuncStub. Make sure it
+ // doesn't get garbage collected.
+ runtime.KeepAlive(ctxt)
}
// methodReceiver returns information about the receiver
// though it's a heap object.
memclrNoHeapPointers(args, frametype.size)
framePool.Put(args)
+
+ // See the comment in callReflect.
+ runtime.KeepAlive(ctxt)
}
// funcName returns the name of f, for use in error messages.
fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
// Using an unexported field forces flagRO.
if !field.name.isExported() {
- if field.name.name() == "" {
+ if field.anon() {
fl |= flagEmbedRO
} else {
fl |= flagStickyRO
// 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 okay.
- ptr := unsafe.Pointer(uintptr(v.ptr) + field.offset)
+ ptr := unsafe.Pointer(uintptr(v.ptr) + field.offset())
return Value{typ, ptr, fl}
}