github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/callgraph/vta/graph.go (about) 1 // Copyright 2021 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 vta 6 7 import ( 8 "fmt" 9 "go/token" 10 "go/types" 11 12 "github.com/powerman/golang-tools/go/callgraph" 13 "github.com/powerman/golang-tools/go/ssa" 14 "github.com/powerman/golang-tools/go/types/typeutil" 15 ) 16 17 // node interface for VTA nodes. 18 type node interface { 19 Type() types.Type 20 String() string 21 } 22 23 // constant node for VTA. 24 type constant struct { 25 typ types.Type 26 } 27 28 func (c constant) Type() types.Type { 29 return c.typ 30 } 31 32 func (c constant) String() string { 33 return fmt.Sprintf("Constant(%v)", c.Type()) 34 } 35 36 // pointer node for VTA. 37 type pointer struct { 38 typ *types.Pointer 39 } 40 41 func (p pointer) Type() types.Type { 42 return p.typ 43 } 44 45 func (p pointer) String() string { 46 return fmt.Sprintf("Pointer(%v)", p.Type()) 47 } 48 49 // mapKey node for VTA, modeling reachable map key types. 50 type mapKey struct { 51 typ types.Type 52 } 53 54 func (mk mapKey) Type() types.Type { 55 return mk.typ 56 } 57 58 func (mk mapKey) String() string { 59 return fmt.Sprintf("MapKey(%v)", mk.Type()) 60 } 61 62 // mapValue node for VTA, modeling reachable map value types. 63 type mapValue struct { 64 typ types.Type 65 } 66 67 func (mv mapValue) Type() types.Type { 68 return mv.typ 69 } 70 71 func (mv mapValue) String() string { 72 return fmt.Sprintf("MapValue(%v)", mv.Type()) 73 } 74 75 // sliceElem node for VTA, modeling reachable slice and array element types. 76 type sliceElem struct { 77 typ types.Type 78 } 79 80 func (s sliceElem) Type() types.Type { 81 return s.typ 82 } 83 84 func (s sliceElem) String() string { 85 return fmt.Sprintf("Slice([]%v)", s.Type()) 86 } 87 88 // channelElem node for VTA, modeling reachable channel element types. 89 type channelElem struct { 90 typ types.Type 91 } 92 93 func (c channelElem) Type() types.Type { 94 return c.typ 95 } 96 97 func (c channelElem) String() string { 98 return fmt.Sprintf("Channel(chan %v)", c.Type()) 99 } 100 101 // field node for VTA. 102 type field struct { 103 StructType types.Type 104 index int // index of the field in the struct 105 } 106 107 func (f field) Type() types.Type { 108 s := f.StructType.Underlying().(*types.Struct) 109 return s.Field(f.index).Type() 110 } 111 112 func (f field) String() string { 113 s := f.StructType.Underlying().(*types.Struct) 114 return fmt.Sprintf("Field(%v:%s)", f.StructType, s.Field(f.index).Name()) 115 } 116 117 // global node for VTA. 118 type global struct { 119 val *ssa.Global 120 } 121 122 func (g global) Type() types.Type { 123 return g.val.Type() 124 } 125 126 func (g global) String() string { 127 return fmt.Sprintf("Global(%s)", g.val.Name()) 128 } 129 130 // local node for VTA modeling local variables 131 // and function/method parameters. 132 type local struct { 133 val ssa.Value 134 } 135 136 func (l local) Type() types.Type { 137 return l.val.Type() 138 } 139 140 func (l local) String() string { 141 return fmt.Sprintf("Local(%s)", l.val.Name()) 142 } 143 144 // indexedLocal node for VTA node. Models indexed locals 145 // related to the ssa extract instructions. 146 type indexedLocal struct { 147 val ssa.Value 148 index int 149 typ types.Type 150 } 151 152 func (i indexedLocal) Type() types.Type { 153 return i.typ 154 } 155 156 func (i indexedLocal) String() string { 157 return fmt.Sprintf("Local(%s[%d])", i.val.Name(), i.index) 158 } 159 160 // function node for VTA. 161 type function struct { 162 f *ssa.Function 163 } 164 165 func (f function) Type() types.Type { 166 return f.f.Type() 167 } 168 169 func (f function) String() string { 170 return fmt.Sprintf("Function(%s)", f.f.Name()) 171 } 172 173 // nestedPtrInterface node represents all references and dereferences 174 // of locals and globals that have a nested pointer to interface type. 175 // We merge such constructs into a single node for simplicity and without 176 // much precision sacrifice as such variables are rare in practice. Both 177 // a and b would be represented as the same PtrInterface(I) node in: 178 // type I interface 179 // var a ***I 180 // var b **I 181 type nestedPtrInterface struct { 182 typ types.Type 183 } 184 185 func (l nestedPtrInterface) Type() types.Type { 186 return l.typ 187 } 188 189 func (l nestedPtrInterface) String() string { 190 return fmt.Sprintf("PtrInterface(%v)", l.typ) 191 } 192 193 // nestedPtrFunction node represents all references and dereferences of locals 194 // and globals that have a nested pointer to function type. We merge such 195 // constructs into a single node for simplicity and without much precision 196 // sacrifice as such variables are rare in practice. Both a and b would be 197 // represented as the same PtrFunction(func()) node in: 198 // var a *func() 199 // var b **func() 200 type nestedPtrFunction struct { 201 typ types.Type 202 } 203 204 func (p nestedPtrFunction) Type() types.Type { 205 return p.typ 206 } 207 208 func (p nestedPtrFunction) String() string { 209 return fmt.Sprintf("PtrFunction(%v)", p.typ) 210 } 211 212 // panicArg models types of all arguments passed to panic. 213 type panicArg struct{} 214 215 func (p panicArg) Type() types.Type { 216 return nil 217 } 218 219 func (p panicArg) String() string { 220 return "Panic" 221 } 222 223 // recoverReturn models types of all return values of recover(). 224 type recoverReturn struct{} 225 226 func (r recoverReturn) Type() types.Type { 227 return nil 228 } 229 230 func (r recoverReturn) String() string { 231 return "Recover" 232 } 233 234 // vtaGraph remembers for each VTA node the set of its successors. 235 // Tailored for VTA, hence does not support singleton (sub)graphs. 236 type vtaGraph map[node]map[node]bool 237 238 // addEdge adds an edge x->y to the graph. 239 func (g vtaGraph) addEdge(x, y node) { 240 succs, ok := g[x] 241 if !ok { 242 succs = make(map[node]bool) 243 g[x] = succs 244 } 245 succs[y] = true 246 } 247 248 // successors returns all of n's immediate successors in the graph. 249 // The order of successor nodes is arbitrary. 250 func (g vtaGraph) successors(n node) []node { 251 var succs []node 252 for succ := range g[n] { 253 succs = append(succs, succ) 254 } 255 return succs 256 } 257 258 // typePropGraph builds a VTA graph for a set of `funcs` and initial 259 // `callgraph` needed to establish interprocedural edges. Returns the 260 // graph and a map for unique type representatives. 261 func typePropGraph(funcs map[*ssa.Function]bool, callgraph *callgraph.Graph) (vtaGraph, *typeutil.Map) { 262 b := builder{graph: make(vtaGraph), callGraph: callgraph} 263 b.visit(funcs) 264 return b.graph, &b.canon 265 } 266 267 // Data structure responsible for linearly traversing the 268 // code and building a VTA graph. 269 type builder struct { 270 graph vtaGraph 271 callGraph *callgraph.Graph // initial call graph for creating flows at unresolved call sites. 272 273 // Specialized type map for canonicalization of types.Type. 274 // Semantically equivalent types can have different implementations, 275 // i.e., they are different pointer values. The map allows us to 276 // have one unique representative. The keys are fixed and from the 277 // client perspective they are types. The values in our case are 278 // types too, in particular type representatives. Each value is a 279 // pointer so this map is not expected to take much memory. 280 canon typeutil.Map 281 } 282 283 func (b *builder) visit(funcs map[*ssa.Function]bool) { 284 // Add the fixed edge Panic -> Recover 285 b.graph.addEdge(panicArg{}, recoverReturn{}) 286 287 for f, in := range funcs { 288 if in { 289 b.fun(f) 290 } 291 } 292 } 293 294 func (b *builder) fun(f *ssa.Function) { 295 for _, bl := range f.Blocks { 296 for _, instr := range bl.Instrs { 297 b.instr(instr) 298 } 299 } 300 } 301 302 func (b *builder) instr(instr ssa.Instruction) { 303 switch i := instr.(type) { 304 case *ssa.Store: 305 b.addInFlowAliasEdges(b.nodeFromVal(i.Addr), b.nodeFromVal(i.Val)) 306 case *ssa.MakeInterface: 307 b.addInFlowEdge(b.nodeFromVal(i.X), b.nodeFromVal(i)) 308 case *ssa.MakeClosure: 309 b.closure(i) 310 case *ssa.UnOp: 311 b.unop(i) 312 case *ssa.Phi: 313 b.phi(i) 314 case *ssa.ChangeInterface: 315 // Although in change interface a := A(b) command a and b are 316 // the same object, the only interesting flow happens when A 317 // is an interface. We create flow b -> a, but omit a -> b. 318 // The latter flow is not needed: if a gets assigned concrete 319 // type later on, that cannot be propagated back to b as b 320 // is a separate variable. The a -> b flow can happen when 321 // A is a pointer to interface, but then the command is of 322 // type ChangeType, handled below. 323 b.addInFlowEdge(b.nodeFromVal(i.X), b.nodeFromVal(i)) 324 case *ssa.ChangeType: 325 // change type command a := A(b) results in a and b being the 326 // same value. For concrete type A, there is no interesting flow. 327 // 328 // Note: When A is an interface, most interface casts are handled 329 // by the ChangeInterface instruction. The relevant case here is 330 // when converting a pointer to an interface type. This can happen 331 // when the underlying interfaces have the same method set. 332 // type I interface{ foo() } 333 // type J interface{ foo() } 334 // var b *I 335 // a := (*J)(b) 336 // When this happens we add flows between a <--> b. 337 b.addInFlowAliasEdges(b.nodeFromVal(i), b.nodeFromVal(i.X)) 338 case *ssa.TypeAssert: 339 b.tassert(i) 340 case *ssa.Extract: 341 b.extract(i) 342 case *ssa.Field: 343 b.field(i) 344 case *ssa.FieldAddr: 345 b.fieldAddr(i) 346 case *ssa.Send: 347 b.send(i) 348 case *ssa.Select: 349 b.selekt(i) 350 case *ssa.Index: 351 b.index(i) 352 case *ssa.IndexAddr: 353 b.indexAddr(i) 354 case *ssa.Lookup: 355 b.lookup(i) 356 case *ssa.MapUpdate: 357 b.mapUpdate(i) 358 case *ssa.Next: 359 b.next(i) 360 case ssa.CallInstruction: 361 b.call(i) 362 case *ssa.Panic: 363 b.panic(i) 364 case *ssa.Return: 365 b.rtrn(i) 366 case *ssa.MakeChan, *ssa.MakeMap, *ssa.MakeSlice, *ssa.BinOp, 367 *ssa.Alloc, *ssa.DebugRef, *ssa.Convert, *ssa.Jump, *ssa.If, 368 *ssa.Slice, *ssa.SliceToArrayPointer, *ssa.Range, *ssa.RunDefers: 369 // No interesting flow here. 370 // Notes on individual instructions: 371 // SliceToArrayPointer: t1 = slice to array pointer *[4]T <- []T (t0) 372 // No interesting flow as sliceArrayElem(t1) == sliceArrayElem(t0). 373 return 374 default: 375 panic(fmt.Sprintf("unsupported instruction %v\n", instr)) 376 } 377 } 378 379 func (b *builder) unop(u *ssa.UnOp) { 380 switch u.Op { 381 case token.MUL: 382 // Multiplication operator * is used here as a dereference operator. 383 b.addInFlowAliasEdges(b.nodeFromVal(u), b.nodeFromVal(u.X)) 384 case token.ARROW: 385 t := u.X.Type().Underlying().(*types.Chan).Elem() 386 b.addInFlowAliasEdges(b.nodeFromVal(u), channelElem{typ: t}) 387 default: 388 // There is no interesting type flow otherwise. 389 } 390 } 391 392 func (b *builder) phi(p *ssa.Phi) { 393 for _, edge := range p.Edges { 394 b.addInFlowAliasEdges(b.nodeFromVal(p), b.nodeFromVal(edge)) 395 } 396 } 397 398 func (b *builder) tassert(a *ssa.TypeAssert) { 399 if !a.CommaOk { 400 b.addInFlowEdge(b.nodeFromVal(a.X), b.nodeFromVal(a)) 401 return 402 } 403 // The case where a is <a.AssertedType, bool> register so there 404 // is a flow from a.X to a[0]. Here, a[0] is represented as an 405 // indexedLocal: an entry into local tuple register a at index 0. 406 tup := a.Type().Underlying().(*types.Tuple) 407 t := tup.At(0).Type() 408 409 local := indexedLocal{val: a, typ: t, index: 0} 410 b.addInFlowEdge(b.nodeFromVal(a.X), local) 411 } 412 413 // extract instruction t1 := t2[i] generates flows between t2[i] 414 // and t1 where the source is indexed local representing a value 415 // from tuple register t2 at index i and the target is t1. 416 func (b *builder) extract(e *ssa.Extract) { 417 tup := e.Tuple.Type().Underlying().(*types.Tuple) 418 t := tup.At(e.Index).Type() 419 420 local := indexedLocal{val: e.Tuple, typ: t, index: e.Index} 421 b.addInFlowAliasEdges(b.nodeFromVal(e), local) 422 } 423 424 func (b *builder) field(f *ssa.Field) { 425 fnode := field{StructType: f.X.Type(), index: f.Field} 426 b.addInFlowEdge(fnode, b.nodeFromVal(f)) 427 } 428 429 func (b *builder) fieldAddr(f *ssa.FieldAddr) { 430 t := f.X.Type().Underlying().(*types.Pointer).Elem() 431 432 // Since we are getting pointer to a field, make a bidirectional edge. 433 fnode := field{StructType: t, index: f.Field} 434 b.addInFlowEdge(fnode, b.nodeFromVal(f)) 435 b.addInFlowEdge(b.nodeFromVal(f), fnode) 436 } 437 438 func (b *builder) send(s *ssa.Send) { 439 t := s.Chan.Type().Underlying().(*types.Chan).Elem() 440 b.addInFlowAliasEdges(channelElem{typ: t}, b.nodeFromVal(s.X)) 441 } 442 443 // selekt generates flows for select statement 444 // a = select blocking/nonblocking [c_1 <- t_1, c_2 <- t_2, ..., <- o_1, <- o_2, ...] 445 // between receiving channel registers c_i and corresponding input register t_i. Further, 446 // flows are generated between o_i and a[2 + i]. Note that a is a tuple register of type 447 // <int, bool, r_1, r_2, ...> where the type of r_i is the element type of channel o_i. 448 func (b *builder) selekt(s *ssa.Select) { 449 recvIndex := 0 450 for _, state := range s.States { 451 t := state.Chan.Type().Underlying().(*types.Chan).Elem() 452 453 if state.Dir == types.SendOnly { 454 b.addInFlowAliasEdges(channelElem{typ: t}, b.nodeFromVal(state.Send)) 455 } else { 456 // state.Dir == RecvOnly by definition of select instructions. 457 tupEntry := indexedLocal{val: s, typ: t, index: 2 + recvIndex} 458 b.addInFlowAliasEdges(tupEntry, channelElem{typ: t}) 459 recvIndex++ 460 } 461 } 462 } 463 464 // index instruction a := b[c] on slices creates flows between a and 465 // SliceElem(t) flow where t is an interface type of c. Arrays and 466 // slice elements are both modeled as SliceElem. 467 func (b *builder) index(i *ssa.Index) { 468 et := sliceArrayElem(i.X.Type()) 469 b.addInFlowAliasEdges(b.nodeFromVal(i), sliceElem{typ: et}) 470 } 471 472 // indexAddr instruction a := &b[c] fetches address of a index 473 // into the field so we create bidirectional flow a <-> SliceElem(t) 474 // where t is an interface type of c. Arrays and slice elements are 475 // both modeled as SliceElem. 476 func (b *builder) indexAddr(i *ssa.IndexAddr) { 477 et := sliceArrayElem(i.X.Type()) 478 b.addInFlowEdge(sliceElem{typ: et}, b.nodeFromVal(i)) 479 b.addInFlowEdge(b.nodeFromVal(i), sliceElem{typ: et}) 480 } 481 482 // lookup handles map query commands a := m[b] where m is of type 483 // map[...]V and V is an interface. It creates flows between `a` 484 // and MapValue(V). 485 func (b *builder) lookup(l *ssa.Lookup) { 486 t, ok := l.X.Type().Underlying().(*types.Map) 487 if !ok { 488 // No interesting flows for string lookups. 489 return 490 } 491 b.addInFlowAliasEdges(b.nodeFromVal(l), mapValue{typ: t.Elem()}) 492 } 493 494 // mapUpdate handles map update commands m[b] = a where m is of type 495 // map[K]V and K and V are interfaces. It creates flows between `a` 496 // and MapValue(V) as well as between MapKey(K) and `b`. 497 func (b *builder) mapUpdate(u *ssa.MapUpdate) { 498 t, ok := u.Map.Type().Underlying().(*types.Map) 499 if !ok { 500 // No interesting flows for string updates. 501 return 502 } 503 504 b.addInFlowAliasEdges(mapKey{typ: t.Key()}, b.nodeFromVal(u.Key)) 505 b.addInFlowAliasEdges(mapValue{typ: t.Elem()}, b.nodeFromVal(u.Value)) 506 } 507 508 // next instruction <ok, key, value> := next r, where r 509 // is a range over map or string generates flow between 510 // key and MapKey as well value and MapValue nodes. 511 func (b *builder) next(n *ssa.Next) { 512 if n.IsString { 513 return 514 } 515 tup := n.Type().Underlying().(*types.Tuple) 516 kt := tup.At(1).Type() 517 vt := tup.At(2).Type() 518 519 b.addInFlowAliasEdges(indexedLocal{val: n, typ: kt, index: 1}, mapKey{typ: kt}) 520 b.addInFlowAliasEdges(indexedLocal{val: n, typ: vt, index: 2}, mapValue{typ: vt}) 521 } 522 523 // addInFlowAliasEdges adds an edge r -> l to b.graph if l is a node that can 524 // have an inflow, i.e., a node that represents an interface or an unresolved 525 // function value. Similarly for the edge l -> r with an additional condition 526 // of that l and r can potentially alias. 527 func (b *builder) addInFlowAliasEdges(l, r node) { 528 b.addInFlowEdge(r, l) 529 530 if canAlias(l, r) { 531 b.addInFlowEdge(l, r) 532 } 533 } 534 535 func (b *builder) closure(c *ssa.MakeClosure) { 536 f := c.Fn.(*ssa.Function) 537 b.addInFlowEdge(function{f: f}, b.nodeFromVal(c)) 538 539 for i, fv := range f.FreeVars { 540 b.addInFlowAliasEdges(b.nodeFromVal(fv), b.nodeFromVal(c.Bindings[i])) 541 } 542 } 543 544 // panic creates a flow from arguments to panic instructions to return 545 // registers of all recover statements in the program. Introduces a 546 // global panic node Panic and 547 // 1) for every panic statement p: add p -> Panic 548 // 2) for every recover statement r: add Panic -> r (handled in call) 549 // TODO(zpavlinovic): improve precision by explicitly modeling how panic 550 // values flow from callees to callers and into deferred recover instructions. 551 func (b *builder) panic(p *ssa.Panic) { 552 // Panics often have, for instance, strings as arguments which do 553 // not create interesting flows. 554 if !canHaveMethods(p.X.Type()) { 555 return 556 } 557 558 b.addInFlowEdge(b.nodeFromVal(p.X), panicArg{}) 559 } 560 561 // call adds flows between arguments/parameters and return values/registers 562 // for both static and dynamic calls, as well as go and defer calls. 563 func (b *builder) call(c ssa.CallInstruction) { 564 // When c is r := recover() call register instruction, we add Recover -> r. 565 if bf, ok := c.Common().Value.(*ssa.Builtin); ok && bf.Name() == "recover" { 566 b.addInFlowEdge(recoverReturn{}, b.nodeFromVal(c.(*ssa.Call))) 567 return 568 } 569 570 for _, f := range siteCallees(c, b.callGraph) { 571 addArgumentFlows(b, c, f) 572 } 573 } 574 575 func addArgumentFlows(b *builder, c ssa.CallInstruction, f *ssa.Function) { 576 // When f has no paremeters (including receiver), there is no type 577 // flow here. Also, f's body and parameters might be missing, such 578 // as when vta is used within the github.com/powerman/golang-tools/go/analysis 579 // framework (see github.com/golang/go/issues/50670). 580 if len(f.Params) == 0 { 581 return 582 } 583 cc := c.Common() 584 // When c is an unresolved method call (cc.Method != nil), cc.Value contains 585 // the receiver object rather than cc.Args[0]. 586 if cc.Method != nil { 587 b.addInFlowAliasEdges(b.nodeFromVal(f.Params[0]), b.nodeFromVal(cc.Value)) 588 } 589 590 offset := 0 591 if cc.Method != nil { 592 offset = 1 593 } 594 for i, v := range cc.Args { 595 // Parameters of f might not be available, as in the case 596 // when vta is used within the github.com/powerman/golang-tools/go/analysis 597 // framework (see github.com/golang/go/issues/50670). 598 // 599 // TODO: investigate other cases of missing body and parameters 600 if len(f.Params) <= i+offset { 601 return 602 } 603 b.addInFlowAliasEdges(b.nodeFromVal(f.Params[i+offset]), b.nodeFromVal(v)) 604 } 605 } 606 607 // rtrn produces flows between values of r and c where 608 // c is a call instruction that resolves to the enclosing 609 // function of r based on b.callGraph. 610 func (b *builder) rtrn(r *ssa.Return) { 611 n := b.callGraph.Nodes[r.Parent()] 612 // n != nil when b.callgraph is sound, but the client can 613 // pass any callgraph, including an underapproximate one. 614 if n == nil { 615 return 616 } 617 618 for _, e := range n.In { 619 if cv, ok := e.Site.(ssa.Value); ok { 620 addReturnFlows(b, r, cv) 621 } 622 } 623 } 624 625 func addReturnFlows(b *builder, r *ssa.Return, site ssa.Value) { 626 results := r.Results 627 if len(results) == 1 { 628 // When there is only one return value, the destination register does not 629 // have a tuple type. 630 b.addInFlowEdge(b.nodeFromVal(results[0]), b.nodeFromVal(site)) 631 return 632 } 633 634 tup := site.Type().Underlying().(*types.Tuple) 635 for i, r := range results { 636 local := indexedLocal{val: site, typ: tup.At(i).Type(), index: i} 637 b.addInFlowEdge(b.nodeFromVal(r), local) 638 } 639 } 640 641 // addInFlowEdge adds s -> d to g if d is node that can have an inflow, i.e., a node 642 // that represents an interface or an unresolved function value. Otherwise, there 643 // is no interesting type flow so the edge is omitted. 644 func (b *builder) addInFlowEdge(s, d node) { 645 if hasInFlow(d) { 646 b.graph.addEdge(b.representative(s), b.representative(d)) 647 } 648 } 649 650 // Creates const, pointer, global, func, and local nodes based on register instructions. 651 func (b *builder) nodeFromVal(val ssa.Value) node { 652 if p, ok := val.Type().(*types.Pointer); ok && !isInterface(p.Elem()) && !isFunction(p.Elem()) { 653 // Nested pointer to interfaces are modeled as a special 654 // nestedPtrInterface node. 655 if i := interfaceUnderPtr(p.Elem()); i != nil { 656 return nestedPtrInterface{typ: i} 657 } 658 // The same goes for nested function types. 659 if f := functionUnderPtr(p.Elem()); f != nil { 660 return nestedPtrFunction{typ: f} 661 } 662 return pointer{typ: p} 663 } 664 665 switch v := val.(type) { 666 case *ssa.Const: 667 return constant{typ: val.Type()} 668 case *ssa.Global: 669 return global{val: v} 670 case *ssa.Function: 671 return function{f: v} 672 case *ssa.Parameter, *ssa.FreeVar, ssa.Instruction: 673 // ssa.Param, ssa.FreeVar, and a specific set of "register" instructions, 674 // satisifying the ssa.Value interface, can serve as local variables. 675 return local{val: v} 676 default: 677 panic(fmt.Errorf("unsupported value %v in node creation", val)) 678 } 679 return nil 680 } 681 682 // representative returns a unique representative for node `n`. Since 683 // semantically equivalent types can have different implementations, 684 // this method guarantees the same implementation is always used. 685 func (b *builder) representative(n node) node { 686 if !hasInitialTypes(n) { 687 return n 688 } 689 t := canonicalize(n.Type(), &b.canon) 690 691 switch i := n.(type) { 692 case constant: 693 return constant{typ: t} 694 case pointer: 695 return pointer{typ: t.(*types.Pointer)} 696 case sliceElem: 697 return sliceElem{typ: t} 698 case mapKey: 699 return mapKey{typ: t} 700 case mapValue: 701 return mapValue{typ: t} 702 case channelElem: 703 return channelElem{typ: t} 704 case nestedPtrInterface: 705 return nestedPtrInterface{typ: t} 706 case nestedPtrFunction: 707 return nestedPtrFunction{typ: t} 708 case field: 709 return field{StructType: canonicalize(i.StructType, &b.canon), index: i.index} 710 case indexedLocal: 711 return indexedLocal{typ: t, val: i.val, index: i.index} 712 case local, global, panicArg, recoverReturn, function: 713 return n 714 default: 715 panic(fmt.Errorf("canonicalizing unrecognized node %v", n)) 716 } 717 } 718 719 // canonicalize returns a type representative of `t` unique subject 720 // to type map `canon`. 721 func canonicalize(t types.Type, canon *typeutil.Map) types.Type { 722 rep := canon.At(t) 723 if rep != nil { 724 return rep.(types.Type) 725 } 726 canon.Set(t, t) 727 return t 728 }