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