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
5package types
6
7import (
8	"container/heap"
9	"fmt"
10)
11
12// initOrder computes the Info.InitOrder for package variables.
13func (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.
105func 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.
125func 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.
136func (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.
156type 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.
167func 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.
195type nodeQueue []*objNode
196
197func (a nodeQueue) Len() int { return len(a) }
198
199func (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
205func (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
212func (a *nodeQueue) Push(x interface{}) {
213	panic("unreachable")
214}
215
216func (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}
223