go.tools/pointer: reflect, part 2: channels.
(reflect.Value).Send
        (reflect.Value).TrySend
        (reflect.Value).Recv
        (reflect.Value).TryRecv
        (reflect.Type).ChanOf
        (reflect.Type).In
        (reflect.Type).Out
        reflect.Indirect
        reflect.MakeChan
Also:
- specialize genInvoke when the receiver is a reflect.Type under the
  assumption that there's only one possible concrete type.  This
  makes all reflect.Type operations context-sensitive since the calls
  are no longer dynamic.
- Rename all variables to match the actual parameter names used in
  the reflect API.
- Add pointer.Config.Reflection flag
  (exposed in oracle as --reflect, default false) to enable reflection.
  It currently adds about 20% running time.  I'll make it true after
  the presolver is implemented.
- Simplified worklist datatype and solver main loop slightly
  (~10% speed improvement).
- Use addLabel() utility to add a label to a PTS.
(Working on my 3 yr old 2x2GHz+4GB Mac vs 8x4GHz+24GB workstation,
one really notices the cost of pointer analysis.
Note to self: time to implement presolver.)
R=crawshaw
CC=golang-dev
https://golang.org/cl/13242062
			
			
This commit is contained in:
		
							parent
							
								
									25a0cc4bfd
								
							
						
					
					
						commit
						3371b79a96
					
				| 
						 | 
					@ -41,6 +41,9 @@ var ptalogFlag = flag.String("ptalog", "",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var formatFlag = flag.String("format", "plain", "Output format: 'plain' or 'json'.")
 | 
					var formatFlag = flag.String("format", "plain", "Output format: 'plain' or 'json'.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO(adonovan): eliminate or flip this flag after PTA presolver is implemented.
 | 
				
			||||||
 | 
					var reflectFlag = flag.Bool("reflect", true, "Analyze reflection soundly (slow).")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const useHelp = "Run 'oracle -help' for more information.\n"
 | 
					const useHelp = "Run 'oracle -help' for more information.\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const helpMessage = `Go source code oracle.
 | 
					const helpMessage = `Go source code oracle.
 | 
				
			||||||
| 
						 | 
					@ -68,8 +71,8 @@ The user manual is available here:  http://golang.org/s/oracle-user-manual
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Examples:
 | 
					Examples:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Describe the syntax at offset 670 in this file (an import spec):
 | 
					Describe the syntax at offset 530 in this file (an import spec):
 | 
				
			||||||
% oracle -mode=describe -pos=src/code.google.com/p/go.tools/cmd/oracle/main.go:#670 \
 | 
					% oracle -mode=describe -pos=src/code.google.com/p/go.tools/cmd/oracle/main.go:#530 \
 | 
				
			||||||
   code.google.com/p/go.tools/cmd/oracle
 | 
					   code.google.com/p/go.tools/cmd/oracle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Print the callgraph of the trivial web-server in JSON format:
 | 
					Print the callgraph of the trivial web-server in JSON format:
 | 
				
			||||||
| 
						 | 
					@ -149,7 +152,7 @@ func main() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Ask the oracle.
 | 
						// Ask the oracle.
 | 
				
			||||||
	res, err := oracle.Query(args, *modeFlag, *posFlag, ptalog, &build.Default)
 | 
						res, err := oracle.Query(args, *modeFlag, *posFlag, ptalog, &build.Default, *reflectFlag)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Fprintf(os.Stderr, "%s\n"+useHelp, err)
 | 
							fmt.Fprintf(os.Stderr, "%s\n"+useHelp, err)
 | 
				
			||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,7 +43,7 @@ import (
 | 
				
			||||||
type Oracle struct {
 | 
					type Oracle struct {
 | 
				
			||||||
	out    io.Writer      // standard output
 | 
						out    io.Writer      // standard output
 | 
				
			||||||
	prog   *ssa.Program   // the SSA program [only populated if need&SSA]
 | 
						prog   *ssa.Program   // the SSA program [only populated if need&SSA]
 | 
				
			||||||
	config pointer.Config // pointer analysis configuration
 | 
						config pointer.Config // pointer analysis configuration [TODO rename ptaConfig]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// need&AllTypeInfo
 | 
						// need&AllTypeInfo
 | 
				
			||||||
	typeInfo map[*types.Package]*importer.PackageInfo // type info for all ASTs in the program
 | 
						typeInfo map[*types.Package]*importer.PackageInfo // type info for all ASTs in the program
 | 
				
			||||||
| 
						 | 
					@ -149,6 +149,7 @@ func (res *Result) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
// mode is the query mode ("callers", etc).
 | 
					// mode is the query mode ("callers", etc).
 | 
				
			||||||
// ptalog is the (optional) pointer-analysis log file.
 | 
					// ptalog is the (optional) pointer-analysis log file.
 | 
				
			||||||
// buildContext is the go/build configuration for locating packages.
 | 
					// buildContext is the go/build configuration for locating packages.
 | 
				
			||||||
 | 
					// reflection determines whether to model reflection soundly (currently slow).
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Clients that intend to perform multiple queries against the same
 | 
					// Clients that intend to perform multiple queries against the same
 | 
				
			||||||
// analysis scope should use this pattern instead:
 | 
					// analysis scope should use this pattern instead:
 | 
				
			||||||
| 
						 | 
					@ -169,14 +170,14 @@ func (res *Result) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
// TODO(adonovan): the ideal 'needsExact' parameter for ParseQueryPos
 | 
					// TODO(adonovan): the ideal 'needsExact' parameter for ParseQueryPos
 | 
				
			||||||
// depends on the query mode; how should we expose this?
 | 
					// depends on the query mode; how should we expose this?
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *build.Context) (*Result, error) {
 | 
					func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *build.Context, reflection bool) (*Result, error) {
 | 
				
			||||||
	minfo := findMode(mode)
 | 
						minfo := findMode(mode)
 | 
				
			||||||
	if minfo == nil {
 | 
						if minfo == nil {
 | 
				
			||||||
		return nil, fmt.Errorf("invalid mode type: %q", mode)
 | 
							return nil, fmt.Errorf("invalid mode type: %q", mode)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	imp := importer.New(&importer.Config{Build: buildContext})
 | 
						imp := importer.New(&importer.Config{Build: buildContext})
 | 
				
			||||||
	o, err := New(imp, args, ptalog)
 | 
						o, err := New(imp, args, ptalog, reflection)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -216,17 +217,19 @@ func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *buil
 | 
				
			||||||
// args specify the main package in importer.CreatePackageFromArgs syntax.
 | 
					// args specify the main package in importer.CreatePackageFromArgs syntax.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// ptalog is the (optional) pointer-analysis log file.
 | 
					// ptalog is the (optional) pointer-analysis log file.
 | 
				
			||||||
 | 
					// reflection determines whether to model reflection soundly (currently slow).
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func New(imp *importer.Importer, args []string, ptalog io.Writer) (*Oracle, error) {
 | 
					func New(imp *importer.Importer, args []string, ptalog io.Writer, reflection bool) (*Oracle, error) {
 | 
				
			||||||
	return newOracle(imp, args, ptalog, needAll)
 | 
						return newOracle(imp, args, ptalog, needAll, reflection)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newOracle(imp *importer.Importer, args []string, ptalog io.Writer, needs int) (*Oracle, error) {
 | 
					func newOracle(imp *importer.Importer, args []string, ptalog io.Writer, needs int, reflection bool) (*Oracle, error) {
 | 
				
			||||||
	o := &Oracle{
 | 
						o := &Oracle{
 | 
				
			||||||
		prog:   ssa.NewProgram(imp.Fset, 0),
 | 
							prog:   ssa.NewProgram(imp.Fset, 0),
 | 
				
			||||||
		timers: make(map[string]time.Duration),
 | 
							timers: make(map[string]time.Duration),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	o.config.Log = ptalog
 | 
						o.config.Log = ptalog
 | 
				
			||||||
 | 
						o.config.Reflection = reflection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Load/parse/type-check program from args.
 | 
						// Load/parse/type-check program from args.
 | 
				
			||||||
	start := time.Now()
 | 
						start := time.Now()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -165,7 +165,9 @@ func doQuery(out io.Writer, q *query, useJson bool) {
 | 
				
			||||||
	res, err := oracle.Query([]string{q.filename},
 | 
						res, err := oracle.Query([]string{q.filename},
 | 
				
			||||||
		q.verb,
 | 
							q.verb,
 | 
				
			||||||
		fmt.Sprintf("%s:#%d,#%d", q.filename, q.start, q.end),
 | 
							fmt.Sprintf("%s:#%d,#%d", q.filename, q.start, q.end),
 | 
				
			||||||
		/*PTA-log=*/ nil, &buildContext)
 | 
							nil, // ptalog,
 | 
				
			||||||
 | 
							&buildContext,
 | 
				
			||||||
 | 
							true) // reflection
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Fprintf(out, "\nError: %s\n", stripLocation(err.Error()))
 | 
							fmt.Fprintf(out, "\nError: %s\n", stripLocation(err.Error()))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -259,7 +261,7 @@ func TestMultipleQueries(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Oracle
 | 
						// Oracle
 | 
				
			||||||
	filename := "testdata/src/main/multi.go"
 | 
						filename := "testdata/src/main/multi.go"
 | 
				
			||||||
	o, err := oracle.New(imp, []string{filename}, nil)
 | 
						o, err := oracle.New(imp, []string{filename}, nil, true)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("oracle.New failed: %s", err)
 | 
							t.Fatalf("oracle.New failed: %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,6 @@ CONSTRAINT GENERATION:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PRESOLVER OPTIMISATIONS
 | 
					PRESOLVER OPTIMISATIONS
 | 
				
			||||||
- use HVN, HRU, LE, PE, HCD, LCD.
 | 
					- use HVN, HRU, LE, PE, HCD, LCD.
 | 
				
			||||||
  But: LE would lose the precise detail we currently enjoy in each label.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
SOLVER:
 | 
					SOLVER:
 | 
				
			||||||
- use BDDs and/or sparse bitvectors for ptsets
 | 
					- use BDDs and/or sparse bitvectors for ptsets
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -185,7 +185,8 @@ type analysis struct {
 | 
				
			||||||
	hasher          typemap.Hasher // cache of type hashes
 | 
						hasher          typemap.Hasher // cache of type hashes
 | 
				
			||||||
	reflectValueObj types.Object   // type symbol for reflect.Value (if present)
 | 
						reflectValueObj types.Object   // type symbol for reflect.Value (if present)
 | 
				
			||||||
	reflectRtypeObj types.Object   // *types.TypeName for reflect.rtype (if present)
 | 
						reflectRtypeObj types.Object   // *types.TypeName for reflect.rtype (if present)
 | 
				
			||||||
	reflectRtype    *types.Pointer // *reflect.rtype
 | 
						reflectRtypePtr *types.Pointer // *reflect.rtype
 | 
				
			||||||
 | 
						reflectType     *types.Named   // reflect.Type
 | 
				
			||||||
	rtypes          typemap.M      // nodeid of canonical *rtype-tagged object for type T
 | 
						rtypes          typemap.M      // nodeid of canonical *rtype-tagged object for type T
 | 
				
			||||||
	reflectZeros    typemap.M      // nodeid of canonical T-tagged object for zero value
 | 
						reflectZeros    typemap.M      // nodeid of canonical T-tagged object for zero value
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -244,8 +245,9 @@ func Analyze(config *Config) CallGraphNode {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
 | 
						if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
 | 
				
			||||||
		a.reflectValueObj = reflect.Object.Scope().Lookup("Value")
 | 
							a.reflectValueObj = reflect.Object.Scope().Lookup("Value")
 | 
				
			||||||
 | 
							a.reflectType = reflect.Object.Scope().Lookup("Type").Type().(*types.Named)
 | 
				
			||||||
		a.reflectRtypeObj = reflect.Object.Scope().Lookup("rtype")
 | 
							a.reflectRtypeObj = reflect.Object.Scope().Lookup("rtype")
 | 
				
			||||||
		a.reflectRtype = types.NewPointer(a.reflectRtypeObj.Type())
 | 
							a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Override flattening of reflect.Value, treating it like a basic type.
 | 
							// Override flattening of reflect.Value, treating it like a basic type.
 | 
				
			||||||
		tReflectValue := a.reflectValueObj.Type()
 | 
							tReflectValue := a.reflectValueObj.Type()
 | 
				
			||||||
| 
						 | 
					@ -265,11 +267,7 @@ func Analyze(config *Config) CallGraphNode {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	root := a.generate()
 | 
						root := a.generate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// ---------- Presolver ----------
 | 
						//a.optimize()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// TODO(adonovan): opt: presolver optimisations.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// ---------- Solver ----------
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	a.solve()
 | 
						a.solve()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,13 @@ type Config struct {
 | 
				
			||||||
	Mains []*ssa.Package // set of 'main' packages to analyze
 | 
						Mains []*ssa.Package // set of 'main' packages to analyze
 | 
				
			||||||
	root  *ssa.Function  // synthetic analysis root
 | 
						root  *ssa.Function  // synthetic analysis root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Reflection determines whether to handle reflection
 | 
				
			||||||
 | 
						// operators soundly, which is currently rather slow since it
 | 
				
			||||||
 | 
						// causes constraint to be generated during solving
 | 
				
			||||||
 | 
						// proportional to the number of constraint variables, which
 | 
				
			||||||
 | 
						// has not yet been reduced by presolver optimisation.
 | 
				
			||||||
 | 
						Reflection bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// -------- Optional callbacks invoked by the analysis --------
 | 
						// -------- Optional callbacks invoked by the analysis --------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Call is invoked for each discovered call-graph edge.  The
 | 
						// Call is invoked for each discovered call-graph edge.  The
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,11 +10,13 @@ pointer analysis algorithm first described in (Andersen, 1994).
 | 
				
			||||||
The implementation is similar to that described in (Pearce et al,
 | 
					The implementation is similar to that described in (Pearce et al,
 | 
				
			||||||
PASTE'04).  Unlike many algorithms which interleave constraint
 | 
					PASTE'04).  Unlike many algorithms which interleave constraint
 | 
				
			||||||
generation and solving, constructing the callgraph as they go, this
 | 
					generation and solving, constructing the callgraph as they go, this
 | 
				
			||||||
implementation has a strict phase ordering: generation before solving.
 | 
					implementation for the most part observes a phase ordering (generation
 | 
				
			||||||
Only simple (copy) constraints may be generated during solving.  This
 | 
					before solving), with only simple (copy) constraints being generated
 | 
				
			||||||
improves the traction of presolver optimisations, but imposes certain
 | 
					during solving.  (The exception is reflection, which creates various
 | 
				
			||||||
restrictions, e.g. potential context sensitivity is limited since all
 | 
					constraints during solving as new types flow to reflect.Value
 | 
				
			||||||
variants must be created a priori.
 | 
					operations.)  This improves the traction of presolver optimisations,
 | 
				
			||||||
 | 
					but imposes certain restrictions, e.g. potential context sensitivity
 | 
				
			||||||
 | 
					is limited since all variants must be created a priori.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We intend to add various presolving optimisations such as Pointer and
 | 
					We intend to add various presolving optimisations such as Pointer and
 | 
				
			||||||
Location Equivalence from (Hardekopf & Lin, SAS '07) and solver
 | 
					Location Equivalence from (Hardekopf & Lin, SAS '07) and solver
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -235,7 +235,7 @@ func (a *analysis) makeRtype(T types.Type) nodeid {
 | 
				
			||||||
	a.addOneNode(T, "reflect.rtype", nil)
 | 
						a.addOneNode(T, "reflect.rtype", nil)
 | 
				
			||||||
	a.endObject(obj, nil, nil).rtype = T
 | 
						a.endObject(obj, nil, nil).rtype = T
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id := a.makeTagged(a.reflectRtype, nil, nil)
 | 
						id := a.makeTagged(a.reflectRtypePtr, nil, nil)
 | 
				
			||||||
	a.nodes[id].obj.rtype = T
 | 
						a.nodes[id].obj.rtype = T
 | 
				
			||||||
	a.nodes[id+1].typ = T // trick (each *rtype tagged object is a singleton)
 | 
						a.nodes[id+1].typ = T // trick (each *rtype tagged object is a singleton)
 | 
				
			||||||
	a.addressOf(id+1, obj)
 | 
						a.addressOf(id+1, obj)
 | 
				
			||||||
| 
						 | 
					@ -244,6 +244,15 @@ func (a *analysis) makeRtype(T types.Type) nodeid {
 | 
				
			||||||
	return id
 | 
						return id
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// rtypeValue returns the type of the *reflect.rtype-tagged object obj.
 | 
				
			||||||
 | 
					func (a *analysis) rtypeTaggedValue(obj nodeid) types.Type {
 | 
				
			||||||
 | 
						tDyn, t, _ := a.taggedValue(obj)
 | 
				
			||||||
 | 
						if tDyn != a.reflectRtypePtr {
 | 
				
			||||||
 | 
							panic(fmt.Sprintf("not a *reflect.rtype-tagged value: obj=n%d tag=%v payload=n%d", obj, tDyn, t))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return a.nodes[t].typ
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// valueNode returns the id of the value node for v, creating it (and
 | 
					// valueNode returns the id of the value node for v, creating it (and
 | 
				
			||||||
// the association) as needed.  It may return zero for uninteresting
 | 
					// the association) as needed.  It may return zero for uninteresting
 | 
				
			||||||
// values containing no pointers.
 | 
					// values containing no pointers.
 | 
				
			||||||
| 
						 | 
					@ -432,6 +441,11 @@ func (a *analysis) offsetAddr(dst, src nodeid, offset uint32) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// typeAssert creates a typeAssert constraint of the form dst = src.(T).
 | 
				
			||||||
 | 
					func (a *analysis) typeAssert(T types.Type, dst, src nodeid) {
 | 
				
			||||||
 | 
						a.addConstraint(&typeAssertConstraint{T, dst, src})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// addConstraint adds c to the constraint set.
 | 
					// addConstraint adds c to the constraint set.
 | 
				
			||||||
func (a *analysis) addConstraint(c constraint) {
 | 
					func (a *analysis) addConstraint(c constraint) {
 | 
				
			||||||
	a.constraints = append(a.constraints, c)
 | 
						a.constraints = append(a.constraints, c)
 | 
				
			||||||
| 
						 | 
					@ -720,13 +734,11 @@ func (a *analysis) genDynamicCall(call *ssa.CallCommon, result nodeid) nodeid {
 | 
				
			||||||
// It returns a node whose pts() will be the set of possible call targets.
 | 
					// It returns a node whose pts() will be the set of possible call targets.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func (a *analysis) genInvoke(call *ssa.CallCommon, result nodeid) nodeid {
 | 
					func (a *analysis) genInvoke(call *ssa.CallCommon, result nodeid) nodeid {
 | 
				
			||||||
	sig := call.Signature()
 | 
						if call.Value.Type() == a.reflectType {
 | 
				
			||||||
 | 
							return a.genInvokeReflectType(call, result)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO(adonovan): optimise this into a static call when there
 | 
						sig := call.Signature()
 | 
				
			||||||
	// can be at most one type that implements the interface (due
 | 
					 | 
				
			||||||
	// to unexported methods).  This is particularly important for
 | 
					 | 
				
			||||||
	// methods of interface reflect.Type (sole impl:
 | 
					 | 
				
			||||||
	// *reflect.rtype), so we can realize context sensitivity.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Allocate a contiguous targets/params/results block for this call.
 | 
						// Allocate a contiguous targets/params/results block for this call.
 | 
				
			||||||
	block := a.nextNode()
 | 
						block := a.nextNode()
 | 
				
			||||||
| 
						 | 
					@ -753,6 +765,63 @@ func (a *analysis) genInvoke(call *ssa.CallCommon, result nodeid) nodeid {
 | 
				
			||||||
	return targets
 | 
						return targets
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// genInvokeReflectType is a specialization of genInvoke where the
 | 
				
			||||||
 | 
					// receiver type is a reflect.Type, under the assumption that there
 | 
				
			||||||
 | 
					// can be at most one implementation of this interface, *reflect.rtype.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// (Though this may appear to be an instance of a pattern---method
 | 
				
			||||||
 | 
					// calls on interfaces known to have exactly one implementation---in
 | 
				
			||||||
 | 
					// practice it occurs rarely, so we special case for reflect.Type.)
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In effect we treat this:
 | 
				
			||||||
 | 
					//    var rt reflect.Type = ...
 | 
				
			||||||
 | 
					//    rt.F()
 | 
				
			||||||
 | 
					// as this:
 | 
				
			||||||
 | 
					//    rt.(*reflect.rtype).F()
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// It returns a node whose pts() will be the (singleton) set of
 | 
				
			||||||
 | 
					// possible call targets.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					func (a *analysis) genInvokeReflectType(call *ssa.CallCommon, result nodeid) nodeid {
 | 
				
			||||||
 | 
						// Unpack receiver into rtype
 | 
				
			||||||
 | 
						rtype := a.addOneNode(a.reflectRtypePtr, "rtype.recv", nil)
 | 
				
			||||||
 | 
						recv := a.valueNode(call.Value)
 | 
				
			||||||
 | 
						a.typeAssert(a.reflectRtypePtr, rtype, recv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Look up the concrete method.
 | 
				
			||||||
 | 
						meth := a.reflectRtypePtr.MethodSet().Lookup(call.Method.Pkg(), call.Method.Name())
 | 
				
			||||||
 | 
						fn := a.prog.Method(meth)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						obj := a.makeFunctionObject(fn) // new contour for this call
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// From now on, it's essentially a static call, but little is
 | 
				
			||||||
 | 
						// gained by factoring together the code for both cases.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sig := fn.Signature // concrete method
 | 
				
			||||||
 | 
						targets := a.addOneNode(sig, "call.targets", nil)
 | 
				
			||||||
 | 
						a.addressOf(targets, obj) // (a singleton)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Copy receiver.
 | 
				
			||||||
 | 
						params := a.funcParams(obj)
 | 
				
			||||||
 | 
						a.copy(params, rtype, 1)
 | 
				
			||||||
 | 
						params++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Copy actual parameters into formal params block.
 | 
				
			||||||
 | 
						// Must loop, since the actuals aren't contiguous.
 | 
				
			||||||
 | 
						for i, arg := range call.Args {
 | 
				
			||||||
 | 
							sz := a.sizeof(sig.Params().At(i).Type())
 | 
				
			||||||
 | 
							a.copy(params, a.valueNode(arg), sz)
 | 
				
			||||||
 | 
							params += nodeid(sz)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Copy formal results block to actual result.
 | 
				
			||||||
 | 
						if result != 0 {
 | 
				
			||||||
 | 
							a.copy(result, a.funcResults(obj), a.sizeof(sig.Results()))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return obj
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// genCall generates contraints for call instruction instr.
 | 
					// genCall generates contraints for call instruction instr.
 | 
				
			||||||
func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) {
 | 
					func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) {
 | 
				
			||||||
	call := instr.Common()
 | 
						call := instr.Common()
 | 
				
			||||||
| 
						 | 
					@ -927,8 +996,7 @@ func (a *analysis) genInstr(cgn *cgnode, instr ssa.Instruction) {
 | 
				
			||||||
		a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
 | 
							a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case *ssa.TypeAssert:
 | 
						case *ssa.TypeAssert:
 | 
				
			||||||
		dst, src := a.valueNode(instr), a.valueNode(instr.X)
 | 
							a.typeAssert(instr.AssertedType, a.valueNode(instr), a.valueNode(instr.X))
 | 
				
			||||||
		a.addConstraint(&typeAssertConstraint{instr.AssertedType, dst, src})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case *ssa.Slice:
 | 
						case *ssa.Slice:
 | 
				
			||||||
		a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
 | 
							a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)
 | 
				
			||||||
| 
						 | 
					@ -1117,7 +1185,9 @@ func (a *analysis) generate() *cgnode {
 | 
				
			||||||
	a.panicNode = a.addNodes(tEface, "panic")
 | 
						a.panicNode = a.addNodes(tEface, "panic")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create nodes and constraints for all methods of reflect.rtype.
 | 
						// Create nodes and constraints for all methods of reflect.rtype.
 | 
				
			||||||
	if rtype := a.reflectRtype; rtype != nil {
 | 
						// (Shared contours are used by dynamic calls to reflect.Type
 | 
				
			||||||
 | 
						// methods---typically just String().)
 | 
				
			||||||
 | 
						if rtype := a.reflectRtypePtr; rtype != nil {
 | 
				
			||||||
		mset := rtype.MethodSet()
 | 
							mset := rtype.MethodSet()
 | 
				
			||||||
		for i, n := 0, mset.Len(); i < n; i++ {
 | 
							for i, n := 0, mset.Len(); i < n; i++ {
 | 
				
			||||||
			a.valueNode(a.prog.Method(mset.At(i)))
 | 
								a.valueNode(a.prog.Method(mset.At(i)))
 | 
				
			||||||
| 
						 | 
					@ -1135,9 +1205,5 @@ func (a *analysis) generate() *cgnode {
 | 
				
			||||||
		a.genFunc(cgn)
 | 
							a.genFunc(cgn)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create a dummy node to avoid out-of-range indexing in case
 | 
					 | 
				
			||||||
	// the last allocated type was of zero length.
 | 
					 | 
				
			||||||
	a.addNodes(tInvalid, "(max)")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return root
 | 
						return root
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,8 @@ func init() {
 | 
				
			||||||
		"(reflect.Value).OverflowInt":     ext۰NoEffect,
 | 
							"(reflect.Value).OverflowInt":     ext۰NoEffect,
 | 
				
			||||||
		"(reflect.Value).OverflowUint":    ext۰NoEffect,
 | 
							"(reflect.Value).OverflowUint":    ext۰NoEffect,
 | 
				
			||||||
		"(reflect.Value).Pointer":         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).Set":             ext۰reflect۰Value۰Set,
 | 
				
			||||||
		"(reflect.Value).SetBool":         ext۰NoEffect,
 | 
							"(reflect.Value).SetBool":         ext۰NoEffect,
 | 
				
			||||||
		"(reflect.Value).SetBytes":        ext۰reflect۰Value۰SetBytes,
 | 
							"(reflect.Value).SetBytes":        ext۰reflect۰Value۰SetBytes,
 | 
				
			||||||
| 
						 | 
					@ -83,6 +85,8 @@ func init() {
 | 
				
			||||||
		"(reflect.Value).SetUint":         ext۰NoEffect,
 | 
							"(reflect.Value).SetUint":         ext۰NoEffect,
 | 
				
			||||||
		"(reflect.Value).Slice":           ext۰reflect۰Value۰Slice,
 | 
							"(reflect.Value).Slice":           ext۰reflect۰Value۰Slice,
 | 
				
			||||||
		"(reflect.Value).String":          ext۰NoEffect,
 | 
							"(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).Type":            ext۰NoEffect,
 | 
				
			||||||
		"(reflect.Value).Uint":            ext۰NoEffect,
 | 
							"(reflect.Value).Uint":            ext۰NoEffect,
 | 
				
			||||||
		"(reflect.Value).UnsafeAddr":      ext۰NoEffect,
 | 
							"(reflect.Value).UnsafeAddr":      ext۰NoEffect,
 | 
				
			||||||
| 
						 | 
					@ -275,10 +279,14 @@ func (a *analysis) findIntrinsic(fn *ssa.Function) intrinsic {
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		impl = intrinsicsByName[fn.String()] // may be nil
 | 
							impl = intrinsicsByName[fn.String()] // may be nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if fn.Pkg != nil && a.reflectValueObj != nil && a.reflectValueObj.Pkg() == fn.Pkg.Object {
 | 
				
			||||||
 | 
								if !a.config.Reflection {
 | 
				
			||||||
 | 
									impl = ext۰NoEffect // reflection disabled
 | 
				
			||||||
 | 
								} else if impl == nil {
 | 
				
			||||||
				// Ensure all "reflect" code is treated intrinsically.
 | 
									// Ensure all "reflect" code is treated intrinsically.
 | 
				
			||||||
		if impl == nil && fn.Pkg != nil && a.reflectValueObj != nil && a.reflectValueObj.Pkg() == fn.Pkg.Object {
 | 
					 | 
				
			||||||
				impl = ext۰NotYetImplemented
 | 
									impl = ext۰NotYetImplemented
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		a.intrinsics[fn] = impl
 | 
							a.intrinsics[fn] = impl
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,9 +33,11 @@ var inputs = []string{
 | 
				
			||||||
	// "testdata/tmp.go",
 | 
						// "testdata/tmp.go",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Working:
 | 
						// Working:
 | 
				
			||||||
 | 
						"testdata/a_test.go",
 | 
				
			||||||
	"testdata/another.go",
 | 
						"testdata/another.go",
 | 
				
			||||||
	"testdata/arrays.go",
 | 
						"testdata/arrays.go",
 | 
				
			||||||
	"testdata/channels.go",
 | 
						"testdata/channels.go",
 | 
				
			||||||
 | 
						"testdata/chanreflect.go",
 | 
				
			||||||
	"testdata/context.go",
 | 
						"testdata/context.go",
 | 
				
			||||||
	"testdata/conv.go",
 | 
						"testdata/conv.go",
 | 
				
			||||||
	"testdata/flow.go",
 | 
						"testdata/flow.go",
 | 
				
			||||||
| 
						 | 
					@ -43,21 +45,19 @@ var inputs = []string{
 | 
				
			||||||
	"testdata/func.go",
 | 
						"testdata/func.go",
 | 
				
			||||||
	"testdata/hello.go",
 | 
						"testdata/hello.go",
 | 
				
			||||||
	"testdata/interfaces.go",
 | 
						"testdata/interfaces.go",
 | 
				
			||||||
 | 
						"testdata/funcreflect.go",
 | 
				
			||||||
 | 
						"testdata/mapreflect.go",
 | 
				
			||||||
	"testdata/maps.go",
 | 
						"testdata/maps.go",
 | 
				
			||||||
	"testdata/panic.go",
 | 
						"testdata/panic.go",
 | 
				
			||||||
	"testdata/recur.go",
 | 
						"testdata/recur.go",
 | 
				
			||||||
 | 
						"testdata/reflect.go",
 | 
				
			||||||
	"testdata/structs.go",
 | 
						"testdata/structs.go",
 | 
				
			||||||
	"testdata/a_test.go",
 | 
					 | 
				
			||||||
	"testdata/mapreflect.go",
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO(adonovan): get these tests (of reflection) passing.
 | 
						// TODO(adonovan): get these tests (of reflection) passing.
 | 
				
			||||||
	// (The tests are mostly sound since they were used for a
 | 
						// (The tests are mostly sound since they were used for a
 | 
				
			||||||
	// previous implementation.)
 | 
						// previous implementation.)
 | 
				
			||||||
	// "testdata/funcreflect.go",
 | 
					 | 
				
			||||||
	// "testdata/arrayreflect.go",
 | 
						// "testdata/arrayreflect.go",
 | 
				
			||||||
	// "testdata/chanreflect.go",
 | 
					 | 
				
			||||||
	// "testdata/finalizer.go",
 | 
						// "testdata/finalizer.go",
 | 
				
			||||||
	// "testdata/reflect.go",
 | 
					 | 
				
			||||||
	// "testdata/structreflect.go",
 | 
						// "testdata/structreflect.go",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -290,6 +290,7 @@ func doOneInput(input, filename string) bool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Run the analysis.
 | 
						// Run the analysis.
 | 
				
			||||||
	config := &pointer.Config{
 | 
						config := &pointer.Config{
 | 
				
			||||||
 | 
							Reflection: true,
 | 
				
			||||||
		Mains:      []*ssa.Package{ptrmain},
 | 
							Mains:      []*ssa.Package{ptrmain},
 | 
				
			||||||
		Log:        &log,
 | 
							Log:        &log,
 | 
				
			||||||
		Print: func(site *ssa.CallCommon, p pointer.Pointer) {
 | 
							Print: func(site *ssa.CallCommon, p pointer.Pointer) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,10 +4,14 @@ package pointer
 | 
				
			||||||
// constraints arising from the use of reflection in the target
 | 
					// constraints arising from the use of reflection in the target
 | 
				
			||||||
// program.  See doc.go for explanation of the representation.
 | 
					// 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.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
// TODO(adonovan): fix: most of the reflect API permits implicit
 | 
					// TODO(adonovan): fix: most of the reflect API permits implicit
 | 
				
			||||||
// conversions due to assignability, e.g. m.MapIndex(k) is ok if T(k)
 | 
					// conversions due to assignability, e.g. m.MapIndex(k) is ok if T(k)
 | 
				
			||||||
// is assignable to T(M).key.  It's not yet clear how best to model
 | 
					// is assignable to T(M).key.  It's not yet clear how best to model
 | 
				
			||||||
// that.
 | 
					// that; perhaps a more lenient version of typeAssertConstraint is
 | 
				
			||||||
 | 
					// needed.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// To avoid proliferation of equivalent labels, instrinsics should
 | 
					// To avoid proliferation of equivalent labels, instrinsics should
 | 
				
			||||||
// memoize as much as possible, like TypeOf and Zero do for their
 | 
					// memoize as much as possible, like TypeOf and Zero do for their
 | 
				
			||||||
| 
						 | 
					@ -17,6 +21,7 @@ package pointer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"go/ast"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.google.com/p/go.tools/go/types"
 | 
						"code.google.com/p/go.tools/go/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -37,25 +42,25 @@ func ext۰reflect۰Value۰Index(a *analysis, cgn *cgnode)           {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ---------- func (Value).Interface() Value ----------
 | 
					// ---------- func (Value).Interface() Value ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// result = rv.Interface()
 | 
					// result = v.Interface()
 | 
				
			||||||
type rVInterfaceConstraint struct {
 | 
					type rVInterfaceConstraint struct {
 | 
				
			||||||
	rv     nodeid // (ptr)
 | 
						v      nodeid // (ptr)
 | 
				
			||||||
	result nodeid
 | 
						result nodeid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVInterfaceConstraint) String() string {
 | 
					func (c *rVInterfaceConstraint) String() string {
 | 
				
			||||||
	return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.rv)
 | 
						return fmt.Sprintf("n%d = reflect n%d.Interface()", c.result, c.v)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVInterfaceConstraint) ptr() nodeid {
 | 
					func (c *rVInterfaceConstraint) ptr() nodeid {
 | 
				
			||||||
	return c.rv
 | 
						return c.v
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
					func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
	resultPts := &a.nodes[c.result].pts
 | 
						resultPts := &a.nodes[c.result].pts
 | 
				
			||||||
	changed := false
 | 
						changed := false
 | 
				
			||||||
	for obj := range delta {
 | 
						for vObj := range delta {
 | 
				
			||||||
		tDyn, _, indirect := a.taggedValue(obj)
 | 
							tDyn, _, indirect := a.taggedValue(vObj)
 | 
				
			||||||
		if tDyn == nil {
 | 
							if tDyn == nil {
 | 
				
			||||||
			panic("not a tagged object")
 | 
								panic("not a tagged object")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -65,7 +70,7 @@ func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
			panic("indirect tagged object")
 | 
								panic("indirect tagged object")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if resultPts.add(obj) {
 | 
							if resultPts.add(vObj) {
 | 
				
			||||||
			changed = true
 | 
								changed = true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -76,33 +81,33 @@ func (c *rVInterfaceConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) {
 | 
					func ext۰reflect۰Value۰Interface(a *analysis, cgn *cgnode) {
 | 
				
			||||||
	a.addConstraint(&rVInterfaceConstraint{
 | 
						a.addConstraint(&rVInterfaceConstraint{
 | 
				
			||||||
		rv:     a.funcParams(cgn.obj),
 | 
							v:      a.funcParams(cgn.obj),
 | 
				
			||||||
		result: a.funcResults(cgn.obj),
 | 
							result: a.funcResults(cgn.obj),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ---------- func (Value).MapIndex(Value) Value ----------
 | 
					// ---------- func (Value).MapIndex(Value) Value ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// result = rv.MapIndex(key)
 | 
					// result = v.MapIndex(_)
 | 
				
			||||||
type rVMapIndexConstraint struct {
 | 
					type rVMapIndexConstraint struct {
 | 
				
			||||||
	cgn    *cgnode
 | 
						cgn    *cgnode
 | 
				
			||||||
	rv     nodeid // (ptr)
 | 
						v      nodeid // (ptr)
 | 
				
			||||||
	result nodeid
 | 
						result nodeid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVMapIndexConstraint) String() string {
 | 
					func (c *rVMapIndexConstraint) String() string {
 | 
				
			||||||
	return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.rv)
 | 
						return fmt.Sprintf("n%d = reflect n%d.MapIndex(_)", c.result, c.v)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVMapIndexConstraint) ptr() nodeid {
 | 
					func (c *rVMapIndexConstraint) ptr() nodeid {
 | 
				
			||||||
	return c.rv
 | 
						return c.v
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
					func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
	changed := false
 | 
						changed := false
 | 
				
			||||||
	for obj := range delta {
 | 
						for vObj := range delta {
 | 
				
			||||||
		tDyn, m, indirect := a.taggedValue(obj)
 | 
							tDyn, m, indirect := a.taggedValue(vObj)
 | 
				
			||||||
		tMap, _ := tDyn.(*types.Map)
 | 
							tMap, _ := tDyn.Underlying().(*types.Map)
 | 
				
			||||||
		if tMap == nil {
 | 
							if tMap == nil {
 | 
				
			||||||
			continue // not a map
 | 
								continue // not a map
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -112,9 +117,9 @@ func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
			panic("indirect tagged object")
 | 
								panic("indirect tagged object")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		vObj := a.makeTagged(tMap.Elem(), c.cgn, nil)
 | 
							obj := a.makeTagged(tMap.Elem(), c.cgn, nil)
 | 
				
			||||||
		a.loadOffset(vObj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem()))
 | 
							a.loadOffset(obj+1, m, a.sizeof(tMap.Key()), a.sizeof(tMap.Elem()))
 | 
				
			||||||
		if a.nodes[c.result].pts.add(vObj) {
 | 
							if a.addLabel(c.result, obj) {
 | 
				
			||||||
			changed = true
 | 
								changed = true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -126,33 +131,33 @@ func (c *rVMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) {
 | 
					func ext۰reflect۰Value۰MapIndex(a *analysis, cgn *cgnode) {
 | 
				
			||||||
	a.addConstraint(&rVMapIndexConstraint{
 | 
						a.addConstraint(&rVMapIndexConstraint{
 | 
				
			||||||
		cgn:    cgn,
 | 
							cgn:    cgn,
 | 
				
			||||||
		rv:     a.funcParams(cgn.obj),
 | 
							v:      a.funcParams(cgn.obj),
 | 
				
			||||||
		result: a.funcResults(cgn.obj),
 | 
							result: a.funcResults(cgn.obj),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ---------- func (Value).MapKeys() []Value ----------
 | 
					// ---------- func (Value).MapKeys() []Value ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// result = rv.MapKeys()
 | 
					// result = v.MapKeys()
 | 
				
			||||||
type rVMapKeysConstraint struct {
 | 
					type rVMapKeysConstraint struct {
 | 
				
			||||||
	cgn    *cgnode
 | 
						cgn    *cgnode
 | 
				
			||||||
	rv     nodeid // (ptr)
 | 
						v      nodeid // (ptr)
 | 
				
			||||||
	result nodeid
 | 
						result nodeid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVMapKeysConstraint) String() string {
 | 
					func (c *rVMapKeysConstraint) String() string {
 | 
				
			||||||
	return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.rv)
 | 
						return fmt.Sprintf("n%d = reflect n%d.MapKeys()", c.result, c.v)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVMapKeysConstraint) ptr() nodeid {
 | 
					func (c *rVMapKeysConstraint) ptr() nodeid {
 | 
				
			||||||
	return c.rv
 | 
						return c.v
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
					func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
	changed := false
 | 
						changed := false
 | 
				
			||||||
	for obj := range delta {
 | 
						for vObj := range delta {
 | 
				
			||||||
		tDyn, m, indirect := a.taggedValue(obj)
 | 
							tDyn, m, indirect := a.taggedValue(vObj)
 | 
				
			||||||
		tMap, _ := tDyn.(*types.Map)
 | 
							tMap, _ := tDyn.Underlying().(*types.Map)
 | 
				
			||||||
		if tMap == nil {
 | 
							if tMap == nil {
 | 
				
			||||||
			continue // not a map
 | 
								continue // not a map
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -164,7 +169,7 @@ func (c *rVMapKeysConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		kObj := a.makeTagged(tMap.Key(), c.cgn, nil)
 | 
							kObj := a.makeTagged(tMap.Key(), c.cgn, nil)
 | 
				
			||||||
		a.load(kObj+1, m, a.sizeof(tMap.Key()))
 | 
							a.load(kObj+1, m, a.sizeof(tMap.Key()))
 | 
				
			||||||
		if a.nodes[c.result].pts.add(kObj) {
 | 
							if a.addLabel(c.result, kObj) {
 | 
				
			||||||
			changed = true
 | 
								changed = true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -180,41 +185,139 @@ func ext۰reflect۰Value۰MapKeys(a *analysis, cgn *cgnode) {
 | 
				
			||||||
	a.endObject(obj, cgn, nil)
 | 
						a.endObject(obj, cgn, nil)
 | 
				
			||||||
	a.addressOf(a.funcResults(cgn.obj), obj)
 | 
						a.addressOf(a.funcResults(cgn.obj), obj)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// resolution rule attached to rv
 | 
					 | 
				
			||||||
	a.addConstraint(&rVMapKeysConstraint{
 | 
						a.addConstraint(&rVMapKeysConstraint{
 | 
				
			||||||
		cgn:    cgn,
 | 
							cgn:    cgn,
 | 
				
			||||||
		rv:     a.funcParams(cgn.obj),
 | 
							v:      a.funcParams(cgn.obj),
 | 
				
			||||||
		result: obj + 1, // result is stored in array elems
 | 
							result: obj + 1, // result is stored in array elems
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode)       {}
 | 
					func ext۰reflect۰Value۰Method(a *analysis, cgn *cgnode)       {}
 | 
				
			||||||
func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {}
 | 
					func ext۰reflect۰Value۰MethodByName(a *analysis, cgn *cgnode) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ---------- func (Value).Recv(Value) ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// result, _ = v.Recv()
 | 
				
			||||||
 | 
					type rVRecvConstraint struct {
 | 
				
			||||||
 | 
						cgn    *cgnode
 | 
				
			||||||
 | 
						v      nodeid // (ptr)
 | 
				
			||||||
 | 
						result nodeid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *rVRecvConstraint) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("n%d = reflect n%d.Recv()", c.result, c.v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *rVRecvConstraint) ptr() nodeid {
 | 
				
			||||||
 | 
						return c.v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *rVRecvConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
						changed := false
 | 
				
			||||||
 | 
						for vObj := range delta {
 | 
				
			||||||
 | 
							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, 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) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("reflect n%d.Send(n%d)", c.v, c.x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *rVSendConstraint) ptr() nodeid {
 | 
				
			||||||
 | 
						return c.v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *rVSendConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
						for vObj := range delta {
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
 | 
							a.store(ch, xtmp, 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)      {}
 | 
					func ext۰reflect۰Value۰Set(a *analysis, cgn *cgnode)      {}
 | 
				
			||||||
func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) {}
 | 
					func ext۰reflect۰Value۰SetBytes(a *analysis, cgn *cgnode) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ---------- func (Value).SetMapIndex(k Value, v Value) ----------
 | 
					// ---------- func (Value).SetMapIndex(k Value, v Value) ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// rv.SetMapIndex(k, v)
 | 
					// v.SetMapIndex(key, val)
 | 
				
			||||||
type rVSetMapIndexConstraint struct {
 | 
					type rVSetMapIndexConstraint struct {
 | 
				
			||||||
	cgn *cgnode
 | 
						cgn *cgnode
 | 
				
			||||||
	rv  nodeid // (ptr)
 | 
						v   nodeid // (ptr)
 | 
				
			||||||
	k   nodeid
 | 
						key nodeid
 | 
				
			||||||
	v   nodeid
 | 
						val nodeid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVSetMapIndexConstraint) String() string {
 | 
					func (c *rVSetMapIndexConstraint) String() string {
 | 
				
			||||||
	return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.rv, c.k, c.v)
 | 
						return fmt.Sprintf("reflect n%d.SetMapIndex(n%d, n%d)", c.v, c.key, c.val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVSetMapIndexConstraint) ptr() nodeid {
 | 
					func (c *rVSetMapIndexConstraint) ptr() nodeid {
 | 
				
			||||||
	return c.rv
 | 
						return c.v
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
					func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
	for obj := range delta {
 | 
						for vObj := range delta {
 | 
				
			||||||
		tDyn, m, indirect := a.taggedValue(obj)
 | 
							tDyn, m, indirect := a.taggedValue(vObj)
 | 
				
			||||||
		tMap, _ := tDyn.(*types.Map)
 | 
							tMap, _ := tDyn.Underlying().(*types.Map)
 | 
				
			||||||
		if tMap == nil {
 | 
							if tMap == nil {
 | 
				
			||||||
			continue // not a map
 | 
								continue // not a map
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -224,28 +327,27 @@ func (c *rVSetMapIndexConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
			panic("indirect tagged object")
 | 
								panic("indirect tagged object")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ksize := a.sizeof(tMap.Key())
 | 
							keysize := a.sizeof(tMap.Key())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Extract k Value's payload to ktmp, then store to map key.
 | 
							// Extract key's payload to keytmp, then store to map key.
 | 
				
			||||||
		ktmp := a.addNodes(tMap.Key(), "SetMapIndex.ktmp")
 | 
							keytmp := a.addNodes(tMap.Key(), "SetMapIndex.keytmp")
 | 
				
			||||||
		a.addConstraint(&typeAssertConstraint{tMap.Key(), ktmp, c.k})
 | 
							a.typeAssert(tMap.Key(), keytmp, c.key)
 | 
				
			||||||
		a.store(m, ktmp, ksize)
 | 
							a.store(m, keytmp, keysize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Extract v Value's payload to vtmp, then store to map value.
 | 
							// Extract val's payload to vtmp, then store to map value.
 | 
				
			||||||
		vtmp := a.addNodes(tMap.Elem(), "SetMapIndex.vtmp")
 | 
							valtmp := a.addNodes(tMap.Elem(), "SetMapIndex.valtmp")
 | 
				
			||||||
		a.addConstraint(&typeAssertConstraint{tMap.Elem(), vtmp, c.v})
 | 
							a.typeAssert(tMap.Elem(), valtmp, c.val)
 | 
				
			||||||
		a.storeOffset(m, vtmp, ksize, a.sizeof(tMap.Elem()))
 | 
							a.storeOffset(m, valtmp, keysize, a.sizeof(tMap.Elem()))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) {
 | 
					func ext۰reflect۰Value۰SetMapIndex(a *analysis, cgn *cgnode) {
 | 
				
			||||||
	// resolution rule attached to rv
 | 
						params := a.funcParams(cgn.obj)
 | 
				
			||||||
	rv := a.funcParams(cgn.obj)
 | 
					 | 
				
			||||||
	a.addConstraint(&rVSetMapIndexConstraint{
 | 
						a.addConstraint(&rVSetMapIndexConstraint{
 | 
				
			||||||
		cgn: cgn,
 | 
							cgn: cgn,
 | 
				
			||||||
		rv:  rv,
 | 
							v:   params,
 | 
				
			||||||
		k:   rv + 1,
 | 
							key: params + 1,
 | 
				
			||||||
		v:   rv + 2,
 | 
							val: params + 2,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -257,45 +359,300 @@ func ext۰reflect۰Value۰Slice(a *analysis, cgn *cgnode)      {}
 | 
				
			||||||
func ext۰reflect۰Append(a *analysis, cgn *cgnode)      {}
 | 
					func ext۰reflect۰Append(a *analysis, cgn *cgnode)      {}
 | 
				
			||||||
func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {}
 | 
					func ext۰reflect۰AppendSlice(a *analysis, cgn *cgnode) {}
 | 
				
			||||||
func ext۰reflect۰Copy(a *analysis, cgn *cgnode)        {}
 | 
					func ext۰reflect۰Copy(a *analysis, cgn *cgnode)        {}
 | 
				
			||||||
func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode)      {}
 | 
					
 | 
				
			||||||
func ext۰reflect۰Indirect(a *analysis, cgn *cgnode)    {}
 | 
					// ---------- func ChanOf(ChanDir, Type) Type ----------
 | 
				
			||||||
func ext۰reflect۰MakeChan(a *analysis, cgn *cgnode)    {}
 | 
					
 | 
				
			||||||
 | 
					// result = ChanOf(_, t)
 | 
				
			||||||
 | 
					type reflectChanOfConstraint struct {
 | 
				
			||||||
 | 
						cgn    *cgnode
 | 
				
			||||||
 | 
						t      nodeid // (ptr)
 | 
				
			||||||
 | 
						result nodeid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectChanOfConstraint) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("n%d = reflect.ChanOf(n%d)", c.result, c.t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectChanOfConstraint) ptr() nodeid {
 | 
				
			||||||
 | 
						return c.t
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectChanOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
						changed := false
 | 
				
			||||||
 | 
						for tObj := range delta {
 | 
				
			||||||
 | 
							T := a.rtypeTaggedValue(tObj)
 | 
				
			||||||
 | 
							// TODO(adonovan): use only the channel direction
 | 
				
			||||||
 | 
							// provided at the callsite, if constant.
 | 
				
			||||||
 | 
							for _, dir := range []ast.ChanDir{1, 2, 3} {
 | 
				
			||||||
 | 
								if a.addLabel(c.result, a.makeRtype(types.NewChan(dir, T))) {
 | 
				
			||||||
 | 
									changed = true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if changed {
 | 
				
			||||||
 | 
							a.addWork(c.result)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) {
 | 
				
			||||||
 | 
						params := a.funcParams(cgn.obj)
 | 
				
			||||||
 | 
						a.addConstraint(&reflectChanOfConstraint{
 | 
				
			||||||
 | 
							cgn:    cgn,
 | 
				
			||||||
 | 
							t:      params + 1,
 | 
				
			||||||
 | 
							result: a.funcResults(cgn.obj),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ---------- func Indirect(v Value) Value ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// result = Indirect(v)
 | 
				
			||||||
 | 
					type reflectIndirectConstraint struct {
 | 
				
			||||||
 | 
						cgn    *cgnode
 | 
				
			||||||
 | 
						v      nodeid // (ptr)
 | 
				
			||||||
 | 
						result nodeid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectIndirectConstraint) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("n%d = reflect.Indirect(n%d)", c.result, c.v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectIndirectConstraint) ptr() nodeid {
 | 
				
			||||||
 | 
						return c.v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectIndirectConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
						changed := false
 | 
				
			||||||
 | 
						for vObj := range delta {
 | 
				
			||||||
 | 
							tDyn, _, _ := a.taggedValue(vObj)
 | 
				
			||||||
 | 
							if tDyn == nil {
 | 
				
			||||||
 | 
								panic("not a tagged value")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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, 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectMakeChanConstraint) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("n%d = reflect.MakeChan(n%d)", c.result, c.typ)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectMakeChanConstraint) ptr() nodeid {
 | 
				
			||||||
 | 
						return c.typ
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectMakeChanConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
						changed := false
 | 
				
			||||||
 | 
						for typObj := range delta {
 | 
				
			||||||
 | 
							T := a.rtypeTaggedValue(typObj)
 | 
				
			||||||
 | 
							tChan, ok := T.Underlying().(*types.Chan)
 | 
				
			||||||
 | 
							if !ok || tChan.Dir() != ast.SEND|ast.RECV {
 | 
				
			||||||
 | 
								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) {}
 | 
					func ext۰reflect۰MakeFunc(a *analysis, cgn *cgnode) {}
 | 
				
			||||||
func ext۰reflect۰MakeMap(a *analysis, cgn *cgnode)     {}
 | 
					
 | 
				
			||||||
 | 
					// ---------- func MakeMap(Type) Value ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// result = MakeMap(typ)
 | 
				
			||||||
 | 
					type reflectMakeMapConstraint struct {
 | 
				
			||||||
 | 
						cgn    *cgnode
 | 
				
			||||||
 | 
						typ    nodeid // (ptr)
 | 
				
			||||||
 | 
						result nodeid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectMakeMapConstraint) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("n%d = reflect.MakeMap(n%d)", c.result, c.typ)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectMakeMapConstraint) ptr() nodeid {
 | 
				
			||||||
 | 
						return c.typ
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectMakeMapConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
						changed := false
 | 
				
			||||||
 | 
						for typObj := range delta {
 | 
				
			||||||
 | 
							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 ext۰reflect۰MakeSlice(a *analysis, cgn *cgnode) {}
 | 
					func ext۰reflect۰MakeSlice(a *analysis, cgn *cgnode) {}
 | 
				
			||||||
func ext۰reflect۰MapOf(a *analysis, cgn *cgnode)     {}
 | 
					func ext۰reflect۰MapOf(a *analysis, cgn *cgnode)     {}
 | 
				
			||||||
func ext۰reflect۰New(a *analysis, cgn *cgnode)         {}
 | 
					
 | 
				
			||||||
func ext۰reflect۰NewAt(a *analysis, cgn *cgnode)       {}
 | 
					// ---------- func New(Type) Value ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// result = New(typ)
 | 
				
			||||||
 | 
					type reflectNewConstraint struct {
 | 
				
			||||||
 | 
						cgn    *cgnode
 | 
				
			||||||
 | 
						typ    nodeid // (ptr)
 | 
				
			||||||
 | 
						result nodeid
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectNewConstraint) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("n%d = reflect.New(n%d)", c.result, c.typ)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectNewConstraint) ptr() nodeid {
 | 
				
			||||||
 | 
						return c.typ
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *reflectNewConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
						changed := false
 | 
				
			||||||
 | 
						for typObj := range delta {
 | 
				
			||||||
 | 
							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): make it easier to report errors of this form,
 | 
				
			||||||
 | 
						// which includes the callsite:
 | 
				
			||||||
 | 
						// a.warnf("unsound: main.reflectNewAt contains a reflect.NewAt() call")
 | 
				
			||||||
 | 
						a.warnf(cgn.Func().Pos(), "unsound: reflect.NewAt() call")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode)   {}
 | 
					func ext۰reflect۰PtrTo(a *analysis, cgn *cgnode)   {}
 | 
				
			||||||
func ext۰reflect۰Select(a *analysis, cgn *cgnode)  {}
 | 
					func ext۰reflect۰Select(a *analysis, cgn *cgnode)  {}
 | 
				
			||||||
func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) {}
 | 
					func ext۰reflect۰SliceOf(a *analysis, cgn *cgnode) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ---------- func TypeOf(v Value) Type ----------
 | 
					// ---------- func TypeOf(v Value) Type ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// result = TypeOf(v)
 | 
					// result = TypeOf(i)
 | 
				
			||||||
type reflectTypeOfConstraint struct {
 | 
					type reflectTypeOfConstraint struct {
 | 
				
			||||||
	cgn    *cgnode
 | 
						cgn    *cgnode
 | 
				
			||||||
	v      nodeid // (ptr)
 | 
						i      nodeid // (ptr)
 | 
				
			||||||
	result nodeid
 | 
						result nodeid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *reflectTypeOfConstraint) String() string {
 | 
					func (c *reflectTypeOfConstraint) String() string {
 | 
				
			||||||
	return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.v)
 | 
						return fmt.Sprintf("n%d = reflect.TypeOf(n%d)", c.result, c.i)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *reflectTypeOfConstraint) ptr() nodeid {
 | 
					func (c *reflectTypeOfConstraint) ptr() nodeid {
 | 
				
			||||||
	return c.v
 | 
						return c.i
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
					func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
	changed := false
 | 
						changed := false
 | 
				
			||||||
	for obj := range delta {
 | 
						for iObj := range delta {
 | 
				
			||||||
		tDyn, _, _ := a.taggedValue(obj)
 | 
							tDyn, _, _ := a.taggedValue(iObj)
 | 
				
			||||||
		if tDyn == nil {
 | 
							if tDyn == nil {
 | 
				
			||||||
			panic("not a tagged value")
 | 
								panic("not a tagged value")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if a.nodes[c.result].pts.add(a.makeRtype(tDyn)) {
 | 
							if a.addLabel(c.result, a.makeRtype(tDyn)) {
 | 
				
			||||||
			changed = true
 | 
								changed = true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -307,7 +664,7 @@ func (c *reflectTypeOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) {
 | 
					func ext۰reflect۰TypeOf(a *analysis, cgn *cgnode) {
 | 
				
			||||||
	a.addConstraint(&reflectTypeOfConstraint{
 | 
						a.addConstraint(&reflectTypeOfConstraint{
 | 
				
			||||||
		cgn:    cgn,
 | 
							cgn:    cgn,
 | 
				
			||||||
		v:      a.funcParams(cgn.obj),
 | 
							i:      a.funcParams(cgn.obj),
 | 
				
			||||||
		result: a.funcResults(cgn.obj),
 | 
							result: a.funcResults(cgn.obj),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -323,29 +680,25 @@ func ext۰reflect۰ValueOf(a *analysis, cgn *cgnode) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ---------- func Zero(Type) Value ----------
 | 
					// ---------- func Zero(Type) Value ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// result = Zero(t)
 | 
					// result = Zero(typ)
 | 
				
			||||||
type reflectZeroConstraint struct {
 | 
					type reflectZeroConstraint struct {
 | 
				
			||||||
	cgn    *cgnode
 | 
						cgn    *cgnode
 | 
				
			||||||
	t      nodeid // (ptr)
 | 
						typ    nodeid // (ptr)
 | 
				
			||||||
	result nodeid
 | 
						result nodeid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *reflectZeroConstraint) String() string {
 | 
					func (c *reflectZeroConstraint) String() string {
 | 
				
			||||||
	return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.t)
 | 
						return fmt.Sprintf("n%d = reflect.Zero(n%d)", c.result, c.typ)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *reflectZeroConstraint) ptr() nodeid {
 | 
					func (c *reflectZeroConstraint) ptr() nodeid {
 | 
				
			||||||
	return c.t
 | 
						return c.typ
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
					func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
	changed := false
 | 
						changed := false
 | 
				
			||||||
	for obj := range delta {
 | 
						for typObj := range delta {
 | 
				
			||||||
		tDyn, v, _ := a.taggedValue(obj)
 | 
							T := a.rtypeTaggedValue(typObj)
 | 
				
			||||||
		if tDyn != a.reflectRtype {
 | 
					 | 
				
			||||||
			panic("not a *reflect.rtype-tagged value")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		T := a.nodes[v].typ
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// memoize using a.reflectZeros[T]
 | 
							// memoize using a.reflectZeros[T]
 | 
				
			||||||
		var id nodeid
 | 
							var id nodeid
 | 
				
			||||||
| 
						 | 
					@ -355,7 +708,7 @@ func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
			id = a.makeTagged(T, c.cgn, nil)
 | 
								id = a.makeTagged(T, c.cgn, nil)
 | 
				
			||||||
			a.reflectZeros.Set(T, id)
 | 
								a.reflectZeros.Set(T, id)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if a.nodes[c.result].pts.add(id) {
 | 
							if a.addLabel(c.result, id) {
 | 
				
			||||||
			changed = true
 | 
								changed = true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -367,7 +720,7 @@ func (c *reflectZeroConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
func ext۰reflect۰Zero(a *analysis, cgn *cgnode) {
 | 
					func ext۰reflect۰Zero(a *analysis, cgn *cgnode) {
 | 
				
			||||||
	a.addConstraint(&reflectZeroConstraint{
 | 
						a.addConstraint(&reflectZeroConstraint{
 | 
				
			||||||
		cgn:    cgn,
 | 
							cgn:    cgn,
 | 
				
			||||||
		t:      a.funcParams(cgn.obj),
 | 
							typ:    a.funcParams(cgn.obj),
 | 
				
			||||||
		result: a.funcResults(cgn.obj),
 | 
							result: a.funcResults(cgn.obj),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -392,15 +745,15 @@ func (c *rtypeElemConstraint) ptr() nodeid {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rtypeElemConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
					func (c *rtypeElemConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
	changed := false
 | 
						// Implemented by *types.{Map,Chan,Array,Slice,Pointer}.
 | 
				
			||||||
	for obj := range delta {
 | 
						type hasElem interface {
 | 
				
			||||||
		T := a.nodes[obj].typ // assume obj is an *rtype
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Works for *types.{Map,Chan,Array,Slice,Pointer}.
 | 
					 | 
				
			||||||
		if T, ok := T.Underlying().(interface {
 | 
					 | 
				
			||||||
		Elem() types.Type
 | 
							Elem() types.Type
 | 
				
			||||||
		}); ok {
 | 
						}
 | 
				
			||||||
			if a.nodes[c.result].pts.add(a.makeRtype(T.Elem())) {
 | 
						changed := false
 | 
				
			||||||
 | 
						for tObj := range delta {
 | 
				
			||||||
 | 
							T := a.nodes[tObj].obj.rtype
 | 
				
			||||||
 | 
							if tHasElem, ok := T.Underlying().(hasElem); ok {
 | 
				
			||||||
 | 
								if a.addLabel(c.result, a.makeRtype(tHasElem.Elem())) {
 | 
				
			||||||
				changed = true
 | 
									changed = true
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -422,7 +775,71 @@ func ext۰reflect۰rtype۰Field(a *analysis, cgn *cgnode)           {}
 | 
				
			||||||
func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode)    {}
 | 
					func ext۰reflect۰rtype۰FieldByIndex(a *analysis, cgn *cgnode)    {}
 | 
				
			||||||
func ext۰reflect۰rtype۰FieldByName(a *analysis, cgn *cgnode)     {}
 | 
					func ext۰reflect۰rtype۰FieldByName(a *analysis, cgn *cgnode)     {}
 | 
				
			||||||
func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {}
 | 
					func ext۰reflect۰rtype۰FieldByNameFunc(a *analysis, cgn *cgnode) {}
 | 
				
			||||||
func ext۰reflect۰rtype۰In(a *analysis, cgn *cgnode)              {}
 | 
					
 | 
				
			||||||
 | 
					// ---------- func (*rtype) In/Out() Type ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// result = In/Out(t)
 | 
				
			||||||
 | 
					type rtypeInOutConstraint struct {
 | 
				
			||||||
 | 
						cgn    *cgnode
 | 
				
			||||||
 | 
						t      nodeid // (ptr)
 | 
				
			||||||
 | 
						result nodeid
 | 
				
			||||||
 | 
						out    bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *rtypeInOutConstraint) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("n%d = (*reflect.rtype).InOut(n%d)", c.result, c.t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *rtypeInOutConstraint) ptr() nodeid {
 | 
				
			||||||
 | 
						return c.t
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *rtypeInOutConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
 | 
						changed := false
 | 
				
			||||||
 | 
						for tObj := range delta {
 | 
				
			||||||
 | 
							T := a.nodes[tObj].obj.rtype
 | 
				
			||||||
 | 
							sig, ok := T.Underlying().(*types.Signature)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								continue // not a func type
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tuple := sig.Params()
 | 
				
			||||||
 | 
							if c.out {
 | 
				
			||||||
 | 
								tuple = sig.Results()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// TODO(adonovan): when a function is analyzed
 | 
				
			||||||
 | 
							// context-sensitively, we should be able to see its
 | 
				
			||||||
 | 
							// caller's actual parameter's ssa.Values.  Refactor
 | 
				
			||||||
 | 
							// the intrinsic mechanism to allow this.  Then if the
 | 
				
			||||||
 | 
							// value is an int const K, skip the loop and use
 | 
				
			||||||
 | 
							// tuple.At(K).
 | 
				
			||||||
 | 
							for i, n := 0, tuple.Len(); i < n; 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) {
 | 
				
			||||||
 | 
						a.addConstraint(&rtypeInOutConstraint{
 | 
				
			||||||
 | 
							cgn:    cgn,
 | 
				
			||||||
 | 
							t:      a.funcParams(cgn.obj),
 | 
				
			||||||
 | 
							result: a.funcResults(cgn.obj),
 | 
				
			||||||
 | 
							out:    out,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 ----------
 | 
					// ---------- func (*rtype) Key() Type ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -443,11 +860,10 @@ func (c *rtypeKeyConstraint) ptr() nodeid {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *rtypeKeyConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
					func (c *rtypeKeyConstraint) solve(a *analysis, _ *node, delta nodeset) {
 | 
				
			||||||
	changed := false
 | 
						changed := false
 | 
				
			||||||
	for obj := range delta {
 | 
						for tObj := range delta {
 | 
				
			||||||
		T := a.nodes[obj].typ // assume obj is an *rtype
 | 
							T := a.nodes[tObj].obj.rtype
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if tMap, ok := T.Underlying().(*types.Map); ok {
 | 
							if tMap, ok := T.Underlying().(*types.Map); ok {
 | 
				
			||||||
			if a.nodes[c.result].pts.add(a.makeRtype(tMap.Key())) {
 | 
								if a.addLabel(c.result, a.makeRtype(tMap.Key())) {
 | 
				
			||||||
				changed = true
 | 
									changed = true
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -467,4 +883,3 @@ func ext۰reflect۰rtype۰Key(a *analysis, cgn *cgnode) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode)       {}
 | 
					func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode)       {}
 | 
				
			||||||
func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) {}
 | 
					func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) {}
 | 
				
			||||||
func ext۰reflect۰rtype۰Out(a *analysis, cgn *cgnode)          {}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,27 +14,21 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *analysis) solve() {
 | 
					func (a *analysis) solve() {
 | 
				
			||||||
	a.work.swap()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Solver main loop.
 | 
						// Solver main loop.
 | 
				
			||||||
	for round := 1; ; round++ {
 | 
						for round := 1; ; round++ {
 | 
				
			||||||
 | 
							if a.log != nil {
 | 
				
			||||||
 | 
								fmt.Fprintf(a.log, "Solving, round %d\n", round)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add new constraints to the graph:
 | 
							// Add new constraints to the graph:
 | 
				
			||||||
		// static constraints from SSA on round 1,
 | 
							// static constraints from SSA on round 1,
 | 
				
			||||||
		// dynamic constraints from reflection thereafter.
 | 
							// dynamic constraints from reflection thereafter.
 | 
				
			||||||
		a.processNewConstraints()
 | 
							a.processNewConstraints()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if a.work.swap() {
 | 
					 | 
				
			||||||
			if a.log != nil {
 | 
					 | 
				
			||||||
				fmt.Fprintf(a.log, "Solving, round %d\n", round)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Next iteration.
 | 
					 | 
				
			||||||
			if a.work.empty() {
 | 
					 | 
				
			||||||
				break // done
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		id := a.work.take()
 | 
							id := a.work.take()
 | 
				
			||||||
 | 
							if id == empty {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if a.log != nil {
 | 
							if a.log != nil {
 | 
				
			||||||
			fmt.Fprintf(a.log, "\tnode n%d\n", id)
 | 
								fmt.Fprintf(a.log, "\tnode n%d\n", id)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -110,9 +104,6 @@ func (a *analysis) processNewConstraints() {
 | 
				
			||||||
			if len(n.prevPts) > 0 {
 | 
								if len(n.prevPts) > 0 {
 | 
				
			||||||
				stale.add(id)
 | 
									stale.add(id)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if a.log != nil {
 | 
					 | 
				
			||||||
				fmt.Fprintf(a.log, "Adding to worklist n%d\n", id)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			a.addWork(id)
 | 
								a.addWork(id)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -152,6 +143,11 @@ func (a *analysis) solveConstraints(n *node, delta nodeset) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// addLabel adds label to the points-to set of ptr and reports whether the set grew.
 | 
				
			||||||
 | 
					func (a *analysis) addLabel(ptr, label nodeid) bool {
 | 
				
			||||||
 | 
						return a.nodes[ptr].pts.add(label)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *analysis) addWork(id nodeid) {
 | 
					func (a *analysis) addWork(id nodeid) {
 | 
				
			||||||
	a.work.add(id)
 | 
						a.work.add(id)
 | 
				
			||||||
	if a.log != nil {
 | 
						if a.log != nil {
 | 
				
			||||||
| 
						 | 
					@ -205,6 +201,10 @@ func (a *analysis) onlineCopy(dst, src nodeid) bool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Returns sizeof.
 | 
					// Returns sizeof.
 | 
				
			||||||
// Implicitly adds nodes to worklist.
 | 
					// Implicitly adds nodes to worklist.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// TODO(adonovan): now that we support a.copy() during solving, we
 | 
				
			||||||
 | 
					// could eliminate onlineCopyN, but it's much slower.  Investigate.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 {
 | 
					func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 {
 | 
				
			||||||
	for i := uint32(0); i < sizeof; i++ {
 | 
						for i := uint32(0); i < sizeof; i++ {
 | 
				
			||||||
		if a.onlineCopy(dst, src) {
 | 
							if a.onlineCopy(dst, src) {
 | 
				
			||||||
| 
						 | 
					@ -263,7 +263,7 @@ func (c *typeAssertConstraint) solve(a *analysis, n *node, delta nodeset) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if tIface != nil {
 | 
							if tIface != nil {
 | 
				
			||||||
			if types.IsAssignableTo(tDyn, tIface) {
 | 
								if types.IsAssignableTo(tDyn, tIface) {
 | 
				
			||||||
				if a.nodes[c.dst].pts.add(ifaceObj) {
 | 
									if a.addLabel(c.dst, ifaceObj) {
 | 
				
			||||||
					a.addWork(c.dst)
 | 
										a.addWork(c.dst)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -316,7 +316,7 @@ func (c *invokeConstraint) solve(a *analysis, n *node, delta nodeset) {
 | 
				
			||||||
		// Make callsite's fn variable point to identity of
 | 
							// Make callsite's fn variable point to identity of
 | 
				
			||||||
		// concrete method.  (There's no need to add it to
 | 
							// concrete method.  (There's no need to add it to
 | 
				
			||||||
		// worklist since it never has attached constraints.)
 | 
							// worklist since it never has attached constraints.)
 | 
				
			||||||
		a.nodes[c.params].pts.add(fnObj)
 | 
							a.addLabel(c.params, fnObj)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Extract value and connect to method's receiver.
 | 
							// Extract value and connect to method's receiver.
 | 
				
			||||||
		// Copy payload to method's receiver param (arg0).
 | 
							// Copy payload to method's receiver param (arg0).
 | 
				
			||||||
| 
						 | 
					@ -324,7 +324,6 @@ func (c *invokeConstraint) solve(a *analysis, n *node, delta nodeset) {
 | 
				
			||||||
		recvSize := a.sizeof(sig.Recv().Type())
 | 
							recvSize := a.sizeof(sig.Recv().Type())
 | 
				
			||||||
		a.onlineCopyN(arg0, v, recvSize)
 | 
							a.onlineCopyN(arg0, v, recvSize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Copy iface object payload to method receiver.
 | 
					 | 
				
			||||||
		src := c.params + 1 // skip past identity
 | 
							src := c.params + 1 // skip past identity
 | 
				
			||||||
		dst := arg0 + nodeid(recvSize)
 | 
							dst := arg0 + nodeid(recvSize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,19 +4,16 @@ package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "reflect"
 | 
					import "reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// This test is very sensitive to line-number perturbations!
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Test of channels with reflection.
 | 
					// Test of channels with reflection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var a, b int
 | 
					var a, b int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func chanreflect1() {
 | 
					func chanreflect1() {
 | 
				
			||||||
	ch := make(chan *int, 0)
 | 
						ch := make(chan *int, 0) // @line cr1make
 | 
				
			||||||
	crv := reflect.ValueOf(ch)
 | 
						crv := reflect.ValueOf(ch)
 | 
				
			||||||
	crv.Send(reflect.ValueOf(&a))
 | 
						crv.Send(reflect.ValueOf(&a))
 | 
				
			||||||
	print(crv.Interface())             // @types chan *int
 | 
						print(crv.Interface())             // @types chan *int
 | 
				
			||||||
	print(crv.Interface().(chan *int)) // @pointsto makechan@testdata/chanreflect.go:15:12
 | 
						print(crv.Interface().(chan *int)) // @pointsto makechan@cr1make:12
 | 
				
			||||||
	print(<-ch)                        // @pointsto main.a
 | 
						print(<-ch)                        // @pointsto main.a
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,25 +26,31 @@ func chanreflect2() {
 | 
				
			||||||
	print(r.Interface().(*int)) // @pointsto main.b
 | 
						print(r.Interface().(*int)) // @pointsto main.b
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO(adonovan): the analysis can't yet take advantage of the
 | 
				
			||||||
 | 
					// ChanOf(dir) parameter so the results are less precise than they
 | 
				
			||||||
 | 
					// should be: all three directions are returned.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func chanOfRecv() {
 | 
					func chanOfRecv() {
 | 
				
			||||||
	// MakeChan(<-chan) is a no-op.
 | 
						// MakeChan(<-chan) is a no-op.
 | 
				
			||||||
	t := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(&a))
 | 
						t := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(&a))
 | 
				
			||||||
	print(reflect.Zero(t).Interface())                      // @types <-chan *int
 | 
						print(reflect.Zero(t).Interface())                      // @types <-chan *int | chan<- *int | chan *int
 | 
				
			||||||
	print(reflect.MakeChan(t, 0).Interface().(<-chan *int)) // @pointsto
 | 
						print(reflect.MakeChan(t, 0).Interface().(<-chan *int)) // @pointsto
 | 
				
			||||||
 | 
						print(reflect.MakeChan(t, 0).Interface().(chan *int))   // @pointsto <alloc in reflect.MakeChan>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func chanOfSend() {
 | 
					func chanOfSend() {
 | 
				
			||||||
	// MakeChan(chan<-) is a no-op.
 | 
						// MakeChan(chan<-) is a no-op.
 | 
				
			||||||
	t := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(&a))
 | 
						t := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(&a))
 | 
				
			||||||
	print(reflect.Zero(t).Interface())                      // @types chan<- *int
 | 
						print(reflect.Zero(t).Interface())                      // @types <-chan *int | chan<- *int | chan *int
 | 
				
			||||||
	print(reflect.MakeChan(t, 0).Interface().(chan<- *int)) // @pointsto
 | 
						print(reflect.MakeChan(t, 0).Interface().(chan<- *int)) // @pointsto
 | 
				
			||||||
 | 
						print(reflect.MakeChan(t, 0).Interface().(chan *int))   // @pointsto <alloc in reflect.MakeChan>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func chanOfBoth() {
 | 
					func chanOfBoth() {
 | 
				
			||||||
	t := reflect.ChanOf(reflect.BothDir, reflect.TypeOf(&a))
 | 
						t := reflect.ChanOf(reflect.BothDir, reflect.TypeOf(&a))
 | 
				
			||||||
	print(reflect.Zero(t).Interface()) // @types chan *int
 | 
						print(reflect.Zero(t).Interface()) // @types <-chan *int | chan<- *int | chan *int
 | 
				
			||||||
	ch := reflect.MakeChan(t, 0)
 | 
						ch := reflect.MakeChan(t, 0)
 | 
				
			||||||
	print(ch.Interface().(chan *int)) // @pointsto reflectMakechan@testdata/chanreflect.go:49:24
 | 
						print(ch.Interface().(chan *int)) // @pointsto <alloc in reflect.MakeChan>
 | 
				
			||||||
	ch.Send(reflect.ValueOf(&b))
 | 
						ch.Send(reflect.ValueOf(&b))
 | 
				
			||||||
	ch.Interface().(chan *int) <- &a
 | 
						ch.Interface().(chan *int) <- &a
 | 
				
			||||||
	r, _ := ch.Recv()
 | 
						r, _ := ch.Recv()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,29 +2,43 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import "reflect"
 | 
					import "reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var a, b int
 | 
					var zero, a, b int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func f(p *int) *int {
 | 
					// func f(p *int) *int {
 | 
				
			||||||
	print(p) // @pointsto
 | 
					// 	print(p) // #@pointsto
 | 
				
			||||||
	return &b
 | 
					// 	return &b
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// func g(p *bool) {
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// func reflectValueCall() {
 | 
				
			||||||
 | 
					// 	rvf := reflect.ValueOf(f)
 | 
				
			||||||
 | 
					// 	res := rvf.Call([]reflect.Value{reflect.ValueOf(&a)})
 | 
				
			||||||
 | 
					// 	print(res[0].Interface())        // #@types
 | 
				
			||||||
 | 
					// 	print(res[0].Interface().(*int)) // #@pointsto
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// #@calls main.reflectValueCall -> main.f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func reflectTypeInOut() {
 | 
				
			||||||
 | 
						var f func(float64, bool) (string, int)
 | 
				
			||||||
 | 
						// TODO(adonovan): when the In/Out argument is a valid index constant,
 | 
				
			||||||
 | 
						// only include a single type in the result.  Needs some work.
 | 
				
			||||||
 | 
						print(reflect.Zero(reflect.TypeOf(f).In(0)).Interface())    // @types float64 | bool
 | 
				
			||||||
 | 
						print(reflect.Zero(reflect.TypeOf(f).In(1)).Interface())    // @types float64 | bool
 | 
				
			||||||
 | 
						print(reflect.Zero(reflect.TypeOf(f).In(-1)).Interface())   // @types float64 | bool
 | 
				
			||||||
 | 
						print(reflect.Zero(reflect.TypeOf(f).In(zero)).Interface()) // @types float64 | bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						print(reflect.Zero(reflect.TypeOf(f).Out(0)).Interface()) // @types string | int
 | 
				
			||||||
 | 
						print(reflect.Zero(reflect.TypeOf(f).Out(1)).Interface()) // @types string | int
 | 
				
			||||||
 | 
						print(reflect.Zero(reflect.TypeOf(f).Out(2)).Interface()) // @types string | int
 | 
				
			||||||
 | 
						print(reflect.Zero(reflect.TypeOf(3).Out(0)).Interface()) // @types
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func g(p *bool) {
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func funcreflect1() {
 | 
					 | 
				
			||||||
	rvf := reflect.ValueOf(f)
 | 
					 | 
				
			||||||
	res := rvf.Call([]reflect.Value{reflect.ValueOf(&a)})
 | 
					 | 
				
			||||||
	print(res[0].Interface())        // @types
 | 
					 | 
				
			||||||
	print(res[0].Interface().(*int)) // @pointsto
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// @calls main.funcreflect1 -> main.f
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	funcreflect1()
 | 
						//reflectValueCall()
 | 
				
			||||||
 | 
						reflectTypeInOut()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import "reflect"
 | 
				
			||||||
var a int
 | 
					var a int
 | 
				
			||||||
var b bool
 | 
					var b bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func mapreflect1() {
 | 
					func reflectMapKeysIndex() {
 | 
				
			||||||
	m := make(map[*int]*bool) // @line mr1make
 | 
						m := make(map[*int]*bool) // @line mr1make
 | 
				
			||||||
	m[&a] = &b
 | 
						m[&a] = &b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,7 @@ func mapreflect1() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func mapreflect2() {
 | 
					func reflectSetMapIndex() {
 | 
				
			||||||
	m := make(map[*int]*bool)
 | 
						m := make(map[*int]*bool)
 | 
				
			||||||
	mrv := reflect.ValueOf(m)
 | 
						mrv := reflect.ValueOf(m)
 | 
				
			||||||
	mrv.SetMapIndex(reflect.ValueOf(&a), reflect.ValueOf(&b))
 | 
						mrv.SetMapIndex(reflect.ValueOf(&a), reflect.ValueOf(&b))
 | 
				
			||||||
| 
						 | 
					@ -64,7 +64,16 @@ func mapreflect2() {
 | 
				
			||||||
	print(reflect.Zero(tmap.Elem()).Interface()) // @types *bool
 | 
						print(reflect.Zero(tmap.Elem()).Interface()) // @types *bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func reflectMakeMap() {
 | 
				
			||||||
	mapreflect1()
 | 
						t := reflect.TypeOf(map[*int]*bool(nil))
 | 
				
			||||||
	mapreflect2()
 | 
						v := reflect.MakeMap(t)
 | 
				
			||||||
 | 
						print(v) // @types map[*int]*bool
 | 
				
			||||||
 | 
						print(v) // @pointsto <alloc in reflect.MakeMap>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						reflectMapKeysIndex()
 | 
				
			||||||
 | 
						reflectSetMapIndex()
 | 
				
			||||||
 | 
						reflectMakeMap()
 | 
				
			||||||
 | 
						// TODO(adonovan): reflect.MapOf(Type)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import "reflect"
 | 
				
			||||||
import "unsafe"
 | 
					import "unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var a, b int
 | 
					var a, b int
 | 
				
			||||||
 | 
					var unknown bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func reflectIndirect() {
 | 
					func reflectIndirect() {
 | 
				
			||||||
	ptr := &a
 | 
						ptr := &a
 | 
				
			||||||
| 
						 | 
					@ -20,19 +21,22 @@ func reflectNewAt() {
 | 
				
			||||||
	print(reflect.NewAt(reflect.TypeOf(3), unsafe.Pointer(&x)).Interface()) // @types *int
 | 
						print(reflect.NewAt(reflect.TypeOf(3), unsafe.Pointer(&x)).Interface()) // @types *int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @warning "unsound: main.reflectNewAt contains a reflect.NewAt.. call"
 | 
					// TODO(adonovan): report the location of the caller, not NewAt.
 | 
				
			||||||
 | 
					// #warning "unsound: main.reflectNewAt contains a reflect.NewAt.. call"
 | 
				
			||||||
 | 
					// @warning "unsound: reflect.NewAt.. call"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func reflectTypeOf() {
 | 
					func reflectTypeOf() {
 | 
				
			||||||
	t := reflect.TypeOf(3)
 | 
						t := reflect.TypeOf(3)
 | 
				
			||||||
	if unknown {
 | 
						if unknown {
 | 
				
			||||||
		t = reflect.TypeOf("foo")
 | 
							t = reflect.TypeOf("foo")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	print(t)                             // @types *reflect.rtype
 | 
						// TODO(adonovan): make types.Eval let us refer to unexported types.
 | 
				
			||||||
 | 
						print(t)                             // #@types *reflect.rtype
 | 
				
			||||||
	print(reflect.Zero(t).Interface())   // @types int | string
 | 
						print(reflect.Zero(t).Interface())   // @types int | string
 | 
				
			||||||
	newint := reflect.New(t).Interface() // @line rtonew
 | 
						newint := reflect.New(t).Interface() // @line rtonew
 | 
				
			||||||
	print(newint)                        // @types *int | *string
 | 
						print(newint)                        // @types *int | *string
 | 
				
			||||||
	print(newint.(*int))                 // @pointsto reflectAlloc@rtonew:23
 | 
						print(newint.(*int))                 // @pointsto <alloc in reflect.New>
 | 
				
			||||||
	print(newint.(*string))              // @pointsto reflectAlloc@rtonew:23
 | 
						print(newint.(*string))              // @pointsto <alloc in reflect.New>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func reflectTypeElem() {
 | 
					func reflectTypeElem() {
 | 
				
			||||||
| 
						 | 
					@ -44,26 +48,9 @@ func reflectTypeElem() {
 | 
				
			||||||
	print(reflect.Zero(reflect.TypeOf(3).Elem()).Interface())                        // @types
 | 
						print(reflect.Zero(reflect.TypeOf(3).Elem()).Interface())                        // @types
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func reflectTypeInOut() {
 | 
					 | 
				
			||||||
	var f func(float64, bool) (string, int)
 | 
					 | 
				
			||||||
	print(reflect.Zero(reflect.TypeOf(f).In(0)).Interface())    // @types float64
 | 
					 | 
				
			||||||
	print(reflect.Zero(reflect.TypeOf(f).In(1)).Interface())    // @types bool
 | 
					 | 
				
			||||||
	print(reflect.Zero(reflect.TypeOf(f).In(-1)).Interface())   // @types float64 | bool
 | 
					 | 
				
			||||||
	print(reflect.Zero(reflect.TypeOf(f).In(zero)).Interface()) // @types float64 | bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	print(reflect.Zero(reflect.TypeOf(f).Out(0)).Interface()) // @types string
 | 
					 | 
				
			||||||
	print(reflect.Zero(reflect.TypeOf(f).Out(1)).Interface()) // @types int
 | 
					 | 
				
			||||||
	print(reflect.Zero(reflect.TypeOf(f).Out(2)).Interface()) // @types string | int
 | 
					 | 
				
			||||||
	print(reflect.Zero(reflect.TypeOf(3).Out(0)).Interface()) // @types
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	reflectIndirect()
 | 
						reflectIndirect()
 | 
				
			||||||
	reflectNewAt()
 | 
						reflectNewAt()
 | 
				
			||||||
	reflectTypeOf()
 | 
						reflectTypeOf()
 | 
				
			||||||
	reflectTypeElem()
 | 
						reflectTypeElem()
 | 
				
			||||||
	reflectTypeInOut()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
var unknown bool
 | 
					 | 
				
			||||||
var zero int
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -278,47 +278,30 @@ func (cs *constraintset) add(c constraint) bool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Worklist -------------------------------------------------------------------
 | 
					// Worklist -------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO(adonovan): interface may not be general enough for certain
 | 
					const empty nodeid = 1<<32 - 1
 | 
				
			||||||
// implementations, e.g. priority queue
 | 
					
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Uses double-buffering so nodes can be added during iteration.
 | 
					 | 
				
			||||||
type worklist interface {
 | 
					type worklist interface {
 | 
				
			||||||
	empty() bool  // Reports whether active buffer is empty.
 | 
						add(nodeid)   // Adds a node to the set
 | 
				
			||||||
	swap() bool   // Switches to the shadow buffer if empty().
 | 
						take() nodeid // Takes a node from the set and returns it, or empty
 | 
				
			||||||
	add(nodeid)   // Adds a node to the shadow buffer.
 | 
					 | 
				
			||||||
	take() nodeid // Takes a node from the active buffer.  Precondition: !empty().
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Horribly naive (and nondeterministic) worklist
 | 
					// Simple nondeterministic worklist based on a built-in map.
 | 
				
			||||||
// based on two hash-sets.
 | 
					 | 
				
			||||||
type mapWorklist struct {
 | 
					type mapWorklist struct {
 | 
				
			||||||
	active, shadow nodeset
 | 
						set nodeset
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (w *mapWorklist) empty() bool {
 | 
					 | 
				
			||||||
	return len(w.active) == 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (w *mapWorklist) swap() bool {
 | 
					 | 
				
			||||||
	if w.empty() {
 | 
					 | 
				
			||||||
		w.shadow, w.active = w.active, w.shadow
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (w *mapWorklist) add(n nodeid) {
 | 
					func (w *mapWorklist) add(n nodeid) {
 | 
				
			||||||
	w.shadow[n] = struct{}{}
 | 
						w.set[n] = struct{}{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (w *mapWorklist) take() nodeid {
 | 
					func (w *mapWorklist) take() nodeid {
 | 
				
			||||||
	for k := range w.active {
 | 
						for k := range w.set {
 | 
				
			||||||
		delete(w.active, k)
 | 
							delete(w.set, k)
 | 
				
			||||||
		return k
 | 
							return k
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	panic("worklist.take(): empty active buffer")
 | 
						return empty
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func makeMapWorklist() worklist {
 | 
					func makeMapWorklist() worklist {
 | 
				
			||||||
	return &mapWorklist{make(nodeset), make(nodeset)}
 | 
						return &mapWorklist{make(nodeset)}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue