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
This commit is contained in:
Alan Donovan 2013-10-11 15:34:19 -04:00
parent 271af2c149
commit aff951c80f
4 changed files with 507 additions and 96 deletions

View File

@ -37,6 +37,7 @@ var inputs = []string{
// Working: // Working:
"testdata/a_test.go", "testdata/a_test.go",
"testdata/another.go", "testdata/another.go",
"testdata/arrayreflect.go",
"testdata/arrays.go", "testdata/arrays.go",
"testdata/channels.go", "testdata/channels.go",
"testdata/chanreflect.go", "testdata/chanreflect.go",
@ -45,9 +46,9 @@ var inputs = []string{
"testdata/flow.go", "testdata/flow.go",
"testdata/fmtexcerpt.go", "testdata/fmtexcerpt.go",
"testdata/func.go", "testdata/func.go",
"testdata/funcreflect.go",
"testdata/hello.go", "testdata/hello.go",
"testdata/interfaces.go", "testdata/interfaces.go",
"testdata/funcreflect.go",
"testdata/mapreflect.go", "testdata/mapreflect.go",
"testdata/maps.go", "testdata/maps.go",
"testdata/panic.go", "testdata/panic.go",
@ -58,7 +59,6 @@ var inputs = []string{
// TODO(adonovan): get these tests (of reflection) passing. // TODO(adonovan): get these tests (of reflection) passing.
// (The tests are mostly sound since they were used for a // (The tests are mostly sound since they were used for a
// previous implementation.) // previous implementation.)
// "testdata/arrayreflect.go",
// "testdata/finalizer.go", // "testdata/finalizer.go",
// "testdata/structreflect.go", // "testdata/structreflect.go",
} }
@ -193,7 +193,7 @@ func doOneInput(input, filename string) bool {
if mainpkg.Func("main") == nil { if mainpkg.Func("main") == nil {
// No main function; assume it's a test. // No main function; assume it's a test.
mainpkg.CreateTestMainFunction() 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 ok := true

View File

@ -31,17 +31,175 @@ import (
// -------------------- (reflect.Value) -------------------- // -------------------- (reflect.Value) --------------------
func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} 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 (Value).Bytes() Value ----------
func ext۰reflect۰Value۰CallSlice(a *analysis, cgn *cgnode) {}
func ext۰reflect۰Value۰Convert(a *analysis, cgn *cgnode) {} // result = v.Bytes()
func ext۰reflect۰Value۰Elem(a *analysis, cgn *cgnode) {} 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۰Field(a *analysis, cgn *cgnode) {}
func ext۰reflect۰Value۰FieldByIndex(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۰FieldByName(a *analysis, cgn *cgnode) {}
func ext۰reflect۰Value۰FieldByNameFunc(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 ---------- // ---------- 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۰Set(a *analysis, cgn *cgnode) {}
func ext۰reflect۰Value۰SetBytes(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) ---------- // ---------- 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۰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 -------------------- // -------------------- Standalone reflect functions --------------------
@ -648,9 +916,85 @@ func ext۰reflect۰NewAt(a *analysis, cgn *cgnode) {
} }
} }
func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) {} // ---------- func PtrTo(Type) Type ----------
func ext۰reflect۰Select(a *analysis, cgn *cgnode) {}
func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) {} // 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 ---------- // ---------- func TypeOf(v Value) Type ----------

View File

@ -193,6 +193,10 @@ func (a *analysis) onlineCopy(dst, src nodeid) bool {
if a.log != nil { if a.log != nil {
fmt.Fprintf(a.log, "\t\t\tdynamic copy n%d <- n%d\n", dst, src) 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) return a.nodes[dst].pts.addAll(nsrc.pts)
} }
} }

View File

