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