github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/go/types/initorder.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package types
     6  
     7  import (
     8  	"container/heap"
     9  	"fmt"
    10  )
    11  
    12  // initOrder computes the Info.InitOrder for package variables.
    13  func (check *Checker) initOrder() {
    14  	// An InitOrder may already have been computed if a package is
    15  	// built from several calls to (*Checker).Files. Clear it.
    16  	check.Info.InitOrder = check.Info.InitOrder[:0]
    17  
    18  	// compute the object dependency graph and
    19  	// initialize a priority queue with the list
    20  	// of graph nodes
    21  	pq := nodeQueue(dependencyGraph(check.objMap))
    22  	heap.Init(&pq)
    23  
    24  	const debug = false
    25  	if debug {
    26  		fmt.Printf("package %s: object dependency graph\n", check.pkg.Name())
    27  		for _, n := range pq {
    28  			for _, o := range n.out {
    29  				fmt.Printf("\t%s -> %s\n", n.obj.Name(), o.obj.Name())
    30  			}
    31  		}
    32  		fmt.Println()
    33  		fmt.Printf("package %s: initialization order\n", check.pkg.Name())
    34  	}
    35  
    36  	// determine initialization order by removing the highest priority node
    37  	// (the one with the fewest dependencies) and its edges from the graph,
    38  	// repeatedly, until there are no nodes left.
    39  	// In a valid Go program, those nodes always have zero dependencies (after
    40  	// removing all incoming dependencies), otherwise there are initialization
    41  	// cycles.
    42  	mark := 0
    43  	emitted := make(map[*declInfo]bool)
    44  	for len(pq) > 0 {
    45  		// get the next node
    46  		n := heap.Pop(&pq).(*objNode)
    47  
    48  		// if n still depends on other nodes, we have a cycle
    49  		if n.in > 0 {
    50  			mark++ // mark nodes using a different value each time
    51  			cycle := findPath(n, n, mark)
    52  			if i := valIndex(cycle); i >= 0 {
    53  				check.reportCycle(cycle, i)
    54  			}
    55  			// ok to continue, but the variable initialization order
    56  			// will be incorrect at this point since it assumes no
    57  			// cycle errors
    58  		}
    59  
    60  		// reduce dependency count of all dependent nodes
    61  		// and update priority queue
    62  		for _, out := range n.out {
    63  			out.in--
    64  			heap.Fix(&pq, out.index)
    65  		}
    66  
    67  		// record the init order for variables with initializers only
    68  		v, _ := n.obj.(*Var)
    69  		info := check.objMap[v]
    70  		if v == nil || !info.hasInitializer() {
    71  			continue
    72  		}
    73  
    74  		// n:1 variable declarations such as: a, b = f()
    75  		// introduce a node for each lhs variable (here: a, b);
    76  		// but they all have the same initializer - emit only
    77  		// one, for the first variable seen
    78  		if emitted[info] {
    79  			continue // initializer already emitted, if any
    80  		}
    81  		emitted[info] = true
    82  
    83  		infoLhs := info.lhs // possibly nil (see declInfo.lhs field comment)
    84  		if infoLhs == nil {
    85  			infoLhs = []*Var{v}
    86  		}
    87  		init := &Initializer{infoLhs, info.init}
    88  		check.Info.InitOrder = append(check.Info.InitOrder, init)
    89  
    90  		if debug {
    91  			fmt.Printf("\t%s\n", init)
    92  		}
    93  	}
    94  
    95  	if debug {
    96  		fmt.Println()
    97  	}
    98  }
    99  
   100  // findPath returns the (reversed) list of nodes z, ... c, b, a,
   101  // such that there is a path (list of edges) from a to z.
   102  // If there is no such path, the result is nil.
   103  // Nodes marked with the value mark are considered "visited";
   104  // unvisited nodes are marked during the graph search.
   105  func findPath(a, z *objNode, mark int) []*objNode {
   106  	if a.mark == mark {
   107  		return nil // node already seen
   108  	}
   109  	a.mark = mark
   110  
   111  	for _, n := range a.out {
   112  		if n == z {
   113  			return []*objNode{z}
   114  		}
   115  		if P := findPath(n, z, mark); P != nil {
   116  			return append(P, n)
   117  		}
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  // valIndex returns the index of the first constant or variable in a,
   124  // if any; or a value < 0.
   125  func valIndex(a []*objNode) int {
   126  	for i, n := range a {
   127  		switch n.obj.(type) {
   128  		case *Const, *Var:
   129  			return i
   130  		}
   131  	}
   132  	return -1
   133  }
   134  
   135  // reportCycle reports an error for the cycle starting at i.
   136  func (check *Checker) reportCycle(cycle []*objNode, i int) {
   137  	obj := cycle[i].obj
   138  	check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
   139  	// print cycle
   140  	for _ = range cycle {
   141  		check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
   142  		i++
   143  		if i >= len(cycle) {
   144  			i = 0
   145  		}
   146  		obj = cycle[i].obj
   147  	}
   148  	check.errorf(obj.Pos(), "\t%s", obj.Name())
   149  }
   150  
   151  // An objNode represents a node in the object dependency graph.
   152  // Each node b in a.out represents an edge a->b indicating that
   153  // b depends on a.
   154  // Nodes may be marked for cycle detection. A node n is marked
   155  // if n.mark corresponds to the current mark value.
   156  type objNode struct {
   157  	obj   Object     // object represented by this node
   158  	in    int        // number of nodes this node depends on
   159  	out   []*objNode // list of nodes that depend on this node
   160  	index int        // node index in list of nodes
   161  	mark  int        // for cycle detection
   162  }
   163  
   164  // dependencyGraph computes the transposed object dependency graph
   165  // from the given objMap. The transposed graph is returned as a list
   166  // of nodes; an edge d->n indicates that node n depends on node d.
   167  func dependencyGraph(objMap map[Object]*declInfo) []*objNode {
   168  	// M maps each object to its corresponding node
   169  	M := make(map[Object]*objNode, len(objMap))
   170  	for obj := range objMap {
   171  		M[obj] = &objNode{obj: obj}
   172  	}
   173  
   174  	// G is the graph of nodes n
   175  	G := make([]*objNode, len(M))
   176  	i := 0
   177  	for obj, n := range M {
   178  		deps := objMap[obj].deps
   179  		n.in = len(deps)
   180  		for d := range deps {
   181  			d := M[d]                // node n depends on node d
   182  			d.out = append(d.out, n) // add edge d->n
   183  		}
   184  
   185  		G[i] = n
   186  		n.index = i
   187  		i++
   188  	}
   189  
   190  	return G
   191  }
   192  
   193  // nodeQueue implements the container/heap interface;
   194  // a nodeQueue may be used as a priority queue.
   195  type nodeQueue []*objNode
   196  
   197  func (a nodeQueue) Len() int { return len(a) }
   198  
   199  func (a nodeQueue) Swap(i, j int) {
   200  	x, y := a[i], a[j]
   201  	a[i], a[j] = y, x
   202  	x.index, y.index = j, i
   203  }
   204  
   205  func (a nodeQueue) Less(i, j int) bool {
   206  	x, y := a[i], a[j]
   207  	// nodes are prioritized by number of incoming dependencies (1st key)
   208  	// and source order (2nd key)
   209  	return x.in < y.in || x.in == y.in && x.obj.order() < y.obj.order()
   210  }
   211  
   212  func (a *nodeQueue) Push(x interface{}) {
   213  	panic("unreachable")
   214  }
   215  
   216  func (a *nodeQueue) Pop() interface{} {
   217  	n := len(*a)
   218  	x := (*a)[n-1]
   219  	x.index = -1 // for safety
   220  	*a = (*a)[:n-1]
   221  	return x
   222  }