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 types2 6 7import ( 8 "container/heap" 9 "fmt" 10 "sort" 11) 12 13// initOrder computes the Info.InitOrder for package variables. 14func (check *Checker) initOrder() { 15 // An InitOrder may already have been computed if a package is 16 // built from several calls to (*Checker).Files. Clear it. 17 check.Info.InitOrder = check.Info.InitOrder[:0] 18 19 // Compute the object dependency graph and initialize 20 // a priority queue with the list of graph nodes. 21 pq := nodeQueue(dependencyGraph(check.objMap)) 22 heap.Init(&pq) 23 24 const debug = false 25 if debug { 26 fmt.Printf("Computing initialization order for %s\n\n", check.pkg) 27 fmt.Println("Object dependency graph:") 28 for obj, d := range check.objMap { 29 // only print objects that may appear in the dependency graph 30 if obj, _ := obj.(dependency); obj != nil { 31 if len(d.deps) > 0 { 32 fmt.Printf("\t%s depends on\n", obj.Name()) 33 for dep := range d.deps { 34 fmt.Printf("\t\t%s\n", dep.Name()) 35 } 36 } else { 37 fmt.Printf("\t%s has no dependencies\n", obj.Name()) 38 } 39 } 40 } 41 fmt.Println() 42 43 fmt.Println("Transposed object dependency graph (functions eliminated):") 44 for _, n := range pq { 45 fmt.Printf("\t%s depends on %d nodes\n", n.obj.Name(), n.ndeps) 46 for p := range n.pred { 47 fmt.Printf("\t\t%s is dependent\n", p.obj.Name()) 48 } 49 } 50 fmt.Println() 51 52 fmt.Println("Processing nodes:") 53 } 54 55 // Determine initialization order by removing the highest priority node 56 // (the one with the fewest dependencies) and its edges from the graph, 57 // repeatedly, until there are no nodes left. 58 // In a valid Go program, those nodes always have zero dependencies (after 59 // removing all incoming dependencies), otherwise there are initialization 60 // cycles. 61 emitted := make(map[*declInfo]bool) 62 for len(pq) > 0 { 63 // get the next node 64 n := heap.Pop(&pq).(*graphNode) 65 66 if debug { 67 fmt.Printf("\t%s (src pos %d) depends on %d nodes now\n", 68 n.obj.Name(), n.obj.order(), n.ndeps) 69 } 70 71 // if n still depends on other nodes, we have a cycle 72 if n.ndeps > 0 { 73 cycle := findPath(check.objMap, n.obj, n.obj, make(map[Object]bool)) 74 // If n.obj is not part of the cycle (e.g., n.obj->b->c->d->c), 75 // cycle will be nil. Don't report anything in that case since 76 // the cycle is reported when the algorithm gets to an object 77 // in the cycle. 78 // Furthermore, once an object in the cycle is encountered, 79 // the cycle will be broken (dependency count will be reduced 80 // below), and so the remaining nodes in the cycle don't trigger 81 // another error (unless they are part of multiple cycles). 82 if cycle != nil { 83 check.reportCycle(cycle) 84 } 85 // Ok to continue, but the variable initialization order 86 // will be incorrect at this point since it assumes no 87 // cycle errors. 88 } 89 90 // reduce dependency count of all dependent nodes 91 // and update priority queue 92 for p := range n.pred { 93 p.ndeps-- 94 heap.Fix(&pq, p.index) 95 } 96 97 // record the init order for variables with initializers only 98 v, _ := n.obj.(*Var) 99 info := check.objMap[v] 100 if v == nil || !info.hasInitializer() { 101 continue 102 } 103 104 // n:1 variable declarations such as: a, b = f() 105 // introduce a node for each lhs variable (here: a, b); 106 // but they all have the same initializer - emit only 107 // one, for the first variable seen 108 if emitted[info] { 109 continue // initializer already emitted, if any 110 } 111 emitted[info] = true 112 113 infoLhs := info.lhs // possibly nil (see declInfo.lhs field comment) 114 if infoLhs == nil { 115 infoLhs = []*Var{v} 116 } 117 init := &Initializer{infoLhs, info.init} 118 check.Info.InitOrder = append(check.Info.InitOrder, init) 119 } 120 121 if debug { 122 fmt.Println() 123 fmt.Println("Initialization order:") 124 for _, init := range check.Info.InitOrder { 125 fmt.Printf("\t%s\n", init) 126 } 127 fmt.Println() 128 } 129} 130 131// findPath returns the (reversed) list of objects []Object{to, ... from} 132// such that there is a path of object dependencies from 'from' to 'to'. 133// If there is no such path, the result is nil. 134func findPath(objMap map[Object]*declInfo, from, to Object, seen map[Object]bool) []Object { 135 if seen[from] { 136 return nil 137 } 138 seen[from] = true 139 140 for d := range objMap[from].deps { 141 if d == to { 142 return []Object{d} 143 } 144 if P := findPath(objMap, d, to, seen); P != nil { 145 return append(P, d) 146 } 147 } 148 149 return nil 150} 151 152// reportCycle reports an error for the given cycle. 153func (check *Checker) reportCycle(cycle []Object) { 154 obj := cycle[0] 155 var err error_ 156 if check.conf.CompilerErrorMessages { 157 err.errorf(obj, "initialization loop for %s", obj.Name()) 158 } else { 159 err.errorf(obj, "initialization cycle for %s", obj.Name()) 160 } 161 // subtle loop: print cycle[i] for i = 0, n-1, n-2, ... 1 for len(cycle) = n 162 for i := len(cycle) - 1; i >= 0; i-- { 163 err.errorf(obj, "%s refers to", obj.Name()) 164 obj = cycle[i] 165 } 166 // print cycle[0] again to close the cycle 167 err.errorf(obj, "%s", obj.Name()) 168 check.report(&err) 169} 170 171// ---------------------------------------------------------------------------- 172// Object dependency graph 173 174// A dependency is an object that may be a dependency in an initialization 175// expression. Only constants, variables, and functions can be dependencies. 176// Constants are here because constant expression cycles are reported during 177// initialization order computation. 178type dependency interface { 179 Object 180 isDependency() 181} 182 183// A graphNode represents a node in the object dependency graph. 184// Each node p in n.pred represents an edge p->n, and each node 185// s in n.succ represents an edge n->s; with a->b indicating that 186// a depends on b. 187type graphNode struct { 188 obj dependency // object represented by this node 189 pred, succ nodeSet // consumers and dependencies of this node (lazily initialized) 190 index int // node index in graph slice/priority queue 191 ndeps int // number of outstanding dependencies before this object can be initialized 192} 193 194// cost returns the cost of removing this node, which involves copying each 195// predecessor to each successor (and vice-versa). 196func (n *graphNode) cost() int { 197 return len(n.pred) * len(n.succ) 198} 199 200type nodeSet map[*graphNode]bool 201 202func (s *nodeSet) add(p *graphNode) { 203 if *s == nil { 204 *s = make(nodeSet) 205 } 206 (*s)[p] = true 207} 208 209// dependencyGraph computes the object dependency graph from the given objMap, 210// with any function nodes removed. The resulting graph contains only constants 211// and variables. 212func dependencyGraph(objMap map[Object]*declInfo) []*graphNode { 213 // M is the dependency (Object) -> graphNode mapping 214 M := make(map[dependency]*graphNode) 215 for obj := range objMap { 216 // only consider nodes that may be an initialization dependency 217 if obj, _ := obj.(dependency); obj != nil { 218 M[obj] = &graphNode{obj: obj} 219 } 220 } 221 222 // compute edges for graph M 223 // (We need to include all nodes, even isolated ones, because they still need 224 // to be scheduled for initialization in correct order relative to other nodes.) 225 for obj, n := range M { 226 // for each dependency obj -> d (= deps[i]), create graph edges n->s and s->n 227 for d := range objMap[obj].deps { 228 // only consider nodes that may be an initialization dependency 229 if d, _ := d.(dependency); d != nil { 230 d := M[d] 231 n.succ.add(d) 232 d.pred.add(n) 233 } 234 } 235 } 236 237 var G, funcG []*graphNode // separate non-functions and functions 238 for _, n := range M { 239 if _, ok := n.obj.(*Func); ok { 240 funcG = append(funcG, n) 241 } else { 242 G = append(G, n) 243 } 244 } 245 246 // remove function nodes and collect remaining graph nodes in G 247 // (Mutually recursive functions may introduce cycles among themselves 248 // which are permitted. Yet such cycles may incorrectly inflate the dependency 249 // count for variables which in turn may not get scheduled for initialization 250 // in correct order.) 251 // 252 // Note that because we recursively copy predecessors and successors 253 // throughout the function graph, the cost of removing a function at 254 // position X is proportional to cost * (len(funcG)-X). Therefore, we should 255 // remove high-cost functions last. 256 sort.Slice(funcG, func(i, j int) bool { 257 return funcG[i].cost() < funcG[j].cost() 258 }) 259 for _, n := range funcG { 260 // connect each predecessor p of n with each successor s 261 // and drop the function node (don't collect it in G) 262 for p := range n.pred { 263 // ignore self-cycles 264 if p != n { 265 // Each successor s of n becomes a successor of p, and 266 // each predecessor p of n becomes a predecessor of s. 267 for s := range n.succ { 268 // ignore self-cycles 269 if s != n { 270 p.succ.add(s) 271 s.pred.add(p) 272 } 273 } 274 delete(p.succ, n) // remove edge to n 275 } 276 } 277 for s := range n.succ { 278 delete(s.pred, n) // remove edge to n 279 } 280 } 281 282 // fill in index and ndeps fields 283 for i, n := range G { 284 n.index = i 285 n.ndeps = len(n.succ) 286 } 287 288 return G 289} 290 291// ---------------------------------------------------------------------------- 292// Priority queue 293 294// nodeQueue implements the container/heap interface; 295// a nodeQueue may be used as a priority queue. 296type nodeQueue []*graphNode 297 298func (a nodeQueue) Len() int { return len(a) } 299 300func (a nodeQueue) Swap(i, j int) { 301 x, y := a[i], a[j] 302 a[i], a[j] = y, x 303 x.index, y.index = j, i 304} 305 306func (a nodeQueue) Less(i, j int) bool { 307 x, y := a[i], a[j] 308 // nodes are prioritized by number of incoming dependencies (1st key) 309 // and source order (2nd key) 310 return x.ndeps < y.ndeps || x.ndeps == y.ndeps && x.obj.order() < y.obj.order() 311} 312 313func (a *nodeQueue) Push(x interface{}) { 314 panic("unreachable") 315} 316 317func (a *nodeQueue) Pop() interface{} { 318 n := len(*a) 319 x := (*a)[n-1] 320 x.index = -1 // for safety 321 *a = (*a)[:n-1] 322 return x 323} 324