go.tools/pointer: allow clients to request both pts(v) and pts(*v) in the same analysis.

Also: add (ptset).String().

R=crawshaw
CC=golang-dev
https://golang.org/cl/36800044
diff --git a/pointer/analysis.go b/pointer/analysis.go
index 0f21424..4602ba5 100644
--- a/pointer/analysis.go
+++ b/pointer/analysis.go
@@ -262,7 +262,8 @@
 		probes:      make(map[*ssa.CallCommon]nodeid),
 		work:        makeMapWorklist(),
 		result: &Result{
-			Queries: make(map[ssa.Value][]Pointer),
+			Queries:         make(map[ssa.Value][]Pointer),
+			IndirectQueries: make(map[ssa.Value][]Pointer),
 		},
 	}
 
diff --git a/pointer/api.go b/pointer/api.go
index 10103b9..5f294aa 100644
--- a/pointer/api.go
+++ b/pointer/api.go
@@ -5,6 +5,7 @@
 package pointer
 
 import (
+	"bytes"
 	"fmt"
 	"go/token"
 	"io"
@@ -43,32 +44,54 @@
 	//
 	Print func(site *ssa.CallCommon, p Pointer)
 
-	// The client populates Queries[v] for each ssa.Value v of
-	// interest.
+	// The client populates Queries[v] or IndirectQueries[v]
+	// for each ssa.Value v of interest, to request that the
+	// points-to sets pts(v) or pts(*v) be computed.  If the
+	// client needs both points-to sets, v may appear in both
+	// maps.
 	//
-	// The boolean (Indirect) indicates whether to compute the
-	// points-to set for v (false) or *v (true): the latter is
-	// typically wanted for Values corresponding to source-level
-	// lvalues, e.g. an *ssa.Global.
+	// (IndirectQueries is typically used for Values corresponding
+	// to source-level lvalues, e.g. an *ssa.Global.)
 	//
-	// The pointer analysis will populate the corresponding
-	// Results.Queries value when it creates the pointer variable
-	// for v or *v.  Upon completion the client can inspect that
-	// map for the results.
+	// The analysis populates the corresponding
+	// Results.{Indirect,}Queries map when it creates the pointer
+	// variable for v or *v.  Upon completion the client can
+	// inspect that map for the results.
 	//
 	// If a Value belongs to a function that the analysis treats
-	// context-sensitively, the corresponding Results.Queries slice
-	// may have multiple Pointers, one per distinct context.  Use
-	// PointsToCombined to merge them.
+	// context-sensitively, the corresponding Results.{Indirect,}Queries
+	// slice may have multiple Pointers, one per distinct context.
+	// Use PointsToCombined to merge them.
 	//
-	Queries map[ssa.Value]Indirect
+	// TODO(adonovan): this API doesn't scale well for batch tools
+	// that want to dump the entire solution.
+	//
+	// TODO(adonovan): need we distinguish contexts?  Current
+	// clients always combine them.
+	//
+	Queries         map[ssa.Value]struct{}
+	IndirectQueries map[ssa.Value]struct{}
 
 	// If Log is non-nil, log messages are written to it.
 	// Logging is extremely verbose.
 	Log io.Writer
 }
 
