.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / go / pointer / reflect.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/go/pointer/reflect.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/go/pointer/reflect.go
new file mode 100644 (file)
index 0000000..7aa1a9c
--- /dev/null
@@ -0,0 +1,1975 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pointer
+
+// This file implements the generation and resolution rules for
+// constraints arising from the use of reflection in the target
+// program.  See doc.go for explanation of the representation.
+//
+// For consistency, the names of all parameters match those of the
+// actual functions in the "reflect" package.
+//
+// To avoid proliferation of equivalent labels, intrinsics should
+// memoize as much as possible, like TypeOf and Zero do for their
+// tagged objects.
+//
+// TODO(adonovan): this file is rather subtle.  Explain how we derive
+// the implementation of each reflect operator from its spec,
+// including the subtleties of reflect.flag{Addr,RO,Indir}.
+// [Hint: our implementation is as if reflect.flagIndir was always
+// true, i.e. reflect.Values are pointers to tagged objects, there is
+// no inline allocation optimization; and indirect tagged objects (not
+// yet implemented) correspond to reflect.Values with
+// reflect.flagAddr.]
+// A picture would help too.
+//
+// TODO(adonovan): try factoring up the common parts of the majority of
+// these constraints that are single input, single output.
+
+import (
+       "fmt"
+       "go/constant"
+       "go/types"
+       "reflect"
+
+       "golang.org/x/tools/go/ssa"
+)
+
+func init() {
+       for name, fn := range map[string]intrinsic{
+               // reflect.Value methods.
+               "(reflect.Value).Addr":            ext۰reflect۰Value۰Addr,
+               "(reflect.Value).Bool":            ext۰NoEffect,
+               "(reflect.Value).Bytes":           ext۰reflect۰Value۰Bytes,
+               "(reflect.Value).Call":            ext۰reflect۰Value۰Call,
+               "(reflect.Value).CallSlice":       ext۰reflect۰Value۰CallSlice,
+               "(reflect.Value).CanAddr":         ext۰NoEffect,
+               "(reflect.Value).CanInterface":    ext۰NoEffect,
+               "(reflect.Value).CanSet":          ext۰NoEffect,
+               "(reflect.Value).Cap":             ext۰NoEffect,
+               "(reflect.Value).Close":           ext۰NoEffect,
+               "(reflect.Value).Complex":         ext۰NoEffect,
+               "(reflect.Value).Convert":         ext۰reflect۰Value۰Convert,
+               "(reflect.Value).Elem":            ext۰reflect۰Value۰Elem,
+               "(reflect.Value).Field":           ext۰reflect۰Value۰Field,
+               "(reflect.Value).FieldByIndex":    ext۰reflect۰Value۰FieldByIndex,
+               "(reflect.Value).FieldByName":     ext۰reflect۰Value۰FieldByName,
+               "(reflect.Value).FieldByNameFunc": ext۰reflect۰Value۰FieldByNameFunc,
+               "(reflect.Value).Float":           ext۰NoEffect,
+               "(reflect.Value).Index":           ext۰reflect۰Value۰Index,
+               "(reflect.Value).Int":             ext۰NoEffect,
+               "(reflect.Value).Interface":       ext۰reflect۰Value۰Interface,
+               "(reflect.Value).InterfaceData":   ext۰NoEffect,
+               "(reflect.Value).IsNil":           ext۰NoEffect,
+               "(reflect.Value).IsValid":         ext۰NoEffect,
+               "(reflect.Value).Kind":            ext۰NoEffect,
+               "(reflect.Value).Len":             ext۰NoEffect,
+               "(reflect.Value).MapIndex":        ext۰reflect۰Value۰MapIndex,
+               "(reflect.Value).MapKeys":         ext۰reflect۰Value۰MapKeys,
+               "(reflect.Value).Method":          ext۰reflect۰Value۰Method,
+               "(reflect.Value).MethodByName":    ext۰reflect۰Value۰MethodByName,
+               "(reflect.Value).NumField":        ext۰NoEffect,
+               "(reflect.Value).NumMethod":       ext۰NoEffect,
+               "(reflect.Value).OverflowComplex": ext۰NoEffect,
+               "(reflect.Value).OverflowFloat":   ext۰NoEffect,
+               "(reflect.Value).OverflowInt":     ext۰NoEffect,
+               "(reflect.Value).OverflowUint":    ext۰NoEffect,
+               "(reflect.Value).Pointer":         ext۰NoEffect,
+               "(reflect.Value).Recv":            ext۰reflect۰Value۰Recv,
+               "(reflect.Value).Send":            ext۰reflect۰Value۰Send,
+               "(reflect.Value).Set":             ext۰reflect۰Value۰Set,
+               "(reflect.Value).SetBool":         ext۰NoEffect,
+               "(reflect.Value).SetBytes":        ext۰reflect۰Value۰SetBytes,
+               "(reflect.Value).SetComplex":      ext۰NoEffect,
+               "(reflect.Value).SetFloat":        ext۰NoEffect,
+               "(reflect.Value).SetInt":          ext۰NoEffect,
+               "(reflect.Value).SetLen":          ext۰NoEffect,
+               "(reflect.Value).SetMapIndex":     ext۰reflect۰Value۰SetMapIndex,
+               "(reflect.Value).SetPointer":      ext۰reflect۰Value۰SetPointer,
+               "(reflect.Value).SetString":       ext۰NoEffect,
+               "(reflect.Value).SetUint":         ext۰NoEffect,
+               "(reflect.Value).Slice":           ext۰reflect۰Value۰Slice,
+               "(reflect.Value).String":          ext۰NoEffect,
+               "(reflect.Value).TryRecv":         ext۰reflect۰Value۰Recv,
+               "(reflect.Value).TrySend":         ext۰reflect۰Value۰Send,
+               "(reflect.Value).Type":            ext۰NoEffect,
+               "(reflect.Value).Uint":            ext۰NoEffect,
+               "(reflect.Value).UnsafeAddr":      ext۰NoEffect,
+
+               // Standalone reflect.* functions.
+               "reflect.Append":      ext۰reflect۰Append,
+               "reflect.AppendSlice": ext۰reflect۰AppendSlice,
+               "reflect.Copy":        ext۰reflect۰Copy,
+               "reflect.ChanOf":      ext۰reflect۰ChanOf,
+               "reflect.DeepEqual":   ext۰NoEffect,
+               "reflect.Indirect":    ext۰reflect۰Indirect,
+               "reflect.MakeChan":    ext۰reflect۰MakeChan,
+               "reflect.MakeFunc":    ext۰reflect۰MakeFunc,
+               "reflect.MakeMap":     ext۰reflect۰MakeMap,
+               "reflect.MakeSlice":   ext۰reflect۰MakeSlice,
+               "reflect.MapOf":       ext۰reflect۰MapOf,
+               "reflect.New":         ext۰reflect۰New,
+               "reflect.NewAt":       ext۰reflect۰NewAt,
+               "reflect.PtrTo":       ext۰reflect۰PtrTo,
+               "reflect.Select":      ext۰reflect۰Select,
+               "reflect.SliceOf":     ext۰reflect۰SliceOf,
+               "reflect.TypeOf":      ext۰reflect۰TypeOf,
+               "reflect.ValueOf":     ext۰reflect۰ValueOf,
+               "reflect.Zero":        ext۰reflect۰Zero,
+               "reflect.init":        ext۰NoEffect,
+
+               // *reflect.rtype methods
+               "(*reflect.rtype).Align":           ext۰NoEffect,
+               "(*reflect.rtype).AssignableTo":    ext۰NoEffect,
+               "(*reflect.rtype).Bits":            ext۰NoEffect,
+               "(*reflect.rtype).ChanDir":         ext۰NoEffect,
+               "(*reflect.rtype).ConvertibleTo":   ext۰NoEffect,
+               "(*reflect.rtype).Elem":            ext۰reflect۰rtype۰Elem,
+               "(*reflect.rtype).Field":           ext۰reflect۰rtype۰Field,
+               "(*reflect.rtype).FieldAlign":      ext۰NoEffect,
+               "(*reflect.rtype).FieldByIndex":    ext۰reflect۰rtype۰FieldByIndex,
+               "(*reflect.rtype).FieldByName":     ext۰reflect۰rtype۰FieldByName,
+               "(*reflect.rtype).FieldByNameFunc": ext۰reflect۰rtype۰FieldByNameFunc,
+               "(*reflect.rtype).Implements":      ext۰NoEffect,
+               "(*reflect.rtype).In":              ext۰reflect۰rtype۰In,
+               "(*reflect.rtype).IsVariadic":      ext۰NoEffect,
+               "(*reflect.rtype).Key":             ext۰reflect۰rtype۰Key,
+               "(*reflect.rtype).Kind":            ext۰NoEffect,
+               "(*reflect.rtype).Len":             ext۰NoEffect,
+               "(*reflect.rtype).Method":          ext۰reflect۰rtype۰Method,
+               "(*reflect.rtype).MethodByName":    ext۰reflect۰rtype۰MethodByName,
+               "(*reflect.rtype).Name":            ext۰NoEffect,
+               "(*reflect.rtype).NumField":        ext۰NoEffect,
+               "(*reflect.rtype).NumIn":           ext۰NoEffect,
+               "(*reflect.rtype).NumMethod":       ext۰NoEffect,
+               "(*reflect.rtype).NumOut":          ext۰NoEffect,
+               "(*reflect.rtype).Out":             ext۰reflect۰rtype۰Out,
+               "(*reflect.rtype).PkgPath":         ext۰NoEffect,
+               "(*reflect.rtype).Size":            ext۰NoEffect,
+               "(*reflect.rtype).String":          ext۰NoEffect,
+       } {
+               intrinsicsByName[name] = fn
+       }
+}
+
+// -------------------- (reflect.Value) --------------------
+
+func ext۰reflect۰Value۰Addr(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).Bytes() Value ----------
+
+// result = v.Bytes()
+type rVBytesConstraint struct {
+       v      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rVBytesConstraint) ptr() nodeid { return c.v }
+func (c *rVBytesConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rVBytes.result")
+}
+func (c *rVBytesConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.result = mapping[c.result]
+}
+
+func (c *rVBytesConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect n%d.Bytes()", c.result, c.v)
+}
+
+func (c *rVBytesConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, slice, 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")
+               }
+
+               tSlice, ok := tDyn.Underlying().(*types.Slice)
+               if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) {
+                       if a.onlineCopy(c.result, slice) {
+                               changed = true
+                       }
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Value۰Bytes(a *analysis, cgn *cgnode) {
+       a.addConstraint(&rVBytesConstraint{
+               v:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func (Value).Call(in []Value) []Value ----------
+
+// result = v.Call(in)
+type rVCallConstraint struct {
+       cgn       *cgnode
+       targets   nodeid // (indirect)
+       v         nodeid // (ptr)
+       arg       nodeid // = in[*]
+       result    nodeid // (indirect)
+       dotdotdot bool   // interpret last arg as a "..." slice
+}
+
+func (c *rVCallConstraint) ptr() nodeid { return c.v }
+func (c *rVCallConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.targets), "rVCall.targets")
+       h.markIndirect(onodeid(c.result), "rVCall.result")
+}
+func (c *rVCallConstraint) renumber(mapping []nodeid) {
+       c.targets = mapping[c.targets]
+       c.v = mapping[c.v]
+       c.arg = mapping[c.arg]
+       c.result = mapping[c.result]
+}
+
+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) solve(a *analysis, delta *nodeset) {
+       if c.targets == 0 {
+               panic("no targets")
+       }
+
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               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(cgn.fn.Signature.Results().At(0).Type(), 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) {} // TODO(adonovan)
+
+// ---------- func (Value).Elem() Value ----------
+
+// result = v.Elem()
+type rVElemConstraint struct {
+       cgn    *cgnode
+       v      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rVElemConstraint) ptr() nodeid { return c.v }
+func (c *rVElemConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rVElem.result")
+}
+func (c *rVElemConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.result = mapping[c.result]
+}
+
+func (c *rVElemConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect n%d.Elem()", c.result, c.v)
+}
+
+func (c *rVElemConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, payload, 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")
+               }
+
+               switch t := tDyn.Underlying().(type) {
+               case *types.Interface:
+                       if a.onlineCopy(c.result, payload) {
+                               changed = true
+                       }
+
+               case *types.Pointer:
+                       obj := a.makeTagged(t.Elem(), c.cgn, nil)
+                       a.load(obj+1, payload, 0, a.sizeof(t.Elem()))
+                       if a.addLabel(c.result, obj) {
+                               changed = true
+                       }
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Value۰Elem(a *analysis, cgn *cgnode) {
+       a.addConstraint(&rVElemConstraint{
+               cgn:    cgn,
+               v:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+func ext۰reflect۰Value۰Field(a *analysis, cgn *cgnode)           {} // TODO(adonovan)
+func ext۰reflect۰Value۰FieldByIndex(a *analysis, cgn *cgnode)    {} // TODO(adonovan)
+func ext۰reflect۰Value۰FieldByName(a *analysis, cgn *cgnode)     {} // TODO(adonovan)
+func ext۰reflect۰Value۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).Index() Value ----------
+
+// result = v.Index()
+type rVIndexConstraint struct {
+       cgn    *cgnode
+       v      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rVIndexConstraint) ptr() nodeid { return c.v }
+func (c *rVIndexConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rVIndex.result")
+}
+func (c *rVIndexConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.result = mapping[c.result]
+}
+
+func (c *rVIndexConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect n%d.Index()", c.result, c.v)
+}
+
+func (c *rVIndexConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, payload, 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")
+               }
+
+               var res nodeid
+               switch t := tDyn.Underlying().(type) {
+               case *types.Array:
+                       res = a.makeTagged(t.Elem(), c.cgn, nil)
+                       a.onlineCopyN(res+1, payload+1, a.sizeof(t.Elem()))
+
+               case *types.Slice:
+                       res = a.makeTagged(t.Elem(), c.cgn, nil)
+                       a.load(res+1, payload, 1, a.sizeof(t.Elem()))
+
+               case *types.Basic:
+                       if t.Kind() == types.String {
+                               res = a.makeTagged(types.Typ[types.Rune], c.cgn, nil)
+                       }
+               }
+               if res != 0 && a.addLabel(c.result, res) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode) {
+       a.addConstraint(&rVIndexConstraint{
+               cgn:    cgn,
+               v:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func (Value).Interface() Value ----------
+
+// result = v.Interface()
+type rVInterfaceConstraint struct {
+       v      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rVInterfaceConstraint) ptr() nodeid { return c.v }
+func (c *rVInterfaceConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rVInterface.result")
+}
+func (c *rVInterfaceConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.result = mapping[c.result]
+}
+
+func (c *rVInterfaceConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v)
+}
+
+func (c *rVInterfaceConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, payload, 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")
+               }
+
+               if isInterface(tDyn) {
+                       if a.onlineCopy(c.result, payload) {
+                               a.addWork(c.result)
+                       }
+               } else {
+                       if a.addLabel(c.result, vObj) {
+                               changed = true
+                       }
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) {
+       a.addConstraint(&rVInterfaceConstraint{
+               v:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func (Value).MapIndex(Value) Value ----------
+
+// result = v.MapIndex(_)
+type rVMapIndexConstraint struct {
+       cgn    *cgnode
+       v      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rVMapIndexConstraint) ptr() nodeid { return c.v }
+func (c *rVMapIndexConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rVMapIndex.result")
+}
+func (c *rVMapIndexConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.result = mapping[c.result]
+}
+
+func (c *rVMapIndexConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v)
+}
+
+func (c *rVMapIndexConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, m, indirect := a.taggedValue(vObj)
+               tMap, _ := tDyn.Underlying().(*types.Map)
+               if tMap == nil {
+                       continue // not a map
+               }
+               if indirect {
+                       // TODO(adonovan): we'll need to implement this
+                       // when we start creating indirect tagged objects.
+                       panic("indirect tagged object")
+               }
+
+               obj := a.makeTagged(tMap.Elem(), c.cgn, nil)
+               a.load(obj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem()))
+               if a.addLabel(c.result, obj) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) {
+       a.addConstraint(&rVMapIndexConstraint{
+               cgn:    cgn,
+               v:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func (Value).MapKeys() []Value ----------
+
+// result = v.MapKeys()
+type rVMapKeysConstraint struct {
+       cgn    *cgnode
+       v      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rVMapKeysConstraint) ptr() nodeid { return c.v }
+func (c *rVMapKeysConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rVMapKeys.result")
+}
+func (c *rVMapKeysConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.result = mapping[c.result]
+}
+
+func (c *rVMapKeysConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v)
+}
+
+func (c *rVMapKeysConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, m, indirect := a.taggedValue(vObj)
+               tMap, _ := tDyn.Underlying().(*types.Map)
+               if tMap == nil {
+                       continue // not a map
+               }
+               if indirect {
+                       // TODO(adonovan): we'll need to implement this
+                       // when we start creating indirect tagged objects.
+                       panic("indirect tagged object")
+               }
+
+               kObj := a.makeTagged(tMap.Key(), c.cgn, nil)
+               a.load(kObj+1, m, 0, a.sizeof(tMap.Key()))
+               if a.addLabel(c.result, kObj) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Value۰MapKeys(a *analysis, cgn *cgnode) {
+       // Allocate an array for the result.
+       obj := a.nextNode()
+       T := types.NewSlice(a.reflectValueObj.Type())
+       a.addNodes(sliceToArray(T), "reflect.MapKeys result")
+       a.endObject(obj, cgn, nil)
+       a.addressOf(T, a.funcResults(cgn.obj), obj)
+
+       a.addConstraint(&rVMapKeysConstraint{
+               cgn:    cgn,
+               v:      a.funcParams(cgn.obj),
+               result: obj + 1, // result is stored in array elems
+       })
+}
+
+func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode)       {} // TODO(adonovan)
+func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).Recv(Value) Value ----------
+
+// result, _ = v.Recv()
+type rVRecvConstraint struct {
+       cgn    *cgnode
+       v      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rVRecvConstraint) ptr() nodeid { return c.v }
+func (c *rVRecvConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rVRecv.result")
+}
+func (c *rVRecvConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.result = mapping[c.result]
+}
+
+func (c *rVRecvConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v)
+}
+
+func (c *rVRecvConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, ch, indirect := a.taggedValue(vObj)
+               tChan, _ := tDyn.Underlying().(*types.Chan)
+               if tChan == nil {
+                       continue // not a channel
+               }
+               if indirect {
+                       // TODO(adonovan): we'll need to implement this
+                       // when we start creating indirect tagged objects.
+                       panic("indirect tagged object")
+               }
+
+               tElem := tChan.Elem()
+               elemObj := a.makeTagged(tElem, c.cgn, nil)
+               a.load(elemObj+1, ch, 0, a.sizeof(tElem))
+               if a.addLabel(c.result, elemObj) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Value۰Recv(a *analysis, cgn *cgnode) {
+       a.addConstraint(&rVRecvConstraint{
+               cgn:    cgn,
+               v:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func (Value).Send(Value) ----------
+
+// v.Send(x)
+type rVSendConstraint struct {
+       cgn *cgnode
+       v   nodeid // (ptr)
+       x   nodeid
+}
+
+func (c *rVSendConstraint) ptr() nodeid   { return c.v }
+func (c *rVSendConstraint) presolve(*hvn) {}
+func (c *rVSendConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.x = mapping[c.x]
+}
+
+func (c *rVSendConstraint) String() string {
+       return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x)
+}
+
+func (c *rVSendConstraint) solve(a *analysis, delta *nodeset) {
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, ch, indirect := a.taggedValue(vObj)
+               tChan, _ := tDyn.Underlying().(*types.Chan)
+               if tChan == nil {
+                       continue // not a channel
+               }
+               if indirect {
+                       // TODO(adonovan): we'll need to implement this
+                       // when we start creating indirect tagged objects.
+                       panic("indirect tagged object")
+               }
+
+               // Extract x's payload to xtmp, then store to channel.
+               tElem := tChan.Elem()
+               xtmp := a.addNodes(tElem, "Send.xtmp")
+               a.typeAssert(tElem, xtmp, c.x, false)
+               a.store(ch, xtmp, 0, a.sizeof(tElem))
+       }
+}
+
+func ext۰reflect۰Value۰Send(a *analysis, cgn *cgnode) {
+       params := a.funcParams(cgn.obj)
+       a.addConstraint(&rVSendConstraint{
+               cgn: cgn,
+               v:   params,
+               x:   params + 1,
+       })
+}
+
+func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).SetBytes(x []byte) ----------
+
+// v.SetBytes(x)
+type rVSetBytesConstraint struct {
+       cgn *cgnode
+       v   nodeid // (ptr)
+       x   nodeid
+}
+
+func (c *rVSetBytesConstraint) ptr() nodeid   { return c.v }
+func (c *rVSetBytesConstraint) presolve(*hvn) {}
+func (c *rVSetBytesConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.x = mapping[c.x]
+}
+
+func (c *rVSetBytesConstraint) String() string {
+       return fmt.Sprintf("reflect n%d.SetBytes(n%d)", c.v, c.x)
+}
+
+func (c *rVSetBytesConstraint) solve(a *analysis, delta *nodeset) {
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, slice, 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")
+               }
+
+               tSlice, ok := tDyn.Underlying().(*types.Slice)
+               if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) {
+                       if a.onlineCopy(slice, c.x) {
+                               a.addWork(slice)
+                       }
+               }
+       }
+}
+
+func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) {
+       params := a.funcParams(cgn.obj)
+       a.addConstraint(&rVSetBytesConstraint{
+               cgn: cgn,
+               v:   params,
+               x:   params + 1,
+       })
+}
+
+// ---------- func (Value).SetMapIndex(k Value, v Value) ----------
+
+// v.SetMapIndex(key, val)
+type rVSetMapIndexConstraint struct {
+       cgn *cgnode
+       v   nodeid // (ptr)
+       key nodeid
+       val nodeid
+}
+
+func (c *rVSetMapIndexConstraint) ptr() nodeid   { return c.v }
+func (c *rVSetMapIndexConstraint) presolve(*hvn) {}
+func (c *rVSetMapIndexConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.key = mapping[c.key]
+       c.val = mapping[c.val]
+}
+
+func (c *rVSetMapIndexConstraint) String() string {
+       return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val)
+}
+
+func (c *rVSetMapIndexConstraint) solve(a *analysis, delta *nodeset) {
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, m, indirect := a.taggedValue(vObj)
+               tMap, _ := tDyn.Underlying().(*types.Map)
+               if tMap == nil {
+                       continue // not a map
+               }
+               if indirect {
+                       // TODO(adonovan): we'll need to implement this
+                       // when we start creating indirect tagged objects.
+                       panic("indirect tagged object")
+               }
+
+               keysize := a.sizeof(tMap.Key())
+
+               // Extract key's payload to keytmp, then store to map key.
+               keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp")
+               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.typeAssert(tMap.Elem(), valtmp, c.val, false)
+               a.store(m, valtmp, keysize, a.sizeof(tMap.Elem()))
+       }
+}
+
+func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) {
+       params := a.funcParams(cgn.obj)
+       a.addConstraint(&rVSetMapIndexConstraint{
+               cgn: cgn,
+               v:   params,
+               key: params + 1,
+               val: params + 2,
+       })
+}
+
+func ext۰reflect۰Value۰SetPointer(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (Value).Slice(v Value, i, j int) Value ----------
+
+// result = v.Slice(_, _)
+type rVSliceConstraint struct {
+       cgn    *cgnode
+       v      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rVSliceConstraint) ptr() nodeid { return c.v }
+func (c *rVSliceConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rVSlice.result")
+}
+func (c *rVSliceConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.result = mapping[c.result]
+}
+
+func (c *rVSliceConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect n%d.Slice(_, _)", c.result, c.v)
+}
+
+func (c *rVSliceConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, payload, 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")
+               }
+
+               var res nodeid
+               switch t := tDyn.Underlying().(type) {
+               case *types.Pointer:
+                       if tArr, ok := t.Elem().Underlying().(*types.Array); ok {
+                               // pointer to array
+                               res = a.makeTagged(types.NewSlice(tArr.Elem()), c.cgn, nil)
+                               if a.onlineCopy(res+1, payload) {
+                                       a.addWork(res + 1)
+                               }
+                       }
+
+               case *types.Array:
+                       // TODO(adonovan): implement addressable
+                       // arrays when we do indirect tagged objects.
+
+               case *types.Slice:
+                       res = vObj
+
+               case *types.Basic:
+                       if t == types.Typ[types.String] {
+                               res = vObj
+                       }
+               }
+
+               if res != 0 && a.addLabel(c.result, res) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode) {
+       a.addConstraint(&rVSliceConstraint{
+               cgn:    cgn,
+               v:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// -------------------- Standalone reflect functions --------------------
+
+func ext۰reflect۰Append(a *analysis, cgn *cgnode)      {} // TODO(adonovan)
+func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+func ext۰reflect۰Copy(a *analysis, cgn *cgnode)        {} // TODO(adonovan)
+
+// ---------- func ChanOf(ChanDir, Type) Type ----------
+
+// result = ChanOf(dir, t)
+type reflectChanOfConstraint struct {
+       cgn    *cgnode
+       t      nodeid // (ptr)
+       result nodeid // (indirect)
+       dirs   []types.ChanDir
+}
+
+func (c *reflectChanOfConstraint) ptr() nodeid { return c.t }
+func (c *reflectChanOfConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectChanOf.result")
+}
+func (c *reflectChanOfConstraint) renumber(mapping []nodeid) {
+       c.t = mapping[c.t]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectChanOfConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t)
+}
+
+func (c *reflectChanOfConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               tObj := nodeid(x)
+               T := a.rtypeTaggedValue(tObj)
+
+               if typeTooHigh(T) {
+                       continue
+               }
+
+               for _, dir := range c.dirs {
+                       if a.addLabel(c.result, a.makeRtype(types.NewChan(dir, T))) {
+                               changed = true
+                       }
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+// dirMap maps reflect.ChanDir to the set of channel types generated by ChanOf.
+var dirMap = [...][]types.ChanDir{
+       0:               {types.SendOnly, types.RecvOnly, types.SendRecv}, // unknown
+       reflect.RecvDir: {types.RecvOnly},
+       reflect.SendDir: {types.SendOnly},
+       reflect.BothDir: {types.SendRecv},
+}
+
+func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) {
+       // If we have access to the callsite,
+       // and the channel argument is a constant (as is usual),
+       // only generate the requested direction.
+       var dir reflect.ChanDir // unknown
+       if site := cgn.callersite; site != nil {
+               if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
+                       v, _ := constant.Int64Val(c.Value)
+                       if 0 <= v && v <= int64(reflect.BothDir) {
+                               dir = reflect.ChanDir(v)
+                       }
+               }
+       }
+
+       params := a.funcParams(cgn.obj)
+       a.addConstraint(&reflectChanOfConstraint{
+               cgn:    cgn,
+               t:      params + 1,
+               result: a.funcResults(cgn.obj),
+               dirs:   dirMap[dir],
+       })
+}
+
+// ---------- func Indirect(v Value) Value ----------
+
+// result = Indirect(v)
+type reflectIndirectConstraint struct {
+       cgn    *cgnode
+       v      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *reflectIndirectConstraint) ptr() nodeid { return c.v }
+func (c *reflectIndirectConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectIndirect.result")
+}
+func (c *reflectIndirectConstraint) renumber(mapping []nodeid) {
+       c.v = mapping[c.v]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectIndirectConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v)
+}
+
+func (c *reflectIndirectConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               vObj := nodeid(x)
+               tDyn, _, _ := a.taggedValue(vObj)
+               var res nodeid
+               if tPtr, ok := tDyn.Underlying().(*types.Pointer); ok {
+                       // load the payload of the pointer's tagged object
+                       // into a new tagged object
+                       res = a.makeTagged(tPtr.Elem(), c.cgn, nil)
+                       a.load(res+1, vObj+1, 0, a.sizeof(tPtr.Elem()))
+               } else {
+                       res = vObj
+               }
+
+               if a.addLabel(c.result, res) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Indirect(a *analysis, cgn *cgnode) {
+       a.addConstraint(&reflectIndirectConstraint{
+               cgn:    cgn,
+               v:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func MakeChan(Type) Value ----------
+
+// result = MakeChan(typ)
+type reflectMakeChanConstraint struct {
+       cgn    *cgnode
+       typ    nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *reflectMakeChanConstraint) ptr() nodeid { return c.typ }
+func (c *reflectMakeChanConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectMakeChan.result")
+}
+func (c *reflectMakeChanConstraint) renumber(mapping []nodeid) {
+       c.typ = mapping[c.typ]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectMakeChanConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ)
+}
+
+func (c *reflectMakeChanConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               typObj := nodeid(x)
+               T := a.rtypeTaggedValue(typObj)
+               tChan, ok := T.Underlying().(*types.Chan)
+               if !ok || tChan.Dir() != types.SendRecv {
+                       continue // not a bidirectional channel type
+               }
+
+               obj := a.nextNode()
+               a.addNodes(tChan.Elem(), "reflect.MakeChan.value")
+               a.endObject(obj, c.cgn, nil)
+
+               // put its address in a new T-tagged object
+               id := a.makeTagged(T, c.cgn, nil)
+               a.addLabel(id+1, obj)
+
+               // flow the T-tagged object to the result
+               if a.addLabel(c.result, id) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰MakeChan(a *analysis, cgn *cgnode) {
+       a.addConstraint(&reflectMakeChanConstraint{
+               cgn:    cgn,
+               typ:    a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+func ext۰reflect۰MakeFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func MakeMap(Type) Value ----------
+
+// result = MakeMap(typ)
+type reflectMakeMapConstraint struct {
+       cgn    *cgnode
+       typ    nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *reflectMakeMapConstraint) ptr() nodeid { return c.typ }
+func (c *reflectMakeMapConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectMakeMap.result")
+}
+func (c *reflectMakeMapConstraint) renumber(mapping []nodeid) {
+       c.typ = mapping[c.typ]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectMakeMapConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ)
+}
+
+func (c *reflectMakeMapConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               typObj := nodeid(x)
+               T := a.rtypeTaggedValue(typObj)
+               tMap, ok := T.Underlying().(*types.Map)
+               if !ok {
+                       continue // not a map type
+               }
+
+               mapObj := a.nextNode()
+               a.addNodes(tMap.Key(), "reflect.MakeMap.key")
+               a.addNodes(tMap.Elem(), "reflect.MakeMap.value")
+               a.endObject(mapObj, c.cgn, nil)
+
+               // put its address in a new T-tagged object
+               id := a.makeTagged(T, c.cgn, nil)
+               a.addLabel(id+1, mapObj)
+
+               // flow the T-tagged object to the result
+               if a.addLabel(c.result, id) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰MakeMap(a *analysis, cgn *cgnode) {
+       a.addConstraint(&reflectMakeMapConstraint{
+               cgn:    cgn,
+               typ:    a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func MakeSlice(Type) Value ----------
+
+// result = MakeSlice(typ)
+type reflectMakeSliceConstraint struct {
+       cgn    *cgnode
+       typ    nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *reflectMakeSliceConstraint) ptr() nodeid { return c.typ }
+func (c *reflectMakeSliceConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectMakeSlice.result")
+}
+func (c *reflectMakeSliceConstraint) renumber(mapping []nodeid) {
+       c.typ = mapping[c.typ]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectMakeSliceConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.MakeSlice(n%d)", c.result, c.typ)
+}
+
+func (c *reflectMakeSliceConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               typObj := nodeid(x)
+               T := a.rtypeTaggedValue(typObj)
+               if _, ok := T.Underlying().(*types.Slice); !ok {
+                       continue // not a slice type
+               }
+
+               obj := a.nextNode()
+               a.addNodes(sliceToArray(T), "reflect.MakeSlice")
+               a.endObject(obj, c.cgn, nil)
+
+               // put its address in a new T-tagged object
+               id := a.makeTagged(T, c.cgn, nil)
+               a.addLabel(id+1, obj)
+
+               // flow the T-tagged object to the result
+               if a.addLabel(c.result, id) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰MakeSlice(a *analysis, cgn *cgnode) {
+       a.addConstraint(&reflectMakeSliceConstraint{
+               cgn:    cgn,
+               typ:    a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+func ext۰reflect۰MapOf(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func New(Type) Value ----------
+
+// result = New(typ)
+type reflectNewConstraint struct {
+       cgn    *cgnode
+       typ    nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *reflectNewConstraint) ptr() nodeid { return c.typ }
+func (c *reflectNewConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectNew.result")
+}
+func (c *reflectNewConstraint) renumber(mapping []nodeid) {
+       c.typ = mapping[c.typ]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectNewConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ)
+}
+
+func (c *reflectNewConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               typObj := nodeid(x)
+               T := a.rtypeTaggedValue(typObj)
+
+               // allocate new T object
+               newObj := a.nextNode()
+               a.addNodes(T, "reflect.New")
+               a.endObject(newObj, c.cgn, nil)
+
+               // put its address in a new *T-tagged object
+               id := a.makeTagged(types.NewPointer(T), c.cgn, nil)
+               a.addLabel(id+1, newObj)
+
+               // flow the pointer to the result
+               if a.addLabel(c.result, id) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰New(a *analysis, cgn *cgnode) {
+       a.addConstraint(&reflectNewConstraint{
+               cgn:    cgn,
+               typ:    a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+func ext۰reflect۰NewAt(a *analysis, cgn *cgnode) {
+       ext۰reflect۰New(a, cgn)
+
+       // TODO(adonovan): also report dynamic calls to unsound intrinsics.
+       if site := cgn.callersite; site != nil {
+               a.warnf(site.pos(), "unsound: %s contains a reflect.NewAt() call", site.instr.Parent())
+       }
+}
+
+// ---------- func PtrTo(Type) Type ----------
+
+// result = PtrTo(t)
+type reflectPtrToConstraint struct {
+       cgn    *cgnode
+       t      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *reflectPtrToConstraint) ptr() nodeid { return c.t }
+func (c *reflectPtrToConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectPtrTo.result")
+}
+func (c *reflectPtrToConstraint) renumber(mapping []nodeid) {
+       c.t = mapping[c.t]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectPtrToConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.PtrTo(n%d)", c.result, c.t)
+}
+
+func (c *reflectPtrToConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               tObj := nodeid(x)
+               T := a.rtypeTaggedValue(tObj)
+
+               if typeTooHigh(T) {
+                       continue
+               }
+
+               if a.addLabel(c.result, a.makeRtype(types.NewPointer(T))) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode) {
+       a.addConstraint(&reflectPtrToConstraint{
+               cgn:    cgn,
+               t:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+func ext۰reflect۰Select(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func SliceOf(Type) Type ----------
+
+// result = SliceOf(t)
+type reflectSliceOfConstraint struct {
+       cgn    *cgnode
+       t      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *reflectSliceOfConstraint) ptr() nodeid { return c.t }
+func (c *reflectSliceOfConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectSliceOf.result")
+}
+func (c *reflectSliceOfConstraint) renumber(mapping []nodeid) {
+       c.t = mapping[c.t]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectSliceOfConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.SliceOf(n%d)", c.result, c.t)
+}
+
+func (c *reflectSliceOfConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               tObj := nodeid(x)
+               T := a.rtypeTaggedValue(tObj)
+
+               if typeTooHigh(T) {
+                       continue
+               }
+
+               if a.addLabel(c.result, a.makeRtype(types.NewSlice(T))) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) {
+       a.addConstraint(&reflectSliceOfConstraint{
+               cgn:    cgn,
+               t:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func TypeOf(v Value) Type ----------
+
+// result = TypeOf(i)
+type reflectTypeOfConstraint struct {
+       cgn    *cgnode
+       i      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *reflectTypeOfConstraint) ptr() nodeid { return c.i }
+func (c *reflectTypeOfConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectTypeOf.result")
+}
+func (c *reflectTypeOfConstraint) renumber(mapping []nodeid) {
+       c.i = mapping[c.i]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectTypeOfConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i)
+}
+
+func (c *reflectTypeOfConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               iObj := nodeid(x)
+               tDyn, _, _ := a.taggedValue(iObj)
+               if a.addLabel(c.result, a.makeRtype(tDyn)) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) {
+       a.addConstraint(&reflectTypeOfConstraint{
+               cgn:    cgn,
+               i:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func ValueOf(interface{}) Value ----------
+
+func ext۰reflect۰ValueOf(a *analysis, cgn *cgnode) {
+       // TODO(adonovan): when we start creating indirect tagged
+       // objects, we'll need to handle them specially here since
+       // they must never appear in the PTS of an interface{}.
+       a.copy(a.funcResults(cgn.obj), a.funcParams(cgn.obj), 1)
+}
+
+// ---------- func Zero(Type) Value ----------
+
+// result = Zero(typ)
+type reflectZeroConstraint struct {
+       cgn    *cgnode
+       typ    nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *reflectZeroConstraint) ptr() nodeid { return c.typ }
+func (c *reflectZeroConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "reflectZero.result")
+}
+func (c *reflectZeroConstraint) renumber(mapping []nodeid) {
+       c.typ = mapping[c.typ]
+       c.result = mapping[c.result]
+}
+
+func (c *reflectZeroConstraint) String() string {
+       return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ)
+}
+
+func (c *reflectZeroConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               typObj := nodeid(x)
+               T := a.rtypeTaggedValue(typObj)
+
+               // 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 objects
+               // that marks whether they are addressable or
+               // readonly, just like the reflect package does.
+
+               // memoize using a.reflectZeros[T]
+               var id nodeid
+               if z := a.reflectZeros.At(T); false && z != nil {
+                       id = z.(nodeid)
+               } else {
+                       id = a.makeTagged(T, c.cgn, nil)
+                       a.reflectZeros.Set(T, id)
+               }
+               if a.addLabel(c.result, id) {
+                       changed = true
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰Zero(a *analysis, cgn *cgnode) {
+       a.addConstraint(&reflectZeroConstraint{
+               cgn:    cgn,
+               typ:    a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// -------------------- (*reflect.rtype) methods --------------------
+
+// ---------- func (*rtype) Elem() Type ----------
+
+// result = Elem(t)
+type rtypeElemConstraint struct {
+       cgn    *cgnode
+       t      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rtypeElemConstraint) ptr() nodeid { return c.t }
+func (c *rtypeElemConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rtypeElem.result")
+}
+func (c *rtypeElemConstraint) renumber(mapping []nodeid) {
+       c.t = mapping[c.t]
+       c.result = mapping[c.result]
+}
+
+func (c *rtypeElemConstraint) String() string {
+       return fmt.Sprintf("n%d = (*reflect.rtype).Elem(n%d)", c.result, c.t)
+}
+
+func (c *rtypeElemConstraint) solve(a *analysis, delta *nodeset) {
+       // Implemented by *types.{Map,Chan,Array,Slice,Pointer}.
+       type hasElem interface {
+               Elem() types.Type
+       }
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               tObj := nodeid(x)
+               T := a.nodes[tObj].obj.data.(types.Type)
+               if tHasElem, ok := T.Underlying().(hasElem); ok {
+                       if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) {
+                               changed = true
+                       }
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰rtype۰Elem(a *analysis, cgn *cgnode) {
+       a.addConstraint(&rtypeElemConstraint{
+               cgn:    cgn,
+               t:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func (*rtype) Field(int) StructField ----------
+// ---------- func (*rtype) FieldByName(string) (StructField, bool) ----------
+
+// result = FieldByName(t, name)
+// result = Field(t, _)
+type rtypeFieldByNameConstraint struct {
+       cgn    *cgnode
+       name   string // name of field; "" for unknown
+       t      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rtypeFieldByNameConstraint) ptr() nodeid { return c.t }
+func (c *rtypeFieldByNameConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result+3), "rtypeFieldByName.result.Type")
+}
+func (c *rtypeFieldByNameConstraint) renumber(mapping []nodeid) {
+       c.t = mapping[c.t]
+       c.result = mapping[c.result]
+}
+
+func (c *rtypeFieldByNameConstraint) String() string {
+       return fmt.Sprintf("n%d = (*reflect.rtype).FieldByName(n%d, %q)", c.result, c.t, c.name)
+}
+
+func (c *rtypeFieldByNameConstraint) solve(a *analysis, delta *nodeset) {
+       // type StructField struct {
+       // 0    __identity__
+       // 1    Name      string
+       // 2    PkgPath   string
+       // 3    Type      Type
+       // 4    Tag       StructTag
+       // 5    Offset    uintptr
+       // 6    Index     []int
+       // 7    Anonymous bool
+       // }
+
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               tObj := nodeid(x)
+               T := a.nodes[tObj].obj.data.(types.Type)
+               tStruct, ok := T.Underlying().(*types.Struct)
+               if !ok {
+                       continue // not a struct type
+               }
+
+               n := tStruct.NumFields()
+               for i := 0; i < n; i++ {
+                       f := tStruct.Field(i)
+                       if c.name == "" || c.name == f.Name() {
+
+                               // a.offsetOf(Type) is 3.
+                               if id := c.result + 3; a.addLabel(id, a.makeRtype(f.Type())) {
+                                       a.addWork(id)
+                               }
+                               // TODO(adonovan): StructField.Index should be non-nil.
+                       }
+               }
+       }
+}
+
+func ext۰reflect۰rtype۰FieldByName(a *analysis, cgn *cgnode) {
+       // If we have access to the callsite,
+       // and the argument is a string constant,
+       // return only that field.
+       var name string
+       if site := cgn.callersite; site != nil {
+               if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
+                       name = constant.StringVal(c.Value)
+               }
+       }
+
+       a.addConstraint(&rtypeFieldByNameConstraint{
+               cgn:    cgn,
+               name:   name,
+               t:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+func ext۰reflect۰rtype۰Field(a *analysis, cgn *cgnode) {
+       // No-one ever calls Field with a constant argument,
+       // so we don't specialize that case.
+       a.addConstraint(&rtypeFieldByNameConstraint{
+               cgn:    cgn,
+               t:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode)    {} // TODO(adonovan)
+func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {} // TODO(adonovan)
+
+// ---------- func (*rtype) In/Out(i int) Type ----------
+
+// result = In/Out(t, i)
+type rtypeInOutConstraint struct {
+       cgn    *cgnode
+       t      nodeid // (ptr)
+       result nodeid // (indirect)
+       out    bool
+       i      int // -ve if not a constant
+}
+
+func (c *rtypeInOutConstraint) ptr() nodeid { return c.t }
+func (c *rtypeInOutConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rtypeInOut.result")
+}
+func (c *rtypeInOutConstraint) renumber(mapping []nodeid) {
+       c.t = mapping[c.t]
+       c.result = mapping[c.result]
+}
+
+func (c *rtypeInOutConstraint) String() string {
+       return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d, %d)", c.result, c.t, c.i)
+}
+
+func (c *rtypeInOutConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               tObj := nodeid(x)
+               T := a.nodes[tObj].obj.data.(types.Type)
+               sig, ok := T.Underlying().(*types.Signature)
+               if !ok {
+                       continue // not a func type
+               }
+
+               tuple := sig.Params()
+               if c.out {
+                       tuple = sig.Results()
+               }
+               for i, n := 0, tuple.Len(); i < n; i++ {
+                       if c.i < 0 || c.i == i {
+                               if a.addLabel(c.result, a.makeRtype(tuple.At(i).Type())) {
+                                       changed = true
+                               }
+                       }
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+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, _ := constant.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,
+       })
+}
+
+func ext۰reflect۰rtype۰In(a *analysis, cgn *cgnode) {
+       ext۰reflect۰rtype۰InOut(a, cgn, false)
+}
+
+func ext۰reflect۰rtype۰Out(a *analysis, cgn *cgnode) {
+       ext۰reflect۰rtype۰InOut(a, cgn, true)
+}
+
+// ---------- func (*rtype) Key() Type ----------
+
+// result = Key(t)
+type rtypeKeyConstraint struct {
+       cgn    *cgnode
+       t      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rtypeKeyConstraint) ptr() nodeid { return c.t }
+func (c *rtypeKeyConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result), "rtypeKey.result")
+}
+func (c *rtypeKeyConstraint) renumber(mapping []nodeid) {
+       c.t = mapping[c.t]
+       c.result = mapping[c.result]
+}
+
+func (c *rtypeKeyConstraint) String() string {
+       return fmt.Sprintf("n%d = (*reflect.rtype).Key(n%d)", c.result, c.t)
+}
+
+func (c *rtypeKeyConstraint) solve(a *analysis, delta *nodeset) {
+       changed := false
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               tObj := nodeid(x)
+               T := a.nodes[tObj].obj.data.(types.Type)
+               if tMap, ok := T.Underlying().(*types.Map); ok {
+                       if a.addLabel(c.result, a.makeRtype(tMap.Key())) {
+                               changed = true
+                       }
+               }
+       }
+       if changed {
+               a.addWork(c.result)
+       }
+}
+
+func ext۰reflect۰rtype۰Key(a *analysis, cgn *cgnode) {
+       a.addConstraint(&rtypeKeyConstraint{
+               cgn:    cgn,
+               t:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// ---------- func (*rtype) Method(int) (Method, bool) ----------
+// ---------- func (*rtype) MethodByName(string) (Method, bool) ----------
+
+// result = MethodByName(t, name)
+// result = Method(t, _)
+type rtypeMethodByNameConstraint struct {
+       cgn    *cgnode
+       name   string // name of method; "" for unknown
+       t      nodeid // (ptr)
+       result nodeid // (indirect)
+}
+
+func (c *rtypeMethodByNameConstraint) ptr() nodeid { return c.t }
+func (c *rtypeMethodByNameConstraint) presolve(h *hvn) {
+       h.markIndirect(onodeid(c.result+3), "rtypeMethodByName.result.Type")
+       h.markIndirect(onodeid(c.result+4), "rtypeMethodByName.result.Func")
+}
+func (c *rtypeMethodByNameConstraint) renumber(mapping []nodeid) {
+       c.t = mapping[c.t]
+       c.result = mapping[c.result]
+}
+
+func (c *rtypeMethodByNameConstraint) String() string {
+       return fmt.Sprintf("n%d = (*reflect.rtype).MethodByName(n%d, %q)", c.result, c.t, c.name)
+}
+
+// changeRecv returns sig with Recv prepended to Params().
+func changeRecv(sig *types.Signature) *types.Signature {
+       params := sig.Params()
+       n := params.Len()
+       p2 := make([]*types.Var, n+1)
+       p2[0] = sig.Recv()
+       for i := 0; i < n; i++ {
+               p2[i+1] = params.At(i)
+       }
+       return types.NewSignature(nil, types.NewTuple(p2...), sig.Results(), sig.Variadic())
+}
+
+func (c *rtypeMethodByNameConstraint) solve(a *analysis, delta *nodeset) {
+       for _, x := range delta.AppendTo(a.deltaSpace) {
+               tObj := nodeid(x)
+               T := a.nodes[tObj].obj.data.(types.Type)
+
+               isIface := isInterface(T)
+
+               // We don't use Lookup(c.name) when c.name != "" to avoid
+               // ambiguity: >1 unexported methods could match.
+               mset := a.prog.MethodSets.MethodSet(T)
+               for i, n := 0, mset.Len(); i < n; i++ {
+                       sel := mset.At(i)
+                       if c.name == "" || c.name == sel.Obj().Name() {
+                               // type Method struct {
+                               // 0     __identity__
+                               // 1    Name    string
+                               // 2    PkgPath string
+                               // 3    Type    Type
+                               // 4    Func    Value
+                               // 5    Index   int
+                               // }
+
+                               var sig *types.Signature
+                               var fn *ssa.Function
+                               if isIface {
+                                       sig = sel.Type().(*types.Signature)
+                               } else {
+                                       fn = a.prog.MethodValue(sel)
+                                       // move receiver to params[0]
+                                       sig = changeRecv(fn.Signature)
+                               }
+
+                               // a.offsetOf(Type) is 3.
+                               if id := c.result + 3; a.addLabel(id, a.makeRtype(sig)) {
+                                       a.addWork(id)
+                               }
+                               if fn != nil {
+                                       // a.offsetOf(Func) is 4.
+                                       if id := c.result + 4; a.addLabel(id, a.objectNode(nil, fn)) {
+                                               a.addWork(id)
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) {
+       // If we have access to the callsite,
+       // and the argument is a string constant,
+       // return only that method.
+       var name string
+       if site := cgn.callersite; site != nil {
+               if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok {
+                       name = constant.StringVal(c.Value)
+               }
+       }
+
+       a.addConstraint(&rtypeMethodByNameConstraint{
+               cgn:    cgn,
+               name:   name,
+               t:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode) {
+       // No-one ever calls Method with a constant argument,
+       // so we don't specialize that case.
+       a.addConstraint(&rtypeMethodByNameConstraint{
+               cgn:    cgn,
+               t:      a.funcParams(cgn.obj),
+               result: a.funcResults(cgn.obj),
+       })
+}
+
+// typeHeight returns the "height" of the type, which is roughly
+// speaking the number of chan, map, pointer and slice type constructors
+// at the root of T; these are the four type kinds that can be created
+// via reflection.  Chan and map constructors are counted as double the
+// height of slice and pointer constructors since they are less often
+// deeply nested.
+//
+// The solver rules for type constructors must somehow bound the set of
+// types they create to ensure termination of the algorithm in cases
+// where the output of a type constructor flows to its input, e.g.
+//
+//     func f(t reflect.Type) {
+//             f(reflect.PtrTo(t))
+//     }
+//
+// It does this by limiting the type height to k, but this still leaves
+// a potentially exponential (4^k) number of of types that may be
+// enumerated in pathological cases.
+//
+func typeHeight(T types.Type) int {
+       switch T := T.(type) {
+       case *types.Chan:
+               return 2 + typeHeight(T.Elem())
+       case *types.Map:
+               k := typeHeight(T.Key())
+               v := typeHeight(T.Elem())
+               if v > k {
+                       k = v // max(k, v)
+               }
+               return 2 + k
+       case *types.Slice:
+               return 1 + typeHeight(T.Elem())
+       case *types.Pointer:
+               return 1 + typeHeight(T.Elem())
+       }
+       return 0
+}
+
+func typeTooHigh(T types.Type) bool {
+       return typeHeight(T) > 3
+}