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