@ -2,107 +2,170 @@
package main package main
import "reflect"
// Test of arrays & slices with reflection. // Test of arrays & slices with reflection.
var a int import "reflect"
func arrayreflect1() { var a, b int
sl := make([]*int, 10) // @line ar1make
sl[0] = &a
srv := reflect.ValueOf(sl).Slice(0, 0) type S string
print(srv.Interface()) // @types []*int
print(srv.Interface().([]*int)) // @pointsto makeslice@ar1make:12 func reflectValueSlice() {
print(srv.Interface().([]*int)[42]) // @pointsto main.a // 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() { func reflectValueBytes() {
var arr [10]*int sl1 := make([]byte, 0) // @line ar5sl1
sl := arr[:] sl2 := make([]byte, 0) // @line ar5sl2
sl[0] = &a
srv := reflect.ValueOf(sl).Slice(0, 0) rvsl1 := reflect.ValueOf(sl1)
print(srv.Interface()) // @types []*int print(rvsl1.Interface()) // @types []byte
print(srv.Interface().([]*int)) // pointsto TODO print(rvsl1.Interface().([]byte)) // @pointsto makeslice@ar5sl1:13
print(srv.Interface().([]*int)[42]) // @pointsto main.a 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() { func reflectValueIndex() {
srv := reflect.ValueOf("hi").Slice(0, 0) slice := []*int{&a} // @line ar6slice
print(srv.Interface()) // @types string rv1 := reflect.ValueOf(slice)
print(rv1.Index(42).Interface()) // @types *int
print(rv1.Index(42).Interface().(*int)) // @pointsto main.a
type S string array := [10]*int{&a}
srv2 := reflect.ValueOf(S("hi")).Slice(0, 0) rv2 := reflect.ValueOf(array)
print(srv2.Interface()) // @types main.S 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() { func reflectValueElem() {
rv1 := reflect.ValueOf("hi") // TODO(adonovan): tests 'interface'. Needs indirect tagged objects.
rv2 := rv1 // backflow! // rv1 := reflect.ValueOf(...)
if unknown { // print(rv1.Elem().Interface()) // #@types *int
rv2 = reflect.ValueOf(123) // print(rv1.Elem().Interface().(*int)) // #@pointsto main.a
}
// We see backflow through the assignment above causing an // Pointer.
// imprecise result for rv1. This is because the SSA builder ptr := &a
// doesn't yet lift structs (like reflect.Value) into rv2 := reflect.ValueOf(&ptr)
// registers so these are all loads/stores to the stack. print(rv2.Elem().Interface()) // @types *int
// Under Das's algorithm, the extra indirection results in print(rv2.Elem().Interface().(*int)) // @pointsto main.a
// (undirected) unification not (directed) flow edges.
// TODO(adonovan): precision: lift aggregates. // No other type works with (rV).Elem, not even those that
print(rv1.Interface()) // @types string | int // work with (rT).Elem: slice, array, map, chan.
print(rv2.Interface()) // @types string | int
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() { func reflectTypeElem() {
sl1 := make([]byte, 0) rt1 := reflect.TypeOf(make([]*int, 0))
sl2 := make([]byte, 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 rt3 := reflect.TypeOf(map[*int]*int{})
print(srv.Interface().([]byte)) // @pointsto makeslice@testdata/arrayreflect.go:62:13 print(reflect.Zero(rt3.Elem())) // @types *int
print(srv.Bytes()) // @pointsto makeslice@testdata/arrayreflect.go:62:13
srv2 := reflect.ValueOf(123) rt4 := reflect.TypeOf(make(chan *int))
srv2.SetBytes(sl2) print(reflect.Zero(rt4.Elem())) // @types *int
print(srv2.Interface()) // @types []byte | int
print(srv2.Interface().([]byte)) // @pointsto makeslice@testdata/arrayreflect.go:63:13 ptr := &a
print(srv2.Bytes()) // @pointsto makeslice@testdata/arrayreflect.go:63:13 rt5 := reflect.TypeOf(&ptr)
print(reflect.Zero(rt5.Elem())) // @types *int
rt6 := reflect.TypeOf(3)
print(reflect.Zero(rt6.Elem())) // @types
} }
func arrayreflect6() { func reflectPtrTo() {
sl1 := []*bool{new(bool)} tInt := reflect.TypeOf(3)
sl2 := []*int{&a} tPtrInt := reflect.PtrTo(tInt)
print(reflect.Zero(tPtrInt)) // @types *int
tPtrPtrInt := reflect.PtrTo(tPtrInt)
print(reflect.Zero(tPtrPtrInt)) // @types **int
}
srv1 := reflect.ValueOf(sl1) func reflectSliceOf() {
print(srv1.Index(42).Interface()) // @types *bool tInt := reflect.TypeOf(3)
print(srv1.Index(42).Interface().(*bool)) // @pointsto alloc@testdata/arrayreflect.go:79:20 tSliceInt := reflect.SliceOf(tInt)
print(reflect.Zero(tSliceInt)) // @types []int
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 main() { func main() {
arrayreflect1() reflectValueSlice()
arrayreflect2() reflectValueBytes()
arrayreflect3() reflectValueIndex()
arrayreflect4() reflectValueElem()
arrayreflect5() reflectTypeElem()
arrayreflect6() reflectPtrTo()
reflectSliceOf()
} }
var unknown bool