github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/ssa/interp/interp.go (about) 1 // Copyright 2013 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 ssa/interp defines an interpreter for the SSA 6 // representation of Go programs. 7 // 8 // This interpreter is provided as an adjunct for testing the SSA 9 // construction algorithm. Its purpose is to provide a minimal 10 // metacircular implementation of the dynamic semantics of each SSA 11 // instruction. It is not, and will never be, a production-quality Go 12 // interpreter. 13 // 14 // The following is a partial list of Go features that are currently 15 // unsupported or incomplete in the interpreter. 16 // 17 // * Unsafe operations, including all uses of unsafe.Pointer, are 18 // impossible to support given the "boxed" value representation we 19 // have chosen. 20 // 21 // * The reflect package is only partially implemented. 22 // 23 // * The "testing" package is no longer supported because it 24 // depends on low-level details that change too often. 25 // 26 // * "sync/atomic" operations are not atomic due to the "boxed" value 27 // representation: it is not possible to read, modify and write an 28 // interface value atomically. As a consequence, Mutexes are currently 29 // broken. 30 // 31 // * recover is only partially implemented. Also, the interpreter 32 // makes no attempt to distinguish target panics from interpreter 33 // crashes. 34 // 35 // * the sizes of the int, uint and uintptr types in the target 36 // program are assumed to be the same as those of the interpreter 37 // itself. 38 // 39 // * all values occupy space, even those of types defined by the spec 40 // to have zero size, e.g. struct{}. This can cause asymptotic 41 // performance degradation. 42 // 43 // * os.Exit is implemented using panic, causing deferred functions to 44 // run. 45 package interp // import "golang.org/x/tools/go/ssa/interp" 46 47 import ( 48 "fmt" 49 "go/token" 50 "go/types" 51 "os" 52 "reflect" 53 "runtime" 54 "sync/atomic" 55 56 "golang.org/x/tools/go/ssa" 57 ) 58 59 type continuation int 60 61 const ( 62 kNext continuation = iota 63 kReturn 64 kJump 65 ) 66 67 // Mode is a bitmask of options affecting the interpreter. 68 type Mode uint 69 70 const ( 71 DisableRecover Mode = 1 << iota // Disable recover() in target programs; show interpreter crash instead. 72 EnableTracing // Print a trace of all instructions as they are interpreted. 73 ) 74 75 type methodSet map[string]*ssa.Function 76 77 // State shared between all interpreted goroutines. 78 type interpreter struct { 79 osArgs []value // the value of os.Args 80 prog *ssa.Program // the SSA program 81 globals map[*ssa.Global]*value // addresses of global variables (immutable) 82 mode Mode // interpreter options 83 reflectPackage *ssa.Package // the fake reflect package 84 errorMethods methodSet // the method set of reflect.error, which implements the error interface. 85 rtypeMethods methodSet // the method set of rtype, which implements the reflect.Type interface. 86 runtimeErrorString types.Type // the runtime.errorString type 87 sizes types.Sizes // the effective type-sizing function 88 goroutines int32 // atomically updated 89 } 90 91 type deferred struct { 92 fn value 93 args []value 94 instr *ssa.Defer 95 tail *deferred 96 } 97 98 type frame struct { 99 i *interpreter 100 caller *frame 101 fn *ssa.Function 102 block, prevBlock *ssa.BasicBlock 103 env map[ssa.Value]value // dynamic values of SSA variables 104 locals []value 105 defers *deferred 106 result value 107 panicking bool 108 panic interface{} 109 } 110 111 func (fr *frame) get(key ssa.Value) value { 112 switch key := key.(type) { 113 case nil: 114 // Hack; simplifies handling of optional attributes 115 // such as ssa.Slice.{Low,High}. 116 return nil 117 case *ssa.Function, *ssa.Builtin: 118 return key 119 case *ssa.Const: 120 return constValue(key) 121 case *ssa.Global: 122 if r, ok := fr.i.globals[key]; ok { 123 return r 124 } 125 } 126 if r, ok := fr.env[key]; ok { 127 return r 128 } 129 panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name())) 130 } 131 132 // runDefer runs a deferred call d. 133 // It always returns normally, but may set or clear fr.panic. 134 func (fr *frame) runDefer(d *deferred) { 135 if fr.i.mode&EnableTracing != 0 { 136 fmt.Fprintf(os.Stderr, "%s: invoking deferred function call\n", 137 fr.i.prog.Fset.Position(d.instr.Pos())) 138 } 139 var ok bool 140 defer func() { 141 if !ok { 142 // Deferred call created a new state of panic. 143 fr.panicking = true 144 fr.panic = recover() 145 } 146 }() 147 call(fr.i, fr, d.instr.Pos(), d.fn, d.args) 148 ok = true 149 } 150 151 // runDefers executes fr's deferred function calls in LIFO order. 152 // 153 // On entry, fr.panicking indicates a state of panic; if 154 // true, fr.panic contains the panic value. 155 // 156 // On completion, if a deferred call started a panic, or if no 157 // deferred call recovered from a previous state of panic, then 158 // runDefers itself panics after the last deferred call has run. 159 // 160 // If there was no initial state of panic, or it was recovered from, 161 // runDefers returns normally. 162 func (fr *frame) runDefers() { 163 for d := fr.defers; d != nil; d = d.tail { 164 fr.runDefer(d) 165 } 166 fr.defers = nil 167 if fr.panicking { 168 panic(fr.panic) // new panic, or still panicking 169 } 170 } 171 172 // lookupMethod returns the method set for type typ, which may be one 173 // of the interpreter's fake types. 174 func lookupMethod(i *interpreter, typ types.Type, meth *types.Func) *ssa.Function { 175 switch typ { 176 case rtypeType: 177 return i.rtypeMethods[meth.Id()] 178 case errorType: 179 return i.errorMethods[meth.Id()] 180 } 181 return i.prog.LookupMethod(typ, meth.Pkg(), meth.Name()) 182 } 183 184 // visitInstr interprets a single ssa.Instruction within the activation 185 // record frame. It returns a continuation value indicating where to 186 // read the next instruction from. 187 func visitInstr(fr *frame, instr ssa.Instruction) continuation { 188 switch instr := instr.(type) { 189 case *ssa.DebugRef: 190 // no-op 191 192 case *ssa.UnOp: 193 fr.env[instr] = unop(instr, fr.get(instr.X)) 194 195 case *ssa.BinOp: 196 fr.env[instr] = binop(instr.Op, instr.X.Type(), fr.get(instr.X), fr.get(instr.Y)) 197 198 case *ssa.Call: 199 fn, args := prepareCall(fr, &instr.Call) 200 fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args) 201 202 case *ssa.ChangeInterface: 203 fr.env[instr] = fr.get(instr.X) 204 205 case *ssa.ChangeType: 206 fr.env[instr] = fr.get(instr.X) // (can't fail) 207 208 case *ssa.Convert: 209 fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X)) 210 211 case *ssa.SliceToArrayPointer: 212 fr.env[instr] = sliceToArrayPointer(instr.Type(), instr.X.Type(), fr.get(instr.X)) 213 214 case *ssa.MakeInterface: 215 fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)} 216 217 case *ssa.Extract: 218 fr.env[instr] = fr.get(instr.Tuple).(tuple)[instr.Index] 219 220 case *ssa.Slice: 221 fr.env[instr] = slice(fr.get(instr.X), fr.get(instr.Low), fr.get(instr.High), fr.get(instr.Max)) 222 223 case *ssa.Return: 224 switch len(instr.Results) { 225 case 0: 226 case 1: 227 fr.result = fr.get(instr.Results[0]) 228 default: 229 var res []value 230 for _, r := range instr.Results { 231 res = append(res, fr.get(r)) 232 } 233 fr.result = tuple(res) 234 } 235 fr.block = nil 236 return kReturn 237 238 case *ssa.RunDefers: 239 fr.runDefers() 240 241 case *ssa.Panic: 242 panic(targetPanic{fr.get(instr.X)}) 243 244 case *ssa.Send: 245 fr.get(instr.Chan).(chan value) <- fr.get(instr.X) 246 247 case *ssa.Store: 248 store(deref(instr.Addr.Type()), fr.get(instr.Addr).(*value), fr.get(instr.Val)) 249 250 case *ssa.If: 251 succ := 1 252 if fr.get(instr.Cond).(bool) { 253 succ = 0 254 } 255 fr.prevBlock, fr.block = fr.block, fr.block.Succs[succ] 256 return kJump 257 258 case *ssa.Jump: 259 fr.prevBlock, fr.block = fr.block, fr.block.Succs[0] 260 return kJump 261 262 case *ssa.Defer: 263 fn, args := prepareCall(fr, &instr.Call) 264 fr.defers = &deferred{ 265 fn: fn, 266 args: args, 267 instr: instr, 268 tail: fr.defers, 269 } 270 271 case *ssa.Go: 272 fn, args := prepareCall(fr, &instr.Call) 273 atomic.AddInt32(&fr.i.goroutines, 1) 274 go func() { 275 call(fr.i, nil, instr.Pos(), fn, args) 276 atomic.AddInt32(&fr.i.goroutines, -1) 277 }() 278 279 case *ssa.MakeChan: 280 fr.env[instr] = make(chan value, asInt64(fr.get(instr.Size))) 281 282 case *ssa.Alloc: 283 var addr *value 284 if instr.Heap { 285 // new 286 addr = new(value) 287 fr.env[instr] = addr 288 } else { 289 // local 290 addr = fr.env[instr].(*value) 291 } 292 *addr = zero(deref(instr.Type())) 293 294 case *ssa.MakeSlice: 295 slice := make([]value, asInt64(fr.get(instr.Cap))) 296 tElt := instr.Type().Underlying().(*types.Slice).Elem() 297 for i := range slice { 298 slice[i] = zero(tElt) 299 } 300 fr.env[instr] = slice[:asInt64(fr.get(instr.Len))] 301 302 case *ssa.MakeMap: 303 var reserve int64 304 if instr.Reserve != nil { 305 reserve = asInt64(fr.get(instr.Reserve)) 306 } 307 if !fitsInt(reserve, fr.i.sizes) { 308 panic(fmt.Sprintf("ssa.MakeMap.Reserve value %d does not fit in int", reserve)) 309 } 310 fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve) 311 312 case *ssa.Range: 313 fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type()) 314 315 case *ssa.Next: 316 fr.env[instr] = fr.get(instr.Iter).(iter).next() 317 318 case *ssa.FieldAddr: 319 fr.env[instr] = &(*fr.get(instr.X).(*value)).(structure)[instr.Field] 320 321 case *ssa.Field: 322 fr.env[instr] = fr.get(instr.X).(structure)[instr.Field] 323 324 case *ssa.IndexAddr: 325 x := fr.get(instr.X) 326 idx := fr.get(instr.Index) 327 switch x := x.(type) { 328 case []value: 329 fr.env[instr] = &x[asInt64(idx)] 330 case *value: // *array 331 fr.env[instr] = &(*x).(array)[asInt64(idx)] 332 default: 333 panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x)) 334 } 335 336 case *ssa.Index: 337 x := fr.get(instr.X) 338 idx := fr.get(instr.Index) 339 340 switch x := x.(type) { 341 case array: 342 fr.env[instr] = x[asInt64(idx)] 343 case string: 344 fr.env[instr] = x[asInt64(idx)] 345 default: 346 panic(fmt.Sprintf("unexpected x type in Index: %T", x)) 347 } 348 349 case *ssa.Lookup: 350 fr.env[instr] = lookup(instr, fr.get(instr.X), fr.get(instr.Index)) 351 352 case *ssa.MapUpdate: 353 m := fr.get(instr.Map) 354 key := fr.get(instr.Key) 355 v := fr.get(instr.Value) 356 switch m := m.(type) { 357 case map[value]value: 358 m[key] = v 359 case *hashmap: 360 m.insert(key.(hashable), v) 361 default: 362 panic(fmt.Sprintf("illegal map type: %T", m)) 363 } 364 365 case *ssa.TypeAssert: 366 fr.env[instr] = typeAssert(fr.i, instr, fr.get(instr.X).(iface)) 367 368 case *ssa.MakeClosure: 369 var bindings []value 370 for _, binding := range instr.Bindings { 371 bindings = append(bindings, fr.get(binding)) 372 } 373 fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings} 374 375 case *ssa.Phi: 376 for i, pred := range instr.Block().Preds { 377 if fr.prevBlock == pred { 378 fr.env[instr] = fr.get(instr.Edges[i]) 379 break 380 } 381 } 382 383 case *ssa.Select: 384 var cases []reflect.SelectCase 385 if !instr.Blocking { 386 cases = append(cases, reflect.SelectCase{ 387 Dir: reflect.SelectDefault, 388 }) 389 } 390 for _, state := range instr.States { 391 var dir reflect.SelectDir 392 if state.Dir == types.RecvOnly { 393 dir = reflect.SelectRecv 394 } else { 395 dir = reflect.SelectSend 396 } 397 var send reflect.Value 398 if state.Send != nil { 399 send = reflect.ValueOf(fr.get(state.Send)) 400 } 401 cases = append(cases, reflect.SelectCase{ 402 Dir: dir, 403 Chan: reflect.ValueOf(fr.get(state.Chan)), 404 Send: send, 405 }) 406 } 407 chosen, recv, recvOk := reflect.Select(cases) 408 if !instr.Blocking { 409 chosen-- // default case should have index -1. 410 } 411 r := tuple{chosen, recvOk} 412 for i, st := range instr.States { 413 if st.Dir == types.RecvOnly { 414 var v value 415 if i == chosen && recvOk { 416 // No need to copy since send makes an unaliased copy. 417 v = recv.Interface().(value) 418 } else { 419 v = zero(st.Chan.Type().Underlying().(*types.Chan).Elem()) 420 } 421 r = append(r, v) 422 } 423 } 424 fr.env[instr] = r 425 426 default: 427 panic(fmt.Sprintf("unexpected instruction: %T", instr)) 428 } 429 430 // if val, ok := instr.(ssa.Value); ok { 431 // fmt.Println(toString(fr.env[val])) // debugging 432 // } 433 434 return kNext 435 } 436 437 // prepareCall determines the function value and argument values for a 438 // function call in a Call, Go or Defer instruction, performing 439 // interface method lookup if needed. 440 func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) { 441 v := fr.get(call.Value) 442 if call.Method == nil { 443 // Function call. 444 fn = v 445 } else { 446 // Interface method invocation. 447 recv := v.(iface) 448 if recv.t == nil { 449 panic("method invoked on nil interface") 450 } 451 if f := lookupMethod(fr.i, recv.t, call.Method); f == nil { 452 // Unreachable in well-typed programs. 453 panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, call.Method)) 454 } else { 455 fn = f 456 } 457 args = append(args, recv.v) 458 } 459 for _, arg := range call.Args { 460 args = append(args, fr.get(arg)) 461 } 462 return 463 } 464 465 // call interprets a call to a function (function, builtin or closure) 466 // fn with arguments args, returning its result. 467 // callpos is the position of the callsite. 468 func call(i *interpreter, caller *frame, callpos token.Pos, fn value, args []value) value { 469 switch fn := fn.(type) { 470 case *ssa.Function: 471 if fn == nil { 472 panic("call of nil function") // nil of func type 473 } 474 return callSSA(i, caller, callpos, fn, args, nil) 475 case *closure: 476 return callSSA(i, caller, callpos, fn.Fn, args, fn.Env) 477 case *ssa.Builtin: 478 return callBuiltin(caller, callpos, fn, args) 479 } 480 panic(fmt.Sprintf("cannot call %T", fn)) 481 } 482 483 func loc(fset *token.FileSet, pos token.Pos) string { 484 if pos == token.NoPos { 485 return "" 486 } 487 return " at " + fset.Position(pos).String() 488 } 489 490 // callSSA interprets a call to function fn with arguments args, 491 // and lexical environment env, returning its result. 492 // callpos is the position of the callsite. 493 func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []value, env []value) value { 494 if i.mode&EnableTracing != 0 { 495 fset := fn.Prog.Fset 496 // TODO(adonovan): fix: loc() lies for external functions. 497 fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn, loc(fset, fn.Pos())) 498 suffix := "" 499 if caller != nil { 500 suffix = ", resuming " + caller.fn.String() + loc(fset, callpos) 501 } 502 defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn, suffix) 503 } 504 fr := &frame{ 505 i: i, 506 caller: caller, // for panic/recover 507 fn: fn, 508 } 509 if fn.Parent() == nil { 510 name := fn.String() 511 if ext := externals[name]; ext != nil { 512 if i.mode&EnableTracing != 0 { 513 fmt.Fprintln(os.Stderr, "\t(external)") 514 } 515 return ext(fr, args) 516 } 517 if fn.Blocks == nil { 518 panic("no code for function: " + name) 519 } 520 } 521 522 // generic function body? 523 if fn.TypeParams().Len() > 0 && len(fn.TypeArgs()) == 0 { 524 panic("interp requires ssa.BuilderMode to include InstantiateGenerics to execute generics") 525 } 526 527 fr.env = make(map[ssa.Value]value) 528 fr.block = fn.Blocks[0] 529 fr.locals = make([]value, len(fn.Locals)) 530 for i, l := range fn.Locals { 531 fr.locals[i] = zero(deref(l.Type())) 532 fr.env[l] = &fr.locals[i] 533 } 534 for i, p := range fn.Params { 535 fr.env[p] = args[i] 536 } 537 for i, fv := range fn.FreeVars { 538 fr.env[fv] = env[i] 539 } 540 for fr.block != nil { 541 runFrame(fr) 542 } 543 // Destroy the locals to avoid accidental use after return. 544 for i := range fn.Locals { 545 fr.locals[i] = bad{} 546 } 547 return fr.result 548 } 549 550 // runFrame executes SSA instructions starting at fr.block and 551 // continuing until a return, a panic, or a recovered panic. 552 // 553 // After a panic, runFrame panics. 554 // 555 // After a normal return, fr.result contains the result of the call 556 // and fr.block is nil. 557 // 558 // A recovered panic in a function without named return parameters 559 // (NRPs) becomes a normal return of the zero value of the function's 560 // result type. 561 // 562 // After a recovered panic in a function with NRPs, fr.result is 563 // undefined and fr.block contains the block at which to resume 564 // control. 565 func runFrame(fr *frame) { 566 defer func() { 567 if fr.block == nil { 568 return // normal return 569 } 570 if fr.i.mode&DisableRecover != 0 { 571 return // let interpreter crash 572 } 573 fr.panicking = true 574 fr.panic = recover() 575 if fr.i.mode&EnableTracing != 0 { 576 fmt.Fprintf(os.Stderr, "Panicking: %T %v.\n", fr.panic, fr.panic) 577 } 578 fr.runDefers() 579 fr.block = fr.fn.Recover 580 }() 581 582 for { 583 if fr.i.mode&EnableTracing != 0 { 584 fmt.Fprintf(os.Stderr, ".%s:\n", fr.block) 585 } 586 block: 587 for _, instr := range fr.block.Instrs { 588 if fr.i.mode&EnableTracing != 0 { 589 if v, ok := instr.(ssa.Value); ok { 590 fmt.Fprintln(os.Stderr, "\t", v.Name(), "=", instr) 591 } else { 592 fmt.Fprintln(os.Stderr, "\t", instr) 593 } 594 } 595 switch visitInstr(fr, instr) { 596 case kReturn: 597 return 598 case kNext: 599 // no-op 600 case kJump: 601 break block 602 } 603 } 604 } 605 } 606 607 // doRecover implements the recover() built-in. 608 func doRecover(caller *frame) value { 609 // recover() must be exactly one level beneath the deferred 610 // function (two levels beneath the panicking function) to 611 // have any effect. Thus we ignore both "defer recover()" and 612 // "defer f() -> g() -> recover()". 613 if caller.i.mode&DisableRecover == 0 && 614 caller != nil && !caller.panicking && 615 caller.caller != nil && caller.caller.panicking { 616 caller.caller.panicking = false 617 p := caller.caller.panic 618 caller.caller.panic = nil 619 620 // TODO(adonovan): support runtime.Goexit. 621 switch p := p.(type) { 622 case targetPanic: 623 // The target program explicitly called panic(). 624 return p.v 625 case runtime.Error: 626 // The interpreter encountered a runtime error. 627 return iface{caller.i.runtimeErrorString, p.Error()} 628 case string: 629 // The interpreter explicitly called panic(). 630 return iface{caller.i.runtimeErrorString, p} 631 default: 632 panic(fmt.Sprintf("unexpected panic type %T in target call to recover()", p)) 633 } 634 } 635 return iface{} 636 } 637 638 // setGlobal sets the value of a system-initialized global variable. 639 func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) { 640 if g, ok := i.globals[pkg.Var(name)]; ok { 641 *g = v 642 return 643 } 644 panic("no global variable: " + pkg.Pkg.Path() + "." + name) 645 } 646 647 // Interpret interprets the Go program whose main package is mainpkg. 648 // mode specifies various interpreter options. filename and args are 649 // the initial values of os.Args for the target program. sizes is the 650 // effective type-sizing function for this program. 651 // 652 // Interpret returns the exit code of the program: 2 for panic (like 653 // gc does), or the argument to os.Exit for normal termination. 654 // 655 // The SSA program must include the "runtime" package. 656 // 657 // Type parameterized functions must have been built with 658 // InstantiateGenerics in the ssa.BuilderMode to be interpreted. 659 func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename string, args []string) (exitCode int) { 660 i := &interpreter{ 661 prog: mainpkg.Prog, 662 globals: make(map[*ssa.Global]*value), 663 mode: mode, 664 sizes: sizes, 665 goroutines: 1, 666 } 667 runtimePkg := i.prog.ImportedPackage("runtime") 668 if runtimePkg == nil { 669 panic("ssa.Program doesn't include runtime package") 670 } 671 i.runtimeErrorString = runtimePkg.Type("errorString").Object().Type() 672 673 initReflect(i) 674 675 i.osArgs = append(i.osArgs, filename) 676 for _, arg := range args { 677 i.osArgs = append(i.osArgs, arg) 678 } 679 680 for _, pkg := range i.prog.AllPackages() { 681 // Initialize global storage. 682 for _, m := range pkg.Members { 683 switch v := m.(type) { 684 case *ssa.Global: 685 cell := zero(deref(v.Type())) 686 i.globals[v] = &cell 687 } 688 } 689 } 690 691 // Top-level error handler. 692 exitCode = 2 693 defer func() { 694 if exitCode != 2 || i.mode&DisableRecover != 0 { 695 return 696 } 697 switch p := recover().(type) { 698 case exitPanic: 699 exitCode = int(p) 700 return 701 case targetPanic: 702 fmt.Fprintln(os.Stderr, "panic:", toString(p.v)) 703 case runtime.Error: 704 fmt.Fprintln(os.Stderr, "panic:", p.Error()) 705 case string: 706 fmt.Fprintln(os.Stderr, "panic:", p) 707 default: 708 fmt.Fprintf(os.Stderr, "panic: unexpected type: %T: %v\n", p, p) 709 } 710 711 // TODO(adonovan): dump panicking interpreter goroutine? 712 // buf := make([]byte, 0x10000) 713 // runtime.Stack(buf, false) 714 // fmt.Fprintln(os.Stderr, string(buf)) 715 // (Or dump panicking target goroutine?) 716 }() 717 718 // Run! 719 call(i, nil, token.NoPos, mainpkg.Func("init"), nil) 720 if mainFn := mainpkg.Func("main"); mainFn != nil { 721 call(i, nil, token.NoPos, mainFn, nil) 722 exitCode = 0 723 } else { 724 fmt.Fprintln(os.Stderr, "No main function.") 725 exitCode = 1 726 } 727 return 728 } 729 730 // deref returns a pointer's element type; otherwise it returns typ. 731 // TODO(adonovan): Import from ssa? 732 func deref(typ types.Type) types.Type { 733 if p, ok := typ.Underlying().(*types.Pointer); ok { 734 return p.Elem() 735 } 736 return typ 737 }