-type Indirect bool // map[ssa.Value]Indirect is not a set
+// AddQuery adds v to Config.Queries.
+func (c *Config) AddQuery(v ssa.Value) {
+	if c.Queries == nil {
+		c.Queries = make(map[ssa.Value]struct{})
+	}
+	c.Queries[v] = struct{}{}
+}
+
+// AddQuery adds v to Config.IndirectQueries.
+func (c *Config) AddIndirectQuery(v ssa.Value) {
+	if c.IndirectQueries == nil {
+		c.IndirectQueries = make(map[ssa.Value]struct{})
+	}
+	c.IndirectQueries[v] = struct{}{}
+}
 
 func (c *Config) prog() *ssa.Program {
 	for _, main := range c.Mains {
@@ -87,9 +110,10 @@
 // See Config for how to request the various Result components.
 //
 type Result struct {
-	CallGraph call.Graph              // discovered call graph
-	Queries   map[ssa.Value][]Pointer // points-to sets for queried ssa.Values
-	Warnings  []Warning               // warnings of unsoundness
+	CallGraph       call.Graph              // discovered call graph
+	Queries         map[ssa.Value][]Pointer // pts(v) for each v in Config.Queries.
+	IndirectQueries map[ssa.Value][]Pointer // pts(*v) for each v in Config.IndirectQueries.
+	Warnings        []Warning               // warnings of unsoundness
 }
 
 // A Pointer is an equivalence class of pointerlike values.
@@ -163,6 +187,18 @@
 	pts nodeset
 }
 
+func (s ptset) String() string {
+	var buf bytes.Buffer
+	fmt.Fprintf(&buf, "[")
+	sep := ""
+	for l := range s.pts {
+		fmt.Fprintf(&buf, "%s%s", sep, s.a.labelFor(l))
+		sep = ", "
+	}
+	fmt.Fprintf(&buf, "]")
+	return buf.String()
+}
+
 func (s ptset) Labels() []*Label {
 	var labels []*Label
 	for l := range s.pts {
diff --git a/pointer/gen.go b/pointer/gen.go
index 297ad63..502fa82 100644
--- a/pointer/gen.go
+++ b/pointer/gen.go
@@ -80,15 +80,17 @@
 		fmt.Fprintf(a.log, "\tval[%s] = n%d  (%T)\n", v.Name(), id, v)
 	}
 
-	// Record the (v, id) relation if the client has queried v.
-	if indirect, ok := a.config.Queries[v]; ok {
-		if indirect {
-			tmp := a.addNodes(v.Type(), "query.indirect")
-			a.genLoad(cgn, tmp, v, 0, a.sizeof(v.Type()))
-			id = tmp
-		}
+	// Record the (v, id) relation if the client has queried pts(v).
+	if _, ok := a.config.Queries[v]; ok {
 		a.result.Queries[v] = append(a.result.Queries[v], ptr{a, cgn, id})
 	}
+
+	// Record the (*v, id) relation if the client has queried pts(*v).
+	if _, ok := a.config.IndirectQueries[v]; ok {
+		indirect := a.addNodes(v.Type(), "query.indirect")
+		a.genLoad(cgn, indirect, v, 0, a.sizeof(v.Type()))
+		a.result.IndirectQueries[v] = append(a.result.IndirectQueries[v], ptr{a, cgn, indirect})
+	}
 }
 
 // endObject marks the end of a sequence of calls to addNodes denoting
diff --git a/pointer/labels.go b/pointer/labels.go
index 10c8ecd..9c6ce11 100644
--- a/pointer/labels.go
+++ b/pointer/labels.go
@@ -18,15 +18,15 @@
 // channel, 'func', slice or interface.  Labels include:
 //
 // Labels include:
-// 	- functions
+//      - functions
 //      - globals
 //      - tagged objects, representing interfaces and reflect.Values
-//      - arrays created by literals (e.g. []byte("foo")) and conversions ([]byte(s))
-// 	- stack- and heap-allocated variables (including composite literals)
-// 	- channels, maps and arrays created by make()
-//	- instrinsic or reflective operations that allocate (e.g. append, reflect.New)
-//	- instrinsic objects, e.g. the initial array behind os.Args.
-// 	- and their subelements, e.g. "alloc.y[*].z"
+//      - arrays created by conversions (e.g. []byte("foo"), []byte(s))
+//      - stack- and heap-allocated variables (including composite literals)
+//      - channels, maps and arrays created by make()
+//      - instrinsic or reflective operations that allocate (e.g. append, reflect.New)
+//      - instrinsic objects, e.g. the initial array behind os.Args.
+//      - and their subelements, e.g. "alloc.y[*].z"
 //
 // Labels are so varied that they defy good generalizations;
 // some have no value, no callgraph node, or no position.