go.tools/pointer: reflect, part 2: channels.
(reflect.Value).Send
(reflect.Value).TrySend
(reflect.Value).Recv
(reflect.Value).TryRecv
(reflect.Type).ChanOf
(reflect.Type).In
(reflect.Type).Out
reflect.Indirect
reflect.MakeChan
Also:
- specialize genInvoke when the receiver is a reflect.Type under the
assumption that there's only one possible concrete type. This
makes all reflect.Type operations context-sensitive since the calls
are no longer dynamic.
- Rename all variables to match the actual parameter names used in
the reflect API.
- Add pointer.Config.Reflection flag
(exposed in oracle as --reflect, default false) to enable reflection.
It currently adds about 20% running time. I'll make it true after
the presolver is implemented.
- Simplified worklist datatype and solver main loop slightly
(~10% speed improvement).
- Use addLabel() utility to add a label to a PTS.
(Working on my 3 yr old 2x2GHz+4GB Mac vs 8x4GHz+24GB workstation,
one really notices the cost of pointer analysis.
Note to self: time to implement presolver.)
R=crawshaw
CC=golang-dev
https://golang.org/cl/13242062
diff --git a/pointer/gen.go b/pointer/gen.go
index 0c448fa..62fb103 100644
--- a/pointer/gen.go
+++ b/pointer/gen.go
@@ -235,7 +235,7 @@
a.addOneNode(T, "reflect.rtype", nil)
a.endObject(obj, nil, nil).rtype = T
- id := a.makeTagged(a.reflectRtype, nil, nil)
+ id := a.makeTagged(a.reflectRtypePtr, nil, nil)
a.nodes[id].obj.rtype = T
a.nodes[id+1].typ = T // trick (each *rtype tagged object is a singleton)
a.addressOf(id+1, obj)
@@ -244,6 +244,15 @@
return id
}
+// rtypeValue returns the type of the *reflect.rtype-tagged object obj.
+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))
+ }
+ return a.nodes[t].typ
+}
+
// valueNode returns the id of the value node for v, creating it (and
// the association) as needed. It may return zero for uninteresting
// values containing no pointers.
@@ -432,6 +441,11 @@
}
}
+// typeAssert creates a typeAssert constraint of the form dst = src.(T).
+func (a *analysis) typeAssert(T types.Type, dst, src nodeid) {
+ a.addConstraint(&typeAssertConstraint{T, dst, src})
+}
+
// addConstraint adds c to the constraint set.
func (a *analysis) addConstraint(c constraint) {
a.constraints = append(a.constraints, c)
@@ -720,13 +734,11 @@
// It returns a node whose pts() will be the set of possible call targets.
//
func (a *analysis) genInvoke(call *ssa.CallCommon, result nodeid) nodeid {
- sig := call.Signature()
+ if call.Value.Type() == a.reflectType {
+ return a.genInvokeReflectType(call, result)
+ }
- // TODO(adonovan): optimise this into a static call when there
- // can be at most one type that implements the interface (due
- // to unexported methods). This is particularly important for
- // methods of interface reflect.Type (sole impl:
- // *reflect.rtype), so we can realize context sensitivity.
+ sig := call.Signature()
// Allocate a contiguous targets/params/results block for this call.
block := a.nextNode()
@@ -753,6 +765,63 @@
return targets
}
+// genInvokeReflectType is a specialization of genInvoke where the
+// receiver type is a reflect.Type, under the assumption that there
+// can be at most one implementation of this interface, *reflect.rtype.
+//
+// (Though this may appear to be an instance of a pattern---method
+// calls on interfaces known to have exactly one implementation---in
+// practice it occurs rarely, so we special case for reflect.Type.)
+//
+// In effect we treat this:
+// var rt reflect.Type = ...
+// rt.F()
+// as this:
+// rt.(*reflect.rtype).F()
+//
+// It returns a node whose pts() will be the (singleton) set of
+// possible call targets.
+//
+func (a *analysis) genInvokeReflectType(call *ssa.CallCommon, result nodeid) nodeid {
+ // Unpack receiver into rtype
+ rtype := a.addOneNode(a.reflectRtypePtr, "rtype.recv", nil)
+ recv := a.valueNode(call.Value)
+ a.typeAssert(a.reflectRtypePtr, rtype, recv)
+
+ // Look up the concrete method.
+ meth := a.reflectRtypePtr.MethodSet().Lookup(call.Method.Pkg(), call.Method.Name())
+ fn := a.prog.Method(meth)
+
+ obj := a.makeFunctionObject(fn) // new contour for this call
+
+ // From now on, it's essentially a static call, but little is
+ // gained by factoring together the code for both cases.
+
+ sig := fn.Signature // concrete method
+ targets := a.addOneNode(sig, "call.targets", nil)
+ a.addressOf(targets, obj) // (a singleton)
+
+ // Copy receiver.
+ params := a.funcParams(obj)
+ a.copy(params, rtype, 1)
+ params++
+
+ // Copy actual parameters into formal params block.
+ // Must loop, since the actuals aren't contiguous.
+ for i, arg := range call.Args {
+ sz := a.sizeof(sig.Params().At(i).Type())
+ a.copy(params, a.valueNode(arg), sz)
+ params += nodeid(sz)
+ }
+
+ // Copy formal results block to actual result.
+ if result != 0 {
+ a.copy(result, a.funcResults(obj), a.sizeof(sig.Results()))
+ }
+
+ return obj
+}
+
// genCall generates contraints for call instruction instr.
func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) {
call := instr.Common()
@@ -927,8 +996,7 @@
a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
case *ssa.TypeAssert:
- dst, src := a.valueNode(instr), a.valueNode(instr.X)
- a.addConstraint(&typeAssertConstraint{instr.AssertedType, dst, src})
+ a.typeAssert(instr.AssertedType, a.valueNode(instr), a.valueNode(instr.X))
case *ssa.Slice:
a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
@@ -1117,7 +1185,9 @@
a.panicNode = a.addNodes(tEface, "panic")
// Create nodes and constraints for all methods of reflect.rtype.
- if rtype := a.reflectRtype; rtype != nil {
+ // (Shared contours are used by dynamic calls to reflect.Type
+ // methods---typically just String().)
+ if rtype := a.reflectRtypePtr; rtype != nil {
mset := rtype.MethodSet()
for i, n := 0, mset.Len(); i < n; i++ {
a.valueNode(a.prog.Method(mset.At(i)))
@@ -1135,9 +1205,5 @@
a.genFunc(cgn)
}
- // Create a dummy node to avoid out-of-range indexing in case
- // the last allocated type was of zero length.
- a.addNodes(tInvalid, "(max)")
-
return root
}