mirror of
https://github.com/golang/go
synced 2024-11-18 20:04:52 -07:00
go.tools/pointer: implement (reflect.Value).Call.
The implementation follows the basic pattern of an indirect function call (genDynamicCall). We use the same trick as SetFinalizer so that direct calls to (r.V).Call, which are overwhelmingly the norm, are inlined. Bug fix (and simplification): calling untag() to unbox a reflect.Value is wrong for reflect.Values containing interfaces (rare). Now, we call untag for concrete types and typeFilter for interface types, and we can use this pattern in all cases. It corresponds to the ssa.TypeAssert operator, so we call it typeAssert. Added tests to cover this. We also specialize reflect.{In,Out} when the operand is an int literal. + Tests. Also: - make taggedValue() panic, not return nil, eliminating many checks. We call isTaggedValue for the one place that cares. - pointer_test: recover from panics in Analyze() and dump the log. R=crawshaw CC=golang-dev https://golang.org/cl/14426050
This commit is contained in:
parent
e1b710c31e
commit
94c387c610
@ -175,10 +175,10 @@ func (s ptset) DynamicTypes() *typemap.M {
|
||||
var tmap typemap.M
|
||||
tmap.SetHasher(s.a.hasher)
|
||||
for ifaceObjId := range s.pts {
|
||||
tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
|
||||
if tDyn == nil {
|
||||
if !s.a.isTaggedObject(ifaceObjId) {
|
||||
continue // !CanHaveDynamicTypes(tDyn)
|
||||
}
|
||||
tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
|
||||
if indirect {
|
||||
panic("indirect tagged object") // implement later
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ reflect.Value
|
||||
v3 := v2.FieldByName("X") // v3 points to an indirect int-tagged object, pointing to s.X
|
||||
v3.Set(y) // pts(s.X) ⊇ pts(y)
|
||||
|
||||
Whether indirect or not, the concrete type of the tagged value
|
||||
Whether indirect or not, the concrete type of the tagged object
|
||||
corresponds to the user-visible dynamic type, and the existence
|
||||
of a pointer is an implementation detail.
|
||||
|
||||
|
@ -182,7 +182,7 @@ func (a *analysis) makeRtype(T types.Type) nodeid {
|
||||
func (a *analysis) rtypeTaggedValue(obj nodeid) types.Type {
|
||||
tDyn, t, _ := a.taggedValue(obj)
|
||||
if tDyn != a.reflectRtypePtr {
|
||||
panic(fmt.Sprintf("not a *reflect.rtype-tagged value: obj=n%d tag=%v payload=n%d", obj, tDyn, t))
|
||||
panic(fmt.Sprintf("not a *reflect.rtype-tagged object: obj=n%d tag=%v payload=n%d", obj, tDyn, t))
|
||||
}
|
||||
return a.nodes[t].typ
|
||||
}
|
||||
@ -224,17 +224,22 @@ func (a *analysis) valueOffsetNode(v ssa.Value, index int) nodeid {
|
||||
return id + nodeid(a.offsetOf(v.Type(), index))
|
||||
}
|
||||
|
||||
// isTaggedObject reports whether object obj is a tagged object.
|
||||
func (a *analysis) isTaggedObject(obj nodeid) bool {
|
||||
return a.nodes[obj].obj.flags&otTagged != 0
|
||||
}
|
||||
|
||||
// taggedValue returns the dynamic type tag, the (first node of the)
|
||||
// payload, and the indirect flag of the tagged object starting at id.
|
||||
// It returns tDyn==nil if obj is not a tagged object.
|
||||
// Panic ensues if !isTaggedObject(id).
|
||||
//
|
||||
func (a *analysis) taggedValue(id nodeid) (tDyn types.Type, v nodeid, indirect bool) {
|
||||
n := a.nodes[id]
|
||||
func (a *analysis) taggedValue(obj nodeid) (tDyn types.Type, v nodeid, indirect bool) {
|
||||
n := a.nodes[obj]
|
||||
flags := n.obj.flags
|
||||
if flags&otTagged != 0 {
|
||||
return n.typ, id + 1, flags&otIndirect != 0
|
||||
if flags&otTagged == 0 {
|
||||
panic(fmt.Sprintf("not a tagged object: n%d", obj))
|
||||
}
|
||||
return
|
||||
return n.typ, obj + 1, flags&otIndirect != 0
|
||||
}
|
||||
|
||||
// funcParams returns the first node of the params block of the
|
||||
@ -351,14 +356,16 @@ func (a *analysis) offsetAddr(dst, src nodeid, offset uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// typeFilter creates a typeFilter constraint of the form dst = src.(I).
|
||||
func (a *analysis) typeFilter(I types.Type, dst, src nodeid) {
|
||||
a.addConstraint(&typeFilterConstraint{I, dst, src})
|
||||
}
|
||||
|
||||
// untag creates an untag constraint of the form dst = src.(C).
|
||||
func (a *analysis) untag(C types.Type, dst, src nodeid, exact bool) {
|
||||
a.addConstraint(&untagConstraint{C, dst, src, exact})
|
||||
// typeAssert creates a typeFilter or untag constraint of the form dst = src.(T):
|
||||
// typeFilter for an interface, untag for a concrete type.
|
||||
// The exact flag is specified as for untagConstraint.
|
||||
//
|
||||
func (a *analysis) typeAssert(T types.Type, dst, src nodeid, exact bool) {
|
||||
if isInterface(T) {
|
||||
a.addConstraint(&typeFilterConstraint{T, dst, src})
|
||||
} else {
|
||||
a.addConstraint(&untagConstraint{T, dst, src, exact})
|
||||
}
|
||||
}
|
||||
|
||||
// addConstraint adds c to the constraint set.
|
||||
@ -591,6 +598,15 @@ func (a *analysis) genStaticCall(caller *cgnode, site *callsite, call *ssa.CallC
|
||||
f: a.valueNode(call.Args[1]),
|
||||
})
|
||||
return
|
||||
|
||||
case a.reflectValueCall:
|
||||
// Inline (reflect.Value).Call so the call appears direct.
|
||||
dotdotdot := false
|
||||
ret := reflectCallImpl(a, caller, site, a.valueNode(call.Args[0]), a.valueNode(call.Args[1]), dotdotdot)
|
||||
if result != 0 {
|
||||
a.addressOf(result, ret)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Ascertain the context (contour/cgnode) for a particular call.
|
||||
@ -700,7 +716,7 @@ func (a *analysis) genInvokeReflectType(caller *cgnode, site *callsite, call *ss
|
||||
// Unpack receiver into rtype
|
||||
rtype := a.addOneNode(a.reflectRtypePtr, "rtype.recv", nil)
|
||||
recv := a.valueNode(call.Value)
|
||||
a.untag(a.reflectRtypePtr, rtype, recv, true)
|
||||
a.typeAssert(a.reflectRtypePtr, rtype, recv, true)
|
||||
|
||||
// Look up the concrete method.
|
||||
meth := a.reflectRtypePtr.MethodSet().Lookup(call.Method.Pkg(), call.Method.Name())
|
||||
@ -1007,12 +1023,7 @@ func (a *analysis) genInstr(cgn *cgnode, instr ssa.Instruction) {
|
||||
a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
|
||||
|
||||
case *ssa.TypeAssert:
|
||||
T := instr.AssertedType
|
||||
if _, ok := T.Underlying().(*types.Interface); ok {
|
||||
a.typeFilter(T, a.valueNode(instr), a.valueNode(instr.X))
|
||||
} else {
|
||||
a.untag(T, a.valueNode(instr), a.valueNode(instr.X), true)
|
||||
}
|
||||
a.typeAssert(instr.AssertedType, a.valueNode(instr), a.valueNode(instr.X), true)
|
||||
|
||||
case *ssa.Slice:
|
||||
a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
|
||||
|
@ -339,9 +339,6 @@ func (c *runtimeSetFinalizerConstraint) ptr() nodeid {
|
||||
func (c *runtimeSetFinalizerConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
for fObj := range delta {
|
||||
tDyn, f, indirect := a.taggedValue(fObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged object")
|
||||
}
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
// when we start creating indirect tagged objects.
|
||||
@ -362,7 +359,7 @@ func (c *runtimeSetFinalizerConstraint) solve(a *analysis, _ *node, delta nodese
|
||||
// Extract x to tmp.
|
||||
tx := tSig.Params().At(0).Type()
|
||||
tmp := a.addNodes(tx, "SetFinalizer.tmp")
|
||||
a.untag(tx, tmp, c.x, false)
|
||||
a.typeAssert(tx, tmp, c.x, false)
|
||||
|
||||
// Call f(tmp).
|
||||
a.store(f, tmp, 1, a.sizeof(tx))
|
||||
|
@ -289,7 +289,6 @@ func doOneInput(input, filename string) bool {
|
||||
probes = append(probes, probe{site, p})
|
||||
},
|
||||
}
|
||||
result := pointer.Analyze(config)
|
||||
|
||||
// Print the log is there was an error or a panic.
|
||||
complete := false
|
||||
@ -299,6 +298,8 @@ func doOneInput(input, filename string) bool {
|
||||
}
|
||||
}()
|
||||
|
||||
result := pointer.Analyze(config)
|
||||
|
||||
// Check the expectations.
|
||||
for _, e := range exps {
|
||||
var pr *probe
|
||||
|
@ -82,9 +82,146 @@ 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 (Value).Call(in []Value) []Value ----------
|
||||
|
||||
// result = v.Call(in)
|
||||
type rVCallConstraint struct {
|
||||
cgn *cgnode
|
||||
targets nodeid
|
||||
v nodeid // (ptr)
|
||||
arg nodeid // = in[*]
|
||||
result nodeid
|
||||
dotdotdot bool // interpret last arg as a "..." slice
|
||||
}
|
||||
|
||||
func (c *rVCallConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = reflect n%d.Call(n%d)", c.result, c.v, c.arg)
|
||||
}
|
||||
|
||||
func (c *rVCallConstraint) ptr() nodeid {
|
||||
return c.v
|
||||
}
|
||||
|
||||
func (c *rVCallConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
if c.targets == 0 {
|
||||
panic("no targets")
|
||||
}
|
||||
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
tDyn, fn, 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")
|
||||
}
|
||||
|
||||
tSig, ok := tDyn.Underlying().(*types.Signature)
|
||||
if !ok {
|
||||
continue // not a function
|
||||
}
|
||||
if tSig.Recv() != nil {
|
||||
panic(tSig) // TODO(adonovan): rethink when we implement Method()
|
||||
}
|
||||
|
||||
// Add dynamic call target.
|
||||
if a.onlineCopy(c.targets, fn) {
|
||||
a.addWork(c.targets)
|
||||
// TODO(adonovan): is 'else continue' a sound optimisation here?
|
||||
}
|
||||
|
||||
// Allocate a P/R block.
|
||||
tParams := tSig.Params()
|
||||
tResults := tSig.Results()
|
||||
params := a.addNodes(tParams, "rVCall.params")
|
||||
results := a.addNodes(tResults, "rVCall.results")
|
||||
|
||||
// Make a dynamic call to 'fn'.
|
||||
a.store(fn, params, 1, a.sizeof(tParams))
|
||||
a.load(results, fn, 1+a.sizeof(tParams), a.sizeof(tResults))
|
||||
|
||||
// Populate P by type-asserting each actual arg (all merged in c.arg).
|
||||
for i, n := 0, tParams.Len(); i < n; i++ {
|
||||
T := tParams.At(i).Type()
|
||||
a.typeAssert(T, params, c.arg, false)
|
||||
params += nodeid(a.sizeof(T))
|
||||
}
|
||||
|
||||
// Use R by tagging and copying each actual result to c.result.
|
||||
for i, n := 0, tResults.Len(); i < n; i++ {
|
||||
T := tResults.At(i).Type()
|
||||
// Convert from an arbitrary type to a reflect.Value
|
||||
// (like MakeInterface followed by reflect.ValueOf).
|
||||
if isInterface(T) {
|
||||
// (don't tag)
|
||||
if a.onlineCopy(c.result, results) {
|
||||
changed = true
|
||||
}
|
||||
} else {
|
||||
obj := a.makeTagged(T, c.cgn, nil)
|
||||
a.onlineCopyN(obj+1, results, a.sizeof(T))
|
||||
if a.addLabel(c.result, obj) { // (true)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
results += nodeid(a.sizeof(T))
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
a.addWork(c.result)
|
||||
}
|
||||
}
|
||||
|
||||
// Common code for direct (inlined) and indirect calls to (reflect.Value).Call.
|
||||
func reflectCallImpl(a *analysis, cgn *cgnode, site *callsite, recv, arg nodeid, dotdotdot bool) nodeid {
|
||||
// Allocate []reflect.Value array for the result.
|
||||
ret := a.nextNode()
|
||||
a.addNodes(types.NewArray(a.reflectValueObj.Type(), 1), "rVCall.ret")
|
||||
a.endObject(ret, cgn, nil)
|
||||
|
||||
// pts(targets) will be the set of possible call targets.
|
||||
site.targets = a.addOneNode(tInvalid, "rvCall.targets", nil)
|
||||
|
||||
// All arguments are merged since they arrive in a slice.
|
||||
argelts := a.addOneNode(a.reflectValueObj.Type(), "rVCall.args", nil)
|
||||
a.load(argelts, arg, 1, 1) // slice elements
|
||||
|
||||
a.addConstraint(&rVCallConstraint{
|
||||
cgn: cgn,
|
||||
targets: site.targets,
|
||||
v: recv,
|
||||
arg: argelts,
|
||||
result: ret + 1, // results go into elements of ret
|
||||
dotdotdot: dotdotdot,
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
func reflectCall(a *analysis, cgn *cgnode, dotdotdot bool) {
|
||||
// This is the shared contour implementation of (reflect.Value).Call
|
||||
// and CallSlice, as used by indirect calls (rare).
|
||||
// Direct calls are inlined in gen.go, eliding the
|
||||
// intermediate cgnode for Call.
|
||||
site := new(callsite)
|
||||
cgn.sites = append(cgn.sites, site)
|
||||
recv := a.funcParams(cgn.obj)
|
||||
arg := recv + 1
|
||||
ret := reflectCallImpl(a, cgn, site, recv, arg, dotdotdot)
|
||||
a.addressOf(a.funcResults(cgn.obj), ret)
|
||||
}
|
||||
|
||||
func ext۰reflect۰Value۰Call(a *analysis, cgn *cgnode) {
|
||||
reflectCall(a, cgn, false)
|
||||
}
|
||||
|
||||
func ext۰reflect۰Value۰CallSlice(a *analysis, cgn *cgnode) {
|
||||
// TODO(adonovan): implement. Also, inline direct calls in gen.go too.
|
||||
if false {
|
||||
reflectCall(a, cgn, true)
|
||||
}
|
||||
}
|
||||
|
||||
func ext۰reflect۰Value۰Convert(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func (Value).Elem() Value ----------
|
||||
|
||||
@ -225,17 +362,13 @@ func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
tDyn, payload, indirect := a.taggedValue(vObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged object")
|
||||
}
|
||||
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
// when we start creating indirect tagged objects.
|
||||
panic("indirect tagged object")
|
||||
}
|
||||
|
||||
if _, ok := tDyn.Underlying().(*types.Interface); ok {
|
||||
if isInterface(tDyn) {
|
||||
if a.onlineCopy(c.result, payload) {
|
||||
a.addWork(c.result)
|
||||
}
|
||||
@ -450,7 +583,7 @@ func (c *rVSendConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
// Extract x's payload to xtmp, then store to channel.
|
||||
tElem := tChan.Elem()
|
||||
xtmp := a.addNodes(tElem, "Send.xtmp")
|
||||
a.untag(tElem, xtmp, c.x, false)
|
||||
a.typeAssert(tElem, xtmp, c.x, false)
|
||||
a.store(ch, xtmp, 0, a.sizeof(tElem))
|
||||
}
|
||||
}
|
||||
@ -545,12 +678,12 @@ func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
|
||||
// Extract key's payload to keytmp, then store to map key.
|
||||
keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp")
|
||||
a.untag(tMap.Key(), keytmp, c.key, false)
|
||||
a.typeAssert(tMap.Key(), keytmp, c.key, false)
|
||||
a.store(m, keytmp, 0, keysize)
|
||||
|
||||
// Extract val's payload to vtmp, then store to map value.
|
||||
valtmp := a.addNodes(tMap.Elem(), "SetMapIndex.valtmp")
|
||||
a.untag(tMap.Elem(), valtmp, c.val, false)
|
||||
a.typeAssert(tMap.Elem(), valtmp, c.val, false)
|
||||
a.store(m, valtmp, keysize, a.sizeof(tMap.Elem()))
|
||||
}
|
||||
}
|
||||
@ -727,10 +860,6 @@ func (c *reflectIndirectConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for vObj := range delta {
|
||||
tDyn, _, _ := a.taggedValue(vObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged value")
|
||||
}
|
||||
|
||||
var res nodeid
|
||||
if tPtr, ok := tDyn.Underlying().(*types.Pointer); ok {
|
||||
// load the payload of the pointer's tagged object
|
||||
@ -1077,10 +1206,6 @@ func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
changed := false
|
||||
for iObj := range delta {
|
||||
tDyn, _, _ := a.taggedValue(iObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged value")
|
||||
}
|
||||
|
||||
if a.addLabel(c.result, a.makeRtype(tDyn)) {
|
||||
changed = true
|
||||
}
|
||||
@ -1132,7 +1257,7 @@ func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
// TODO(adonovan): if T is an interface type, we need
|
||||
// to create an indirect tagged object containing
|
||||
// new(T). To avoid updates of such shared values,
|
||||
// we'll need another flag on indirect tagged values
|
||||
// we'll need another flag on indirect tagged objects
|
||||
// that marks whether they are addressable or
|
||||
// readonly, just like the reflect package does.
|
||||
|
||||
@ -1293,18 +1418,19 @@ func ext۰reflect۰rtype۰Field(a *analysis, cgn *cgnode) {
|
||||
func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode) {}
|
||||
func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {}
|
||||
|
||||
// ---------- func (*rtype) In/Out() Type ----------
|
||||
// ---------- func (*rtype) In/Out(i int) Type ----------
|
||||
|
||||
// result = In/Out(t)
|
||||
// result = In/Out(t, i)
|
||||
type rtypeInOutConstraint struct {
|
||||
cgn *cgnode
|
||||
t nodeid // (ptr)
|
||||
result nodeid
|
||||
out bool
|
||||
i int // -ve if not a constant
|
||||
}
|
||||
|
||||
func (c *rtypeInOutConstraint) String() string {
|
||||
return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d)", c.result, c.t)
|
||||
return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d, %d)", c.result, c.t, c.i)
|
||||
}
|
||||
|
||||
func (c *rtypeInOutConstraint) ptr() nodeid {
|
||||
@ -1324,15 +1450,11 @@ func (c *rtypeInOutConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
if c.out {
|
||||
tuple = sig.Results()
|
||||
}
|
||||
// TODO(adonovan): when a function is analyzed
|
||||
// context-sensitively, we should be able to see its
|
||||
// caller's actual parameter's ssa.Values. Refactor
|
||||
// the intrinsic mechanism to allow this. Then if the
|
||||
// value is an int const K, skip the loop and use
|
||||
// tuple.At(K).
|
||||
for i, n := 0, tuple.Len(); i < n; i++ {
|
||||
if a.addLabel(c.result, a.makeRtype(tuple.At(i).Type())) {
|
||||
changed = true
|
||||
if c.i < 0 || c.i == i {
|
||||
if a.addLabel(c.result, a.makeRtype(tuple.At(i).Type())) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1342,11 +1464,22 @@ func (c *rtypeInOutConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||
}
|
||||
|
||||
func ext۰reflect۰rtype۰InOut(a *analysis, cgn *cgnode, out bool) {
|
||||
// If we have access to the callsite,
|
||||
// and the argument is an int constant,
|
||||
// return only that parameter.
|
||||
index := -1
|
||||
if site := cgn.callersite; site != nil {
|
||||
if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
|
||||
v, _ := exact.Int64Val(c.Value)
|
||||
index = int(v)
|
||||
}
|
||||
}
|
||||
a.addConstraint(&rtypeInOutConstraint{
|
||||
cgn: cgn,
|
||||
t: a.funcParams(cgn.obj),
|
||||
result: a.funcResults(cgn.obj),
|
||||
out: out,
|
||||
i: index,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,10 @@ func (a *analysis) solve() {
|
||||
}
|
||||
}
|
||||
|
||||
if len(a.nodes[0].pts) > 0 {
|
||||
panic(fmt.Sprintf("pts(0) is nonempty: %s", a.nodes[0].pts))
|
||||
}
|
||||
|
||||
if a.log != nil {
|
||||
fmt.Fprintf(a.log, "Solver done\n")
|
||||
}
|
||||
@ -258,9 +262,6 @@ func (c *offsetAddrConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
for ifaceObj := range delta {
|
||||
tDyn, _, indirect := a.taggedValue(ifaceObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged value")
|
||||
}
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
// when we start creating indirect tagged objects.
|
||||
@ -282,9 +283,6 @@ func (c *untagConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
}
|
||||
for ifaceObj := range delta {
|
||||
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged value")
|
||||
}
|
||||
if indirect {
|
||||
// TODO(adonovan): we'll need to implement this
|
||||
// when we start creating indirect tagged objects.
|
||||
@ -306,9 +304,6 @@ func (c *untagConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
func (c *invokeConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||
for ifaceObj := range delta {
|
||||
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
||||
if tDyn == nil {
|
||||
panic("not a tagged value")
|
||||
}
|
||||
if indirect {
|
||||
// TODO(adonovan): we may need to implement this if
|
||||
// we ever apply invokeConstraints to reflect.Value PTSs,
|
||||
|
11
pointer/testdata/chanreflect.go
vendored
11
pointer/testdata/chanreflect.go
vendored
@ -17,6 +17,16 @@ func chanreflect1() {
|
||||
print(<-ch) // @pointsto main.a
|
||||
}
|
||||
|
||||
func chanreflect1i() {
|
||||
// Exercises reflect.Value conversions to/from interfaces:
|
||||
// a different code path than for concrete types.
|
||||
ch := make(chan interface{}, 0)
|
||||
reflect.ValueOf(ch).Send(reflect.ValueOf(&a))
|
||||
v := <-ch
|
||||
print(v) // @types *int
|
||||
print(v.(*int)) // @pointsto main.a
|
||||
}
|
||||
|
||||
func chanreflect2() {
|
||||
ch := make(chan *int, 0)
|
||||
ch <- &b
|
||||
@ -66,6 +76,7 @@ func chanOfUnknown() {
|
||||
|
||||
func main() {
|
||||
chanreflect1()
|
||||
chanreflect1i()
|
||||
chanreflect2()
|
||||
chanOfRecv()
|
||||
chanOfSend()
|
||||
|
77
pointer/testdata/funcreflect.go
vendored
77
pointer/testdata/funcreflect.go
vendored
@ -5,39 +5,73 @@ package main
|
||||
import "reflect"
|
||||
|
||||
var zero, a, b int
|
||||
var false2 bool
|
||||
|
||||
// func f(p *int) *int {
|
||||
// print(p) // #@pointsto
|
||||
// return &b
|
||||
// }
|
||||
func f(p *int, q hasF) *int {
|
||||
print(p) // @pointsto main.a
|
||||
print(q) // @types *T
|
||||
print(q.(*T)) // @pointsto new@newT1:22
|
||||
return &b
|
||||
}
|
||||
|
||||
// func g(p *bool) {
|
||||
// }
|
||||
func g(p *bool) (*int, *bool, hasF) {
|
||||
return &b, p, new(T) // @line newT2
|
||||
}
|
||||
|
||||
// func reflectValueCall() {
|
||||
// rvf := reflect.ValueOf(f)
|
||||
// res := rvf.Call([]reflect.Value{reflect.ValueOf(&a)})
|
||||
// print(res[0].Interface()) // #@types
|
||||
// print(res[0].Interface().(*int)) // #@pointsto
|
||||
// }
|
||||
func reflectValueCall() {
|
||||
rvf := reflect.ValueOf(f)
|
||||
res := rvf.Call([]reflect.Value{
|
||||
// argument order is not significant:
|
||||
reflect.ValueOf(new(T)), // @line newT1
|
||||
reflect.ValueOf(&a),
|
||||
})
|
||||
print(res[0].Interface()) // @types *int
|
||||
print(res[0].Interface().(*int)) // @pointsto main.b
|
||||
}
|
||||
|
||||
// #@calls main.reflectValueCall -> main.f
|
||||
// @calls main.reflectValueCall -> main.f
|
||||
|
||||
func reflectValueCallIndirect() {
|
||||
rvf := reflect.ValueOf(g)
|
||||
call := rvf.Call // kids, don't try this at home
|
||||
|
||||
// Indirect call uses shared contour.
|
||||
//
|
||||
// Also notice that argument position doesn't matter, and args
|
||||
// of inappropriate type (e.g. 'a') are ignored.
|
||||
res := call([]reflect.Value{
|
||||
reflect.ValueOf(&a),
|
||||
reflect.ValueOf(&false2),
|
||||
})
|
||||
res0 := res[0].Interface()
|
||||
print(res0) // @types *int | *bool | *T
|
||||
print(res0.(*int)) // @pointsto main.b
|
||||
print(res0.(*bool)) // @pointsto main.false2
|
||||
print(res0.(hasF)) // @types *T
|
||||
print(res0.(*T)) // @pointsto new@newT2:19
|
||||
}
|
||||
|
||||
// @calls main.reflectValueCallIndirect -> bound$(reflect.Value).Call
|
||||
// @calls bound$(reflect.Value).Call -> main.g
|
||||
|
||||
func reflectTypeInOut() {
|
||||
var f func(float64, bool) (string, int)
|
||||
// TODO(adonovan): when the In/Out argument is a valid index constant,
|
||||
// only include a single type in the result. Needs some work.
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(0)).Interface()) // @types float64 | bool
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(1)).Interface()) // @types float64 | bool
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(0)).Interface()) // @types float64
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(1)).Interface()) // @types bool
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(-1)).Interface()) // @types float64 | bool
|
||||
print(reflect.Zero(reflect.TypeOf(f).In(zero)).Interface()) // @types float64 | bool
|
||||
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(0)).Interface()) // @types string | int
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(1)).Interface()) // @types string | int
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(2)).Interface()) // @types string | int
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(0)).Interface()) // @types string
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(1)).Interface()) // @types int
|
||||
print(reflect.Zero(reflect.TypeOf(f).Out(2)).Interface()) // @types
|
||||
|
||||
print(reflect.Zero(reflect.TypeOf(3).Out(0)).Interface()) // @types
|
||||
}
|
||||
|
||||
type hasF interface {
|
||||
F()
|
||||
}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (T) F() {}
|
||||
@ -76,7 +110,8 @@ func reflectTypeMethod() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
//reflectValueCall()
|
||||
reflectValueCall()
|
||||
reflectValueCallIndirect()
|
||||
reflectTypeInOut()
|
||||
reflectTypeMethodByName()
|
||||
reflectTypeMethod()
|
||||
|
14
pointer/testdata/mapreflect.go
vendored
14
pointer/testdata/mapreflect.go
vendored
@ -64,6 +64,19 @@ func reflectSetMapIndex() {
|
||||
print(reflect.Zero(tmap.Elem()).Interface()) // @types *bool
|
||||
}
|
||||
|
||||
func reflectSetMapIndexInterface() {
|
||||
// Exercises reflect.Value conversions to/from interfaces:
|
||||
// a different code path than for concrete types.
|
||||
m := make(map[interface{}]interface{})
|
||||
reflect.ValueOf(m).SetMapIndex(reflect.ValueOf(&a), reflect.ValueOf(&b))
|
||||
for k, v := range m {
|
||||
print(k) // @types *int
|
||||
print(k.(*int)) // @pointsto main.a
|
||||
print(v) // @types *bool
|
||||
print(v.(*bool)) // @pointsto main.b
|
||||
}
|
||||
}
|
||||
|
||||
func reflectSetMapIndexAssignable() {
|
||||
// SetMapIndex performs implicit assignability conversions.
|
||||
type I *int
|
||||
@ -97,6 +110,7 @@ func reflectMakeMap() {
|
||||
func main() {
|
||||
reflectMapKeysIndex()
|
||||
reflectSetMapIndex()
|
||||
reflectSetMapIndexInterface()
|
||||
reflectSetMapIndexAssignable()
|
||||
reflectMakeMap()
|
||||
// TODO(adonovan): reflect.MapOf(Type)
|
||||
|
31
pointer/testdata/reflect.go
vendored
31
pointer/testdata/reflect.go
vendored
@ -48,9 +48,40 @@ func reflectTypeElem() {
|
||||
print(reflect.Zero(reflect.TypeOf(new(interface{})).Elem()).Interface()) // @types
|
||||
}
|
||||
|
||||
// reflect.Values within reflect.Values.
|
||||
func metareflection() {
|
||||
// "box" a *int twice, unbox it twice.
|
||||
v0 := reflect.ValueOf(&a)
|
||||
print(v0) // @types *int
|
||||
v1 := reflect.ValueOf(v0) // box
|
||||
print(v1) // @types reflect.Value
|
||||
v2 := reflect.ValueOf(v1) // box
|
||||
print(v2) // @types reflect.Value
|
||||
v1a := v2.Interface().(reflect.Value) // unbox
|
||||
print(v1a) // @types reflect.Value
|
||||
v0a := v1a.Interface().(reflect.Value) // unbox
|
||||
print(v0a) // @types *int
|
||||
print(v0a.Interface().(*int)) // @pointsto main.a
|
||||
|
||||
// "box" an interface{} lvalue twice, unbox it twice.
|
||||
var iface interface{} = 3
|
||||
x0 := reflect.ValueOf(&iface).Elem()
|
||||
print(x0) // @types interface{}
|
||||
x1 := reflect.ValueOf(x0) // box
|
||||
print(x1) // @types reflect.Value
|
||||
x2 := reflect.ValueOf(x1) // box
|
||||
print(x2) // @types reflect.Value
|
||||
x1a := x2.Interface().(reflect.Value) // unbox
|
||||
print(x1a) // @types reflect.Value
|
||||
x0a := x1a.Interface().(reflect.Value) // unbox
|
||||
print(x0a) // @types interface{}
|
||||
print(x0a.Interface()) // @types int
|
||||
}
|
||||
|
||||
func main() {
|
||||
reflectIndirect()
|
||||
reflectNewAt()
|
||||
reflectTypeOf()
|
||||
reflectTypeElem()
|
||||
metareflection()
|
||||
}
|
||||
|
@ -44,6 +44,12 @@ func CanHaveDynamicTypes(T types.Type) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// isInterface reports whether T is an interface type.
|
||||
func isInterface(T types.Type) bool {
|
||||
_, ok := T.Underlying().(*types.Interface)
|
||||
return ok
|
||||
}
|
||||
|
||||
// mustDeref returns the element type of its argument, which must be a
|
||||
// pointer; panic ensues otherwise.
|
||||
func mustDeref(typ types.Type) types.Type {
|
||||
@ -113,7 +119,7 @@ func (a *analysis) flatten(t types.Type) []*fieldInfo {
|
||||
switch t := t.(type) {
|
||||
case *types.Named:
|
||||
u := t.Underlying()
|
||||
if _, ok := u.(*types.Interface); ok {
|
||||
if isInterface(u) {
|
||||
// Debuggability hack: don't remove
|
||||
// the named type from interfaces as
|
||||
// they're very verbose.
|
||||
|
Loading…
Reference in New Issue
Block a user