aboutsummaryrefslogtreecommitdiff
path: root/go/pointer/gen.go
diff options
context:
space:
mode:
Diffstat (limited to 'go/pointer/gen.go')
-rw-r--r--go/pointer/gen.go57
1 files changed, 30 insertions, 27 deletions
diff --git a/go/pointer/gen.go b/go/pointer/gen.go
index ef5108a5b..5e527f21a 100644
--- a/go/pointer/gen.go
+++ b/go/pointer/gen.go
@@ -14,9 +14,11 @@ import (
"fmt"
"go/token"
"go/types"
+ "strings"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
+ "golang.org/x/tools/internal/typeparams"
)
var (
@@ -37,7 +39,6 @@ func (a *analysis) nextNode() nodeid {
// analytically uninteresting.
//
// comment explains the origin of the nodes, as a debugging aid.
-//
func (a *analysis) addNodes(typ types.Type, comment string) nodeid {
id := a.nextNode()
for _, fi := range a.flatten(typ) {
@@ -56,7 +57,6 @@ func (a *analysis) addNodes(typ types.Type, comment string) nodeid {
//
// comment explains the origin of the nodes, as a debugging aid.
// subelement indicates the subelement, e.g. ".a.b[*].c".
-//
func (a *analysis) addOneNode(typ types.Type, comment string, subelement *fieldInfo) nodeid {
id := a.nextNode()
a.nodes = append(a.nodes, &node{typ: typ, subelement: subelement, solve: new(solverState)})
@@ -69,7 +69,6 @@ func (a *analysis) addOneNode(typ types.Type, comment string, subelement *fieldI
// setValueNode associates node id with the value v.
// cgn identifies the context iff v is a local variable.
-//
func (a *analysis) setValueNode(v ssa.Value, id nodeid, cgn *cgnode) {
if cgn != nil {
a.localval[v] = id
@@ -125,7 +124,6 @@ func (a *analysis) setValueNode(v ssa.Value, id nodeid, cgn *cgnode) {
//
// obj is the start node of the object, from a prior call to nextNode.
// Its size, flags and optional data will be updated.
-//
func (a *analysis) endObject(obj nodeid, cgn *cgnode, data interface{}) *object {
// Ensure object is non-empty by padding;
// the pad will be the object node.
@@ -150,7 +148,6 @@ func (a *analysis) endObject(obj nodeid, cgn *cgnode, data interface{}) *object
//
// For a context-sensitive contour, callersite identifies the sole
// callsite; for shared contours, caller is nil.
-//
func (a *analysis) makeFunctionObject(fn *ssa.Function, callersite *callsite) nodeid {
if a.log != nil {
fmt.Fprintf(a.log, "\t---- makeFunctionObject %s\n", fn)
@@ -190,7 +187,6 @@ func (a *analysis) makeTagged(typ types.Type, cgn *cgnode, data interface{}) nod
// payload points to the sole rtype object for T.
//
// TODO(adonovan): move to reflect.go; it's part of the solver really.
-//
func (a *analysis) makeRtype(T types.Type) nodeid {
if v := a.rtypes.At(T); v != nil {
return v.(nodeid)
@@ -210,7 +206,7 @@ func (a *analysis) makeRtype(T types.Type) nodeid {
return id
}
-// rtypeValue returns the type of the *reflect.rtype-tagged object obj.
+// rtypeTaggedValue 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 {
@@ -222,7 +218,6 @@ func (a *analysis) rtypeTaggedValue(obj nodeid) types.Type {
// 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.
-//
func (a *analysis) valueNode(v ssa.Value) nodeid {
// Value nodes for locals are created en masse by genFunc.
if id, ok := a.localval[v]; ok {
@@ -247,7 +242,6 @@ func (a *analysis) valueNode(v ssa.Value) nodeid {
// valueOffsetNode ascertains the node for tuple/struct value v,
// then returns the node for its subfield #index.
-//
func (a *analysis) valueOffsetNode(v ssa.Value, index int) nodeid {
id := a.valueNode(v)
if id == 0 {
@@ -264,7 +258,6 @@ func (a *analysis) isTaggedObject(obj nodeid) bool {
// taggedValue returns the dynamic type tag, the (first node of the)
// payload, and the indirect flag of the tagged object starting at id.
// Panic ensues if !isTaggedObject(id).
-//
func (a *analysis) taggedValue(obj nodeid) (tDyn types.Type, v nodeid, indirect bool) {
n := a.nodes[obj]
flags := n.obj.flags
@@ -276,7 +269,6 @@ func (a *analysis) taggedValue(obj nodeid) (tDyn types.Type, v nodeid, indirect
// funcParams returns the first node of the params (P) block of the
// function whose object node (obj.flags&otFunction) is id.
-//
func (a *analysis) funcParams(id nodeid) nodeid {
n := a.nodes[id]
if n.obj == nil || n.obj.flags&otFunction == 0 {
@@ -287,7 +279,6 @@ func (a *analysis) funcParams(id nodeid) nodeid {
// funcResults returns the first node of the results (R) block of the
// function whose object node (obj.flags&otFunction) is id.
-//
func (a *analysis) funcResults(id nodeid) nodeid {
n := a.nodes[id]
if n.obj == nil || n.obj.flags&otFunction == 0 {
@@ -305,7 +296,6 @@ func (a *analysis) funcResults(id nodeid) nodeid {
// copy creates a constraint of the form dst = src.
// sizeof is the width (in logical fields) of the copied type.
-//
func (a *analysis) copy(dst, src nodeid, sizeof uint32) {
if src == dst || sizeof == 0 {
return // trivial
@@ -337,7 +327,6 @@ func (a *analysis) addressOf(T types.Type, id, obj nodeid) {
// load creates a load constraint of the form dst = src[offset].
// offset is the pointer offset in logical fields.
// sizeof is the width (in logical fields) of the loaded type.
-//
func (a *analysis) load(dst, src nodeid, offset, sizeof uint32) {
if dst == 0 {
return // load of non-pointerlike value
@@ -358,7 +347,6 @@ func (a *analysis) load(dst, src nodeid, offset, sizeof uint32) {
// store creates a store constraint of the form dst[offset] = src.
// offset is the pointer offset in logical fields.
// sizeof is the width (in logical fields) of the stored type.
-//
func (a *analysis) store(dst, src nodeid, offset uint32, sizeof uint32) {
if src == 0 {
return // store of non-pointerlike value
@@ -379,7 +367,6 @@ func (a *analysis) store(dst, src nodeid, offset uint32, sizeof uint32) {
// offsetAddr creates an offsetAddr constraint of the form dst = &src.#offset.
// offset is the field offset in logical fields.
// T is the type of the address.
-//
func (a *analysis) offsetAddr(T types.Type, dst, src nodeid, offset uint32) {
if !a.shouldTrack(T) {
return
@@ -398,7 +385,6 @@ func (a *analysis) offsetAddr(T types.Type, dst, src nodeid, offset uint32) {
// 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})
@@ -417,7 +403,6 @@ func (a *analysis) addConstraint(c constraint) {
// copyElems generates load/store constraints for *dst = *src,
// where src and dst are slices or *arrays.
-//
func (a *analysis) copyElems(cgn *cgnode, typ types.Type, dst, src ssa.Value) {
tmp := a.addNodes(typ, "copy")
sz := a.sizeof(typ)
@@ -553,7 +538,6 @@ func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) {
// choose a policy. The current policy, rather arbitrarily, is true
// for intrinsics and accessor methods (actually: short, single-block,
// call-free functions). This is just a starting point.
-//
func (a *analysis) shouldUseContext(fn *ssa.Function) bool {
if a.findIntrinsic(fn) != nil {
return true // treat intrinsics context-sensitively
@@ -705,11 +689,13 @@ func (a *analysis) genInvoke(caller *cgnode, site *callsite, call *ssa.CallCommo
// practice it occurs rarely, so we special case for reflect.Type.)
//
// In effect we treat this:
-// var rt reflect.Type = ...
-// rt.F()
+//
+// var rt reflect.Type = ...
+// rt.F()
+//
// as this:
-// rt.(*reflect.rtype).F()
//
+// rt.(*reflect.rtype).F()
func (a *analysis) genInvokeReflectType(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
// Unpack receiver into rtype
rtype := a.addOneNode(a.reflectRtypePtr, "rtype.recv", nil)
@@ -789,13 +775,15 @@ func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) {
// a simple copy constraint when the sole destination is known a priori.
//
// Some SSA instructions always have singletons points-to sets:
-// Alloc, Function, Global, MakeChan, MakeClosure, MakeInterface, MakeMap, MakeSlice.
+//
+// Alloc, Function, Global, MakeChan, MakeClosure, MakeInterface, MakeMap, MakeSlice.
+//
// Others may be singletons depending on their operands:
-// FreeVar, Const, Convert, FieldAddr, IndexAddr, Slice, SliceToArrayPointer.
+//
+// FreeVar, Const, Convert, FieldAddr, IndexAddr, Slice, SliceToArrayPointer.
//
// Idempotent. Objects are created as needed, possibly via recursion
// down the SSA value graph, e.g IndexAddr(FieldAddr(Alloc))).
-//
func (a *analysis) objectNode(cgn *cgnode, v ssa.Value) nodeid {
switch v.(type) {
case *ssa.Global, *ssa.Function, *ssa.Const, *ssa.FreeVar:
@@ -992,7 +980,10 @@ func (a *analysis) genInstr(cgn *cgnode, instr ssa.Instruction) {
a.sizeof(instr.Type()))
case *ssa.Index:
- a.copy(a.valueNode(instr), 1+a.valueNode(instr.X), a.sizeof(instr.Type()))
+ _, isstring := typeparams.CoreType(instr.X.Type()).(*types.Basic)
+ if !isstring {
+ a.copy(a.valueNode(instr), 1+a.valueNode(instr.X), a.sizeof(instr.Type()))
+ }
case *ssa.Select:
recv := a.valueOffsetNode(instr, 2) // instr : (index, recvOk, recv0, ... recv_n-1)
@@ -1156,7 +1147,6 @@ func (a *analysis) makeCGNode(fn *ssa.Function, obj nodeid, callersite *callsite
// genRootCalls generates the synthetic root of the callgraph and the
// initial calls from it to the analysis scope, such as main, a test
// or a library.
-//
func (a *analysis) genRootCalls() *cgnode {
r := a.prog.NewFunction("<root>", new(types.Signature), "root of callgraph")
root := a.makeCGNode(r, 0, nil)
@@ -1217,6 +1207,19 @@ func (a *analysis) genFunc(cgn *cgnode) {
return
}
+ if fn.TypeParams().Len() > 0 && len(fn.TypeArgs()) == 0 {
+ // Body of generic function.
+ // We'll warn about calls to such functions at the end.
+ return
+ }
+
+ if strings.HasPrefix(fn.Synthetic, "instantiation wrapper ") {
+ // instantiation wrapper of a generic function.
+ // These may contain type coercions which are not currently supported.
+ // We'll warn about calls to such functions at the end.
+ return
+ }
+
if a.log != nil {
fmt.Fprintln(a.log, "; Creating nodes for local values")
}