From aff951c80f02f909dbff86eafa3e1a5993095bbe Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Fri, 11 Oct 2013 15:34:19 -0400 Subject: [PATCH] go.tools/pointer: more reflection operators. (reflect.Value).Bytes (reflect.Value).Elem (reflect.Value).Index (reflect.Value).SetBytes (reflect.Value).Slice reflect.PtrTo reflect.SliceOf + Tests. Also: comment out an 'info-'level print statement in the test; it was distracting. R=crawshaw CC=golang-dev https://golang.org/cl/14454055 --- pointer/pointer_test.go | 6 +- pointer/reflect.go | 370 +++++++++++++++++++++++++++++-- pointer/solve.go | 4 + pointer/testdata/arrayreflect.go | 223 ++++++++++++------- 4 files changed, 507 insertions(+), 96 deletions(-) diff --git a/pointer/pointer_test.go b/pointer/pointer_test.go index 460cc8dd..ebb2e02a 100644 --- a/pointer/pointer_test.go +++ b/pointer/pointer_test.go @@ -37,6 +37,7 @@ var inputs = []string{ // Working: "testdata/a_test.go", "testdata/another.go", + "testdata/arrayreflect.go", "testdata/arrays.go", "testdata/channels.go", "testdata/chanreflect.go", @@ -45,9 +46,9 @@ var inputs = []string{ "testdata/flow.go", "testdata/fmtexcerpt.go", "testdata/func.go", + "testdata/funcreflect.go", "testdata/hello.go", "testdata/interfaces.go", - "testdata/funcreflect.go", "testdata/mapreflect.go", "testdata/maps.go", "testdata/panic.go", @@ -58,7 +59,6 @@ var inputs = []string{ // TODO(adonovan): get these tests (of reflection) passing. // (The tests are mostly sound since they were used for a // previous implementation.) - // "testdata/arrayreflect.go", // "testdata/finalizer.go", // "testdata/structreflect.go", } @@ -193,7 +193,7 @@ func doOneInput(input, filename string) bool { if mainpkg.Func("main") == nil { // No main function; assume it's a test. mainpkg.CreateTestMainFunction() - fmt.Printf("%s: synthesized testmain package for test.\n", imp.Fset.Position(f.Package)) + // fmt.Printf("%s: synthesized testmain package for test.\n", imp.Fset.Position(f.Package)) } ok := true diff --git a/pointer/reflect.go b/pointer/reflect.go index 781f1d59..51e49f5c 100644 --- a/pointer/reflect.go +++ b/pointer/reflect.go @@ -31,17 +31,175 @@ import ( // -------------------- (reflect.Value) -------------------- -func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} -func ext۰reflect۰Value۰Bytes(a *analysis, cgn *cgnode) {} -func ext۰reflect۰Value۰Call(a *analysis, cgn *cgnode) {} -func ext۰reflect۰Value۰CallSlice(a *analysis, cgn *cgnode) {} -func ext۰reflect۰Value۰Convert(a *analysis, cgn *cgnode) {} -func ext۰reflect۰Value۰Elem(a *analysis, cgn *cgnode) {} +func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} + +// ---------- func (Value).Bytes() Value ---------- + +// result = v.Bytes() +type rVBytesConstraint struct { + v nodeid // (ptr) + result nodeid +} + +func (c *rVBytesConstraint) String() string { + return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v) +} + +func (c *rVBytesConstraint) ptr() nodeid { + return c.v +} + +func (c *rVBytesConstraint) solve(a *analysis, _ *node, delta nodeset) { + changed := false + for vObj := range delta { + tDyn, slice, indirect := a.taggedValue(vObj) + if indirect { + // TODO(adonovan): we'll need to implement this + // when we start creating indirect tagged objects. + panic("indirect tagged object") + } + + tSlice, ok := tDyn.Underlying().(*types.Slice) + if ok && types.IsIdentical(tSlice.Elem(), types.Typ[types.Uint8]) { + if a.onlineCopy(c.result, slice) { + changed = true + } + } + } + if changed { + a.addWork(c.result) + } +} + +func ext۰reflect۰Value۰Bytes(a *analysis, cgn *cgnode) { + a.addConstraint(&rVBytesConstraint{ + v: a.funcParams(cgn.obj), + result: a.funcResults(cgn.obj), + }) +} + +func ext۰reflect۰Value۰Call(a *analysis, cgn *cgnode) {} +func ext۰reflect۰Value۰CallSlice(a *analysis, cgn *cgnode) {} +func ext۰reflect۰Value۰Convert(a *analysis, cgn *cgnode) {} + +// ---------- func (Value).Elem() Value ---------- + +// result = v.Elem() +type rVElemConstraint struct { + cgn *cgnode + v nodeid // (ptr) + result nodeid +} + +func (c *rVElemConstraint) String() string { + return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v) +} + +func (c *rVElemConstraint) ptr() nodeid { + return c.v +} + +func (c *rVElemConstraint) solve(a *analysis, _ *node, delta nodeset) { + changed := false + for vObj := range delta { + tDyn, payload, indirect := a.taggedValue(vObj) + if indirect { + // TODO(adonovan): we'll need to implement this + // when we start creating indirect tagged objects. + panic("indirect tagged object") + } + + switch t := tDyn.Underlying().(type) { + case *types.Interface: + // A direct tagged object can't hold an + // interface type. Implement when we support + // indirect tagged objects. + panic("unreachable") + + case *types.Pointer: + obj := a.makeTagged(t.Elem(), c.cgn, nil) + a.load(obj+1, payload, 0, a.sizeof(t.Elem())) + if a.addLabel(c.result, obj) { + changed = true + } + } + } + if changed { + a.addWork(c.result) + } +} + +func ext۰reflect۰Value۰Elem(a *analysis, cgn *cgnode) { + a.addConstraint(&rVElemConstraint{ + cgn: cgn, + v: a.funcParams(cgn.obj), + result: a.funcResults(cgn.obj), + }) +} + func ext۰reflect۰Value۰Field(a *analysis, cgn *cgnode) {} func ext۰reflect۰Value۰FieldByIndex(a *analysis, cgn *cgnode) {} func ext۰reflect۰Value۰FieldByName(a *analysis, cgn *cgnode) {} func ext۰reflect۰Value۰FieldByNameFunc(a *analysis, cgn *cgnode) {} -func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode) {} + +// ---------- func (Value).Index() Value ---------- + +// result = v.Index() +type rVIndexConstraint struct { + cgn *cgnode + v nodeid // (ptr) + result nodeid +} + +func (c *rVIndexConstraint) String() string { + return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v) +} + +func (c *rVIndexConstraint) ptr() nodeid { + return c.v +} + +func (c *rVIndexConstraint) solve(a *analysis, _ *node, delta nodeset) { + changed := false + for vObj := range delta { + tDyn, payload, indirect := a.taggedValue(vObj) + if indirect { + // TODO(adonovan): we'll need to implement this + // when we start creating indirect tagged objects. + panic("indirect tagged object") + } + + var res nodeid + switch t := tDyn.Underlying().(type) { + case *types.Array: + res = a.makeTagged(t.Elem(), c.cgn, nil) + a.onlineCopyN(res+1, payload+1, a.sizeof(t.Elem())) + + case *types.Slice: + res = a.makeTagged(t.Elem(), c.cgn, nil) + a.load(res+1, payload, 1, a.sizeof(t.Elem())) + + case *types.Basic: + if t.Kind() == types.String { + res = a.makeTagged(types.Typ[types.Rune], c.cgn, nil) + } + } + if res != 0 && a.addLabel(c.result, res) { + changed = true + } + } + if changed { + a.addWork(c.result) + } +} + +func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode) { + a.addConstraint(&rVIndexConstraint{ + cgn: cgn, + v: a.funcParams(cgn.obj), + result: a.funcResults(cgn.obj), + }) +} // ---------- func (Value).Interface() Value ---------- @@ -296,8 +454,51 @@ func ext۰reflect۰Value۰Send(a *analysis, cgn *cgnode) { }) } -func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode) {} -func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) {} +func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode) {} + +// ---------- func (Value).SetBytes(x []byte) ---------- + +// v.SetBytes(x) +type rVSetBytesConstraint struct { + cgn *cgnode + v nodeid // (ptr) + x nodeid +} + +func (c *rVSetBytesConstraint) String() string { + return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x) +} + +func (c *rVSetBytesConstraint) ptr() nodeid { + return c.v +} + +func (c *rVSetBytesConstraint) solve(a *analysis, _ *node, delta nodeset) { + for vObj := range delta { + tDyn, slice, indirect := a.taggedValue(vObj) + if indirect { + // TODO(adonovan): we'll need to implement this + // when we start creating indirect tagged objects. + panic("indirect tagged object") + } + + tSlice, ok := tDyn.Underlying().(*types.Slice) + if ok && types.IsIdentical(tSlice.Elem(), types.Typ[types.Uint8]) { + if a.onlineCopy(slice, c.x) { + a.addWork(slice) + } + } + } +} + +func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) { + params := a.funcParams(cgn.obj) + a.addConstraint(&rVSetBytesConstraint{ + cgn: cgn, + v: params, + x: params + 1, + }) +} // ---------- func (Value).SetMapIndex(k Value, v Value) ---------- @@ -355,7 +556,74 @@ func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) { } func ext۰reflect۰Value۰SetPointer(a *analysis, cgn *cgnode) {} -func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode) {} + +// ---------- func (Value).Slice(v Value, i, j int) ---------- + +// result = v.Slice(_, _) +type rVSliceConstraint struct { + cgn *cgnode + v nodeid // (ptr) + result nodeid +} + +func (c *rVSliceConstraint) String() string { + return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v) +} + +func (c *rVSliceConstraint) ptr() nodeid { + return c.v +} + +func (c *rVSliceConstraint) solve(a *analysis, _ *node, delta nodeset) { + changed := false + for vObj := range delta { + tDyn, payload, indirect := a.taggedValue(vObj) + if indirect { + // TODO(adonovan): we'll need to implement this + // when we start creating indirect tagged objects. + panic("indirect tagged object") + } + + var res nodeid + switch t := tDyn.Underlying().(type) { + case *types.Pointer: + if tArr, ok := t.Elem().Underlying().(*types.Array); ok { + // pointer to array + res = a.makeTagged(types.NewSlice(tArr.Elem()), c.cgn, nil) + if a.onlineCopy(res+1, payload) { + a.addWork(res + 1) + } + } + + case *types.Array: + // TODO(adonovan): implement addressable + // arrays when we do indirect tagged objects. + + case *types.Slice: + res = vObj + + case *types.Basic: + if t == types.Typ[types.String] { + res = vObj + } + } + + if res != 0 && a.addLabel(c.result, res) { + changed = true + } + } + if changed { + a.addWork(c.result) + } +} + +func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode) { + a.addConstraint(&rVSliceConstraint{ + cgn: cgn, + v: a.funcParams(cgn.obj), + result: a.funcResults(cgn.obj), + }) +} // -------------------- Standalone reflect functions -------------------- @@ -648,9 +916,85 @@ func ext۰reflect۰NewAt(a *analysis, cgn *cgnode) { } } -func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) {} -func ext۰reflect۰Select(a *analysis, cgn *cgnode) {} -func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) {} +// ---------- func PtrTo(Type) Type ---------- + +// result = PtrTo(t) +type reflectPtrToConstraint struct { + cgn *cgnode + t nodeid // (ptr) + result nodeid +} + +func (c *reflectPtrToConstraint) String() string { + return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t) +} + +func (c *reflectPtrToConstraint) ptr() nodeid { + return c.t +} + +func (c *reflectPtrToConstraint) solve(a *analysis, _ *node, delta nodeset) { + changed := false + for tObj := range delta { + T := a.rtypeTaggedValue(tObj) + + if a.addLabel(c.result, a.makeRtype(types.NewPointer(T))) { + changed = true + } + } + if changed { + a.addWork(c.result) + } +} + +func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) { + a.addConstraint(&reflectPtrToConstraint{ + cgn: cgn, + t: a.funcParams(cgn.obj), + result: a.funcResults(cgn.obj), + }) +} + +func ext۰reflect۰Select(a *analysis, cgn *cgnode) {} + +// ---------- func SliceOf(Type) Type ---------- + +// result = SliceOf(t) +type reflectSliceOfConstraint struct { + cgn *cgnode + t nodeid // (ptr) + result nodeid +} + +func (c *reflectSliceOfConstraint) String() string { + return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t) +} + +func (c *reflectSliceOfConstraint) ptr() nodeid { + return c.t +} + +func (c *reflectSliceOfConstraint) solve(a *analysis, _ *node, delta nodeset) { + changed := false + for tObj := range delta { + T := a.rtypeTaggedValue(tObj) + + if a.addLabel(c.result, a.makeRtype(types.NewSlice(T))) { + changed = true + } + } + if changed { + a.addWork(c.result) + } +} + +func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) { + a.addConstraint(&reflectSliceOfConstraint{ + cgn: cgn, + t: a.funcParams(cgn.obj), + result: a.funcResults(cgn.obj), + }) +} // ---------- func TypeOf(v Value) Type ---------- diff --git a/pointer/solve.go b/pointer/solve.go index 907a6b86..0cb8b5ba 100644 --- a/pointer/solve.go +++ b/pointer/solve.go @@ -193,6 +193,10 @@ func (a *analysis) onlineCopy(dst, src nodeid) bool { if a.log != nil { fmt.Fprintf(a.log, "\t\t\tdynamic copy n%d <- n%d\n", dst, src) } + // TODO(adonovan): most calls to onlineCopy + // are followed by addWork, possibly batched + // via a 'changed' flag; see if there's a + // noticeable penalty to calling addWork here. return a.nodes[dst].pts.addAll(nsrc.pts) } } diff --git a/pointer/testdata/arrayreflect.go b/pointer/testdata/arrayreflect.go index ce742962..125843a4 100644 --- a/pointer/testdata/arrayreflect.go +++ b/pointer/testdata/arrayreflect.go @@ -2,107 +2,170 @@ package main -import "reflect" - // Test of arrays & slices with reflection. -var a int +import "reflect" -func arrayreflect1() { - sl := make([]*int, 10) // @line ar1make - sl[0] = &a +var a, b int - srv := reflect.ValueOf(sl).Slice(0, 0) - print(srv.Interface()) // @types []*int - print(srv.Interface().([]*int)) // @pointsto makeslice@ar1make:12 - print(srv.Interface().([]*int)[42]) // @pointsto main.a +type S string + +func reflectValueSlice() { + // reflect.Value contains a slice. + slice := make([]*int, 10) // @line slice + slice[0] = &a + rvsl := reflect.ValueOf(slice).Slice(0, 0) + print(rvsl.Interface()) // @types []*int + print(rvsl.Interface().([]*int)) // @pointsto makeslice@slice:15 + print(rvsl.Interface().([]*int)[42]) // @pointsto main.a + + // reflect.Value contains an arrayay (non-addressable). + array := [10]*int{&a} // @line array + rvarray := reflect.ValueOf(array).Slice(0, 0) + print(rvarray.Interface()) // @types + print(rvarray.Interface().([]*int)) // @pointsto + print(rvarray.Interface().([]*int)[42]) // @pointsto + + // reflect.Value contains a pointer-to-array + rvparray := reflect.ValueOf(&array).Slice(0, 0) + print(rvparray.Interface()) // @types []*int + print(rvparray.Interface().([]*int)) // @pointsto array@array:2 + print(rvparray.Interface().([]*int)[42]) // @pointsto main.a + + // reflect.Value contains a string. + rvstring := reflect.ValueOf("hi").Slice(0, 0) + print(rvstring.Interface()) // @types string + + // reflect.Value contains a (named) string type. + rvS := reflect.ValueOf(S("hi")).Slice(0, 0) + print(rvS.Interface()) // @types S + + // reflect.Value contains a non-array pointer. + rvptr := reflect.ValueOf(new(int)).Slice(0, 0) + print(rvptr.Interface()) // @types + + // reflect.Value contains a non-string basic type. + rvint := reflect.ValueOf(3).Slice(0, 0) + print(rvint.Interface()) // @types } -func arrayreflect2() { - var arr [10]*int - sl := arr[:] - sl[0] = &a +func reflectValueBytes() { + sl1 := make([]byte, 0) // @line ar5sl1 + sl2 := make([]byte, 0) // @line ar5sl2 - srv := reflect.ValueOf(sl).Slice(0, 0) - print(srv.Interface()) // @types []*int - print(srv.Interface().([]*int)) // pointsto TODO - print(srv.Interface().([]*int)[42]) // @pointsto main.a + rvsl1 := reflect.ValueOf(sl1) + print(rvsl1.Interface()) // @types []byte + print(rvsl1.Interface().([]byte)) // @pointsto makeslice@ar5sl1:13 + print(rvsl1.Bytes()) // @pointsto makeslice@ar5sl1:13 + + rvsl2 := reflect.ValueOf(123) + rvsl2.SetBytes(sl2) + print(rvsl2.Interface()) // @types int + print(rvsl2.Interface().([]byte)) // @pointsto + print(rvsl2.Bytes()) // @pointsto + + rvsl3 := reflect.ValueOf([]byte(nil)) + rvsl3.SetBytes(sl2) + print(rvsl3.Interface()) // @types []byte + print(rvsl3.Interface().([]byte)) // @pointsto makeslice@ar5sl2:13 + print(rvsl3.Bytes()) // @pointsto makeslice@ar5sl2:13 } -func arrayreflect3() { - srv := reflect.ValueOf("hi").Slice(0, 0) - print(srv.Interface()) // @types string +func reflectValueIndex() { + slice := []*int{&a} // @line ar6slice + rv1 := reflect.ValueOf(slice) + print(rv1.Index(42).Interface()) // @types *int + print(rv1.Index(42).Interface().(*int)) // @pointsto main.a - type S string - srv2 := reflect.ValueOf(S("hi")).Slice(0, 0) - print(srv2.Interface()) // @types main.S + array := [10]*int{&a} + rv2 := reflect.ValueOf(array) + print(rv2.Index(42).Interface()) // @types *int + print(rv2.Index(42).Interface().(*int)) // @pointsto main.a + + rv3 := reflect.ValueOf("string") + print(rv3.Index(42).Interface()) // @types rune + + rv4 := reflect.ValueOf(&array) + print(rv4.Index(42).Interface()) // @types + + rv5 := reflect.ValueOf(3) + print(rv5.Index(42).Interface()) // @types } -func arrayreflect4() { - rv1 := reflect.ValueOf("hi") - rv2 := rv1 // backflow! - if unknown { - rv2 = reflect.ValueOf(123) - } - // We see backflow through the assignment above causing an - // imprecise result for rv1. This is because the SSA builder - // doesn't yet lift structs (like reflect.Value) into - // registers so these are all loads/stores to the stack. - // Under Das's algorithm, the extra indirection results in - // (undirected) unification not (directed) flow edges. - // TODO(adonovan): precision: lift aggregates. - print(rv1.Interface()) // @types string | int - print(rv2.Interface()) // @types string | int +func reflectValueElem() { + // TODO(adonovan): tests 'interface'. Needs indirect tagged objects. + // rv1 := reflect.ValueOf(...) + // print(rv1.Elem().Interface()) // #@types *int + // print(rv1.Elem().Interface().(*int)) // #@pointsto main.a + + // Pointer. + ptr := &a + rv2 := reflect.ValueOf(&ptr) + print(rv2.Elem().Interface()) // @types *int + print(rv2.Elem().Interface().(*int)) // @pointsto main.a + + // No other type works with (rV).Elem, not even those that + // work with (rT).Elem: slice, array, map, chan. + + rv3 := reflect.ValueOf([]*int{&a}) + print(rv3.Elem().Interface()) // @types + + rv4 := reflect.ValueOf([10]*int{&a}) + print(rv4.Elem().Interface()) // @types + + rv5 := reflect.ValueOf(map[*int]*int{&a: &b}) + print(rv5.Elem().Interface()) // @types + + ch := make(chan *int) + ch <- &a + rv6 := reflect.ValueOf(ch) + print(rv6.Elem().Interface()) // @types + + rv7 := reflect.ValueOf(3) + print(rv7.Elem().Interface()) // @types } -func arrayreflect5() { - sl1 := make([]byte, 0) - sl2 := make([]byte, 0) +func reflectTypeElem() { + rt1 := reflect.TypeOf(make([]*int, 0)) + print(reflect.Zero(rt1.Elem())) // @types *int - srv := reflect.ValueOf(sl1) + rt2 := reflect.TypeOf([10]*int{}) + print(reflect.Zero(rt2.Elem())) // @types *int - print(srv.Interface()) // @types []byte - print(srv.Interface().([]byte)) // @pointsto makeslice@testdata/arrayreflect.go:62:13 - print(srv.Bytes()) // @pointsto makeslice@testdata/arrayreflect.go:62:13 + rt3 := reflect.TypeOf(map[*int]*int{}) + print(reflect.Zero(rt3.Elem())) // @types *int - srv2 := reflect.ValueOf(123) - srv2.SetBytes(sl2) - print(srv2.Interface()) // @types []byte | int - print(srv2.Interface().([]byte)) // @pointsto makeslice@testdata/arrayreflect.go:63:13 - print(srv2.Bytes()) // @pointsto makeslice@testdata/arrayreflect.go:63:13 + rt4 := reflect.TypeOf(make(chan *int)) + print(reflect.Zero(rt4.Elem())) // @types *int + + ptr := &a + rt5 := reflect.TypeOf(&ptr) + print(reflect.Zero(rt5.Elem())) // @types *int + + rt6 := reflect.TypeOf(3) + print(reflect.Zero(rt6.Elem())) // @types } -func arrayreflect6() { - sl1 := []*bool{new(bool)} - sl2 := []*int{&a} +func reflectPtrTo() { + tInt := reflect.TypeOf(3) + tPtrInt := reflect.PtrTo(tInt) + print(reflect.Zero(tPtrInt)) // @types *int + tPtrPtrInt := reflect.PtrTo(tPtrInt) + print(reflect.Zero(tPtrPtrInt)) // @types **int +} - srv1 := reflect.ValueOf(sl1) - print(srv1.Index(42).Interface()) // @types *bool - print(srv1.Index(42).Interface().(*bool)) // @pointsto alloc@testdata/arrayreflect.go:79:20 - - srv2 := reflect.ValueOf(sl2) - print(srv2.Index(42).Interface()) // @types *int - print(srv2.Index(42).Interface().(*int)) // @pointsto main.a - - p1 := &sl1[0] - p2 := &sl2[0] - - prv1 := reflect.ValueOf(p1) - print(prv1.Elem().Interface()) // @types *bool - print(prv1.Elem().Interface().(*bool)) // @pointsto alloc@testdata/arrayreflect.go:79:20 - - prv2 := reflect.ValueOf(p2) - print(prv2.Elem().Interface()) // @types *int - print(prv2.Elem().Interface().(*int)) // @pointsto main.a +func reflectSliceOf() { + tInt := reflect.TypeOf(3) + tSliceInt := reflect.SliceOf(tInt) + print(reflect.Zero(tSliceInt)) // @types []int } func main() { - arrayreflect1() - arrayreflect2() - arrayreflect3() - arrayreflect4() - arrayreflect5() - arrayreflect6() + reflectValueSlice() + reflectValueBytes() + reflectValueIndex() + reflectValueElem() + reflectTypeElem() + reflectPtrTo() + reflectSliceOf() } - -var unknown bool