diff --git a/oracle/peers.go b/oracle/peers.go index 3ec50c21a95..bfd2e11de06 100644 --- a/oracle/peers.go +++ b/oracle/peers.go @@ -88,7 +88,7 @@ func peers(o *Oracle, qpos *QueryPos) (queryResult, error) { var sends, receives []token.Pos for _, op := range ops { for _, ptr := range ptares.Queries[op.ch] { - if ptr != nil && ptr.PointsTo().Intersects(queryChanPts) { + if ptr.PointsTo().Intersects(queryChanPts) { if op.dir == ast.SEND { sends = append(sends, op.pos) } else { diff --git a/pointer/api.go b/pointer/api.go index 5f294aa0610..9e9c048bb3a 100644 --- a/pointer/api.go +++ b/pointer/api.go @@ -40,7 +40,10 @@ type Config struct { // Pointer p may be saved until the analysis is complete, at // which point its methods provide access to the analysis // (The result of callings its methods within the Print - // callback is undefined.) p is nil if x is non-pointerlike. + // callback is undefined.) + // + // CanPoint(site.Args[0].Type()) reports whether p is + // pointerlike. // Print func(site *ssa.CallCommon, p Pointer) @@ -117,53 +120,26 @@ type Result struct { } // A Pointer is an equivalence class of pointerlike values. -type Pointer interface { - // PointsTo returns the points-to set of this pointer. - PointsTo() PointsToSet - - // MayAlias reports whether the receiver pointer may alias - // the argument pointer. - MayAlias(Pointer) bool - - // Context returns the context of this pointer, - // if it corresponds to a local variable. - Context() call.GraphNode - - String() string +// +// A pointer doesn't have a unique type because pointers of distinct +// types may alias the same object. +// +type Pointer struct { + a *analysis + cgn *cgnode + n nodeid // non-zero } // A PointsToSet is a set of labels (locations or allocations). -// -type PointsToSet interface { - // PointsTo returns the set of labels that this points-to set - // contains. - Labels() []*Label - - // Intersects reports whether this points-to set and the - // argument points-to set contain common members. - Intersects(PointsToSet) bool - - // If this PointsToSet came from a Pointer of interface kind - // or a reflect.Value, DynamicTypes returns the set of dynamic - // types that it may contain. (For an interface, they will - // always be concrete types.) - // - // The result is a mapping whose keys are the dynamic types to - // which it may point. For each pointer-like key type, the - // corresponding map value is a set of pointer abstractions of - // that dynamic type, represented as a []Pointer slice. Use - // PointsToCombined to merge them. - // - // The result is empty unless CanHaveDynamicTypes(T). - // - DynamicTypes() *typemap.M +type PointsToSet struct { + a *analysis // may be nil if pts is nil + pts nodeset } // Union returns the set containing all the elements of each set in sets. func Union(sets ...PointsToSet) PointsToSet { - var union ptset + var union PointsToSet for _, set := range sets { - set := set.(ptset) union.a = set.a union.pts.addAll(set.pts) } @@ -180,14 +156,7 @@ func PointsToCombined(ptrs []Pointer) PointsToSet { return Union(ptsets...) } -// ---- PointsToSet public interface - -type ptset struct { - a *analysis // may be nil if pts is nil - pts nodeset -} - -func (s ptset) String() string { +func (s PointsToSet) String() string { var buf bytes.Buffer fmt.Fprintf(&buf, "[") sep := "" @@ -199,7 +168,9 @@ func (s ptset) String() string { return buf.String() } -func (s ptset) Labels() []*Label { +// PointsTo returns the set of labels that this points-to set +// contains. +func (s PointsToSet) Labels() []*Label { var labels []*Label for l := range s.pts { labels = append(labels, s.a.labelFor(l)) @@ -207,7 +178,20 @@ func (s ptset) Labels() []*Label { return labels } -func (s ptset) DynamicTypes() *typemap.M { +// If this PointsToSet came from a Pointer of interface kind +// or a reflect.Value, DynamicTypes returns the set of dynamic +// types that it may contain. (For an interface, they will +// always be concrete types.) +// +// The result is a mapping whose keys are the dynamic types to +// which it may point. For each pointer-like key type, the +// corresponding map value is a set of pointer abstractions of +// that dynamic type, represented as a []Pointer slice. Use +// PointsToCombined to merge them. +// +// The result is empty unless CanHaveDynamicTypes(T). +// +func (s PointsToSet) DynamicTypes() *typemap.M { var tmap typemap.M tmap.SetHasher(s.a.hasher) for ifaceObjId := range s.pts { @@ -219,13 +203,14 @@ func (s ptset) DynamicTypes() *typemap.M { panic("indirect tagged object") // implement later } prev, _ := tmap.At(tDyn).([]Pointer) - tmap.Set(tDyn, append(prev, ptr{s.a, nil, v})) + tmap.Set(tDyn, append(prev, Pointer{s.a, nil, v})) } return &tmap } -func (x ptset) Intersects(y_ PointsToSet) bool { - y := y_.(ptset) +// Intersects reports whether this points-to set and the +// argument points-to set contain common members. +func (x PointsToSet) Intersects(y PointsToSet) bool { for l := range x.pts { if _, ok := y.pts[l]; ok { return true @@ -234,31 +219,28 @@ func (x ptset) Intersects(y_ PointsToSet) bool { return false } -// ---- Pointer public interface - -// ptr adapts a node to the Pointer interface. -type ptr struct { - a *analysis - cgn *cgnode - n nodeid // non-zero -} - -func (p ptr) String() string { +func (p Pointer) String() string { return fmt.Sprintf("n%d", p.n) } -func (p ptr) Context() call.GraphNode { +// Context returns the context of this pointer, +// if it corresponds to a local variable. +func (p Pointer) Context() call.GraphNode { return p.cgn } -func (p ptr) PointsTo() PointsToSet { - return ptset{p.a, p.a.nodes[p.n].pts} +// PointsTo returns the points-to set of this pointer. +func (p Pointer) PointsTo() PointsToSet { + return PointsToSet{p.a, p.a.nodes[p.n].pts} } -func (p ptr) MayAlias(q Pointer) bool { +// MayAlias reports whether the receiver pointer may alias +// the argument pointer. +func (p Pointer) MayAlias(q Pointer) bool { return p.PointsTo().Intersects(q.PointsTo()) } -func (p ptr) DynamicTypes() *typemap.M { +// DynamicTypes returns p.PointsTo().DynamicTypes(). +func (p Pointer) DynamicTypes() *typemap.M { return p.PointsTo().DynamicTypes() } diff --git a/pointer/gen.go b/pointer/gen.go index 502fa825641..215204950ef 100644 --- a/pointer/gen.go +++ b/pointer/gen.go @@ -82,14 +82,14 @@ func (a *analysis) setValueNode(v ssa.Value, id nodeid, cgn *cgnode) { // 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}) + a.result.Queries[v] = append(a.result.Queries[v], Pointer{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}) + a.result.IndirectQueries[v] = append(a.result.IndirectQueries[v], Pointer{a, cgn, indirect}) } } @@ -539,7 +539,7 @@ func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) { if probe == 0 { probe = a.addNodes(t, "print") a.probes[call] = probe - Print(call, ptr{a, nil, probe}) // notify client + Print(call, Pointer{a, nil, probe}) // notify client } a.copy(probe, a.valueNode(call.Args[0]), a.sizeof(t)) diff --git a/pointer/pointer_test.go b/pointer/pointer_test.go index 2c833e79d93..ca4abb96c5f 100644 --- a/pointer/pointer_test.go +++ b/pointer/pointer_test.go @@ -309,9 +309,9 @@ func doOneInput(input, filename string) bool { e.errorf("unreachable print() statement has expectation %s", e) continue } - if pr.arg0 == nil { + if tArg := pr.instr.Args[0].Type(); !pointer.CanPoint(tArg) { ok = false - e.errorf("expectation on non-pointerlike operand: %s", pr.instr.Args[0].Type()) + e.errorf("expectation on non-pointerlike operand: %s", tArg) continue } } diff --git a/pointer/testdata/another.go b/pointer/testdata/another.go index f871f67313b..443c94d060c 100644 --- a/pointer/testdata/another.go +++ b/pointer/testdata/another.go @@ -31,5 +31,4 @@ func main() { // labels, even though it may contain pointers that do. print(i) // @pointsto makeinterface:func(x int) int | makeinterface:func(x int, y int) | makeinterface:func(int, int) | makeinterface:int | makeinterface:main.S print(i.(func(int) int)) // @pointsto main.incr - print(i.(S)) // @pointsto } diff --git a/pointer/testdata/structs.go b/pointer/testdata/structs.go index a10760c7924..9036d608db9 100644 --- a/pointer/testdata/structs.go +++ b/pointer/testdata/structs.go @@ -87,7 +87,6 @@ func structs2() { var s3 S // @line s2s3 s3.c[2] = new(T) // @line s2s3c - print(s3.c) // @pointsto print(&s3.c) // @pointsto s3.c@s2s3:6 print(s3.c[1]) // @pointsto new@s2s3c:15 print(&s3.c[1]) // @pointsto s3.c[*]@s2s3:6